Les interfaces en C sharp : tout ce que vous voulez savoir a propos des interfaces au csharp

Une interface est un ensemble de prototypes de méthodes ou de propriétés qui forme un contrat. Une classe qui décide d'implémenter une interface s'engage à fournir une implémentation de toutes les méthodes définies dans l'interface. C'est le compilateur qui vérifie cette implémentation.

Les interfaces en C sharp
Les interfaces en C sharp




Voici par exemple la définition de l'interface System.Collections.IEnumerator :
public interface System.Collections.IEnumerator
{
    // Properties
    Object Current { get; }
    // Methods
    bool MoveNext();
    void Reset();
}
Les propriétés et méthodes de l'interface ne sont définies que par leurs signatures. Elles ne sont pas implémentées (n'ont pas de
code). Ce sont les classes qui implémentent l'interface qui donnent du code aux méthodes et propriétés de l'interface.
1. public class C : IEnumerator{
2. ...
3. Object Current{ get {...}}
4. bool MoveNext{...}
5. void Reset(){...}
6. }
• ligne 1 : la classe C implémente la classe IEnumerator. On notera que le signe : utilisé pour l'implémentation d'une interface
est le même que celui utilisé pour la dérivation d'une classe.
• lignes 3-5 : l'implémentation des méthodes et propriétés de l'interface IEnumerator.
Considérons l'interface suivante :
1. namespace Chap2 {
2. public interface IStats {
3. double Moyenne { get; }
3. double Moyenne { get; }
4. double EcartType();
5. }
6. }
L'interface IStats présente :
• une propriété en lecture seule Moyenne : pour calculer la moyenne d'une série de valeurs
• une méthode EcartType : pour en calculer l'écart-type
On notera qu'il n'est nulle part précisé de quelle série de valeurs il s'agit. Il peut s'agir de la moyenne des notes d'une classe, de la
moyenne mensuelle des ventes d'un produit particulier, de la température moyenne dans un lieu donné, ... C'est le principe des
interfaces : on suppose l'existence de méthodes dans l'objet mais pas celle de données particulières.
Une première classe d'implémentation de l'interface IStats pourrait une classe servant à mémoriser les notes des élèves d'une classe
dans une matière donnée. Un élève serait caractérisé par la structure Elève suivante :
1. public struct Elève {
2. public string Nom { get; set; }
3. public string Prénom { get; set; }
4. }//Elève
L'élève serait identifié par son nom et son prénom. Lignes 2-3, on trouve les propriétés automatiques pour ces deux attributs.
Une note serait caractérisée par la structure Note suivante :
1. public struct Note {
2. public Elève Elève { get; set; }
3. public double Valeur { get; set; }
4. }//Note
La note serait identifiée par l'élève noté et la note elle-même. Lignes 2-3, on trouve les propriétés automatiques pour ces deux
attributs.
Les notes de tous les élèves dans une matière donnée sont rassemblées dans la classe TableauDeNotes suivante :
1. using System;
2. using System.Text;
3.
4. namespace Chap2 {
5.
6. public class TableauDeNotes : IStats {
7. // attributs
8. public string Matière { get; set; }
9. public Note[] Notes { get; set; }
10. public double Moyenne { get; private set; }
11. private double ecartType;
12.
13. // constructeur
14. public TableauDeNotes(string matière, Note[] notes) {
15. // mémorisation via les propriétés publiques
16. Matière = matière;
17. Notes = notes;
18. // calcul de la moyenne des notes
19. double somme = 0;
20. for (int i = 0; i < Notes.Length; i++) {
21. somme += Notes[i].Valeur;
22. }
23. if (Notes.Length != 0) Moyenne = somme / Notes.Length;
24. else Moyenne = -1;
25. // écart-type
26. double carrés = 0;
27. for (int i = 0; i < Notes.Length; i++) {
28. carrés += Math.Pow((Notes[i].Valeur - Moyenne), 2);
29. }//for
30. if (Notes.Length != 0)
31. ecartType = Math.Sqrt(carrés / Notes.Length);
32. else ecartType = -1;
33. }//constructeur
34.
35. public double EcartType() {
36. return ecartType;
37. }
38.
39. // ToString
40. public override string ToString() {
41. StringBuilder valeur = new StringBuilder(String.Format("matière={0}, notes=(", Matière));
42. int i;
43. // on concatène toutes les notes
44. for (i = 0; i < Notes.Length-1; i++) {
45.valeur.Append("[").Append(Notes[i].Elève.Prénom).Append(",").Append(Notes[i].Elève.Nom).Append(","
).Append(Notes[i].Valeur).Append("],");
46. };
47. //dernière note
48. if (Notes.Length != 0) {
49.valeur.Append("[").Append(Notes[i].Elève.Prénom).Append(",").Append(Notes[i].Elève.Nom).Append(","
).Append(Notes[i].Valeur).Append("]");
50. }
51. valeur.Append(")");
52. // fin
53. return valeur.ToString();
54. }//ToString
55.
56. }//classe
57.}
• ligne 6 : la classe  TableauDeNotes  implémente l'interface  IStats. Elle doit donc implémenter la propriété  Moyenne  et la
méthode EcartType. Celles-ci sont implémentées lignes 10 (Moyenne) et 35-37 (EcartType)
• lignes 8-10 : trois propriétés automatiques
• ligne 8 : la matière dont l'objet mémorise les notes
• ligne 9 : le tableau des notes des élèves (Elève, Note)
• ligne 10 : la moyenne des notes - propriété implémentant la propriété Moyenne de l'interface IStats.
• ligne 11 : champ mémorisant l'écart-type des notes - la méthode get associée EcartType des lignes 35-37 implémente la
méthode EcartType de l'interface IStats.
• ligne 9 : les notes sont mémorisées dans un tableau. Celui-ci est transmis lors de la construction de la classe TableauDeNotes
au constructeur des lignes 14-33.
• lignes 14-33 : le constructeur. On suppose ici que les notes transmises au constructeur ne bougeront plus par la suite.
Aussi utilise-t-on le constructeur pour calculer tout de suite la moyenne et l'écart-type de ces notes et les mémoriser dans
les champs des lignes 10-11. La moyenne est mémorisée dans le champ privé sous-jacent à la propriété automatique
Moyenne de la ligne 10 et l'écart-type dans le champ privé de la ligne 11.
• ligne 10 : la méthode get de la propriété automatique Moyenne rendra le champ privé sous-jacent.
• lignes 35-37 : la méthode EcartType rend la valeur du champ privé de la ligne 11.
Il y a quelques subtilités dans ce code :
• ligne 23 : la méthode set de la propriété Moyenne est utilisée pour faire l'affectation. Cette méthode a été déclarée privée
ligne 10 afin que l'affectation d'une valeur à la propriété Moyenne ne soit possible qu'à l'intérieur de la classe.
• lignes 40-54 : utilisent un objet StringBuilder pour construire la chaîne représentant l'objet TableauDeNotes afin d'améliorer
les performances. On peut noter que la lisibilité du code en pâtit beaucoup. C'est le revers de la médaille.
Dans la classe précédente, les notes étaient enregistrées dans un tableau. Il n'était pas possible d'ajouter une nouvelle note après
construction de l'objet  TableauDeNotes. Nous proposons maintenant une seconde implémentation de l'interface  IStats, appelée
ListeDeNotes, où cette fois les notes seraient enregistrées dans une liste, avec possibilité d'ajouter des notes après construction initiale
de l'objet ListeDeNotes.
Le code de la classe ListeDeNotes est le suivant :
1. using System;
2. using System.Text;
3. using System.Collections.Generic;
4.
5. namespace Chap2 {
6.
7. public class ListeDeNotes : IStats {
8. // attributs
9. public string Matière { get; set; }
10. public List<Note> Notes { get; set; }
11. public double moyenne = -1;
12. public double ecartType = -1;
13.
14. // constructeur
15. public ListeDeNotes(string matière, List<Note> notes) {
16. // mémorisation via les propriétés publiques
17. Matière = matière;
18. Notes = notes;
19. }//constructeur
20.
21. // ajout d'une note
22. public void Ajouter(Note note) {
23. // ajout de la note
24. Notes.Add(note);
25. // moyenne et écart type réinitialisés
26. moyenne = -1;
27. ecartType = -1;
28. }
29.
30. // ToString
31. public override string ToString() {
32. StringBuilder valeur = new StringBuilder(String.Format("matière={0}, notes=(", Matière));
33. int i;
34. // on concatène toutes les notes

35. for (i = 0; i < Notes.Count - 1; i++) {
36.valeur.Append("[").Append(Notes[i].Elève.Prénom).Append(",").Append(Notes[i].Elève.Nom).Append(","
).Append(Notes[i].Valeur).Append("],");
37. };
38. //dernière note
39. if (Notes.Count != 0) {
40.valeur.Append("[").Append(Notes[i].Elève.Prénom).Append(",").Append(Notes[i].Elève.Nom).Append(","
).Append(Notes[i].Valeur).Append("]");
41. }
42. valeur.Append(")");
43. // fin
44. return valeur.ToString();
45. }//ToString
46.
47. // moyenne des notes
48. public double Moyenne {
49. get {
50. if (moyenne != -1) return moyenne;
51. // calcul de la moyenne des notes
52. double somme = 0;
53. for (int i = 0; i < Notes.Count; i++) {
54. somme += Notes[i].Valeur;
55. }
56. // on rend la moyenne
57. if (Notes.Count != 0) moyenne = somme / Notes.Count;
58. return moyenne;
59. }
60. }
61.
62. public double EcartType() {
63. // écart-type
64. if (ecartType != -1) return ecartType;
65. // moyenne
66. double moyenne = Moyenne;
67. double carrés = 0;
68. for (int i = 0; i < Notes.Count; i++) {
 69. carrés += Math.Pow((Notes[i].Valeur - moyenne), 2);
70. }//for
71. // on rend l'écart type
72. if (Notes.Count != 0)
73. ecartType = Math.Sqrt(carrés / Notes.Count);
74. return ecartType;
75. }
76. }//classe
77.}
• ligne 7 : la classe ListeDeNotes implémente l'interface IStats
• ligne 10 : les notes sont mises maintenant dans une liste plutôt qu'un tableau
• ligne 11 : la propriété automatique Moyenne de la classe TableauDeNotes a été abandonnée ici au profit d'un champ privé
moyenne, ligne 11, associé à la propriété publique en lecture seule Moyenne des lignes 48-60
• lignes 22-28 : on peut désormais ajouter une note à celles déjà mémorisées, ce qu'on ne pouvait pas faire précédemment.
• lignes 15-19 : du coup, la moyenne et l'écart-type ne sont plus calculés dans le constructeur mais dans les méthodes de
l'interface elles-mêmes : Moyenne (lignes 48-60) et EcartType (62-76). Le recalcul n'est cependant relancé que si la moyenne
et l'écart-type sont différents de -1 (lignes 50 et 64).
Une classe de test pourrait être la suivante :
1. using System;
2. using System.Collections.Generic;
3.
4. namespace Chap2 {
5. class Program1 {
6. static void Main(string[] args) {
7. // qqs élèves & notes d'anglais
8. Elève[] élèves1 =  { new Elève { Prénom = "Paul", Nom = "Martin" }, new Elève { Prénom =
"Maxime", Nom = "Germain" }, new Elève { Prénom = "Berthine", Nom = "Samin" } };
9. Note[] notes1 = { new Note { Elève = élèves1[0], Valeur = 14 }, new Note { Elève =
élèves1[1], Valeur = 16 }, new Note { Elève = élèves1[2], Valeur = 18 } };
10. // qu'on enregistre dans un objet TableauDeNotes
11. TableauDeNotes anglais = new TableauDeNotes("anglais", notes1);
12. // affichage moyenne et écart-type
13. Console.WriteLine("{2}, Moyenne={0}, Ecart-type={1}", anglais.Moyenne, anglais.EcartType(),
anglais);
14. // on met les élèves et la matière dans un objet ListeDeNotes
 15. ListeDeNotes français = new ListeDeNotes("français", new List<Note>(notes1));
16. // affichage moyenne et écart-type
17. Console.WriteLine("{2}, Moyenne={0}, Ecart-type={1}", français.Moyenne,
français.EcartType(), français);
18. // on rajoute une note
19. français.Ajouter(new Note { Elève = new Elève { Prénom = "Jérôme", Nom = "Jaric" }, Valeur
= 10 });
20. // affichage moyenne et écart-type
21. Console.WriteLine("{2}, Moyenne={0}, Ecart-type={1}", français.Moyenne,
français.EcartType(), français);
22. }
23. }
24.}
• ligne 8 : création d'un tableau d'élèves avec utilisation du constructeur sans paramètres et initialisation via les propriétés
publiques
• ligne 9 : création d'un tableau de notes selon la même technique
• ligne 11 : un objet TableauDeNotes dont on calcule la moyenne et l'écart-type ligne 13
• ligne 15 : un objet ListeDeNotes dont on calcule la moyenne et l'écart-type ligne 17. La classe List<Note> a un constructeur
admettant un objet implémentant l'interface IEnumerable<Note>. Le tableau notes1 implémente cette interface et peut être
utilisé pour construire l'objet List<Note>.
• ligne 19 : ajout d'une nouvelle note
• ligne 21 : recalcul de la moyenne et écart-type
Les résultats de l'exécution sont les suivants :

