les objets C#

 

        Nous allons dans ce chapitre revenir à un peu plus de théorie afin de mieux comprendre ce qui a été fait dans le dernier cours et éclaircir les points obscurs.
Nous y avons vu comment créer une classe facilement. La question qui peut maintenant nous venir à l'esprit est : "Comment créer une instance de cette classe (c'est à dire un objet) ?".
Ce chapitre a pour but de répondre à cette question en nous apprenant à faire des classes dotées de membres, à créer et à manipuler des objets.

 

Sommaire

1. Les constructeurs : générateurs d'objets

2. Méthode constructeur

3. Création d'un objet

4. Retenir les objets : les handles

5. Créer un handle dans un programme

6. Faire pointer un handle

7. La référence à un membre d'une classe

8. Fin de vie des objets : le garbage collector

 

Les constructeurs : générateurs d'objets

 

        Lorsque nous avons abordé le concept de la programmation orientée objet dans la première partie de ce livre, nous avons appris qu'une classe pouvait être assimilée à un moule à partir duquel nous pouvions générer autant d'objets aux caractéristiques différentes que nous le voulions. Pour créer un objet à partir d'une classe, cette dernière doit disposer d'une méthode spéciale qu'on appelle constructeur.
Cette méthode a pour particularité de porter le même nom que la classe à laquelle elle appartient.
Les méthodes constructeurs permettent de créer de nouvelles instances de classe en leur attribuant, si besoin est, des valeurs pour leurs caractéristiques. Elles effectuent de même les opérations dont l'objet a besoin pour s'initialiser. Si ces méthodes sont appelées au moment de la création de l'objet, ce n'est pas le programmeur qui s'en charge, mais C# lui-même.

 

Méthode constructeur

 

        Revenons sur le programme que nous avons écrit dans le dernier chapitre. Nous allons changer notre nom de classe en SecondProgramme et nous allons ajouter dans le fichier une classe que nous allons nommer Chien :

 
//SecondProgramme.cs
/**************************************************/
/*                                                */
/*    second programme d'apprentissage du C#      */
/*              (premiere version)                */
/*  Valentin BILLOTTE - " Le langage C # " 2001   */
/*                                                */
/**************************************************/


//utiliser le namespace System 
using System ;



/**
 * objet qui contient notre fonction main
 */
class SecondProgramme
{

 /**
  * méthode principale de notre programme
  */
  public static void Main()
  {
    Console.Out.WriteLine("Nous n'avons pas encore créé de chien");
  }

}




/**
 * classe qui permet la création d'un objet de type Chien
 */
class Chien
{

  //chien ne dispose pour l'instant d'aucun membre

}
 

        Donnez le nom SecondProgramme.cs à votre fichier source et compilez.
Notre programme dispose ici de deux classes. La première, similaire à celle de notre chapitre précédent, contient la méthode Main qui va lancer notre programme. La seconde classe est un "moule" qui va nous permettre de créer des objets de type Chien. A l'exécution le programme ne va rien faire puisque notre classe SecondProgramme se contente juste d'afficher la chaîne de caractères "Nous n'avons pas encore créé de chien". Il n'y a ici, en effet, aucune interaction avec la classe Chien.
Pour créer un chien il va, tout d'abord, falloir rajouter dans la seconde classe un constructeur :

 
/**
 * classe qui permet la création d'un objet de type Chien
 */
class Chien : object
{

 /**
  * constructeur
  */
  Chien()
  {
    //la méthode ne fait rien pour l'instant
  }

}
 

        Le constructeur Chien() respecte toutes les conventions que nous avons énoncées : la méthode porte le nom de la classe et est terminée par des parenthèses. Un constructeur n'a pas à renvoyer de valeur comme c'est le cas pour une méthode standard, nous n'indiquerons donc pas avant son nom si elle renvoie ou non une donnée.
Rajoutons maintenant à la méthode Main de la classe SecondProgramme une instruction qui va demander, à partir de la classe Chien, la création d'une instance.

 

