C# 的 var 是極度的好物,它可以幫你解決長名稱或容易弄錯的型別的自動轉換,但如果用 var 只是拿來規避重構所產生的物件設計問題,那其實只是鴕鳥心態而已。
var
有著很奇怪的解釋而來,所以我不會對 var
有太多很細節的描述,如果想快快樂樂的學習 var
的話,我推薦這篇文章:[.NET]快快樂樂學LINQ系列前哨戰-var與匿名型別
為什麼會有 var
?
要了解它真正的用意,就要了解為什麼 C# 設計者要設計這個指令,當然你也可以不去了解,但慢慢的你就會發現不知道為什麼的時候,就像在浮砂上築高塔一樣,總會覺得不夠踏實。
var
是在 C# 3.0 時產生的一個指令,最初的目的是為了要解兩個問題,一個是型別名稱太長,例如像是:
Microsoft.Office.Inteop.Excel.Sheet sheet = new Microsoft.Office.Interop.Excel.Sheel();
這麼長的名稱雖然可以用 using Microsoft.Office.Interop.Excel
取代,但若程式的引用的命名空間太多時,就會有滿到溢出來的 using
充斥在你的 C# 檔案開頭,這時候 var
就可以幫你解決這個問題了,因為它是編譯時期的型別推論 (compiler time type inference),可幫助開發人員減少這部份的負擔,如果你覺得這個例子太爛,那我再舉一個:
Tuple<int, int, int, int, int, int, int, int, int, int, int, int, int, int> GenerateReportFunc();
如果用明顯的型別宣告,我看開發人員可能真的要跳樓了吧,這時候 var 的強項就很明顯了,只要寫個 var report = GenerateReportFunc();
,就能優雅的解決問題。
第二種情況就是引用來源的型別不定性,這種情況容易發生在使用別人的元件時。
如果是同一個團隊可異動原始碼的,那是設計問題,應該重構,而不是用
var
去規避,那是鴕鳥心態。除了 LINQ (IQueryable
與 IEnumerable
外),我記得最經典的算是 NPOI 1.x->2.x 這一段,因為原本 1.x 用的是實作型別,例如 Row
(類別),但到了 2.x,它反而變成了 IRow
(介面):
// NPOI 1.x
Row row = sheet.GetRow(1);
// NPOI 2.x
IRow row = sheet.GetRow(i);
像這種原本使用實作但後面突然改成抽象時,使用明顯型別宣告的程式會出現編譯錯誤,因為編譯器不會知道傳來的抽象確實就是你指定的物件類別,因此會送你 Compile Error。這時你有兩個解決方法:
// solution 1: explicit type cast
Row row = (Row)sheet.GetRow(1);
// solution 2: compile-time type inference.
var row = sheet.GetRow(1);
使用顯式型別轉換 (Explicit Type Cast) 可以解決問題,但會增加 InvalidCastException
的風險,因為你不會知道它下次會不會變成 Row2
的實作,這時使用編譯時期型別推論的優點的浮現出來了。
為什麼要推薦使用 var
?
原因很簡單,因為它可以幫你解決在型態不明顯、型別名稱過長或是型態可能會異動的情境,而且可以統一團隊的 coding style,也減少一些初階的開發人員在型別上的使用錯誤。
另外也有一些原因是要求一定要使用 var
的,例如前面有提過的 named tuple。
不能使用 var 的情境
var 真的是極度好物,但是有幾個地方不能使用或不宜使用。
- 無法事先給予初值的情況,因為這樣無法讓編譯器推論它應有的型態。
- 值域相當接近的情況,例如
float
,double
和int
,long
,若對值的正確性很敏感的話,最好用明確宣告。 - 已經要求要有明確的型別宣告時,這時就不宜使用
var
。 - 若要求型別是抽象 (abstract, 例如 interface),但是來源傳回的是具體類別 (concrete class) 時,不宜使用
var
,它會將型別推斷成具體類別,會喪失抽象的優勢。
Reference:
https://dotblogs.com.tw/hatelove/2012/04/20/var-and-anonymous-types-introduction
https://stackoverflow.com/questions/356846/will-using-var-affect-performance