Polymorphisme : Compilation vs Exécution
Fait partie de la section Programmation Orientée Objet du Journey C# de Coddy — leçon 25 sur 70.
Le polymorphisme signifie « plusieurs formes » - la capacité pour le même code de se comporter différemment selon le contexte. C# prend en charge deux types distincts de polymorphisme, et comprendre quand chacun est résolu vous aide à écrire du code plus flexible.
Le polymorphisme à la compilation (également appelé polymorphisme statique) est résolu par le compilateur avant l'exécution de votre programme. La surcharge de méthodes en est le principal exemple : plusieurs méthodes partagent le même nom mais ont des paramètres différents :
public class Calculator
{
public int Add(int a, int b) => a + b;
public double Add(double a, double b) => a + b;
}
var calc = new Calculator();
calc.Add(5, 3); // Le compilateur choisit la version int
calc.Add(5.0, 3.0); // Le compilateur choisit la version doubleLe compilateur examine les types d'arguments et sélectionne la méthode correcte au moment de la compilation. Cette décision est fixée avant même que le programme ne s'exécute.
Le polymorphisme d'exécution (polymorphisme dynamique) est résolu pendant l'exécution du programme. Cela se produit avec les méthodes virtual et override - le type réel de l'objet détermine quelle méthode est exécutée :
Animal pet = new Dog(); // Déclaré comme Animal, en réalité un Dog
pet.Speak(); // Appelle le Speak() de Dog à l'exécutionLe compilateur ne sait pas quel Speak() s'exécutera - cette décision se prend à l'exécution en fonction du type réel de l'objet. C'est ce qui rend le polymorphisme basé sur l'héritage si puissant pour construire des systèmes flexibles et extensibles.
Défi
FacileConstruisons un système de conversion qui illustre les deux types de polymorphisme en action. Vous allez créer une classe qui utilise la surcharge de méthodes (polymorphisme au moment de la compilation) ainsi qu'une hiérarchie d'héritage qui utilise des méthodes virtuelles/redéfinies (polymorphisme au moment de l'exécution).
Vous organiserez votre code sur quatre fichiers :
Converter.cs: Définissez une classeConverterdans l'espace de nomsConversion. Cette classe illustre le polymorphisme au moment de la compilation par la surcharge de méthodes. Créez trois méthodesConvertsurchargées :Convert(int value)retourne"Integer: {value}"Convert(double value)retourne"Double: {value}"Convert(string value)retourne"String: {value}"
Shape.cs: Définissez une classe de baseShapedans l'espace de nomsConversion. Cette classe possède une propriétéName(string) et un constructeur qui la définit. Incluez une méthodevirtualappeléeDescribe()qui retourne"This is a {Name}".Circle.cs: Définissez une classeCircledans l'espace de nomsConversionqui hérite deShape. Ajoutez une propriétéRadius(double). Le constructeur accepte un rayon et passe"Circle"au constructeur de base. Redéfinissez (override)Describe()pour retourner"This is a Circle with radius {Radius}".Program.cs: Dans votre fichier principal, illustrez les deux types de polymorphisme. Tout d'abord, créez unConverteret appelez les trois méthodes surchargées avec les valeurs d'entrée. Ensuite, créez unCircle, stockez-le dans une variableShape, et appelezDescribe()pour montrer le polymorphisme au moment de l'exécution.
Vous recevrez quatre entrées :
- Une valeur entière (int)
- Une valeur décimale (double)
- Une valeur textuelle (string)
- Un rayon de cercle (double)
Affichez la sortie dans ce format :
Compile-time Polymorphism:
{Convert(int) result}
{Convert(double) result}
{Convert(string) result}
Runtime Polymorphism:
{Describe() result from Shape variable holding Circle}Par exemple, si les entrées sont 42, 3.14, Hello, et 5.5, la sortie devrait être :
Compile-time Polymorphism:
Integer: 42
Double: 3.14
String: Hello
Runtime Polymorphism:
This is a Circle with radius 5.5Remarquez la différence clé : le compilateur décide quelle méthode Convert appeler en fonction des types d'arguments au moment de la compilation, tandis que la méthode Describe() est résolue au moment de l'exécution en fonction du type réel de l'objet, même s'il est stocké dans une variable Shape !
Aide-mémoire
Le polymorphisme signifie « plusieurs formes » - la capacité pour le même code de se comporter différemment selon le contexte.
Le polymorphisme à la compilation (polymorphisme statique) est résolu par le compilateur avant l'exécution du programme. La surcharge de méthode en est le principal exemple :
public class Calculator
{
public int Add(int a, int b) => a + b;
public double Add(double a, double b) => a + b;
}
var calc = new Calculator();
calc.Add(5, 3); // Le compilateur choisit la version int
calc.Add(5.0, 3.0); // Le compilateur choisit la version doubleLe compilateur examine les types d'arguments et sélectionne la méthode correcte au moment de la compilation.
Le polymorphisme à l'exécution (polymorphisme dynamique) est résolu pendant l'exécution du programme à l'aide des méthodes virtual et override :
Animal pet = new Dog(); // Déclaré comme Animal, est en réalité un Dog
pet.Speak(); // Appelle Speak() de Dog à l'exécutionLe type réel de l'objet détermine quelle méthode s'exécute au moment de l'exécution, et non le type de variable déclaré.
Essayez vous-même
using System;
using Conversion;
class Program
{
public static void Main(string[] args)
{
// Lire les entrées
int intValue = Convert.ToInt32(Console.ReadLine());
double doubleValue = Convert.ToDouble(Console.ReadLine());
string stringValue = Console.ReadLine();
double radius = Convert.ToDouble(Console.ReadLine());
// TODO: Démontrer le polymorphisme à la compilation (Compile-time Polymorphism)
// Créer un objet Converter et appeler les trois méthodes Convert surchargées
Console.WriteLine("Compile-time Polymorphism:");
// Appeler Convert avec intValue, doubleValue, et stringValue
// TODO: Démontrer le polymorphisme au moment de l'exécution (Runtime Polymorphism)
// Créer un objet Circle et le stocker dans une variable Shape
// Ensuite, appeler Describe() sur la variable Shape
Console.WriteLine("Runtime Polymorphism:");
// Créer Circle et appeler Describe()
}
}
Cette leçon comprend un petit quiz. Commencez la leçon pour y répondre et suivre votre progression.
Toutes les leçons de Programmation Orientée Objet
1Fondamentaux de la POO
Fichiers externesEspaces de noms et directivesIntro aux classes et objetsLe mot-clé 'this'Méthodes et paramètresChamps vs PropriétésConstructeursInitialiseurs d'objetsRécapitulatif - Calculatrice simple4Héritage
Syntaxe de base de l'héritage (:)Le mot-clé 'base'Mots-clés Virtual & OverrideClasses scelléesLa classe de base 'object'Récapitulatif - Hiérarchie des employés7Fonctionnalités avancées
Surcharge d'opérateursIndexeurs (this[])Redéfinition de ToString()Méthodes d'extensionRécapitulatif - Liste personnalisée10Patrons de conception - Partie 1
Introduction aux patrons de conceptionSingleton Thread-SafePatron FabriquePatron Observateur (Événements)Patron Stratégie2Propriétés et membres statiques
Propriétés auto-implémentéesPropriétés en lecture/écriture seuleChamps et méthodes statiquesClasses statiquesMembres à corps d'expression5Polymorphisme & Interfaces
Polymorphisme : Compilation vs ExécutionInterface vs Classe AbstraiteInterfaces MultiplesInterfaces ExplicitesUpcasting & DowncastingRécapitulatif - Calculateur de Formes8Concepts avancés de la POO
Composition plutôt qu'héritageGénériques (Classes et Méthodes)Délégués et ÉvénementsAttributs et RéflexionIDisposable et l'instruction usingBases de l'Injection de Dépendances11Patrons de conception, Partie 2
Patron CommandePatron AdaptateurPatron DécorateurPatron Méthode TemplatePatron ÉtatPatron Composite3Architecture des classes
Données d'instance vs statiquesMots-clés 'readonly' & 'const'Champs de supportRécapitulatif - Gestionnaire de compte bancaire6Encapsulation
Modificateurs d'accèsPropriétés pour l'encapsulationImplémentation du masquage de donnéesPatterns d'immuabilitéRécapitulatif - Dossiers d'étudiants12Projet : Système de gestion de bibliothèque
Structure du projetModèles Livre et Utilisateur