C#函數語言程式設計之可選值
在我們的實際開發中已經會遇到可空型別,而在C#中自從2.0之後就提供了可空型別(Nullable<T>),普通的值型別是不可以賦值為NULL,但是在型別的後面加上問號就變成了可空型別,這樣就可以賦值為NULL了。當然這樣的方式也可以用於函數語言程式設計中,但函數語言程式設計有自己的獨特方式來解決這種問題,今天我們將圍繞這個問題,雖然篇幅比較少,但也請讀者可以閱讀完。
我們當然不能改變語言的設計,所以我們只能使用現有的來實現可選值。這裡我們利用類來實現,下面是Option<T>初期的程式碼:
1 public sealed classOption<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; 13public 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>進行對比了,到這裡我們就完成了我們自己的可選值的實現,在具體的專案中是使用可空型別還是可選值完全可以根據你個人的喜好,筆者建議使用本節介紹的可選值。