Contenu du cours
C# au-delà des Bases
C# au-delà des Bases
Polymorphisme
Le terme Polymorphisme signifie "se produire sous plusieurs formes différentes". Dans le contexte de la programmation orientée objet, le polymorphisme fait référence à la capacité d'une classe à fournir une interface commune pour ses classes dérivées tout en permettant à chaque classe dérivée d'implémenter des comportements spécifiques pour les méthodes héritées.
Normalement, lorsque nous créons un objet d'une classe, nous créons une variable pour contenir la référence de cet objet. Et le type de la variable est généralement la classe de cet objet.
Par exemple :
index
Dog myVar = new Dog(); // or even in case of implicit declaration, 'myVar' will be of type 'Dog' var myVar = new Dog();
Cependant, nous pouvons également créer une variable d'une classe de base et y stocker un objet d'une classe dérivée.
Par exemple :
index
using System; class Mammal { public int age; public Mammal(int age) { this.age = age; } } class Dog : Mammal { public string breed; public Dog(int age, string breed) : base(age) { this.breed = breed; } } class ConsoleApp { static void Main() { Mammal example = new Dog(10, "Labrador"); Console.WriteLine(example.age); Console.WriteLine(example.breed); // Error because it's not accessible (read explanation below) } }
L'attribut breed
de l'objet Dog n'est pas accessible car lorsque nous stockons l'objet Dog
dans une variable Mammal
, il est implicitement converti en Mammal
. Cela signifie que nous ne pouvons accéder qu'aux attributs et méthodes présents dans la classe Mammal
. Cela ne signifie pas que la valeur stockée dans la classe breed
est perdue, elle est simplement inaccessible et nous pouvons dire qu'elle est essentiellement cachée.
Nous pouvons simplement convertir example
en Dog
par un transtypage explicite et l'attribut breed
redeviendra disponible :
index
using System; class Mammal { public int age; public Mammal(int age) { this.age = age; } public void speak() { // Empty for now } } class Dog : Mammal { public string breed; public Dog(int age, string breed) : base(age) { this.breed = breed; } } class ConsoleApp { static void Main() { Mammal example = new Dog(10, "Labrador"); Console.WriteLine(example.age); Dog casted = (Dog)example; Console.WriteLine(casted.breed); } }
Cependant, pour les méthodes, il existe une fonctionnalité supplémentaire appelée surcharge de méthode, qui est quelque peu similaire à la surcharge de méthode, mais nous redéfinissons essentiellement une méthode d'une classe de base dans la classe dérivée.
Par exemple, nous voudrions que Mammal
ait une méthode speak()
de sorte qu'elle produise Woof!
si elle est appelée à partir d'un objet Dog
, et Meow!
si elle est appelée à partir d'un objet Cat
:
index
using System; class Mammal { public void speak() { Console.WriteLine("Mammal Speaks"); } } class Dog : Mammal { public void speak() { Console.WriteLine("Woof!"); } } class Cat : Mammal { public void speak() { Console.WriteLine("Meow!"); } } class ConsoleApp { static void Main() { // The following execute the overriden version of the methods Dog d1 = new Dog(); d1.speak(); Cat c1 = new Cat(); c1.speak(); Mammal m1 = new Mammal(); Mammal m2 = new Cat(); Mammal m3 = new Dog(); // All of these execute the 'Mammal' version of the method m1.speak(); m2.speak(); m3.speak(); } }
Les avertissements que nous recevons lors de cette compilation sont dus à des noms de méthodes qui se chevauchent dans les classes parent et enfant. Dans ce cas, il y a un chevauchement entre les méthodes speak
des classes Cat
et Mammal
, et aussi entre les classes Dog
et Mammal
, c'est pourquoi nous recevons deux avertissements.
Cependant, remarquez à la fin comment m1
, m2
et m3
exécutent la méthode définie dans la classe Mammal
, c'est parce qu'ils sont convertis en objets Mammal
.
Si nous voulons que la méthode speak
se comporte comme elle le fait dans son objet d'origine, nous pouvons simplement la convertir en une méthode virtuelle. Une méthode virtuelle est simplement une méthode qui nous permet de la remplacer à partir de classes dérivées de telle manière que l'implémentation spécifique exécutée est déterminée au moment de l'exécution en fonction du type de l'objet d'origine.
Pour rendre une méthode virtuelle, nous ajoutons simplement le mot-clé virtual
avant son type de retour :
index
class Mammal { public virtual void speak() { Console.WriteLine("Mammal Speaks"); } }
Pour faire une implémentation remplacée de cette méthode, nous utilisons le mot-clé override
avant le type de retour dans l'implémentation de cette nouvelle implémentation dans la classe dérivée :
index
class Dog : Mammal { public override void speak() { Console.WriteLine("Woof!"); } } class Cat : Mammal { public override void speak() { Console.WriteLine("Meow!"); } }
Mettons maintenant ensemble les deux extraits et voyons comment cela affecte la sortie :
index
using System; class Mammal { public virtual void speak() { Console.WriteLine("Mammal Speaks"); } } class Dog : Mammal { public override void speak() { Console.WriteLine("Woof!"); } } class Cat : Mammal { public override void speak() { Console.WriteLine("Meow!"); } } class ConsoleApp { static void Main() { Mammal m1 = new Mammal(); Mammal m2 = new Cat(); Mammal m3 = new Dog(); m1.speak(); // Since it stores a Mammal() object, it executes the Mammal's implementation m2.speak(); // Output: Meow! m3.speak(); // Output: Woof! // The following behave the same as they did in the previous example: Dog d1 = new Dog(); d1.speak(); Cat c1 = new Cat(); c1.speak(); } }
Vous voyez que dans la sortie, m2
et m3
utilisent l'implémentation de la méthode de Cat
et Dog
parce que ces objets étaient à l'origine Cat
et Dog
.
Ce processus entier de redéfinition des méthodes de sorte qu'elles se comportent en fonction de leur type d'origine est appelé polymorphisme.
Une situation où cela peut être très utile est de pouvoir stocker des objets de plusieurs types de données différents dans un seul tableau ou une seule liste.
Par exemple :
index
List<Shape> shapes = new List<Shape>(); shapes.Add(new Rectangle(25, 50)); shapes.Add(new Square(50)); shapes.Add(new Circle(10)); shapes[0].getArea(); // Output: 1250.0f shapes[1].getArea(); // Output: 2500.0f shapes[2].getArea(); // Output: 314.15f
1. Quels sont les deux mots-clés utilisés dans la redéfinition de méthode ?
2. Quelle est la bonne façon de rendre une méthode substituable ?
Merci pour vos commentaires !