3.2. Propriétés et indexeurs en C#



Plan général:   ...........retour au plan général

1. Les propriétés

1.1 Définition et déclaration de propriété
1.2 Accesseurs de propriété
1.3 Détail et exemple de fonctionnement d'une propriété
             Exemple du fonctionnement
             Explication des actions
1.4 Les propriétés sont de classes ou d'instances
1.5 Les propriétés peuvent être masquées comme les méthodes
1.6 Les propriétés peuvent être virtuelles et redéfinies comme les méthodes
1.7 Les propriétés peuvent être abstraites comme les méthodes
1.8 Les propriétés peuvent être déclarées dans une interface
1.9 Exemple complet exécutable
            1.9.1 Détail du fonctionnement en écriture
            1.9.2 Détail du fonctionnement en lecture

2. Les indexeurs

    2.1 Définitions et comparaisons avec les propriétés
                2.1.1 Déclaration
                2.1.2 Utilisation
                2.1.3 Paramètres
                2.1.4 Liaison dynamique abstraction et interface
    2.2 Code C# complet compilable



1. Les propriétés

Les propriétés du langage C# sont très proches de celle du langage Delphi, mais elles sont plus complètes et restent cohérentes avec la notion de membre en C#.


1.1 Définition et déclaration de propriété

Définition d'une propriété

Une propriété définie dans une classe permet d'accéder à certaines informations contenues dans les objets instanciés à partir de cette classe. Une propriété a la même syntaxe de définition et d'utilisation que celle d'un champ d'objet (elle possède un type de déclaration), mais en fait elle invoque une ou deux méthodes internes pour fonctionner. Les méthodes internes sont déclarées à l'intérieur d'un bloc de définition de la propriété.


Déclaration d'une propriété propr1 de type int :
public int propr1 {
           //...... bloc de définition
}


Un champ n'est qu'un emplacement de stockage dont le contenu peut être consulté (lecture du contenu du champ) et modifié (écriture dans le champ), tandis qu'une propriété associe des actions spécifiques à la lecture ou à l'écriture ainsi que la modification des données que la propriété représente.






1.2 Accesseurs de propriété

En C#, une propriété fait systématiquement appel à une ou à deux méthodes internes dont les noms sont les mêmes pour toutes les propriétés afin de fonctionner soit en lecture, soit en écriture. On appelle ces méthodes internes des accesseurs; leur noms sont get et set , ci-dessous un exemple de lecture et d'écriture d'une propriété au moyen d'affectations :




Accesseur de lecture de la propriété :
Syntaxe :
   get
{  return ..... ;}

cet accesseur indique que la propriété est en lecture et doit renvoyer un résultat dont le type doit être le même que celui de la propriété. La propriété propr1 ci-dessous est déclarée en lecture seule et renvoie le contenu d'un champ de même type qu'elle :
private int champ;
public int propr1{
            get { return champ ; }
}


Accesseur d'écriture dans la propriété :
Syntaxe :
  set
{ ....}

cet accesseur indique que la propriété est en écriture et sert à initialiser ou à modifier la propriété. La propriété propr1 ci-dessous est déclarée en écriture seule et stocke une donnée de même type qu'elle dans la variable champ :
private int champ;
public int propr1{
            set { champ = value  ; }
}
Le mot clef value est une sorte de paramètre implicite interne à l'accesseur set, il contient la valeur effective qui est transmise à la propriété lors de l'accès en écriture.



D'un manière générale lorsqu'une propriété fonctionne à travers un attribut (du même type que la propriété), l'attribut contient la donnée brute à laquelle la propriété permet d'accéder.

Ci-dessous une déclaration d'une propriété en lecture et écriture avec attribut de stockage :

private int champ;

public int propr1{ 
         get
{
return champ ; }
         set { champ = value  ; }
}

Le mécanisme de fonctionnement est figuré ci-après :



Dans l'exemple précédent, la propriété accéde directement sans modification à la donnée brute stockée dans le champ, mais il est tout à fait possible à une propriété d'accéder à cette donnée en en modifiant sa valeur avant stockage ou après récupération de sa valeur.


1.3 Détail et exemple de fonctionnement d'une propriété

