緣起
前一陣子參與的專案裡, 用了不少 LINQ 的語法, 但沒有時間細推其原埋.依 MSDN 的官方說法: LINQ 的語法共有 2 種: 一個是 Query Syntax, 一個是 Method Syntax. 本文將作淺顯的說明.
另外, 亦將介紹一個工具 LINQPad 的工具, 在您還不是很熟悉 Method Syntax 的時候, 可以協助將 Query Syntax 轉為對應的 Method Syntax.
完整程式範例, 筆者放在 GitHub, 請由此下載.
名詞定義
- Query Syntax: 係指採用類類 SQL 敍述的 LINQ 語法.
- Method Syntax: 係指採用 Fluent Interface (or Extension Method) + Lambda Expression 的 LINQ 語法.
- 對於 Fluent Interface 有興趣者, 可以參考 小朱 的 [.NET] Fluent Interface: 實作 Method Chaining 又不會有耦合性的作法
- 註: 該篇文章有其它人留言: "不錯的Pattern; 不過因為 C# 3 之後有了Extension Methods 就沒再使用此方式, 因為 Extension Methods 把定義 interface 的步驟都省了...雖然只能用在.Net.但在其他語言這是個好方法."
- 對於 Lambda Expression 有興趣者, 可以參考 小朱 的 [.NET] 由委派演進到 Lambda Expression
class Program { static void Main(string[] args) { int[] numbers = { 5, 10, 8, 3, 6, 12 }; //Query syntax: IEnumerable<int> numQuery1 = from num in numbers where num % 2 == 0 orderby num select num; //Method syntax: IEnumerable<int> numQuery2 = numbers.Where(num => num % 2 == 0).OrderBy(n => n); foreach (int i in numQuery1) { Console.Write(i + " "); } Console.WriteLine(System.Environment.NewLine); foreach (int i in numQuery2) { Console.Write(i + " "); } // Keep the console open in debug mode. Console.WriteLine(System.Environment.NewLine); Console.WriteLine("Press any key to exit"); Console.ReadKey(); /* Output: 6 8 10 12 6 8 10 12 */ } }
各位應該有注意到這段 IEnumerable<int> numQuery2 = numbers.Where(num => num % 2 == 0).OrderBy(n => n); Where 之後, 又來一個 OrderBy; 其實只要去看一下 Enumerable 的源碼就可以了解.
Where 的回傳值是 IEnumerable<TSource>, 而 OrderBy 也是 Enumerable 裡的一個擴充方法 (擴充 IEnumerable<TSource>, 所以可以這樣一直 . 下去.
Where 的源碼:
public static IEnumerable<TSource> Where<TSource>(this IEnumerable<TSource> source, Func<TSource, bool> predicate) { if (source == null) throw Error.ArgumentNull("source"); if (predicate == null) throw Error.ArgumentNull("predicate"); if (source is Iterator<TSource>) return ((Iterator<TSource>)source).Where(predicate); if (source is TSource[]) return new WhereArrayIterator<TSource>((TSource[])source, predicate); if (source is List<TSource>) return new WhereListIterator<TSource>((List<TSource>)source, predicate); return new WhereEnumerableIterator<TSource>(source, predicate); }
OrderBy 的源碼:
public static IOrderedEnumerable<TSource> OrderBy<TSource, TKey>(this IEnumerable<TSource> source, Func<TSource, TKey> keySelector) { return new OrderedEnumerable<TSource, TKey>(source, keySelector, null, false); }
LINQPad 的操作
那麼上述的 Query Syntax 如何轉換為 Method Syntax 呢? 請參考下圖
Convert Query Syntax to Method Syntax with LINQPad |
總結
LINQ 的語法共有 2 種: 一個是 Query Syntax, 一個是 Method Syntax; 對於初學者而言, Query Syntax 是比較容易入門的; 但若要使程式更精鍊, 則還是要學習 Method Syntax.至於如何將 Query Syntax 轉為對應的 Method Syntax, 則可以利用 LINQPad 這個工具.
補充 (2015.12.04)
1. 修正參考自 小朱 部落格的相關文件連結.
2. 補充以下語法, 以處理回傳多個欄位的狀況
//Query syntax: var numQuery1 = from num in numbers.AsQueryable() where num % 2 == 1 orderby num select new { Num1 = num, Num2 = num + 1 } ; foreach (var x in numQuery1) { Console.WriteLine(x.Num1 + " " + x.Num2); } //Method syntax: var numQuery2 = numbers.Where(num => ((num % 2) == 1)) .OrderBy(num => num) .Select(num => new { Num1 = num, Num2 = num + 1 } ) ; foreach (var x in numQuery2 ) { Console.WriteLine(x.Num1 + " " + x.Num2); } /* Output 3 4 5 6 */
string[] words = { "aPpLe", "BaNaNa", "ORaNGe" }; //編繹時, var 會被改成 IEnumerable<<>f__AnonymousType0> 資料型態 var newwords = words.Select((w) => new { Upper = w.ToUpper(), Lower = w.ToLower() }); foreach (var x in newwords) { Console.WriteLine(x.Upper + " : " + x.Lower); } /* Output APPLE : apple BANANA : banana ORANGE : orange */
參考文件
- 小朱 的 [.NET] Fluent Interface: 實作 Method Chaining 又不會有耦合性的作法
- 對 Fluent Interface 有興趣者, 可以參考這篇
- 小朱 的 [.NET] 由委派演進到 Lambda Expression
- 對 Lambda Expression 有興趣者, 可以參考這篇
- Query Syntax and Method Syntax in LINQ (C#)
- MSDN 官方網站
- mrkt 的 LINQPad - 好用到爆炸、.NET開發人員必備的好用工具
- demo 的 LINQPad 有在用LINQ不可或缺的好工具
- demo 的 保護眼睛專欄-LINQ 超好工具 LINQPad 也要變黑色主題(Dark Template)
- LINQPad 下載
沒有留言:
張貼留言