5.4.3 值的型別推斷
一般情況下,型別推斷是一種機制,從**推斷出型別。其目的為了簡化**,消除了顯式指定所有型別的需要。在本節中,我們會看到值的型別推斷,使建立值更容易,不需要寫出它們的型別。這並不是型別推斷出現的唯一地方——尤其是在 f# 中——這是描述型別推的斷第一部分。我們將在下一章中討論函式(和方法)的型別推斷,以及自動泛型化。
c# 3.0 中的型別推斷
在 c# 中,值型別推斷主要由 var 關鍵字表現,這是 c# 3.0 中的新功能。我們已經看到,清單 5.12 顯示了幾個例子,我們可以更詳細地討論一下。
listing 5.12 type inference using the var keyword (c#)
var num = 10 + (2 * 16);
var str = string.concat(new string );
var unk = null;
型別推斷機制是看賦值運算子的右邊,計算出表示式的型別。即使你不使用 var,也要這樣做,以確保要賦給的變數相容於要賦的值。在最後乙個示例中,c# 編譯器拒絕推斷型別,並報告一條錯誤訊息。空文字可以隱式轉換成任何 .net 引用型別(甚至是乙個可空的值型別),而它本身並沒有真正的 .net 型別。編譯器不知道我們想給變數 unk 哪種型別,所以,必須顯式指定型別。我們早先使用過帶選項型別的 var 關鍵字,那麼,我們來詳細分析幾個示例:
var s1 = option.some
(10);
var s2 = option.some(10);
var n1 = option.none
();
var n2 = option.none();
第一和第三行並不令人吃驚,我們呼叫泛型方法,並顯式指定其型別引數值,因此,編譯器可以推斷返回型別。在第二行,我們沒有指定方法的型別引數值,但編譯器知道第乙個引數值的型別必須與型別引數值相容,因此,正確推斷要建立 option
[2] 型別的值。對最後一行,我們得到錯誤,說"方法 '… …' 的型別引數的不能從用法推斷。"這是因為,在這裡,編譯器沒有足夠的線索,知道應該是什麼型別。
在 c# 中的型別推斷在很多方面有限制,但它仍然是非常有用。在 f# 中,演算法更聰明,在更多的情況下,可以推斷出型別,讓我們看一些 f# 示例。
f# 中的型別推理
在 f# 中,我們可以經常寫大段的**,而無需顯式指定任何型別,因為型別推理機制更複雜。建立值,我們使用 let 關鍵字,事實上,我們沒有見過任何示例,在使用 let 寫的值繫結中,需要顯式指定其型別。清單 5.13 顯示了一些示例,可能希望工作。
listing 5.13 type inference for basic values (f#)
let num = 123
let tup = (123, "hello world")
let opt = some(10)
let input = printfn "calculating..."
if (num = 0) then none
else some(num.tostring())
第乙個示例宣告了乙個的基本型別 int 值。第二種情況中,我們使用元組值建構函式,所以我們會得到乙個 int * string 型別的值。在清單 5.13 中,接下來的兩個繫結建立選項型別的值,更具體地說,是 int 選項與 string 選項。
這些繫結之外,只有最後乙個例子是特別有趣或令人吃驚。我們已經知道,f# 中的一切都是乙個表示式,因此,型別推斷不必要使用任何 f# 表示式(即,任何 f# **,因為一切都是表示式)。在這種情況下,我們的**,首先列印一些內容到螢幕,然後,使用條件表示式返回選項型別。注意,在 f# 的輕量化語法中,空格是有意義的,因此,這個 if 表示式的起始位置與 printfn 呼叫的偏移量相同。
f# 編譯器將看到,賦給 input 的值是從條件分支中返回的。從真的分支中,可以看到,該型別是泛型 'a 選項型別 (因為我們將返回 none),但它並不知道型別例項化是什麼。這要從假分支來推斷,將返回包含字串的 some 值。
我們提到過,f# 型別推斷更複雜,所以,我們現在看一些稍微複雜的示例:
> let (n : option
) = none;;
val n : system.random option
> let n = (none : option
);;
val n : system.random option
> let n = none;;
val n : 'a option
前兩個示例顯示了兩種不同的方法,用於新增型別注釋。通常,,可以將型別批註任何 f# **塊(如果需要)附近。下乙個示例更有趣,f# 無法推斷出值的完整型別。它知道我們要建立乙個泛型選項型別值,但它並不知道我們想要使用什麼泛型型別引數。有趣的是,這並不會導致錯誤,f# 建立乙個泛型值。沒有此構造的 c# 等效,它是只有部分指定型別的值。而不是具體型別(如 int 或 system.object),f# 使用型別引數(可以看到,f# 自動使用以 a 開頭命名的字母型別引數),其型別則由稍後使用的值完全指定。例如,我們可以比較有不同選項值的型別的值,而不會收到錯誤的:
> some("testing...") = n;;
val it : bool = false
> some(123) = n;;
val it : bool = false
現在,我們知道如何宣告和建立泛型值,就應該討論一下如何使用它們來編寫函式了!有關詳細的泛型函式以後再說,現在,有乙個例子將會吊起你的胃口。
[2] 這不是唯一有效的型別,例如,我們可能想用 option
。在 c# 3.0 中,有泛型方法的型別推斷規則漫長而複雜,但是在編譯器願意執行推斷的情況下,它通常會得到所需的結果。
型別推斷 var
型別推斷 type inference 使用 var 關鍵字。編譯器可以根據變數的初始化值 推斷 變數的型別。例如 int number 0 就可以寫成 var number 0 編譯器可以 推斷 出變數number為int型別。上面兩個語句是等價的。var name bu iancai var a...
推斷型別var
1 為什麼使用推斷型別var var可以根據變數的初始值自動推斷區域性變數型別,當無法確定所用變數的具體型別時可使用var 2 如何使用推斷型別var 客戶端 static void main string args 建立物件 var student new 匿名類 console.writelin...
理解auto型別推斷
template void f paramtype param 使用下面的函式呼叫 f expr 我們看到模板型別推斷過程涉及到了模板template 函式f以及引數 包括模板引數和函式引數 呼叫f的時候,編譯器會推斷t和paramtype的型別。auto的實現和這三個部分是有著對應關係的。當使用a...