为什么带有隐式转换运算符的自定义结构上的Assert.AreEqual失败? [英] Why does Assert.AreEqual on custom struct with implicit conversion operator fail?

查看:85
本文介绍了为什么带有隐式转换运算符的自定义结构上的Assert.AreEqual失败?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我创建了一个自定义结构来表示金额.它基本上是decimal的包装.它具有一个隐式转换运算符,可将其强制转换回decimal.

I've created a custom struct to represent an amount. It is basically a wrapper around decimal. It has an implicit conversion operator to cast it back to decimal.

在单元测试中,我断言Amount等于原始十进制值,但是测试失败.

In my unit test, I assert that the Amount equals the original decimal value, but the test fails.

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

    Assert.AreEqual(1.5M, amount);
}

尽管我使用int(但我没有为其创建转换运算符),但测试确实成功.

When I use an int though (for which I did not create a conversion operator), the test does succeed.

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

    Assert.AreEqual(1, amount);
}

当我将鼠标悬停在AreEqual上时,它表明第一个解析为

When I hover the AreEqual, it shows that the first one resolves to

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

第二个指向

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

看起来int1隐式转换为Amount,而decimal1.5M不是.

It looks like the int value 1 is implicitly cast to an Amount, while the decimal value 1.5M is not.

我不明白为什么会这样.我本来是相反的期望.第一个单元测试应该能够将decimal强制转换为Amount.

I don't understand why this happens. I would've expected just the opposite. The first unit test should be able to cast the decimal to an Amount.

当我向int添加隐式强制转换(这没有意义)时,第二个单元测试也将失败.因此,添加隐式强制转换运算符会破坏单元测试.

When I add an implicit cast to int (which wouldn't make sense), the second unit test also fails. So adding an implicit cast operator breaks the unit test.

我有两个问题:

  1. 对此行为的解释是什么?
  2. 如何修复Amount结构,以便两个测试都能成功?
  1. What is the explanation for this behavior?
  2. How can I fix the Amount struct so both tests will succeed?

(我知道我可以更改测试以进行显式转换,但是如果我不是绝对必须这样做,则不会)

我的金额结构(只是一个最小的实现来显示问题)

My Amount struct (just a minimal implementation to show the problem)

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时,会发生错误的事情,这是一个示例.

Bad things happen when you can convert implicitly in both directions, and this is one example.

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

Because of the implicit conversions the compiler is able to pick Assert.AreEqual<decimal>(1.5M, amount); and Assert.AreEqual<Amount>(1.5M, amount); with equal value.*

由于它们相等,因此不会通过推理选择任何重载.

Since they're equal, neither overload will be picked by inference.

由于没有重载可通过推理选择,因此也没有将其放入用于选择最佳匹配的列表中,并且仅(object, object)形式可用.因此,它就是被选中的.

Since there's no overload to pick by inference, neither make it into the list for picking the best-match and only the (object, object) form is available. So it's the one picked.

对于Assert.AreEqual(1, amount),则由于存在从intAmount的隐式转换(通过隐式int->小数),但是没有从Amountint的隐式转换,因此编译器认为显然,它们的意思是Assert.AreEqual<Amount>()在这里"†,因此被选中.

With Assert.AreEqual(1, amount) then since there is an implicit conversion from int to Amount (via implicit int->decimal) but no implicit conversion from Amount to int the compiler thinks "obviously they mean the Assert.AreEqual<Amount>() here"†, and so it is picked.

您可以使用Assert.AreEqual<Amount>()Assert.AreEqual<decimal>()显式选择一个重载,但最好将其中一个转换尽可能缩小为explicit,因为您的此功能结构会再次伤害您. (Hurrah在单元测试中发现了缺陷).

You can explicitly pick an overload with Assert.AreEqual<Amount>() or Assert.AreEqual<decimal>() but you're probably better off making one of your conversions the "narrowing" from that has to be explicit if at all possible because this feature of your struct will hurt you again. (Hurrah for unit tests finding flaws).

*另一个有效的重载选择是选择Assert.AreEqual<object>,但由于以下原因,它永远不会被推理选择:

*Another valid overload choice is to pick Assert.AreEqual<object>, but it's never picked by inference because:

  1. 两个被拒绝的重载都被认为是更好的.
  2. 总是被采用object的非通用形式击败.
  1. Both the rejected overloads were considered even better.
  2. It is always beaten by the non-generic form that takes object anyway.

因此只能通过在代码中包含<object>来调用它.

As such it can only ever be called by including the <object> in the code.

†编译器将对它说的一切视为明显的含义或完全不可理解的.也有这样的人.

†The compiler treats everything said to it as either obvious in meaning or completely incomprehensible. There are people like that too.

这篇关于为什么带有隐式转换运算符的自定义结构上的Assert.AreEqual失败?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