In versions of .net prior to 4.0, if a structure contained only primitive fields, the Equals
operator for the struct would only return true
if all fields matched precisely. In 4.0, Microsoft changed this behavior so that structures with fields of type Decimal
could return true if the corresponding fields held values representing the same numerical quantity, even if they did not match in other details (most notably, for Decimal
, values which differed only in trailing zeroes were considered equal). Unfortunately, there are some contexts where this is absolutely disastrous. For example, given two immutable objects X
and Y
, it should be possible to substitute Y
for X
if all fields of X
precisely match those of Y
. For such substitution to be safe, however, it is imperative that the match be precise. If a field in X
contains 0.1m, and the corresponding field in Y
contains 0.10m, the objects should not be considered equal, because the observable behavior of Y
would differ from that of X
.
If Microsoft hadn't overridden Equals
on those types to mean something other than equivalence (which is what it means for just about every other type), one could safely assume if one object reports that it Equals
another, that instances of the latter could be substituted for the former; even given the non-equivalence overrides on Decimal
, one could test for equivalence by wrapping those types in a structure. Given that Microsoft no longer allows that method of equivalence testing, what would one have to do to achieve the same result? Would there be any way for an AlternateEqualityComparer<T>.Default
property to determine whether a struct defines its own override of Object.Equals
[which should be used] or a system-provided one [in which case, if the struct contains any fields which use non-equivalence-based Equals
, it should use an equality operator that tests for equivalence]?
Edit
I had tested the behavior using a struct that contained floating-point types as well as Decimal
; even though Decimal
is not a reference type, its existence within the struct caused the auto-generated Equals
method not to use binary comparison for any of the fields.
Edit 2 Here's an example program that shows the issue in .net 4.0; I'd read that the behavior had changed since earlier versions of .net, though this program seems to run the same in on 2.0 and 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();
}
}
If the structure contains just type Double
, the structure's equality comparer reports structures as distinct if they don't match perfectly. If it contains a field of type Decimal
, even though Decimal
is a structure which contains no reference fields, then the equality comparer reports them as identical if their numerical values are equal, even if they are not equivalent.
In any case, whether or not .net has ever insisted upon binary matching for Decimal
objects, the question would remain: what would be the best way of performing a structure comparison such that equality-comparisons of value types that do not contain reference-type fields are determined using bit-wise equality rather than numerical equality? Is there any way to honor structure types' "explicit" overrides of Equals
while using an alternate equality comparer for those whose Equals
is auto-generated?