L'héritage en C sharp: Redéfinition d'une méthode ou d'une propriété + Le polymorphisme + Redéfinition et polymorphisme

 Dans l'exemple précédent, nous avons eu l'identité de la partie  Personne  de l'enseignant mais il manque certaines informations
propres à la classe Enseignant (la section). On est donc amené à écrire une propriété permettant d'identifier l'enseignant :
1. using System;
2.
3. namespace Chap2 {
4. class Enseignant : Personne {
5. // attributs
6. private int section;
7.
8. // constructeur
9. public Enseignant(string prenom, string nom, int age, int section)
10. : base(prenom, nom, age) {
11. // on mémorise la section via la propriété Section
12. Section = section;
13. // suivi
14. Console.WriteLine("Construction Enseignant(string, string, int, int)");
15. }//constructeur
16.
17. // propriété Section
18. public int Section {
19. get { return section; }
20. set { section = value; }
21. }// section
22.
23. // propriété Identite
24. public new string Identite {
25. get { return String.Format("Enseignant[{0},{1}]", base.Identite, Section); }
26. }
27. }
28.}
Lignes 24-26, la propriété Identite de la classe Enseignant s'appuie sur la propriété Identite de sa classe mère (base.Identite) (ligne 25) pour
afficher sa partie "Personne" puis complète avec le champ section qui est propre à la classe Enseignant. Notons la déclaration de la
propriété Identite :
public new string Identite{
Soit un objet enseignant E. Cet objet contient en son sein un objet Personne :
La propriété  Identite  est définie à la fois dans la classe  Enseignant  et sa classe mère Personne. Dans la classe fille Enseignant, la
propriété Identite doit être précédée du mot clé new pour indiquer qu'on redéfinit une nouvelle propriété Identite pour la classe
Enseignant.
public new string Identite{
La classe Enseignant dispose maintenant de deux propriétés Identite :
 celle héritée de la classe parent Personne
 la sienne propre
Si E est un ojet Enseignant, E.Identite désigne la propriété Identite de la classe Enseignant. On dit que la propriété Identite de la classe
fille redéfinit ou cache la propriété Identite de la classe mère. De façon générale, si O est un objet et M une méthode, pour exécuter
la méthode O.M, le système cherche une méthode M dans l'ordre suivant :
 dans la classe de l'objet O
 dans sa classe mère s'il en a une
 dans la classe mère de sa classe mère si elle existe
Enseignant
Identite
Personne
Identite
E

 etc…
L'héritage permet donc de redéfinir dans la classe fille des méthodes/propriétés de même nom dans la classe mère. C'est ce qui
permet d'adapter la classe fille à ses propres besoins. Associée au polymorphisme que nous allons voir un peu plus loin, la
redéfinition de méthodes/propriétés est le principal intérêt de l'héritage.
Considérons le même programme de test que précédemment :
1. using System;
2.
3. namespace Chap2 {
4. class Program {
5. static void Main(string[] args) {
6. Console.WriteLine(new Enseignant("Jean", "Dupont", 30, 27).Identite);
7. }
8. }
9. }
Les résultats obtenus sont cette fois les suivants :
1. Constructeur Personne(string, string, int)
2. Construction Enseignant(string, string, int, int)
3. Enseignant[[Jean, Dupont, 30],27]


2.2.4 Le polymorphisme
Considérons une lignée de classes : C0
 ← C
1
 ← C
2
 ← … ←Cn
où Ci
 ← C
j
 indique que la classe Cj
 est dérivée de la classe Ci
. Cela entraîne que la classe Cj
 a toutes les caractéristiques de la classe Ci
plus d'autres. Soient des objets Oi de type C
i
. Il est légal d'écrire :
Oi=Oj
 avec j>i
En effet, par héritage, la classe Cj a toutes les caractéristiques de la classe C
i plus d'autres. Donc un objet O
j de type C
j contient en
lui un objet de type C
i. L'opération
Oi=Oj
fait que O
i est une référence à l'objet de type C
i contenu dans l'objet O
j
.
Le fait qu'une variable Oi de classe Ci puisse en fait référencer non seulement un objet de la classe Ci mais en fait tout objet dérivé
de la classe Ci, est appelé polymorphisme : la faculté pour une variable de référencer différents types d'objets.
Prenons un exemple et considérons la fonction suivante indépendante de toute classe (static):
public static void Affiche(Personne p){
….
}
On pourra aussi bien écrire
Personne p;
...
Affiche(p);
que
Enseignant e;
...
Affiche(e);
Dans ce dernier cas, le paramètre formel p de type Personne de la méthode statique Affiche va recevoir une valeur de type Enseignant.
Comme le type Enseignant dérive du type Personne, c'est légal.


2.2.5 Redéfinition et polymorphisme
Complétons notre méthode Affiche :
1. public static void Affiche(Personne p) {
2. // affiche identité de p
3. Console.WriteLine(p.Identite);
4. }//affiche
La propriété p.Identite rend une chaîne de caractères identifiant l'objet Personne p. Que se passe-t-il dans l'exemple précédent si le
paramètre passé à la méthode Affiche est un objet de type Enseignant :
Enseignant e = new Enseignant(...);
Affiche(e);
 Regardons l'exemple suivant :
1. using System;
2.
3. namespace Chap2 {
4. class Program2 {
5. static void Main(string[] args) {
6. // un enseignant
7. Enseignant e = new Enseignant("Lucile", "Dumas", 56, 61);
8. Affiche(e);
9. // une personne
10. Personne p = new Personne("Jean", "Dupont", 30);
11. Affiche(p);
12. }
13.
14. // affiche
15. public static void Affiche(Personne p) {
16. // affiche identité de p
17. Console.WriteLine(p.Identite);
18. }//affiche
19. }
20.}
Les résultats obtenus sont les suivants :
1. Constructeur Personne(string, string, int)
2. Construction Enseignant(string, string, int, int)
3. [Lucile, Dumas, 56]
4. Constructeur Personne(string, string, int)
5. [Jean, Dupont, 30]
L'exécution montre que l'instruction p.Identite (ligne 17) a exécuté à chaque fois la propriété Identite d'une Personne, d'abord (ligne 7) la
personne contenue dans l'Enseignant e, puis (ligne 10) la Personne p elle-même. Elle ne s'est pas adaptée à l'objet réellement passé en
paramètre à Affiche. On aurait préféré avoir l'identité complète de l'Enseignant e. Il aurait fallu pour cela que la notation p.Identite
référence la propriété  Identite  de l'objet réellement pointé par  p  plutôt que la propriété  Identite  de partie "Personne" de l'objet
réellement par p.

Il est possible d'obtenir ce résultat en déclarant Identite comme une propriété virtuelle (virtual) dans la classe de base Personne :
1. public virtual string Identite {
2. get { return String.Format("[{0}, {1}, {2}]", prenom, nom, age); }
3. }
Le mot clé virtual fait de Identite une propriété virtuelle. Ce mot clé peut s'appliquer également aux méthodes. Les classes filles qui
redéfinissent une propriété ou méthode virtuelle doivent alors utiliser le mot clé  override  au lieu de  new  pour qualifier leur
propriété/méthode redéfinie. Ainsi dans la classe Enseignant, la propriété Identite est redéfinie comme suit :
1. public override string Identite {
2. get { return String.Format("Enseignant[{0},{1}]", base.Identite, Section); }
3. }
Le programme précédent produit alors les résultats suivants :
1. Constructeur Personne(string, string, int)
2. Construction Enseignant(string, string, int, int)
3. Enseignant[[Lucile, Dumas, 56],61]
4. Constructeur Personne(string, string, int)
5. [Jean, Dupont, 30]
Cette fois-ci, ligne 3, on a bien eu l'identité complète de l'enseignant. Redéfinissons maintenant une méthode plutôt qu'une
propriété. La classe object (alias C# de System.Object) est la classe "mère" de toutes les classes C#. Ainsi lorsqu'on écrit :
public class Personne
on écrit implicitement :
public class Personne : System.Object
La classe System.Object définit une méthode virtuelle ToString :

définit une méthode virtuelle ToString
La méthode ToString rend le nom de la classe à laquelle appartient l'objet comme le montre l'exemple suivant :
1. using System;
2.
3. namespace Chap2 {
4. class Program2 {
5. static void Main(string[] args) {
6. // un enseignant
7. Console.WriteLine(new Enseignant("Lucile", "Dumas", 56, 61).ToString());
8. // une personne
9. Console.WriteLine(new Personne("Jean", "Dupont", 30).ToString());
10. }
11. }
12.}
Les résultats produits sont les suivants :
1. Constructeur Personne(string, string, int)
2. Construction Enseignant(string, string, int, int)
3. Chap2.Enseignant
4. Constructeur Personne(string, string, int)
5. Chap2.Personne
On remarquera que bien que nous n'ayons pas redéfini la méthode ToString dans les classes Personne et Enseignant, on peut cependant
constater que la méthode ToString de la classe Object a été capable d'afficher le nom réel de la classe de l'objet.
Redéfinissons la méthode ToString dans les classes Personne et Enseignant :
1. // méthode ToString
2. public override string ToString() {
3. return Identite;
4. }

 La définition est la même dans les deux classes. Considérons le programme de test suivant :
1. using System;
2. namespace Chap2 {
3. class Program3 {
4. public static void Main() {
5. // un enseignant
6. Enseignant e = new Enseignant("Lucile", "Dumas", 56, 61);
7. Affiche(e);
8. // une personne
9. Personne p = new Personne("Jean", "Dupont", 30);
10. Affiche(p);
11. }
12. // affiche
13. public static void Affiche(Personne p) {
14. // affiche identité de p
15. Console.WriteLine(p);
16. }//Affiche
17. }
18.}
Attardons-nous sur la méthode Affiche qui admet pour paramètre une personne p. Ligne 15, la méthode WriteLine de la classe Console
n'a aucune variante admettant un paramètre de type Personne. Parmi les différentes variantes de Writeline, il en existe une qui admet
comme paramètre un type Object. Le compilateur va utiliser cette méthode, WriteLine(Object o), parce que cette signature signifie que
le paramètre o peut être de type Object ou dérivé. Puisque Object est la classe mère de toutes les classes, tout objet peut être passé en
paramètre à WriteLine et donc un objet de type Personne ou Enseignant. La méthode WriteLine(Object o) écrit o.ToString() dans le flux
d'écriture Out. La méthode ToString étant virtuelle, si l'objet o (de type Object ou dérivé) a redéfini la méthode ToString, ce sera cette
dernière qui sera utilisée. C'est ici le cas avec les classes Personne et Enseignant.
C'est ce que montrent les résultats d'exécution :

1. Constructeur Personne(string, string, int)
2. Construction Enseignant(string, string, int, int)
3. Enseignant[[Lucile, Dumas, 56],61]
4. Constructeur Personne(string, string, int)
5. [Jean, Dupont, 30]


Publié par Drupal french Study