C# 为什么Assert.AreEqual(x,y)失败了,但是Assert.AreEqual(y,x)没有';T
考虑这个结构:C# 为什么Assert.AreEqual(x,y)失败了,但是Assert.AreEqual(y,x)没有';T,c#,struct,assert,equality,C#,Struct,Assert,Equality,考虑这个结构: 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)
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.Are在两种方式中都起相同的作用?顺便说一下,我正在使用MSTest
更新
实现
IEquatable
或IComparable
没有帮助。第一个断言将调用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
中的特殊处理,您现在有了一个与Equals不可交换的类型,例如MyNumber(123)等于Int32(123)
为true,但Int32(123)等于MyNumber(123)
为false。您应该避免这种情况,因此我建议您在MyNumber.Equals
中删除int的特殊情况处理,而改用隐式强制转换,这在大多数情况下都适用。如果没有,则必须进行显式转换。在MyNumber
结构中实现IComparable
,如下所示:
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));
引自:
对于Equals(Object)方法的所有实现,以下语句必须为true。在列表中,x、y和z表示不为空的对象引用
- x、 Equals(y)返回与y.Equals(x)相同的值
Equals
实现打破了这一硬要求((对象)x).Equals(123)
必须返回与((对象)123)相同的值。Equals(x)
,如果x
不是null
,则无论其类型如何
有很多代码,正确地说,假设两个对象中的哪一个被要求执行比较并不重要。设计您的代码,使该假设不会失效
实际上,这意味着您必须以这样一种方式来设计类,即它不会与任何整数类型进行比较,无论您多么希望使用其他方法。不知道是否值得实现
IEquatable
?@Charleh:如果您要将值存储在集合中,则值得实现IEquatable
。它将加速操作,如包含
,索引
等。不可计算
@Charleh,可比较
。这将确保类也可以使用更大的
和更少的
断言。您使用的是哪个单元测试框架?您是使用Microsoft.VisualStudio.TestTools.UnitTesting.Assert进行断言还是使用不同的单元测试框架?好问题,我确实在使用MSTest。但是如果MyNumber是class,我认为实现接口IEquatable,那么这两种断言都可以。是吗?@Davecz:我相信Assert.AreEqual
基本上映射到静态对象.Equals
,在做一些简单的检查后,它将调用objA.Equals(objB)
。我不相信通用接口IEquatable
在这个过程中被使用。正如我在下面提到的,实现IComparable
将解决OP的问题。这个答案不包括OP的问题“我如何使Assert.AreEqual在两种方式下都工作?”@RobEpstein:IComparable
是关于小于,等于或大于,例如用于排序的。当比较与此问题有关的相等性时,不使用此接口。此结构按照比较两个MyNumber
s的断言中的设计工作。此结构不工作。正如Martin在其回答的评论中提到的,iCopy用于排序和排序。它不是用Assert.AreEqual调用的。这可能在您的单元测试框架中起作用,这显然是因为出现了Assert.Greater
和Assert.Less
与问题中使用的MSTest单元测试框架不同,但如果您使用MSTest,它将失败。我回答时的原始问题没有提到使用测试框架,所以我提供了一个在NUnit中完美运行的答案。我将尝试找到一个在MSTest中工作的解决方案。