1. Introduction
Imagine, t'as une classe de base Animal avec une méthode Speak(). Par défaut, pour tous les animaux, elle affiche "Un certain son". Tu veux créer des classes héritées, genre Dog et Cat, pour qu'elles disent pas "Un certain son", mais leurs vraies phrases ("Ouaf!" et "Miaou!").
Tu pourrais écrire ta propre méthode Speak() dans chaque héritier, mais si tu bosses avec une collection du type Animal[], l'appel animal.Speak() va quand même taper sur la méthode de Animal, pas celle du chien ou du chat — parce que C# se base par défaut sur le type de la variable, pas sur l'objet réel derrière.
Petit exemple pour illustrer :
Animal dog = new Dog();
dog.Speak(); // Hop, qu'est-ce qui s'affiche ?
Par défaut — "Un certain son", pas "Ouaf!", même si dans Dog t'as ta propre méthode Speak(). Alors, comment faire ? Il faut déclarer la méthode comme virtual (dans la classe de base) et override (dans la dérivée).
C'est quoi une méthode virtuelle ?
Une méthode virtual — c'est une méthode déclarée dans la classe de base avec le modificateur virtual et qui est faite pour être overridée dans les héritiers ( override).Quand tu appelles une telle méthode via une référence à la classe de base, c'est la méthode de l'objet "réel" qui est appelée — comme dans la vraie vie : si t'as apprivoisé un animal, et que c'est un chat, il dira "Miaou!", pas "Un certain son".
Les méthodes virtuelles, c'est la base du polymorphisme en C#.
2. Déclaration des méthodes virtuelles
Comment déclarer une méthode virtuelle
Dans la classe de base, tu déclares la méthode avec le mot-clé virtual :
public class Animal
{
public virtual void Speak()
{
Console.WriteLine("Un certain son");
}
}
Dans l'héritier, tu utilises le mot-clé override :
public class Dog : Animal
{
public override void Speak()
{
Console.WriteLine("Ouaf!");
}
}
public class Cat : Animal
{
public override void Speak()
{
Console.WriteLine("Miaou!");
}
}
Exemple : démo du polymorphisme
// Par exemple, dans le fichier Program.cs
// Classe de base Animal avec la méthode virtuelle Speak
public class Animal
{
public virtual void Speak()
{
Console.WriteLine("Un certain son");
}
}
// Classe Dog, override le comportement de Speak
public class Dog : Animal
{
public override void Speak()
{
Console.WriteLine("Ouaf!");
}
}
// Classe Cat, override aussi Speak
public class Cat : Animal
{
public override void Speak()
{
Console.WriteLine("Miaou!");
}
}
public static class Program
{
public static void Main()
{
Animal[] animals = new Animal[]
{
new Animal(),
new Dog(),
new Cat()
};
foreach (Animal animal in animals)
{
animal.Speak(); // Affichera : "Un certain son", "Ouaf!", "Miaou!"
}
}
}
Tu vois la magie des méthodes virtuelles ? Même si la variable est du type Animal, c'est la version de la méthode de l'implémentation concrète qui est appelée.
3. Ce qui se passe "sous le capot" : la table virtuelle
En bref sur le mécanisme
Quand tu déclares une méthode comme virtuelle, le compilateur l'ajoute dans une "table virtuelle" spéciale de méthodes pour chaque type (c'est un peu comme un menu de cantine pour chaque plat). Quand tu appelles une telle méthode, C# regarde pas le type de la variable, mais ce qu'il y a vraiment derrière la référence — et il "sert" la méthode qui correspond au vrai type de l'objet.
On peut dire que C# te file la bonne recette à la place de la version générique, si la classe a sa propre version.
Analogie : contrat avec possibilité "d'ajouter une clause en petit"
Imagine que t'as un contrat de location (classe de base), et dedans c'est écrit que le locataire paie par défaut une somme fixe chaque mois (méthode PayRent()). Mais toi — propriétaire malin — tu autorises à ajouter des conditions spéciales en annexe (méthode virtuelle). Si un des locataires (héritier) utilise ce droit, il écrit son propre override, et maintenant le paiement se fait différemment.
4. Quand utiliser les méthodes virtuelles ?
- Si la méthode dans la classe de base doit marcher "par défaut" pour la plupart des héritiers, mais que certains peuvent avoir besoin d'un comportement différent.
- Quand tu construis une hiérarchie de classes où tu prévois d'étendre ou de modifier une partie de la logique.
- Quand tu développes une archi flexible, genre pour la logique métier, où différents types d'opérations peuvent demander un comportement spécifique.
Dans les vrais projets, les méthodes virtuelles — c'est la base des patterns Méthode Template (Template Method), Stratégie (Strategy), et franchement la moitié de la POO.
Comparaison avec les méthodes classiques
| Méthode classique | Méthode virtuelle | |
|---|---|---|
| Peut être overridée | Non | Oui (override) |
| Comment elle est appelée | Par le type de la variable | Par le "vrai" type de l'objet |
| Polymorphisme | Non | Oui |
FAQ
- Peut-on rendre un constructeur virtuel ?
Non ! Les constructeurs ne sont jamais virtuels — ils bossent toujours selon le type de l'objet. - Les méthodes virtuelles peuvent-elles être statiques ?
Non, seules les méthodes d'instance peuvent être virtuelles. Les méthodes statiques ne participent pas au polymorphisme, vu qu'il n'y a pas d'objet — qui va overrider sinon ? - Les champs peuvent-ils être virtuels ?
Non, seulement les méthodes, propriétés et événements.
5. Pratique : on fait évoluer l'appli "Monde animal"
Dans notre appli d'apprentissage, t'as déjà fait une petite hiérarchie d'animaux. On va ajouter de nouvelles méthodes virtuelles pour que chaque animal puisse manger à sa façon !
Exemple de code avec commentaires
// Dans la classe de base, on définit la méthode virtuelle Eat
public class Animal
{
public virtual void Speak()
{
Console.WriteLine("Un certain son");
}
public virtual void Eat()
{
Console.WriteLine("Mange de la nourriture générique.");
}
}
// Dans Dog, on override Eat
public class Dog : Animal
{
public override void Speak() => Console.WriteLine("Ouaf!");
public override void Eat() => Console.WriteLine("Mange de la nourriture pour chien.");
}
// Dans Cat, pareil, comportement à lui
public class Cat : Animal
{
public override void Speak() => Console.WriteLine("Miaou!");
public override void Eat() => Console.WriteLine("Mange du poisson.");
}
public static class Program
{
public static void Main()
{
Animal[] animals = new Animal[]
{
new Animal(),
new Dog(),
new Cat()
};
foreach (var animal in animals)
{
animal.Speak();
animal.Eat();
Console.WriteLine("---");
}
}
}
Voilà, maintenant chaque chat et chien mange à sa façon ! Et le Animal de base continue de manger un truc chelou, comme un vrai comportement par défaut.
6. Erreurs fréquentes et subtilités
- Tu peux oublier override dans l'héritier — et du coup la méthode de la classe de base reste utilisée.
- Ou alors, tu oublies virtual dans la classe de base, et là tu peux même pas écrire override dans l'héritier.
Impossible d'écrire override sans virtual
Le compilateur C# ne te laissera pas overrider une méthode qui n'a pas été déclarée comme virtuelle (ou abstraite) dans la classe de base.
Pourquoi on a besoin de override
override dit clairement au compilateur et à ceux qui lisent ton code : "Je ne fais pas juste une nouvelle méthode — je change exprès ce qui est défini dans la classe de base". Ça évite les bugs où tu crois "overrider", mais en fait tu crées juste une nouvelle méthode avec la même signature.
Si t'as oublié override et que t'as écrit une nouvelle méthode
public class Dog : Animal
{
public void Speak()
{
Console.WriteLine("Ouaf!");
}
}
Mais ça n'est pas un override ! Si tu utilises Dog via une variable de type Animal, c'est la méthode de l'animal ("Un certain son") qui sera appelée, pas celle du chien. Ah, ces surprises avec les types !
GO TO FULL VERSION