Une remarque pertinente ?
Une critique impertinente ?
Un lynchage en règle ?
Une invitation sous les tropiques ?

Ecrivez-moi !

Conçu et enseigné tel qu'en lui même, avec pertes, fracas et humour de qualité supérieure            
 par Christophe Darmangeat dans le M2 PISE du Master MECI (Université Paris 7)            


 

 

 

Référence du langage

Structures de données

1. Les variables

En C#, la déclaration des variables est obligatoire. Cette déclaration nous confronte à deux problèmes : le choix du type de la variable, et celui de sa portée.

1.1 Types de variables

C# propose une série de types tout à fait classique, dont voici les principaux :

 

Type Plage des valeurs
Entiers
sbyte-128 à 127
short-32 768 à 32 767
int- 2 147 483 648 à 2 147 483 647
long-9 223 372 036 854 775 808 à -9 223 372 036 854 775 807 à
Décimaux
float-3,4028235E+38 à 3,4028234E+38
double-1,79769313486231570E+308 à 1,7976931348623157E+308
Logique
boolTrue / False
Caractère
stringde 0 à 2 147 483 648 caractères

 

Cette liste est loin d'être limitative. En particulier, elle omet une dimension essentielle du C#, celle des variables servant à manipuler des objets ‐ sans parler du fait étrange que même les types simples qu'on vient d'exposer sont en réalité, eux aussi des objets. Mais chaque chose en son temps ; ces points sont traités sur cette autre page de l'aide-mémoire.

1.2 Nom des variables : gare à la casse !

Le C# ne montre pas non plus d'originalité particulière en ce qui concerne le nom des variables : il interdit seulement de commencer par un chiffre et d'utiliser un signe de ponctuation (à part « _ »). En revanche, il fait partie des langages dits sensibles à la casse.

La variable toto ne sera donc pas la même chose que la variable Toto, ce qui est annonciateur de violentes crises de nerfs lors de débogages nocturnes. Une habitude consiste à pratiquer le CamelCase, en français la « casse de chameau ». Il n'agit pas de massacrer ces braves bêtes au marteau : la casse dont il s'agit est celle de typographes : elle désigne la distinction entre majuscules et minuscules. La « casse de chameau », c'est donc le fait de mettre les initiales des mots en majuscules, pour avoir des noms de variables lisibles, comme tauxEmpruntVariable, ou sommeElements (par convention supplémenaire, on laisse le premier mot en minuscules). Et cela dessine un joli dos de chameau – ou de dromadaire, s'il n'y a qu'un mot, mais un dromadaire, c'est un chameau d'Arabie, alors arrêtez de m'embêter.

1.3 Portée des variables

En déclarant une variable, on ne lui assigne pas seulement un type, mais également une portée, c'est-à-dire une durée de vie : au cours de l'exécution d'une application, certaines variables seront ainsi conservées du début à la fin, tandis que d'autres seront créées et détruites des dizaines ou des centaines de fois.

En C#, la portée d'une variable correspond au bloc de code dans lequel elle est déclarée. Autrement dit, et à l'envers, on ne peut pas accéder à une variable par une instruction qui se trouve en-dehors du bloc de code dans laquelle cette variable a été déclarée. Moralité, plus la variable doit être publique, plus il convient de la déclarer dans un bloc « englobant ». Le bloc le plus vaste dans une Form est celui qui instancie cette Form  il porte un titre qui ressemble à :

public partial class Form1 : Form

Si l'on souhaite que la variable, en plus d'être accessible par toutes les procédures d'une Form, le soit également par les procédures liées à d'autres Form, il conviendra de préciser que cette variable est statique, c'est-à-dire indépendante des instanciations / destructions de la classe dans laquelle cette variable a été créée.

La déclaration d'une telle variable se fera sur ce modèle :

static double taux = 0.196;

1.4 Déclaration des variables

La déclaration d'une variable s'effectue en la faisant précéder du type voulu :

int monEntier;
string maPhrase

Il est toutefois possible d'initialiser une variable, c'est-à-dire de lui affecter diectement une valeur lors de sa création :

int monEntier = 12;
string maPhrase = "Bonjour tout le monde !"

2. Les tableaux

2.1 Déclaration

