2012-02-08 44 views
4

如果我的一類做的operator ==超載,我一定要比較的字段之前進行一些檢查:平等運營商結構重載和類

  • 如果兩個參數都爲空,或者兩個參數是相同的實例,則返回true

    例子:if (System.Object.ReferenceEquals(arg1, arg2)) return true;

  • 如果爲空,但不同時,則返回false

    例子:if (((object)arg1 == null) || ((object)arg2 == null)) return false;

事實上,如果我有一個結構,我想做的operator ==超載,這些檢查是沒有必要的,而他們是沒用的,有以下原因:一個結構是值類型爲,因此它不能爲空,例如DateTime date = null;無效,因爲DateTime(即一個結構體)不是一個引用類型,所以無法比較兩個DateTime,其中之一設置爲null

我創建了一個簡單的結構Point2Doperator ==的話,我比較Point2D的實例與null

Point2D point = new Point2D(0,0); 
Console.WriteLine((point == null)); 
  1. 顯然operator ==它不叫,但比較返回False。哪種方法被稱爲?

  2. documentation指出不推薦在非不可變類型中重載此運算符。爲什麼?

+1

您應該嘗試一次只詢問一個問題。如果你有兩個問題,請分別提問。 – svick 2012-02-08 01:40:25

+0

@svick:對不起。對於接下來的問題,我會避免這一點。 – enzom83 2012-02-08 01:50:36

回答

5

因爲看起來編譯器優化了這一點。我想這樣的代碼:

System.Drawing.Point point = new System.Drawing.Point(0,0); 
Console.WriteLine((point == null)); 

它生成以下IL:

IL_0000: ldloca.s 00 
IL_0002: ldc.i4.0  
IL_0003: ldc.i4.0  
IL_0004: call  System.Drawing.Point..ctor 
IL_0009: ldc.i4.0  
IL_000A: call  System.Console.WriteLine 

這最終歸結爲「創建點,然後寫假命令行」

這也解釋了爲什麼它不打電話給您的運營商。一個struct永遠不能爲null,並且在編譯器可以保證你總是會因此而失敗的情況下,它根本不會打擾發出代碼來調用操作符。

同樣的事情發生與此代碼,即使字符串是一類和重載==操作符:

System.Drawing.Point point = new System.Drawing.Point(0,0); 
Console.WriteLine("foo" == null); 

至於不變性... C#中的==操作符通常被解釋爲是指「參考平等「,例如這兩個變量指向類的同一個實例。如果你超載了它,那麼你一般的意思是說這個類的兩個實例,而不是同一個實例,當它們的數據相同時,它們應該表現得好像它們是相同的實例。經典的例子是Strings。即使由GiveMeAnA返回的實際字符串引用可能與文字"A"所代表的字符串引用不同,也可能不會是。

如果您對==運算符重載不是不可變的類,那麼==之後的類的變異可能會導致大量細微的錯誤。

+0

我如何看IL? – enzom83 2012-02-08 01:43:42

+3

我使用LINQPad(http://linqpad.net)或Reflector(http://reflector.net)。前者向您顯示任意代碼段的IL,後者將程序集反編譯爲IL,並可以從該IL重新生成等效的C#。還有一個名爲ILDASM的內置工具(http://msdn.microsoft.com/en-us/library/f7dy01k1(v=vs.80).aspx)瞭解IL是一件棘手的事情 - 請參閱http:// codebetter.com/raymondlewallen/2005/02/07/getting-started-understanding-msil-assembly-language/ – 2012-02-08 01:46:59

12

克里斯謝恩的回答是正確但沒有解釋爲什麼這是合法的

當你重寫等式操作符,兩個操作數是非空值類型和返回類型是布爾,則免費我們會給您一個解除運營商。也就是說,如果你有

public static bool operator ==(S s1, S s2) { ... } 

然後無需任何額外費用

public static bool operator ==(S? s1, S? s2) { ... } 

這是那是被稱爲運營商。當然,編譯器知道結果總是錯誤的,因爲其中一個操作數是空的,而另一個操作數是從不的。

曾經有一個警告,說明你的代碼總是返回false,但是我們意外地禁用了它的幾個版本,並且從未實際上將它重新打開。我明天在Roslyn編譯器中編寫這個代碼,所以我會看看我能做些什麼來恢復它的形狀。

+3

即使在編寫.NET代碼近十年之後,閱讀您的答案始終具有教育意義。感謝:-) – 2012-02-08 02:05:28

+0

埃裏克,你在說我們有兩個類型爲'Point'和'null'的操作數;它們之間沒有隱含的轉換,但是編譯器調用另一個類型的操作符,兩個操作數都可以被隱式轉換。這似乎與條件運算符的處理不一致,你不能說'int? x = someBool? 7:null;'我從C#的第2版開始;像'1 == null'這樣的表達式在早期版本中無法編譯? – phoog 2012-02-08 18:18:20

+0

@phoog:這不是很合適的比喻。 *操作符重載分辨率*的正確類比是*方法重載分辨率*。如果你有一個靜態方法'int? Operators.Add(int?,int?)'並且調用Operator.Add(1,null)',那麼你會期望選擇該方法,即使int不能轉換爲null,null也不能轉換爲int , 對?將所有內建和解除運算符看作是類運算符上的靜態方法;運算符重載分辨率簡單地對這些方法進行重載分辨 – 2012-02-08 20:49:03