Jakiś czas temu Maciej Aniserowicz na swoim blogu opublikował kod klasy LambdaEqualityComarer
Bardzo często chcielibyśmy aby równość elementów jakiejś klasy zależała tylko od jakiegoś pola (najczęściej od ID), aby to osiągnąć należy nadpisać metody GetHashcode i Equals tej klasy jest to jednak praca dosyć żmudna i powtarzalna, dodatkowo okazuje się że kod ten należy często pisać ponieważ wiele klas wywołuje te metody w celu porównania elementów, przykładem takich klas są kontrolki WPF zawierające Listy jak np. ComboBox. Aby pracę tą trochę skrócić można użyć następującego fragmentu kodu:
class User { /// <summary> /// Identyfikator /// </summary> public int ID { get; set; } // Inne pola private LambdaEqualityComparer<User, int> _comparer = new LambdaEqualityComparer<User, int>(u => u.ID); public override int GetHashCode() { return _comparer.GetHashCode(this); } public override bool Equals(object obj) { return _comparer.Equals(this, obj as User); } }
Aby ten kod działał w 100% poprawnie należy jeszcze zmodyfikować kod Maćka tak aby uwzględniała fakt iż któryś z parametrów metody Equals może być nullem.
class LambdaEqualityComparer<T, TValue> : IEqualityComparer<T> { #region Private members private Func<T, TValue> converter; #endregion Private members #region Constructors public LambdaEqualityComparer(Func<T, TValue> converter) { this.converter = converter; } #endregion Constructors #region IEqualityComparer<T> Members public bool Equals(T x, T y) { if (x == null && y == null) { return true; } else if (x == null || y == null) { return false; } else { return converter(x).Equals(converter(y)); } } public int GetHashCode(T obj) { if (obj == null) { return 0; } else { return converter(obj).GetHashCode(); } } #endregion }
Na koniec dodam że implementację tą można rozbudować zgodnie z wzorcem dekoratora, co daje nam możliwość wykorzystania istniejących implementacji IEqualityComparer'a.
class LambdaEqualityComparer<T, TValue> : IEqualityComparer<T> { #region Private members private Func<T, TValue> converter; private IEqualityComparer<TValue> realComparer; #endregion Private members #region Constructors public LambdaEqualityComparer(Func<T, TValue> converter) : this(converter, null) { } public LambdaEqualityComparer(Func<T, TValue> converter, IEqualityComarer<TValue> realComparer) { this.converter = converter; this.realComparer = realComparer; } #endregion Constructors #region IEqualityComparer<T> Members public bool Equals(T x, T y) { if (x == null && y == null) { return true; } else if (x == null || y == null) { return false; } else { if (realComparer == null) { return converter(x).Equals(converter(y)); } else { return realComparer.Equals(converter(x), converter(y)); } } } public int GetHashCode(T obj) { if (obj == null) { return 0; } else { if (realComparer == null) { return converter(obj).GetHashCode(); } else { return realComparer.GetHashCode(converter(obj)); } } } #endregion }