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

Le , par François DORIN, Responsable .NET & Magazine
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 ce troisième billet, nous allons étudier une autre utilisation possible : implémenter des interfaces définissant les mêmes méthodes, mais avec des sémantiques différentes.


Multiples interfaces... une méthode
Que se passe-t-il si une classe doit implémenter deux interfaces qui définissent la même méthode ?

Prenons l'exemple suivant :
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
public interface ITemperatureCelsius 
{ 
   float GetTemperature(); 
} 
  
public interface ITemperatureKelvin 
{ 
   float GetTemperature(); 
} 
  
public interface ITemperatureFarhenheit 
{ 
   float GetTemperature(); 
} 
  
public class Temperature : ITemperatureCelsius, ITemperatureKelvin, ITemperatureFarhenheit 
{ 
   private float _temperature; 
  
   public Temperature(float temperature) 
   { 
      _temperature= temperature; 
   } 
  
   public float GetTemperature() 
   { 
     return _temperature; 
   } 
}

Ce code compile correctement, mais est-il correct pour autant ? La réponse est non.

Non, car si la classe implémente bien les différentes interfaces, on constate que la même implémentation implicite est partagée par les 3 interfaces. Pourtant, nous savons bien qu'une température en degré Celsius, degré Kelvin ou degré Farenheit, même si elles sont liées, ne sont pas égales.

Comment faire donc pour résoudre ce cas ? Utilisons l'implémentation explicite des interfaces !

Rappel métrologique
Sans rentrer dans les détails (les pages Wikipédia consacré au degré Farhenheit et degré Kelvin feront très bien l'affaire), je rappelle juste ici les formules qui nous seront utiles pour la suite du billet :
  • Celsius vers Farhenheit : \[T_F = T_C \times 1.8 + 32\]
  • Farhenheit vers Celsius : \[T_C = \frac{T_F - 32}{1.8}\]
  • Celsius vers Kelvin : \[T_K = T_C - 273.15\]
  • Kelvin vers Celsius : \[T_C = T_K + 273.15\]


Implémentation explicite à la rescousse
Utilisons l'implémentation explicite pour implémenter chacune des interfaces. Au passage, rendons le constructeur privé, et définissons des méthodes statiques pour instancier des températures en utilisant le référentiel souhaité.

Nous obtenons donc :
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
public class Temperature : ITemperatureCelsius, ITemperatureKelvin, ITemperatureFarenheit 
{ 
   private float _temperature; // La température est stockée en Celsius 
  
   private Temperature(float temperature) 
   { 
      _temperature = temperature; 
   } 
  
   public static FromCelsius(float celsius) 
   { 
      return new Temperature(celsius); 
   } 
  
   public static FromKelvin(float kelvin) 
   { 
      return new Temperature(kelvin + 273.15); 
   } 
  
   public static FromFarhenheit(float farhenheit) 
   { 
      return new Temperature((farhenheit - 32) / 1.8); 
   } 
  
   float ITemperatureCelsius.GetTemperature() 
   { 
     return _temperature; 
   } 
  
   float ITemperatureKelvin.GetTemperature() 
   { 
      return _temperature - 273.15; 
   } 
  
   float ITemperatureFehrenheit.GetTemperature() 
   { 
      return _temperature * 1.8 + 32; 
   } 
}

Nous avons ainsi défini une classe permettant de faire facilement des conversions de température.
Son utilisation est des plus simples :
Code C# : Sélectionner tout
1
2
3
4
Temperature t = Temperature.FromCelsius(25); 
float t_C = (t as ITemperatureCelsius).GetTemperature(); 
float t_K = (t as ITemperatureKelvin).GetTemperature(); 
float t_F = (t as ITemperatureFahrenheit).GetTemperature();


Conclusions
Nous venons de voir un cas d'usage supplémentaire de l'instanciation explicite : le cas où plusieurs interfaces définissent la même méthode.

Il y a de nombreuses situations où cela peut servir. Dans un cas de conversion d'unité par exemple (comme l'exemple ci-dessus). Mais il peut arriver également que deux interfaces définissent la même méthode avec une sémantique totalement différente. L'instanciation explicite nous permet alors de répondre à cette problématique.


Vous avez aimé cette actualité ? Alors partagez-la avec vos amis en cliquant sur les boutons ci-dessous :
Responsables bénévoles de la rubrique Microsoft DotNET : Hinault Romaric - François DORIN -