.net 测试结构(特别是包含'Decimal`)是否精确相等的好方法

.net 测试结构(特别是包含'Decimal`)是否精确相等的好方法,.net,struct,equality,.net,Struct,Equality,在4.0之前的.net版本中,如果结构仅包含基本字段,则如果所有字段都精确匹配,则结构的Equals运算符将仅返回true。在4.0中,Microsoft更改了此行为,因此,如果相应字段包含表示相同数字量的值,则具有Decimal类型字段的结构可以返回true,即使它们在其他细节中不匹配(最值得注意的是,对于十进制,只有尾随零不同的值被视为相等)。不幸的是,在某些情况下,这绝对是灾难性的。例如,给定两个不可变对象X和Y,如果X的所有字段都与Y的字段精确匹配,则应该可以用Y替换X。但是,为了安全起

在4.0之前的.net版本中,如果结构仅包含基本字段,则如果所有字段都精确匹配,则结构的
Equals
运算符将仅返回
true
。在4.0中,Microsoft更改了此行为,因此,如果相应字段包含表示相同数字量的值,则具有
Decimal
类型字段的结构可以返回true,即使它们在其他细节中不匹配(最值得注意的是,对于
十进制
,只有尾随零不同的值被视为相等)。不幸的是,在某些情况下,这绝对是灾难性的。例如,给定两个不可变对象
X
Y
,如果
X
的所有字段都与
Y
的字段精确匹配,则应该可以用
Y
替换
X
。但是,为了安全起见,这种替换是可行的匹配必须精确。如果
X
中的字段包含0.1m,而
Y
中的相应字段包含0.10m,则不应将对象视为相等,因为
Y
的可观察行为与
X
不同

如果微软没有在这些类型上覆盖
Equals
,以表示等价以外的其他含义(这就是它对几乎所有其他类型的含义),我们可以安全地假设,如果一个对象报告它与另一个对象相等,那么后者的实例可以替换前者;即使在
Decimal
上存在非等价覆盖,我们也可以通过将这些类型包装到结构中来测试等价性。鉴于Microsoft不再允许这种等价方法在测试中,要获得相同的结果,必须做些什么?对于
AlternateQualityComparer.Default
属性,是否有任何方法来确定结构是定义自己对
对象.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();
    }
}
struct测试
{
公共T1 f1;
公共t2f2;
公共测试(T1 p1、T2 p2)
{
f1=p1;
f2=p2;
}
}
班级计划
{
静态空隙文件对比(测试件1、测试件2)
{
Console.WriteLine(“{0}/{1}/{2}{3}”,
thing1.f1,thing2.f1,thing1.f1等于((对象)thing2.f1),
事物1.等于(事物2);
}
静态空隙点测试(T1 p1a、T1 p1b、T2 p2)
{
试验内容1=新试验(p1a,p2);
测试内容2=新测试(p1b,p2);
文件比较(第1件、第2件);
}
静态void Main(字符串[]参数)
{
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();
}
}
如果结构仅包含类型
Double
,如果结构不完全匹配,则结构的相等比较器会将结构报告为不同的。如果结构包含类型为
Decimal
的字段,即使
Decimal
是一个不包含引用字段的结构,如果它们的数值相等,即使它们不相等


在任何情况下,无论.net是否坚持为
十进制
对象进行二进制匹配,问题仍然存在:执行结构比较的最佳方式是什么,这样不包含引用类型字段的值类型的相等比较就可以使用位相等而不是数字相等来确定平等性?有没有办法尊重结构类型的“显式”呢当为那些自动生成
等于的对象使用备用相等比较器时,对
等于的重写?

我不确定在输入是
0.1m
还是
0.10m
会产生不同的行为,但你不是在试图修复错误的问题吗?我也不确定是什么原因您提到的是正确的。假设当前实例和obj的字段都不是引用类型,那么Equals方法将对内存中的两个对象执行逐字节比较。Bit-idential与您所能得到的几乎相同。我是否误解了这个问题?我刚刚测试了正零和负零的Equals,看起来s在4.0下,它们是按位比较的(使用VS 2012编译),即双负零字段使结构不等于双正零字段的结构。@Jon:我猜
double
仍然使用二进制比较,但
Decimal
不使用;当我写问题时,我认为因为Decimal的struct equals行为现在反映了它的(不幸)
等于
重写行为,
double
/
float
也会。让我重写这个问题。@SergeBelov:如果结构包含类型为
Decimal
的字段,即使它不是引用类型,它也不会对浮点字段使用二进制比较。