Programmation C sharp/Héritage de classes

Une classe peut hériter d'une autre, c'est à dire posséder les mêmes méthodes et attributs que celle-ci et en avoir des supplémentaires. Cette nouvelle classe est appelée « classe fille » ou « sous-classe ». Elle hérite d'une « classe mère » ou « classe de base » ou « super-classe ».

Programmation C#
Programmation C#
Modifier ce modèle

L'héritage entre deux classes traduit la relation « est un type de ». Par exemple, une automobile est un type de véhicule. Elle possède la même fonctionnalité de transport que n'importe quel véhicule. En même temps, une automobile a des fonctionnalités plus précises qu'un véhicule : transport de personnes principalement, capacité de transport limitée, nombre de portes, puissance du moteur, ...

Ces spécificités de la sous-classe sont les seules qui ont besoin d'être définies dans celle-ci. Les autres fonctionnalités sont héritées de la super-classe.

Classe de base et sous-classe

modifier

La classe de base (ou classe mère) est celle qui définit les membres qui seront hérités par les sous-classes. Une sous-classe (ou classe fille) hérite des membres de la classe mère, peut ajouter de nouveaux membres, implémenter de nouvelles interfaces et redéfinir le comportement des méthodes de la classe de base.

Lorsqu'une classe ne déclare aucune classe de base, par défaut elle hérite de la classe System.Object. La classe System.Object est la seule qui n'hérite d'aucune autre classe.

Syntaxe

modifier

La relation d'héritage est définie à la déclaration de la classe par le symbole deux-points suivi du nom de la super-classe.

Exemple de super-classe :

public class Vehicule
{
    int roues, places, kilometrage;
    public Vehicule()
    {
    }
}

Exemple de sous-classe :

public class Automobile : Vehicule
{
    string couleur;
    public Automobile()
    {
    }
}

Constructeurs

modifier

Le constructeur de la sous-classe appelle toujours celui de la classe de base, implicitement ou explicitement. Si rien n'est spécifié, le compilateur génère un appel implicite au constructeur de la classe de base ne comportant aucun paramètre. C'est pourquoi ce constructeur est nommé « constructeur par défaut ».

Si la classe de base ne possède aucun constructeur par défaut, ou si un autre serait plus approprié, il est possible de spécifier explicitement le constructeur à appeler. Le mot-clé base désigne la classe de base.

Exemple :

public class Vehicule
{
    int roues, places, kilometrage;
    // Constructeur "protégé" : l'accès est limité
    // à cette classe et aux sous-classes
    protected Vehicule(int roues,int places)
    {
        this.roues = roues;
        this.places = places;
    }
}

public class Automobile : Vehicule
{
    string couleur;
    public Automobile(string couleur)
        : base( 4, 5 ) // appel au constructeur de Vehicule
                       // 4 roues et 5 places
    {
        this.couleur = couleur;
    }

    // Ajouter un constructeur par défaut n'est pas obligatoire
    // mais permet d'illustrer qu'il est possible d'appeler un
    // autre constructeur de la même classe
    public Automobile()
        : this( "indéfinie" ) // couleur indéfinie par défaut
    {
    }
}

Référence d'instance

modifier

Une référence à une instance de la sous-classe peut être stockée par une référence du même type :

Automobile voiture = new Automobile();

Il est également possible d'utiliser une référence du type de la classe de base :

Vehicule voiture = new Automobile();

Redéfinition de méthodes et de propriétés

modifier

Dans cette section, la redéfinition de propriétés est similaire à la redéfinition de méthodes. Ce paragraphe discutera de la redéfinition de méthodes, mais tout ceci s'applique également aux propriétés.

Redéfinition sans polymorphisme

modifier

La redéfinition d'une méthode consiste à créer une méthode dans une sous-classe ayant le même nom et les mêmes types d'arguments (la même signature) qu'une méthode de la classe de base, afin de modifier son comportement.

Exemple :

