RecordGenerator icon indicating copy to clipboard operation
RecordGenerator copied to clipboard

Custom EqualityComparer for fields

Open bboyle1234 opened this issue 5 years ago • 2 comments

Sometimes we want to use a custom EqualityComparer for particular fields.

// Example class
[Record]
public sealed partial class SomeObject { 
  public string Name { get; }
  [EqualityComparer(typeof(EnumerableComparer<int>))]
  public ImmutableList<int> Values { get; }
}

// Example code-gen
partial class SomeObject : IEquatable<SomeObject> { 
  public bool Equals(SomeObject other) { 
    if (null == other) return false;
    return EqualityComparer<string>.Default.Compare(Name, other.Name)
      && EnumerableComparer<int>.Default.Compare(Values, other.Values);
  }
}

Example custom comparer class:

// The user has created a custom comparer that he/she wants used for comparing a certain field
public class EnumerableComparer<TItem> : IEqualityComparer<IEnumerable<TItem>> {

    public bool Equals(IEnumerable<TItem> x, IEnumerable<TItem> y) {
        if (ReferenceEquals(x, y)) return true;
        if (null == x || null == y) return false;
        if (x.GetHashCode() != y.GetHashCode()) return false;
        var enumX = x.GetEnumerator();
        var enumY = y.GetEnumerator();
        while (enumX.MoveNext()) {
            if (!enumY.MoveNext()) return false;
            if (!EqualityComparer<TItem>.Default.Equals(enumX.Current, enumY.Current)) return false;
        }
        return !enumY.MoveNext();
    }

    public int GetHashCode(IEnumerable<TItem> obj) {
        if (null == obj) return 0;
        var hash = obj.GetType().GetHashCode();
        unchecked {
            foreach (var item in obj) {
                hash = hash * 123 + EqualityComparer<TItem>.Default.GetHashCode(item);
            }
        }
        return hash;
    }
}

Also, though it wasn't demonstrated in the example code above, the custom equality comparer type could be used for getting the hashcode of the particular field for use in calculating the hashcode of the record.

bboyle1234 avatar Sep 20 '19 02:09 bboyle1234

If we were to do that, the comparer should definitely be used in any place the property is used.

amis92 avatar Sep 20 '19 09:09 amis92

You might like to provide a pallete of comparers as well, like the example one I provided, but I'm sure you'd have many ideas in mind.

bboyle1234 avatar Sep 20 '19 12:09 bboyle1234