A generic List<T>
can contain any type - value or reference. When checking to see if a list contains an object, .Contains()
uses the default EqualityComparer<T>
for type T, and calls .Equals()
(is my understanding). If no EqualityComparer has been defined, the default comparer will call .Equals()
. By default, .Equals()
calls .ReferenceEquals()
, so .Contains()
will only return true if the list contains the exact same object.
Until you need to override .Equals()
to implement value equality, at which point the default comparer says two objects are the same if they have the same values. I can't think of a single case where that would be desirable for a reference type.
What I'm hearing from @Enigmativity is that implementing IEqualityComparer<StagingDataRow>
will give my typed DataRow a default equality comparer that will be used instead of the default comparer for Object
– allowing me to implement value equality logic in StagingDataRow.Equals()
.
Questions:
EqualityComparer<StagingDataRow>.Equals()
instead of StagingDataRow.Equals()
?IEqualityComparer<StagingDataRow>.GetHashCode(StagingDataRow obj)
hash against, and should it return the same value as StagingDataRow.GetHashCode()
?IEqualityComparer<StagingDataRow>.GetHashCode(StagingDataRow obj)
? The object I'm looking for or the object in the list? Both? It would be strange to have an instance method accept itself as a parameter...In general, how does one separate value equality from reference equality when overriding .Equals()
?
The original line of code spurring this question:
// For each ID, a collection of matching rows
Dictionary<string, List<StagingDataRow>> stagingTableDictionary;
StagingTableMatches.AddRange(stagingTableDictionary[perNr].Where(row => !StagingTableMatches.Contains(row)));
.
Ok, let's handle a few misconceptions first:
By default,
.Equals()
calls.ReferenceEquals()
, so.Contains()
will only return true if the list contains the exact same object.
This is true, but only for reference types. Value types will implement a very slow reflection-based Equals
function by default, so it's in your best interest to override that.
I can't think of a single case where that would be desirable for a reference type.
Oh I'm sure you can... String
is a reference type for instance :)
What I'm hearing from @Enigmativity is that implementing
IEqualityComparer<StagingDataRow>
will give my typedDataRow
a default equality comparer that will be used instead of the default comparer forObject
– allowing me to implement value equality logic inStagingDataRow.Equals()
.
Err... No.
IEqualityComaprer<T>
is an interface which lets you delegate equality comparison to a different object. If you want a different default behavior for your class, you implement IEquatable<T>
, and also delegate object.Equals
to that for consistency. Actually, overriding object.Equals
and object.GetHashCode
is sufficient to change the default equality comparison behavior, but also implementing IEquatable<T>
has additional benefits:
object.Equals
)So, for your actual questions:
Am I understanding that correctly?
You still seem a bit confused about this, but don't worry :)
Enigmativity actually suggested that you create a different type which implements IEqualityComparer<T>
. Looks like you misunderstood that part.
Am I guaranteed that everything in the .NET framework will call
EqualityComparer<StagingDataRow>.Equals()
instead ofStagingDataRow.Equals()
By default, the (properly written) framework data structures will delegate equality comparison to EqualityComparer<StagingDataRow>.Default
, which will in turn delegate to StagingDataRow.Equals
.
What should
IEqualityComparer<StagingDataRow>.GetHashCode(StagingDataRow obj)
hash against, and should it return the same value asStagingDataRow.GetHashCode()
Not necessarily. It should be self-consistent: if myEqualitycomaprer.Equals(a, b)
then you must ensure that myEqualitycomaprer.GetHashCode(a) == myEqualitycomaprer.GetHashCode(b)
.
It can be the same implementation than StagingDataRow.GetHashCode
, but not necessarily.
What is passed to
IEqualityComparer<StagingDataRow>.GetHashCode(StagingDataRow obj)
? The object I'm looking for or the object in the list? Both? It would be strange to have an instance method accept itself as a parameter...
Well, by now I hope you've understood that the object which implements IEqualityComparer<T>
is a different object, so this should make sense.
Please read my answer on Using of IEqualityComparer interface and EqualityComparer class in C# for more in-depth information.