1. matière=anglais, notes=([Paul,Martin,14],[Maxime,Germain,16],[Berthine,Samin,18]), Moyenne=16,
Ecart-type=1,63299316185545
2. matière=français, notes=([Paul,Martin,14],[Maxime,Germain,16],[Berthine,Samin,18]), Moyenne=16,
Ecart-type=1,63299316185545
3. matière=français, notes=([Paul,Martin,14],[Maxime,Germain,16],[Berthine,Samin,18],
[Jérôme,Jaric,10]), Moyenne=14,5, Ecart-type=2,95803989154981
Dans l'exemple précédent, deux classes implémentent l'interface  IStats. Ceci dit, l'exemple ne fait pas apparaître l'intérêt de
l'interface IStats. Réécrivons le programme de test de la façon suivante :
1. using System;
2. using System.Collections.Generic;
3.
4. namespace Chap2 {
5. class Program2 {
6. static void Main(string[] args) {
7. // qqs élèves & notes d'anglais
8. Elève[] élèves1 =  { new Elève { Prénom = "Paul", Nom = "Martin" }, new Elève { Prénom =
"Maxime", Nom = "Germain" }, new Elève { Prénom = "Berthine", Nom = "Samin" } };
9. Note[] notes1 = { new Note { Elève = élèves1[0], Valeur = 14 }, new Note { Elève =
élèves1[1], Valeur = 16 }, new Note { Elève = élèves1[2], Valeur = 18 } };
10. // qu'on enregistre dans un objet TableauDeNotes
11. TableauDeNotes anglais = new TableauDeNotes("anglais", notes1);
12. // affichage moyenne et écart-type
13. AfficheStats(anglais);
14. // on met les élèves et la matière dans un objet ListeDeNotes
15. ListeDeNotes français = new ListeDeNotes("français", new List<Note>(notes1));
16. // affichage moyenne et écart-type
17. AfficheStats(français);
18. // on rajoute une note
19. français.Ajouter(new Note { Elève = new Elève { Prénom = "Jérôme", Nom = "Jaric" }, Valeur
= 10 });
20. // affichage moyenne et écart-type
21. AfficheStats(français);
22. }
23.
24. // affichage moyenne et écart-type d'un type IStats
25. static void AfficheStats(IStats valeurs) {
26. Console.WriteLine("{2}, Moyenne={0}, Ecart-type={1}", valeurs.Moyenne, valeurs.EcartType(),
valeurs);
27. }
28. }
29.}
• lignes 25-27 : la méthode statique AfficheStats reçoit pour paramètre un type IStats, donc un type Interface. Cela signifie
que le paramètre effectif peut être tout objet implémentant l'interface IStats. Quand on utilise une donnée ayant le  type d'une interface, cela signifie qu'on n'utilisera que les méthodes de l'interface implémentées par la donnée. On fait
abstraction du reste. On a là une propriété proche du polymorphisme vu pour les classes. Si un ensemble de classes Ci
non liées entre-elles par héritage (donc on ne peut utiliser le polymorphisme de l'héritage) présente un ensemble de
méthodes de même signature, il peut être intéressant de regrouper ces méthodes dans une interface I qu'implémenteraient
toutes les classes concernées. Des instances de ces classes Ci peuvent alors être utilisées comme paramètres effectifs de
fonctions admettant un paramètre formel de type I, c.a.d. des fonctions n'utilisant que les méthodes des objets Ci définies
dans l'interface I et non les attributs et méthodes particuliers des différentes classes Ci.
• ligne 13 : la méthode AfficheStats est appelée avec un type TableauDeNotes qui implémente l'interface IStats
• ligne 17 : idem avec un type ListeDeNotes
Les résultats de l'exécution sont identiques à ceux de la précédente.
Une variable peut être du type d'une interface. Ainsi, on peut écrire :
1. IStats stats1=new TableauDeNotes(...);
2. ...
3. stats1=new ListeDeNotes(...);
La déclaration de la ligne 1 indique que stats1 est l'instance d'une classe implémentant l'interface IStats. Cette déclaration
implique que le compilateur ne permettra l'accès dans stats1 qu'aux méthodes de l'interface : la propriété Moyenne et la méthode
EcartType.
Notons enfin que l'implémentation d'interfaces peut être multiple, c.a.d. qu'on peut écrire
public class ClasseDérivée:ClasseDeBase,I1,I2,..,In{
...
}
où les I
 sont des interfaces.

Publié par Drupal french Study