承上文(LINQ 系列:基礎與本質(上)),深入分析 System.Linq.Enumerable 與 System.Func 兩大核心支柱。
LINQ 查詢運算子編譯時轉換為 Enumerable 靜態方法調用:
public static IEnumerable<TSource> Where<TSource>(this IEnumerable<TSource> source, Func<TSource,int,bool> predicate)
public static IEnumerable<TSource> Where<TSource>(this IEnumerable<TSource> source, Func<TSource,bool> predicate)
public static int? Sum(this IEnumerable<int?> source)
大多接受委託參數,廣泛使用 Func<> 與擴充方法(C# 3.0 新特性)。
最後一個型別參數 TResult 為回傳型別:
public delegate TResult Func<T0,T1,T2,T3,TResult>(T0 arg0, T1 arg1, T2 arg2, T3 arg3)
public delegate TResult Func<T0,T1,T2,TResult>(T0 arg0, T1 arg1, T2 arg2)
public delegate TResult Func<T0,T1,TResult>(T0 arg0, T1 arg1)
public delegate TResult Func<T0,TResult>(T0 arg0)
public delegate TResult Func<TResult>()
var subset = from i in numbers where i < 10 select i;
最直接簡潔,編譯器自動映射為 Lambda。
var subset1 = numbers.Where(j => j < 10).OrderBy(j => j);
方法鏈風格,靈活可組合。
Func<int, bool> searchFilter = delegate(int i) { return i < 10; };
Func<int, int> item = delegate(int s) { return s; };
var subset4 = numbers.Where(searchFilter).OrderBy(item).Select(item);
較繁瑣,需手動定義 Func<> 委託。
public static bool Filter(int s) { return s < 10; }
public static int ProcessItem(int s) { return s; }
// 調用:numbers.Where(Filter).OrderBy(ProcessItem).Select(ProcessItem);
IL 代碼顯示:查詢運算子與 Lambda 表達式編譯後完全一致,均調用 Enumerable.Where + 編譯器生成的快取委託 (CS$<>9__CachedAnonymousMethodDelegateN) + 靜態匿名方法 (<QueryOverInts>b__N)。
結論:所有需委託參數的函數均可用 Lambda 替代。Lambda 僅是匿名方法語法糖,大幅提升可讀性。匿名方法是手動定義委託的速記。委託 → 匿名方法 → Lambda 一脈相承,打好基礎最關鍵。
var subset = from i in numbers where i < 10 select i; // 型別:WhereIterator`1
var subset1 = numbers.Where(j => j < 10).OrderBy(j => j); // 型別:OrderedEnumerable`2
表達式形式不同 → 結果型別不同,但皆實作 IEnumerable<T> 可統一遍歷。建議使用 var 接收結果。
延遲執行:僅迭代訪問時計算,保證獲取最新資料:
numbers[0] = 5;
foreach (int i in subset) Console.WriteLine(i); // 輸出包含 5
立即執行:.ToList<T>() 具體化結果集,脫離查詢表達式:
var subset3 = (from i in numbers where i < 10 select i).ToList<int>();
待續…