2016-11-24 35 views
7

我已經創建了一個自定義結構來表示一個金額。它基本上是圍繞decimal的包裝。它有一個隱式轉換運算符,將其轉換回decimal爲什麼具有隱式轉換運算符的自定義結構上的Assert.AreEqual失敗?

在我的單元測試中,我聲稱金額等於原始的十進制值,但測試失敗。

[TestMethod] 
public void AmountAndDecimal_AreEqual() 
{ 
    Amount amount = 1.5M; 

    Assert.AreEqual(1.5M, amount); 
} 

當我使用雖然一個int(對此我沒有創建一個轉換操作符),測試成功。

[TestMethod] 
public void AmountAndInt_AreEqual() 
{ 
    Amount amount = 1; 

    Assert.AreEqual(1, amount); 
} 

當我將鼠標懸停在AreEqual,這表明第一個解析爲

public static void AreEqual(object expected, object actual); 

,第二個導致

public static void AreEqual<T>(T expected, T actual); 

它看起來像int價值1是隱式投到Amount,而decimal1.5M不是。

我不明白爲什麼會發生這種情況。我會期望恰恰相反。第一次單元測試應該能夠將decimal轉換爲Amount

當我向int添加一個隱式轉換(這沒有意義)時,第二個單元測試也失敗。因此,添加一個隱式投射運算符會打破單元測試。

我有兩個問題:

  1. ,這是什麼行爲,如何解釋呢?
  2. 如何修復Amount結構,以便兩個測試都能成功?

(我知道我可以改變測試做一個明確的轉換,但如果我沒有絕對要,我不會)

我金額結構(只是最低限度的實施顯示問題)

public struct Amount 
{ 
    private readonly decimal _value; 

    private Amount(decimal value) 
    { 
     _value = value; 
    } 

    public static implicit operator Amount(decimal value) 
    { 
     return new Amount(value); 
    } 

    public static implicit operator decimal(Amount amount) 
    { 
     return amount._value; 
    } 
} 
+1

您的兩個操作符都是「隱式」的。那麼,它應該是'AreEqual '還是'AreEqual '? – PetSerAl

+1

@PetSerAl是對的。如果你使用'AreEqual '或'AreEqual '它會通過。 – Nkosi

回答

6

不好的事情發生時,你可以在兩個方向LY轉換implicit,這就是一個例子。

因爲隱式轉換的編譯器能夠挑Assert.AreEqual<decimal>(1.5M, amount);Assert.AreEqual<Amount>(1.5M, amount);同等價值。*

因爲他們是平等的,沒有超載會被推斷採摘。

由於沒有超載可以按推理進行選擇,因此既不會將其放入選擇最佳匹配的列表中,也只能使用(object, object)表單。所以這是挑選的人。

隨着Assert.AreEqual(1, amount)則由於存在隱式轉換從intAmount(通過隱含的內部 - >十進制)但是從Amountint的隱式轉換的編譯器認爲「很明顯,他們的意思是Assert.AreEqual<Amount>()這裏」†,因此它是採摘。

你可以明確地挑Assert.AreEqual<Amount>()Assert.AreEqual<decimal>()過載,但你可能會更好過讓你轉換一個「縮小」從有到是explicit,如果在所有可能的,因爲你的結構的這一特徵會傷害你。 (華友單元測試發現瑕疵)。


*另一個有效超載的選擇是挑Assert.AreEqual<object>,但它永遠不會被推斷,因爲選擇的是:

  1. 無論是拒絕超載被認爲是更好的。
  2. 無論如何,它總是被非通用形式打敗,它需要object

因此,只能通過在代碼中包含<object>來調用它。

†編譯器將所有對它的描述看作是明顯的或完全不可理解的。也有這樣的人。

+0

感謝您的精心解答。在我的情況下,「隱式」退回到「decimal」並不是真的需要,所以我將它改爲「explicit」。我想知道,我怎麼能自己發現爲什麼AreEqual(object,object)被調用。沒有編譯器錯誤,但它只是沒有達到我的預期。任何提示呢?你能以某種方式看到編譯器的某種決策樹嗎? – comecme

+1

@comecme如果我被重載選擇困惑我首先寫一些匹配簽名的方法(所以在這裏我可能會創建一個'AssertAreEqual(object x,object y)'和'AssertAreEqual (T x,T然後確認我得到了相同的行爲(取得對象形式),然後刪除選中的行爲看看會發生什麼:'CS0411方法'AssertAreEqual (T,T)'的類型參數不能從「想知道爲什麼他們不能被推斷是一個很大的線索,現在當我明確地指定時,我有這個問題...... –

+0

......我應該明確地說''還是顯式地說''?哈哈!我不知道,那麼編譯器怎麼樣?問題ID'd。然而,最大的問題是,我是一對新的眼睛看着你的代碼,而沒有在它的計劃記住w這使得最後的心智跳躍更容易做出。當它是自己的代碼時,人們有一個計劃,很難看出爲什麼制定該計劃的人對編譯器而言顯而易見。唯一要做的就是去喝杯咖啡和散步,然後回頭看看。 –

相關問題