C# 空合併操作符(??)不可重載?其實有黑科技可以間接重載!
- 2020 年 2 月 10 日
- 筆記
C# 空合併操作符(??)不可重載?其實有黑科技可以間接重載!
發佈於 2018-09-15 14:59 更新於 2018-12-14 01:54
?? 操作符叫做 null-coalescing operator,即 null 合併運算符。如果此運算符的左操作數不為 null,則此運算符將返回左操作數;否則返回右操作數。
在微軟的官方 C# 文檔中,此操作符被定義為不可重載。不過我們有方法可以間接實現這樣的重載。
運算符重載
你可以閱讀 C# 中那些可以被重載的操作符,以及使用它們的那些喪心病狂的語法糖 了解 C# 中提供的所有可以重載的操作符。在此文中,?? 被明確定義為不可重載。
你更可以在微軟官方文檔中找到這樣的說法:
=, ., ?:, ??, ->, =>, f(x), as, checked, unchecked, default, delegate, is, new, sizeof, typeof These operators cannot be overloaded. 這些運算符無法進行重載。
編寫 NullableString 的 ?? 重載
我們先寫一個空殼子。連構造函數都是 private 的,這個類當然幾乎不可用啦。
特別注意,我們的 Walterlv.NullableString 用的是 struct 類型,這樣能與 Nullable<T> 的用法上接近。也就是說,我們可以確保其值實際上永不為 null。
namespace Walterlv { public struct NullableString { private readonly string _value; private NullableString(string value) { _value = value; } } }
現在我們挑戰一下官方說好了不能重載的 ?? 重載(作死):

▲ 試著重載 ??
很明顯,既不是可重載的一員運算符也不是可重載的二元運算符。
現在我們試試隱式轉換:
public static implicit operator NullableString(string value) { return new NullableString(value); } public static implicit operator string(NullableString nullableString) { return nullableString._value; }
然而這樣的寫法實際上並無實際用途。
但是,我們可以在 NullableString 後面加上 ?:
public static implicit operator NullableString?(string value) { return string.IsNullOrEmpty(value) ? (NullableString?) null : new NullableString(value); } public static implicit operator string(NullableString? nullableString) { return nullableString?.ToString() ?? string.Empty; }
也就是說,C# 竟然允許隱式轉換的時候,參數和返回值都不是此類型。當然,實際上這隻對 Nullable<T> 生效,如果你試圖寫別的類型,是不可以的。
為了方便,我們重寫一下 ToString(),部分場景下可以代替隱式轉換,少寫一些程式碼。
public override string ToString() { return string.IsNullOrEmpty(_value) ? string.Empty : _value; }
於是,我們的 NullableString 類型的完整程式碼如下:
namespace Walterlv { public readonly struct NullableString { private readonly string _value; private NullableString(string value) { _value = value; } public static implicit operator NullableString?(string value) { return string.IsNullOrEmpty(value) ? (NullableString?) null : new NullableString(value); } public static implicit operator string(NullableString? nullableString) { return nullableString?.ToString() ?? string.Empty; } public override string ToString() { return string.IsNullOrEmpty(_value) ? string.Empty : _value; } } }
注釋就你自己添加吧。
一些注意事項
這裡有一些好玩的事情需要分享。比如我們寫出如下程式碼:
NullableString? value = ""; var value0 = value?.ToString(); var value1 = value.ToString();
你覺得 value0 和 value1 分別會得到什麼呢?
呃……
value0 得到 null,而 value1 得到 ""。
另外,如果你將一開始的初始值設為 null,那又可以得到什麼結果呢?
NullableString? value = null; var value0 = value?.ToString(); var value1 = value.ToString();
一樣的,value0 得到 null,而 value1 得到 ""。
另外,你可以從 null 強轉出你需要的類:
var value = (NullableString?) null;
本文會經常更新,請閱讀原文: https://blog.walterlv.com/post/overload-null-coalescing-operator-in-csharp.html ,以避免陳舊錯誤知識的誤導,同時有更好的閱讀體驗。
本作品採用 知識共享署名-非商業性使用-相同方式共享 4.0 國際許可協議 進行許可。歡迎轉載、使用、重新發布,但務必保留文章署名 呂毅 (包含鏈接: https://blog.walterlv.com ),不得用於商業目的,基於本文修改後的作品務必以相同的許可發布。如有任何疑問,請 與我聯繫 ([email protected]) 。

