︿
Top

2015年1月23日 星期五

C# LINQ: QuerySyntax and Method Syntax with LINQPad

緣起

前一陣子參與的專案裡, 用了不少 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 語法.

MSDN 的範例

以下範例, 展示了 Query Syntax 及其對應的 Method Syntax, 該範例的邏輯很簡單, 就是取出陣列裡的偶數值.


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
*/


參考文件


沒有留言:

張貼留言