L'exemple ci-dessous reprend la propriété propr1 en lecture et écriture du paragraphe précédent et montre comment elle peut modifier la valeur brute de la donnée stockée dans l'attribut "
int champ "  :

private int champ;

public int propr1{
            get {return champ*10;}
            set {champ = value + 5 ;}
}


Utilisons cette propriété en mode écriture à travers une affectation :
prop1 = 14 ;

Le mécanisme d'écriture est simulé ci-dessous :

La valeur 14 est passée comme paramètre dans la méthode set à la variable implicite value, le calcul value+5 est effectué et le résultat 19 est stocké dans l'attribut champ.



Utilisons maintenant notre propriété en mode lecture à travers une affectation :
int x = propr1 ;

Le mécanisme de lecture est simulé ci-dessous :

La valeur brute 19 stockée dans l'attribut champ est récupérée par la propriété qui l'utilise dans la méthode accesseur get en la multipliant par 10, c'est cette valeur modifiée de 190 qui renvoyée par la propriété.




Exemple pratique d'utilisation d'une propriété

Une propriété servant à fournir automatiquement  le prix d'un article en y intégrant la TVA au taux de 19.6% et arrondi à l'unité d'euro supérieur  :

private Double prixTotal ;
private Double tauxTVA = 1.196 ;

public Double prix {
            get {
                      return
Math.Round(prixTotal);
                   }
            set {
                      prixTotal = value * tauxTVA ;
                   }
}


Ci-dessous le programme console C#Builder exécutable :
using System ;

namespace  ProjPropIndex
{
 class 
Class   {
  static private 
Double prixTotal  ;
  static private 
Double tauxTVA  1.196  ;
  
  static public 
Double prix    {
   
get     {
      return 
Math.Round ( prixTotal ) ;
    }
   
set     {
    
prixTotal  value  tauxTVA  ;
    }
  }
   
  
[STAThread]
  
static void  Main ( string [] args {
   
 Double val  100 ;
   
 System .Console.WriteLine ("Valeur entrée :" + val );
   
 prix  val ;
   
 System .Console.WriteLine ("Valeur stockée :" + prixTotal );
   
 val  prix ;
   
 System .Console.WriteLine ("valeur arrondie (lue) : " + val  ) ;
   
 System .Console.ReadLine ( );
  }
 }
}

Résultats d'exécution :



Explications des actions exécutées :

On rentre 100€ dans la variable prix :
Double val  100 ;
prix  val ;
Action effectuée :
On écrit 100 dans la propriété prix et celle-ci stocke 100*1.196=119.6 dans le champ prixTotal.
On exécute l'instruction :
val  prix ;
Action effectuée :
On lit la propriété qui arrondi le champ prixTotal à l'euro supérieur soit : 120€



1.4 Les propriétés sont de classes ou d'instances

Les propriétés, comme les champs peuvent être des propriétés de classes et donc qualifiées par les mots clefs comme static, abstract etc ...Dans l'exemple précédent nous avons qualifié tous les champs et la propriété prix en static afin qu'ils puissent être accessibles à la méthode Main qui est elle-même obligatoirement static.

Voici le même exemple utilisant une version avec des propriétés et des champs d'instances et non de classe (non static) :

using  System ;

namespace  ProjPropIndex
{
 class 
clA   {
   private 
Double prixTotal  ;
   private 
Double tauxTVA  1.196  ;
  
   public 
Double prix    {
  
  get     {   return  Math.Round ( prixTotal ) ;  }
  
  set     {   prixTotal  value  tauxTVA  ;   }
   }
 }
 
