C#函數式編程之可選值
在我們的實際開發中已經會遇到可空類型,而在C#中自從2.0之後就提供了可空類型(Nullable<T>),普通的值類型是不可以賦值為NULL,但是在類型的後面加上問號就變成了可空類型,這樣就可以賦值為NULL了。當然這樣的方式也可以用於函數式編程中,但函數式編程有自己的獨特方式來解決這種問題,今天我們將圍繞這個問題,雖然篇幅比較少,但也請讀者可以閱讀完。
我們當然不能改變語言的設計,所以我們只能使用現有的來實現可選值。這裡我們利用類來實現,下面是Option<T>初期的代碼:
1 public sealed class Option<T>
2 {
3 private readonly T value;
4 public T Value
5 {
6 get
7 {
8 return value;
9 }
10 }
11
12 private readonly bool hasValue;
13 public bool HasValue { get { return hasValue; } }
14 public bool IsSome { get { return hasValue; } }
15 public bool IsNone { get { return !hasValue; } }
16
17 public Option(T value)
18 {
19 this.value = value;
20 this.hasValue = true;
21 }
22
23 private Option() { }
24 public static readonly Option<T> None = new Option<T>();
25 }
這樣我們就可以利用這個類來實現和可空類型一樣的效果,比如下面這段代碼:
1 var p1 = new Option<int>(10);
2 var p2 = Option<int>.None;
3 if (p1.HasValue) // 等價於 x != null
4 {
5
6 }
我們可以看到在實例化Option的時候還需要傳遞類型參數,這裡我們可以通過一個技巧來避免輸入類型參數,而依賴於類型推斷,這裡我們寫個輔助類:
1 public sealed class Option
2 {
3 public static Option<T> Some<T>(T value)
4 {
5 return new Option<T>(value);
6 }
7 }
其實我們只是利用了一個泛型方法來創建這個實例就可以了,下面我們看看下面的示例:
1 static void Main(string[] args)
2 {
3 var p1 = Option.Some(10);
4 var p2 = Option.Some(10);
5 if (p1 == p2)
6 {
7 Console.WriteLine("Y");
8 }
9 Console.ReadKey();
10 }
這裡我們看到Option.Some節省了一些功夫,但是讀者如果運行上面這個例子會發現,“Y”並不會輸出到控制台中,但是我們可以看到10本來就應該等於10。因為這裡我們對比的是Option<T>類型,並且p1和p2是兩個不同的Option<T>所以我們還需要加以改善,下面我們重載操作符(在Option<T>類中繼續追加):
1 public static bool operator ==(Option<T> a, Option<T> b)
2 {
3 return a.HasValue == b.HasValue && EqualityComparer<T>.Default.Equals(a.Value, b.Value);
4 }
5 public static bool operator !=(Option<T> a, Option<T> b)
6 {
7 return !(a == b);
8 }
9
10 public override int GetHashCode()
11 {
12 int hashCode = hasValue.GetHashCode();
13 if (hasValue)
14 hashCode ^= value.GetHashCode();
15 return hashCode;
16 }
這裡我們通過重載“==”判斷其中的Value,這樣就可以解決上面的問題了。但是我們還有一個問題沒有解決,就是在使用None的時候還需要傳遞類型參數,這裡我們依然需要使用一個技巧來避免(在Option類中繼續追加):
public static readonly Option None = new Option();
僅僅這樣還不足夠,因為Option無法轉換成Option<T>類型所以我們還需要對Option<T>增加 功能,能夠隱式的將Option轉換成對應的Option<T>類型,下面是針對的代碼(在Option<T>中增加):
1 public static implicit operator Option<T>(Option option)
2 {
3 return Option<T>.None;
4 }
這樣我們就可以直接將Option與Option<T>進行對比了,到這裡我們就完成了我們自己的可選值的實現,在具體的項目中是使用可空類型還是可選值完全可以根據你個人的喜好,筆者建議使用本節介紹的可選值。