Особенности Distinct на коллекциях объектов ссылочной природы

Бывает, что коллекция содержит повторяющиеся объекты и требуется из неё получить коллекцию, содержащую только уникальные объекты. Этого можно добиться с помощью LINQ оператора Distinct, но есть одно но. Оператор Distinct() хорош с простыми типами, но если ваш объект представляет что-то посложнее (например экземпляр класса), то тут вас постигнет разочарование. Дубли так и останутся в коллекции. Это происходит потому, что оператор Distinct не умеет сравнивать экземпляры классов из-за их ссылочной природы.

Чтобы заставить оператор Distinct работать корректно потребуется создать класс, реализующий интерфейс IEqualityComparer<T> и работающий с нашим классом (закрытый им). Этот интерфейс содержит всего два метода: bool Equals(T x, T y) и int GetHashCode(T obj). Следовательно, реализовав эти два метода мы научим Distinct сравнивать объекты нашего класса, т.к. оператор Distinct имеет перегрузку, принимающую экземпляр, реализующий IEqualityComparer закрытый нашим классом.

Допустим наш класс определён следующим образом:

1
2
3
4
5
6
public class Employee
{
public int ID { get; set; }
public string Name { get; set; }
public string Department { get; set; }
}

Тогда класс реализующий корректное сравнение экземпляров нашего класса будет выглядеть так:

1
2
3
4
5
6
7
8
9
10
11
12
public class EmployeeComparer: IEqualityComparer<Employee>
{
public bool Equals(Employee x, Employee y)
{
return x.ID == y.ID;
}

public int GetHashCode(Employee obj)
{
return obj.GetHashCode();
}
}

Теперь при вызове оператора Distinct на нашей коллекции ему требуется в параметрах передать экземпляр нашего сравнивателя EmployeeComparer:

1
var uniqueEmployees = employees.Distinct(new EmployeeComparer()).ToList();

Такой запрос уже вернёт список уникальных экземпляров нашего класса.