Introduction To Generic IEqualityComparer

IEqualityComparer is a very important interface for comparer tasks in the LinQ world. The next extended methods have an overload with this parameter type: Contains, Distinct, Except, Intersect, GrouBy, GroupJoin, Join, SecuenceEqual, ToDictionary, ToLookUp and Union.

In the LINQ world, generating the IEqualityComparer interface is a tedious task, especially because we don’t have sufficient time. Our generic class comes in handy for making things easier for us.

  • Classic IEqualityComparer Implementation (for fields)
  • Generic IEqualityComparer Implementation (for fields)
  • Classic IEqualityComparer Implementation (for expression)
  • Generic IEqualityComparer Implementation (for Expression)
  • Code GenericIEqualityComparer class

Classic IEqualityComparer Implementation (for fields)

This example generates different customers for a sequence.

Class Customer

  1. public class Customer {  
  2.     public int ID {  
  3.         get;  
  4.         set;  
  5.     }  
  6.     public string Name {  
  7.         get;  
  8.         set;  
  9.     }  
  10.     public decimal Sales {  
  11.         get;  
  12.         set;  
  13.     }  
  14.     public string City {  
  15.         get;  
  16.         set;  
  17.     }  
  18.     public static IEnumerable < Customer > GetData() {  
  19.         return new List < Customer > () {  
  20.             new Customer {  
  21.                 ID = 1, Name = "Philips", Sales = 2000000 m, City = "Madrid"  
  22.             },  
  23.             new Customer {  
  24.                 ID = 2, Name = "Pionner", Sales = 1000000 m, City = "Berlin"  
  25.             },  
  26.             new Customer {  
  27.                 ID = 3, Name = "Renault", Sales = 2000000 m, City = "Paris"  
  28.             },  
  29.             new Customer {  
  30.                 ID = 4, Name = "Sony Music", Sales = 500000 m, City = "London"  
  31.             },  
  32.             new Customer {  
  33.                 ID = 5, Name = "Sony SCEE", Sales = 2000000 m, City = "Tokio"  
  34.             },  
  35.             new Customer {  
  36.                 ID = 6, Name = "Pepsi", Sales = 9000000 m, City = "New York"  
  37.             },  
  38.             new Customer {  
  39.                 ID = 6, Name = "LG", Sales = 2000000 m, City = "Rome"  
  40.             }  
  41.         };  
  42.     }  
  43. }  
Note

Look, the 2 last customers have the same ID.

IEqualityComparer

If we call a Distinct LinQ Extension Method, the result would be a new sequence with 7 elements, and we would not find any distinct member.

This is because Distinct compares instances for default, and all are different.
  1. using System;  
  2. using System.Linq;  
  3. using DAL;  
  4. using static System.Console;  
  5. namespace ConsoleClient {  
  6.     class Program {  
  7.         static void Main(string[] args) {  
  8.             var customers = Customer.GetData();  
  9.             WriteLine($ "There are {customers.Count()} customers.");  
  10.             var customersDifferents = customers.Distinct();  
  11.             WriteLine($ "There are {customersDifferents.Count()} DIFFERENT customers.");  
  12.             Console.Read();  
  13.         }  
  14.     }  
  15. }  
IEqualityComparer

Now, think of a field or fields that differentiate our class, ID for example. IEqualityComparer helps you to manage and improve the efficiency
  1. public class CustomerForIDEqualityComparer: IEqualityComparer < Customer > {  
  2.     public bool Equals(Customer x, Customer y) {  
  3.         bool result = x.ID == y.ID;  
  4.         return result;  
  5.     }  
  6.     public int GetHashCode(Customer obj) {  
  7.         return obj.ID.GetHashCode();  
  8.     }  
  9. }  
For more information of IEqualityComparer classic implementation: Spanish - English

The IEqualityComparer class name tell us your purpose, different customers for a field ID. Could be more IEqualityComparer classes for different objective and different fields (ForCity, ForSales, etc), as we needed.

We use our IEqualityComparer class
  1. static void Main(string[] args) {  
  2.     var customers = Customer.GetData();  
  3.     WriteLine($ "There are {customers.Count()} customers.");  
  4.     var customersDifferents = customers.Distinct(new CustomerForIDEqualityComparer()).ToList();  
  5.     WriteLine($ "There are {customersDifferents.Count()} DIFFERENT customers.");  
  6.     Console.Read();  
  7. }  
IEqualityComparer

Good work, the result is fine because there are 2 customers with the same ID.

Generic IEqualityComparer Implementation (for fields)

In this moment, we’ll downloaded the GenericEqualityComparer in nuget.

We have 2 possibilities

Through menu AddReference

IEqualityComparer

IEqualityComparer

Or by Package Manager Console

IEqualityComparer

IEqualityComparer

Install-Package MoralesLarios.GenericEqualityComparer

IEqualityComparer

The namespace for the class is
  1. using MoralesLarios.Generics  
We will make the previous example, but we’ll use GenericEqualityComparer class. Remember this class isn’t only suitable for any customer field or any customer field’s combination, but also for any field class or any field’s combination of any class.

GenericEqualityComparer class has an overload with a Func<T, object> parameter, this type is the same as Select method (System.LinQ).

Example
  1. static void Main(string[] args) {  
  2.     var customers = Customer.GetData();  
  3.     // Field Generic Comparator  
  4.     Func < Customer, object > fieldComparator = customer => customer.ID;  
  5.     // Instanct GenericIEqualityComparerClass  
  6.     GenericEqualityComparer < Customer > genericCustomerIEqualityComparer = new GenericEqualityComparer < Customer > (fieldComparator);  
  7.     WriteLine($ "There are {customers.Count()} customers.");  
  8.     var customersDifferents = customers.Distinct(genericCustomerIEqualityComparer).ToList();  
  9.     WriteLine($ "There are {customersDifferents.Count()} DIFFERENT customers.");  
  10.     Console.Read();  
  11. }  
