Comment comparer des types complexes avec Enumerable.Distinct et LINQ ?

Comment doit-on s’y prendre pour recouper, en éléments distincts, une liste composée d’objets complexes? À un moment donné ou à un autre, nous avons tous eu à faire cela. Heureusement, c’est là qu’intervient la méthode Enumerable.Distinct().

Par définition, la méthode Distinct() permet de retourner une liste composée des éléments distincts de la collection d’origine.

Naturellement, il est simple d’utiliser Distinct avec une liste composée de types primitifs comme int, string, long et double, par exemple. La comparaison est déjà prévue dans le framework .NET.

Cependant, la situation est entièrement différente lorsqu’il est temps d’utiliser un type complexe avec Distinct. Comment doit s’y prendre pour deviner la clé pour comparer les différents éléments de la liste?

L’option la plus évidente est d’implémenter l’interface IEqualityComparer. Ce n’est pas par hasard que je le mentionne, car c’est justement une des signatures permises par Distinct. Vous pouvez créer une classe pour implémenter une comparaison, selon vos besoins, de votre type complexe.

Cela signifie que vous devez spécifier votre propre définition qu’égalité entre deux instances de ce type. Pour .NET, la définition d’égalité se définit par les méthodes suivantes:

  1. la valeur retournée par GetHashCode() doit être égale pour les deux objets comparés
  2. En cas de valeur différente au point précédent, une comparaison sera effectuée avec la méthode Equals() des deux instances.

Distinct et IEqualityComparer en exemple

Considérez les deux extraits de code suivant


public class User
{
public string FirstName { get; set; }
public string LastName { get; set; }
public LoginInformation LoginInformation { get; set; }
public class Comparer : IEqualityComparer<User>
{
public bool Equals(User x, User y)
{
return x.LoginInformation.LoginLocation == y.LoginInformation.LoginLocation;
}
public int GetHashCode(User obj)
{
return obj.LoginInformation.LoginLocation.GetHashCode();
}
}
}
public class LoginInformation
{
public string LoginLocation { get; set; }
}

view raw

user.cs

hosted with ❤ by GitHub

et l’application console suivante


class Program
{
static void Main(string[] args)
{
var users = new List<User>
{
new User
{
FirstName = "Pascal",
LastName = "Paradis",
LoginInformation = new LoginInformation
{
LoginLocation = "Montréal"
}
},
new User
{
FirstName = "L'homme",
LastName = "Poux",
LoginInformation = new LoginInformation
{
LoginLocation = "Montréal"
}
}
};
Console.WriteLine(users.Distinct(new User.Comparer()).Single().LoginInformation.LoginLocation);
Console.ReadLine();
}
}

view raw

distinct.cs

hosted with ❤ by GitHub

Êtes-vous en mesure de prédire le résultat de l’exécution? Dans la console, la chaîne de texte « Montréal » sera retournée à l’écran, car il s’agit de la valeur qui est comparée par la classe User.Comparer.

Certains pourraient argumenter que de faire un override des méthodes GetHashCode et Equals directement revient exactement au même. Là où je ne suis pas en accord c’est sur le principe que d’implémenter IEqualityComparer a le mérite d’exprimer clairement une intention.

Dans ce cas-ci, c’est de définir que deux utilisateurs sont recoupés en fonction de leur emplacement de connexion. L’élégance de la chose est qu’il n’y a rien qui vous empêche de développer plusieurs comparateurs implémentant IEqualityComparer selon le contexte où vous vous trouvez.

Sources

Publicité

Auteur : Pascal Paradis

Je suis les mains et le cerveau derrière http://frenchcoding.com. Je développe des microservices chez @UbisoftMTL. Amateur de Hockey et j'aime la technologie, en général.

2 réflexions sur “Comment comparer des types complexes avec Enumerable.Distinct et LINQ ?”

Laisser un commentaire

Entrer les renseignements ci-dessous ou cliquer sur une icône pour ouvrir une session :

Logo WordPress.com

Vous commentez à l’aide de votre compte WordPress.com. Déconnexion /  Changer )

Photo Facebook

Vous commentez à l’aide de votre compte Facebook. Déconnexion /  Changer )

Connexion à %s

Ce site utilise Akismet pour réduire les indésirables. En savoir plus sur la façon dont les données de vos commentaires sont traitées.

%d blogueueurs aiment cette page :