Création d'un objet

 

        Nous savons que c'est le constructeur de la classe Chien : Chien() qui va être à l'origine de la création de l'objet. Il nous manque maintenant la syntaxe nécessaire à cette action.
Pour créer une instance d'une classe en C#, il nous faut utiliser l'opérateur new suivi du nom du constructeur de la classe dont on veut un objet. Pour faire une instance de notre classe Chien nous ferons :

 
//SecondProgramme.cs
/**************************************************/
/*                                                */
/*    second programme d'apprentissage du C#      */
/*              (seconde version)                 */
/*  Valentin BILLOTTE - " Le langage C # " 2001   */
/*                                                */
/**************************************************/


//utiliser le namespace System 
using System ;



/**
 * objet qui contient notre fonction main
 */
class SecondProgramme 
{

 /**
  * méthode principale du programme
  */
  public static void Main()
  {
    Console.Out.WriteLine("Nous n'avons pas encore créé de chien");

    //création d'un objet chien
    new Chien();

    Console.Out.WriteLine("un nouveau Chien est créé !");
  }

}




/**
 * classe qui permet la création d'un objet de type Chien
 */
class Chien 
{

 /**
  * constructeur de notre classe chien
  */
  Chien()
  {
    //la méthode ne fait rien pour l'instant
  }

}

 

        Si nous compilons le programme à ce stade le compilateur va générer une erreur nous indiquant que le constructeur de la classe Chien est inaccessible.

 


le constructeur est inaccessible

 

        En effet, tout comme la méthode Main, le constructeur Chien doit être déclaré en public afin de pouvoir être utilisé par d'autres objets. Pour mieux comprendre ce principe, nous pouvons imaginer notre classe comme un boîte noire qu'il est impossible d'ouvrir. Le terme public nous permet de rendre visible certaines fonctionnalités de cet objet et de les utiliser. A l'inverse private les rend invisibles.


Nous reviendrons sur public et private ainsi que sur les autres termes permettant de contrôler l'accès à des membres de classes dans le chapitre 6

tout s'éclaire : Nous pouvons comprendre ainsi que la méthode Main soit déclarée public et static : elle est public afin que le programme puisse y accéder à l'intérieur de la classe pour démarrer le programme et static car elle unique pour tous les objets de toutes les classes.


Nous allons donc rajouter public devant notre méthode Chien() :

 
 public Chien()
  {
    //la méthode ne fait rien pour l'instant
  }
 

        La compilation et l'exécution de notre programme se fait maintenant sans aucun problème.

 


la compilation et l'exécution du programme se fait sans erreur

 

        Que fait ici notre programme ? Il crée une instance de la classe Chien par l'instruction :

 
new Chien();
 

        Lorsque cette ligne est exécutée, C# cherche la classe Chien. il regarde alors si elle contient un constructeur. Dans ce cas celui-ci est exécuté et une instance de Chien est créée. C# alloue alors de la mémoire à cet objet, pour sa future utilisation par le programme, et le place à un endroit que lui seul connaît.
Nous savons qu'un objet Chien est présent en mémoire sans savoir précisément où C# l'a placé. Pourtant il est très simple de tester la création de la nouvelle instance de Chien en modifiant notre programme comme ci-dessous :

 
/**
  * constructeur de notre classe chien
  */
  public Chien()
  {

    Console.Out.WriteLine("Ouah ! ouah !");

  }

 

        Nous obtenons la sortie :

 


la sortie "Ouah ! ouah !" nous indique que le constructeur a été appelé

 

        nous indiquant que le constructeur a été appelé et donc qu'une instance de Chien a été créée.
Comment manipuler des instances de classe sans savoir où elles se trouvent ? C'est impossible ; Il faut donc essayer de savoir où se trouvent les objets créés par notre programme afin de pouvoir les retrouver après leur apparition pour les manipuler. C# permet cela grâce aux handles.

 


sans handle notre programme ne peut manipuler les objets que nous créons

 

