Découvrir l'implémentation explicite des interfaces en C# (partie 5),
Un tutoriel de François DORIN

Le , par François DORIN

0PARTAGES

Ce billet s'inscrit dans une série de billets autour de l'implémentation explicite des interfaces en C#.

Dans le premier billet, nous avons vu qu'il existe deux manières d'implémenter une interface en C#. De manière implicite (la voie "classique") et de manière explicite. Le billet était illustré par un exemple d'utilisation, avec la "surcharge" d'une méthode en modifiant uniquement son type de retour.

Dans le second billet, nous avons abordé un autre de ces cas d'usage : le masquage d'une méthode.

Dans le troisième billet, nous avons étudié une autre utilisation possible : implémenter des interfaces définissant les mêmes méthodes, mais avec des sémantiques différentes.

Dans ce quatrième billet, nous avons abordé les propriétés et les événements.

Dans ce 5e billet, qui a priori sera le dernier de la série, nous allons aborder les interactions entre implémentation explicite et polymorphisme.


Contexte
Prenons le code suivant, histoire d'illustrer ce billet :
Code C# : Sélectionner tout
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
using System; 
  
namespace InstanciationExplicite 
{ 
    public interface IHelloWorld 
    { 
        void SayHello(); 
    } 
  
    public class Mere : IHelloWorld 
    { 
        public virtual void SayHello() 
        { 
            Console.WriteLine("Mère"); 
        } 
    } 
  
    public class Fille: Mere, IHelloWorld 
    { 
        void IHelloWorld.SayHello() 
        { 
            Console.WriteLine("Fille"); 
        } 
  
    } 
  
    public class PetiteFille: Fille 
    { 
        public override void SayHello() 
        { 
            Console.WriteLine("Petite fille"); 
        } 
    } 
  
    class Program 
    { 
        static void Main(string[] args) 
        { 
            PetiteFille petiteFille = new PetiteFille(); 
            Fille fille = petiteFille; 
            Mere mere = fille; 
            IHelloWorld hello = mere; 
  
            petiteFille.SayHello(); 
            fille.SayHello(); 
            mere.SayHello(); 
            hello.SayHello(); 
        } 
    } 
}

Rien de bien méchant ici. On définie une interface qui est implémentée par la classe Mere. La classe Fille dérive de la classe Mere, et la classe PetiteFille de la classe Fille.

La classe Mere implémente l'interface IHelloWorld de manière implicite, et définie la méthode SayHello() comme étant virtuelle, afin de pouvoir la redéfinir au niveau des classes filles.

Notez également que la classe Fille implémente l'interface IHelloWorld de manière explicite.

Et maintenant, regardons ce que nous obtenons lorsque nous appelons la méthode SayHello().

Résultats
Essayer de deviner la sortie n'est pas forcément très trivial.
Petite fille
Petite fille
Petite fille
Fille
Les 3ères lignes ne présentent aucune surprise. Passons à la 4e. Contre toute attente, la valeur affichée est "Fille" et non "Petite fille" malgré le fait que la variable hello contienne une instance de la classe PetiteFille. Ici, nous constatons donc que l'implémentation explicite prime sur le polymorphisme ! Ainsi, la présence d'une implémentation explicite (et ce quel que soit le niveau dans la chaîne d'héritage) vient masquer le polymorphisme.

Et le pire dans tout cela, c'est qu'il n'y a aucun moyen de préciser le comportement souhaité.

Conclusion
Ainsi, nous constatons que l'instanciation explicite casse le polymorphisme, et surtout, qu'il est impossible d'empêcher cela. Il faut donc faire attention à ne pas se retrouver dans cette situation, au risque d'avoir des comportements contre intuitifs.

Une erreur dans cette actualité ? Signalez-le nous !

Responsable bénévole de la rubrique Microsoft DotNET : Hinault Romaric -

Partenaire : Hébergement Web