public class Vehicule
{
    private int poids;

    public Vehicule(int poids)
    { this.poids = poids; }

    public string Description()
    {
        return "Véhicule de "+poids+" tonnes";
    }
}
public class Automobile : Vehicule
{
    private string couleur;

    public Automobile(int poids,string couleur)
        : base(poids)
    { this.couleur = couleur; }

    // méthode redéfinie
    public string Description()
    {
        return base.Description()+" de couleur "+couleur;
    }
}

Cependant, le compilateur génère un avertissement (warning CS0108) dans la classe dérivée. Il faut spécifier que l'on redéfinit une méthode, en utilisant le mot clé new :

public class Automobile : Vehicule
{
    private string couleur;

    public Automobile(int poids,string couleur)
        : base(poids)
    { this.couleur = couleur; }

    // méthode redéfinie
    public new string Description()
    {
        return base.Description()+" de couleur "+couleur;
    }
}

Utilisation :

Automobile voiture = new Automobile(3, "rouge");
Console.WriteLine( voiture.Description() );
// affiche : Véhicule de 3 tonnes de couleur rouge

Par contre, si on utilise une référence à la classe de base Vehicule, la méthode appelée sera celle de la classe de base :

Vehicule vehicule = new Automobile(3, "rouge");
Console.WriteLine( vehicule.Description() );
// affiche : Véhicule de 3 tonnes

Une conversion de la référence vers la classe réelle de l'objet permet d'appeler la méthode surchargée :

Console.WriteLine( ((Automobile)vehicule).Description() );
// affiche : Véhicule de 3 tonnes de couleur rouge

Cet exemple n'utilise donc pas le polymorphisme, c'est à dire que pour appeler la méthode Description, le compilateur se base sur le type de la référence plutôt que sur le type réel de l'objet référencé.

Redéfinition avec polymorphisme

modifier

Le polymorphisme permet d'utiliser le type réel de l'objet référencé plutôt que le type de la référence pour déterminer la méthode, la propriété ou l'indexeur à utiliser. Pour cela, il faut utiliser le mot clé virtual dans la déclaration de la méthode de la classe de base.

Exemple :

public class Vehicule
{
    private int poids;

    public Vehicule(int poids)
    { this.poids = poids; }

    public virtual string Description()
    {
        return "Véhicule de "+poids+" tonnes";
    }
}

Et il faut utiliser le mot clé override dans la classe dérivée :

public class Automobile : Vehicule
{
    private string couleur;

    public Automobile(int poids,string couleur)
        : base(poids)
    { this.couleur = couleur; }

    // méthode redéfinie
    public override string Description()
    {
        return base.Description()+" de couleur "+couleur;
    }
}

Utilisation :

Vehicule vehicule = new Automobile(3, "rouge");
Console.WriteLine( vehicule.Description() );
// affiche : Véhicule de 3 tonnes de couleur rouge

Si le mot clé new est utilisé à la place du mot clé override, le polymorphisme n'est pas effectif, comme dans le paragraphe précédent.

Résumé

modifier

Pour résumer :

Méthode de la classe de base
normale virtual
Méthode redéfinie
dans la
Classe dérivée
Sans polymorphisme,
avertissement CS0108
Sans polymorphisme,
avertissement CS0114
new Sans polymorphisme Sans polymorphisme
override erreur CS0506 Avec polymorphisme


Classe sans héritière

modifier

Le mot-clé sealed empêche la création de classes dérivées. Cette fonctionnalité est également utile pour les classes ne déclarant que des membres statiques.

Syntaxe:

sealed class nom_classe

Exemple:

public sealed class Ferrari : Automobile
{
    private int consommation;
    public Ferrari(int litre_par_km)
        : base(5100, "Rouge")
    { consommation = litre_par_km; }
}

Ce type de classe ne peut avoir de méthodes abstraites (abstract) ou de membres protégés (protected) car aucune classe dérivée ne pourra être créée pour les implémenter / y accéder.