Refactor to LINQ => ForEach
LINQ 是 C# 3.0 實現 FP 重要里程碑,提供大量的 Operator,讓我們以 Pure Function 將 data 以 Dataflow 與 Pipeline 方式實現。本系列將先以 Imperative 實作,然後再重構成 FP,最後再重構成 LINQ Operator。
首先從最基本的 ForEach Operator 談起。
Version
macOS High Sierra 10.13.6
.NET Core 2.1
C# 7.2
Rider 2018.2.3
Imperative
1 | using System; |
使用 Enumerable.Range() 產生 1, 2, 3,最後使用 foreach() 印出每個值。
foreach statement 是 Imperative 慣用手法。
Refactor to HOF
實務上這種 foreach 天天都要用到,但用 foreach 這種 statement 寫法,重複使用能力為 0,就每天都要不斷的寫 foreach。
若我們能將 foreach 抽成 ForEach() Higher Order Function,我們就能不斷 reuse ForEach(),只要將不同的商業邏輯以 function 傳進 ForEach() 即可。
1 | using System; |
17 行
1 | private static void MyForEach(IEnumerable<int> data, Action<int> action) |
自己以 MyForEach() 實作出 foreach statement 的 Higher Order Function 版本。
第一個參數為 data,第二個參數為 function。
將 foreach statement 包進 MyForEach() function,如此 foreach statement 就能被重複使用。
13 行
1 | MyForEach(data, Console.WriteLine); |
原來的 foreach statement 重構成 MyForEach() Higher Order Function,只要將 data 與 Console.WriteLine 傳入即可。
Refactor to Generics
1 | using System; |
16 行
1 | private static void MyForEach<T>(IEnumerable<T> data, Action<T> action) |
事實上 MyForEach() 不只適用於 int,而且可適用於任何型別,因此重構成 <T>。
Refactor to Extension Method
1 | using System; |
17 行
1 | private static void MyForEach<T>(this IEnumerable<T> data, Action<T> action) |
MyForEach() 需要兩個參數,使用上不是那麼方便,而且也無法 Pipeline 般使用,因此將第一個參數加上 this,成為 Extension Method,
11 行
1 | Enumerable |
如此 MyForEach() 就與 Range() 串起來了,而且也減少了一個參數。
Refactor to LINQ
1 | using System; |
事實上 LINQ 早已提供 ForEach(),不必我們自己實作。
因為
ForEach()為List所提供的 operator,因此必須先將IEnumerable由ToList()轉成List。
Refactor to Using Static
1 | using static System.Linq.Enumerable; |
使用 using static 之後,則 Range() 與 WriteLine() 可進一步縮短,更符合 FP 風格。
Conclusion
- 就算自己重構,也會重構出
ForEach()Higher Order Function,只是因為太常使用,LINQ 已經內建ForEach() - 善用
using static,可讓 class 的 static method 更像 function
Sample Code
完整的範例可以在我的 GitHub 上找到