Retenir les objets : les handles

 

        S'il nous est possible de créer un objet facilement grâce à l'opérateur new, il est en revanche impossible d'utiliser cet objet dans le programme car nous ne savons pas où il est placé en mémoire.
Nous avons besoin de pouvoir identifier cet objet.
Dans la vie courante nous n'appelons pas les êtres de notre entourage par leur type "ma femme", "mon chien" etc. mais par leur nom (par exemple Berthe et Pepette). On a ainsi pour habitude de donner aux personnes, aux animaux et à certains objets un nom propre.
Si nous nous retrouvons en face d'un groupe de personnes, en appelant "Berthe" nous verrons venir la personne souhaitée (en partant du principe qu'il n'y a qu'une seule Berthe) sans pour autant savoir où celle-ci était placée.
En C# ce nom propre peut être assimilé à un handle. Un handle est un lien vers un objet qui va nous permettre de le retrouver. En anglais ce terme signifie "poignée", nous pouvons donc imaginer qu'à la création de notre Chien, nous lui ajouterons une poignée avec laquelle nous pourrons nous adresser à lui et le manipuler. En C# il peut y avoir plusieurs handles par objet, mais un handle ne peut pas référencer plusieurs objets. Enfin un handle qui correspond à un objet à un certain moment du programme peut correspondre à un autre objet à un autre moment.

 


avec le handle notre programme dispose d'un "lien" vers une instance d'une classe et peut la manipuler à souhait

 

        codeur C++ : On peut comparer le système de handle du C# aux pointeurs du C++. Il est ainsi courant de dire qu'un handle pointe sur un objet en mémoire. Nous reviendrons plus précisément sur cette comparaison plus tard.


La vie d'un handle dans un programme peut être divisée en trois temps : d'une part sa déclaration pour que le compilateur en prenne connaissance, dans un second temps son utilisation où on le fera pointer sur un objet et enfin en dernier sa destruction par le garbage collector.

 

Créer un handle dans un programme

 

        Pour déclarer un handle, nous avons juste à indiquer le type de l'objet pointé par le handle (c'est à dire le nom de la classe), suivi par le nom que nous donnons à l'objet. Par exemple si nous voulons créer un handle sur une objet de type Chien que nous allons nommer pepette, nous ferons :

 
Chien pepette;
 

        à l'intérieur de la classe qui gère notre programme (soit SecondProgramme). A ce stade le handle pepette ne pointe sur rien. On pourrait dire qu'il pointe sur null. Nous avons déclaré le handle mais nous ne l'avons pas initialisé.

 

Faire pointer un handle

 

        Pour faire pointer un handle sur un objet nouvellement créé à l'aide de l'opérateur new, nous allons utiliser le signe égal "=". Ce signe, dans ce cas, doit être lu comme "pointe sur".
Revenons à notre méthode Main et à la création de l'objet. Si nous faisons dans celle-ci :

 
Chien Pepette;
new Chien();
 

        il n'y a aucun lien entre notre handle de la première ligne et l'objet venant d'être créé sur la seconde. Nous perdons donc notre objet dans la mémoire et restons avec une poignée ne s'agrippant à rien. Par contre avec le code suivant :

 
Chien Pepette;
pepette = new Chien();
 

        pepette va pointer sur la nouvelle instance de la classe Chien que nous venons de créer. L'instruction :

 
Chien Pepette;
pepette = new Chien();
 

        peut-être lue comme : "pepette pointe sur la nouvelle instance de la classe Chien". L'handle pepette va maintenant nous permettre de manipuler l'objet.
Notons que le C# nous permet de condenser les deux lignes ci-dessus en une seule :

 
Chien Pepette = new Chien();
 

        Changez maintenant le programme ainsi :

 
//SecondProgramme.cs
/**************************************************/
/*                                                */
/*    second programme d'apprentissage du C#      */
/*              (troisième version)               */
/*  Valentin BILLOTTE - " Le langage C # " 2001   */
/*                                                */
/**************************************************/


//utiliser le namespace System 
using System ;



