2018年1月1日 星期一

簡單介紹 C# 的 yield return

情境:
找出 1 到 n 的整數中被 x 整除的數

第一版 C# 程式碼
  1. private static void OutputFor1(int max, int divisible)
  2. {
  3. for (int currentNum = 1; currentNum <= max; currentNum++)
  4. {
  5. if (currentNum % divisible != 0)
  6. {
  7. continue;
  8. }
  9. System.Console.Write($"{currentNum} ");
  10. }
  11. System.Console.WriteLine();
  12. }

第一版修改原因:
1.每行顯示一個數字
2.邏輯改成被 x 乘於二整除的數

  1. private static void OutputFor2(int max, int divisible)
  2. {
  3. //
  4. // 某數的2倍整除 & 顯示由上而下
  5. //
  6. #region 說明
  7. // 改變顯示方式及運算邏輯時都改動這段程式碼
  8. // 違反「單一職責」
  9. #endregion 說明
  10. int numMultiplyByTwo_ = divisible * 2;
  11. for (int currentNum = 1; currentNum <= max; currentNum++)
  12. {
  13. //if (currentNum % num != 0)
  14. if (currentNum % numMultiplyByTwo_ != 0)
  15. {
  16. continue;
  17. }
  18. //System.Console.Write($"{currentNum} ");
  19. System.Console.WriteLine($"{currentNum} ");
  20. }
  21. System.Console.WriteLine();
  22. }

針對違反「單一責任」對程式碼進行優化

  1. private static void OutputForeachList(int max, int divisible)
  2. {
  3. foreach (int item in EnumerableList(max, divisible))
  4. {
  5. System.Console.WriteLine($"{item} ");
  6. }
  7. System.Console.WriteLine();
  8. }
  9. private static IEnumerable EnumerableList(int max, int divisible)
  10. {
  11. List result_ = new List();
  12. for (int currentNum = 1; currentNum <= max; currentNum++)
  13. {
  14. System.Console.WriteLine($"讀取{currentNum} ");
  15. if (currentNum % divisible != 0)
  16. {
  17. continue;
  18. }
  19. result_.Add(currentNum);
  20. }
  21. return result_;
  22. }



























雖然執行結果正確,但是會發現一個效能問題。
EnumerableList(50, 2) 會無條件跑完 1 到 50
OutputForeachList 會跑 2,4,6...50
但是 OutputFor2(50, 2) 最多只會跑 50 次
雖然提升了維護性
為了效能
只好再對程式碼再進行一次優化

  1. private static void OutputForeachList2(int max, int divisible)
  2. {
  3. foreach (int item in EnumerableYield(max, divisible))
  4. {
  5. System.Console.WriteLine($"{item}");
  6. }
  7. System.Console.WriteLine();
  8. }
  9. private static IEnumerable EnumerableYield(int max, int divisible)
  10. {
  11. for (int currentNum = 1; currentNum <= max; currentNum++)
  12. {
  13. System.Console.WriteLine($"讀取:{currentNum} ");
  14. if (currentNum % divisible != 0)
  15. {
  16. continue;
  17. }
  18. yield return currentNum;
  19. }
  20. }



























程式碼在遇到 yield return
會暫時停止 EnumerableYield 的執行
並把 currentNum 回傳到 OutputForeachList2 函式
等 OutputForeachList2 結束一個迴圈
會再次回到 EnumerableYield 函式
繼續之前的執行 EnumerableYield 函式

總結:
一般程式碼會執行到函式結束才回到上一層的函式呼叫層
yield return 能打破這個限制
暫時交出執行權
並回傳資料到上一層















Visual Studio 2017/2019 推薦的擴充功能與更新

參考文章: 覺得 Google 的 Blogger 不太順手?透過 HTML 的 iframe 移花接木 HackMD