The first step is to create the FieldComparator (Func<T, object>). With this variable we tell Field(s) is/are our virtual PK.

The second step is to instantiate the GenericEqualityComparer class. Your TypeParameter is Customer and your constructor parameter is the FieldComparator.

Result

IEqualityComparer

The same result, but it’s much more  readable.

The same code, but the generic version, is available online.

IEqualityComparer

Classic IEqualityComparer Implementation (for expression)

We’ll write a classic implementation of IEqualityComparer class. In our example, we need to compare customers. Two customers are distinct if your first char Name is different.

This is our IEqualityComparer class for expression.
  1. public class Customer1stCharNameComparer: IEqualityComparer < Customer > {  
  2.     public bool Equals(Customer x, Customer y) {  
  3.         bool result = x.Name ? [0] == y.Name ? [0];  
  4.         return result;  
  5.     }  
  6.     public int GetHashCode(Customer obj) {  
  7.         return obj.Name == null ? 0 : obj.Name[0].GetHashCode();  
  8.     }  
  9. }  
  10. We do the same example, but with the IEqualityComparer  
  11. for  
  12. expression now: static void Main(string[] args) {  
  13.     var customers = Customer.GetData();  
  14.     WriteLine($ "There are {customers.Count()} customers.");  
  15.     var customersDifferents = customers.Distinct(new Customer1stCharNameComparer()).ToList();  
  16.     WriteLine($ "There are {customersDifferents.Count()} DIFFERENT customers.");  
  17.     Console.Read();  
  18. }  
IEqualityComparer

Result

IEqualityComparer

Four different customers start with: P, R, S and L.

Generic IEqualityComparer Implementation (for expression)

For Expression is a broader scope, and doesn’t have limits.

GenericEqualityComparer has an overload with a Func<T, T, bool>, this parameter shows the comparison expression.

This is the same previous example, with GenericEqualityComparer.
  1. static void Main(string[] args) {  
  2.     var customers = Customer.GetData();  
  3.     // Field Generic Comparator  
  4.     Func < Customer, Customer, bool > expressionComparator = (customer1, customer2) => customer1.Name ? [0] == customer2.Name ? [0];  
  5.     // Instanct GenericIEqualityComparerClass  
  6.     GenericEqualityComparer < Customer > genericCustomerIEqualityComparer = new GenericEqualityComparer < Customer > (expressionComparator);  
  7.     WriteLine($ "There are {customers.Count()} customers.");  
  8.     var customersDifferents = customers.Distinct(genericCustomerIEqualityComparer).ToList();  
  9.     WriteLine($ "There are {customersDifferents.Count()} DIFFERENT customers.");  
  10.     Console.Read();  
  11. }  
The same result

IEqualityComparer

As in the previous case, the same result, but it’s much more readable.

The same code, but generic version, it online code.

IEqualityComparer

Code GenericIEqualityComparer class

I’ll try to explain the code of GenericEqualityComparer.

This is the code
  1. using System;  
  2. using System.Collections.Generic;  
  3. namespace CodComun {  
  4.     public class GenericEqualityComparer < T > : IEqualityComparer < T > {  
  5.         private Func < T,  
  6.         object > _virtualFieldComparator;  
  7.         private Func < T,  
  8.         T,  
  9.         bool > _virtualFilterComparator;  
  10.         public GenericEqualityComparer(Func < T, object > virtualFieldComparator) {  
  11.             if (virtualFieldComparator == nullthrow new ArgumentNullException(nameof(virtualFieldComparator), $ "{nameof(virtualFieldComparator)}  doesn't be null");  
  12.             Reset();  
  13.             this._virtualFieldComparator = virtualFieldComparator;  
  14.         }  
  15.         public GenericEqualityComparer(Func < T, T, bool > virtualFilterComparator) {  
  16.             if (virtualFilterComparator == nullthrow new ArgumentNullException(nameof(virtualFilterComparator), $ "{nameof(virtualFilterComparator)}  doesn't be null");  
  17.             Reset();  
  18.             this._virtualFilterComparator = virtualFilterComparator;  
  19.         }  
  20.         private void Reset() {  
  21.             _virtualFieldComparator = null;  
  22.             _virtualFilterComparator = null;  
  23.         }  
  24.         public bool Equals(T x, T y) {  
  25.             bool result = false;  
  26.             if (_virtualFieldComparator != null) result = _virtualFieldComparator(x).Equals(_virtualFieldComparator(y));  
  27.             else result = _virtualFilterComparator(x, y);  
  28.             return result;  
  29.         }  
  30.         public int GetHashCode(T obj) {  
  31.             int result = 0;  
  32.             if (_virtualFieldComparator != null) result = _virtualFieldComparator(obj).GetHashCode();  
  33.             else result = _virtualFilterComparator(obj, obj).GetHashCode();  
  34.             return result;  
  35.         }  
  36.     }  
  37. }  
First, this class implements the IEqualityComparer<T>, as the classics implementations.

It has two private fields. This fields store (dependency injection mode) the value for your two constructors’ parameters. Each represent the two modes of class: For Field or For Expression, therefore one will be always null.

It has two constructors, to enable For Field mode or For Expression mode.

The private method Reset, resets all modes.

Finally, the two public methods, Equals and GetHashCode implement IEqualityComparer<T>, and use the encapsulate actions of their two private fields.

Hope this article will help you to understand the IEqualityComparer<T> interface. Don't hesitate to write questions or suggestions.

X

Build smarter apps with Machine Learning, Bots, Cognitive Services - Start free.

Start Learning Now