为什么Assert.AreEqual(x,y)失败了,而Assert.Are Equal(y,x)却没有';t

本文关键字:Assert 失败 AreEqual 为什么 Are Equal | 更新日期: 2023-09-27 18:20:31

考虑这个结构:

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>没有帮助。

为什么Assert.AreEqual(x,y)失败了,而Assert.Are Equal(y,x)却没有';t

第一个断言将调用MyNumber.Equals,并且您已经实现了这样一种方式:如果要比较的参数是Int32并且等于MyNumber的值,那么它将成功。

第二个断言将调用Int32.Equals,它将失败,因为要与之比较的参数是Int32不知道或不理解的MyNumber

您不能使第二个单元测试成功,因为您断言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)是true,但Int32(123) equals MyNumber(123)是false。您应该避免这种情况,因此我建议您删除MyNumber.Equals中对int的特殊情况处理,而改用在大多数情况下都适用的隐式强制转换。当他们不这样做时,你将不得不做出明确的演员阵容。

引用Object.Equals:

以下语句对于Equals(Object)方法的所有实现都必须为true。在列表中,x、y和z表示不是null的对象引用

  • 。。。

  • x.Equals(y)返回的值与y.Equal(x)相同。

您的Equals实现打破了这一硬性要求。如果x不是null,则无论其类型如何,((object)x).Equals(123)都必须返回与((object)123).Equals(x)相同的值。

有很多代码正确地假设,要求两个对象中的哪一个执行比较并不重要。设计你的代码,这样假设就不会失效。

实际上,这意味着必须以这样一种方式设计类,即不会与任何整数类型进行比较,无论您多么喜欢其他类型。

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