 class 
Class   {
 
 
  
[STAThread]
  
static void  Main ( string [] args )  {
   
  clA Obj  new  clA ( );
   
  Double val  100 ;
   
  System .Console.WriteLine ("Valeur entrée :" + val );
   
  Obj.prix  val ;
   
  // le champ prixTotal n'est pas accessible car il est privé
   
  val  Obj.prix ;
   
  System .Console.WriteLine ("valeur arrondie (lue) : " + val  ) ;
   
  System .Console.ReadLine ( );
   }
 }
}

Résultats d'exécution :




1.5 Les propriétés peuvent être masquées comme les méthodes

Une propriété sans spécificateur particulier de type de liaison est  considérée comme une entité à liaison statique par défaut.

Dans l'exemple ci-après nous dérivons une nouvelle classe de la classe clA nommée clB, nous redéclarons dans la classe fille une nouvelle propriété ayant le même nom, à l'instar d'un champ ou d'une méthode C# considère que nous masquons la propriété mère et nous suggère le conseil  suivant :

[C# Avertissement] Class...... : Le mot clé new est requis sur '...........', car il masque le membre hérité...... '

Nous mettons donc le mot clef new devant la nouvelle déclaration de la propriété dans la classe fille. En reprenant l'exemple précédent supposons que dans la classe fille clB, la TVA soit à 5%, nous redéclarons dans clB une propriété prix qui va masquer celle de la mère :

using  System ;

namespace  ProjPropIndex
{
 class 
clA   {
   private 
Double prixTotal  ;
   private 
Double tauxTVA  1.196  ;
  
   public 
Double prix    { // propriété de la classe mère
  
  get     {   return  Math.Round ( prixTotal ) ;  }
  
  set     {   prixTotal  value  tauxTVA  ;   }
   }
 }
 
class  clB  clA   {
  private 
Double prixLocal  ;
  public new 
Double prix    { // masquage de la propriété de la classe mère
    get     {  return  Math.Round ( prixLocal ) ;  }
   
set     {   prixLocal  value  1.05  ;   }
  }
 }
 class 
Class  {
 
   [STAThread]
  
static void  Main ( string [] args )   {
   
clA Obj  new  clA ( );
   
Double val  100 ;
   
System .Console.WriteLine ("Valeur entrée clA Obj :" + val );
   
Obj.prix  val ;
   
val  Obj.prix ;
   
System .Console.WriteLine ("valeur arrondie (lue)clA Obj : " + val  ) ;
   
System .Console.WriteLine ("--------------------------------------");
   
clB Obj2  new  clB ( );
   
val  100 ;
   
System .Console.WriteLine ("Valeur entrée clB Obj2 :" + val );
   
Obj2.prix  val ;
   
val  Obj2.prix ;
   
System .Console.WriteLine ("valeur arrondie (lue)clB Obj2: " + val  ) ;
   
System .Console.ReadLine ( );
  }

 }
}

Résultats d'exécution :




1.6 Les propriétés peuvent être virtuelles et redéfinies comme les méthodes

Les propriété en C# ont l'avantage important d'être utilisables dans le contexte de liaison dynamique d'une manière strictement identique à celle des méthodes en C# , ce qui confère au langage une "orthogonalité" solide relativement à la notion de polymorphisme.

Une propriété peut donc être déclarée virtuelle dans une classe de base et être surchargée dynamiquement dans les classes descendantes de cette classe de base.

Dans l'exemple ci-après semblable au précédent, nous déclarons dans la classe mère clA la propriété prix comme virtual, puis :



Notre objectif est de comparer les résultats d'exécution obtenus lorsque l'on utilise une référence d'objet de classe mère instanciée soit en objet de classe clB ou clB2. C'est le comportement de la propriété prix dans chacun de deux cas (statique ou dynamique) qui nous intéresse :
using  System ;

namespace  ProjPropIndex
{
 class 
clA   {
   private 
Double prixTotal  ;
   private 
Double tauxTVA  1.196  ;
  
   public virtual 
Double prix    { // propriété virtuelle de la classe mère
  
  get     {   return  Math.Round ( prixTotal ) ;  }
  
  set     {   prixTotal  value  tauxTVA  ;   }
   }
 }
 
class  clB  clA   {
  private 
Double prixLocal  ;
  public new 
Double prix    { // masquage de la propriété de la classe mère
    get     {  return  Math.Round ( prixLocal ) ;  }
   
set     {   prixLocal  value  1.05  ;   }
  }
 }

 class  clB2  clA   {
   private 
Double prixLocal  ;
   public override 
Double prix   {// redéfinition de la propriété de la classe mère
    get    {  return  Math.Round ( prixLocal ) ;  }
   
set    {   prixLocal  value  1.05  ;   }
   }
 }

 class 
Class  {
  static private 
Double prixTotal  ;
  static private 
Double tauxTVA  1.196  ;
  
  static public 
Double prix    {
   
get    {   return  Math.Round ( prixTotal ) ;  }
   
set    {   prixTotal  value  tauxTVA  ;   }
  }
 
  
[STAThread]
  
static void  Main ( string [] args )  {
   
clA Obj  new  clA ( );
   
Double val  100 ;
   
System.Console.WriteLine ("Valeur entrée  Obj=new clA :" + val );
   
Obj.prix  val ;
   
val  Obj.prix ;
   
System.Console.WriteLine ("valeur arrondie (lue)Obj=new clA  : " + val  ) ;
   
System.Console.WriteLine ("----------------------------------------");
   
Obj  new  clB ( );
   
val  100 ;
   
System.Console.WriteLine ("Valeur entrée  Obj=new clB  :" + val );
   
Obj.prix  val ;
   
val  Obj.prix ;
   
System.Console.WriteLine ("valeur arrondie (lue)Obj=new clB : " + val  ) ;
   
System.Console.WriteLine ("----------------------------------------");
   
Obj  new  clB2 ( );
   
val  100 ;
   
System.Console.WriteLine ("Valeur entrée  Obj=new clB2  :" + val );
   
Obj.prix  val ;
   
val  Obj.prix ;
   
System.Console.WriteLine ("valeur arrondie (lue)Obj=new clB2 : " + val  ) ;
   
System.Console.ReadLine ( );
  }
 }
}

Résultats d'exécution :


Nous voyons bien que le même objet Obj instancié en classe clB ou en classe clB2 ne fournit pas les mêmes résultats pour la propriété prix, ces résulats sont conformes à la notion de polymorphisme en particulier pour l'instanciation en clB2.

Rappelons que le masquage statique doit être utilisé comme pour les méthodes à bon escient, plus spécifiquement lorsque nous ne souhaitons pas utiliser le polymorphisme, dans le cas contraire c'est la liaison dynamique qui doit être utilisée pour définir et redéfinir des propriétés.



1.7 Les propriétés peuvent être abstraites comme les méthodes

Les propriétés en C# peuvent être déclarées abstract, dans ce cas comme les méthodes elles sont automatiquement virtuelles sans necéssiter l'utilisation du mot clef virtual.

Comme une méthode abstraite, une propriété abstraite n'a pas de corps de définition pour le ou les accesseurs qui la composent, ces accesseurs sont implémentés dans une classe fille.

Toute classe déclarant une propriété abstract doit elle-même être déclarée abstract, l'implémentation de la propriété a lieu dans une classe fille, soit en masquant la propriété de la classe mère (grâce à une déclaration à liaison statique avec le mot clef new), soit en la redéfinissant (grâce à une déclaration à liaison dynamique avec le mot clef override) :

abstract class  clA   {
   public abstract Double prix    { // propriété abstraite virtuelle de la classe mère
  
  get   // propriété abstraite en lecture
     set    ; // propriété abstraite en écriture 
   }
 }
 
class  clB1  clA   {
  
private  Double prixTotal  ;
   private 
Double tauxTVA  1.196  ;
  public new 
Double prix    { // implantation par masquage de la propriété de la classe mère
    get     {  return  Math.Round ( prixLocal ) ;  }
   
set     {    prixTotal  value  tauxTVA  ;   }
  }
 }

class  clB2  clA   {
  
private  Double prixTotal  ;
   private 
Double tauxTVA  1.05  ;
  public override 
Double prix    { // implantation par redéfinition de la propriété de la classe mère
    get     {  return  Math.Round ( prixLocal ) ;  }
   
set     {    prixTotal  value  tauxTVA  ;   }
  }
 }




1.8 Les propriétés peuvent être déclarées dans une interface

Les propriétés en C# peuvent être déclarées dans une interface comme les événements et les méthodes sans le mot clef abstract, dans ce cas comme dans le cas de propriété abstraites la déclaration  ne contient pas de corps de définition pour le ou les accesseurs qui la composent, ces accesseurs sont implémentés dans une classe fille qui implémente elle-même l'interface.

Les propriétés déclarées dans une interface lorsqu'elles sont implémentées dans une classe peuvent être définies soit à liaison statique, soit à liaison dynamique.

Ci dessous une exemple de hiérarchie abstraite de véhicules, avec une interface IVehicule  contenant un événement ( cet exemple est spécifié au chapitre 2.4 sur les interfaces ) :



abstract class  Vehicule  {  // classe abstraite mère
    ....

}

interface  IVehicule {
    ....
    string 
TypeEngin    { // déclaration de propriété abstraite par défaut
      
get ;
      
set
;
    }

     ....

}

abstract class  UnVehicule : Vehicule , IVehicule  {  
   private 
string  nom = "";
  
....
   public virtual  string  TypeEngin  {    // implantation virtuelle de la propriété
   
   get       {         return  nom ;       }
      
set       {          nom = "["+value+"]" ;       }
   }
   ....

}

abstract class  Terrestre  UnVehicule {
 
private  string  nomTerre = "";
 
....
  public override 
string  TypeEngin     { // redéfinition de propriété
   get   {   return  base .TypeEngin ;  }
  
set   {   string  nomTerre = value + "-Terrestre";
   
            base .TypeEngin = nomTerre
;  }
  } 

}


1.9 Exemple complet exécutable

Code C# complet compilable avec l'événement et une classe concrète


public delegate void Starting ( );   // delegate declaration de type pour l'événement

abstract class  Vehicule  {  // classe abstraite mère
   public abstract void 
Demarrer ( ); // méthode abstraite
   public void  RépartirPassagers ( )  {  } // implantation de méthode avec corps vide
   public void  PériodicitéMaintenance ( ) {  } // implantation de méthode avec corps vide
}
interface  IVehicule {
    event 
Starting OnStart ;   // déclaration d'événement du type délégué : Starting
   
string  TypeEngin    { // déclaration de propriété abstraite par défaut
      
get ;
      
set
;
    }

     void 
Stopper ( ); // déclaration de méthode abstraite par défaut
}

//-- classe abstraite héritant de la classe mère et implémentant l'interface :
abstract class  UnVehicule : Vehicule , IVehicule  {  
   private 
string  nom = "";
   private 
string [ ] ArrayInfos = new  string [10] ;
   public  event 
Starting OnStart ;
   protected void 
LancerEvent ( )   {
      if(
OnStart  !=  null)
         
OnStart ( );
   }
   public virtual  string  TypeEngin  {    // implantation virtuelle de la propriété
   
   get       {         return  nom ;       }
      
set       {          nom = "["+value+"]" ;       }
   }
   public virtual void 
Stopper ( )   {  } // implantation virtuelle de méthode avec corps vide
}

abstract class  Terrestre  UnVehicule {
 
private  string  nomTerre = "";
  public new void 
RépartirPassagers ( ) {   }
  public new void 
PériodicitéMaintenance ( ) {  }
  public override 
string  TypeEngin     { // redéfinition de propriété
   get   {   return  base.TypeEngin ;  }
  
set   {   string  nomTerre = value + "-Terrestre";
   
            base.TypeEngin = nomTerre
;  }
  } 

}


class Voiture  : Terrestre   {
        public override  string  TypeEngin     { // redéfinition de propriété
           get   {   return  base.TypeEngin + "-voiture";  }
 
        set   {   
base.TypeEngin =  "(" + value + ")";   }
        }
        public override void  Demarrer ( )  {
            LancerEvent( );
        }
        public override void  Stopper ( )  {
            //...
        }
}

class UseVoiture   { // instanciation d'une voiture particulière
        static void  Main ( string [] args {
            UnVehicule x =  new Voiture ( ) ;
            x.TypeEngin  = "Picasso" ; // propriété en écriture
            System.Console.WriteLine  ( "x est une " + x.TypeEngin  ) ; // propriété en lecture
            System.Console.ReadLine ( ) ;
        }

}

Résultats d'exécution :



1.9.1 Détails de fonctionnement de la propriété TypeEngin en écriture

La propriété TypeEngin est en écriture dans :
      x.TypeEngin  = "Picasso" ;

Elle remonte à ses définitions successives grâce l'utilisation du mot clef base qui fait référence à la classe mère de la classe en cours.

propriété TypeEngin dans la classe Voiture :

propriété TypeEngin dans la classe Terrestre :
.........

propriété TypeEngin dans la classe UnVehicule :
.....................



Définition de la propriété dans la classe Voiture
(écriture) :



base référence ici la classe Terrestre.


Définition de la propriété dans la classe Terrestre
(écriture) :



base référence ici la classe UnVehicule.



Définition de la propriété dans la classe UnVehicule (écriture) :



nom est le champ privé dans lequel est stocké la valeur effective de la propriété.
 



1.9.2 Détails de fonctionnement de la propriété TypeEngin en lecture

La propriété TypeEngin est en lecure dans :
      System.Console.WriteLine  ( "x est une " + x.TypeEngin  ) ;

Pour aller chercher la valeur effective, elle remonte à ses définitions successives grâce l'utilisation du mot clef base qui fait référence à la classe mère de la classe en cours.

valeur transmise à partir de la classe Voiture :



Définition de la propriété dans la classe Voiture (lecture) :

class Voiture  : Terrestre { ...
  public override 
string  TypeEngin     { // redéfinition de propriété
           get   {   return  base.TypeEngin + "-voiture";  }
 
        ...

  }
...
}


L'accesseur get va chercher le résultat dans base.TypeEngin et lui concatène le mot "-voiture".
base.TypeEngin référence ici la propriété dans la classe Terrestre.


Définition de la propriété dans la classe Terrestre (lecture) :

abstract class  Terrestre  UnVehicule { ...
  public override  string  TypeEngin     { // redéfinition de propriété
     
get   {   return  base.TypeEngin ;  }
  
   ...

  } 

... }


L'accesseur get va chercher le résultat dans base.TypeEngin.
base.TypeEngin référence ici la propriété dans la classe  UnVehicule.


Définition de la propriété dans la classe UnVehicule (lecture) :

abstract class  UnVehicule : Vehicule , IVehicule  { ...
  private  string  nom = "";
  public virtual  string  TypeEngin  {    // implantation virtuelle de la propriété
   
   
get   {   return  nom ;  }
      
...
  }
...
}


L'accesseur get va chercher le résultat dans le champ nom.
nom est le champ privé dans lequel est stocké la valeur effective de la propriété.
 




2. Les indexeurs

Nous savons en Delphi qu'il existe une notion de propriété par défaut qui nous permet par exemple dans un objet Obj de type TStringList se nomme strings, d'écrire Obj[5] au lieu de Obj.strings[5]. La notion d'indexeur de C# est voisine de cette notion de propriété par défaut en Delphi.


2.1 Définitions et comparaisons avec les propriétés


Un indexeur est un membre de classe qui permet à un objet d'être indexé de la même manière qu'un tableau. La signature d'un indexeur doit être différente des signatures de tous les autres indexeurs déclarés dans la même classe. Les indexeurs et les propriétés sont très similaires de par leur concept, c'est pourquoi nous allons définir les indexeurs à partir des propriétés.

Tous les indexeurs sont représentés par l' opérateur [  ] . Les liens sur les propriétés ou les indexeurs du tableau ci-dessous renvoient directement au paragraphe associé.

Propriété
Indexeur
Déclarée par son nom.
Déclaré par le mot clef this.
Identifiée et utilisée par son nom.
Identifié par sa signature, utilisé par l'opérateur  [ ] . L'opérateur [ ] doit être situé immédiatement après le nom de l'objet.
Peut être un membre de classe static ou un membre d'instance.
Ne peut pas être un membre static, est toujours un membre d'instance.
L'accesseur get correspond à une méthode sans paramètre.
L'accesseur get correspond à une méthode pourvue de la même liste de paramètres formels que l'indexeur.
L'accesseur set correspond à une méthode avec un seul paramètre implicite value.
L'accesseur set correspond à une méthode  pourvue de la même liste de paramètres formels que l'indexeur plus le paramètre implicite value.
Une propriété Prop héritée est accessible par la syntaxe base.Prop
Un indexeur Prop hérité est accessible par la syntaxe base.[  ]
Les propriétés peuvent être à liaison statique, à liaison dynamique, masquées ou redéfinies.
Les indexeurs peuvent être à liaison statique, à liaison dynamique, masqués ou redéfinis.
Les propriétés peuvent être abstraites.
Les indexeurs peuvent être abstraits.
Les propriétés peuvent être déclarées dans une interface.
Les indexeurs peuvent être déclarés dans une interface.



2.1.1 Déclaration
Propriété
Indexeur
Déclarée par son nom, avec champ de stockage :
private int champ;

public int propr1{ 
         get
{
return champ ; }
         set { champ = value  ; }

}

Déclaré par le mot clef this, avec champ de stockage :
private int [ ] champ = new int [10];

public int this [int index]{ 
         get
{
return champ[ index ] ; }
         set { champ[ index ] = value  ; }
}





2.1.2 Utilisation
Propriété
Indexeur
Déclaration :
class clA {
  private int champ;

  public int propr1{ 
         get
{
return champ ; }
         set { champ = value  ; }
  }
}
Utilisation :
clA Obj = new clA( );
Obj.propr1 = 100 ;




int x = Obj.prop1 ;  // x = 100
Déclaration :
class clA {
  private int [ ] champ = new int [10];

  public int this [int index]{ 
         get
{
return champ[ index ] ; }
         set { champ[ index ] = value  ; }
  }
}
Utilisation :
clA Obj = new clA( );
for ( int i =0; i<5; i++ )
   
Obj[ i ] =  i ;



int x = Obj[ 2 ] ; // x = 2
int y = Obj[ 3 ] ; // y = 3
...



2.1.3 Paramètres

Propriété
Indexeur
Déclaration :
class clA {
  private int champ;

  public int propr1{ 
         get
{
return champ*10 ; }
         set { champ = value + 1  ; }
  }
}

value est un paramètre implicite.

Utilisation :
clA Obj = new clA( );
Obj.propr1 = 100 ;
int x = Obj.prop1 ;  // x = 1010
Déclaration :
class clA {
  private int [ ] champ = new int [10];

  public int this [int k]{ 
         get
{
return champ[ k ]*10 ; }
         set { champ[ k ] = value + 1  ; }
  }
}

k est un paramètre formel de l'indexeur.

Utilisation :
clA Obj = new clA( );
for ( int i =0; i<5; i++ )
   Obj[ i ] =  i ;
int x = Obj[ 2 ] ; // x = 30
int y = Obj[ 3 ] ; // y = 40
...



2.1.4 Indexeur à liaison dynamique, abstraction et interface


Reprenons l'exemple de hiérarchie de véhicules, traité avec la propriété TypeEngin de type string, en y ajoutant un indexeur de type string en proposant des définitions parallèles à la propriété et à l'indexeur :



abstract class  Vehicule  {  // classe abstraite mère
    ....

}


interface  IVehicule {
    ....
    string 
TypeEngin    { // déclaration de propriété abstraite par défaut
      
get ;
      
set
;
    }

    ....

    string 
this [ int k ]    { // déclaration d'indexeur abstrait par défaut
      
get ;
      
set
;
    }

     ....

}


abstract class  UnVehicule : Vehicule , IVehicule  {  
   
private  string [ ] ArrayInfos = new  string [10] ;
   private  string  nom = "";
   ....
   public virtual  string  TypeEngin  {    // implantation virtuelle de la propriété
   
   get       {         return  nom ;       }
      
set       {          nom = "["+value+"]" ;       }
   }
   ....

   public virtual  string  this [ int k ]   {    // implantation virtuelle de l'indexeur
   
   get  {  return  ArrayInfos[ k ] ;  }
      
set   { ArrayInfos[ k ]
value  ;  }
   }
   ....

}


abstract class  Terrestre  UnVehicule {
 
private  string  nomTerre = "";
 
....
  public override 
string  TypeEngin     { // redéfinition de propriété
   get   {   return  base .TypeEngin ;  }
  
set   {   string  nomTerre = value + "-Terrestre";
   
            base .TypeEngin = nomTerre
;  }
  } 

  ....
  public override 
string  this [ int k ]     { // redéfinition de  l'indexeur
   get   {   return  base[ k ] ;  }
  
set   {   string  nomTerre = value + "-Terrestre"
;
   
            base[ k ] = nomTerre +
"/set =" + k.ToString( ) + "/" ;  }
  } 

}



class Voiture  : Terrestre   {
  public override  string  TypeEngin     { // redéfinition de propriété
           get   {   return  base.TypeEngin + "-voiture";  }
 
        set   {   
base.TypeEngin =  "(" + value + ")";   }
   }
  public override  string  this [ int n ]     { // redéfinition de  l'indexeur
           get   {   return  base[ n ] + "-voiture{get =" + n.ToString( ) + "}" ;   }
 
        set   {   
base[ n ] =  "(" + value + ")";   }
  }
        ...
}


2.2 Code C# complet compilable


Code avec un événement une propriété et un indexeur
public delegate void  Starting ( );    // delegate declaration de type

abstract class  Vehicule {
   public abstract void 
Demarrer ( );
   public void 
RépartirPassagers ( ) {  }
 public void 
PériodicitéMaintenance ( ) {  }
}

interface 
IVehicule {
  event 
Starting OnStart ;   // déclaration événement
  string  this  [ int  index]    // déclaration Indexeur
 
{
  
get ;
  
set ;
 }
 
string  TypeEngin    // déclaration propriété
 
{
  
get ;
  
set ;
  }
 void 
Stopper ( );
}

abstract class 
UnVehicule : Vehicule, IVehicule {
  private 
string  nom = "";
  private 
string [] ArrayInfos = new  string [10] ;
  public event 
Starting OnStart ; // implantation événement
  protected void  LancerEvent ( )  {
   if(
OnStart  !=  null)
   
 OnStart ( );
  }
  public virtual 
string  this  [ int  index]  { // implantation indexeur virtuel
    get   {   return  ArrayInfos[index] ;    }
  
set   {    ArrayInfos[index] = value ;  }
  }
 public virtual 
string  TypeEngin  { // implantation propriété virtuelle
   get   {   return  nom ;  }
  
set   {    nom = "[" + value + "]";  }
 }
 
public virtual void  Stopper ( ) {  }
}

abstract class 
Terrestre  UnVehicule
 public new void 
RépartirPassagers ( ) {  }
 public new void 
PériodicitéMaintenance ( ) {  }
 public override 
string  this  [ int  k]    { // redéfinition indexeur
   get   {   return  base [k] ;  }
  
set   {   string  nomTerre = value + "-Terrestre";
   
           base [k] = nomTerre + "/set = " + k.ToString () + "/";
           }
 }
 public override 
string  TypeEngin   { // redéfinition propriété
   get   {   return  base .TypeEngin ;  }
  
set   {   string  nomTerre = value + "-Terrestre";
   
            base .TypeEngin = nomTerre ;
           }
 }

}

class 
Voiture  Terrestre {
 public override  string  this  [ int  n]    { // redéfinition indexeur
   get   {   return  base [n] + "-voiture {get = " + n.ToString ( )+ " }";  }
  
set   {   string  nomTerre = value + "-Terrestre";
   
           base [n] = "(" + value + ")";
           }
 }
 public override 
string  TypeEngin   { // redéfinition propriété
   get   {   return  base .TypeEngin + "-voiture";  }
  
set   {    base .TypeEngin = "(" + value + ")";   }
 }
 public override void  
Demarrer ( ) {
  
LancerEvent ( );
 }
 public override void 
Stopper ( ) {
  
//...
 
}
}

class  UseVoiture   
 
 
static void   Main  string  [] args  )  
 {
  
// instanciation d'une voiture particulière :
   UnVehicule automobile 
new  Voiture ( );

   // utilisation de la propriété TypeEngin :  
   
automobile .TypeEngin = "Picasso";
  
System .Console.WriteLine ("x est une " + automobile.TypeEngin );

   
// utilisation de l'indexeur :  
   automobile [0] = "Citroen";
  
 automobile [1] = "Renault";
  
 automobile [2] = "Peugeot";
  
 automobile [3] = "Fiat";
  for(
int  i = 0 i < i ++ )
   
System .Console.WriteLine ("Marque possible : " + automobile [i] );
  
System .Console.ReadLine ( );
 }
}



Résultats d'exécution :