I've done some research and found several components that were not quite what I wanted:
- EqualityComparer (nuget) - does not seem to provide meaningful
GetHashCode()
by default and too heavyweight to my taste. - AnonymousComparer (nuget) - does not support
GetHashCode()
composition. - MemberwiseEqualityComparer - requires adding custom attribute to exclude member from comparison this way it's not possible to flexibly configure comparisons for existing types. Personally doing
Emit
for this task is a little bit overkill. - System.DataStructures.FuncComparer (nuget) - does not support composition.
And also a couple of related discussions:
- Hows to quick check if data transfer two objects have equal properties in C#?
- Is there a better way to implment Equals for object with lots of fields?
So far idea of having explicitly configured list of members seemed unique. And I implemented my own library https://github.com/alabax/YetAnotherEqualityComparer. It's better than the code suggested by TylerOhlsen in that it does not box extracted members and it uses EqualityComparer<T>
to compare members.
Now the code looks like:
public class TestItem
{
private static readonly MemberEqualityComparer<TestItem> Comparer = new MemberEqualityComparer<TestItem>()
.Add(o => o.BoolValue)
.Add(o => o.DateTimeValue)
.Add(o => o.DoubleValue) // IEqualityComparer<double> can (and should) be specified here
.Add(o => o.EnumValue)
.Add(o => o.LongValue)
.Add(o => o.StringValue)
.Add(o => o.NullableDecimal);
// property list is the same
public override bool Equals(object obj)
{
return Comparer.Equals(this, obj);
}
public override int GetHashCode()
{
return Comparer.GetHashCode(this);
}
}
Also the MemberEqualityComparer implements IEqualityComparer<T>
and follows its semantics: it can successfully compare default(T)
which may be null
for reference types and Nullables.
UPDATE: There are tools that can solve the same problem of creating member based IEqualityComparer<T>
but also these can provide composite IComparer<T>
!