Voir le flux RSS

François DORIN

[Actualité] C# : Implémentation explicite des interfaces

Note : 3 votes pour une moyenne de 5,00.
par , 03/08/2018 à 16h45 (1409 Affichages)
Nom : logo-csharp.png
Affichages : 4174
Taille : 50,9 Ko

Le billet du jour va nous permettre de découvrir et/ou d'approfondir un aspect souvent méconnu de C# : l'implémentation des interfaces. En effet, lors qu'une classe implémente une méthode d'une interface, elle peut le faire de deux manières différentes :
  • soit de manière implicite (la méthode "classique") ;
  • soit de manière explicite (la méthode "méconnue").


Outre les syntaxes différentes, dans ce billet, nous allons voir exactement les différences entre les deux, et un cas pratique de l'utilité de ces deux syntaxes.

Exemple d'interface : ICloneable
Tout au long de ce billet, nous allons utiliser l'interface ICloneable pour illustrer les propos, mais bien entendu, cela est généralisable à toutes les interfaces.

Cette interface est définie comme suit :
Code C# : Sélectionner tout - Visualiser dans une fenêtre à part
1
2
3
4
public interface ICloneable
{
	object Clone();
}

Exemple de classe : MonObjet
Maintenant, imaginons que nous ayons la classe suivante :
Code C# : Sélectionner tout - Visualiser dans une fenêtre à part
1
2
3
4
5
public class MonObjet
{
	public int ID {get;set;}
	public string Nom {get;set;}
}


Implémentation implicite
Nous souhaitons maintenant pouvoir dupliquer cet objet facilement. Une des méthodes utilisables est de créer un constructeur prenant en paramètre une instance de MonObjet, et qui permet d'initialiser la nouvelle instance à partir de l'instance passée en paramètre.

Une autre méthode est d'implémenter l'interface IClonable afin d'ajouter la méthode Clone() qui permettra de réaliser cette copie. Utilisons donc cette dernière méthode afin d'illustrer ce billet.

Faisons-le en utilisant l'implémentation implicite. Cela donne :

Code C# : Sélectionner tout - Visualiser dans une fenêtre à part
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
public class MonObjet : ICloneable
{
	public int ID {get;set;}
	public string Nom {get;set;}
 
	public object Clone()
	{
		MonObject clone = new MonObjet()
		{
			ID = this.ID,
			Nom = this.Nom
		};
 
		return clone;
	}
}

Voilà, rien de plus simple pour cet exemple. Maintenant, l'oeil averti remarque que la méthode Clone() renvoie une instance de type Object et non pas du type MonObjet. Aussi, si on veut utiliser la nouvelle instance il est nécessaire de la caster (par exemple, avec le mot-clé as).

On pourrait se dire que c'est un peu dommage. Ne serait-il pas possible de réaliser la même chose, mais en ayant le bon type en retour ?

De la modification du type de retour...

Si on essaie de le faire directement, c'est-à-dire ainsi :
Code C# : Sélectionner tout - Visualiser dans une fenêtre à part
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
public class MonObjet : ICloneable
{
	public int ID {get;set;}
	public string Nom {get;set;}
 
	public MonObjet Clone()
	{
		MonObject clone = new MonObjet()
		{
			ID = this.ID,
			Nom = this.Nom
		};
 
		return clone;
	}
}

Alors le compilateur va générer une erreur, précisant que la classe MonObjet n'implémente pas la méthode Clone() de l'interface ICloneable car le type de retour ne correspond pas à celui de l'interface.

Impossible non plus de surcharger la méthode :
Code C# : Sélectionner tout - Visualiser dans une fenêtre à part
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
public class MonObjet : ICloneable
{
	public int ID {get;set;}
	public string Nom {get;set;}
 
	public object Clone()
	{
		MonObject clone = new MonObjet()
		{
			ID = this.ID,
			Nom = this.Nom
		};
 
		return clone;
	}
 
	public MonObjet Clone()
	{
		MonObject clone = new MonObjet()
		{
			ID = this.ID,
			Nom = this.Nom
		};
 
		return clone;
	}
}

car les deux méthodes ne différeraient alors que par le type de retour. Or, le type de retour ne fait pas partie de la signature d'une méthode, et cela reviendrait donc à redéfinir la même méthode deux fois.

...à l'implémentation explicite

C'est ici que va intervenir l'implémentation explicite d'une méthode d'une interface. Si nous écrivons :
Code C# : Sélectionner tout - Visualiser dans une fenêtre à part
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
 
public class MonObjet : ICloneable
{
	public int ID {get;set;}
	public string Nom {get;set;}
 
