Aller au contenu principal

Héritage

Notions théoriques

Qu'est-ce que l'héritage ?

L'héritage permet à une classe (sous-classe) de réutiliser et d'étendre les membres d'une autre classe (classe parente). En C#, on utilise : pour déclarer l'héritage.

class Personne
{
public string Prenom { get; set; } = "";
public string Nom { get; set; } = "";
public string NomComplet => $"{Prenom} {Nom}";

public void SePresenter()
=> Console.WriteLine($"Bonjour, je suis {NomComplet}.");
}

class Etudiant : Personne // hérite de Personne avec ':'
{
public double Note { get; set; }

public void AfficherNote()
=> Console.WriteLine($"{NomComplet} a obtenu {Note:F1}/20.");
}

var alice = new Etudiant { Prenom = "Alice", Nom = "Martin", Note = 15.5 };
alice.SePresenter(); // méthode héritée de Personne
alice.AfficherNote(); // méthode propre à Etudiant
C# n'a pas d'héritage multiple

En C#, une classe ne peut hériter que d'une seule classe parente. En revanche, elle peut implémenter plusieurs interfaces (vues dans la séance suivante).

base — accéder à la classe parente

base permet d'appeler le constructeur ou une méthode de la classe parente :

class Personne
{
public string Prenom { get; }
public string Nom { get; }

public Personne(string prenom, string nom)
{
Prenom = prenom;
Nom = nom;
}
}

class Etudiant : Personne
{
public string Filiere { get; }

public Etudiant(string prenom, string nom, string filiere)
: base(prenom, nom) // appelle le constructeur de Personne
{
Filiere = filiere;
}
}

virtual et override — redéfinir une méthode

Pour qu'une sous-classe puisse redéfinir une méthode, la classe parente doit la déclarer virtual. La sous-classe utilise override :

class Animal
{
public string Nom { get; }
public Animal(string nom) { Nom = nom; }

public virtual string CriDe() => "..."; // virtual = peut être redéfini
}

class Chien : Animal
{
public Chien(string nom) : base(nom) { }
public override string CriDe() => "Woof !"; // override = redéfinition
}

class Chat : Animal
{
public Chat(string nom) : base(nom) { }
public override string CriDe() => "Miaou !";
}

Animal[] animaux = [new Chien("Rex"), new Chat("Mimi"), new Chien("Lassie")];
foreach (Animal a in animaux)
Console.WriteLine($"{a.Nom} : {a.CriDe()}");
virtual obligatoire en C# (différence avec Java)

En Java, toutes les méthodes sont virtual par défaut. En C#, une méthode doit être explicitement déclarée virtual pour être redéfinissable. Si vous oubliez virtual dans la classe parente, override provoque une erreur de compilation.

Appeler la méthode parente depuis override

Avec base.Methode(), on appelle la version parente et on ajoute des comportements :

class Vehicule
{
public string Marque { get; }
public Vehicule(string marque) { Marque = marque; }
public virtual string Demarrer() => $"{Marque} démarre.";
}

class VehiculeElectrique : Vehicule
{
public VehiculeElectrique(string marque) : base(marque) { }
public override string Demarrer()
=> base.Demarrer() + " (En silence — moteur électrique)";
}

sealed — empêcher l'héritage

sealed sur une classe empêche qu'on en hérite. Sur une méthode override, empêche de la redéfinir à nouveau.

sealed class BDDConfig // personne ne peut hériter de BDDConfig
{
public string Host { get; } = "localhost";
}

Exemple pratique

class Employe
{
public string Prenom { get; }
public string Nom { get; }
public decimal Salaire { get; protected set; }

public Employe(string prenom, string nom, decimal salaire)
{
Prenom = prenom;
Nom = nom;
Salaire = salaire;
}

public virtual decimal CalculerPrime() => Salaire * 0.05m;

public virtual string Afficher()
=> $"{Prenom} {Nom,-12} | Salaire : {Salaire,8:F0} € | Prime : {CalculerPrime(),6:F0} €";
}

class Developpeur : Employe
{
public string Langage { get; }

public Developpeur(string prenom, string nom, decimal salaire, string langage)
: base(prenom, nom, salaire) { Langage = langage; }

public override decimal CalculerPrime() => Salaire * 0.12m;
public override string Afficher() => base.Afficher() + $" | {Langage}";
}

class Manager : Employe
{
public int NbSubordonnes { get; }

public Manager(string prenom, string nom, decimal salaire, int nbSubordonnes)
: base(prenom, nom, salaire) { NbSubordonnes = nbSubordonnes; }

public override decimal CalculerPrime() => Salaire * 0.15m + NbSubordonnes * 100m;
public override string Afficher() => base.Afficher() + $" | {NbSubordonnes} subordonné(s)";
}

Employe[] equipe =
[
new Employe ("Alice", "Martin", 2800m),
new Developpeur("Bob", "Durand", 3500m, "C#"),
new Manager ("Clara", "Petit", 5000m, 8),
];

Console.WriteLine("=== Équipe ===");
foreach (Employe e in equipe)
Console.WriteLine(e.Afficher());

decimal masse = equipe.Sum(e => e.Salaire + e.CalculerPrime());
Console.WriteLine($"\nMasse salariale totale (avec primes) : {masse:F0} €");

Test de mémorisation/compréhension


Quel symbole déclare l'héritage en C# ?


Quelle est la différence avec Java concernant `virtual` ?


À quoi sert `: base(prenom, nom)` dans un constructeur de sous-classe ?


Que fait `sealed` sur une classe ?


Comment appelle-t-on la version parente d'une méthode depuis une méthode `override` ?


TP pour réfléchir et résoudre des problèmes

Vous allez créer une hiérarchie de formes géométriques.

Étape 1 — Classe parente Forme


Bonne pratique - Classe parente légère avec comportements par défaut sensés

La classe parente Forme retourne 0 pour Aire() et Perimetre(). Ce sont des valeurs par défaut sensées. Les sous-classes spécialisées redéfinissent ces méthodes avec les formules appropriées.

Étape 2 — Sous-classes Rectangle et Cercle


Bonne pratique - Un seul ToString() dans la parente

Grâce au polymorphisme, ToString() dans Forme appelle Aire() et Perimetre(). Quand on l'appelle sur un Rectangle, C# appelle automatiquement les versions override. On n'a pas besoin de redéfinir ToString() dans chaque sous-classe.

Étape 3 — Polymorphisme sur la liste de formes


Bonne pratique - Travailler avec le type parente, pas les types concrets

En déclarant Forme[] formes, on peut ajouter un Triangle ou un Hexagone plus tard sans modifier le code de traitement. C'est le principe ouvert/fermé (Open/Closed Principle) : ouvert à l'extension, fermé à la modification.

📌 Une solution