• 20 hours
  • Medium

Free online content available in this course.

course.header.alt.is_certifying

Got it!

Last updated on 3/10/17

Empêchez l’héritage et masquez des méthodes

Log in or subscribe for free to enjoy all this course has to offer!

Vous pensez tout savoir de la POO ? Que vous êtes naîf ^^ ...

Nous allons maintenant voir des instructions C# supplémentaires qui vont nous permettre de compléter nos connaissances en POO et faire des trucs un peu inattendus. Bref, de nouvelles choses, histoire d'être encore plus efficace avec le C#.

Ces nouveaux concepts vous serviront sans doute moins souvent, mais sont importants à connaître.

Empêcher une classe de pouvoir être héritée

Parmi ces nouveaux concepts, nous allons voir comment il est possible de faire en sorte qu'une classe ne puisse pas être héritée. Par exemple, avez-vous tenté de faire une classe qui dérive de la classe String... Eh oui, ce n'est pas possible :waw: ! 

C’est quoi ce mystère ? Nous, quand nous créons une classe, n’importe qui peut en hériter. Pourquoi pas la classe String ?

C’est parce que je ne vous ai pas encore parlé du mot-clé sealed. Il permet d’empêcher la classe d’être héritée. Tout simplement.

Pourquoi vouloir empêcher d’étendre une classe en la dérivant ?
Pour plusieurs raisons, mais généralement nous allons recourir à ce mot-clé lorsqu’une classe est trop spécifique à une méthode ou à une bibliothèque et que l’on souhaite empêcher quelqu’un de pouvoir obtenir du code instable en étendant son fonctionnement. C’est typiquement le cas pour la classe String. Il y a beaucoup de classes dans le framework .NET qui sont marquées comme sealed afin d’éviter que l’on puisse faire n’importe quoi.

Il faut par contre faire attention car l’emploi de ce mot-clé restreint énormément la façon dont on pourrait employer cette classe.
Pour déclarer une classe sealed (scellée en français) il suffit de précéder le mot-clé class du mot-clé sealed :

public sealed class ClasseImpossibleADeriver
{
}

Ainsi, toute tentative d’héritage :

public class MaClasse : ClasseImpossibleADeriver
{
}

se soldera par une erreur de compilation déshonorante :

impossible de dériver du type sealed 'MaPremiereApplication.ClasseImpossibleADeriver'

À noter que le mot-clé sealed fonctionne également pour les méthodes, dans ce cas, cela permet d’empêcher la substitution d’une méthode. Reprenons notre exemple du chien muet :

public class Chien
{
    public virtual void Aboyer()
    {
        Console.WriteLine("Wouf");
    }
}

public class ChienMuet : Chien
{
    public sealed override void Aboyer()
    {
        Console.WriteLine("...");
    }
}

Ici, nous marquons la méthode comme sealed, ce qui fait qu’une classe qui dérive de ChienMuet ne sera pas capable de remplacer la méthode permettant d’aboyer. Le code suivant :

public class ChienAvecSyntheseVocale : ChienMuet
{
    public override void Aboyer()
    {
        Console.WriteLine("Bwarf");
    }
}

entrainera l’erreur de compilation :

'MaPremiereApplication.ChienAvecSyntheseVocale.Aboyer()' : ne peut pas se substituer à un membre hérité 'MaPremiereApplication.ChienMuet.Aboyer()', car il est sealed

Voilà pour ce mot-clé qui peut paraître farfelu, à utiliser en connaissance de cause.

Masquer une méthode

Quand nous avons parlé de substitution, nous avons vu qu'on pouvait substituer une méthode grâce au mot-clé override. Cette méthode devait d'ailleurs s'être déclarée comme candidate à cette substitution grâce au mot-clé virtual.
Rappelez-vous de ce code :

public class Chien
{
    public virtual void Aboyer()
    {
        Console.WriteLine("Wouaf !");
    }
}

public class Caniche : Chien
{
    public override void Aboyer()
    {
        Console.WriteLine("Wiiff");
    }
}

