RecordGenerator
RecordGenerator copied to clipboard
Custom EqualityComparer for fields
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.
If we were to do that, the comparer should definitely be used in any place the property is used.
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.