這個建議做法推薦我們多使用 var
(隱含型別的區域變數),讓 compiler 自行推斷實際的類型。
當編譯器看到 var
關鍵字時會參考 =
右側的運算式,背後宣告這個區域變數的型別,可以省掉我們自行判斷宣告型別的流程。
第二個好處是能避免變數被錯誤宣告為不正確的型別。常見的問題就是 IEnumerable<T>
和 IQueryable<T>
轉換錯誤導致的性能問題。
當我們使用 EFCore
搭配 LINQ
來存取資料庫數據時,會使用到LINQ to Objects
與 LINQ to SQL
來篩選或排序我們想要的資料。
由於 IEnumerable<T>
和 IQueryable<T>
在使用上相似,因此在進行 LINQ to SQL
操作時,
有時會強制將結果變數宣告為 IEnumerable<T>
。然而,這種強制轉換會使程式無法享受 IQueryable<T>
所帶來的好處。
使用 var
關鍵字讓編譯器自動推斷變數類型,則能避免此問題並充分利用 IQueryable<T>
的特性。
使用 var
搭配有可讀性的變數名稱,能幫助開發者更容易理解變數的用途。例如,當我們看到 Dictionary<int, Queue<string>>
這樣的類型時,
可能需要花些時間思考它的用途。然而,這種明確宣告的類型並不一定能幫助理解。因此,不如直接使用 var 並將變數名稱改為更具意義的名稱,
例如 jobsQueuedByRegion
,這樣反而能讓我們更快地理解變數的目的和功能。
有可讀性的變數名稱
這個例子中我們可以很輕易的從右側的運算式推測出 foo
變數的類型為 MyType
。
var foo = new MyType();
從這個 Factory 也能夠推測出這個 thing
變數應該是跟 Account
類型有相關。
var thing = AccountFactory.CreateSavingAccount();
但是也有可能沒辦法從右側的運算式推測出具體的類型,這個例子中 result
這個名稱沒辦法從語意推測出類型或涵義,
並且右側的 DoSomeWork
方法回傳類型也很不明確。
var result = someObject.DoSomeWork(anotherParameter);
所以可以從 result
這個變數名稱下手,至少讓開發者推測這個變數應該和 Product
類型有關。
var HighestSellingProduct = someObject.DoSomeWork(anotherParameter)
這也產生另一種可讀性的問題,可能第一個工程師推斷這個變數是 Product
類型,但第二個工程師可能覺得是另一個衍生類型 HighestSellingProduct
。
假設我們想要的類型為 Product
但編譯器推斷成 HighestSellingProduct
那麼這種情況就不應該使用使用 var
,
數值轉型問題
在宣告數值相關的變數時要額外小心,var
推測可能會導致精度的喪失。
在下方的例子中,GetMagicNumber
方法回傳的類型為 decimal,同時宣告的變數 f 類型也會為 decimal,最後在計算 total 時也會知道類型為 decimal,
另一個常見的錯誤是計算 total1 時,右側沒有指定類型導致結果精度丟失。
void Main()
{
var f = GetMagicNumber();
var total = 100 * f / 6;
Console.WriteLine($"Declared Type: {total.GetType().Name}, Value: {total}");
decimal total1 = 100 * 10 / 6;
Console.WriteLine($"Declared Type: {total1.GetType().Name}, Value: {total1}");
}
public decimal GetMagicNumber()
{
return 10;
}
Declared Type: Decimal, Value: 166.66666666666666666666666667
Declared Type: Decimal, Value: 166
Summary
- 除非開發者必須看到宣告型別才能理解程式,否則就使用 var 宣告區域變數。
- 明確宣告數值類別的型別(int、float、double...),不要使用 var 宣告區域變數。
- 其他情況就使用 var 搭配有可讀性的變數名稱。