2014-09-02 131 views
4

考慮這個結構:爲什麼Assert.AreEqual(x,y)失敗,但Assert.AreEqual(y,x)不?

public struct MyNumber 
{ 
    private readonly int _value; 

    public MyNumber(int myNumber) 
    { 
     _value = myNumber; 
    } 

    public int Value 
    { 
     get { return _value; } 
    } 

    public override bool Equals(object obj) 
    { 
     if (obj is MyNumber) 
      return this == (MyNumber) obj; 

     if (obj is int) 
      return Value == (int)obj; 

     return false; 
    } 

    public override string ToString() 
    { 
     return Value.ToString(); 
    } 

    public static implicit operator int(MyNumber myNumber) 
    { 
     return myNumber.Value; 
    } 

    public static implicit operator MyNumber(int myNumber) 
    { 
     return new MyNumber(myNumber); 
    } 
} 

當我這樣做在一個單元測試:

Assert.AreEqual(new MyNumber(123), 123); 

它是綠色的。

但此測試失敗:

Assert.AreEqual(123, new MyNumber(123)); 

爲什麼會這樣呢?我猜這是因爲int類決定了相等性,而在第一種情況下,我的類決定了它。但我的班級可以隱式轉換爲int,所以這不應該有幫助嗎?

如何使Assert.AreEqual工作在兩種方式?順便說一句,我正在使用MSTest。

更新

實施IEquatable<int>IComparable<int>沒有幫助。

+0

不知道它是否值得實施'IEquatable'? – Charleh 2014-09-02 12:58:57

+0

@Charleh:如果你打算將你的值存儲在一個集合中,那麼值得實現'IEquatable '。它會加速像「Contains」,「IndexOf」等操作。 – 2014-09-02 13:11:34

+0

不是'IEquatable' @Charleh,'IComparable '。這將確保該類與「更大」和「更少」斷言一起工作。 – 2014-09-02 13:12:30

回答

6

的第一個斷言將調用MyNumber.Equals和你的方式,其中如果參數來比較是Int32就會成功實施,它等於MyNumber價值。

第二個斷言將調用Int32.Equals,它將失敗,因爲要比較的參數是MyNumber,其中Int32不知道或不明白。

由於您斷言Int32應該等於您的價值,您無法使第二個單元測試成功。它不可能是因爲它不同。 Int32.Equals中的代碼決定第二個值是否相等。你已經實現了一些轉換操作符,你可以單元測試,以便這些斷言應該工作:

Assert.AreEqual(123, (int) new MyNumber(123)); 
Assert.AreEqual((MyNumber) 123, new MyNumber(123)); 

即使強制類型轉換實現implicit他們不會自動被Assert.AreEquals和調用,因爲這種方法期望Object類型的兩個參數,你將不得不像上面所做的那樣明確地調用它們。

由於您的MyNumber.Equals中的特殊處理,您現在有一種類型與等於等於不交換, MyNumber(123) equals Int32(123)爲真,但Int32(123) equals MyNumber(123)爲假。您應該避免這種情況,所以我建議您刪除MyNumber.Equals中的整數的特殊情況處理,而是依賴隱式強制轉換,這些強制轉換在大多數情況下都適用於您。當他們沒有時,你將不得不做出明確的演員。

+0

但是,如果MyNumber會是類,實現接口IEquatable我認爲,這兩個斷言將是好的。不是嗎? – Davecz 2014-09-02 13:04:08

+0

@Davecz:我相信''Assert.AreEqual'基本上映射到靜態的'Object.Equals',它在做了一些簡單的檢查之後將調用'objA.Equals(objB)'。我不相信在這個過程中使用通用接口「IEquatable 」。 – 2014-09-02 13:09:28

+0

如下所述,實現'IComparable'將解決OP的問題。 – 2014-09-02 13:10:19

0

MyNumber結構實現IComparable<int>如下:

public int CompareTo(int other) 
    { 
     return other.CompareTo(Value); 
    } 

這將確保所有斷言按預期方式工作,如:

Assert.AreEqual(new MyNumber(123), 123); 
    Assert.AreEqual(123, new MyNumber(123)); 
    Assert.Greater(124, new MyNumber(123)); 
    Assert.Less(124, new MyNumber(125)); 
+0

該結構的工作原理與斷言比較兩個MyNumber – 2014-09-02 13:18:51

+0

這是行不通的。正如馬丁在評論中提到他的回答時,IComporable是爲了排序和排序。它不用Assert.AreEqual調用。 – Peter 2014-09-02 13:25:54

+0

這可能在你的單元測試框架中起作用,這顯然是由'Assert.Greater'和'Assert.Less'的出現不同於問題中使用的MSTest單元測試框架,但如果使用MSTest則失敗。 – 2014-09-02 13:31:04

2

Object.Equals引用:

的以下語句對於Equals(Object)的所有實現都必須如此) 方法。在列表中,x,y和z表示不是null的對象引用。

  • ...

  • x.Equals(y)的返回相同的值作爲y.Equals(X)。

Equals實施打破了這種硬性要求。 ((object)x).Equals(123)必須返回與((object)123).Equals(x)相同的值,如果x不是null,則無論它具有何種類型。

這裏有很多代碼,正確地說,假定兩個對象中的哪一個被要求執行比較並不重要。設計您的代碼,以便該假設不會失效。

實際上,這意味着你必須設計類以這樣一種方式,它比較等於任何整數類型,無論你可能有多大,否則寧可。

相關問題