Si nous instancions des chiens et des caniches de cette façon :

static void Main(string[] args)
{
    Chien chien = new Chien();
    Caniche caniche = new Caniche();
    Chien canicheTraiteCommeUnChien = new Caniche();

    chien.Aboyer();
    caniche.Aboyer();
    canicheTraiteCommeUnChien.Aboyer();
}

Nous aurons :

Wouaf !
Wiiff
Wiiff

En effet, le premier objet est un chien et les deux suivants sont des caniches. Grâce à la substitution, nous faisons aboyer notre chien, notre caniche et notre caniche qui se fait manipuler comme un chien.

Il est possible de masquer des méthodes qui ne sont pas forcément marquées comme virtuelles grâce au mot-clé new.
À ce moment-là, la méthode ne se substitue pas à la méthode dérivée mais la masque pour les types dérivés. Voyons cet exemple :

public class Chien
{
    public void Aboyer()
    {
        Console.WriteLine("Wouaf !");
    }
}

public class Caniche : Chien
{
    public new void Aboyer()
    {
        Console.WriteLine("Wiiff");
    }
}

Remarquons que la méthode Aboyer() de la classe Chien n’est pas marquée virtual et que la méthode Aboyer de la classe Caniche est marquée avec le mot-clé new. Il ne s’agit pas ici de substitution. Il s’agit juste de deux méthodes qui portent le même nom, mais la méthode Aboyer() de la classe Caniche se charge de masquer la méthode Aboyer() de la classe mère.
Ce qui fait que les précédentes instanciations :

static void Main(string[] args)
{
    Chien chien = new Chien();
    Caniche caniche = new Caniche();
    Chien canicheTraiteCommeUnChien = new Caniche();

    chien.Aboyer();
    caniche.Aboyer();
    canicheTraiteCommeUnChien.Aboyer();
}

produiront cette fois-ci :

Wouaf !
Wiiff
Wouaf !

C’est le dernier cas qui peut surprendre. Rappelez-vous, il ne s’agit pas d’un remplacement de méthode cette fois-ci, et donc le fait de manipuler le caniche en tant que Chien ne permet pas de remplacer le comportement de la méthode Aboyer(). Dans ce cas, on appelle bien la méthode Aboyer correspondant au type Chien, ce qui a pour effet d’afficher « Wouaf ! ».
Si nous n’avions pas utilisé le mot-clé new, nous aurions eu le même comportement mais le compilateur nous aurait avertis de ceci grâce à un avertissement :

'MaPremiereApplication.Caniche.Aboyer()' masque le membre hérité 'MaPremiereApplication.Chien.Aboyer()'. Utilisez le mot clé new si le masquage est intentionnel.</citation>

Il nous précise que nous masquons effectivement la méthode de la classe mère et que si c’est fait exprès, alors il faut l’indiquer grâce au mot-clé new afin de lui montrer que nous savons ce que nous faisons.
Notez quand même que dans la majorité des cas, en POO, ce que vous voudrez faire c’est bien substituer la méthode et non la masquer.

C’est le même principe pour les variables, il est également possible de masquer une variable, quoiqu’en général, cela reflète plutôt une erreur dans le code. Si nous faisons :

public class Chien
{
    protected int age;
}

public class Caniche : Chien
{
    private int age;

    public Caniche()
    {
        age = 0;
    }

    public override string ToString()
    {
        return "J'ai " + age + " ans";
    }
}

Alors le compilateur nous indique que la variable age de la classe Caniche masque celle dont nous avons hérité de la classe Chien. Si c’est intentionnel, il nous propose de la marquer avec le mot-clé new. Sinon, cela nous permet de constater que cette variable est peut-être inutile… (sûrement d’ailleurs !)

En résumé

  • Il est possible d'empêcher une classe d'être dérivée grâce au mot-clé sealed.

  • Pour masquer une méthode (ou une variable), on utilisera le mot-clé new  devant la méthode (ou la variable)

Example of certificate of achievement
Example of certificate of achievement