	object ICloneable.Clone()
	{
		MonObject clone = new MonObjet()
		{
			ID = this.ID,
			Nom = this.Nom
		};
 
		return clone;
	}
 
	public MonObjet Clone()
	{
		MonObject clone = new MonObjet()
		{
			ID = this.ID,
			Nom = this.Nom
		};
 
		return clone;
	}
}

Le compilateur ne se plaint pas. Il accepte notre code. Si nous regardons bien la première méthode, on peut constater :
  • qu'elle ne précise pas la visibilité de la méthode (pas de public ou internal) ;
  • que le nom de la méthode est précédé par le nom de l'interface (ICloneable.Clone), d'où le nom d'explicite.


Qu'est-ce que cela signifie ? En fait, lorsqu'une méthode d'une interface est implémentée de manière explicite, cette méthode ne sera appelable que si la variable contenant l'objet est du type de l'interface. Si la variable est du type MonObjet, la méthode implémentée explicitement ne sera pas visible. Ainsi, si on a une variable de type MonObjet, l'appel à la méthode Clone() ne peut faire référence qu'à la méthode retournant un objet de type MonObjet :
Code C# : Sélectionner tout - Visualiser dans une fenêtre à part
1
2
3
4
5
6
MonObjet monObjet = new MonObjet();
ICloneable clonable = monObjet;
MonObjet clone;
 
clone = monObjet.Clone(); // OK, la méthode Clone de la classe est appelée car la méthode issue de l'interface est masquée
clone = clonable.Clone(); // Erreur de compilation ! La méthode Clone de l'interface retourne une instance de Object et non une instance de MonObjet

Si, par contre, on manipule l'objet via une variable de type ICloneable, alors la seule méthode Clone() disponible sera celle de l'interface.

Dans tous les cas, il n'y a donc plus d'ambiguïté !

Revenons sur l'absence d'indicateur de visibilité pour la méthode dans le cas d'une implémentation explicite. Il n'y en a pas besoin, car c'est... inutile ! Du fait que cette méthode n'est appelable que via une variable du type de l'interface, la méthode est forcément visible. C'est pourquoi il est inutile (et même impossible !) de préciser la visibilité.

Le mot de la fin
Il faut bien avoir en tête que le choix de l'implémentation implicite/explicite d'une interface se fait au niveau des méthodes, et non au niveau de l'interface elle-même. Cela implique donc qu'il est tout à fait possible d'implémenter certaines méthodes de manière implicite, et d'autres de manière explicite !

Nous verrons dans d'autres billets d'autre type d'usage des interfaces explicites.

Envoyer le billet « C# : Implémentation explicite des interfaces » dans le blog Viadeo Envoyer le billet « C# : Implémentation explicite des interfaces » dans le blog Twitter Envoyer le billet « C# : Implémentation explicite des interfaces » dans le blog Google Envoyer le billet « C# : Implémentation explicite des interfaces » dans le blog Facebook Envoyer le billet « C# : Implémentation explicite des interfaces » dans le blog Digg Envoyer le billet « C# : Implémentation explicite des interfaces » dans le blog Delicious Envoyer le billet « C# : Implémentation explicite des interfaces » dans le blog MySpace Envoyer le billet « C# : Implémentation explicite des interfaces » dans le blog Yahoo

Mis à jour 14/08/2018 à 22h00 par François DORIN

Catégories
DotNET , C#

Commentaires

  1. Avatar de Piraaate
    • |
    • permalink
    Je ne lis pas forcément en détail lorsque ces articles sont postés.
    Par contre je sais que, grâce aux auteurs qui postent sur Developpez, j'ai une source d'article sur beaucoup de concepts C# à ma disposition et je suis déjà venu piocher les infos lorsque c'était nécessaire.
    Donc, MERCI !
  2. Avatar de lamaison
    • |
    • permalink
    Grand merci pour cet article très clair et très agréable à lire.
    Cependant je bute sur l’explication finale (à partir de "Qu'est-ce que cela signifie ?[...]").
    Pourriez-vous détailler un peu plus le fonctionnement ? Je suis frustré de coincer sur la partie qui explicite le tout
  3. Avatar de François DORIN
    • |
    • permalink
    J'ai rajouté un petit morceau de code. Est-ce plus parlant ainsi ?
  4. Avatar de blbird
    • |
    • permalink
    En fait c'est une surcharge de classe, possible car la méthode réelle de l'interface est déclarée comme étant explicitement propre à l'interface?
  5. Avatar de François DORIN
    • |
    • permalink
    Non, on ne peut pas le considérer comme cela, puisque la "surcharge" n'est pas appelable directement depuis la classe. On pourrait plutôt le voir comme une redéfinition.