/**
 * objet qui contient notre fonction Main
 */
class SecondProgramme 
{


 /**
  * méthode principale du programme
  */
  public static void Main()
  {

    Console.Out.WriteLine("Nous n'avons pas encore créé de chien");

    //création d'un objet chien
    Chien pepette = new Chien();

    Console.Out.WriteLine("un nouveau Chien est créé !");
  }

}




/**
 * classe qui permet la création d'un objet de type Chien
 */
class Chien 
{

 /**
  * constructeur de notre classe chien
  */
  public Chien()
  {

    Console.Out.WriteLine("Ouah ! ouah !");

  }

}
 

        Si vous le compilez et l'exécutez, vous obtiendrez la même sortie qu'avec notre version sans handle, mais nous savons maintenant que notre programme garde une trace du chien créé.
Il nous est évidemment possible de créer autant d'instances de chien que nous le voulons :

 
/**
  * méthode principale du programme
  */
  public static void Main()
  {

    Console.Out.WriteLine("Nous n'avons pas encore créé de chien");

    //création d'un objet chien
    Chien pepette = new Chien();
    Chien brutus = new Chien();
    Chien rex = new Chien();

    Console.Out.WriteLine("plusieurs objets de type Chien ont été créés !");
  }

 

        nous donnant la sortie :

 


une véritable meute de chiens&

 

        Si nous avons de quoi manipuler nos objets désormais, notre classe Chien ne contient aucun membre (mis à part le constructeur) et ne peut donc faire aucune action. Nous allons donc rajouter une méthode à notre classe que nous nommerons Aboyer() qui va permettre aux instances de Chien de pouvoir émettre des sons après leur création.

 
/**
 * classe qui permet la création d'un objet de type Chien
 */
class Chien 
{

 /**
  * constructeur de notre classe chien
  */
  public Chien()
  {

    Console.Out.WriteLine("création d'un chien");

  }


 /**
  * méthode qui permet à notre chien d'aboyer
  */
  public void Aboyer()
  {

    Console.Out.WriteLine("Ouah ! ouah ! grrrrrrrrrrrrrrrr Ouah !");

  }

}
 

        Notre nouvelle classe indiquera à chaque nouvelle création d'une instance qu'un chien à été créé grâce à l'instruction

 
Console.Out.WriteLine("un chien a été créé");
 

        située l'intérieur du constructeur. Nous avons de même rajouté la méthode Aboyer() qui ne renvoie rien, comme l'indique le mot clé void la précédant, et n'accepte aucun paramètre (aucune données entre les parenthèses). Nous rajoutons enfin le terme public, afin de permettre d'utiliser la méthode depuis l'extérieur de l'objet. Celle-ci se contente d'afficher les cris d'un chien sur la sortie standard.

 
/**
  * méthode qui permet à notre chien d'aboyer
  */
  public void Aboyer()
  {

    Console.Out.WriteLine("Ouah ! ouah ! grrrrrrrrrrrrrrrr Ouah !");

  }
 

La référence à un membre d'une classe

 

        Grâce au handle sur pepette, nous allons pouvoir faire aboyer le chien à volonté. Pour faire référence à un membre d'un objet en C#, il suffit d'indiquer le handle de cet objet, suivi du nom du membre à utiliser, en les séparant à l'aide d'un point. Ainsi pour faire aboyer pepette, nous ferons :

 
pepette.Aboyer();
 

        à l'intérieur de notre méthode Main :

 
/**
  * méthode principale du programme
  */
  public static void Main()
  {

    Console.Out.WriteLine("Nous n'avons pas encore créé de chien");

    //création d'un objet chien
    Chien pepette = new Chien();
    //Console.Out.WriteLine("un nouveau Chien est créé !");
    pepette.Aboyer();

  }

 

        nous donnant la sortie :

 


