10. Overloading
Overloading signifie “surcharger” ou définir plusieurs versions d’une méthode, en utilisant le même nom mais une implémentation différente.
“Implémenter” une méthode implique d’avoir le même nombre et/ou le type d’arguments dans l’instruction. Sur la base des arguments, l’implémentation sera reconnue et rappellera la méthode correcte.
Regardons un exemple :
public static void Imprime(string Message) { System.Console.WriteLine(“Le message est : ” + Message); } public static void Imprime(int Numero) { System.Console.WriteLine(“Le numéro : ” + Numéro); }
Les deux définitions de la méthode Print() ne diffèrent que par le type d’argument : string dans le premier cas, int dans le second. En appelant ces deux méthodes, nous obtiendrons le résultat suivant :
// Affiche : “Le message est : Ceci est un élément de type string”. Imprime(“Ceci est un élément de type string”); // Affiche : “Le numéro est 15”. Imprime(15);
L’overloading est souvent utilisée dans la définition du constructeur d’une classe. Reprenons le constructeur de notre classe :
public Personne(string Prenom, string Nom) { if (Prenom == string.Empty) mNom = “(Aucun nom)”; else mPrenom = Prenom; if (Nom == string.Empty) mNom = “(Aucun nom)”; else mNom = Nom; }
Jusqu’à présent, pour créer une Personne sans nom ou prénom, il était nécessaire de définir une chaîne vide dans notre constructeur :
Personne p = new Personne(“”, “”);
Dans ce cas, cependant, il serait préférable d’instancier la classe Personne sans passer d’arguments dans le constructeur. Ce résultat peut facilement être atteint par la l’overloading du constructeur :
public Personne() { mPrenom = “Aucun prénom”; mNom = “Aucun nom”; } public Personne(string Prenom, string Nom) { mPrenom = Prenom; mNom = Nom; }
Ce faisant, si nous créons une Personne en indiquant son prénom et son nom, ces informations seront sauvegardés dans les variables mPrenom et mNom, puisque ce le constructeur Personne(string Prenom, string Nom) est invoqué, et si nous créons un Personne sans paramètres, le constructeur Personne(), qui définit respectivement mPrenom et mNom, on aura “Aucun Prénom” et “Aucun Nom”.
// Définit: mPrenom = Marc, mNom = Minerva Personne p1 = new Personne(“Marc”, “Minerva”); // Définit mPrenom = Aucun prénom, mNom = Aucun nom Personne p2 = new Personne();
Un constructeur peut en appeler un autre. Dans notre exemple, au lieu d’assigner explicitement les variables mPrenom et mNom au constructeur sans arguments, il serait possible d’appeler un autre constructeur, “en lui disant” comment définir le nom et le prénom de la personne :
public Personne() : this(“Aucun prénom”, “Aucun nom”) {}
Le mot clé “this” permet d’accéder aux méthodes, variables et propriétés de la classe courante, ainsi que “base” permet l’accès à la classe dont elle hérite. L’équivalent de “this” en VB.NET est “Me”.
Cette solution est préférable car, en cas de modification du constructeur, il suffit d’intervenir sur un seul point du code.
Si, par exemple, après avoir créé la classe Personne, on décide que le prénom doit toujours être précédée par la chaîne « M. », dans le premier exemple de notre overloading, on devrait changer le code en deux parties différentes.
Dans le cas où on prévoit plusieurs constructeurs, le nombre de lignes de code à modifier peut être plus grand et, par conséquent, augmente la probabilité d’oublier certaines modifications.
Overloading des opérateurs
L’overloading des opérateurs, dérivé du C++, et permet de redéfinir le comportement des opérateurs tels que +, -, <,>, ==, etc., afin d’assumer des comportements différents en fonction des objets dont ils sont appliqués.
C# supporte l’overloading des opérateurs depuis sa première version, tandis que VB.NET a introduit cette fonctionnalité uniquement avec la version 2.0 du Framework.
Nous commençons toujours par la classe Personne. N’ayant aucune autre information, C# suppose que deux variables de type Personne sont égales si elles se réfèrent à l’instance du même objet, à savoir :
Personne p1 = new Personne(“Donald”, “Duck”); Personne p2 = new Personne(“Donald”, “Duck”); // Affiche true Console.WriteLine(p1 == p1); // Affiche False, les objets p1 et p2 sono différents Console.WriteLine(p1 == p2); // Maintenant p2 fait référence à la même instance de p1 p2 = p1; // Affiche true Console.WriteLine(p1 == p2);
Ce que nous aimerions, cependant, c’est que deux Personnes se ressemblent si leurs prénom et nom sont les mêmes (dans l’exemple ci-dessus, nous aimerions que la première comparaison ‘p1 == p2’ retourne vrai).
Pour ce faire, chaque fois que nous contrôlons les propriétés Prénom et Nom, on peut simplement redéfinir l’opérateur ‘==’ pour la classe Personne en créant une méthode avec le mot clé ‘operator‘ :
public static bool operator == (Personne p1, Personne p2) { if (p1.Prenom == p2.Prenom && p1.Nom == p2.Nom) return true; return false; } public static bool operator != (Personne p1, Personne p2) { if (p1.Prenom != p2.Prenom || p1.Nom != p2.Nom) return true; return false; }
Il est nécessaire de redéfinir les opérateurs qui établissent des relations similaires. Après avoir redéfini l’opérateur ‘==‘, le Framework nous oblige à redéfinir l’opérateur ‘! =‘ (Et vice versa), comme si nous avions redéfini < nous aurions dû aussi définir > (et vice versa).
L’overloading des opérateurs sont toujours définis par des méthodes statiques. Après avoir indiqué le type de données renvoyées (généralement bool ou le même type que la classe pour laquelle nous créons une surcharge, en fonction de l’opérateur en question), nous devons spécifier l’opérateur à redéfinir.
Dans la documentation en ligne nous trouvons la liste de tous les opérateurs qui peuvent être surchargés.
Enfin, nous devons indiquer à quels types de données s’appliquent : dans notre exemple, ‘==’ et ‘!=‘ s’appliquent aux objets de la classe Personne.
Les arguments de la méthode sont deux dans le cas des opérateurs binaires (comme montré dans notre exemple), ou un seul si l’opérateur est unaire (++, –, etc.).
Equals et GetHashCode
En redéfinissant les opérateurs ‘==‘ et ‘!=‘, le compilateur s’attend à ce que les méthodes Equals() et GetHashCode() soient redéfinies, car chaque classe a hérité de la classe base Object. Cependant, s’il n’est pas spécifié, vous recevrez seulement un avertissement de compilation. Définissons-les quand même, pour avoir une classe plus robuste :
public override bool Equals(object o)
{
if (o is Personne)
return this == (Personne) o;
return (object)this == o;
}
public override int GetHashCode()
{
// Cette routinedevrait réduire un code hash d'identification
// de l’instance courante de notre classe.
return 0;
}
Dans la méthode Equals(), l’opérateur “is” est utilisé pour vérifier que l’objet passé à la fonction est de type Personne : dans le cas d’un positif, l’objet est converti et le contrôle d’égalité est fait en utilisant notre définition de ‘==‘.
Sinon, l’objet courant est converti en objet, puis la comparaison standard est utilisée, de sorte que deux variables de type objet sont égales si elles se réfèrent à l’instance du même objet.
Grâce à cette redéfinition, nous pouvons maintenant atteindre le résultat souhaité :
Personne p1 = new Personne(“Donald”, “Duck”); Personne p2 = new Personne(“Donald”, “Duck”); Console.WriteLine(p1 == p2); // Affiche true Console.WriteLine(p1.Equals(p2)); // Affiche true
Précédent : 9. Polymorphisme Suivant : 11. Les classes abstraites
Étiquette :binaire, Equals, GetHashCode, opérateurs, Structure du langage, unaire