C# 重載條件邏輯運算符(&& 和 ||)
- 2020 年 2 月 10 日
- 筆記
C# 重載條件邏輯運算符(&& 和 ||)
發佈於 2018-10-16 21:04 更新於 2018-12-14 01:54
在微軟的官方文檔中,規定 &&
和 ||
運算符不可被重載,但允許通過重載 &
、|
、true
和 false
實現間接重載。
本文將介紹重載方法和原理。感謝 Opportunity 的指導。
條件邏輯運算符是可以重載的
在微軟的官方文檔 true Operator (C# Reference) – Microsoft Docs 中,解釋了 &&
和 ||
這兩個條件邏輯運算符的重載方法:
A type cannot directly overload the Caseal logical operators (
&&
and||
), but an equivalent effect can be achieved by overloading the regular logical operators and operatorstrue
andfalse
. 類型不能直接重載條件邏輯運算符(&&
和||
),但通過重載常規邏輯運算符&
、|
及運算符true
和false
可以達到同樣的效果。
也就是說,在官方的概念中,&&
和 ||
是允許被重載的,只是不能直接重載。
原因在於,&&
和 ||
是短路運算符(Circuit Operator),具有短路求值特性。具體來說,A && B
運算中,如果 A
是 false
,那麼 B
的值便不會計算;同樣的,A || B
中,如果 A
是 true
,那麼 B
的值也不會計算。
於是,如果允許自定義 &&
和 ||
運算符,那麼必然會導致這個運算符重載的方法有兩個參數傳入,於是這兩個參數一定會被計算值;這樣就無法實現短路求值了。於是對於 &&
和 ||
的重載採用的方案是重載 &
和 |
運算符,然後重載 true
和 false
運算符來指定短路求值。
試錯實驗
我們寫一個類型進行實驗:
using System; namespace Walterlv.Demo { public class Case { public static bool operator &(Case a, Case b) { throw new NotImplementedException(); } } }
直接使用 &
是沒有問題的,但如果使用 &&
就會提示錯誤。
var a = new Case(); var b = new Case(); if (a && b) { }
Error CS0217: In order to be applicable as a short circuit operator a user-defined logical operator (『Case.operator &(Case, Case)』) must have the same return type and parameter types Error CS0217: 為了可以像短路運算符一樣應用,用戶定義的邏輯運算符(「Case.operator &(Case, Case)」)的返回類型和參數類型必須相同
也就是說,本身重載 &
運算符的時候允許返回不同的類型;但如果希望 &&
運算符在此重載下也生效,就必須確保 &
的返回類型與參數中的類型相同。
public static Case operator &(Case a, Case b) { throw new NotImplementedException(); }
var a = new Case(); var b = new Case(); var c = a && b;
改為相同的類型後,還會繼續提示需要定義 true
和 false
運算符。
Error CS0218: In order for 『Case.operator &(Case, Case)』 to be applicable as a short circuit operator, its declaring type 『Case』 must define operator true and operator false
重載 && 和 ||
以下代碼中,true
表示字符串中包含大寫字母,false
表示字符串中不包含大寫字母(null
和沒有大小寫的區域也屬於不包含大寫字母)。&
運算符僅留下兩者共有的字符;|
則取所有字符。
public class Case { private string _value; public Case(string value) { _value = value; } public static Case operator &(Case a, Case b) { if (a is null || b is null) return null; if (a._value is null || b._value is null) return new Case(null); return new Case(new string(b._value.Except(a._value.Except(b._value)).ToArray())); } public static Case operator |(Case a, Case b) => new Case(a._value + b._value); public static bool operator true(Case a) => a?._value != null && !a._value.ToLower(CultureInfo.CurrentCulture).Equals(a._value); public static bool operator false(Case a) => a?._value == null || a._value.ToLower(CultureInfo.CurrentCulture).Equals(a._value); public override string ToString() => _value; }
測試重載了條件邏輯運算符的類型
我們測試以上代碼所用的代碼如下:
var a = new Case("A"); var b = new Case("b"); Console.WriteLine(a); Console.WriteLine(b); Console.WriteLine(a ? "a 是 truthy" : "a 是 falsy"); Console.WriteLine(b ? "b 是 truthy" : "b 是 falsy"); Console.WriteLine(a & b); Console.WriteLine(a | b); Console.WriteLine(a && b); Console.WriteLine(a || b);
以上各個 Console.WriteLine
的輸出為:
[1] A [2] b [3] a 是 truthy [4] b 是 falsy [5] [6] Ab [7] [8] A
注意,空行其實指的是輸出 null
。
truthy 和 falsy
剛剛的測試代碼中,我們使用了 truthy 和 falsy 概念,而這是邏輯判斷概念:
- 如果在邏輯判斷中,對象與
true
等價,但其數值上並非true
(不等於true
),那麼稱此對象為 truthy; - 如果在邏輯判斷中,對象與
false
等價,但其數值上並非false
(不等於false
),那麼稱此對象為 falsy。
對以上測試輸出的解釋
第 5 行由於 a
和 b
沒有共有字符,所以得到 null
。
第 7 行的執行過程是這樣的:
- 對
a
求值,即a
本身; - 對
a
進行 truthy / falsy 邏輯判斷,得到 truthy; - 由於
a
為 truthy,對於&&
運算符而言,可以對 b 求值,於是對b
求值得到b
本身; - 對
a
和b
進行&
運算,得到 `,也就是
null`。
第 8 行的執行過程是這樣的:
- 對
a
求值,即a
本身; - 對
a
進行 truthy / falsy 邏輯判斷,得到 truthy; - 由於
a
為 truthy,對於||
運算符而言,已無需對b
求值,最終得到的結果為a
,也就是A
。
參考資料
- C# 中那些可以被重載的操作符 – walterlv – 請閱讀文章末尾的評論
- true Operator (C# Reference) – Microsoft Docs
- JavaScript: Truthy? Falsy? – 格物致知
本作品採用 知識共享署名-非商業性使用-相同方式共享 4.0 國際許可協議 進行許可。歡迎轉載、使用、重新發佈,但務必保留文章署名 呂毅 (包含鏈接: https://blog.walterlv.com ),不得用於商業目的,基於本文修改後的作品務必以相同的許可發佈。如有任何疑問,請 與我聯繫 ([email protected]) 。