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