Effective C# (Covers C# 6.0), (includes Content Update Program): 50 Specific Ways to Improve Your C#, 3rd Edition By Bill Wagner 讀後心得
使用 var 宣告物件型別,會讓編譯器自動推斷最適合的型別。作者使用"prefer"而非"always";表示各有其適用的情境。以下為"不應該"使用 var 推斷物件型別的情況。
範例程式碼:
var f = GetMagicNumber( );
var total = 100 * f / 6;
Console.WriteLine( $"Declared Type : {total.GetType( ).Name}, Value : {total}" );
Output:
Declared Type : Double, Value : 166.666666666667
Declared Type : Single, Value : 166.6667
Declared Type : Decimal, Value : 166.666666666666666666666666667
Declared Type : Int32, Value : 166
Declared Type : Int64, Value : 166
其中 f 的型別會與 GetMagicNumber 回傳的型別結果一致;這會直接影響 total 的計算精度。若想在編譯階段明確指定 total 的型別,則 f 不應該交由 var 推斷;而是明確指定 f 型別。
除此之外,物件的型別宣告應該盡量使用 var 宣告;讓編譯器推斷最適合且合理的型別。以下為一個向 DB 取得資料的例子。
範例程式碼:
public IEnumerable<string> FindCustomersStartWith1( string start )
{
IEnumerable<string> q = from c in db.Customers
select c.ContactName;
var q2 = q.Where( s => s.StartWith( start ) );
return q2;
}
此處有一個很大的效能問題,原因在於 q 被明確指定為 IEnumerable<string>;接著在本機端記憶體操作 q.Where 指定給 q2 (IEnumerable<string>),意味著全部客戶的 ContactName 皆從遠端 DB(假設透過網路)暫存進本機記憶體 q。最後回傳的資料 q2 可能只有幾筆,但已經浪費的大量頻寬在取得所有客戶的資訊(c.ContactName)與額外的本機記憶體操作。
改良後程式碼:
public IEnumerable<string> FindCustomersStartWith2( string start )
{
var q = from c in db.Customers
select c.ContactName;
var q2 = q.Where( s => s.StartWith( start ) );
return q2;
}
現在 q 的型別被推斷為 IQueryable<string>,也就是只組合 queryString 而非直接向 DB 取得資料(節省頻寬);接著呼叫 q.Where 做進一步篩選並指定給 q2(IQueryable<string>)。目前為止,皆尚未真正將客戶資料透過網路讀進本機記憶體;直到外部呼叫 ToList( ) 或是 foreach。如此做法不僅節省頻寬,同時也增加了查詢的效率(節省本機記憶體)。
1. 在不明確指定型別會造成語意不清時,不應該使用 var 推斷型別。
2. 除了 1. 的情況,盡可能使用 var 推斷型別;讓編譯器選擇最有效率的方式。
參考資料:
Returning IEnumerable<T> vs. IQueryable<T>
關於 IQueryable<T> 特性的小實驗