• Aucun résultat trouvé

Les interfaces

Dans le document Télécharger cours de C# en pdf (Page 58-61)

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.

Voici par exemple la définition de l'interface System.Collections.Ienumerator :

// from module 'c:\winnt\microsoft.net\framework\v1.0.2914\mscorlib.dll'

public interface System.Collections.IEnumerator {

// Properties

object Current { get; } // Methods

bool MoveNext(); void Reset();

} // end of System.Collections.IEnumerator

Toute classe implémentant cette interface sera déclarée comme public class C : IEnumerator{

...

object Current{ get {...}} bool MoveNext{...} void Reset(){...} }

La propriété Current et les méthodes MoveNext et Reset devront être définies dans la classe C. Considérons le code suivant :

using System;

public struct élève{

public string nom;

public double note;

// constructeur

public élève(string NOM, double NOTE){ nom=NOM;

note=NOTE; }//constructeur

}//élève

public class notes{

// attribut

protected string matière;

protected élève[] élèves;

// constructeur

public notes (string MATIERE, élève[] ELEVES){ // mémorisation élèves & matière

matière=MATIERE; élèves=ELEVES; }//notes

// ToString

public override string ToString(){

string valeur="matière="+matière +", notes=("; int i;

// on concatène toutes les notes

for (i=0;i<élèves.Length-1;i++){ valeur+="["+élèves[i].nom+","+élèves[i].note+"],"; }; //dernière note if(élèves.Length!=0){ valeur+="["+élèves[i].nom+","+élèves[i].note+"]";} valeur+=")"; // fin return valeur; }//ToString }//classe

La classe notes rassemble les notes d'une classe dans une matière :

public class notes{

// attribut

protected string matière; protected élève[] élèves;

Classes, Structures, Interfaces 59

Les attributs sont déclarés protected pour être accessibles d'une classe dérivée. Le type élève est une structure mémorisant le nom de l'élève et sa note dans la matière :

public struct élève{ public string nom; public double note;

Nous décidons de dériver cette classe notes dans une classe notesStats qui aurait deux attributs supplémentaires, la moyenne et l'écart-type des notes :

public class notesStats : notes, Istats {

// attribut

private double _moyenne;

private double _écartType;

La classe notesStats implémente l'interface Istats suivante :

public interface Istats{ double moyenne(); double écartType(); }//

Cela signifie que la classe notesStats doit avoir deux méthodes appelées moyenne et écartType avec la signature indiquée dans l'interface Istats. La classe notesStats est la suivante :

public class notesStats : notes, Istats {

// attribut

private double _moyenne;

private double _écartType;

// constructeur

public notesStats (string MATIERE, élève[] ELEVES): base(MATIERE, ELEVES){ // calcul moyenne des notes

double somme=0;

for (int i=0;i<élèves.Length;i++){ somme+=élèves[i].note; } if(élèves.Length!=0) _moyenne=somme/élèves.Length; else _moyenne=-1; // écart-type double carrés=0;

for (int i=0;i<élèves.Length;i++){

carrés+=Math.Pow((élèves[i].note-_moyenne),2); }//for if(élèves.Length!=0) _écartType=Math.Sqrt(carrés/élèves.Length); else _écartType=-1; }//constructeur // ToString

public override string ToString(){

return base.ToString()+",moyenne="+_moyenne+",écart-type="+_écartType; }//ToString

// méthodes de l'interface Istats public double moyenne(){

// rend la moyenne des notes

return _moyenne; }//moyenne

public double écartType(){ // rend l'écart-type

return _écartType; }//écartType

}//classe

La moyenne _moyenne et l'écart-type _ecartType sont calculés dès la construction de l'objet. Aussi les méthodes moyenne et écartType n'ont-elles qu'à rendre la valeur des attributs _moyenne et _ecartType. Les deux méthodes rendent -1 si le tableau des élèves est vide. La classe de test suivante :

// classe de test

public class test{

public static void Main(){ // qqs élèves & notes

élève[] ELEVES=new élève[] { new élève("paul",14),new élève("nicole",16), new élève("jacques",18)}; // qu'on enregistre dans un objet notes

notes anglais=new notes("anglais",ELEVES); // et qu'on affiche

Console.Out.WriteLine(""+anglais); // idem avec moyenne et écart-type anglais=new notesStats("anglais",ELEVES); Console.Out.WriteLine(""+anglais); }

Classes, Structures, Interfaces 60 donne les résultats :

matière=anglais, notes=([paul,14],[nicole,16],[jacques,18])

matière=anglais, notes=([paul,14],[nicole,16],[jacques,18]),moyenne=16,écart-type=1,63299316185545

La classe notesStats aurait très bien pu implémenter les méthodes moyenne et écartType pour elle-même sans indiquer qu'elle implémentait l'interface Istats. Quel est donc l'intérêt des interfaces ? C'est le suivant : une fonction peut admettre pour paramètre une donnée ayant le type d'une interface I. Tout objet d'une classe C implémentant l'interface I pourra alors être paramètre de cette fonction. Considérons l'exemple suivant :

using System;

// une interface Iexemple public interface Iexemple{

int ajouter(int i,int j);

int soustraire(int i,int j); }

public class classe1: Iexemple{

public int ajouter(int a, int b){ return a+b+10;

}

public int soustraire(int a, int b){ return a-b+20;

} }//classe

public class classe2: Iexemple{

public int ajouter(int a, int b){ return a+b+100;

}

public int soustraire(int a, int b){ return a-b+200;

} }//classe

L'interface Iexemple définit deux méthodes ajouter et soustraire. Les classes classe1 et classe2 implémentent cette interface. On remarquera que ces classes ne font rien d'autre ceci par souci de simplification de l'exemple. Maintenant considérons l'exemple suivant :

// classe de test

public class test{

// une fonction statique

private static void calculer(int i, int j, Iexemple inter){ Console.Out.WriteLine(inter.ajouter(i,j));

Console.Out.WriteLine(inter.soustraire(i,j)); }//calculer

// la fonction Main

public static void Main(){

// création de deux objets classe1 et classe2 classe1 c1=new classe1();

classe2 c2=new classe2();

// appels de la fonction statique calculer calculer(4,3,c1);

calculer(14,13,c2); }//Main

}//classe test

La fonction statique calculer admet pour paramètre un élément de type Iexemple. Elle pourra donc recevoir pour ce paramètre aussi bien un objet de type classe1 que de type classe2. C'est ce qui est fait dans la fonction Main avec les résultats suivants :

17 21 127 201

On voit donc qu'on a là une propriété proche du polymorphisme vu pour les classes. Si donc 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ésentent un ensemble de méthodes de même signature, il peut être intéressant de regrouper ces méthodes dans une interface I dont hériteraient toutes les classes concernées. Des instances de ces classes Ci peuvent alors être utilisées comme paramètres de fonctions admettant un paramètre 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.

Notons enfin que l'héritage d'interfaces peut être multiple, c.a.d. qu'on peut écrire public class classeDérivée:classeDeBase,i1,i2,..,in{

Classes, Structures, Interfaces 61 }

où les ij sont des interfaces.

Dans le document Télécharger cours de C# en pdf (Page 58-61)