• Aucun résultat trouvé

Nous avons vu dans la première partie qu’il était possible de comparer facilement des types valeur grâce aux opérateurs de comparaison. En effet, vu que des variables de ces types possèdent directement la valeur que nous lui affectons, on peut facilement comparer un entier avec la valeur 5, ou un entier avec un autre entier. Par contre, cela ne fonctionne pas avec les objets. En effet, nous avons vu que les variables qui représentent des instances d’objet contiennent en fait une référence vers l’instance.

Cela n’a pas vraiment de sens de comparer des références. De plus, en imaginant que je veuille vraiment comparer deux voitures, sur quels critères puis-je déterminer qu’elles sont égales ? La couleur ? La vitesse ?

Sans rien faire, la comparaison en utilisant par exemple l’opérateur d’égalité « == » permet simplement de vérifier si les références pointent vers le même objet.

Pour les exemples suivants, nous nous baserons sur la classe Voiture suivante : Code : C#

public class Voiture {

public string Couleur { get; set; } public string Marque { get; set; } public int Vitesse { get; set; } }

Ainsi, si nous écrivons : Code : C#

Voiture voitureNicolas = new Voiture(); voitureNicolas.Couleur = "Bleue";

Voiture voitureJeremie = voitureNicolas; voitureJeremie.Couleur = "Verte";

if (voitureJeremie == voitureNicolas) {

Console.WriteLine("Les objets référencent la même instance"); }

Ce code affichera la chaine « Les objets référencent la même instance » car effectivement, nous avons affecté la référence de voitureNicolas à voitureJeremie. (Ce qui implique également que la modification de la voiture de Jérémie affecte également la voiture de Nicolas, comme nous l’avons déjà vu).

Par contre, le code suivant : Code : C#

Voiture voitureNicolas = new Voiture(); Voiture voitureJeremie = new Voiture();

if (voitureJeremie == voitureNicolas) {

n'affichera évidemment rien car ce sont deux instances différentes.

S’il s’avère qu’il est vraiment pertinent de pouvoir comparer deux voitures entre elles, il faut savoir que c’est quand même possible. La première chose à faire est de définir les critères de comparaison.

Par exemple, nous n’avons qu’à dire que deux voitures sont identiques quand la couleur, la marque et la vitesse sont égales. Je sais, c’est un peu irréel, mais c’est pour l’exemple.

Ainsi, nous pourrons par exemple vérifier que deux voitures sont égales avec l’instruction suivante : Code : C#

if (voitureNicolas.Couleur == voitureJeremie.Couleur && voitureNicolas.Marque == voitureJeremie.Marque &&

voitureNicolas.Vitesse == voitureJeremie.Vitesse) {

Console.WriteLine("Les deux voitures sont identiques"); }

La comparaison d’égalité entre deux objets, c’est en fait le rôle de la méthode Equals() dont chaque objet hérite de la classe mère Object. A part pour les types valeur, le comportement par défaut de la méthode Equals() est de comparer les références des objets. Seulement, il est possible de définir un comportement plus approprié pour notre classe Voiture, grâce à la fameuse spécialisation.

Comme on l’a déjà vu, on utilise le mot clé override. Ceci est possible dans la mesure où la classe « Object » a défini la méthode Equals comme virtuelle, avec le mot clé virtual.

Ce qui donne : Code : C#

public class Voiture {

public string Couleur { get; set; } public string Marque { get; set; } public int Vitesse { get; set; }

public override bool Equals(object obj) {

Voiture v = obj as Voiture; if (v == null)

return false;

return Vitesse == v.Vitesse && Couleur == v.Couleur && Marque == v.Marque;

} }

Remarquons que la méthode Equals prend en paramètre un object. La première chose à faire est donc de vérifier que nous avons réellement une voiture, grâce au cast dynamique.

Ensuite, il ne reste qu’à comparer les propriétés de l’instance courante et de l’objet passé en paramètre. Pour faire une comparaison entre deux voitures, nous pourrons utiliser le code suivant :

Code : C#

Voiture voitureNicolas = new Voiture { Vitesse = 10, Marque = "Peugeot", Couleur = "Grise"};

Voiture voitureJeremie = new Voiture { Vitesse = 10, Marque = "Peugeot", Couleur = "Grise" };

if (voitureNicolas.Equals(voitureJeremie)) {

Console.WriteLine("Les objets ont les mêmes valeurs dans leurs propriétés");

}

Nos deux voitures sont identiques car leurs marques, leurs couleurs et leurs vitesses sont identiques :

Facile de comparer .

Sauf que vous aurez peut-être remarqué que la compilation de ce code provoque un avertissement. Il ne s’agit pas d’une erreur, mais Visual C# express nous informe qu’il faut faire attention :

Il nous dit que nous avons substitué la méthode Equals() sans avoir redéfini la méthode GetHashCode(). Nous n'avons pas besoin ici de savoir à quoi sert vraiment la méthode GetHashCode(), mais si vous voulez en savoir plus, n'hésitez pas à consulter la documentation.

Toujours est-il que nous devons rajouter une spécialisation de la méthode GetHashCode(), dont le but est de renvoyer un identifiant plus ou moins unique représentant l'objet, ce qui donnera :

Code : C#

public class Voiture {

public string Couleur { get; set; } public string Marque { get; set; } public int Vitesse { get; set; }

if (v == null) return false;

return Vitesse == v.Vitesse && Couleur == v.Couleur && Marque == v.Marque;

}

public override int GetHashCode() {

return Couleur.GetHashCode() * Marque.GetHashCode() * Vitesse.GetHashCode();

} }

Nous nous servons du fait que chaque variable de la classe possède déjà un identifiant obtenu avec la méthode GetHashCode(). En combinant chaque identifiant de chaque propriété nous pouvons en créer un nouveau.

Ici, la classe est complète et prête à être comparée. Elle pourra donc fonctionner correctement avec tous les algorithmes d’égalité du framework .NET. Notez quand même que devoir substituer ces deux méthodes est une opération relativement rare, nous l’avons étudié pour la culture.

Ce qui est important à retenir c’est ce fameux warning. La conclusion à tirer est que notre façon de comparer, bien que fonctionnelle pour notre voiture, n’est pas parfaite.

Pourquoi ? Parce qu’en ayant substitué la méthode Equals(), nous croyons que la comparaison est bonne sauf que le compilateur nous apprend que ce n’est pas le cas. Heureusement qu’il était là ce compilateur. Comme c’est une erreur classique, il est capable de la détecter. Mais si c’est autre chose et qu’il ne le détecte pas ?

Cela manque d’une uniformisation tout ça, vous ne trouvez pas ? Il faudrait quelque chose qui nous assure que la classe est correctement comparable. Une espèce de contrat que l’objet s’engagerait à respecter pour être sûr que toutes les comparaisons soient valides.

Un contrat ? Un truc qui finit par « able » ? Ça me rappelle quelque chose ça. Mais oui, les interfaces.