Przejdź do treści

DevStyle - Strona Główna
LambdaEqualityComparer

LambdaEqualityComparer

Maciej Aniserowicz

28 lipca 2010

Backend

Szkoda, że w C# nie ma znanych z Javy anonimowych klas. Nie mylić z anonimowymi typami, które nie pozwalają na implementację metod! W internecie jest wiele skarg i próśb próbujących wymusić na Microsofcie dodania tego, jakże wygodnego, ficzera do naszego języka.

Powstaje jednak pytanie: gdzie tak naprawdę byśmy owych klas używali? Jedna odpowiedź szczególnie regularnie przewija się przez praktycznie każdy wątek o tej tematyce: do zaimplementowania inline interfejsu IEqualityComparer<T>. Czy nie irytuje Was konieczność definiowania nowej klasy za każdym razem, gdy jesteście zmuszeni do wykorzystania metody żądającej implementacji tego interfejsu? Oj, mnie irytowała, i to bardzo.

Aż w końcu za którymś razem zatrzymałem się na chwilę i zamiast definiować nowy twór, jakiś zagnieżdżony prywatny typ aby nie zaśmiecał mi API, pomyślałem jak obejść tą durną przeszkodę. Okazało się, że to… proste!

Mając nadużywaną we wszelkich przykładach klasę User:

  1:  public class User
  2:  {
  3:  	public int Id { get; set; }
  4:  	public string Name { get; set; }
  5:  }

chcemy dowiedzieć się za pomocą LINQ czy jakiś zbiór użytkowników zawiera Tego, O Którego Nam Chodzi, jednak nie możemy porównać ich przez referencję. Dotychczas mój kod wyglądałby tak:

  1:  public class User
  2:  {
  3:  	public class EqualityComparerById : IEqualityComparer<User>
  4:  	{
  5:  		public bool Equals(User x, User y)
  6:  		{
  7:  			return GetHashCode(x) == GetHashCode(y);
  8:  		}
  9:  
 10:  		public int GetHashCode(User obj)
 11:  		{
 12:  			return obj.Id.GetHashCode();
 13:  		}
 14:  	}
 15:  
 16:  	//...

i dalej:

  1:  List<User> users = //...
  2:  User searchedUser = //...
  3:  
  4:  bool contains = users.Contains(searchedUser, new User.EqualityComparerById());

Po napisaniu takiego czegoś raz, drugi… miałem dość. Oto do czego doszedłem:

  1:  List<User> users = //...
  2:  User searchedUser = //...
  3:  
  4:  bool contains = users.Contains(searchedUser, new LambdaEqualityComparer<User, int>(u => u.Id));

, gdzie kluczową rolę pełni:

  1:  public class LambdaEqualityComparer<T, TValue> : IEqualityComparer<T>
  2:  {
  3:  	private readonly Func<T, TValue> _extractKey;
  4:  
  5:  	public LambdaEqualityComparer(Func<T, TValue> extractKey)
  6:  	{
  7:  		_extractKey = extractKey;
  8:  	}
  9:  
 10:  	public bool Equals(T x, T y)
 11:  	{
 12:  		return GetHashCode(x) == GetHashCode(y);
 13:  	}
 14:  
 15:  	public int GetHashCode(T obj)
 16:  	{
 17:  		return _extractKey(obj).GetHashCode();
 18:  	}
 19:  }

WRESZCIE mam możliwość zdefiniowania najprostszych implementacji IEqualityComparer<T> inline! Bierzcie i kodujcie z tego wszyscy.

Zobacz również