为什么在具有隐式转换操作员的自定义结构上宣称areequal失败



我已经创建了一个自定义结构来表示金额。它基本上是decimal周围的包装器。它具有一个隐式转换操作员,将其放回decimal

在我的单位测试中,我断言该金额等于原始小数值,但测试失败。

[TestMethod]
public void AmountAndDecimal_AreEqual()
{
    Amount amount = 1.5M;
    Assert.AreEqual(1.5M, amount);
}

当我使用int时(为此我没有创建转换操作员),test do 成功。

[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);

看起来int1隐式施放到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;
    }
}

当您可以在两个方向上转换 implicit ly时,就会发生坏事,这是一个例子。

由于具有隐式转换,因此编译器能够以相等值选择Assert.AreEqual<decimal>(1.5M, amount);Assert.AreEqual<Amount>(1.5M, amount);。*

由于它们相等,因此都不会通过推理来挑选超载。

由于没有推断要选择的过载,因此都不会将其纳入列表,以选择最佳匹配,只有(object, object)表单可用。所以这是挑选的。

使用Assert.AreEqual(1, amount),然后是因为从intAmount有隐式转换(通过Intionit Int-> Decimal),但是从Amountint CC_21没有任何隐含的转换,这显然是在此处,显然它们是在这里的Assert.AreEqual<Amount>(),因此选择了它。。

您可以明确选择使用Assert.AreEqual<Amount>()Assert.AreEqual<decimal>()的过载,但是如果可能的话,您最好将其中一种转换为"缩小",因为您的结构的此功能会再次伤害您。(用于单位测试发现缺陷的呼吸)。


*另一个有效的超载选择是选择Assert.AreEqual<object>,但从未通过推理选择,因为:

  1. 两个被拒绝的超载都被认为更好。
  2. 无论如何,它总是被采用object的非生成形式击败。

因此,只能通过在代码中加入<object>来调用。

†编译器将其视为明显的意义或完全难以理解的一切。也有这样的人。

最新更新