C# – Héritage et interfaces

Ceci est un exemple illustrant les interfaces et l'héritage en C#. Vous pourrez comparer par vous-mêmes avec des programmes équivalents dans les langages que vous connaissez.

L'interface que nous utiliserons ici se nomme Dessinable et expose un seul service, Dessiner(), qui prend en paramètre un flux sur lequel écrire (en fait, il s'agit plus d'une interface vers un objet capable de formater du texte en vue d'une projection sur un flux, mais bon).

Le type ConsoleTypique est un type valeur, un struct, qui serait placé sur la pile s'il était instancié; cela dit, ici, nous ne l'instancierons pas puisqu'il ne contient que des membres de classe. Notez que les constantes (const) d'une classe sont nécessairement des membres de classe (en Java et en C++, les constantes d'instance sont aussi possibles).

La classe Affichable gère la politique de validation d'un symbole affichable (on évite les blancs et les caractères de contrôles). Remarquez les services de System.Char qui sont utilisés pour mettre en place la politique de validité en question. La propriété Symbole d'instances d'Affichable est publique, donc accessible aux enfants comme aux clients.

Deux cas particulier, chacun à la fois Affichable et Dessinable, sont définis. L'un représente un Carré, l'autre représente un Triangle. L'héritage d'un parent et l'implémentation d'une interface sont exprimés par la même notation, mais C# exige que la classe parent, lorsqu'elle est indiquée, précède les interfaces. Ainsi, ici,

class Carré
   : Affichable, Dessinable 
{
   // ...
}

...est légal, mais

class Carré
   : Dessinable, Affichable 
{
   // ...
}

ne le serait pas.

Remarquez la syntaxe utilisée pour invoquer, dans le constructeur de ces classes, celui de leur parent (Affichable) : un peu comme les appels à super() en Java, le fait que seul l'héritage simple d'implémentation soit permis en C# a pour conséquence qu'un mot clé, base, puisse être utilisé de manière homogène pour référer au parent immédiat. La forme que prend l'appel au constructeur du parent s'apparente, elle, à celle qu'on trouve en C++.

Dans la classe Program, la méthode de classe Afficher() reçoit un Dessinable et invoque, par polymorphisme, la méthode Dessiner() d'un objet du type véritablement référé.

 

using System;

namespace ExempleInterfaces
{
   // exemple d'interface
   public interface Dessinable
   {
      void Dessiner(System.IO.TextWriter flux);
   }
   // exemple de type valeur (très, très simple)
   public struct ConsoleTypique
   {
      public const int LARGEUR = 80, HAUTEUR = 25;
      public static bool EstLargeurValide(int val)
      {
         return val >= 0 && val < LARGEUR;
      }
      public static bool EstHauteurValide(int val)
      {
         return val >= 0 && val < HAUTEUR;
      }
   }
   // exemple d'une classe de base simple
   public class Affichable
   {
      private char symbole;
      public char Symbole
      {
         get
         {
            return symbole;
         }
         set
         {
            if (System.Char.IsControl(value) ||
                System.Char.IsWhiteSpace(value))
               throw new Exception("symbole non affichable");
            symbole = value;
         }
      }
      public Affichable(char sym)
      {
         Symbole = sym;
      }
   }
   // exemple de classe implémentant une interface et dérivant d'un parent
   public class Carré
      : Affichable, Dessinable // héritage (classe avant interface)
   {
      private int taille;
      private static bool EstTailleValide(int n)
      {
         return n > 0 &&
                n < Math.Min(ConsoleTypique.LARGEUR, ConsoleTypique.HAUTEUR);
      }
      public Carré(int n, char symbole)
         : base(symbole) // constructeur du parent
      {
         if (!EstTailleValide(n))
            throw new Exception("taille hors des bornes");
         taille = n;
      }
      public void Dessiner(System.IO.TextWriter flux)
      {
         for (int i = 1; i <= taille_; ++i)
         {
            for (int j = 1; j <= taille_; ++j)
               flux.Write(Symbole);
            flux.WriteLine();
         }
      }
   }
   // autre exemple de classe implémentant une interface et dérivant d'un parent
   public class Triangle
      : Affichable, Dessinable // héritage (classe avant interface)
   {
      private int hauteur;
      private static bool EstHauteurValide(int n)
      {
         return n > 0 && ConsoleTypique.EstHauteurValide(n);
      }
      public Triangle(int n, char symbole)
         : base(symbole)
      {
         if (!EstHauteurValide(hauteur))
            throw new Exception("taille hors des bornes");
         hauteur = n;
      }
      public void dessiner(System.IO.TextWriter flux)
      {
         for (int i = 1; i <= hauteur; ++i)
         {
            for (int j = 1; j <= i; ++j)
               flux.Write(Symbole);
            flux.WriteLine();
         }
      }
   }
   class Program
   {
      private static void Afficher(Dessinable d)
      {
         System.IO.TextWriter scribe = new System.IO.StringWriter();
         d.Dessiner(scribe);
         Console.Write(scribe);
      }
      static void Main(string[] args)
      {
         Afficher(new Carré(5, '*'));
         Afficher(new Triangle(7, '#'));
      }
   }
}

Valid XHTML 1.0 Transitional

CSS Valide !