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 上找到