1. Introduction
Si le mot redéfinition te fait un peu transpirer, t’inquiète — c’est pas si terrible, c’est même super pratique ! Redéfinir une méthode, c’est la possibilité de remplacer le comportement d’une méthode de la classe de base par le tien dans une classe dérivée. Grâce à ça, ton code devient flexible, évolutif et prêt pour la vraie vie, où chaque animal n’a pas envie d’être juste "un certain bruit".
Pour autoriser la redéfinition d’une méthode, la classe de base la marque avec le mot-clé virtual. La classe dérivée, pour remplacer l’implémentation, utilise le mot-clé override.
Exemple
public class Animal
{
public string Name { get; set; }
public virtual void MakeSound()
{
Console.WriteLine("Un son universel d’animal...");
}
}
Et maintenant dans la classe du chien :
public class Dog : Animal
{
public override void MakeSound()
{
Console.WriteLine("Ouaf-ouaf !");
}
}
- Dans la classe de base — virtual.
- Dans la classe dérivée — override.
- La signature de la méthode (nom, type de retour, paramètres) doit être identique.
Schéma visuel
2. Démonstration du fonctionnement
Voyons comment la redéfinition marche en vrai. On crée des animaux et on teste le son :
Animal pet1 = new Animal { Name = "Animal sans nom" };
Dog pet2 = new Dog { Name = "Barbos" };
pet1.MakeSound(); // Affichera : Un son universel d’animal...
pet2.MakeSound(); // Affichera : Ouaf-ouaf !
Maintenant, on complique un peu :
Et si on stocke un chien dans une variable de type Animal ?
Animal pet3 = new Dog { Name = "Sharik" };
pet3.MakeSound(); // ???
À ton avis, qu’est-ce qui va se passer ?
Réponse : Ça affichera "Ouaf-ouaf !"
Parce que même si la variable est de type Animal, elle "pointe" sur un chien, donc c’est la version redéfinie de la méthode qui sera appelée !
Voilà la magie du binding dynamique (ou tardif).
3. Utilisation du mot-clé base lors de la redéfinition
Parfois, on n’a pas envie de remplacer complètement l’implémentation d’une méthode, mais plutôt de l’étendre — genre ajouter un truc à soi, puis exécuter aussi l’ancien comportement. Pour ça, on utilise le mot-clé base. Il permet d’appeler la version de la méthode de la classe de base.
public class Cat : Animal
{
public override void MakeSound()
{
base.MakeSound(); // appel de l’implémentation de base
Console.WriteLine("Miaou !");
}
}
Quand tu appelles cette méthode, d’abord ça écrit "Un son universel d’animal...", puis "Miaou !"
4. Comment le choix de la méthode fonctionne lors de la redéfinition
Pour bien piger ce qui se passe "sous le capot", imagine ce genre de tableau (dispatch virtuel) :
| Type de variable | Type d’objet | Quelle méthode sera appelée |
|---|---|---|
| Animal | Animal | Animal.MakeSound |
| Animal | Dog | Dog.MakeSound |
| Animal | Labrador | Labrador.MakeSound |
| Dog | Labrador | Labrador.MakeSound |
| Dog | Dog | Dog.MakeSound |
La règle principale :
Le type de la variable compte seulement pour le compilateur, mais à l’exécution c’est le type réel de l’objet (celui qu’on a créé avec new) qui décide.
Ce mécanisme s’appelle liaison dynamique (ou tardive) — c’est la base du polymorphisme (on en reparle dans la prochaine leçon !).
Pourquoi redéfinir des méthodes
- Dans les frameworks GUI : tu as une classe de base pour une fenêtre, et tu redéfinis les méthodes pour dessiner des éléments spécifiques.
- Dans les moteurs de jeu : classe de base Enemy, et les classes héritées font des comportements différents.
- Dans les tests unitaires : tu peux créer des "bouchons" (stubs, mocks) pour les méthodes.
Les frameworks modernes .NET utilisent à fond ce mécanisme pour les événements, le code template, l’héritage de configs et même la sérialisation d’objets (genre via des propriétés virtuelles).
5. Le mot-clé new pour masquer des méthodes
On a déjà vu que pour redéfinir des méthodes, il faut le duo virtual/override. Mais en C#, il y a aussi un autre modificateur lié aux méthodes dans une hiérarchie d’héritage — c’est new.
À quoi sert new ?
new s’utilise si, dans une classe dérivée, tu déclares une méthode avec la même signature que dans la classe de base, MAIS tu ne veux pas redéfinir une méthode virtuelle, tu veux juste masquer (cacher) la méthode de base.
- Ce n’est pas une redéfinition, c’est un masquage.
- Cette méthode est appelée selon le type de la variable, pas selon le type réel de l’objet (pas de polymorphisme dynamique !).
- Le compilateur te prévient si tu "masques" une méthode sans le mot-clé new.
Exemple : différence entre override et new
public class Animal
{
public virtual void MakeSound()
{
Console.WriteLine("L’animal fait un certain bruit...");
}
}
public class Dog : Animal
{
// On masque la méthode de la classe de base (PAS une redéfinition)
public new void MakeSound()
{
Console.WriteLine("Ceci n’est pas un override ! Juste la méthode du chien.");
}
}
Regardons le comportement :
Animal a = new Dog();
Dog d = new Dog();
a.MakeSound(); // "L’animal fait un certain bruit..."
d.MakeSound(); // "Ceci n’est pas un override ! Juste la méthode du chien."
- Si la variable est de type Dog — c’est la méthode de Dog qui est appelée.
- Si la variable est de type Animal, même si elle contient un Dog — c’est la méthode Animal qui est appelée !
6. Feedback et particularités d’implémentation
Quand on débute en prog, on se plante souvent en pensant qu’une méthode est "redéfinie", mais elle marche comme avant. La raison est simple : dans la classe de base, il n’y a pas virtual, ou dans la dérivée la méthode est déclarée avec new au lieu de override. Le deuxième cas est particulièrement traître — si tu appelles la méthode via une variable du type de base, c’est la version de base qui sera appelée, pas la redéfinie. Donc fais toujours gaffe à bien utiliser les bons mots-clés.
En plus des erreurs de syntaxe, parfois les débutants essaient de changer le type de retour lors de la redéfinition. Par exemple, faire une fonction de base qui retourne object, et dans la dérivée — string. Ça, c’est pas possible : la signature doit être strictement identique.
Tableau comparatif : override vs new
| Particularité | override | new |
|---|---|---|
| Mécanisme | Redéfinit une méthode virtuelle | Masque la méthode de la classe de base |
| Liaison tardive | Oui — fonctionne via le polymorphisme dynamique | Non — fonctionne selon le type de la variable |
| Exige que la base soit... | virtual, abstract ou déjà override | Non |
| Recommandé d’utiliser | Oui, presque toujours | Seulement dans des cas exceptionnels |
7. Fonctionnement des méthodes redéfinies avec les hiérarchies
Toute cette histoire de redéfinition devient vraiment fun quand on a de longues chaînes d’héritage :
public class Animal
{
public virtual void MakeSound()
{
Console.WriteLine("L’animal fait quelque chose...");
}
}
public class Dog : Animal
{
public override void MakeSound()
{
Console.WriteLine("Ouaf-ouaf !");
}
}
public class Labrador : Dog
{
public override void MakeSound()
{
Console.WriteLine("Je suis un labrador : waou-waou !");
}
}
Que se passe-t-il si on écrit :
Animal pet = new Labrador();
pet.MakeSound(); // => "Je suis un labrador : waou-waou !"
C# choisira toujours l’implémentation la plus "profonde" de la méthode virtuelle qui existe sur l’objet.
8. Erreurs typiques lors de la redéfinition de méthodes
Le monde n’est pas parfait, et les étudiants (et même les devs expérimentés !) font parfois des erreurs. Apprenons tout de suite à éviter les pièges les plus fréquents :
1. Oubli de virtual dans la classe de base
public class Animal
{
public void MakeSound() { ... } // Pas de 'virtual'
}
public class Dog : Animal
{
// Erreur de compilation ! Impossible de redéfinir.
public override void MakeSound()
{
Console.WriteLine("Ouaf !");
}
}
C# dira direct : 'Dog.MakeSound()': no suitable method found to override
2. Signatures différentes
Vérifie bien que le nom de la méthode, le type de retour et les paramètres sont identiques :
public class Animal
{
public virtual void MakeSound() { ... }
}
public class Dog : Animal
{
// Erreur : la signature est différente (par exemple, un paramètre ajouté)
public override void MakeSound(string sound)
{
Console.WriteLine(sound);
}
}
3. N’utilise pas new à la place de override sans raison valable
Le mot-clé new permet de masquer la méthode de la classe de base, mais ce n’est pas une redéfinition et ça ne marche pas avec le polymorphisme dynamique. C’est un autre mécanisme, à éviter sauf si tu sais vraiment ce que tu fais.
GO TO FULL VERSION