Pour déclarer un tableau (ou variable indicée), c'est un peu plus délicat. En C#, un tableau est en effet directement un objet : pour le créer, on devra procéder à une instanciation de la classe correspondante, ce qui passe par l'emploi du constructeur New. La création du tableau nécessite trois étapes :

  • la déclaration de la variable indicée
  • l'affectation de cette variable à une instanciation de la classe tableau correspondante
  • le remplissage (l'affectation) des cases du tableau

Sous sa forme la plus développée, le code distingue ces trois étapes. Si l'on veut déclarer un tableau d'entiers nommé températures et contenant quatre relevés, on pourra écrire :

int[] temperatures;
temperatures = new int[4];
temperatures[0] = 15;
temperatures[1] = -6;
temperatures[2] = 5;
temperatures[3] = 28;

On notera donc que la syntaxe des tableaux se caractérise par l'emploi des crochets. On notera aussi que lors de l'instanciation, on fournit en argument le nombre de cases (et non, comme dans mon cours d'algorithmique, le plus grand indice).

L'écriture ci-dessus peut toutefois être contractée, par exemple en fusionnant la déclaration de la variable et l'instanciation de la classe :

int[] temperatures = new int[4];
temperatures[0] = 15;
temperatures[1] = -6;
temperatures[2] = 5;
temperatures[3] = 28;

On peut même aller encore plus loin en procédant, tout comme pour les variables, à l'affectation dès la déclaration, sous la forme suivante :

int[] temperatures = new int[4] {15, -6, 5, 28};

2.2 Tableaux à plusieurs dimensions

Les principes précédents restent valables ; les différents indices sont simplement séparés par des virgules. En revanche, il devient très délicat d'affecter directement le tableau lors de sa création. Plutôt que de se torturer sur des syntaxes épouvantables, on évitera donc ce genre d'acrobaties.

2.3 Méthodes pour tableaux

Le fait que les tableaux soient des objets a certes un tout petit peu alourdi la phase de la déclaration. Mais le gros avantage de la chose, c'est que tout tableau va avoir à sa disposition les méthodes et les propriétés écrites par les concepteurs du langage pour la classe dont il est issu. Deux d'entre elles sont particulièrement utiles :

  • Length : cette propriété renvoie, sous forme d'entier, le nombre d'éléments d'un tableau. C'est donc un « détecteur » très pratique pour écrire des boucles sur un tableau dont on ne connaît pas a priori le nombre d'éléments.
  • GetLength(n) : méthode qui renvoie, pour un tableau multidimensionel, le nombre d'élements pour la dimension spécifiée
  • IndexOf(élément) : méthode qui effecte une recherche, et qui renvoie l'indice de l'élément spécifié dans le tableau (et bing, tout un morceau d'algorithmique qui devient subitement inutile). À noter que cette méthode ne renvoie que l'indice de la première occurrence de la valeur ; si la valeur figure plusieurs autres fois dans le tableau, on ne saura pas à quels emplacement en utilisant cette méthode.
  • Sort(tableau) : méthode s'appliquant à la classe Array, et qui trie le tableau passé en argument (et une nouvelle grosse fatigue pour le prof d'algorithmique, qui se sent d'un seul coup devenu inutile à la société).

On pourra ainsi avoir les écritures suivantes :

NbElements = monTableau.Length;

position = monTableau.IndexOf("Durand");

Array.Sort(monTableau);

2.4 Tableaux dynamiques

C'est bien simple : il n'existe pas de tableaux dynamiques en C#. Moralité, quand on a besoin de tableaux dynamiques... On utilise autre chose que des tableaux.

2.5 Collections

En fait, les tableaux ne sont qu'un des nombreux outils que propose C# pour organiser une série des données du même type. Plus exactement, ils sont une variante particulière d'une catégorie catégorie plus générale : les collections.

Nous ne rentrerons pas ici dans le détail des collections ; l'une d'entre elles, qui nous intéresse tout particulièrement pour le dévelopement des Form Windows, concerne les collections de contrôles, présentées dans ce chapitre.

On peut simplement retenir pour l'instant que les tableaux et les collections possèdent deux grands points communs :

  • les tableaux et les collections sont des regroupements d'éléments
  • Les tableaux et les collections sont des organisations indicées. On peut donc accéder à tout élément via son indice.

... et trois grandes différences :

  • les tableaux ne regoupent que des éléments du même type. Les collections peuvent rassembler des éléments de types différents.
  • les tableaux peuvent comporter plusieurs dimensions ; les collections sont limitées à une seule dimension.
  • les tableaux, une fois créés, ne peuvent être redimensionnés. Les collections, en revanche, acceptent l'ajout et la suppression d'éléments au cours de l'exécution.

3. Les structures

Qui peut le plus peut le moins, et C#, en tant que langage objet, peut évidemment gérer des structures, c'est-à-dire (faut-il le rappeler aux programmeurs expérimentés qui lisent ces lignes), des types composites rassemblant des variables individuelles de différents types.

Il faudra donc dans un premier temps expliciter la manière dont est construite la structure, en prenant soin de déclarer toutes les variables individuelles (les champs) en public :

structure mesDisques
{
   public string titre;
   public string artiste;
   public int annee;
   public int duree;
   etc.
}

Au passage, on notera qu'il n'existe aucun équivalent aux string de largeur fixe en C#. Poursuivons : une fois la structure créée, on pourra crééer à partir d'elle autant de variables que l'on voudra :

mesDisques disk01 = new mesDisques();
mesDisques disk02 = new mesDisques();
etc.

Et on pourra ensuite accéder aux différents champs des variables sans aucun souci :

disk02.annee = 1996;

4. Les énumérations

Terminons cette revue des troupes avec un concept qui existe dans tous les langages, et que nous allons rencontrer plus souvent qu'à son tour en C# : les énumérations.

Le principe de base ne soulève pas de difficulté particulière : le principe d'une énumération est d'associer une série d'entiers à une série de libellés. Il s'agit donc d'une table de correspondance entre des textes et des nombres. Le but est de permettre au développeur (voire de l'obliger) à affecter certaines variables avec certains nombres entiers, tout en écrivant des libellés beaucoup plus explicites. Pour faire une analogie qui vaut ce qu'elle vaut (et réciproquement), on pourrait penser à un téléphone dépourvu de clavier, dans lequel on ne pourrait appeler que des membres du carnet d'adresse mémorisé dans l'appareil. L'utilisateur du téléphone (le programmeur) rechercherait les gens par leur nom, tandis que le téléphone (le compilateur), lui, transformerait aussitôt ce nom en un numéro de téléphone à composer.

Imaginons donc que nous programmions un jeu dans lequel les différents pions ne peuvent posséder que huit couleurs définies à l'avance. Il pourra alors être judicieux de créer une énumération récapitulant ces couleurs (attention, dans ce cas, les différentes valeurs sont séparées par des virgules) :

enum couleurPion
{
   blanc,
   noir,
   jaune,
   bleu,
   rouge,
   vert,
   rose,
   gris
}

Ce faisant, j'ai implicitement attribué la valeur 0 au libellé blanc, la valeur 1 au libellé noir, etc. Il aurait été possible d'attribuer à chaque libellé des nombres différents de la suite 0, 1, 2, etc., en écrivant :

const int blanc = 23;
const int noir = 29;
etc.

Bon, encore faut-il que ce soit utile. Revenons au cas standard, celui de l'énumération par défaut. À présent que celle-ci est créée, on peut l'utiliser comme un type de variable :

couleurPion pionAlbert

À partir de là, on ne peut affecter à pionAlbert qu'un membre de l'énumération mesCouleurs, c'est-à-dire une des valeurs contenues dans couleurPion. Au passage, lorsqu'on commencé à écrire l'instruction d'affectation et que l'on tape le nom de l'énumération, une liste déroulante apparaît, proposant exclusivement ses membres. On aura donc un code qui ressemblera à :

pionAlbert = couleurPion.rouge

Ce qu'il faut bien comprendre avec les énumérations, c'est que les variables créées à partir d'elles sont en réalité des entiers. En l'occurrence, l'instruction précédente revient à affecter la valeur 4 à la variable pionAlbert. Mais le fait d'avoir créé une énumération interdit dorénavant de traiter directement cette variable comme l'entier qu'elle est : on doit obligatoirement passer par l'énumération. En plus de donner davantage de lisibilité au code, cela assure qu'aucune valeur erronnée ne pourra jamais être affectée à cette variable.