在 4.0 之前的 .net 版本中,如果结构仅包含原始字段,则该结构的运算符仅在所有字段精确匹配时Equals
才会返回。true
在 4.0 中,Microsoft 更改了此行为,因此Decimal
如果相应字段包含表示相同数字量的值,则具有类型字段的结构可以返回 true,即使它们在其他细节上不匹配(最值得注意的是,对于Decimal
仅在尾随不同的值零被认为是相等的)。不幸的是,在某些情况下,这绝对是灾难性的。例如,给定两个不可变对象X
和Y
,如果 的所有字段都精确Y
匹配X
X
Y
. 然而,为了确保这种替换是安全的,匹配必须是精确的。如果 in 的一个字段X
包含 0.1m,而 in 的相应字段Y
包含 0.10m,则不应认为对象相等,因为 的可观察行为与 的Y
不同X
。
如果 Microsoft 没有重写Equals
这些类型以表示等价以外的含义(这对几乎所有其他类型都意味着),那么人们可以安全地假设如果一个对象报告了Equals
另一个对象,则后者的实例可以替换为前者; 即使考虑到 上的非等价覆盖Decimal
,也可以通过将这些类型包装在结构中来测试等价性。鉴于 Microsoft 不再允许这种等效测试方法,要获得相同的结果需要做什么?属性是否有任何方法AlternateEqualityComparer<T>.Default
可以确定结构是否定义了自己的Object.Equals
[应该使用的]覆盖还是系统提供的覆盖[在这种情况下,如果结构包含任何使用基于非等价的字段Equals
,它应该使用一个相等性运算符来测试等价性]?
编辑
我已经使用包含浮点类型的结构以及Decimal
; 即使Decimal
不是引用类型,它在结构中的存在也导致自动生成的Equals
方法不对任何字段使用二进制比较。
编辑 2 这是一个显示 .net 4.0 中问题的示例程序;我读到自早期版本的 .net 以来行为已经改变,尽管这个程序在 2.0 和 4.0 上似乎运行相同:
struct Test<T1,T2>
{
public T1 f1;
public T2 f2;
public Test(T1 p1, T2 p2)
{
f1 = p1;
f2 = p2;
}
}
class Program
{
static void DoCompares<T1,T2>(Test<T1,T2> thing1, Test<T1,T2> thing2)
{
Console.WriteLine("{0}/{1}/{2} {3}",
thing1.f1, thing2.f1, thing1.f1.Equals((Object)thing2.f1),
thing1.Equals(thing2));
}
static void DoTest<T1, T2>(T1 p1a, T1 p1b, T2 p2)
{
Test<T1,T2> thing1 = new Test<T1,T2>(p1a,p2);
Test<T1,T2> thing2 = new Test<T1,T2>(p1b,p2);
DoCompares(thing1, thing2);
}
static void Main(string[] args)
{
DoTest(1.0m, 1.00m, 1.0);
DoTest(1.0m, 1.00m, 1.0m);
DoTest(1.0 / (1.0 / 0.0), -1.0 / (1.0 / 0.0), 1.0m);
DoTest(1.0 / (1.0 / 0.0), -1.0 / (1.0 / 0.0), 1.0);
Console.ReadLine();
}
}
如果结构仅包含 type Double
,则如果结构不完全匹配,则结构的相等比较器会将结构报告为不同的。如果它包含一个类型为 的字段Decimal
,即使它Decimal
是一个不包含引用字段的结构,如果它们的数值相等,相等比较器也会将它们报告为相同,即使它们不相等。
在任何情况下,无论 .net 是否曾坚持对Decimal
对象进行二进制匹配,问题仍然存在:执行结构比较的最佳方法是什么,以便不包含引用类型字段的值类型的相等比较是使用按位相等而不是数值相等来确定的?有没有办法在为自动生成Equals
的那些使用替代相等比较器时尊重结构类型的“显式”覆盖?Equals