la méthode de notre instance a été appelée

 

        En poussant notre expérience un peu plus loin, nous pourrions créer une histoire avec deux chiens que nous nommerions brutus et pepette. Nous allons les faire vivre quelques secondes. Il faut pour cela permettre aux chiens de sentir et de couiner grâce aux méthodes Sentir() et Couiner() rajoutées à la classe Chien. Nous allons évidemment créer deux handles portant le nom de nos deux chiens dans notre méthode Main().

 
//SecondProgramme.cs
/**************************************************/
/*                                                */
/*    second programme d'apprentissage du C#      */
/*              (dernière version)                */
/*  Valentin BILLOTTE - " Le langage C # " 2001   */
/*                                                */
/**************************************************/


//utiliser le namespace System 
using System ;



/**
 * objet qui contient notre fonction main
 */
class SecondProgramme : object
{


 /**
  * méthode principale du programme
  */
  public static void Main()
  {
    Chien pepette;    
    Chien brutus;

    //créer nos chiens
    Console.Out.WriteLine("création du chien brutus dans notre univers");
    brutus = new Chien();
    Console.Out.WriteLine("création du chien pepette dans notre univers");
    pepette = new Chien();

    //raconter l'histoire
    Console.Out.WriteLine("brutus et pepette se rencontrent...");
    Console.Out.WriteLine("brutus sent pepette...");
    brutus.Sentir();
    Console.Out.WriteLine("et pepette sent brutus");
    pepette.Sentir();

    Console.Out.WriteLine("brutus s'énerve et aboie !");
    brutus.Aboyer();

    Console.Out.WriteLine("pepette se sauve...");
    pepette.Couiner();

    Console.Out.WriteLine("notre histoire est terminée...");


  }

}




/**
 * classe qui permet la création d'un objet de type Chien
 */
class Chien : object
{

 /**
  * constructeur de notre classe chien
  */
  public Chien()
  {

    Console.Out.WriteLine("création d'un chien");

  }


 /**
  * méthode qui permet à notre chien d'aboyer
  */
  public void Aboyer()
  {

    Console.Out.WriteLine("Ouah ! ouah ! grrrrrrrrrrrrrrrr Ouah !");

  }


 /**
  * méthode qui permet à notre chien de couiner
  */
  public void Couiner()
  {

    Console.Out.WriteLine("Kaï Kaï Kaï !!!!");

  }


 /**
  * méthode qui permet à notre chien de sentir
  */
  public void Sentir()
  {

    Console.Out.WriteLine("snif ? snif ! snif ?!");

  }

}

 

        Comme nous le voyons ici, grâce aux handles nous pouvons manipuler nos objets comme nous le voulons.
Si nous voulons qu'un handle ne référence aucun objet alors nous ferons pointer sur null :

 
pepette = null;
 


notre histoire est complète !

 

Fin de vie des objets : le garbage collector

 

        Dans certains langages, les programmeurs doivent continuellement se préoccuper de libérer la mémoire des différents objets qu'ils ont créés.
Cette préoccupation à pour origine le bon fonctionnement des programmes et, à un degré plus élevé, du système d'exploitation. Si un programme génère un grand nombre d'objets et ne les libère pas à la fin de son exécution, la mémoire risque de rester encombrée par des objets dont la vie est terminée. Certes un objet ne prend qu'une place infime dans la mémoire, mais si le PC reste allumé longtemps et que le programme est exécuté souvent, alors le système sur lequel le programme est lancé finira irrémédiablement par planter. Ceci sans compter les autres programmes ne libérant pas la mémoire correctement. C# résout ce problème par l'intermédiaire du garbage collector qui peut être traduit par "ramasseur d'ordures" . Sa tâche sera de libérer les ressources prises par le C# automatiquement lorsque la mémoire libre viendra à manquer. Le GC (garbage collector) s'attaquera uniquement aux objets qui ne sont plus utilisés par le programme. Le GC s'active également au moment où le programme se termine et va libérer toutes les ressources qu'il a prises. Nous n'avons donc pas dans notre code source à nous préoccuper de détruire nos objets à un quelconque moment.

 

[ Précédent | Index | Suivant ]