新手概念 · 型別

C# 裡 var 與顯式型別

最常見的誤解:var 不是動態型別。變數依然有唯一的型別,由編譯器從等號右邊挑出來——只是你沒親筆寫下它。

var 到底做了什麼

型別來自右邊,在編譯期決定。Visual Studio 裡這個程式會列印型別——而且從此再也不變:

var price = 9.99;        // double —— 因為 9.99 是 double 字面量
var count = 42;          // int    —— 因為 42 是 int 字面量
var name = "Ada";        // string

Console.WriteLine(price.GetType());   // System.Double
Console.WriteLine(count.GetType());   // System.Int32
Console.WriteLine(name.GetType());    // System.String

(.GetType() 反射不在瀏覽器引擎的教學子集裡——下面的論斷用能在引擎裡跑的檢查來驗證。)

引擎嚴格的型別檢查能在現場證明同一件事——var 變數的行為和它的推斷型別一模一樣:

var x = 9.99;   // 從這一行起 x 就是 double
int y = x;      // CS0266: 和你寫的 'double x = 9.99;' 一模一樣

如果 var 是動態的(像 JavaScript),這個賦值早就編譯過了。它沒有——因為 x 是貨真價實的 double,只是被推斷出來而已。

第一天就該知道的兩個字面量陷阱

var a = 7 / 2.0;    // double: 3.5
var b = 7 / 2;      // int: 3 —— 整數除法,小數沒了!

Console.WriteLine(a);
Console.WriteLine(b);

var 忠實地繼承表示式產出的東西——包括整數除法的意外(完整故事:為什麼 7 / 2 是 3)。

var 的高光時刻

當型別在同一行裡已經一目瞭然時用 var——那它就是純粹的降噪:

var names = new List<string>();      // 顯然是 List<string>
var dog   = new Dog("Rex", 3);         // 顯然是 Dog
var total = price * quantity;          // 型別簡單時也行

泛型容器是最能感受它的地方——對比:

Dictionary<string, List<int>> scores = new Dictionary<string, List<int>>();  // 寫了兩遍
var scores2 = new Dictionary<string, List<int>>();                          // 只寫一遍
var scores = new Dictionary>();
scores["Ada"] = new List { 90, 85 };

Console.WriteLine($"Ada has {scores["Ada"].Count} scores");

顯式型別什麼時候贏

當型別是讀者無法從別處得知的資訊時,寫出來:

var label = "count: ";
int n = 5;
Console.WriteLine(label + n);   // 沒問題:string + int 拼接

var wrong = n = label;          // 故意寫的胡話:把 string 塞進 int 槽

var 必須遵守的規矩

團隊慣例,壓縮成一張表

場景用為什麼
同一行有 newvar型別已經在右邊寫過了
數值宣告顯式int 還是 double 是承重資訊
方法呼叫的結果顯式(或好名字)讀者不該被迫去查
LINQ / 複雜泛型var完整型別名比邏輯本身還長

大多數風格指南(包括微軟自己的)落在同一句話:右邊能讓型別一目瞭然時用 var,否則寫出來。兩個老手吵起來,吵的幾乎全是"什麼算一目瞭然"。