考虑这个结构:
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 :(得分: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
中的整数的特殊情况处理,而不是依赖于大多数时候都适合你的隐式强制转换。如果他们不这样做,你将不得不做一个明确的演员。
答案 1 :(得分:2)
以下语句必须适用于Equals(Object)方法的所有实现。在列表中,x,y和z表示不是 null 的对象引用。
...
x.Equals(y)返回与y.Equals(x)相同的值。
您的Equals
实施违反了这一要求。 ((object)x).Equals(123)
必须返回与((object)123).Equals(x)
相同的值,如果x
不是null
,则无论其类型如何。
那里有很多代码,正确地说,假设要求两个对象中的哪一个执行比较并不重要。设计您的代码,以使该假设无效。
实际上,这意味着你必须设计你的类,使不比较等于任何整数类型,无论你有多少可能更愿意。
答案 2 :(得分:0)
在IComparable<int>
结构中实施MyNumber
,如下所示:
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));