《Effective C#》讀書筆記——條目8:推薦使用查詢語法而不是循環


  查詢語法(query syntax)可以讓程序邏輯的表達由“命令式”轉換為“聲明式”。查詢語法定義了想要的結果,而把具體實現交給其他的專門實現。使用查詢語法(實現了查詢表達式模式的方法語法也可以)要比傳統的命令式循環結果更加清晰的表達你的意圖。

  下面我們觀察一個使用命令式方法填充一個數組,然后將其內容輸出至控制台:

 1   static void Main(string[] args)
 2         {
 3             int[] foo = new int[100];
 4             for (int num = 0; num < foo.Length; num++)
 5             {
 6                 foo[num] = num * num;
 7             }
 8             foreach (int i in foo)
 9             {
10                 Console.WriteLine(i);
11             }
12             Console.Read();
13         }

 

編寫命令式的代碼需要關注具體的實現細節。但是如果采用查詢語法,實現同樣的功能,代碼更加易於重用且易讀,我們來看上面示例“聲明式”的寫法:

 1   static void Main(string[] args)
 2         {
 3             //生成數組的工作交個一個查詢完成
 4             int[] foo = (from n in Enumerable.Range(0, 100) select n * n).ToArray();
 5 
 6             //循環打印的工作交給一個數組的擴展方法來完成
 7             foo.ForAll((n) => Console.WriteLine(n.ToString()));
 8 
 9             Console.Read();
10         }

我們看到在負責循環打印部分我們使用了一個擴展方法,這個擴展方法帶來了更好的重用性,每次需要對一個序列的元素執行某個操作都可以使用ForAll()方法:

 1 public static class Extensions
 2 {
 3     /// <summary>
 4     /// 為IEnumerable<T>類型添加擴展方法
 5     /// </summary>
 6     /// <typeparam name="T"></typeparam>
 7     /// <param name="sequence"></param>
 8     /// <param name="action"></param>
 9     public static void ForAll<T>(this IEnumerable<T> sequence, Action<T> action)
10     {
11         foreach (T item in sequence)
12         {
13             action(item);
14         }
15     }
16 }

 

上面的實例比較簡單,似乎看不出二者有多大的區別,在下面的實例中我們分別使用“命令式”和“聲明式”來實現比較二者的區別 。

命令式:

View Code
 1   private static IEnumerable<Tuple<int, int>> ProduceIndices()
 2     {
 3         #region 用0到99的整數生產所以的(X,Y)二元組
 4 
 5         //for (int x = 0; x < 100; x++)
 6         //    for (int y = 0; y < 100; y++)
 7         //        yield return Tuple.Create(x, y);
 8 
 9         #endregion
10 
11         #region X和Y的和要小於100
12 
13         //for (int x = 0; x < 100; x++)
14         //    for (int y = 0; y < 100; y++)
15         //        if (x + y < 100)
16         //            yield return Tuple.Create(x, y);
17 
18         #endregion
19 
20         #region 二元組按照其離遠點的距離逆序排列
21 
22         var storage = new List<Tuple<int, int>>();
23 
24         for (int x = 0; x < 100; x++)
25             for (int y = 0; y < 100; y++)
26                 if (x + y < 100)
27                     storage.Add(Tuple.Create(x, y));
28 
29         storage.Sort((point1, point2) => (point1.Item1 * point2.Item1 + point2.Item2 * point2.Item2).CompareTo(point1.Item1 * point1.Item1 + point1.Item2 * point1.Item2));
30         return storage;
31 
32         #endregion
33     }

 

聲明式:

View Code
 1  private static IEnumerable<Tuple<int, int>> QueryIndices()
 2     {
 3         #region 用0到99的整數生產所以的(X,Y)二元組
 4 
 5         //return from x in Enumerable.Range(0, 100)
 6         //       from y in Enumerable.Range(0, 100)
 7         //       select Tuple.Create(x, y);
 8 
 9         #endregion
10 
11         #region X和Y的和要小於100
12 
13         //return from x in Enumerable.Range(0, 100)
14         //       from y in Enumerable.Range(0, 100)
15         //       where x + y < 100
16         //       select Tuple.Create(x, y);
17 
18         #endregion
19 
20         #region 二元組按照其離遠點的距離逆序排列
21 
22         return from x in Enumerable.Range(0, 100)
23                from y in Enumerable.Range(0, 100)
24                where x + y < 100
25                orderby (x * x + y * y) descending
26                select Tuple.Create(x, y);
27 
28         #endregion
29     }

 我們可以看到隨着編程任務的復雜:

  “命令式”版本變得越來越難以理解。如果仔細看的話,甚至都不會發現比較函數中參數被顛倒了(這是個錯誤),而這只是為了能夠降序排列而已。要是沒有任何注釋和穩定,命令式代碼將會更加難以閱讀。”命令式“代碼太過於強調實現目標所需要的詳細步驟,以至於讓人很容易陷入具體的細節中。

  “聲明式”版本的最后一個實現,實際只是將以此過濾(where子句)、以此排序(orderby子句)和一個投射(select)組合起來。查詢語法比循環結構能夠提供更具組合性的API。查詢語法很自然的將算法分解成小塊代碼,每一塊代碼靜對序列中的元素進行單一操作。查詢語法的延遲執行模式也讓開發者能夠將這些單一的操作組合成多步的操作,且只要一次遍歷序列就可以完整執行,而循環語法結構則必須為每一步操作都創建臨時的存儲,或者為序列將要執行的每一批操作都創建專用的方法。

 

小節:

當你需要編寫循環時,首先看看能否用查詢語法實現,若是無法使用查詢語法,那么再看看是否能以方法調用語法替代。這樣寫出的代碼總會比命令式循環結構要簡潔一些。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM