Sie sind auf Seite 1von 362

Gilles Tourreau

LE GUIDE DE SURVIE

C#

LESSENTIEL DU CODE ET DES CLASSES

C#
Gilles Tourreau

Pearson Education France a apport le plus grand soin la ralisation de ce livre afin de vous fournir une information complte et fiable. Cependant, Pearson Education France nassume de responsabilits, ni pour son utilisation, ni pour les contrefaons de brevets ou atteintes aux droits de tierces personnes qui pourraient rsulter de cette utilisation. Les exemples ou les programmes prsents dans cet ouvrage sont fournis pour illustrer les descrip tions thoriques. Ils ne sont en aucun cas destins une utilisation commerciale ou professionnelle. Pearson Education France ne pourra en aucun cas tre tenu pour responsable des prjudices ou dommages de quelque nature que ce soit pouvant rsulter de lutilisation de ces exemples ou programmes. Tous les noms de produits ou marques cits dans ce livre sont des marques dposes par leurs pro pritaires respectifs.

Publi par Pearson Education France 47 bis, rue des Vinaigriers 75010 PARIS Tl. : 01 72 74 90 00 www.pearson.fr Avec la contribution technique de Nicolas Etienne Collaboration ditoriale : Jean-Philippe Moreux Ralisation pao : La B ISBN : 978-2-7440-4163-1 Copyright 2010 Pearson Education France Tous droits rservs

Aucune reprsentation ou reproduction, mme partielle, autre que celles prvues larticle L. 122-5 2 et 3 a) du code de la proprit intellectuelle ne peut tre faite sans lautorisation expresse de Pearson Education France ou, le cas chant, sans le respect des modalits prvues larticle L.122-10 dudit code.

Table des matires

Introduction . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
Objectif de ce livre. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Organisation de ce livre . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Remerciements. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Ressources . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . propos de lauteur . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

1 1 2 3 3 4 5 5 6 7 8 10 10 12 13 16 19 20 21 23 24 25

lments du langage . . . . . . . . . . . . . . . . . . . . . . . . . . .
Hello world ! . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Les commentaires . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Les identicateurs . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Les variables . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Dclarer une variable avec var (C# 3.0) . . . . . . . . . . . . . . . Les types primitifs . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Les constantes . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Les tests et conditions . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Les boucles . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Les tableaux unidimensionnels . . . . . . . . . . . . . . . . . . . . . . . Les tableaux multidimensionnels . . . . . . . . . . . . . . . . . . . . . Les tableaux en escalier (ou tableaux de tableaux) . . . . . Les oprateurs arithmtiques . . . . . . . . . . . . . . . . . . . . . . . . Les oprateurs logiques . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Les oprateurs binaires . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

Les classes . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 27
Dclarer et instancier des classes . . . . . . . . . . . . . . . . . . . . . Grer les noms de classe laide des espaces de noms . Dclarer et utiliser des champs . . . . . . . . . . . . . . . . . . . . . . . Dclarer et appeler des mthodes . . . . . . . . . . . . . . . . . . . .
28 29 31 33

IV

C#

Dclarer des classes et membres statiques . . . . . . . . . . . . . 34 Accder linstance courante avecthis . . . . . . . . . . . . . . . 36 Dfinir les niveaux devisibilit des membres . . . . . . . . . . . 37 Dclarer et appeler desconstructeurs . . . . . . . . . . . . . . . . . 38 Dclarer unchamp enlecture seule . . . . . . . . . . . . . . . . . . . 39 Dclarer et utiliser des proprits . . . . . . . . . . . . . . . . . . . . . 40 Implmenter automatiquement des proprits (C#3.0) . . 44 Initialiser des proprits lors delacration dunobjet (C#3.0) . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 46 Les indexeurs . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 48 Les dlgus . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 50 Dclarer des mthodes anonymes . . . . . . . . . . . . . . . . . . . . . 52 Les vnements . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 57 Surcharger unemthode . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 60 Dclarer des paramtres facultatifs (C#4.0) . . . . . . . . . . . 62 Utiliser des paramtres nomms (C#4.0) . . . . . . . . . . . . . 64 Surcharger unconstructeur . . . . . . . . . . . . . . . . . . . . . . . . . . 66 Surcharger unoprateur . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 68 Les numrations . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 75 Les classes imbriques . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 78 Les classes partielles . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 80 Crer untype anonyme (C#3.0) . . . . . . . . . . . . . . . . . . . . . 82 Les structures . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 83 Passer des paramtres parrfrence . . . . . . . . . . . . . . . . . . 87 Loprateur defusion null . . . . . . . . . . . . . . . . . . . . . . . . . . . . 90 Les mthodes partielles (C#3.0) . . . . . . . . . . . . . . . . . . . . . 92 Les mthodes dextension (C#3.5) . . . . . . . . . . . . . . . . . . . 94

3 Lhritage . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 97
Utiliser lhritage . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 97 Redfinir unemthode . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 100 Redfinir uneproprit . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 103 Appeler leconstructeur delaclasse debase . . . . . . . . . . . 105 Masquer unemthode . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 106 Masquer uneproprit . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 109 Utiliser les interfaces . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 112

Table des matires

Implmenter uneinterface . . . . . . . . . . . . . . . . . . . . . . . . . . . 113 Implmenter uneinterface explicitement . . . . . . . . . . . . . . 116 Les classes, mthodes etproprits abstraites . . . . . . . . . . 118 Les classes scelles . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 122 Tester untype avecloprateuris . . . . . . . . . . . . . . . . . . . . . 123 Caster uneinstance avecloprateur as . . . . . . . . . . . . . . . 124

4 La gestion deserreurs . . . . . . . . . . . . . . . . . . . . . . . . . . 125


Dclencher uneexception . . . . . . . . . . . . . . . . . . . . . . . . . . . 127 Capturer uneexception . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 128 Laclause finally . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 132 Proprits et mthodes delaclasse Exception . . . . . . . . . . 134 Propager uneexception aprs sa capture . . . . . . . . . . . . . . 136

5 Les gnriques . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 141


Utiliser les classes gnriques . . . . . . . . . . . . . . . . . . . . . . . . 143 Dclarer et utiliser des mthodes gnriques . . . . . . . . . . . 147 Contraindre des paramtres gnriques . . . . . . . . . . . . . . . 149 Utiliser lemot-cl default . . . . . . . . . . . . . . . . . . . . . . . . . . . 151 Utiliser les dlgus gnriques (.NET3.5) . . . . . . . . . . . . . 152 Utiliser lacovariance (C#4.0) . . . . . . . . . . . . . . . . . . . . . . . 154 Utiliser lacontravariance (C#4.0) . . . . . . . . . . . . . . . . . . . . 159

6 Les chanes de caractres . . . . . . . . . . . . . . . . . . . . . . 163


Crer unechane decaractres . . . . . . . . . . . . . . . . . . . . . . . 164 Obtenir lalongueur dunechane decaractres . . . . . . . . 166 Obtenir uncaractre . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 166 Comparer deux chanes decaractres . . . . . . . . . . . . . . . . . 167 Concatner deux chanes decaractres . . . . . . . . . . . . . . . 170 Extraire unesous-chane decaractres . . . . . . . . . . . . . . . . 171 Rechercher unechane decaractres dans uneautre . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 172 Formater unechane decaractres . . . . . . . . . . . . . . . . . . . . 174 Construire unechane avecStringBuilder . . . . . . . . . . . . . . 178 Encoder et dcoder unechane . . . . . . . . . . . . . . . . . . . . . . . 180

VI

C#

7 LINQ (Language Integrated Query) . . . . . . . . . . . . . 183


Slectionner des objets (projection) . . . . . . . . . . . . . . . . . . 184 Filtrer des objets . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 186 Trier des objets . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 188 Effectuer unejointure . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 189 Rcuprer lepremier ouledernier objet . . . . . . . . . . . . . . . 191 Compter lenombre dobjets . . . . . . . . . . . . . . . . . . . . . . . . . . 193 Effectuer unesomme . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 194 Grouper des objets . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 194 Dterminer si unesquence contient unobjet . . . . . . . . . 198 Dclarer unevariabledeporte . . . . . . . . . . . . . . . . . . . . . . . 198

8 Les classes et interfaces de base . . . . . . . . . . . . . . . 201


Laclasse Object . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 201 Laclasse Array . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 205 Laclasse Enum . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 209 Laclasse TimeSpan . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 212 Laclasse DateTime . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 214 Laclasse Nullable<T> . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 217 Linterface IDisposable . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 219 Linterface IClonable . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 222 Laclasse BitConverter . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 226 Laclasse Buffer . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 228

9 Les collections . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 231


Les itrateurs . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 231 Les listes: List<T> . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 240 Les dictionnaires: Dictionary<TCl, TValeur> . . . . . . . . . . . 243 Les piles: Stack<T> . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 246 Les files: Queue<T> . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 247 Initialiser unecollection lorsdesa cration (C#3.0) . . . 249

10 Les flux . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 251


Utiliser les flux (Stream) . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 252 Utiliser les flux defichier (FileStream) . . . . . . . . . . . . . . . . . 253

Table des matires

VII

Utiliser les flux enmmoire (MemoryStream) . . . . . . . . . . 255 crire surunflux avecStreamWriter . . . . . . . . . . . . . . . . . . 256 Lire surunflux avecStreamReader . . . . . . . . . . . . . . . . . . . 258 crire surunflux avecBinaryWriter . . . . . . . . . . . . . . . . . . . 260 Lire unflux avecBinaryReader . . . . . . . . . . . . . . . . . . . . . . . 262

11 Les fichiers et rpertoires . . . . . . . . . . . . . . . . . . . . . . 265


Manipuler les fichiers (File) . . . . . . . . . . . . . . . . . . . . . . . . . . 265 Manipuler les rpertoires (Directory) . . . . . . . . . . . . . . . . . . 268 Obtenir des informations surunfichier (FileInfo) . . . . . . . 272 Obtenir des informations surunrpertoire (DirectoryInfo) . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 275 Obtenir des informations surunlecteur (DriveInfo) . . . . 277

12 Les threads . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 281


Crer et dmarrer unthread . . . . . . . . . . . . . . . . . . . . . . . . . . 282 Mettre enpause unthread . . . . . . . . . . . . . . . . . . . . . . . . . . . 284 Attendre lafin dunthread . . . . . . . . . . . . . . . . . . . . . . . . . . . 285 Rcuprer lethread encours dexcution . . . . . . . . . . . . . . 287 Crer des variables statiques associes unthread . . . . . 288 Utilisez les smaphores (Semaphore) . . . . . . . . . . . . . . . . . 290 Utiliser les mutex (Mutex) . . . . . . . . . . . . . . . . . . . . . . . . . . . 294 Utiliser les moniteurs (Monitor) . . . . . . . . . . . . . . . . . . . . . . 297 Appeler unemthodedefaon asynchrone . . . . . . . . . . . . 302

13 La srialisation . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 307
Dclarer uneclasse srialisable avec SerializableAttribute . . . . . . . . . . . . . . . . . . . . . . . . . . . . 308 Srialiser et dsrialiser unobjet avecBinaryFormatter . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 309 Personnaliser leprocessus desrialisation aveclinterface ISerializable . . . . . . . . . . . . . . . . . . . . . . . . . 312 Dclarer uneclasse srialisable avec DataContractAttribute (.NET3.0) . . . . . . . . . . . . . . . . 315 Srialiser et dsrialiser unobjet avecDataContractSerializer (.NET3.0). . . . . . . . . . . . . . . . 317

VIII

C#

14 Lintrospection . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 321
Rcuprer ladescription duntype . . . . . . . . . . . . . . . . . . . 322 Rcuprer ladescription dunassembly . . . . . . . . . . . . . . . . 325 Rcuprer et appeler unconstructeur . . . . . . . . . . . . . . . . . 327 Instancier unobjet partir deson Type . . . . . . . . . . . . . . . 330 Rcuprer et appeler unemthode . . . . . . . . . . . . . . . . . . . . 331 Dfinir et appliquer unattribut . . . . . . . . . . . . . . . . . . . . . . . 334 Rcuprer des attributs . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 338 Lemot-cl dynamic (C#4.0) . . . . . . . . . . . . . . . . . . . . . . . . . 341

Index . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 343

Introduction
C# ( prononcer C-sharp ) est un langage cr par Microsoft en 2001 et normalis par lECMA (ECMA334) et par lISO/CEI (ISO/CEI 23270). Il est trs proche de Java et de C++, dont il reprend la syntaxe gnrale ainsi que les concepts orients objet. Depuis sa cration, et contrairement dautres langages de program mation, C# a beaucoup volu travers les diffrentes versions du .NET Framework, en particulier dans la version3.5 o est introduit un langage de requte intgr appel LINQ. Bien videmment, il y a fort parier que Microsoft ne sarrtera pas l et proposera certainement dans les versions ultrieures dautres nouveauts! C# est lun des langages qui permet de manipuler la bibliothque des classes du .NET Framework, plateforme de base permettant dunifier la conception dapplications Windows ou Web.

Objectif dece livre


Il nexiste pas douvrages qui permettent aux dveloppeurs dapprendre le C# trs rapidement, pour ceux disposant dj dun minimum de connaissance en algorithmique ou en programmation oriente objet. Le plus souvent, pas loin de la moiti du contenu des livres disponibles est consacre dtailler les bases de la programmation. Ce genre de livres peut tre rbarbatif pour les dveloppeurs ayant un minimum dexprience.

C#

Lobjectif de ce titre de la collection des Guides de survie est donc de prsenter les fonctionnalits et les concepts de base de C# aux dveloppeurs familiers de la programmation. Il peut tre lu de manire linaire, mais il est possible de lire isolment un passage ou un chapitre particulier. Par ailleurs, les sections de ce livre sont conues pour tre indpendantes: il nest donc pas ncessaire de lire les sections prcdentes pour comprendre les diffrents exemples de code dune section donne. En crivant ce livre, jai essay de satisfaire plusieurs besoins plus ou moins opposs: le format des Guides de survie imposant une approche trs pragmatique du langage, des extraits et exemples de code sont fournis quasiment chaque section (ce qui est une trs bonne chose!). Ce livre est consacr aux versions2.0, 3.0, 3.5 et4.0 de C# (et du .NET Framework).

Organisation dece livre


Ce livre est divis en deux grandes parties: la premire est consacre exclusivement au langage C# et se divise en sept chapitres qui prsentent les lments du langage, la programmation oriente objet, la gestion des erreurs, les gnriques, les chanes de caractres et le langage de requte intgr LINQ. La seconde partie est consacre diverses classes de base permettant de manipuler certaines fonctionnalits du .NET Framework telles que les collections, les flux, les fichiers et rpertoires, les threads, la srialisation et lintrospection.

Introduction

Remerciements
Je souhaite remercier les ditions Pearson pour mavoir permis de vivre laventure qua t la rdaction de cet ouvrage, ainsi que Nicolas Etienne et Jean-Philippe Moreux pour leur relecture. Je tenais aussi remercier Martine Tiphaine qui ma mis en contact avec les ditions Pearson.

Ressources
Le site http://msdn.microsoft.com/fr-fr/library est le site de rfrence pour accder la documentation officielle de C# et du .NET Framework. Le site http://social.msdn.microsoft.com/forums/ fr-fr/categories est un ensemble de forums consacrs aux dveloppements des technologies Microsoft, auxquels je participe activement.

C#

propos de lauteur
Expert reconnu par Microsoft, Gilles Tourreau sest vu attribuer le label MVP C# (Most Valuable Professional) durant trois annes conscutives (2008, 2009 et 2010). Architecte .NET et formateur dans une socit de services, il intervient pour des missions dexpertise sur diffrentes technologies .NET telles quASP .NET, Windows Communication Foundation, Windows Workow Foundation et Entity Framework ; il opre chez des clients importants dans de nombreux secteurs dactivit. Gilles Tourreau est trs actif dans la communaut Microsoft, en particulier sur les forums MSDN. Il publie galement sur son blog personnel (http://gilles.tourreau.fr) des articles et billets concernant le .NET Framework.

1
lments du langage
Hello world!
using System; public class MaClasse { public static void Main(string[] args) { Console.WriteLine(Hello world !); } }

Ce code affiche sur la console Hello world!. La premire ligne permet dutiliser toutes les classes contenues dans lespace de noms System du .NET Framework. La deuxime ligne permet de dfinir une classe contenant des variables et des mthodes.

CHAPITRE 1 lments du langage

Dans cet exemple, la classe MaClasse contient une mthode statique Main() qui reprsente le point dentre de toute application console .NET, cest--dire que cette mthode sera appele automatiquement lors du lancement du programme.

Les commentaires
// Un commentaire /* Un commentaire sur plusieurs lignes */ /// <summary> /// Commentaire pour documenter un identificateur /// </summary>

Les commentaires sont des lignes de code qui sont ignores par le compilateur et permettent de documenter votre code. Les commentaires peuvent tre: entours dun slash suivi dun astrisque /* et dun astrisque suivi dun slash */. Cela permet dcrire un commentaire sur plusieurs lignes; placs aprs un double slash // jusqu la fin de la ligne. Les commentaires prcds par un triple slash sont des commentaires XML qui permettent de documenter des identificateurs tels quune classe ou une mthode. Le compilateur rcupre ces commentaires et les place dans un document XML quil sera possible de traiter afin de gnrer une documentation dans un format particulier (HTML, par exemple).

Les identificateurs

Les identificateurs
Les identificateurs permettent dassocier un nom une donne. Ces noms doivent respecter certaines rgles dictes par le langage. Tous les caractres alphanumriques Unicode UTF-16 sont autoriss (y compris les caractres accentus). Le soulign _ est le seul caractre non alphanumrique autoris. Un identificateur doit commencer par une lettre ou le caractre soulign. Les identificateurs respectent la casse; ainsi mon_identificateur est diffrent de MON_IDENTIFICATEUR. Voici des exemples didentificateurs:
// // // // identificateur5 // _mon_identificateur // mon_identificateur // mon identificateur // // *mon-identificateur // // identificateur IDENTificateur 5Identificateurs Correct Correct Incorrect: commence par un chiffre Correct Correct Correct Incorrect: contient un espace Incorrect: contient des caractres incorrects

Les identificateurs ne doivent pas correspondre certains mots-cls du langage C#, dont le Tableau1.1 donne la liste.

CHAPITRE 1 lments du langage

Tableau 1.1: Liste des noms didentificateur non autoriss abstract bool break byte casecatch double char checked class const continue decimal default delegate do else enum event if false finally fixed foreach goto int long explicit extern float for implicit in lock object private return throw virtual null ref

interface internal is namespace new readonly override params

operator out protected public

sbyte sealed short sizeof static string struct switch this true try unchecked unsafe void while ushort using typeof uint ulong

Les variables
// Dclarer une variable <type> <nomVariable>; // Affecter une valeur une variable <nomVariable> = <uneValeur>;

Une variable est un emplacement mmoire contenant une donne et nomm laide dun identificateur. Chaque variable doit tre dun type pralablement dfini qui ne peut changer au cours du temps.

Les variables

Pour crer une variable, il faut dabord la dclarer. La dclaration consiste dfinir le type et le nom de la variable.
int unEntier; // Dclaration dune variable nomme // unEntier et de type int

On utilise loprateur daffectation= pour affecter une valeur une variable. Pour utiliser cet oprateur, il faut que le type de la partie gauche et le type de la partie droite de loprateur soient les mmes.
int unEntier; int autreEntier; double unReel; // Affectation de la valeur 10 la variable unEntier unEntier = 10; // Affectation de la valeur de la variable unEntier // dans autreEntier autreEntier = unEntier // Erreur de compilation: les types ne sont pas // identiques des deux cts de loprateur = autreEntier = unReel

Lidentificateur dune variable doit tre unique dans une porte daccolades ouvrante{ et fermante}.
{ int entier1; ... int entier1; { int entier1; } } // Correct // Incorrect // Incorrect

10

CHAPITRE 1 lments du langage

Dclarer unevariableavecvar (C#3.0)


// Dclarer une variable avec var var <nomVariable> = <valeur>;

Le mot-cl var permet de dclarer une variable type. Le type est dtermin automatiquement par le compilateur grce au type de la valeur qui lui est affect. Laffectation doit forcment avoir lieu au moment de la dclaration de la variable:
var monEntier = 10;

tant donn que cette variable est type, le compilateur vrifie si lutilisation de cette dernire est correcte. Lexemple suivant illustre cette vrification.
var monEntier = 10; monEntier = 1664; // Correct monEntier = c; // Erreur de compilation car // monEntier est de type int

Attention vitez dutiliser le mot-cl var car cela rend le code plus difficile comprendre; il est en effet plus difficile de connatre immdiatement le type dune variable.

Les types primitifs


Le langage C# inclut des types primitifs qui permettent de reprsenter des donnes informatiques de base (cest-dire les nombres et les caractres). Le programmeur devra

Les types primitifs

11

utiliser ces types de base afin de crer de nouveaux types plus complexes laide des classes ou des structures. Les types primitifs offerts par C# sont lists au Tableau1.2.
Tableau 1.2: Les types primitifs de C#
Type Porte Description Boolen 8bits Entier 8bits sign Entier 8bits non sign Caractre Unicode 16 bits Entier 16bits sign Entier 16bits non sign Entier 32bits sign Entier 32bits non sign
38

bool sbyte byte char short ushort int uint float long ulong double decimal

true or false
128 127 0 255 U+0000 U+ffff 32768 32767 0 65535 2 2 1
31 31

0 232 1 1,5e
-45

3,4e

Rel 32bits sign (virgule flottante) Entier 64bits sign Entier 64bits sign Rel 64bits sign (virgule flottante) Rel 128bits sign (grande prcision)

263 263 1 0 2 1
64

5,0e-324 1,7e308 1,0e-28 7,9e28

Le choix dun type de variable dpend de la valeur qui sera contenue dans celle-ci. Il faut viter dutiliser des types occupant beaucoup de place mmoire pour reprsenter des donnes dont les valeurs sont trs petites. Par exemple, si lon veut crer une variable stockant lge dun tre humain, une variable de type byte suffit amplement.

12

CHAPITRE 1 lments du langage

Les constantes
// Dclarer une constante nomme const <type> <nomConstante> = <valeur> A a 10 0x0A 10U 10L 10UL 30.51 3.51e1 30.51F 30.51M // // // // // // // // // // // Lettre majuscule A Lettre minuscule a Entier 10 Entier 10 (exprim en hexadcimale) Entier 10 de type uint Entier 10 de type long Entier 10 de type ulong Rel 30.51 de type double Rel 30.51 de type double Rel 30.51 de type float Rel 30.51 de type decimal

En C#, il existe deux catgories de constantes: les constantes non nommes qui possdent un type et une valeur et les constantes nommes qui possdent en plus un identificateur. Lors de laffectation dune constante une variable, le type de la constante et celui de la variable doivent correspondre.
long entier; entier = 10L; entier = 30.51M;

// // // //

Correct: la constante est de type long Incorrect: la constante est de type decimal

Une constante nomme se dclare presque comme une variable, except quil faut obligatoirement linitialiser avec une valeur au moment de sa dclaration. Une fois dclare, il nest plus possible de modifier la valeur dune constante.

Les tests et conditions

13

const double pi = 3.14159; const double constante; // Incorrect: doit tre // initialis double primtre; primtre = pi * 20; // Incorrect: il est pi = 9.2; // impossible de changer // la valeur dune constante

Les tests et conditions


if (<condition>) { // Code excut si condition est vrai } [else if (<autreCondition>) { // Code excut si autreCondition est vraie }] [else { // Code excut si condition et autreCondition // sont fausses }] <rsultat> = <test>? <valeur si vrai>: <valeur si faux> switch (<uneValeur>) { case <val1>: // Code excut si uneValeur est gale val1 break; case <val2>:

14

CHAPITRE 1 lments du langage

case <val3>: // Code excut si uneValeur est gale // val2 ou val3 break; [default: // Code excut si uneValeur est diffrente // de val1, val2 et val3 break;] }

Linstruction if permet dexcuter des instructions uniquement si la condition qui la suit est vraie. Si la condition est fausse alors, les instructions contenues dans le bloc else sont excutes. Le bloc else est facultatif; en labsence dun tel bloc, si la condition spcifie dans le if est fausse, aucune instruction ne sera excute. La condition contenue dans le if doit tre de type boolen. Lexemple suivant affiche des messages diffrents en fonction dun ge contenu dans une variable de type int.
if (ge <= 50) { Console.WriteLine(Vous tes jeune !); } else { Console.WriteLine(Vous tes vieux;-) !); }

Il existe une variante condense du if qui utilise les sym boles(?) et(:). Elle permet en une seule ligne de retourner un rsultat en fonction dune condition. Lexemple qui suit illustre cette variante en retournant false si la valeur contenue dans ge est infrieure 50 ou true dans le cas contraire.

Les tests et conditions

15

bool vieux; vieux = ge <= 50? false: true;

Linstruction switch permet de tester une valeur spcifie par rapport dautres valeurs. Si lune des valeurs correspond la valeur teste, alors le code associ est automatiquement excut. Si aucune valeur ne correspond la valeur teste, alors le code associ clause default (si elle existe) sera excut.
Attention Veillez ne pas oublier linstruction break entre chaque case, sinon les instructions associes aux valeurs suivantes seront excutes.

Le switch ne peut tre utilis quavec les types entiers, char, bool ainsi que les numrations et les chanes de caractres. Lexemple suivant affiche des messages diffrents en fonction du sexe dune personne contenu dans une variable de type char.
switch (sexe) { case M: Console.WriteLine(Vous tes un homme !); break; case F: Console.WriteLine(Vous tes une femme !); break; default: Console.WriteLine(Vous tes un extraterrestre !); break; }

16

CHAPITRE 1 lments du langage

Les boucles
while (<condition>) { // Corps de la boucle } do { // Corps de la boucle } while (<condition>); for(<initialisation>; <condition arrt>; <incrmentation>) { // Corps de la boucle } // Sortir de la boucle break; // Continuer litration suivante continue;

Les boucles permettent dexcuter du code de manire rptitive (des itrations) tant que la condition associe est vraie. Le corps de la boucle est donc excut tant que la condition est vraie. La boucle while permet de tester la condition avant dentrer dans la boucle. Si la condition est fausse avant dentrer dans la boucle, aucune itration ne sera excute. Lexemple suivant illustre lutilisation dune boucle while afin dafficher sur la console les chiffres allant de 1 5.

Les boucles

17

int i; i = 1; while (i <= 5) { Console.WriteLine(i); i = i + 1; }

La boucle dowhile permet dexcuter au moins une fois une itration de la boucle. Lexemple suivant illustre lutilisation dune boucle do while qui ne ralise quune seule itration car la condition de la boucle est fausse.
int i; i = 5; do { Console.WriteLine(Bonjour!); } while (i < 5);

La boucle for est lquivalent de la boucle while, mais elle permet de spcifier plusieurs instructions qui seront excutes linitialisation et litration de la boucle (le plus souvent une initialisation et une incrmentation dune variable). Le code suivant illustre lquivalent de la boucle for en utilisant la boucle while.
<initialisation>; while (<condition>) { // Corps de la boucle <incrmentation>; }

18

CHAPITRE 1 lments du langage

Lexemple suivant illustre lutilisation dune boucle for affichant sur la console les chiffres allant de1 5.
for(int i = 1; i <= 5; i++) { Console.WriteLine(i); }

Linstruction break permet de quitter la boucle tout moment (linstruction dincrmentation nest pas excute dans le cas dune boucle for). Linstruction continue permet de passer directement litration suivante (la condition est vrifie avant). Dans le cas dune boucle for, linstruction dincrmentation est excute avant la vrification de la condition. Lexemple suivant illustre lutilisation dune boucle for devant raliser mille itrations. Linstruction continue permet dempcher laffichage du message Ne sera pas affich! sur chaque itration. La boucle est arrte au bout de dix itrations en utilisant linstruction break.
for(int i = 1; i <= 1000; i++) { Console.WriteLine(Jitre!); // Arrter la boucle au bout de 10 itrations if (i == 10) { break; } // Passer litration suivante continue; Console.WriteLine(Ne sera pas affich!); }

Les tableaux unidimensionnels

19

Les tableaux unidimensionnels


// Dclarer un tableau une dimension <type>[] <nomTableau>; // Crer un tableau avec une taille spcifie <nomTableau> = new <type>[<taille>]; // Crer un tableau avec les valeurs spcifies <nomTableau> = new <type>[] { [valeur1][, valeur2] [, ...] }; // Affecter une valeur lindice spcifi <nomTableau>[<indice>] = <valeur>; // Obtenir la valeur lindice spcifi <valeur> = <nomTableau>[<indice>];

// Obtenir la taille du tableau <taille> = <nomTableau>.Length;

Les tableaux sont des variables contenant plusieurs valeurs (ou cases) de mme type. Il est possible daccder ou de modifier la valeur dune case dun tableau grce loprateur[] et en spcifiant un indice. Un indice est un entier compris entre0 et la taille du tableau1 et il reprsente le numro de la case du tableau accder o modifier. Un tableau a toujours une taille fixe. Il nest donc plus possible de le redimensionner! Cette taille peut tre rcupre laide de la proprit Length.

20

CHAPITRE 1 lments du langage

Lexemple suivant montre comment calculer la moyenne dune srie de notes dexamen contenue dans un tableau:
int[] notes = new int[] { 10, 5, 20, 15, 18 }; int total = 0; for(int i=0; i<notes.Length; i++) { total = total + notes[i]; } Console.WriteLine(Moyenne : + total / notes.Length);

Les tableaux multidimensionnels


// Dclarer un tableau deux dimensions <type>[,] <nomTableau>; //Crer un tableau deux dimensions <nomTableau> = new <type>[<tailleDim1>][<tailleDim2>]; // Crer un tableau avec les valeurs spcifies <nomTableau> = new <type>[,] { {<valeur0_0>,<valeur0_1>}, {<valeur1_0>,<valeur1_1>} }; // Affecter une valeur aux indices spcifis nomTableau[indice1, indice2] = valeur; // Obtenir la valeur aux indices spcifis valeur = nomTableau[indice1, indice2]; // Obtenir le nombre total de cases du tableau <taille = <nomTableau>.Length; // Obtenir le nombre dlments dans une dimension <taille = <nomTableau>.GetLength(<numDimension>);

Les tableaux enescalier (outableaux detableaux)

21

Il est possible de crer et dutiliser des tableaux plusieurs dimensions (accessible via plusieurs indices). Comme pour les tableaux unidimensionnels, les valeurs contenues dans ces tableaux sont accessibles laide de plusieurs indices dont les valeurs sont comprises entre0 et la taille dune dimension1 du tableau. Dans les tableaux multidimensionnels, la proprit Length retourne le nombre total de cases du tableau. Il faut utiliser la mthode GetLength() pour rcuprer la taille dune dimension particulire dun tableau multidimensionnel. Lexemple suivant illustre lutilisation dun tableau deux dimensions pour raliser la somme de deux matrices de taille 23.
int[,] matrice1 = new int[,] { { 10, 4, 1 }, { 3, 7, 9 } }; int[,] matrice2 = new int[,] { { 1, 5, 7 }, { 4, 8, 0 } }; int[,] resultat = new int[2, 3]; for (int i = 0; i < matrice1.GetLength(0); i++) { for (int j = 0; j < matrice1.GetLength(1); j++) { resultat[i, j] = matrice1[i, j] + matrice2[i, j]; } }

Les tableaux enescalier (outableaux detableaux)


// Dclarer un tableau de tableaux <type>[][] <nomTableau>; // Crer un tableau de tableaux <nomTableau> = new <type>[<taille>][]; // Crer un tableau imbriqu la case spcifie <nomTableau>[<indice>] = new <type>[<taille>];

22

CHAPITRE 1 lments du langage

// Affecter une valeur aux indices spcifis <nomTableau>[<indice1>][<indice2>] = <valeur>; // Obtenir la valeur aux indices spcifis <valeur> = <nomTableau>[<indice1>][<indice2>];

Comme son nom lindique, les tableaux en escalier sont des tableaux contenant des tableaux (qui peuvent contenir leur tour des tableaux, et ainsi de suite). Contrairement aux tableaux multidimensionnels, les tableaux en escalier peuvent avoir des dimensions de taille variable. Par exemple, il est possible de crer un tableau de deux tableaux dentiers de tailles4 et10. Les tableaux inclus dans un tableau en escalier doivent tre crs explicitement. Lexemple suivant montre comment crer un tableau en escalier contenant dix tableaux. Ces dix tableaux sont de la taille de lindice du tableau en escalier+1.
int[][] tableau = new int[10][]; // Pour chaque case du tableau en escalier, cre un // tableau de taille i + 1 for (int i = 0; i < 10; i++) { tableau[i] = new int[i + 1]; }

Les tableaux en escalier ayant des dimensions variables, il nexiste aucune proprit ou mthode permettant de connatre le nombre de cases dun tel tableau. Le code suivant montre comment calculer le nombre de cases dun tableau en escalier deux dimensions.

Les oprateurs arithmtiques

23

int nombreCase; for (int i = 0; i < tableau.Length; i++) { nombreCase = nombreCase + tableau[i].Length; } Console.WriteLine(nombreCase); // Affichage du nombre de cases

Les oprateurs arithmtiques


c c c c c a a a a = = = = = a a a a a + * / % b; b; b; b; b; // // // // // // // // // // // // // Addition Soustraction Multiplication Division Modulo (reste de la div. euclidienne) a a a a = = = = a a a a + * / b; b; b; b;

+= -= *= /=

b; b; b; b;

a++; ++a; a--; --a;

Post-incrmentation Pr-incrmentation Post-dcrmentation Pr-dcrmentation

Les oprateurs arithmtiques permettent de raliser des oprations mathmatiques de base: addition, soustraction, multiplication, division, modulo (reste de la division euclidienne).

24

CHAPITRE 1 lments du langage

Loprateur de post-incrmentation reprsente la valeur de loprande avant son incrmentation. Tandis que loprateur de pr-incrmentation reprsente la valeur de loprande aprs son incrmentation. Voici un exemple qui illustre lutilisation de certains de ces oprateurs:
int a; a = 5; a *= 10; // a = 5 * 10; // a = 10 car le reste de 50/40 est 10 a = a % 40; Console.WriteLine(a++); // Affiche 10; // Aprs laffichage, a = 11 Console.WriteLine(++a); // Affiche 12;

Les oprateurs logiques


c = a == b; // Test lgalit c = a != b; // Test lingalit c c c c = = = = a a a a < b; <= b; > b; >= b; // // // // Retourne Retourne Retourne Retourne true true true true si si si si a a a a infrieur b; inf. ou gal b suprieur b sup. ou gal b

a && b; a || b; !a

// Retourne true si a et b sont true // Retourne true si a ou b sont true // Retourne linverse de a

Les oprateurs logiques retournent tous des boolens (soit true, soit false). Ils sont trs utiliss dans les conditions if et les conditions des boucles. Ils peuvent tre combins grce aux oprateurs ET(&&) et OU(||).

Les oprateurs binaires

25

Loprande qui se trouve droite de loprateur ET(&&) nest pas valu dans le cas o loprande de gauche est faux. Loprande qui se trouve droite de loprateur OU(||) nest pas valu dans le cas o loprande de gauche est vrai. Les conditions ET (&&) sont prioritaires par rapport aux conditions OU(||). Utilisez les parenthses si ncessaire pour changer lordre de traitement des conditions.
int a = 16; int b = 64; int c = 51; if (a > b && (c!= b || a== b)) { Console.WriteLine(a est suprieur b ET); Console.WriteLien(c diffrent de b ou a gal b); }

Dans lexemple prcdent, on a utilis des parenthses afin que lexpression c!= b ne soit pas traite avec loprateur&& mais avec loprateur||. Loprande de droite de loprateur && ne sera jamais test, car loprande de gauche est dj faux. Lexpression tant fausse, aucun message ne sera affich sur la console.

Les oprateurs binaires


c c c c = = = = a & b; a | b; ~a; a ^ b; // ET binaire // OU binaire // NON binaire // XOR binaire (OU exclusif) // a = a & b; // a = a | b; // a = a ^ b; // Dcale a de b bits vers la gauche // Dcale a de b bits vers la droite

a &= b; a |= b; a ^= b; c = a << b; c = a >> b;

26

CHAPITRE 1 lments du langage

Les oprateurs binaires agissent sur les bits des types primitifs int, uint, long et ulong. Il est possible dutiliser ces oprateurs pour dautres types primitifs mais la valeur retourne sera un int. Utilisez loprateur cast si ncessaire (voir page 99). Lexemple suivant illustre lutilisation des divers oprateurs binaires.
short a, b, c; a = 3; // 0000 0011 b = 13; // 0000 1101 c c c c = = = = (byte)(a & b); (byte)(a | b); (byte)~a; (byte)(a ^ b); // // // // = = = = 1 15 252 14 (0000 (0000 (1111 (0000 0001) 1111) 1100) 1110)

c = (byte)b << 2; // = 52 (0011 0100) c = (byte)b >> 2; // = 3 (0000 0011)

2
Les classes
Concept de base de la programmation oriente objet, les classes permettent de dcrire les attributs et les oprations qui sont associs un objet. Par lexemple, lobjet Personne peut contenir: Nom, Prnom, Age et Sexe comme attributs, Marcher(), Manger(), Courir(), PasserLaTondeuse() comme oprations. Une classe peut tre vue comme un moule permettant instances dun objet. Par exemple, les de fabriquer des personnes Gilles et Claude sont des instances de la classe Personne prcdemment dcrite. Les attributs et les oprations dune classe sont des membres dune classe. Ces membres ont des niveaux de visibilit permettant dtre accessibles ou non depuis dautres classes.

28

CHAPITRE 2 Les classes

Dclarer et instancier des classes


<visibilit> <nom classe> { // Membres dune classe } // Dclarer une variable du type de la classe <nom classe> <variable>; // Crer une instance <variable> = new <nom classe>(); // Faire rfrence au mme objet <autre variable> = <variable>; // Faire rfrence aucun objet <variable> = null;

Lexemple suivant illustre la dclaration dune classe Personne ne contenant aucun membre.
class Personne { }

Voici un exemple illustrant la cration de deux instances de la classe Personne.


Personne gilles; Personne claude; gilles = new Personne(); claude = new Personne();

Grer les noms declasse laidedes espaces denoms

29

Il est important de noter que les variables de type dune classe ne contiennent pas rellement lobjet mais une rfrence vers un objet. Il est donc possible de dclarer deux variables de type Personne faisant rfrence au mme objet Personne. Loprateur daffectation ne ralise en aucun cas des copies dobjets.
Personne gilles; Personne gilles_bis; gilles = new Personne(); gilles_bis = gilles;

Dans lexemple prcdent gilles et gilles_bis font rfrence au mme objet instanci. Pour indiquer quune variable ne fait rfrence aucun objet, il faut affecter la valeur null. Dans lexemple suivant, la variable gilles ne rfrence aucun objet.
gilles = null;

Grer les noms declasse laidedes espaces denoms


// Utiliser un espace de noms using <espace de noms>; // Dclarer un espace de noms namespace <espace de noms> { // Dclaration des classes contenues // dans lespace de noms }

30

CHAPITRE 2 Les classes

Pour viter dventuels conflits entre noms de classe, les classes peuvent tre dclares lintrieur dun espace de noms (namespace). Un espace de noms peut tre vu comme un rpertoire logique contenant des classes. Comme pour les fichiers, les classes doivent avoir un nom unique dans un espace de noms donn. Les espaces de noms peuvent tre composs de plusieurs mots spars par un point. Lexemple suivant illustre la dclaration dune classe Personne et Maison dans le mme espace de noms. Une autre classe Personne est ensuite dclare dans un autre espace de noms.
namespace Exemple.EspaceNom1 { class Personne { } class Maison { } } namespace Exemple.EspaceNom2 { class Personne { } }

Si une classe est dclare dans un espace de noms, il est alors ncessaire dcrire son espace de noms en entier lors de lutilisation de la classe.
Exemple.EspaceNom1.Personne gilles; gilles = new Exemple.EspaceNom1.Personne();

Dclarer et utiliser des champs

31

Pour viter dcrire chaque fois lespace de noms en entier lors de lutilisation dune classe, on peut utiliser le mot-cl using au dbut du fichier, suivi de lespace de noms.
// Utiliser lespace de noms Exemple.EspaceNom1 using Exemple.EspaceNom1.Personne; ... ... Personne gilles; gilles = new Personne();

Attention Si vous utilisez le mot-cl using pour utiliser deux espaces de noms diffrents contenant chacun une classe de mme nom, le compilateur ne pouvant pas choisir la classe utiliser, il vous faudra spcifier explicitement lespace de noms complet de la classe utiliser lors de lutilisation de cette dernire.

Dclarer et utiliser des champs


// Dclarer un champ <visibilit> <type> <nom>; // Affecter une valeur un champ <instance>.<nom> = <valeur>; // Obtenir la valeur dun champ <valeur> = <instance>.<nom>;

Les champs dune classe sont des variables reprsentant les attributs dun objet, par exemple lge dune personne. Comme pour les variables, les champs ont un identificateur et un type.

32

CHAPITRE 2 Les classes

Lexemple suivant illustre la dclaration de la classe Personne constitu de trois champs.


class Personne { public int age; public bool sexe; // On suppose que true = Homme public Maison maison; // Rfrence une maison }

Il est important de noter que comme expliqu prcdemment, le champ maison est une variable faisant rfrence une instance de la classe Maison. La classe Personne ne contient en aucun cas un objet embot Maison. Laccs aux champs dune classe se fait en utilisant la notation pointe. Lexemple suivant illustre la cration dune personne en spcifiant ses attributs, puis affiche lge et le code postal o habite cette personne. Dans cet exemple, nous supposons que la classe Maison contient un champ codePostal de type entier.
Personne gilles; Maison maison; gilles = new Personne(); maison = new Maison(); maison.CodePostal = 75001; gilles.age = 26; gilles.sexe = true; gilles.maison = maison; Console.WriteLine(Lage de Gilles est : + gilles.age); Console.WriteLine(Il habite : + gilles.maison.codePostal);

Dclarer et appeler des mthodes

33

Dclarer et appeler des mthodes


// Dclarer une mthode retournant une valeur <visibilit> <type retour> <nom>([paramtre1[, ...]]) { // Code return <valeur>; } // Dclarer une mthode sans valeur de retour <visibilit> void <nom>([paramtre1[, ...]]) { // Code } // Dclarer un paramtre dune mthode: <type paramtre> <nom du paramtre> // Appeler une mthode sans valeur de retour <instance>.<nom>([valeur paramtre,[...]]); // Appeler une mthode avec une valeur de retour <valeur> = <instance>.<nom>([valeur paramtre,[...]]);

Les mthodes dune classe reprsentent les oprations (ou les actions) que lon peut effectuer sur un objet instance de cette classe. Les mthodes prennent facultativement des paramtres et peuvent retourner si ncessaire une valeur. Lexemple suivant illustre les mthodes Marcher() et Courir() contenues dans lobjet Personne permettant daugmenter le compteur du nombre de mtres parcourus par la personne.
class Personne { public int compteur; public void Marcher()

34

CHAPITRE 2 Les classes

{ compteur++; } public void Courir(int nbMetres) { compteur += nbMetres; } }

Voici maintenant un exemple qui utilise ces deux mthodes.


Personne gilles; gilles = new Personne(); gilles.Marcher(); gilles.Courir(10); Console.WriteLine(Gilles a parcouru : ); Console.WriteLine(gilles.Compteur + mtres);

Dclarer des classes et membres statiques


// Dclarer une classe statique <visibilit> static class <nom classe> { // Membres statiques uniquement } // Dclarer un membre statique <visibilit> static <membre> // Utiliser un membre statique <nom classe>.<membre>

Dclarer des classes et membres statiques

35

Les membres statiques sont des membres qui sont accessibles sans instancier une classe. Ils sont donc communs toutes les instances des classes et accessibles en utilisant directement le nom de la classe (et non une instance). Pour dclarer un membre statique, on utilise le mot-cl static. Les classes statiques sont des classes contenant uniquement des membres statiques et ne sont pas instanciables. Ces classes contiennent le plus souvent des fonctionnalits utilitaires ne ncessitant aucune approche objet. Lexemple suivant illustre lutilisation dun champ statique dans la classe Personne permettant de comptabiliser le nombre dappels la mthode Marcher().
class Personne { public static int compteurMarcher; public void Marcher() { compteurMarcher++; } }

Voici un exemple qui utilise la classe cre prcdemment.


static void Main(string[] args) { Personne gilles; Personne claude; gilles = new Personne(); claude = new Personne(); gilles.Marcher(); claude.Marcher(); Console.WriteLine(Personne.compteurMarcher); }

Lexemple prcdent affichera sur la console le rsultat 2.

36

CHAPITRE 2 Les classes

Accder linstance courante avecthis


this.<membre>

Le mot-cl this reprsente linstance courante dune classe (il ne sutilise pas dans les classes statiques). Il permet daccder aux membres de la classe de linstance courante. Ce mot-cl nest pas obligatoire lorsque vous utilisez des membres de la classe courante mais il permet de rsoudre les conflits entre les paramtres dune mthode et les champs contenus dans une classe.
Astuce Mme si le mot-cl this nest pas obligatoire dans certains cas, il est recommand de lutiliser explicitement afin que dautres dveloppeurs puissent comprendre instantanment si lidentifi cateur que vous utilisez est un paramtre de la mthode ou un champ de la classe.

Lexemple suivant illustre lutilisation du mot-cl this afin que le compilateur puisse faire la diffrence entre le champ nom de la classe Personne et le paramtre nom de la mthode SetNom().
class Personne { string nom; void SetNom(string nom) { // Ici le mot-cl this est obligatoire this.nom = nom; } }

Dfinir les niveaux devisibilit des membres

37

Dfinir les niveaux devisibilit des membres


class <nom classe> { private <membre priv> protected <membre protg> internal <membre interne> protected internal <membre protg et interne> public <membre priv> }

Les niveaux de visibilits prcdent toujours la dclaration dun membre dune classe. Ils permettent de dfinir si un membre dune classe est visible ou non par une autre classe. Le Tableau2.1 prsente ces niveaux de visibilit et leurs implications.
Tableau 2.1: Niveaux de visibilit des membres
Mot-cl Description Le membre est visible uniquement dans la classe elle-mme. Le membre est visible dans la classe elle-mme et ses classes drives. Le membre est visible dans la classe elle-mme, ses classes drives et toutes les classes incluses dans le mme assembly (voir la section Rcuprer la description dun assembly au Chapitre13). Le membre est visible dans la classe elle-mme, et toutes les classes incluses dans le mme assembly. Le membre est visible par toutes les classes.

private protected protected internal

internal public

Par dfaut, si aucun niveau de visibilit nest dfini, les membres sont private.

38

CHAPITRE 2 Les classes

Une bonne pratique en programmation oriente objet est de dfinir tous les champs en priv, et de crer des mthodes ou des proprits permettant de rcuprer ou de modifier les valeurs de ces champs.

Dclarer et appeler desconstructeurs


<visibilit> <nom classe>([paramtres]) { // Code du constructeur } // Appel du constructeur durant linstanciation <nom classe> <instance>; <instance> = new <nom classe>([paramtres]);

Les constructeurs sont des mthodes particulires appeles au moment de la construction dun objet. Ils permettent le plus souvent dinitialiser les champs dune instance dun objet lors de son instanciation. Le nom dun constructeur est celui de la classe o il est dclar et il ne retourne aucune valeur. Si aucun constructeur nest dclar, le compilateur ajoute un constructeur par dfaut avec un niveau de visibilit dfini public et qui ne contient aucun paramtre. Lexemple suivant illustre une classe Personne contenant un constructeur prenant en paramtre lge et le sexe de la personne crer.
class Personne { private int age; private bool sexe;

Dclarer unchamp enlecture seule

39

public Personne(int a, bool s) { this.age = a; this.sexe = s; } }

Le code suivant montre comment utiliser le constructeur dclar lexemple prcdent.


Personne gilles; gilles = new Personne(26, true);

Astuce Les constructeurs offrent un moyen pour forcer les utilisa teurs de votre classe initialiser les champs de cette dernire.

Dclarer unchamp enlecture seule


// Dclarer un champ en lecture seule <visibilit> readonly <type> <nom>;

Les champs peuvent tre dclars en lecture seule. La valeur de ce champ est initialise dans le constructeur de la classe qui le contient. Une fois initialis, il est impossible de changer la valeur dun tel champ. La dclaration dun champ en lecture seule se fait en utilisant le mot-cl readonly.
Astuce Utilisez les champs en lecture seule afin de vous assurer qu lacompilation, aucune ligne de code ne tentera de modifier la valeur associe.

40

CHAPITRE 2 Les classes

Lexemple suivant illustre la dclaration et lutilisation dun champ en lecture seule nomm sexe.
class Personne { private readonly bool sexe; public Personne(bool s) { this.sexe = s; } public bool GetSexe() { return this.sexe; // Correct } public void ModifierSexe(bool nouvelleValeur) { this.sexe = nouvelleValeur; // Erreur de compilation } }

Dclarer et utiliser des proprits


<visibilit> <type> <nom proprit> { [<visibilit du get>] get { // Retourner la valeur de la proprit return valeur; } [<visibilit du set>] set { // Modifier la valeur de la proprit valeur = value; } }

Dclarer et utiliser des proprits

41

// Rcuprer la valeur dune proprit <valeur> = <instance>.<nom proprit>; // Dfinir la valeur de la proprit <instance>.<nom proprit> = <valeur>;

Les proprits permettent de dfinir des oprations sur la rcupration ou la modification dune valeur portant sur une classe. Le plus souvent, les proprits dfinissent des oprations de rcupration/modification sur un champ de la classe associe. En programmation oriente objet, on sinterdit daccder directement aux champs dune classe depuis dautres classes. En effet, les programmeurs utilisateurs de la classe nont pas connatre (et contrler) sa structure interne. Les proprits permettent doffrir un moyen daccder publiquement vos champs. Ainsi, si la structure interne de la classe change (cest--dire les champs contenus dans la classe), il suffit alors de modifier le contenu des proprits. Le code qui utilise les proprits ne sera donc pas impact. Il est possible de crer des proprits permettant de rcuprer uniquement une valeur (lecture seule); pour cela, il suffit de ne pas dclarer laccesseur set associ la proprit. Il en est de mme pour les proprits permettant de modifier uniquement une valeur; il suffit dans ce cas de supprimer laccesseur get. Le mot-cl value sutilise uniquement dans laccesseur set dune proprit. Il contient la valeur affecte la proprit.
// Dans le bloc set de Proprit, // value aura comme valeur 1664 instance.proprit = 1664;

value est du mme type que la proprit associe.

42

CHAPITRE 2 Les classes

Les accesseurs get et set ont un niveau de visibilit gal celle de la proprit. Il est possible de spcifier des niveaux de visibilit diffrents pour lun des accesseurs. Par exemple, une proprit Age avec un niveau de visibilit public peut contenir un accesseur set avec un niveau de visibilit private. La proprit get quand elle sera automatiquement du mme niveau de visibilit que la proprit (cest--dire public). Le niveau de visibilit spcifique aux accesseurs doit tre plus restreint que le niveau de visibilit de la proprit. Par exemple, il nest pas possible de spcifier une proprit ayant un niveau de visibilit protected avec un accesseur get ayant un niveau de visibilit public. Lexemple suivant montre une classe Personne contenant une proprit permettant de modifier et de rcuprer lge dune personne. Une deuxime proprit en lecture seule est ajoute afin de rcuprer uniquement le sexe dune personne. Et enfin, une troisime proprit EstUnEcrivain est ajoute afin de savoir si la personne est un crivain. Laccesseur set de cette dernire proprit est private afin quelle ne puisse tre modifie qu lint rieur de la classe.
class Personne { private int age; private bool sexe; private bool estUnEcrivain; public Personne(int a, bool s, bool ecrivain) { this.age = a; this.sexe = s; // Appel de la proprit this.EstUnEcrivain = ecrivain; }

Dclarer et utiliser des proprits

43

public bool Sexe { get { return this.sexe; } } public int Age { get { return this.age; } set { this.age = value; } } public int EstUnEcrivain { get { return this.estUnEcrivain; } private set { this.estUnEcrivain = value; } } }

Le code suivant illustre lutilisation des proprits prcdemment dclares.


Personne gilles; gilles = new Personne(26, true, true); gilles.Age = gilles.Age + 1; // Vieillir la personne if (gilles.Sexe == true) { Console.WriteLine(Vous tes un homme.); } if (gilles.EstUnEcrivain == true) { Console.WriteLine(Vous tes un crivain.); }

44

CHAPITRE 2 Les classes

Implmenter automatiquement des proprits (C#3.0)


<visibilit> <type> <nom proprit> { [<visibilit du get>] get; [<visibilit du set>] set; }

Depuis la version3.0 de C#, il est possible dimplmenter automatiquement une proprit. Il suffit pour cela de ne pas mettre de code dans les accesseurs get et set. la compilation, un champ priv sera automatiquement gnr et utilis pour implmenter les blocs get et set de la proprit, comme le montre lexemple qui suit.
private <type> <champ gnr>; <visibilit> <type> <nom proprit> { [<visibilit du get>] get { return this.<champ gnr>; } [<visibilit du set>] set { this.<champ gnr> = value; } }

Le champ priv automatiquement gnr nest pas accessible par programmation. Il sera donc ncessaire dutiliser la proprit lintrieur de la classe pour pouvoir rcuprer ou affecter sa valeur. Les accesseurs get et set doivent tre tous deux implments automatiquement ou manuellement. Il nest pas possible den implmenter un automatiquement et lautre manuellement.

Implmenter automatiquement des proprits (C#3.0)

45

Info Les proprits implmentes automatiquement permettent dcrire du code beaucoup plus rapidement. En revanche, elles ne permettent pas dexcuter du code personnalis. Par exemple, il est impossible de contrler la valeur affecte une proprit dans le bloc set. Nhsitez donc pas, dans ce cas, implmenter votre proprit manuellement.

Lexemple suivant illustre la dclaration dune classe Personne contenant une proprit Age implmente automatiquement.
class Personne { public Personne(int a) { this.Age = a; } public int Age { get; set; } }

Le code suivant illustre lutilisation de la proprit Age prcdemment dclare.


Personne gilles; gilles = new Personne(26); gilles.Age = gilles.Age + 1; // Vieillir la personne Console.WriteLine(gilles.Age);

46

CHAPITRE 2 Les classes

Initialiser des proprits lors delacration dunobjet (C#3.0)


<instance> = new <type>([<paramtres constructeur>]) { <nom proprit 1> = <valeur 1>[, <nom proprit N> = <valeur N>] }

Lors de linstanciation dun objet, il est possible dinitialiser automatiquement aprs lappel du constructeur les valeurs des proprits contenues dans lobjet instanci. Ces proprits doivent tre public et contenir un bloc set. Lexemple suivant illustre linitialisation des proprits Prnom et Age de la classe Personne au moment de son instanciation.Voici le code correspondant la dclaration de la classe Personne.
class Personne { public string Prnom { get; set; } public int Age { get; set; } }

Initialiser des proprits lors delacration dunobjet (C#3.0)

47

Le code suivant illustre linitialisation de deux instances de la classe Personne.


Personne gilles; Personne claude; // Instancier une personne avec le Prnom dfini // Gilles et lge 26 gilles = new Personne() { Prnom = Gilles, Age = 26 }; // Instancier une personne avec le Prnom dfini // Claude claude = new Personne() { Prnom = Claude };

Voici maintenant lquivalent du code prcdent sans lutilisation des initialiseurs de proprits.
Personne gilles; Personne claude; // Instancier une personne avec le Prnom dfini // Gilles et lge 26. gilles = new Personne(); gilles.Prnom = Gilles; gilles.Age = 26; // Instancier une personne avec le Prnom dfini // Claude claude = new Personne(); claude.Prnom = Claude;

48

CHAPITRE 2 Les classes

Les indexeurs
// Dclarer un indexeur dans une classe <visibilit> <type> this[<type index> <nom index>] { get { // Retourner la valeur de la proprit } set { } } // Rcuprer la valeur dune proprit <valeur> = <instance>[<index>]; // Dfinir la valeur de la proprit <instance>[<index>] = <valeur>; // Modifier la valeur de la proprit ... = value;

Les indexeurs sont des proprits particulires comportant un ou plusieurs paramtres. Ces paramtres reprsentent le plus souvent des index portant sur une classe. Il ne peut exister quun seul indexeur avec les mmes types et le mme nombre de paramtres dans une classe. Lexemple suivant illustre la dfinition dun indexeur contenant des notes dun examen.
class Examen { private int[] notes; public Examen(int effectifs)

Les indexeurs

49

{ this.notes = new int[effectifs]; } public int this[int indexNote] { get { return this.notes[indexNote]; } set { this.notes[indexNote] = value; } } }

Voici maintenant un exemple dutilisation de la classe Examen contenant trois notes. Un calcul de la moyenne des notes obtenues lexamen est ensuite ralis.
Examen mathmatique; int total; // Cration dun examen contenant 3 notes mathmatiques = new Examen(3); mathmatiques[0] = 10; mathmatiques[1] = 20; mathmatiques[2] = 15; // Calcul de la moyenne des 3 notes total = 0; for (int i = 0; i < 3; i++) { total += mathmatiques[i]; } Console.WriteLine(total / 3);

50

CHAPITRE 2 Les classes

Les dlgus
// Dclarer un dlgu delegate <type retour> <nom dlgu>([paramtres]); // Dclarer une variable du type du dlgu <nom dlgu> <instance>; // Affecter une mthode une variable du type // du dlgu <instance> = <mthode>; // Appeler la mthode contenue dans la variable <instance>([paramtres]);

Un dlgu est une classe permettant de reprsenter des mthodes dun mme type, cest--dire des mthodes ayant: le mme type de valeur de retour; le mme nombre de paramtres; les mmes types pour chaque paramtre. Grce aux dlgus, il est possible de dclarer et dutiliser des variables faisant rfrence une mthode (du mme type que le dlgu). On peut alors appeler la mthode rfrence en utilisant ces variables sans connatre la mthode rellement appele. Les classes de type dlgu sont dclares laide du motcl delegate. Lexemple suivant illustre la dclaration et lutilisation dun dlgu Opration ayant deux entiers en paramtre et retournant un entier.
class Calcul { // Dclaration dun dlgu Opration delegate int Opration(int valeur1, int valeur2);

Les dlgus

51

// Dclaration dune mthode Addition du mme type // que le dlgu Opration static int Addition(int v1, int v2) { return v1 + v2; } // Dclaration dune mthode Soustraction du mme // type que le dlgu Opration static int Soustraction(int v1, int v2) { return v1 - v2; } // Applique lopration spcifie avec les // oprandes associes static int AppliquerOpration(Opration o, int v1, int v2) { return o(v1, v2); } static void Main() { int total; // Appliquer laddition sur 10 et 5 total = AppliquerOpration(Addition, 10, 5); // Appliquer laddition sur le total prcdent // et 20 total = AppliquerOpration(Soustraction, total, 20); // Affiche -5 Console.WriteLine(total); } }

52

CHAPITRE 2 Les classes

Dans lexemple prcdent, les deux mthodes Addition() et Soustraction() sont de type Opration. On peut donc faire rfrence lune de ces mthodes dans une variable de type Opration. Cest le cas du paramtreo de la mthode AppliquerOpration(). Lappel de la mthode rfrence par cette variable se fait simplement en passant les paramtres entre parenthses.
Attention Si une variable de type dlgu ne fait rfrence aucune mthode (cest--dire si la variable est rfrence null), lappel de la mthode (inexistante) contenu dans cette variable provoquera une erreur lexcution.

Dclarer des mthodes anonymes


// Dclarer une mthode anonyme <nom dlgu> <instance>; <instance> = delegate ([paramtres de la mthode]) { // Code de la mthode }

Les mthodes anonymes sont des mthodes sans nom qui sont cres directement dans le code dune mthode. Elles sont rfrences et appelables grce aux variables de type dlgu. Les mthodes anonymes doivent donc prendre en paramtre les mmes paramtres que le dlgu associ. Le type de retour (si diffrent de void) est dtermin par le compilateur grce aux return contenus dans la mthode anonyme. Bien videmment, le type de retour dtermin doit correspondre au type de retour du type dlgu associ.

Dclarer des mthodes anonymes

53

Les mthodes anonymes ont la possibilit dutiliser les variables contenues dans la mthode qui les dclare. Lexemple suivant illustre la cration dune mthode anonyme de type Opration prenant deux oprandes en paramtre. Cette mthode anonyme consiste multiplier les deux valeurs de ces deux oprandes, et multiplier de nouveau le rsultat par une autre valeur se trouvant dans une variable locale de la mthode qui dfinit la mthode anonyme.
class Calcul { // Dclaration dun dlgu Opration delegate int Opration(int valeur1, int valeur2); // Applique lopration spcifie avec les // oprandes associes static int AppliquerOpration(Opration o, int v1, int v2) { return o(v1, v2); } static void Main() { int total; Opration o; int autreValeur; autreValeur = 20; // Cration dune mthode anonyme de type // Opration o = delegate(int v1, int v2) { return autreValeur * v1 * v2; };

54

CHAPITRE 2 Les classes

// Appliquer le dlgu anonyme sur 10 et 5 total = AppliquerOpration(o, 10, 5); // Afficher 1000 Console.WriteLine(total); } }

Utiliser des expressions lambda (C#3.0)


// Dclarer une expression lambda <nom dlgu> <instance>; <instance> = ([paramtres]) => { // Code de la mthode } // Dclaration dune expression lambda simple <instance> = ([paramtres]) => <code de lexpression>

Une expression lambda est une autre faon dcrire un dlgu anonyme de manire beaucoup plus concise. Le motcl delegate nest plus utilis et il est remplac par loprateur=>.
Info Les expressions lambda sont trs utilises dans LINQ.

Si lexpression contient une instruction, il est possible dcrire le code de lexpression directement sans les accolades et sans le mot-cl return:
Dlgu multiplication = (x, y) => x * y;

Dclarer des mthodes anonymes

55

Si une expression lambda contient uniquement un paramtre, les parenthses autour de cette dernire sont facultatives:
Dlgu auCarr = x => x * x;

Contrairement aux mthodes anonymes, il nest pas ncessaire de spcifier les types des paramtres de lexpression si ces derniers peuvent tre dduits automatiquement par le compilateur:
delegate bool CritreDelegate(int nombre); ... CritreDelegate d = x => x > 2;

Dans lexemple prcdent, il nest pas ncessaire de spcifier le type du paramtrex. En effet, le compilateur sait que la variabled est un dlgu prenant en paramtre un entier de type int. Le paramtrex de lexpression lambda associe sera donc automatiquement de type int. Il est possible dcrire une expression lambda ne prenant pas de paramtre. Dans ce cas, il est ncessaire dutiliser des parenthses vides:
Dlgu d = () => Console.WriteLine(Bonjour!);

Lexemple qui suit illustre la cration dune mthode GetPremier() permettant de rechercher et de rcuprer le premier entier qui correspond au critre spcifi en paramtre. Si aucun nombre ne satisfait cette condition, alors la valeur-1 est retourne.

56

CHAPITRE 2 Les classes

// Dclaration du dlgu utiliser delegate bool CritreDelegate(int nombre); class ExpressionLambda { static int GetPremier(int[] t, CritreDelegate critre) { for(int i=0; i<tableau.Length; i++) { if (critre(tableau[i]) == true) { return tableau[i]; } } return -1; } }

Voici un exemple qui utilise cette mthode en passant en paramtre une expression lambda permettant de rcuprer le premier nombre infrieur 10.
int[] t; int valeur; t = new int[] { 16, 64, 3, 51, 33 }; valeur = ExpressionLambda.GetPremier(t, e => e < 10);

Les vnements

57

Les vnements
// Dclarer un vnement dans une classe <visibilit> event <type dlgu> <nom vnement>; // Dclencher un vnement synchrone <nom vnement>([paramtres]); //Dclencher un vnement asynchrone <nom vnement>.BeginInvoke([paramtres], null, null); // Associer une mthode un vnement <instance>.<nom vnement> += new <type dlgu>(<mthode>); // Version simplifie <instance>.<nom vnement> += <mthode>; // Dissocier une mthode dun vnement <instance>.<nom vnement> -= <dlgu>; <instance>.<nom vnement> -= <mthode>;

Les vnements permettent de signaler une ou plusieurs classes que quelque chose sest produit (changement dtat, etc.). Les vnements sont des conteneurs de dlgus de mme type. Dclencher un vnement consiste appeler tous les dlgus contenus dans ce dernier (cest--dire toutes les mthodes associes aux dlgus). La dclaration dun vnement consiste spcifier le type des mthodes (dlgus) que lvnement appellera au moment du dclenchement de ce dernier. Cette dclaration se fait en utilisant le mot-cl event. Le dclenchement dun vnement consiste appeler lvnement en spcifiant les paramtres ncessaires (les paramtres dpendent du dlgu). Un vnement ne peut tre dclench que dans la classe o il est dclar.

58

CHAPITRE 2 Les classes

Attention Le dclenchement dun vnement ne peut se faire que si lvnement contient au moins un dlgu (cest--dire si lvnement est diffrent de null). Pensez vrifier cette pr-condition avant le dclenchement dun vnement.

Par dfaut, le dclenchement dun vnement est synchrone. Son dclenchement provoque lappel de toutes les mthodes abonnes lvnement. Une fois que toutes les mthodes sont appeles, le code qui a dclench lvnement poursuit son excution. Il est possible de dclencher un vnement asynchrone afin que le code qui a dclench lvnement poursuive immdiatement son excution. Les mthodes abonnes sont donc excutes en parallle. Le dclenchement dun vnement asynchrone se fait en appelant la mthode BeginInvoke de lvnement concern. Lassociation (lajout) dune mthode un vnement se fait trs simplement en utilisant loprateur+= et en spcifiant un dlgu (ou la mthode) ajouter. La dissociation (la suppression) dune mthode dun vnement se fait en utilisant loprateur-= et en spcifiant le dlgu (ou la mthode) supprimer. Lexemple suivant illustre la cration dune classe CompteBancaire contenant une mthode permettant de dbiter le compte. Cette mthode dclenche lvnement Mouvement en spcifiant en paramtre le nouveau solde du compte bancaire.
// Dclaration de la signature des dlgus de // lvnement Mouvement de CompteBancaire delegate void MouvementHandler(int nouveauSolde); class CompteBancaire {

Les vnements

59

private int solde; public CompteBancaire(int solde) { this.solde = solde; } // Dclaration dun vnement de type // MouvementHandler public event MouvementHandler Mouvement; public void Dbiter(int montant) { this.solde += montant; // Dclencher lvnement si au moins // une mthode est associe if (this.Mouvement != null) { this.Mouvement(this.solde); } } }

Voici maintenant un code utilisant la classe CompteBancaire contenant une mthode Surveiller() qui affiche un message si le compte bancaire est dbiteur.
static void Main(string[] args) { CompteBancaire cb; cb = new CompteBancaire(150); // Associer la mthode Surveillance() // lvnement Mouvement cb.Mouvement += new MouvementHandler(Surveillance);

60

CHAPITRE 2 Les classes

// Dbiter le compte cb.Dbiter(50); cb.Dbiter(50); cb.Dbiter(100); } static void Surveillance(int nouveauSolde) { if (nouveauSolde < 0) { Console.WriteLine(Vous tes dcouvert !); } }

Lexemple suivant illustre le dclenchement de lvnement Mouvement de manire asynchrone.


if (this.Mouvement != null) { this.Mouvement.BeginInvoke(this.solde); }

Surcharger unemthode
// Dclarer une mthode dans une classe <type retour> <nom mthode>([paramtres]) { } // Dclarer une surcharge de la mthode prcdente <autre type retour> <nom mthode>([autres paramtres]) { }

Surcharger une mthode consiste dfinir une autre mthode de mme nom ayant des paramtres diffrents.

Surcharger unemthode

61

Deux mthodes de mme nom sont considres comme diffrentes (lune est une surcharge de lautre) si: le nombre de paramtres est diffrent; ou au moins un paramtre est de type diffrent. La surcharge de mthode permet le plus souvent de proposer diffrentes mthodes avec des paramtres par dfaut. Lexemple suivant illustre la dfinition dune classe Personne contenant trois mthodes Marcher() surcharges.
class Personne { private float compteur; // Mthode n 1 public void Marcher() { // Appeler la mthode n 3 this.Marcher(0.5F); } // Mthode n 2 public void Marcher(int nbMetres) { // Appeler la mthode n 3 this.Marcher((float)nbMetres); } // Mthode n 3 public void Marcher(float nbMetres) { this.compteur += nbMetres; } }

62

CHAPITRE 2 Les classes

Voici maintenant un exemple qui utilise ces trois mthodes.


Personne p; p = new Personne(); p.Marcher(); // Avance de 50 cm (appelle mthode 1) p.Marcher(10); // Avance de 10 m (appelle mthode 2) p.Marcher(3.5);// Avance de 3,5 m (appelle mth. 3)

Comme les mthodes ont des paramtres diffrents, le compilateur peut trouver automatiquement la bonne mthode appeler.

Dclarer des paramtres facultatifs (C#4.0)


// Dclarer une mthode retournant une valeur <visibilit> <type retour> <nom>([paramtre1[, ...]]) { // Code return <valeur>; } // Dclarer un paramtre dune mthode <type paramtre> <nom du paramtre> // Dclarer un paramtre facultatif dune mthode <type paramtre> <nom du paramtre> = <valeur par dfaut>

Les paramtres facultatifs permettent domettre des arguments pour certains paramtres en les associant avec une valeur par dfaut. Cette valeur sera utilise si aucun argument na t affect au paramtre lors de lappel de la

Dclarer des paramtres facultatifs (C#4.0)

63

mthode. Les valeurs par dfaut des paramtres doivent tre constantes. Les paramtres facultatifs doivent tre dfinis la fin de la liste des paramtres aprs tous les paramtres obligatoires. Si lors de lappel dune mthode, un argument est fourni un paramtre facultatif, alors tous les paramtres facultatifs prcdents doivent tre spcifis. Lexemple suivant illustre la dclaration de la mthode Marcher() dans une classe Personne. Cette mthode prend en paramtre un nombre de mtres parcourus par la personne. Par dfaut, si aucun argument nest spcifi au paramtre nbMetres, ce dernier aura comme valeur0,5.
class Personne { private float compteur; public void Marcher(float nbMetres = 0.5F) { this.compteur += nbMetres; } }

Voici maintenant un exemple qui utilise cette mthode.


Personne p; p = new Personne(); p.Marcher(); p.Marcher(3.5); // Avance de 50 cm // Avance de 3,5 mtres

Voici la dclaration quivalente de la classe Personne sans utiliser les paramtres facultatifs mais avec uniquement des surcharges dune mthode.

64

CHAPITRE 2 Les classes

class Personne { private float compteur; // Mthode n1 public void Marcher() { // Appeler la mthode n2 this.Marcher(0.5F); } // Mthode n2 public void Marcher(float nbMetres) { this.compteur += nbMetres; } }

Utiliser des paramtres nomms (C#4.0)


// Appeler une mthode laide de paramtres nomms <instance>.<nom mthode>(<nom paramtre>: <valeur>, ...);

Depuis la version4.0 de C#, il est possible dappeler une mthode en spcifiant explicitement ses paramtres laide de leur nom associ. Les paramtres peuvent donc tre spcifis dans nimporte quel ordre. Lexemple suivant illustre la dclaration dune mthode Marcher() contenue dans une classe Personne. Cette mthode est ensuite appele en utilisant les paramtres nomms.

Utiliser des paramtres nomms (C#4.0)

65

class Personne { private float compteur; public void Marcher(bool sens, float nbMetres) { if (sens == true) { this.compteur += nbMetres; } else { this.compteur -= nbMetres; } } }

Voici maintenant un exemple qui appelle deux fois la mthode Marcher() en utilisant les paramtres nomms.
Personne p; p = new Personne(); // Avancer de 50 cm en avant p.Marcher(sens: true, nbMettres: 3.5); // Avancer de 70 cm en arrire p.Marcher(nbMetres: 70, sens: false);

66

CHAPITRE 2 Les classes

Surcharger unconstructeur
class <nom classe> { // Dclarer un constructeur dune classe <nom classe>([paramtres]) { } // Dclarer une surcharge du constructeur // prcdent <nom classe>([autres paramtres]) [: this(<paramtres>)] // Appel dun autre // constructeur { } }

Comme pour les mthodes, surcharger un constructeur consiste dfinir un constructeur ayant des paramtres diffrents. Deux constructeurs sont considrs comme dif frents (lun est une surcharge de lautre) si: le nombre de paramtres est diffrent; ou au moins un paramtre est de type diffrent. La surcharge de constructeur permet le plus souvent de proposer diffrents constructeurs avec des paramtres par dfaut. Lexemple suivant illustre la dfinition dune classe Personne contenant deux constructeurs surchargs.
class Personne { private string nom; // Constructeur n1 public Personne() {

Surcharger unconstructeur

67

this.nom = Inconnu; } // Constructeur n2 public Personne(string nom) { this.nom = nom; } public string Nom { get { return this.nom; } } }

Voici maintenant un exemple qui utilise ces deux constructeurs.


Personne p; p = new Personne(); Console.WriteLine(p.Nom); // Affiche Inconnu p = new Personne(TOURREAU); Console.WriteLine(p.Nom); // Affiche TOURREAU

Comme les constructeurs ont des paramtres diffrents, le compilateur peut trouver automatiquement le bon construc teur appeler. Afin de factoriser le code, les constructeurs peuvent appeler dautres constructeurs laide du mot-cl this suivi des paramtres. Lexemple suivant illustre la dfinition dune classe Personne contenant deux constructeurs surchargs. Le constructeur sans paramtre n1 appelle le constructeur n2 en passant en paramtre la chane Inconnu.

68

CHAPITRE 2 Les classes

class Personne { private string nom; // Constructeur n1 public Personne() : this(Inconnu) { } // Constructeur n2 public Personne(string nom) { this.nom = nom; } public string Nom { get { return this.nom; } } }

Surcharger unoprateur
// Surcharger un oprateur unaire <visibilit> static <retour> operator<operateur> (<oprande>); // Surcharger un oprateur binaire <visibilit> static <retour> operator<operateur> (<opr. gauche>, <opr. droit>); Oprateurs unaires surchargeables: +, -,!, ~, ++, -Oprateurs binaires surchargeables: +, -, *, /, %, &, |, ^, <<, >> Oprateurs binaires de comparaison surchargeables: ==,!=, <, >, <=, >=

Surcharger unoprateur

69

// Surcharger loprateur de conversion explicite <visibilit> static explicit operator <retour>(<oprande>); // Surcharger loprateur de conversion implicite <visibilit> static implicit operator <retour>(<oprande>);

Par dfaut, les oprateurs C# sutilisent avec les types primitifs, par exemple loprateur addition(+) entre deux entiers. Il est possible de redfinir ces oprateurs afin quils soient utilisables avec les types dfinis par lutilisateur. Imaginons que lon dispose dune classe modlisant un point gomtrique2D (avec des coordonnesx ety). Il serait intressant de redfinir loprateur addition entre deux points, mais aussi laddition entre un point et un entier. La surcharge dun oprateur consiste tout simplement implmenter une mthode static ayant comme nom operator suivi du symbole de loprateur surcharger. Les paramtres de cette mthode dpendent des oprandes de loprateur surcharger. En effet, un oprateur unaire prend un seul paramtre car il agit sur un seul oprande tandis quun oprateur binaire prend deux paramtres, car il agit sur deux oprandes.
// Exemple dun oprateur unaire (incrmentation) public static Point operator++(Point p) { ... } // Exemple dun oprateur binaire (addition) public static Point operator+(Point p1, Point p2) { ... }

70

CHAPITRE 2 Les classes

Pour les oprateurs binaires, lordre des paramtres doit correspondre lordre des oprandes de loprateur redfinir. Par exemple, si lon dfinit une surcharge de loprateur addition comme ceci:
// Exemple dun oprateur binaire (addition) public static Point operator+(Point p1, int valeur) { ... }

Alors on ne peut appeler lopration addition avec un Point comme oprande de gauche et un entier comme oprande de droite. Il est ncessaire dans ce cas dajouter une surcharge du mme oprateur avec les paramtres inverss.
Point p, p1; p = p1 + 10; // OK p = 10 + p1; // Erreur

Les oprateurs de comparaison doivent ncessairement retourner une valeur boolenne (de type bool).
Info Lors de la dfinition dune surcharge dun oprateur de comparaison, pensez dfinir une surcharge pour le ou les oprateurs opposs. Par exemple, si vous implmentez une surcharge de loprateur galit (==), pensez implmenter une surcharge de loprateur oppos (!=) avec les mmes oprandes et dans le mme ordre.

Surcharger unoprateur

71

Il est possible de redfinir les oprateurs de conversion entre deux types en utilisant les oprateurs implicit et explicit. Loprateur de conversion explicit est utilis lors dune conversion avec lutilisation de loprateur cast (voir page 99). Lexemple suivant illustre ce genre de conversion.
Kilomtre k; Miles m; m = new Miles(16); k = (Kilomtre)m; // Conversion explicite

Loprateur de conversion implicit est utilis lors dune conversion simple entre deux types. Lexemple suivant illustre ce genre de conversion.
Kilomtre k; Miles m; m = new Miles(16); k = m; // Conversion implicite

La surcharge dun oprateur ne peut se faire que dans la classe du type dun de ses oprandes. Par exemple, la redfinition de loprateur addition entre un point et un entier ne peut se faire que dans la classe Int32 (ce qui est impossible car on na pas accs au code source de cette classe) ou dans la classe Point. Lexemple suivant illustre la dclaration dune classe Point avec la surcharge de deux oprateurs (laddition et lincrmentation). Pour loprateur addition, trois surcharges sont dclares afin de faire laddition entre deux points, entre un point et un entier et entre un entier et un point.

72

CHAPITRE 2 Les classes

class Point { private int x; private int y; public Point(int x, int y) { this.x = x; this.y = y; } public int X { get { return this.x; } } public int Y { get { return this.y; } } public static Point operator +(Point p1, Point p2) { return new Point(p1.x + p2.x, p1.y + p2.y); } public static Point operator +(Point p, int valeur) { return new Point(p.x + valeur, p.y + valeur); } public static Point operator +(int valeur, Point p) { // Appeler loprateur operator+(Point, int) return p + valeur; } public static Point operator ++(Point p) { // Appeler loprateur operator+(Point, int) return p + 1; } }

Surcharger unoprateur

73

Remarquez quil est tout fait possible, dans un oprateur, dappeler une surcharge dun autre oprateur. Lexemple suivant illustre maintenant lutilisation des divers oprateurs crs prcdemment.
Point p1, p2, p3; p1 = new Point(2, 5); p2 = new Point(10, 20); p3 = p1 + p2; // (12, 25) p3 = p1 + 15; // (17, 20) p3 = 15 + p1; // (17, 20) p3 = ++p1; p3 = p1++; // (3, 6) // (2, 5)

Lexemple suivant illustre la redfinition des oprateurs de conversion. Deux classes sont dclares afin de reprsenter des mesures en kilomtres et en miles. Un oprateur de conversion explicit est ajout dans la classe Miles, afin de convertir des miles en kilomtres. Un oprateur de conversion implicit est dclar dans la classe Kilomtre et ralise la conversion inverse.
class Miles { private double valeur; public Miles(double valeur) { this.valeur = valeur; } public double Valeur { get { return this.valeur; } }

74

CHAPITRE 2 Les classes

public static explicit operator Kilomtre(Miles miles) { return new Kilomtre(miles.Valeur * 1.609344); } } class Kilomtre { private double valeur; public Kilomtre(double valeur) { this.valeur = valeur; } public double Valeur { get { return this.valeur; } } public static implicit operator Miles(Kilomtre kilomtres) { return new Miles(kilomtres.Valeur / 1.609344); } }

Voici un exemple dutilisation des deux oprateurs de conversion.


Kilomtre k; Miles m; m = new Miles(16); k = (Kilomtre)m; m = k; // Conversion explicite // Conversion implicite

Les numrations

75

Astuce Les oprateurs sont surchargs le plus souvent dans des classes ayant une smantique mathmatique (point gomtrique, nombre complexe, etc.). vitez de surcharger des oprateurs pour dautres types de classes (par exemple laddition de deux instances de Maison qui consisterait faire la somme des surfaces de celles-ci). La comprhension du code en est rendue plus difficile. Prfrez dans ce cas une mthode avec un nom vocateur (SommeSurface() par exemple).

Les numrations
[Flags] // Pour utiliser les oprations de bits // (AND, OR, etc.) <visibilit> enum <nom numration> { <nom champ1> [= <valeur 1>,] <nom champ2> [= <valeur 2>,] ... }

Les numrations sont des classes particulires contenant uniquement des champs publics qui sont constants. Les numrations ne peuvent contenir des champs variables, des mthodes ou des proprits. Une numration permet le plus souvent de modliser et limiter le choix dune valeur dans le code. Par exemple, reprsenter le genre dune personne (Homme ou Femme). On peut bien videmment modliser cet attribut laide dun entier (1pour Homme, 2pour Femme), mais il serait possible dans ce cas daffecter dautres valeurs incorrectes (3, 100, etc.). Une numration est une classe dfinie en utilisant le motcl enum. Il est alors possible de dfinir des variables du

76

CHAPITRE 2 Les classes

type de cette numration. Il nest cependant pas possible dinstancier une numration. Les instances possibles dune numration sont les champs contenus dans cette dernire. Chaque champ est associ une valeur entire qui doit tre diffrente dun champ un autre. Si aucune valeur nest affecte un champ, le compilateur se charge daffecter des valeurs en partant de0. Lexemple suivant illustre la dclaration dune numra tion Genre:
public enum Genre { Homme = 1, Femme = 2 }

Il est maintenant possible dutiliser cette numration comme un nouveau type. Lexemple suivant illustre la dclaration dune classe Personne contenant un champ genre de type Genre.
public class Personne { private Genre genre; public Personne(Genre genre) { this.genre = genre; } public void AfficherGenre() { if (this.genre == Genre.Homme) { Console.WriteLine(Vous tes un homme !); } else

Les numrations

77

{ Console.WriteLine(Vous tes une femme !); } } }

Lattribut [Flags] doit tre plac au-dessus de lnumration si des oprations binaires (AND, OR ou XOR) doivent tre effectues sur les valeurs des champs associs. Dans ce cas, les valeurs des champs doivent tre des puissances de 2: 1, 2, 4, 8, etc., afin que les valeurs associes ne se chevauchent pas. Lexemple suivant illustre la dclaration dune numration modlisant des droits sur un fichier.
[Flags] public enum Droits { Lecture = 1, criture = 2, Crer = 4, Effacer = 8, Tout = Lecture | criture | Crer | Effacer }

Il est possible maintenant possible dutiliser cette numration comme ceci:


Droits d; d = Droits.Effacer | Droits.Ecriture; if ((d & Droits.Lecture) == Droits.Lecture) { Console.WriteLine(Vous avez le droit de lire); }

78

CHAPITRE 2 Les classes

if ((d & Droits.Ecriture) == Droits.Ecriture) { Console.WriteLine(Vous avez le droit dcrire); }

Dans lexemple prcdent, on affecte la variabled le droit deffacer et dcrire laide de loprateur binaire OR(|). On regarde ensuite si lon dispose des droits dcriture ou de lecture; pour cela on utilise loprateur binaire AND(&).

Les classes imbriques


<visibilit> class <nom classe conteneur> { // Dclarer une classe imbrique <visibilit> class <nom classe imbrique> { // Membre contenu dans la classe imbrique } } // Dclarer une variable du type de la classe // imbrique <nom classe conteneur>.<nom classe imbrique> <instance>; // Appeler un constructeur dune classe imbrique <instance> = new <nom classe conteneur>.<nom classe imbrique>([paramtres]);

Les classes imbriques sont par dfaut private. Elles permettent le plus souvent de crer et dutiliser de nouvelles classes qui sont utilises uniquement par la classe conteneur. Les classes imbriques peuvent avoir accs tous les membres (privs inclus) de la classe conteneur; il faudra

Les classes imbriques

79

dans ce cas passer linstance de la classe conteneur la classe imbrique ( laide du constructeur par exemple). Les classes imbriques marques comme private peuvent tre utilises dans la classe conteneur, mais cette dernire ne peut bien videmment pas lexposer de manire public. Lexemple suivant illustre une classe Conteneur, contenant une classe imbrique Imbrique. La classe Imbrique dtient une rfrence vers Conteneur permettant davoir accs la donne private de Conteneur.
public class Conteneur { private int donne; public Conteneur(int donne) { this.donne = donne; } // Classe imbrique public class Imbrique { // Rfrence au conteneur private Conteneur conteneur; public Imbrique(Conteneur conteneur) { this.conteneur = conteneur; } public int DonnePriveConteneur { get { return this.conteneur.donne; } } } }

80

CHAPITRE 2 Les classes

Le code qui suit montre comment utiliser la classe Imbrique.


Conteneur conteneur; Conteneur.Imbrique imbrique; // Crer le conteneur conteneur = new Conteneur(1664); // Crer une classe imbrique avec le conteneur spcifi imbrique = new Conteneur.Imbrique(conteneur); // Afficher la donne prive du conteneur partir // de linstance de la classe imbrique Console.WriteLine(imbrique.DonnePriveConteneur);

Les classes partielles


<visibilit> partial class <nom> { // Code de la classe }

De manire gnrale, on dfinit une classe dans un fichier (portant comme nom le nom de la classe associ). En C#, il est possible dclater une classe dans plusieurs fichiers. Dans chacun de ces fichiers, on dfinit une classe ayant le mme nom et tant partielle ( laide du mot-cl partial). Le compilateur se chargera de regrouper ces fichiers et de former une seule classe. Il est donc possible dans un fichier dutiliser un membre dclar dans un autre fichier (dans la mme classe). Si un mme membre est dclar dans deux fichiers distincts dans une mme classe, une erreur se produira la compilation. Une classe peut tre marque partielle, mme si elle est dfinie dans un seul fichier.

Les classes partielles

81

Lexemple suivant illustre la dclaration dune classe partielle dans deux fichiers.Voici le premier fichier:
partial class Personne { public void Marcher() { this.compteur++; } }

Et ensuite le second fichier:


partial class Personne { private int compteur; }

Une classe partielle sutilise de manire classique:


Personne p; p = new Personne(); p.Marcher();

Info De manire gnrale, il est dconseill dclater une classe dans plusieurs fichiers, cela afin de favoriser une bonne comprhension du code. Les classes partielles doivent tre utilises uniquement avec les gnrateurs de code. Un gnrateur de code gnre le plus souvent une classe laquelle vous pouvez ajouter des fonctionnalits. Le code est gnr dans une classe partielle dans un fichier ayant comme extension .designer.cs, vous laissant ainsi la possibilit de complter limplmentation de cette classe dans un autre fichier. Cela vous permet dviter de perdre vos modifications suite une rgnration du code.

82

CHAPITRE 2 Les classes

Crer untype anonyme (C#3.0)


var <nom variable> { <proprit1> = <proprit2> = <propritN> = } = new <valeur1>[, <valeur2>[, <valeurN>]]

Les types anonymes permettent de crer et dinstancier des classes contenant des proprits en lecture seule sans avoir dfinir une classe. Cette classe est automatiquement gnre par le compilateur, mais son nom est inaccessible au dveloppeur. Il est donc ncessaire dutiliser le mot-cl var pour rcuprer linstance de la classe gnre. Le type des proprits est automatiquement dfini par le compilateur en fonction des types des valeurs affectes. Lexemple suivant illustre la cration dun type anonyme reprsentant lidentit dune personne.
var personne = new { Nom = TOURREAU, Prnom = Gilles, Age = 26 };

Une fois le type anonyme dclar, il est possible de rcuprer la valeur des proprits affectes au moment de sa dclaration.
Console.WriteLine(Nom : + personne.Nom); Console.WriteLine(Prnom : + personne.Prnom); Console.WriteLine(Age : + personne.Age);

Les structures

83

Info Les types anonymes doivent de prfrence tre utiliss dans le code local dune mthode. Ils sont trs utiliss avec les requtes LINQ. Cependant, il faut viter de les utiliser car ils rendent le code beaucoup plus difficile lire et maintenir.

Les structures
<visibilit> struct <nom structure> { // Membres de la structure }

Les structures sont semblables aux classes. Elles contiennent des membres tels que des champs, des mthodes, des vnements et des proprits. Les structures permettent de crer des types valeur alors que les classes permettent decrer des types rfrence. Les structures ont par dfaut un constructeur vide public quil nest pas possible de modifier ou de supprimer. Ce constructeur se charge dinitialiser les champs avec leur valeur par dfaut. Dautres surcharges de constructeur peuvent tre ajoutes, mais ces derniers devront initialiser tous les champs contenus dans la structure. Lexemple qui suit montre la dclaration dune structure Pointreprsentant un point 2D (avec une abscisse et une ordonne).
public struct Point { private int x; private int y; public Point(int x, int y)

84

CHAPITRE 2 Les classes

{ this.x = x; this.y = y; } public int X { get { return this.x; } set { this.x = value; } } public int Y { get { return this.y; } set { this.y = value; } } }

Le runtime du .NET Framework cre automatiquement une instance lors de la dclaration dune variable de type valeur ( laide du constructeur par dfaut). Une variable de type valeur ne peut donc jamais tre null. Mme si une instance est cre, le compilateur vous obligera instancier votre structure une nouvelle fois avant son uti lisation.
Info Les types valeur sont allous sur la pile et sont plus rapides daccs que les types rfrence. Microsoft recommande de ne pas crer des structures lorsque la taille (somme de toutes les tailles des champs) dpasse 16octets.

Les structures ne peuvent pas hriter dune classe, mais elles peuvent implmenter des interfaces. Elles hritent automatiquement de la classe System.ValueType.

Les structures

85

Contrairement aux types rfrence, loprateur daffectation sur une variable de type valeur ralise une copie des champs contenus dans le type. Il en est de mme avec le passage des paramtres une mthode. Lexemple suivant illustre laffectation dune variable de type Point vers une autre variable de type Point et change la valeur de cette variable.
Point p1, p2; p1 = new Point(16, 64); p2 = p2; Console.WriteLine(p1.X + - + p1.Y); // Affiche 16-64 Console.WriteLine(p2.X + - + p2.Y); // Affiche 16-64 p2.X = 33; p2.Y = 51; Console.WriteLine(p1.X + - + p1.Y); // Affiche 16-64 Console.WriteLine(p2.X + - + p2.Y); // Affiche 33-51

Si lon convertit la structure en une classe, le rsultat sera le suivant:


16-64 16-64 33-51 <-- Car p1 et p2 rfrencent le mme objet (alias) 33-51

Les deux exemples qui suivent montrent maintenant comment fonctionnent les structures avec les paramtres de mthode.
// Mthode prenant un Point en paramtre public void Mthode(Point paramtre) { paramtre.X = 33;

86

CHAPITRE 2 Les classes

paramtre.Y = 51; Console.Write(Pendant: ); Console.WriteLine(paramtre.X + - + paramtre.Y); }

Un exemple dappel cette mthode:


Point p; p = new Point(16, 64); Console.WriteLine(Avant: + p.X + - + p.Y); Mthode(p); // La copie de p est envoye en paramtre Console.WriteLine(Aprs: + p.X + - + p.Y);

Le rsultat produit sur la console est le suivant:


Avant: 16-64 Pendant: 33-51 <-- Modification de la copie Aprs: 16-64

Si lon convertit la structure Point en une classe, le rsultat sera le suivant:


Avant: 16-64 Pendant: 33-51 <-Aprs: 33-51
Info Il est possible de contrler la disposition physique des champs (par exemple le chevauchement de certains champs) dune structure grce lattribut StructLayout. Ainsi, on peut obtenir, par exemple, lquivalent du mot-cl union du langageC.

Modification de lobjet rfrenc en paramtres

Passer des paramtres parrfrence

87

Passer des paramtres parrfrence


// Dclarer une mthode retournant une valeur <visibilit> <type retour> <nom>([paramtre1[, ...]]) { // Code return <valeur>; } // Dclarer un paramtre dune mthode [out | ref] <type paramtre> <nom du paramtre> // Appeler une mthode <instance>.<nom>([out | ref] <valeur paramtre1> [,...]]);

Par dfaut, les paramtres sont passs par copie dans les mthodes. Pour les types rfrence, une copie de la rfrence est passe en paramtre; pour les types par valeur une copie de la valeur (structure complte) est ralise. Les paramtres passs par copie sont des paramtres dentre. Lexemple suivant illustre cette copie et ses implications lors de la modification des valeurs dun paramtre. Voici dans un premier temps une classe Personne contenant un champ nom modifiable via la proprit Nom.
class Personne { private string nom; public string Nom { get { return this.nom; } set { this.nom = value; } } }

88

CHAPITRE 2 Les classes

Ensuite, voici une mthode Modifier() qui modifie la rfrence dune Personne ainsi que la valeur dun entier de type int passs tous deux en paramtre.
class Exemple { static void Modifier(Personne p, int nombre) { p = new Personne(); p.Nom = TOURREAU; nombre = 1664; } }

Voici un exemple dun code qui utilise la mthode prcdemment dclare.


static void Main() { Personne personne; int unEntier; personne = new Personne(); personne.Nom = DUPONT; unEntier = 33; Exemple.Modifier(personne, unEntier); Console.WriteLine(personne.Nom); Console.WriteLine(unEntier); }

Le rsultat affich sur la console est le suivant:


DUPONT 33

Passer des paramtres parrfrence

89

Ce rsultat sexplique par le fait que la mthode Modifier() modifie une copie de la rfrence personne et une copie de la valeur unEntier qui sont tous deux passs en paramtre. Ces modifications nont donc aucune incidence sur les variables contenues dans le Main(). Pour passer un paramtre par rfrence, cest--dire la variable elle-mme, il est ncessaire dutiliser le mot-cl ref lors de la dclaration et lappel de la mthode.Voici la version corrige de la mthode Modifier().
class Exemple { static void Modifier(ref Personne p, ref int nombre) { p = new Personne(); p.Nom = TOURREAU; nombre = 1664; } }

Le code qui appelle la mthode Modifier() doit tre aussi modifi afin de passer les paramtres par rfrence:
Exemple.Modifier(ref personne, ref unEntier);

Voici maintenant le rsultat produit sur la console:


TOURREAU 1664

Il nest pas possible de passer la rfrence null ou une constante par rfrence. Le passage par rfrence avec le mot-cl ref ncessite de passer une variable qui est initialise. Le mot-cl ref permet de dfinir des paramtres dentre et de sortie.

90

CHAPITRE 2 Les classes

Le mot-cl out produit le mme rsultat que ref, mais il nest pas ncessaire dinitialiser la variable qui est passe en paramtre. Cependant, la mthode appele doit ncessairement lui affecter une valeur. Le mot-cl out permet de dfinir des paramtres de sortie. Voici un exemple de dclaration dune mthode qui utilise le mot-cl out afin de rcuprer le rsultat dune division dans le paramtre res.
class Exemple { static void Division(decimal a, decimal b, out decimal res) { res = a / b; } }

Le code suivant illustre lutilisation de cette mthode.


int rsultat; Exemple.Division(64, 16, out rsultat); Console.WriteLine(rsultat); // Affiche 4

Remarquez que la variable rsultat na pas t initialise.

Loprateur defusion null


// Oprateur de fusion null <resultat> = <valeur>?? <valeur si null>

Loprateur fusion null sutilise uniquement avec les types rfrences et permet de tester en une seule ligne si une

Loprateur de fusion null

91

variable est null. Si cette condition est vrifie, la valeur qui suit loprateur est affecte la variable rsultante. Dans le cas contraire, cest la valeur de la variable elle-mme qui est retourne. Lquivalent de cet oprateur avec une instruction conditionnelle if peut scrire ainsi:
if (<valeur> == null) { <resultat> = <valeur si null>; } else { <resultat> = <valeur>; }

Lexemple suivant illustre lutilisation de cet oprateur.


Personne gilles; Personne p; Personne rsultat; gilles = new Personne(Gilles TOURREAU); p = null; rsultat = p?? gilles; //rsultat = gilles car p = null p = new Personne(Jean DUPONT); rsultat = p?? gilles; //rsultat = p car p!= null

92

CHAPITRE 2 Les classes

Les mthodes partielles (C#3.0)


<visibilit> partial class <nom> { // Dfinition dune mthode partielle ( complter) partial <type retour> <nom mthode>([<paramtres>]); } // Classe partielle dfinie dans un autre fichier partial class <nom> { // Implmentation de la mthode partielle partial <type retour> <nom mthode >([<paramtres>]) { // Code de la mthode } }

Les mthodes partielles sutilisent avec les classes partielles. Elles permettent de dfinir des mthodes prives sans code qui pourront tre implmentes dans un autre fichier de la mme classe. Ces mthodes tant dclares, il est alors possible de les utiliser dans le code comme une mthode classique. La dclaration dune mthode partielle se fait en utilisant le mot-cl partial. Limplmentation de la mthode nest pas obligatoire; dans ce cas, le compilateur supprimera automatiquement tous les appels cette mthode. Il ne peut y avoir quune seule dclaration et une seule implmentation dune mthode partielle. Lexemple suivant illustre un exemple dune mthode partielle dfinie et implmente dans deux fichiers diffrents.

Les mthodes partielles (C#3.0)

93

Voici le premier fichier:


partial class Personne { private int compteur; public Personne() { this.Initialiser(); } partial void Initialiser(); }

Et ensuite le second fichier:


partial class Personne { partial void Initialiser() { this.compteur = 10; } }

Info Comme pour les classes partielles, les mthodes partielles sont utiliser conjointement avec un gnrateur de code, vous permettant dimplmenter si ncessaire une mthode utilise et gnre par ce dernier.

94

CHAPITRE 2 Les classes

Les mthodes dextension (C#3.5)


// Les mthodes dextension doivent tre dans // une classe statique public static class <nom classe> { // Dclarer une mthode dextension public static <retour> <nom>(this <type tendu> <nom paramtre>[, <paramtres>]) { // Code de la mthode } } // Utiliser une mthode dextension <type tendu> <instance>; // Appeler une mthode dextension <instance>.<nom>([<paramtres>]); // Ou alors comme une mthode statique: <type tendu>.<nom>(<instance>, [<paramtres>]);

Les mthodes dextension permettent dajouter virtuellement une mthode public une classe dj existante sans avoir besoin de modifier cette dernire. Les mthodes dextensions sont des mthodes static dclares dans une classe static. Elles ne peuvent donc pas avoir accs tous les membres private ou protected de la classe associe. Le premier paramtre indique linstance o est appele la mthode.

Les mthodes dextension (C#3.5)

95

Lexemple suivant illustre la cration dune mthode dextension permettant dajouter une mthode Afficher() la classe int (Int32) et permettant dafficher le nombre associ.
public static class MesExtensions { public static void Afficher(this int nombre) { Console.WriteLine(nombre); } }

Voici un exemple qui illustre lutilisation de cette mthode dextension.


int entier; entier = 1664; // Appel de la mthode dextension entier.Afficher(); // Autre alternative quivalente MesExtensions.Afficher(entier);

Attention Les mthodes dextension permettent dtendre les fonctionnalits de classes dj existantes. vitez de trop les utiliser, car cela dnature la programmation oriente objet et peut rendre votre code trs difficile comprendre.

3
Lhritage
Lhritage permet de crer de nouvelles classes en sappuyant sur des classes dj existantes afin de leur ajouter de nouvelles fonctionnalits ou de modifier les fonctionnalits existantes. Cela permet notamment aux dveloppeurs de factoriser le code.

Utiliser lhritage
class <nom classe drive> : <nom classe base> { // Nouveaux membres ou redfinition des membres } // Utilisation du polymorphisme <nom classe base> <instance>; <instance> = new <nom classe drive>(); <instance>.<nom classe drive>; // Oprateur cast <instance drive> = (<classe drive>)<instance dune classe de base>;

Lorsquune classe drive dune classe de base, elle peut accder tous les membres de la classe de base qui ne sont pas privs. Dans le .NET Framework, si une classe nhrite daucune classe explicitement, alors le compilateur la fait

98

CHAPITRE 3 Lhritage

hriter de la classe System.Object. Une classe ne peut hriter que dune seule classe. Lexemple suivant illustre la dclaration dune classe Voiture qui hrite de la classe Vhicule.
class Vhicule // Hrite implicitement de Object { private int compteur; public void Avancer() { this.compteur++; } } class Voiture: Vhicule // Hrite de vhicule { public void OuvrirCoffre() { Console.WriteLine(Ouverture du coffre); } }

La classe Voiture hritant de Vhicule, elle hrite des membres non privs de la classe Vhicule. Il est donc possible dappeler la mthode Avancer() sur une instance de la classe Voiture. Lexemple suivant illustre lutilisation de cet hritage.
Voiture v; v = new Voiture(); v.OuvrirCoffre(); // Voiture hrite de Vhicule, elle hrite donc // de la mthode Avancer() de la classe Vhicule v.Avancer();

Utiliser lhritage

99

Si lon considre que la classe Camion hrite de Vhicule, on peut dire que: Un Camion est un Vhicule. Une Voiture est un Vhicule. Un Vhicule nest pas forcment un Camion ou une Voiture. Ces affirmations permettent dintroduire un concept li lhritage qui sappelle le polymorphisme . Grce au polymorphisme, il est possible de dclarer une variable dun type de base faisant rfrence une instance drive. Lexemple suivant illustre ce concept.
Vhicule v; v = new Voiture(); // Mme si la variable v fait rfrence une Voiture, // elle est considre comme de type Vhicule: il est // impossible dappeler la mthode v.OuvrirCoffre(); // v tant de type Vhicule, il est possible dappeler // la mthode Avancer() v.Avancer(); v = new Camion(); // Un Camion est un Vhicule v.Avancer();

Comme expliqu en commentaires, si lon dclare une variable dun type de base, il nest plus possible daccder aux membres des classes drives, mme si cette variable fait rfrence une instance dun type driv. Pour pallier ce problme, on peut utiliser loprateur cast qui consiste tout simplement changer et forcer le type dune variable. Lexemple suivant reprend lexemple prcdent en utilisant cet oprateur.

100

CHAPITRE 3 Lhritage

Vhicule v; v = new Voiture(); ((Voiture)v).OuvrirCoffre(); // Caster v en Voiture v = new Camion(); ((Voiture)v).OuvrirCoffre(); // Erreur lexcution

Attention Loprateur cast permet de forcer la compilation en spcifiant le type rel dune variable dinstance. Si le type spcifi est incorrect, une erreur aura lieu lexcution et non la compilation! Vous devez donc tre trs vigilant lorsque vous utilisez cet oprateur.

Redfinir unemthode
// Dclarer une mthode dans la classe de base // pouvant tre redfinie <visibilit> virtual <type retour> <nom>([paramtres]) { // Code de la mthode de la classe de base } // Dclarer une redfinition dune mthode // dans la classe drive <visibilit> override <type retour> <nom>([paramtres]) { // Code de la mthode de classe drive } // Appeler la mthode de la classe de base dans // la classe drive base.<nom>([paramtres]);

Redfinir unemthode

101

Par dfaut, les mthodes des classes de base ne peuvent pas tre redfinies; il faut spcifier le mot-cl virtual dans la dfinition des mthodes, afin dautoriser les classes drives redfinir la mthode si ncessaire. Dans les classes drives, la redfinition dune mthode se fait en utilisant le mot-cl override. Lexemple suivant illustre la redfinition de la mthode Avancer() dans la classe Voiture afin dincrmenter beaucoup plus rapidement le compteur kilomtrique.
class Vhicule { // Afin que compteur soit accessible pour les // classes drives, on spcifie le niveau // de visibilit protected protected int compteur; public virtual void Avancer() { this.compteur++; } } class Voiture: Vhicule { public override void Avancer() { this.compteur += 5; } }

Dans lexemple prcdent, si lon cre une instance de la classe Voiture et que lon appelle la mthode Avancer(), alors le compteur kilomtrique sera automatiquement incrment de 5. Dans le cas de plusieurs hritages, cest la mthode la plusdrive (cest--dire celle se trouvant dans la classe laplus drive) qui sera appele. La mthode rellement

102

CHAPITRE 3 Lhritage

appele dpend uniquement du type rel et non du type apparent. Par exemple, lappel de la mthode Avancer() sur un objet de type Voiture rfrenc par une variable de type Vhicule sera ralis sur la classe Voiture.
Vhicule v; // Type apparent v = new Voiture(); // Type rel v.Avancer(); // La mthode Avancer() de la classe // Voiture sera appele.

Il est possible de faire appel la mthode de la classe de base redfinie en utilisant le mot-cl base. Ainsi, il nest plus ncessaire de dfinir le champ compteur comme protected. Lexemple suivant illustre lutilisation du mot-cl base permettant dappeler cinq fois la mthode Avancer() de la classe Vhicule.
class Vhicule { private int compteur; public virtual void Avancer() { this.compteur++; } } class Voiture: Vhicule { public override void Avancer() { for(int i=0; i<5; i++) { // Appeler la mthode Avancer() de la classe // de base base.Avancer(); } } }

Redfinir uneproprit

103

Redfinir uneproprit
// Dclarer une proprit dans la classe de base // pouvant tre redfinie <visibilit> virtual <type retour> <nom> { get { // Code permettant de rcuprer la valeur } set { // Code permettant de modifier la valeur } } // Dclarer une redfinition dune proprit // dans la classe drive <visibilit> override <type retour> <nom proprit> { get { // Code permettant de rcuprer la valeur } set { // Code permettant de modifier la valeur } } // Appeler une proprit de la classe base dans la // classe drive <valeur> = base.<nom proprit>; base.<nom proprit> = <valeur>;

Comme pour les mthodes, les proprits ne peuvent pas tre redfinies par dfaut. Il faut explicitement spcifier laide du mot-cl virtual les proprits pouvant tre redfinies dans les classes drives. Il est possible dutiliser le mot-cl base afin daccder ou de modifier la proprit de la classe de base. Dans les classes drives, la redfinition dune mthode se fait en utilisant le mot-cl override. Dans le cas de plusieurs hritages, cest la proprit la plus drive (cest--dire celle se trouvant dans la classe la plus drive) qui sera appele. La proprit rellement appele dpend uniquement du type rel et non du type apparent. Par exemple, lappel de la proprit Immatriculation sur

104

CHAPITRE 3 Lhritage

un objet de type Voiture rfrenc par une variable de type Vhicule sera ralis sur la classe Voiture.
Vhicule v; v = new Voiture(); // Type apparent // Type rel

v.Immatriculation = ZZ; // La proprit // Immatriculation de la classe Vhicule sera appele

Lexemple suivant illustre la redfinition de la proprit Immatriculation de la classe Vhicule dans la classe Voiture afin de faire prfixer limmatriculation par VL au moment de la rcupration de la proprit.
class Vhicule { private string immatriculation; public virtual string Immatriculation { get { return this.immatriculation; } set { this.immatriculation = value; } } } class Voiture: Vhicule { public override string Immatriculation { get { return VL + base.Immatriculation; } set { base.Immatriculation = value; } } }

Appeler leconstructeur delaclasse debase

105

Appeler leconstructeur delaclasse debase


<visibilit> class <nom classe drive> : <nom classe base> { // Dfinition dun constructeur de la classe drive <visibilit> <nom classe drive>([paramtres]) : base([paramtres]) // Appel du constructeur // de base { // Code du constructeur driv } }

Lors de linstanciation dune classe drive, le constructeur sans paramtre de la classe de base est automatiquement appel. Si celui-ci nexiste pas, il faut alors lappeler explicitement. Pour cela, on utilise le mot-cl base suivi des paramtres envoyer lun des constructeurs de la classe de base. Lexemple suivant illustre une classe Animal contenant un champ age qui est initialis laide dun constructeur. Une classe Chien est ensuite dfinie hritant dAnimal et contenant un champ nom qui est initialis laide dun constructeur. Ce dernier appelle le constructeur de la classe Animal afin de passer lge du Chien.
class Animal { private int age; public Animal(int age) { this.age = age; } }

106

CHAPITRE 3 Lhritage

class Chien: Animal { private string nom; public Chien(string nom, int age): base(age) { this.nom = nom; } }

Masquer unemthode
// Masquer une mthode contenue dans // une classe drive <visibilit> new <type retour> <nom>([paramtres]) { // Code de la mthode de classe drive }

Le masquage dune mthode consiste remplacer une mthode dj existante dans une classe drive. Les mthodes de la classe de base nont pas tre marques avec le quantifieur virtual. Le remplacement dune mthode se fait en utilisant le mot-cl new. Il permet de rompre son hritage et permet le plus souvent de changer sa signature (cest--dire ses paramtres et sa valeur de retour). Lexemple suivant illustre la dclaration dune classe Vhicule contenant une mthode Avancer(). Cette dernire est masque dans la classe drive Voiture.

Masquer unemthode

107

class Vhicule { // Afin que compteur soit accessible pour les // classes drives, on spcifie le niveau // de visibilit protected protected int compteur; public void Avancer() { this.compteur++; } } class Voiture: Vhicule { public new void Avancer() { this.compteur += 5; } }

La mthode rellement appele dpend du type apparent de lobjet et non du type rel. Lexemple suivant illustre cette diffrence.
Vhicule v; v = new Voiture(); v.Avancer(); // Type apparent // Type rel // La mthode Avancer() de la // classe Vhicule sera appele

((Voiture)v).Avancer(); // La mthode Avancer() de // la classe Voiture sera appele

108

CHAPITRE 3 Lhritage

Le masquage de mthode est souvent utilis pour changer le type de retour dune mthode. Ce type de retour est le plus souvent celui dune classe plus drive que le type de retour dorigine. Lexemple suivant illustre la dclaration dune classe Voiture qui hrite de Vhicule. Ces classes sont fabriques respec tivement par les classes UsineVoiture et UsineVhicule. La classe UsineVhicule contient une mthode Fabriquer() permettant la fabrication dun vhicule. Cette mthode est ensuite redfinie dans la classe UsineVoiture afin de fabriquer des voitures laide dun masquage.
class Vhicule { } class Voiture : Vhicule { } class UsineVhicule { public Vhicule Fabriquer() { return new Vhicule(); } } class UsineVoiture : UsineVhicule { public new Voiture Fabriquer() { return new Voiture(); } }

Masquer uneproprit

109

La classe UsineVoiture masque la mthode Fabriquer() de la classe de base afin de changer le type de la classe drive. Cela vite de raliser un cast afin de rcuprer un objet de type Voiture chaque appel de la mthode Fabriquer().

Masquer uneproprit
// Dclarer une redfinition dune proprit // dans la classe drive <visibilit> new <type retour> <nom proprit> { get { // Code permettant de rcuprer la valeur } set { // Code permettant de modifier la valeur } }

Le masquage dune proprit consiste remplacer une proprit dj existante dans une classe drive. Les proprits de la classe de base nont pas tre marques avec le quantifieur virtual. Le remplacement dune mthode se fait en utilisant le mot-cl new. Il permet de rompre son hritage et permet le plus souvent de changer son type de retour. Lexemple suivant illustre la dclaration dune classe Vhicule contenant une proprit Immatriculation. Cette dernire est remplace dans la classe drive Voiture laide du quantificateur new.
class Vhicule { private string immatriculation; public string Immatriculation { get { return this.immatriculation; }

110

CHAPITRE 3 Lhritage

set { this.immatriculation = value; } } } class Voiture: Vhicule { public new string Immatriculation { get { return VL + base.Immatriculation; } set { base.Immatriculation = value; } } }

La proprit rellement appele dpend du type apparent de lobjet et non du type rel. Lexemple suivant illustre cette diffrence.
Vhicule v; v = new Vhicule(); // Type apparent // Type rel

v.Immatriculation = ZZ;// La proprit Immatriculation // de la classe Vhicule sera appele ((Voiture)v).Immatriculation = ZZ; // La proprit // Immatriculation de la classe Voiture sera appele

Le masquage de proprit est souvent utilis pour changer le type de retour dune proprit. Ce type de retour est le plus souvent dun type dune classe plus drive que le type de retour dorigine. Lexemple suivant illustre une classe Camion hritant de Vhicule. La classe Vhicule fait rfrence une Personne en utilisant une proprit. Cette proprit est alors redfinie dans la classe Camion afin de faire rfrence un objet Homme.

Masquer uneproprit

111

class Personne { } class Homme : Personne { } class Vhicule { private Personne personne; public Vhicule(Personne personne) { this.personne = personne; } public Personne Personne { get { return this.personne; } } } class Camion : Vhicule { public Camion(Homme homme) : base(homme) { } public new Homme Personne { get { return (Homme)base.Personne; } } }

112

CHAPITRE 3 Lhritage

Dans lexemple prcdent, on suppose que la personne associe un Camion doit tre ncessairement de type Homme. Cette condition est vrifie automatiquement grce au constructeur de Camion qui prend en paramtre un Homme. On peut donc en dduire quune instance dun objet Homme sera toujours retourne par la proprit Personne. Afin dviter de nombreux cast, il est donc possible de remplacer dans la classe Camion la proprit Personne de la classe Vhicule par une proprit de mme nom retournant un objet de type Homme (le cast sera ralis une seule fois dans la proprit et non par le code appelant). Lexemple suivant illustre lutilisation des classes dclares prcdemment.
Camion camion; Vhicule vhicule; Homme homme; camion = new Camion(new Homme()); homme = camion.Personne; // Cast inutile vhicule = camion; homme = (Homme)vhicule.Personne; // Cast obligatoire

Utiliser les interfaces


<visibilit> interface <nom> { // Membres de linterface }

Les interfaces contiennent uniquement des signatures de mthodes, de proprits ou dvnements. Elles ne contien nent donc aucun code. Elles permettent de dfinir un contrat que doivent implmenter les classes qui drivent de cette interface. Une interface peut hriter de plusieurs interfaces.

Implmenter uneinterface

113

Les interfaces permettent souvent de contourner le manque de la notion dhritage multiple dans C#. Elles permettent de regrouper des classes ayant des fonctionnalits identiques (mthodes, proprits et vnements) tout en ntant pas dans la mme hirarchie dhritage. Les membres contenus dans une interface nont pas de niveau de visibilit. Lexemple suivant illustre la dclaration dune interface IIdentifiable contenant une proprit Id.
public interface IIdentifiable { int Id { get; } }

Toutes les classes qui hriteront de cette interface devront implmenter une proprit Id en lecture seule.

Implmenter uneinterface
// Dclarer une classe implmentant des interfaces // implicitement <visibilit> class <nom>: [<classe drive>,] <interfaces> { public <membre de linterface> }

Limplmentation dune interface dans une classe consiste tout simplement redfinir tous les membres contenus dans linterface. Les membres qui sont implments doivent tre obligatoirement public.

114

CHAPITRE 3 Lhritage

Voici un exemple qui illustre une classe Voiture et Personne implmentant linterface IIdentifiable du prcdent exemple.
class Personne : IIdentifiable { private int id; private int age; public Personne(int id, int age) { this.id = id; this.age = age; } public int Id { get { return this.id; } } public int Age { get { return this.age; } } } class Voiture : IIdentifiable { private int id; private string immatriculation; public Voiture(int id, string immatriculation) { this.id = id; this.immatriculation = immatriculation; }

Implmenter uneinterface

115

public int Id { get { return this.id; } } public string Immatriculation { get { return this.immatriculation; } } }

Ces deux classes implmentant la mme interface, il est possible de regrouper ces objets qui nont aucun lien dhritage ( part la classe System.Object du .NET Framework). Lexemple suivant illustre la cration dun tableau dobjet implmentant linterface IIdentifiable contenant une Voiture et une Personne. Les identifiants de ces objets sont ensuite affichs sur la console.
IIdentifiable[] tab; tab = new IIdentifiable[] { new Voiture(16, AA-000-ZZ), new Personne(64, 26) }; for (int i = 0; i < tab.Length; i++) { Console.WriteLine(tab[i]); }

116

CHAPITRE 3 Lhritage

Implmenter uneinterface explicitement


// Dclarer une classe implmentant des interfaces // explicitement <visibilit> class <nom>: [<classe drive>,] <interfaces> { <nom interface>.<membre de linterface> }

Il arrive parfois que lon souhaite implmenter une interface de manire explicite afin dhriter dune interface sans rendre publique ses membres dans la classe drive. Implmenter une interface de manire explicite consiste tout simplement prfixer les membres par le nom de linterface. Il est possible de mlanger les implmentations explicites ou implicites des membres dune interface. Limplmentation explicite des interfaces permet de rsoudre les conflits dus des membres qui seraient prsents dans plusieurs interfaces implmentes par une classe. Les membres implments de manire explicite ne sont pas visibles. Leur accs ne peut se faire que sur une variable du type de linterface. Utilisez loprateur cast si ncessaire. Lexemple suivant illustre limplmentation de manire explicite de la proprit Id de linterface IIdentifiable pour les classes Vhicule et Personne.
class Personne : IIdentifiable { private int numroScu; private int age; public Personne(int numroScu, int age) {

Implmenter uneinterface explicitement

117

this.numroScu = numroScu; this.age = age; } int IIdentifiable.Id { get { return this.NumroScu; } } public int NumroScu { get { return this.numroScu; } } public int Age { get { return this.age; } } } class Voiture : IIdentifiable { private int numroSrie; private string immatriculation; public Voiture(int id, string immatriculation) { this.id = id; this.immatriculation = immatriculation; } int IIdentifiable.Id { get { return this.NumroSrie; } } public int NumroSrie {

118

CHAPITRE 3 Lhritage

get { return this.numroSrie; } } public string Immatriculation { get { return this.immatriculation; } } }

Dans lexemple prcdent, on a voulu implmenter de manire explicite la proprit Id de linterface IIdentifiant, car les deux classes disposent dj dune proprit (Num roScu et NumroSrie) permettant didentifier respectivement une Personne et une Voiture. Ainsi, la proprit Id nest pas ajoute aux classes mais peut tre utilisable lors de lutilisation de linterface IIdentifiable. Lexemple suivant illustre lutilisation de la proprit Id sur une instance dune voiture. La proprit Id tant implment de manire explicite, elle est donc non visible; un cast est alors ncessaire.
Voiture v; v = new Voiture(16, AA-000-ZZ); Console.WriteLine(((IIdentifiable)v).Id);

Les classes, mthodes etproprits abstraites


// Dclarer une classe abstraite <visibilit> abstract class <nom> { // Dclarer une mthode abstraite

Les classes, mthodes etproprits abstraites

119

<visibilit> abstract <retour> <nom mthode> (<paramtres>); // Dclarer une proprit abstraite <visibilit> abstract <type> <nom proprit> { get; // Si la proprit est en lecture set; // Si la proprit est en criture } } // Implmentation dune classe abstraite <visibilit> class <nom>: <nom classe abstraite> { // Implmentation dune mthode abstraite <visibilit> override <retour> <nom mthode> (<paramtres>); // Implmentation dune proprit <visibilit> override <type> <nom { get; // Si la proprit est en set; // Si la proprit est en } } abstraite proprit> lecture criture

Les classes abstraites sont des classes qui ne peuvent pas tre instancies. Elles doivent tre hrites et instancies par une classe drive non abstraite afin dtre utilise. Les classes abstraites peuvent contenir des mthodes abstraites et des proprits abstraites qui ne contiennent aucun code. Ces mthodes et ces proprits devront tre obligatoirement implmentes par le ou les classes drives non abstraites. Contrairement aux interfaces, les classes abstraites peuvent contenir des champs ainsi que des mthodes et des proprits contenant du code.

120

CHAPITRE 3 Lhritage

La dfinition dune classe, dune mthode ou dune proprit abstraite se fait en utilisant le mot-cl abstract. Comme pour les interfaces, les classes abstraites permettent de jouer avec le polymorphisme. Il est donc possible dappeler des mthodes abstraites sur un type apparent; cest la mthode implmente qui sera automatiquement appele sur le type rel.
ClasseAbstraitec; // Type apparent c = new ClasseDrive(); // Type rel c.MthodeAbstraite(); // Ici on appellera la mthode // implmente dans ClasseDrive

Lexemple suivant dfinit une classe abstraite Animal contenant une mthode abstraite protected EmettreSon() et une proprit public abstraite PeauType. Cette classe est ensuite hrite et implmente par deux autres classes Chien et Oiseau, qui implmentent les membres abstraits de la classe Animal.
public abstract class Animal { // Son mit par lanimal ( implmenter) protected abstract void EmettreSon(); // Type de peau ( implmenter) public abstract string PeauType { get; } public void Chatouiller() { Console.WriteLine(Je chatouille lanimal...); this.EmettreSon(); } } public class Chien : Animal

Les classes, mthodes etproprits abstraites

121

{ protected override void EmettreSon() { Console.WriteLine(Waf ! Waf !); } public override string PeauType { get { return Poils; } } } public class Oiseau : Animal { protected override void EmettreSon() { Console.WriteLine(Cui ! Cui !); } public override string PeauType { get { return Plumes; } } }

Lexemple qui suit illustre lutilisation de ces trois classes en dclarant et en initialisant un tableau dAnimal. Pour chaque Animal contenu dans ce tableau, on affiche son type de peau et on le chatouille.
Animal[] animaux = new Animal[] { new Chien(), new Oiseau(), new Chien() }; for (int i = 0; i < animaux.Length; i++) { Console.WriteLine(Peau : + animaux[i].PeauType); animaux[i].Chatouiller(); Console.WriteLine(); }

122

CHAPITRE 3 Lhritage

On obtient en sortie sur la console:


Peau : Poils Je chatouille lanimal... Waf ! Waf ! Peau : Plumes Je chatouille lanimal... Cui ! Cui ! Peau : Poils Je chatouille lanimal... Waf ! Waf !

Les classes scelles


// Dclarer une classe scelle <visibilit> sealed class <nom> { // Code de la classe }

Il est possible de dclarer des classes qui ne peuvent pas tre drives. On utilise pour cela le mot-cl sealed. Les classes scelles permettent dassurer aux dveloppeurs que le comportement de leurs classes ne pourra pas tre modifi. Lexemple suivant illustre une classe scelle Chien.
class sealed Chien { public void Aboyer() { Console.WriteLine(Waf! Waf!); } }

Tester untype avecloprateuris 123

Crons maintenant une classe drive, comme ceci:


class SuperToutou: Chien { }

On obtient alors une erreur la compilation.

Tester untype avecloprateuris


// Retourner true si instance est du type spcifi, // false dans le cas contraire bool b = <instance> is <type>;

Loprateur is permet de tester si une variable contient une instance dun type spcifi (driv ou non). Cet opra teur est trs utile lors que vous souhaitez utiliser loprateur cast afin de contrler le type dune instance. Lexemple suivant illustre lutilisation de cet oprateur. On suppose quil existe une classe Homme hritant de Personne contenant une mthode BoireUneBire().
Personne p; p = new Homme(); if (p is Homme) { // p est de type Homme, il est donc possible de // caster p en Homme ((Homme)p).BoireUneBire(); } Info Si vous souhaitez tester le type dune instance et effectuer si possible un cast, prfrez lutilisation de loprateuras qui est beaucoup plus performant.

124

CHAPITRE 3 Lhritage

Caster uneinstance avecloprateur as


// Retourner linstance si instance est du type // spcifi, null dans le cas contraire <type> <rsultat> = <instance> as <type>;

Loprateur as fonctionne comme loprateur is: il teste si une variable contient une instance dun type spcifi (driv ou non). Si la variable est bien une instance du type spcifi, loprateur as ralise un cast et retourne linstance. Dans le cas contraire, loprateur as retourne la valeur null. Lexemple suivant illustre lutilisation de cet oprateur. On suppose quil existe une classe Homme hritant de Personne contenant une mthode BoireUneBire().
Personne p; Homme h; p = new Homme(); h = p as Homme; if (h!= null) { h.BoireUneBire(); }

4
La gestion deserreurs
Le programmeur doit considrer durant le dveloppement tous les cas de figure relatifs lexcution de son code, et en particulier les erreurs dexcution pouvant survenir. Entraitant une erreur dexcution, le dveloppeur doit aussi sassurer que le systme repart dans un tat stable. Cetravail est trs fastidieux et il est fort probable que le dveloppeur oublie de traiter certains cas. Prenons lexemple suivant (on considre que la mthode OuvrirFichier() retourne null si le fichier ouvrir est inexistant).
StreamWriter sw; sw = OuvrirFichier(C:\\Mes documents\\Exemple.txt); sw.WriteLine(Bonjour !);

Dans le cas o le fichier nexiste pas, tenter dcrire la chane de caractres Bonjour! dans le fichier va provoquer une erreur dexcution. Bien videmment, il suffit au

126

CHAPITRE 4 La gestion deserreurs

dveloppeur de corriger ce problme en ajoutant une condition permettant de vrifier le rsultat retourn par la mthode OuvrirFichier().
StreamWriter sw; sw = OuvrirFichier(C:\\Mes documents\\Exemple.txt); if (sw == null) { Console.WriteLine(Fichier inexistant !); } else { sw.WriteLine(Bonjour !); }

Cette correction napporte pas ncessairement une solution efficace au problme. En effet, dans le cas o la mthode OuvrirFichier() retourne null, un message est affich sur la console pour prvenir lutilisateur quil est impossible douvrir le fichier. Mais aprs, est-ce que lappli cation va continuer de fonctionner? Lcriture de la chane Bonjour! dans le fichier nest-elle pas importante pour la suite de lexcution de lapplication? Pour bien grer une erreur dexcution, il faut sassurer quaprs son traitement, cation se trouve dans un tat stable et que lerreur lappli prcdemment traite ne risque pas dengendrer dautres erreurs dexcution. Pour rsoudre ce problme fastidieux et de manire fiable, le .NET Framework utilise le mcanisme des exceptions. La gestion des erreurs avec les exceptions se droule en deux phases: On code uniquement le code fonctionnel; si lon saperoit que le code se trouve dans un tat incorrect (par exemple un diviseur 0 ou un objet ayant une rfrence null), on signale une erreur dans lapplication. Cest ce que lon appelle la leve dune exception.

Dclencher uneexception 127

Si

lon souhaite traiter lerreur (la leve dune exception), on englobe la portion de code qui est susceptible de la dclencher et on traite lerreur. Il est important dtre sr quune fois lerreur traite, lapplication repart dans un tat stable.

Le deuxime point est facultatif. Dans le cas o aucun code nest capable de traiter une exception, lapplication est automatiquement arrte par le systme dexploitation. Cet arrt brutal de lapplication vite dexcuter une application instable produisant et utilisant des donnes incohrentes. Lors de la leve dune exception, il est possible de passer des informations complmentaires au code qui est susceptible de traiter lexception. Ces informations doivent tre contenues dans une classe hritant de la classe System. Exception du .NET Framework.

Dclencher uneexception
throw <exception>;

Le dclenchement dune exception se fait laide du motcl throw. Il est suivi dune instance dune classe hritant de la classe System.Exception. Lexemple suivant illustre la leve dune exception une mthode Diviser() dans le cas o le diviseur vaut0.
static int Diviser(int a, int b) { if (b == 0) { throw new Exception(Division par 0 impossible); } return a / b; }

128

CHAPITRE 4 La gestion deserreurs

Voici maintenant un code utilisant cette mthode dans un Main():


static void Main(string[] args) { int rsultat; rsultat = Diviser(10, 0); Console.WriteLine(Le rsultat est gal + rsultat); }

Si maintenant on excute le programme, voici ce qui saffi chera sur la console:


Exception non gre: System.Exception: Division par 0 impossible Program.Diviser(Int32 a, Int32 b) dans C:\...\Program.cs:ligne 9 Program.Main(String[] args) dans C:\...\Program.cs:ligne 19

Remarquez que la ligne qui devait afficher le rsultat na pas t excute.

Capturer uneexception
try { // Code susceptible de dclencher une exception } catch(<type dexception> [<nom paramtre>]) { // Traitement de lexception } [catch(<autre type dexception> [<nom paramtre>]) { // Traitement dune autre exception }]

Capturer uneexception 129

Le code qui est susceptible de dclencher une exception doit tre entour dans un bloc prcd par le mot-cl try. Lors de la leve dune exception dans ce bloc, le bloc catch associ sera excut.
Attention Aprs lexcution dun bloc catch, lexcution du code ne revient en aucun cas en arrire dans le bloc try. Le code poursuit son excution normale aprs le bloc catch.

Lexemple suivant illustre le traitement de la leve dune exception dans la mthode Diviser() de lexemple prcdent.
int rsultat; try { rsultat = Diviser(10, 0); Console.WriteLine(Le rsultat est gal + rsultat); } catch (Exception) { Console.WriteLine(Une erreur sest produite); }

Remarquez que laffichage du rsultat se fait aussi dans le bloc try. lexcution, cela produira sur la console:
Une erreur sest produite

Diffrentes exceptions peuvent se produire dans un bloc try. Il est possible dans ce cas dutiliser plusieurs blocs catch afin de traiter diffrents cas dexception.

130

CHAPITRE 4 La gestion deserreurs

Lexemple suivant illustre le traitement de deux exceptions, lune de type FileNotFoundException provoque par louver ture dun fichier inexistant, lautre de type Exception se produisant dans tous les autres cas.
try { EcrireFichier(); rsultat = Diviser(10, 0); Console.WriteLine(Le rsultat est gal + rsultat); } catch (FileNotFoundException) { Console.WriteLine(Fichier inexistant); } catch (Exception) { Console.WriteLine(Une erreur sest produite); }

Lorsque vous traitez plusieurs exceptions, le runtime du .NET Framework excutera le bloc catch dont le type de lexception est la plus spcifique dans la hirarchie dhritage de la classe System.Exception en partant de haut en bas dans lordre des blocs catch. Dans lexemple prcdent, si une exception de type NullReferenceException (hritant bien videmment de Exception) est dclenche, le bloc catch traitant lexception de type FileNotFoundException ne sera pas excut. En revanche le bloc catch traitant les exceptions de type Exception sera quant lui excut. Inversons maintenant lordre des blocs catch de lexemple prcdent.

Capturer uneexception

131

try { EcrireFichier(); rsultat = Diviser(10, 0); Console.WriteLine(Le rsultat est gal + rsultat); } catch (Exception) { Console.WriteLine(Une erreur sest produite); } catch (FileNotFoundException) { Console.WriteLine(Fichier inexistant); }

Le deuxime bloc catch ne sera jamais excut. En effet, lors de la leve dune exception de type FileNotFoundException, le premier bloc catch permet de traiter toutes les exceptions de type Exception. Or FileNotFoundException hrite de Exception, cest donc le premier bloc catch qui sera excut. Lorsque vous traitez plusieurs types dexceptions, veuillez traiter les exceptions les plus spcifiques dabord et ensuite les exceptions les plus gnriques. Lors de la leve dune exception avec le mot-cl throw, une instance de la classe Exception doit tre spcifie. Cette instance contient des informations sur lexception pouvant tre rcupre dans le bloc catch en donnant un nom au paramtre de lexception. Lexemple suivant illustre lutilisation dun paramtre dune exception permettant dafficher le message spcifi au moment du dclenchement de lexception.

132

CHAPITRE 4 La gestion deserreurs

try { throw new Exception(Une erreur sest produite); } catch (Exception e) { Console.WriteLine(e.Message); }

Les membres contenus dans la classe Exception sont prsents en dtail dans la section Proprits et mthodes de la classe Exception de ce chapitre.
Attention Nutilisez pas les exceptions pour tester ltat dun objet. Par exemple, la mthode File.Open() du .NET Framework dclenche une exception si le fichier spcifi est inexistant. Prfrez lutilisation dune mthode permettant de retourner ltat dun objet (par exemple File.Exists()) et ralisez un test sur ltat de lobjet obtenu avec une instruction conditionnelle if.

Laclause finally
try { // Code susceptible de dclencher une exception } catch (<exception> <nom paramtre>) { // Traitement de lexception } finally { // Code excut aprs la sortie du bloc try ou catch }

La clause finally 133

La clause finally permet dexcuter du code lors de la sortie du bloc try ou catch. Le bloc finally permet le plus souvent de librer une ressource en cas de leve ou non dune exception dans le bloc try associ. Voici un exemple illustrant lutilisation de la clause finally.
try { throw new Exception(Une exception...); Console.WriteLine(Je suis dans le bloc try); } catch (Exception) { Console.WriteLine(Je suis dans le bloc catch); } finally { Console.WriteLine(Je suis dans le bloc finally);

Le rsultat produit sur la console est le suivant:


Je suis dans le bloc catch Je suis dans le bloc finally

Lexcution du bloc try dans lexemple prcdent lve une exception; on passe alors au bloc catch. Une fois le bloc catch excut, on passe dans le bloc finally. Si lon supprime la leve de lexception dans le bloc try, le rsultat produit sur la console est le suivant:
Je suis dans le bloc try Je suis dans le bloc finally

Quune exception soit dclenche ou non, le bloc finally sera toujours appel en sortie du bloc try ou catch.
Info En cas de dclenchement dune exception dans le bloc catch, le flot dexcution sort du bloc catch, le bloc finally est donc appel.

134

CHAPITRE 4 La gestion deserreurs

Proprits et mthodes delaclasse Exception


// Message dinformation sur lexception String Message { get; } // Pile des appels de mthode o sest produite // lexception String StackTrace { get; } // Contient lexception qui a dclench lexception Exception InnerException { get; } // Obtenir la premire exception lorigine de // lexception Exception GetBaseException();

La classe Exception contient des informations permettant daider les dveloppeurs comprendre et localiser le dclenchement dune exception. La proprit Message de la classe Exception contient un message dcrivant lexception. La proprit InnerException contient une rfrence vers une exception lorigine de la nouvelle exception. En cas de dclenchement successif dune exception, il est possible de remonter lexception dorigine. La mthode GetBaseException() permet daccder directement lexception dorigine en remontant toutes les exceptions laide de la proprit InnerException. Les proprits Message et InnerException doivent tre spcifies par lutilisateur au moment de linstanciation de la classe Exception avant la leve dune exception avec le mot-cl throw.

Proprits et mthodes delaclasse Exception 135

La

proprit StackTrace est une proprit automatiquement alimente par le runtime du .NET Framework, qui contient la liste des appels des mthodes permettant de localiser prcisment dans le code o sest dclenche lexception.

Lexemple suivant illustre le dclenchement dune exception contenant un message Une exception. Cette exception est ensuite traite dans un bloc catch, qui redclenche une nouvelle exception associe la prcdente contenant le message Autre exception.
try { try { // Spcifier le message : Une exception throw new Exception(Une exception); } catch (Exception e) { // Redclencher une exception en spcifiant le // message Autre exception et en indiquant que // lexception dorigine (InnerException) est e throw new Exception(Autre exception, e); } } catch (Exception e) { Console.WriteLine(Message : + e.Message); Console.WriteLine(StackTrace : ); Console.WriteLine(e.StackTrace); Console.WriteLine(****** Exception interne ******) Console.WriteLine(Message: + e.InnerException.Message)); Console.WriteLine(StackTrace : ); Console.WriteLine(e.InnerException.StackTrace); }

136

CHAPITRE 4 La gestion deserreurs

Le rsultat produit sur la console est le suivant:


Message : Autre exception StackTrace : Program.Main(String[] args) dans C:\..\Program.cs:ligne 24 ****** Exception interne ****** Message : Une exception StackTrace : Program.Main(String[] args) dans C:\...\Program.cs:ligne 16

Remarquez que la proprit StackTrace prcise le nom du fichier source ainsi que le numro de la ligne permettant de localiser lexception.
Attention Lorsque vous dclenchez une exception, faites attention aux informations que vous divulguez dans la proprit Message. Souvent, le contenu des exceptions est enregistr dans des fichiers logs (le journal dvnements Windows par exemple). Les informations contenues dans les exceptions sont incomprhensibles pour les non-informaticiens, mais des personnes comptentes et mal intentionnes pourraient se servir de ces informations pour trouver une faille dans votre application

Propager uneexception aprs sa capture


try { // Code susceptible de dclencher une exception } catch (Exception) { // Traiter lexception throw; }

Propager uneexception aprs sa capture 137

Parfois, en traitant une exception, il est ncessaire de raliser certaines actions (la libration dune ressource par exemple) et de continuer propager lexception sans changer les informations contenues dans celle-ci. La premire ide consiste tout simplement dclencher nouveau lexception en utilisant le paramtre de lexception.
catch (Exception e) { // Librer les ressources throw e; }

En cas dexception, lexcution du code prcdent va dclencher la mme exception avec les mmes informations contenues dans celle-ci, except la proprit StackTrace qui sera automatiquement rgnre par le runtime du .NET Framework avec comme emplacement dorigine la ligne o a t de nouveau dclench lexception. On perd ainsi la trace de lemplacement dorigine o sest rellement dclenche lexception. Pour propager rellement lexception tout en prservant le StackTrace, il faut utiliser le mot-cl throw tout seul. Lexemple suivant illustre lutilisation du mot-cl throw sans utiliser le paramtre de lexception du bloc catch.
static void DclencherException() { throw new Exception(Une exception); } static void Main(string[] args) { try {

138

CHAPITRE 4 La gestion deserreurs

try { DclencherException(); } catch (Exception e) { Console.WriteLine(Libration des ressources); throw; } } catch (Exception e) { Console.WriteLine(Message : + e.Message); Console.WriteLine(StackTrace : ); Console.WriteLine(e.StackTrace); } }

Le rsultat produit sur la console est le suivant:


Libration des ressources Message : Une exception StackTrace : Program.DclencherException() dans C:\...\Program.cs:ligne 11 Program.Main(String[] args) dans C:\...\Program.cs:ligne 25

On constate que la pile des appels des mthodes contient tout en haut la mthode DclencherException() qui est lorigine relle de lexception. On modifie maintenant le bloc catch en ajoutant le paramtre dexception.
catch (Exception e) { Console.WriteLine(Libration des ressources); throw e; }

Propager uneexception aprs sa capture 139

Le rsultat produit sur la console est maintenant le suivant:


Libration des ressources Message : Une exception StackTrace : Program.Main(String[] args) dans C:\Temp\ ConsoleApplication7\Program.cs:ligne 25

On vient de perdre la mthode qui est rellement lorigine de lexception.

5
Les gnriques
On souhaiterait crer une classe Couple contenant un couple de deux objets de mme type (par exemple la classe Personne).Voici une premire implmentation qui rpondrait ce problme.
class Couple { private Personne premier; private Personne deuxime; public Couple(Personne premier, Personne deuxime) { this.premier = premier; this.deuxime = deuxime; } public Personne Premier { get { return this.premier; } } public Personne Deuxime { get { return this.deuxime; } } }

142

CHAPITRE 5 Les gnriques

Cette premire version fonctionne sans problme et correspond parfaitement notre objectif. Mais il est impossible de rutiliser cette classe pour dautres types (par exemple la classe Voiture). Dans ce cas, il faudrait crer une deuxime classe, avec un nom diffrent, donc le code serait la copie conforme du code de cette classe, mais en changeant Personne par Voiture. Cette solution double le volume de code et pnalise donc la maintenance logicielle. Une autre solution serait de considrer les deux lments du couple comme des objets (object).
class Couple { private object premier; private object deuxime; public Couple(object premier, object deuxime) { this.premier = premier; this.deuxime = deuxime; } public object Premier { get { return this.premier; } } public object Deuxime { get { return this.deuxime; } } }

Ainsi, on a une seule classe Couple qui peut tre utilise avec nimporte quel type dobjet. Il existe cependant un gros dfaut dans cette implmentation: la scurit des types. En effet, avec limplmentation de lexemple prc-

Utiliser les classes gnriques 143

dent, il est possible de crer un couple constitu dune Personne et dun Vhicule. De plus, chaque fois que lon veut rcuprer lun des composants du couple, un cast est ncessaire. Pour pallier ce problme, les gnriques sont disponibles depuis la version2.0 de C#; ils permettent de crer des classes paramtres et offrent un moyen de rutiliser et typer fortement votre code.

Utiliser les classes gnriques


// Dclarer une classe utilisant les gnriques class <nom><<nom type 1>[, ...]> { // Utiliser le type gnrique dans un champ <visibilit> <nom type 1> <nom champ>; // Utiliser le type gnrique dans une // proprit <visibilit> <nom type 1> <nom proprit> { get { ... } } // Utiliser le type gnrique dans une mthode. // Les paramtres des mthodes peuvent utiliser // aussi le type gnrique <visibilit> <nom type 1> <nom mthode> (<paramtres>) { } } // Instancier une classe gnrique <nom><<type 1>> <instance>; <instance> = new <nom><<type 1>>(<paramtres>);

144

CHAPITRE 5 Les gnriques

Les gnriques (paramtres de type) se dclarent aprs le nom de la classe en leur donnant un nom distinct (classiquement T ). Une fois un paramtre de type dclar, il suffit alors de lutiliser nimporte quel endroit du code de la classe. Un paramtre de type est par dfaut considr comme un object. Il est donc impossible dappeler des oprateurs, tels que laddition, sur ces types. Lexemple suivant illustre une version gnrique de la classe Couple prsente en introduction.
class Couple<T> { private T premier; private T deuxime; public Couple(T premier, T deuxime) { this.premier = premier; this.deuxime = deuxime; } public T Premier { get { return this.premier; } } public T Deuxime { get { return this.deuxime; } } }

Utiliser les classes gnriques 145

Voici maintenant comment utiliser cette classe gnrique avec un couple de Personne.
Personne p1; Personne p2; // Dclaration dun couple de Personne Couple<Personne> couple; p1 = new Personne(Aurlie); p2 = new Personne(Gilles); // Instanciation dune couple de Personne couple = new Couple<Personne>(p1, p2); // Affichage du nom de la premire personne Console.WriteLine(couple.Premier.Nom); // Affichage du nom de la deuxime personne Console.WriteLine(couple.Deuxime.Nom);

Cest au moment de la dclaration et de linstanciation que lon spcifie les paramtres du type utiliser. Les proprits Premier et Deuxime de la classe Couple retournent des objets de typeT qui correspondent au paramtre du type Couple. Dans lexemple prcdent, le typeT dclar pour linstance couple est de type Personne. Lappel aux proprits Premier et Deuxime retourne donc des Personne, il ny a donc aucun cast raliser et on peut accder directement aux proprits de la classe Personne (la proprit Nom dans le cas prsent). Bien videmment, il est possible de crer dans le mme code un autre couple dun autre type; il faudra par contre dclarer une nouvelle variable du type dsir. Lexemple qui suit illustre lutilisation de deux types de couple dans le mme code.

146

CHAPITRE 5 Les gnriques

Personne p1; Personne p2; Voiture v1; Voiture v2; // Dclaration dun couple de Personne et de Voiture Couple<Personne> couple; Couple<Voiture> autre; p1 p2 v1 v2 = = = = new new new new Personne(Aurlie); Personne(Gilles); Voiture(00-AAA-99); Voiture(55-ZZZ-33);

// Instanciation dun couple de Personne couple = new Couple<Personne>(p1, p2); // Instanciation dun couple de Voiture voiture = new Couple<Voiture>(v1, v2); // Affichage du nom et de limmatriculation du premier // composant des couples. Console.WriteLine(couple.Premier.Nom); Console.WriteLine(autre.Premier.Immatriculation);

Info Les classes utilisant des gnriques sont considres comme des types diffrents par le runtime .NET, en fonction des valeurs des paramtres du type. Par exemple, le type Couple<Voiture> est diffrent du type Couple<Personne>.

Dclarer et utiliser des mthodes gnriques 147

Dclarer et utiliser des mthodes gnriques


// Dclarer une mthode utilisant les gnriques <visibilit> <retour> <nom><<nom type 1> [, ...]>([paramtres]) { // Code de la mthode } // Utiliser une mthode gnrique contenant au // moins un paramtre utilisant un paramtre de type // gnrique valeur = <nom>([paramtres]); // Utiliser une mthode gnrique ne contenant // pas de paramtre utilisant un paramtre de type // gnrique valeur = <nom><<type 1>[, ...]>([paramtres]);

Comme pour les classes gnriques, il est possible de dfinir des mthodes gnriques permettant de typer les paramtres et la valeur de retour. Lexemple suivant illustre une mthode gnrique permettant de remplir un tableau avec un objet spcifi.
static void Remplir<T>(T[] tableau, T objet) { for (int i = 0; i < tableau.Length; i++) { tableau[i] = objet; } }

148

CHAPITRE 5 Les gnriques

Lavantage de rendre cette mthode gnrique est quelle force les dveloppeurs donner au paramtre objet un objet qui est identique celui du tableau.
int[] t; t = new int[10]; Remplir(t, 1664);// OK Remplir(t, a); // Ne compile pas car t est un // tableau de int et objet est un char

Lors du passage du tableau en paramtre dans la mthode Remplir(), le compilateur sait que le paramtre de typeT est de type int. Cest ce que lon appelle linfrence de type. Linfrence de type ne peut pas fonctionner dans les mthodes gnriques qui ne contiennent pas au moins un paramtre utilisant le type gnrique. Pour pallier ce problme, il faut, au moment de lappel de la mthode, spcifier explicitement le type du paramtre gnrique de la mthode. Lexemple suivant illustre une mthode permettant de faire un cast dun objet en un type spcifi en paramtre de type gnrique.
static void Caster<T>(object o) { return (T)o; }

Voici un exemple illustrant lutilisation de la mthode gnrique de lexemple prcdent.


object o; Personne p; o = new Personne(Gilles); p = Caster<Personne>(o);

Contraindre des paramtres gnriques 149

Lors de lappel de la mthode Caster(), il est ncessaire de spcifier le paramtre gnrique Personne car le compilateur nest pas en mesure de le dduire.

Contraindre des paramtres gnriques


// Dclarer une classe utilisant des gnriques // avec des contraintes class <nom><<nom type 1>[, ...]> [where <contraintes>] { // Code de la classe } // Dclarer une mthode utilisant des gnriques // avec des contraintes <visibilit> <retour> <nom><<nom type 1>[, ...]>([paramtres]) [where <contraintes>] { // Code de la mthode } // Les diffrentes contraintessur les paramtres // gnriques : // Doit tre une classe where <nom type>: class // Doit tre une structure where <nom type>: struct // Doit avoir un constructeur sans paramtre public where <nom type>: new() // Doit tre ou driver de la classe spcifie where <nom type>: <nom classe>

150

CHAPITRE 5 Les gnriques

// Doit tre ou implmenter linterface spcifie where <nom type>: <nom interface1> // Doit tre ou driver dun autre type gnrique where <nom type>: <autre nom type gnrique>

Les contraintes sur les paramtres de type permettent de restreindre les arguments de type gnrique dune classe ou dune mthode. Si cette restriction nest pas respecte, il en rsultera une erreur de compilation. Par exemple, en appliquant la contrainte suivante dans la classe Couple:
class Couple<T>where T: Personne

il nest possible que de dclarer des couples de Personne ou drivant de la classe Personne (on suppose que la classe Homme hrite de la classe Personne).
Couple<Personne> c1; // Correct Couple<Homme> c2; // Correct (Homme hrite de Personne) // La dclaration suivante provoquera une erreur // de compilation Couple<Chien> c3; // Chien nhrite pas de Personne

Lors de lapplication dune contrainte sur un argument de type, il devient possible pour ce type dutiliser les membres se rapportant aux contraintes appliques. Par exemple, si lon applique la contrainte prcdente sur le typeT de la classe Couple, T tant forcment de type Personne (driv ou non), il est donc permis dutiliser les membres de Personne sur les instances de typeT contenues dans la classe Couple.

Utiliser lemot-cl default

151

class Couple<T>where T: Personne { private T premier; ... void AfficherNomPremier() { // Le champ premier est de type T et donc une // Personne daprs la contrainte. Il est donc // possible dutiliser la proprit Nom. Console.WriteLine(this.premier.Nom); } }

Utiliser lemot-cl default


<nom argument type> <instance> = default (<nom argument type>);

Lors de lutilisation dun paramtre gnrique, il est impossible de savoir si le type de ce paramtre est de type rfrence (class) ou de type valeur (struct). Il nest donc pas possible, par exemple, dinitialiser une rfrence une variable de type gnrique null. Lexemple suivant illustre ce problme.
class Generique<T> { public void Exemple() { T a; a = null; // Erreur de compilation } }

152

CHAPITRE 5 Les gnriques

Dans cet exemple, la variablea est de type T, etT peut tre soit un type rfrence (par exemple Personne) soit un type valeur (par exemple int). Laffectation null dune variable de typeT est incorrecte dans le cas oT est de type valeur. Pour pallier ce problme, on peut utiliser le mot-cl default qui permet de rcuprer la valeur null pour les types rfrence et la valeur par dfaut pour les types valeur.
class Generique<T> { public void Exemple() { T a; a = default(T); // Erreur de compilation } }

Il est tout fait possible dutiliser des contraintes sur les arguments de type pour pallier ce problme, mais il faudra choisir si le typeT doit tre de type rfrence ou de type valeur. Cela ne permet donc pas de crer une classe gnrique permettant de manipuler la fois les deux types de classe.

Utiliser les dlgus gnriques (.NET3.5)


// Dlgus gnriques ne retournant aucune valeur delegate void Action<[T1,[T2[,...]]]>([T1 arg1 [, T2 arg2[,...]]]) // Dlgus gnriques retournant une valeur delegate TResult Func<[T1,[T2[,...]]], TResult>([T1 arg1[, T2 arg2[,...]]])

Utiliser les dlgus gnriques (.NET3.5) 153

Les dlgus gnriques permettent dutiliser directement des dlgus sans les avoir pralablement dfinis. Ces dlgus sont trs utiliss pour les expressions lambda (voir Chapitre2) et en particulier dans LINQ (voir Chapitre7). Tous les types des paramtres (sils existent) du dlgu doivent tre spcifis dans les paramtres de type gnrique T1, T2, etc. Le dlgu Action permet dutiliser des dlgus ne retournant aucune valeur. Lexemple suivant illustre la dclaration dune mthode ExcuterAction prenant en paramtre un dlgu Action contenant trois entiers.
public void ExcuterAction(Action<int, int, int> action, int valeur1, int valeur2, int valeur3) { action(valeur1, valeur2, valeur3); }

Voici maintenant un exemple dutilisation de la mthode prcdemment dclare.


Action<int, int, int> uneAction; uneAction = (e1, e2, e3) => { Console.WriteLine(e1); Console.WriteLine(e2); Console.WriteLine(e3); }; ExcuterAction(uneAction, 51, 16, 64);

Lexemple suivant affiche sur la console les nombres 51, 16 et64.

154

CHAPITRE 5 Les gnriques

Le dlgu Func permet dutiliser des dlgus retournant une valeur de type TResult. Lexemple suivant illustre la dclaration dune mthode ExcuterOpration prenant en paramtre un dlgu Func contenant deux entiers et retournant un double.
public int ExcuterOpration(Func<int, int, double> opration, int valeur1, int valeur2) { return (double)opration(valeur1, valeur2); }

Voici maintenant un exemple dutilisation de la mthode prcdemment dclare.


Func<int, int, double> uneOpration; double rsultat; uneOpration = (e1, e2) => e1 + e2; rsultat = ExcuterOpration(uneOpration, 16, 64); Console.WriteLine(rsultat);

Lexemple suivant affiche sur la console le nombre 80.


Info La version3.5 du .NET Framework limite le nombre de para mtre 8 pour le dlgu gnrique Func et 9 pour le dlgu gnrique Action. Ces limites ont t repousses 16 dans la version4.0 du .NET Framework pour les deux types de dlgus.

Utiliser lacovariance (C#4.0) 155

Utiliser lacovariance (C#4.0)


// Dclarer un paramtre de type covariant dans // une interface <visibilit> interface <nom><out <paramtre type>, ...> { // Dclaration des membres de linterface. // Le type T doit tre utilis comme type de retour // dune mthode, dune proprit ou dun vnement. } // Dclarer un paramtre de type covariant dans // un dlgu <visibilit> delegate <paramtre type covariant> <nom> <out <paramtre type covariant>, ...> ([<paramtres>]);

La covariance permet deffectuer des assignations trs similaires au polymorphisme ordinaire sur des types gnriques. Par exemple, on suppose que lon dispose dune classe de base Vhicule et dune classe drive Voiture. Le polymorphisme permet dassigner une instance de Voiture une variable de type Vhicule.
Voiture voiture; Vhicule vhicule; voiture = new Voiture(); vhicule = voiture;

Grce la covariance, si lon dispose dune interface ICouple<T> avecT un type covariant, qui contient une

156

CHAPITRE 5 Les gnriques

proprit Premier retournant un objet de typeT, il est alors possible dcrire le code suivant :
VhiculeunVhicule; Voiture uneVoiture; ICouple<Voiture> voitures; ICouple<Vhicule> vhicules; ... voitures.Premier = uneVoiture; vhicules = voitures; unVhicule = vhicules.Premier;

Le polymorphisme agit alors sur les paramtres du type gnrique. Pour dfinir quun paramtre gnrique est covariant, il faut le prcder du mot-cl out. Les paramtres gnriques covariants ne peuvent tre utiliss quavec des interfaces et des dlgus. Le paramtre de type covariant ne peut tre utilis que sur le type de retour dun dlgu ou sur des mthodes, proprits et vnements qui sont contenus dans une interface. Lexemple suivant illustre une interface ICouple<T> avecT un type covariant. Une implmentation de cette interface est ralise par la classe Couple.
interface ICouple<out T> { T Premier { get; } T Deuxime { get;

Utiliser lacovariance (C#4.0) 157

} } class Couple<T>: ICouple<T> { private T premier; private T deuxime; public Couple(T premier, T deuxime) { this.premier = premier; this.deuxime = deuxime; } public T Premier { get { return this.premier; } } public T Deuxime { get { return this.deuxime; } } }

Pour illustrer lutilisation de linterface ICouple<T> dclare prcdemment, il est ncessaire de dclarer deux classes dont lune drive de lautre. Le code suivant reprsente la dclaration de deux classes Voiture et Vhicule.
class Vhicule { } class Voiture: Vhicule { }

158

CHAPITRE 5 Les gnriques

Il est maintenant possible dutiliser le polymorphisme travers les paramtres des types gnriques.
VhiculeunVhicule; Voiture uneVoiture; ICouple<Voiture> voitures; ICouple<Vhicule> vhicules; uneVoiture = new Voiture(); voitures = new Couple<Voiture>(voiture, new Voiture()); vhicules = voitures; unVhicule = vhicules.Premier;

Pour les dlgus, il est aussi possible dutiliser la covariance. Lexemple suivant illustre la dclaration dun dlgu gnrique utilisant la covariance ainsi quune mthode CrerVoiture() respectant le prototype du dlgu ActionGnrique<Voiture>.
// Dfinition du dlgu gnrique delegate T ActionGnrique<out T>(); // Dfinition dune mthode respectant le prototype: // ActionGnrique<Voiture> static Voiture CrerVoiture() { return new Voiture(); }

Le code qui suit illustre lutilisation du dlgu et de la mthode tous deux dclars prcdemment.
ActionGnrique<Vhicule> action; Vhicule vhicule; action = new ActionGnrique<Vhicule>(CrerVoiture); vhicule = action();

Utiliser lacontravariance (C#4.0) 159

Grce la covariance, il est possible daffecter une variable de type ActionGnrique<Vhicule> une mthode respectant le prototype dun dlgu de type ActionGnrique <Voiture>.
Info La covariance est trs utilise par les expressions lambda (voir Chapitre2).

Utiliser lacontravariance (C#4.0)


// Dclarer un paramtre de type contravariant // dans une interface <visibilit> interface <nom><in <paramtre type>, ...> { // Dclaration des membres de linterface. Le // type T doit tre utilis comme type de paramtre // dune mthode, dun indexeur ou dun vnement. } // Dclarer un paramtre de type contravariant // dans un dlgu <visibilit> delegate <type retour> <nom> <in <paramtre type covariant>, ...> ([<paramtres (contravariant ou non)>]);

La contravariance permet deffectuer des assignations trs similaires au polymorphisme ordinaire sur des types gnriques. Par exemple, on suppose que lon dispose dune classe de base Vhicule et dune classe drive Voiture. Le polymorphisme permet dassigner une instance de Voiture une variable de type Vhicule.

160

CHAPITRE 5 Les gnriques

Voiture voiture; Vhicule vhicule; voiture = new Voiture(); vhicule = voiture;

Grce la contravariance, si lon dispose dune interface IAction<T> avecT un type covariant contenant une mthode FaireAction(T), il est alors possible dcrire le code suivant.
Voiture uneVoiture; IAction<Voiture> actionVoiture; IAction<Vhicule> actionVhicules; ... uneVoiture = new Voiture(); actionVhicules = new UneAction<Vehicule>(); actionVoiture = actionVhicules; actionVoiture.FaireAction(uneVoiture);

Le polymorphisme agit alors sur les paramtres du type gnrique. Pour dfinir quun paramtre gnrique est contravariant, il faut le faire prcder du mot-cl in. Les paramtres gnriques contravariants ne peuvent tre utiliss quavec des interfaces et des dlgus. Le paramtre de type contravariant ne peut tre utilis que sur les types des paramtres dun dlgu ou sur les mthodes, indexeurs et vnements qui sont contenus dans une interface. Lexemple suivant illustre une interface IAction<T> avecT un type contravariant. Une implmentation de cette interface est ralise par la classe AfficherSurConsole.

Utiliser lacontravariance (C#4.0)

161

interface IAction<in T> { void FaireAction(T objet); } public class AfficherSurConsole<T> : IAction<T> { public void FaireAction(T objet) { Console.WriteLine(objet); } }

Pour illustrer lutilisation de linterface IAction<T> dclare prcdemment, il est ncessaire de dclarer deux classes dont lune drive de lautre. Le code suivant reprsente la dclaration de deux classes Voiture et Vhicule.
class Vhicule { } class Voiture: Vhicule { }

Il est maintenant possible dutiliser le polymorphisme travers les paramtres des types gnriques.
Voiture voiture; IAction<Voiture> actionVoiture; IAction<Vhicule> actionVhicule; voiture = new Voiture(); actionVhicule = new AfficherSurConsole<Vhicule>(); actionVoiture = actionVhicule;

162

CHAPITRE 5 Les gnriques

// La mthode FaireAction() nest maintenant utilisable // quavec des types Voiture actionVoiture.FaireAction(voiture);

Pour les dlgus, il est aussi possible dutiliser la contravariance. Lexemple suivant illustre la dclaration dun dlgu gnrique utilisant la contravariance ainsi quune mthode AfficherVhicule(Vhicule) respectant le prototype du dlgu ActionGnrique<Voiture>.
delegate void ActionGnrique<in T>(T objet); static void AfficherVhicule(Vhicule vhicule) { Console.WriteLine(vhicule); }

Le code qui suit illustre lutilisation du dlgu et de la mthode dclars prcdemment.


ActionGnrique<Voiture> action; Voiture voiture; voiture = new Voiture(); action = new ActionGnrique<Voiture>(AfficherVhicule); action(voiture);

Grce la contravariance, il est possible daffecter une variable de type ActionGnrique<Voiture> une mthode respectant le prototype dun dlgu de type ActionGnrique <Vhicule>.
Info La contravariance est trs utilise par les expressions lambda (voir Chapitre2).

6
Les chanes de caractres
Les chanes de caractres sont reprsentes par des instances de la classe System.String du .NET Framework. Les instances de cette classe sont immuables, cest--dire que la cration dune nouvelle chane (suite une concatnation par exemple), ncessite la cration dune nouvelle instance de la classe String. La classe String contient en interne un tableau de char, cest--dire un tableau de caractres; les caractres dune chane sont donc indics en partant de0. Les caractres tant au format Unicode UTF-16, les chanes de caractres en .NET sont donc toujours au format Unicode UTF-16. Une chane de caractres peut tre vide, cest--dire dune longueur 0. En C#, le mot-cl string est un raccourci pour la classe System.String.

164

CHAPITRE 6 Les chanes de caractres

Crer unechane decaractres


// Dclarer une chane de caractres string <nom variable>; // Affecter une chane de caractres <nom variable> = <chane>; // Caractres dchappement \t // Tabulation \n // Saut de ligne \r // Retour chariot \ // Caractre \ // Caractre \\ // Caractre \uXXXX // Caractre Unicode XXXX (hexadcimal) // Oprateur verbatim <nom variable> = @<chane sans caractres dchappement>;

Une chane de caractres est stocke dans une variable de type string (quivalent un alias vers System.String). Pour crer une chane de caractres, il suffit dcrire une suite de caractres comprise entre guillemets .
string s; s = *** Bonjour tout le monde! ***

Certains caractres ne sont pas autoriss ou ne peuvent pastre spcifis (car non affichables) entre les guillemets. Le caractre antislash \ et la tabulation en sont de trs bons exemples. Pour les reprsenter dans une chane de caractres, il faut utiliser des caractres dchappement. Les caractres

Crer unechane decaractres 165

dchappement commencent par un antislash\ et sont suivis dune lettre. Voici un exemple qui utilise des caractres dchappement.
Console.WriteLine(Une tabulation \t avec antislash \\)

Le rsultat produit sur la console est le suivant:


Une tabulation avec antislash \

Pour spcifier un caractre particulier en fonction de son code Unicode, il faut utiliser le caractre dchappement\u suivit du code en hexadcimal du caractre afficher. Lexemple suivant cre une chane de caractres contenant le caractre 0021 qui correspond au point dexclamation!.
s = Le caractre : \u0021

Dans certains cas, le fait dutiliser trop de caractres dchappement peut rendre difficile la lecture dune chane de caractres dans le code:
s = C:\\Users\\Gilles.Tourreau\\Documents\\Livre

Pour pallier ce problme, le C# dispose dun oprateur@ appel verbatim. Cet oprateur se place au dbut de la chane de caractres et permet dviter dcrire des caractres dchappement.Voici la mme chane que prcdemment mais compose laide de loprateur verbatim:
s = @C:\Users\Gilles.Tourreau\Documents\Livre

166

CHAPITRE 6 Les chanes de caractres

Obtenir lalongueur dunechane decaractres


public int Length { get; }

La proprit Length permet de rcuprer la longueur dune chane de caractres. Les chanes de caractres vides ont une longueur de0. Lexemple suivant illustre la rcupration de la longueur dune chane de caractres Bonjour!.
string chane = Bonjour!; int longueur = chane.Length;

// Retourne 9

Obtenir uncaractre
public char this[int index] { get; }

Les chanes de caractres sont contenues dans des tableaux de char, il est possible de rcuprer un caractre de ce tableau en utilisant loprateur []. Lexemple suivant illustre la rcupration du 4e caractre ( lindex3 de la chane de caractres).
string chane = Bonjour!; char caractre = c[3]; // Retourne le caractre j

Comparer deux chanes decaractres 167

Comparer deux chanes decaractres


int static Compare(string s1, string s2); int static Compare(string s1, string s2, bool ignorerCasse); int static Compare(string s1, string s2, StringComparison typeComparaison); int static Compare(string s1, string s2, bool ignorerCasse, CultureInfo culture); int static Compare(string s1, int index1, string s2, int index2, int longueur); int static Compare(string s1, int index1, string s2, int index2, int longueur, bool ignorerCasse); int static Compare(string s1, int index1, string s2, int index2, int longueur); int static Compare(string s1, int index1, string s2, int index2, int longueur, String typeComparaison); int static Compare(string s1, int index1, string s2, int index2, int longueur, bool ignorerCasse, CultureInfo culture); // Obtenir la culture spcifie CultureInfo static CultureInfo.GetCultureInfo(string nom);

Les diffrentes surcharges de la mthode statique Compare() permettent de comparer deux chanes de caractres s1 ets2. Les paramtres index1 et index2 permettent de spcifier une position o la comparaison doit commencer dans les chanes s1 et s2, respectivement. Le paramtre longueur permet de spcifier la longueur des deux chanes sur laquelle sapplique la comparaison. Toutes les surcharges de la mthode Compare() retournent:

168

CHAPITRE 6 Les chanes de caractres

0 si les deux chanes de caractres sont identiques; < 0 si la chane s1 est infrieure s2; > 0 si la chane s1 est suprieure s2.
Les relations dordres dpendent des options spcifies dans les paramtres ignorerCasse, typeComparaison et culture: ignorerCasse: indique si la comparaison tient compte de la casse; culture: indique la culture utiliser pour effectuer la comparaison; typeComparaison : indique le type de comparaison ra liser. Les valeurs possibles sontdonnes au Tableau6.1.
Tableau 6.1: Valeurs de lnumration StringComparison
Valeur Description Compare les chanes en utilisant les rgles de tri de la culture courante. Compare les chanes en utilisant les rgles de tri de la culture courante et sans tenir compte de la casse. Compare les chanes en utilisant les rgles de tri de la culture invariante. Compare les chanes en utilisant les rgles de tri de la culture invariante et sans tenir compte de la casse. Compare les chanes en utilisant les rgles de tri ordinal. Compare les chanes en utilisant les rgles de tri ordinal et sans tenir compte de la casse.

CurrentCulture

CurrentCultureIgnoreCase

InvariantCulture

InvariantCultureIgnoreCase

Ordinal

OrdinalIgnoreCase

Comparer deux chanes decaractres 169

Dans le .NET Framework, la classe System.Globalization. CultureInfo dcrit une culture dune langue dun pays. Elle contient des informations sur les rgles de tri des caractres. Une instance de cette classe peut tre obtenue en utilisant la mthode static GetCultureInfo() en spcifiant en paramtre la culture rcuprer. Lexemple suivant illustre la rcupration de diffrentes cultures.
CultureInfo c; // Rcupre la culture franaise de la France c = CultureInfo.GetCultureInfo(fr-FR); // Rcupre la culture anglaise des tats-Unis c = CultureInfo.GetCultureInfo(en-US);

La culture dite invariante est une culture associe la langue anglaise mais elle nest associe aucun pays. En spcifiant une culture la mthode Compare(), vous imposez les rgles de tri associes cette culture. Les rgles de tri respectent le plus souvent lordre lexicographique du dictionnaire de la langue de la culture associe. Si vous utilisez le tri ordinal, Compare() effectue une comparaison binaire, cest--dire en comparant la valeur du code Unicode de chaque caractre. Si les prcdents paramtres ne sont pas spcifis, la mthode Compare() utilise par dfaut les rgles de tri de laculture courante en tenant compte de la casse. Lexemple suivant illustre trois comparaisons de chanes decaractres. La premire utilise des rgles de tri de la culture en cours dexcution en tenant compte de la casse, la deuxime utilise aussi les rgles de tri de la culture en

170

CHAPITRE 6 Les chanes de caractres

cours dexcution mais sans tenir compte de la casse, la deuxime utilise le tri ordinal.
// Affiche une valeur ngative, car coeur < Cur Console.WriteLine(String.Compare(coeur, Cur, StringComparison.CurrentCulture)); // Affiche 0, car coeur = Cur (Ignore la casse) Console.WriteLine(String.Compare(coeur, Cur, StringComparison.CurrentCultureIgnoreCase)); // Affiche une valeur ngative, car le code du caractre // o (0x06F) est infrieur au caractre (0x153) Console.WriteLine(String.Compare(Coeur, Cur, StringComparison.Ordinal));

Remarquez que la mthode Compare() considre le caractre et les caractres o et e comme identique si lon utilise les rgles de tri de la langue courante (la langue courante dans cet exemple est la langue franaise).
Attention Il est possible de comparer des chanes de caractres avec le tri ordinal en utilisant les oprateurs ==,!=, <, <=, >=, > et la mthode String.Equals(). Il est fortement dconseill dutiliser ces oprateurs et cette mthode, car ils ne spcifient pas explicitement le type de comparaison effectu entre deux chanes de caractres, rendant ainsi le code plus difficile comprendre.

Concatner deux chanes decaractres


string static Concat(string s1, string s2); string s = s1 + s2;

Extraire unesous-chane decaractres

171

La concatnation peut tre ralise soit en utilisant la mthode Concat() soit avec loprateur +. La concatnation cre une nouvelle instance de la String. Un grand nombre de concatnations (par exemple dans une boucle) peut pnaliser les performances du processeur et aussi de la mmoire. Il est fortement conseill dutiliser la classe StringBuilder qui a pour vocation la construction de chanes de caractres issues de multiples concatnations. Lexemple suivant illustre la concatnation de trois chanes de caractres en utilisant la mthode Concat() et loprateur+.
string s; s = String.Concat(Bonjour, tout le); s = s + monde !; Console.WriteLine(s); // Affiche Bonjour tout le monde!

Extraire unesous-chane decaractres


string Substring(int dbut); string Substring(int dbut, int longueur);

La mthode Substring() extrait une partie dune instance dune chane de caractres. Le premier paramtre indique la position de dpart de la chane extraire, le deuxime la longueur de la chane rcuprer. Si la longueur nest pas spcifie, la chane est extraite de la position spcifie dans le paramtre dbut jusqu la fin de la chane de caractres.

172

CHAPITRE 6 Les chanes de caractres

Lexemple suivant montre comment extraire le mot tout dans la chane de caractres Bonjour tout le monde!.
string s; s = Bonjour tout le monde !; s = s.Substring(8, 4); // La variable s contient tout

Rechercher unechane decaractres dans uneautre


int IndexOf(string valeur); int IndexOf(string valeur, StringComparison typeComparaison); int IndexOf(string valeur, int dbut); int IndexOf(string valeur, int dbut, StringComparison typeComparaison); int LastIndexOf(string valeur); int LastIndexOf(string valeur, StringComparison typeComparaison); int LastIndexOf(string valeur, int dbut); int LastIndexOf(string valeur, int dbut, StringComparison typeComparaison);

La mthode IndexOf() recherche dans une instance dune chane de caractres la premire position de la chane spcifie dans le paramtre valeur. Si la chane recherche est trouve, la mthode IndexOf() retourne la position (index) de la premire lettre du mot trouv. Si la chane nest pas rencontre, la mthode IndexOf() retourne1.

Rechercher unechane decaractres dans uneautre 173

La mthode LastIndexOf() recherche une chane de caractres en partant de la fin. Le paramtre dbut permet de spcifier lindex de dpart o doit commencer la recherche. Si ce paramtre est non spcifi, la recherche commence au dbut de la chane ( la fin de la chane pour la mthode LastIndexOf()). Le paramtre typeComparaison permet de spcifier le type de comparaison utiliser pour rechercher la chane de caractres (voir Tableau 6.1). Lexemple suivant illustre la recherche de la chane ou dans la chane de caractres Bonjour tout le monde!. La recherche seffectue dans une boucle tant que la mthode String.IndexOf() ne retourne pas1.
int position; string s; s = Bonjour tout le monde !; // Rechercher le premier ou position = s.IndexOf(ou); // Tant que String.IndexOf() na pas retourn -1... while (position != -1) { // Afficher la position trouve Console.WriteLine(position); // Recherche la position suivante de la chane ou position = s.IndexOf(ou, position + 1); }

174

CHAPITRE 6 Les chanes de caractres

Formater unechane decaractres


string static Format(string chaneComposite, params object[] arguments);

La mthode statique Format() permet de mettre en forme laide de la chane de format composite spcifie les objets contenus dans le tableau du paramtre arguments. Une chane de format composite est une chane compose de texte fixe mlange avec plusieurs lments de format. Un lment de format correspond un objet contenu dans le tableau du paramtre arguments. Les lments de format sont de la forme:
{index[,[signe]alignement]:[format]}

index: est un nombre commenant 0 permettant diden

tifier llment formater dans le tableau arguments; alignement: est un entier indiquant la largeur du champ mettre en forme. Si cette valeur est suprieure la longueur de llment de format format, des espaces sont automatiquement ajouts; signe: + pour indiquer que llment de format doit tre align droite, -pour aligner llment de format gauche. Par dfaut, llment de format est align droite si signe nest pas spcifi; format: chane de format spcifique lobjet formater. La composante format dun lment de format dpend du type de donnes formater. Les tableaux suivants indiquent une partie des diffrents formats pris en charge en fonction du type de donnes formater.

Formater unechane decaractres 175

Tableau 6.2: Les diffrentes valeurs de format pour les valeurs numriques
Valeur Description Montaire (par exemple: 9,4 ) Dcimal standard (par exemple: 9,4) Scientifique (par exemple: 9,4e3) Virgule fixe (par exemple: 9,40) Gnral (convertit dans le format le plus compact possible) Numrique standard (par exemple: 9,4) Pourcentage (par exemple: 9,4%) Aller-retour (garantit les conversions de chane vers numrique et inversement) Hexadcimal (par exemple: F4)

C ou c D ou d E ou e F ou f G ou g N ou n P ou p R ou r X ou x

Tableau 6.3: Les diffrentes valeurs de format pour les valeurs date/heure
Valeur Description Modle de date courte (par exemple: 01/04/2010) Modle de date longue (par exemple: Jeudi, 1, avril 2010) Date longue + heure abrge (par exemple: Jeudi, 1, avril2010 18:12) Date longue + heure longue (par exemple: Jeudi, 1, avril2010 18:12:52) Date courte + heure abrge (par exemple: 01/04/2010 18:12) Date courte + heure longue (par exemple: 01/04/2010 18:12:52) Mois + jour (par exemple: 1 avril) Aller-retour (garantit les conversions de chane vers numrique et inversement) Heure abrge (par exemple: 18:12) Heure longue (par exemple: 18:12:52) Date/heure universelle (par exemple: 01/04/2010 18:12:52Z)

d D f F g G M ou m o t T U

176

CHAPITRE 6 Les chanes de caractres

Lexemple suivant, illustre le formatage dune chane de caractres.


string s; decimal prix; int quantit; // Initialiser la chane de format composite s = Total : {0:C} x {1:N} = {2:C}; // Prix unitaire de lobjet achet prix = 10M; // Quantit achete quantit = 1234; // Afficher le total achet s = String.Format(s, prix, quantit, prix * quantit); Console.WriteLine(s);

Le rsultat affich dans la console est le suivant:


Total : 10,00 x 1234,00 = 12340,00

Il est possible dutiliser des formats personnaliss pour les types numriques et les date/heure. Les tableaux suivants indiquent une partie des diffrents spcificateurs de format permettant de crer des formats personnaliss.
Tableau 6.4: Les diffrents spcificateurs de format numriques personnaliss
Valeur Description Espace rserv du zro (un zro est marqu explicitement siaucun chiffre ne se trouve la position du format) Espace rserv de chiffre Virgule dcimale Sparateur des milliers Espace rserv pour le pourcentage

0 # . , %

Formater unechane decaractres 177

Tableau 6.5: Les diffrents spcificateurs de format date et heure personnaliss


Valeur Description Jour de lanne en chiffre (sans zro significatif), (par exemple: 1) Jour de lanne en chiffre (avec zro significatif), (par exemple: 01) Jour de lanne en lettre abrg (par exemple: jeu) Jour de lanne en lettre (par exemple: jeudi) Mois de lanne en chiffre (sans zro significatif), (par exemple: 4) Mois de lanne en chiffre (avec zro significatif), (par exemple: 04) Mois de lanne en lettre abrg (par exemple: avr.) Mois de lanne en lettre (par exemple: avril) Anne sur 2 chiffres (par exemple: 10) Anne sur 4 chiffres (par exemple: 2010) Heure en chiffres (sans zro significatif), (par exemple: 4) Heure en chiffres (avec zro significatif), (par exemple: 04) Minutes en chiffres (sans zro significatif), (par exemple: 8) Minutes en chiffres (avec zro significatif), (par exemple: 08) Secondes en chiffres (sans zro significatif), (par exemple: 6) Secondes en chiffre (avec zro significatif), (par exemple: 06)

d dd ddd dddd M MM MMM MMMM yy yyyy h hh m mm s ss

Lexemple suivant illustre laffichage dun nombre avec un format numrique personnalis.
string s; decimal nombre; nombre = 5116.64; // Afficher le total achet s = String.Format(Nombre : {0:00-00000.####}, nombre); Console.WriteLine(s);

178

CHAPITRE 6 Les chanes de caractres

Le rsultat affich dans la console est le suivant:


Nombre : 00-05116,64
Info Le formatage des chanes des caractres est un mcanisme du .NET Framework trs puissant, permettant de crer des chanes des caractres sans faire de concatnation explicite; le code sen trouve alors plus lisible. Le formatage des chanes de caractres permet aussi de traduire facilement une chane de caractres; en effet, il suffit de changer la chane de format composite (traduite) tout en gardant les mmes lments formater. Astuce Il existe beaucoup de mthodes contenues dans des classes du .NET Framework permettant de formater une chane de caractres sans passer par la mthode Format(). Cest le cas par exemple de la mthode Console.WriteLine() qui prend en paramtre une chane de format composite et les arguments mettre en forme.

Construire unechane avecStringBuilder


// Crer un StringBuilder StringBuilder sb; sb = new StringBuilder(); // Ajouter des valeurs dans le StringBuilder StringBuilder Append(object valeur); StringBuilder AppendFormat(string chaneComposite, params object[] arguments); StringBuilder AppendLine(object o); // Insrer une valeur dans le StringBuilder StringBuilder Insert(int index, object valeur);

Construire unechane avecStringBuilder 179

// Supprimer une partie du StringBuilder StringBuilder Remove(int dbut, int longeur); // Taille de la chane de caractres en cours // de construction int Length { get; } // Construire et rcuprer la chane de caractres // contenue dans le StringBuilder string ToString();

La classe System.Text.StringBuilder permet de construire de faon optimale une chane de caractres.


Info La concatnation de deux chanes de caractres ncessite la reconstruction dune nouvelle chane. Cette opration est trs couteuse si des concatnations sont ralises de manire intensive (par exemple dans une boucle). Utilisez dans ce cas la classe StringBuilder qui permet de raliser trs rapidement un grand nombre de concatnations.

La mthode Append() et AppendLine() ajoute un objet (automatiquement format en une chane de caractres si ncessaire) la fin de la chane. La mthode AppendLine() ajoute en plus un retour la ligne juste aprs. La mthode AppendFormat() permet dajouter une chane de caractres formate comme pour la mthode Format(). StringBuilder permet dinsrer une chane de caractres (ou un objet formater) avec lutilisation de la mthode Insert() en spcifiant la position o doit tre insr lobjet. Il est possible de supprimer une partie de la chane contenue dans un StringBuilder en appelant la mthode Remove(). Il faut dans ce cas spcifier lindex de dbut et la longueur de la chane supprimer.

180

CHAPITRE 6 Les chanes de caractres

Une fois la chane de caractres construite, il faut appeler la mthode ToString() afin de rcuprer une instance String de la chane construite. Lexemple suivant montre comment construire une chane de caractres contenant les chiffres allant de1 10 spars par un tiret. On insert au dbut de la chane la chane NOMBRES: et on supprime le dernier tiret ajout lafin.
StringBuilder sb; sb = new StringBuilder(); // Ajouter les chiffres de 1 10 espacs par des - for (int i = 1; i <= 10; i++) { sb.AppendFormat({0}-, i); } // Insrer au dbut de la chane : NOMBRES : sb.Insert(0, NOMBRES : ); // Supprimer le dernier - la fin de la chane sb.Remove(sb.Length - 1, 1); // Construire et afficher la chane gnre Console.WriteLine(sb.ToString());

Le rsultat affich sur la console est le suivant:


NOMBRES : 1-2-3-4-5-6-7-8-9-10

Encoder et dcoder unechane


// Rcuprer un codage spcifique Encoding static GetEncoding(string nom); // Encoder une chane de caractres byte[] GetBytes(string chane);

Encoder et dcoder unechane

181

// Dcoder une chane de caractres string GetString(byte[] octets);

En .NET, les chanes de caractres en mmoire sont toujours codes en Unicode UTF-16. Lorsque vous chargez un fichier (flux doctets) cod diffremment, vous devez convertir la chane stocke au format Unicode UTF-16. Il en est de mme pour lopration inverse; si vous souhaitez enregistrer un fichier contenant des chanes de caractres dans un format diffrent dUnicode UTF-16, vous devez convertir les chanes de caractres contenues en mmoire vers le format dsir. La classe permettant dencoder ou de dcoder une chane de caractres sappelle System.Text.Encoding. La mthode statique GetEncoding() permet de rcuprer le codage utiliser pour encoder/dcoder une chane de caractres. La classe Encoding contient des proprits constantes stati ques reprsentant les codages les plus utiliss (voir Tableau6.6).
Tableau 6.6: Liste des proprits contenues dans Encoding reprsentant les codages les plus utiliss
Nom de la proprit Description Codage ASCII (7 bits) Codage ANSI du systme dexploitation actuel Codage pour le format UTF-7 Codage pour le format UTF-8 Codage pour le format UTF-16 Codage pour le format UTF-32

ASCII Default UTF7 UTF8 Unicode UTF-32

182

CHAPITRE 6 Les chanes de caractres

Une fois un codage obtenu, il suffit dappeler la mthode GetBytes() pour convertir une chane de caractres .NET avec le codage spcifi. Le rsultat obtenu se trouve dans un tableau doctets. La mthode GetString() ralise lopration inverse en convertissant un tableau doctets vers une chane de caractres .NET avec le codage spcifi. Lexemple suivant illustre lencodage de la chane de caractres ABC au format ANSI et le dcodage des octets avec comme valeur 70, 71,72.
string s; byte[] octets; s = ABC; // Encoder la chane ABC au format ANSI octets = Encoding.Default.GetBytes(s); // Le tableau octets contient les valeurs 65, 66, 67 // Dcoder les octets 70, 71, 72 octets = new byte[] { 70, 71, 72 }; s = Encoding.Default.GetString(octets); Console.WriteLine(s); // Affiche FGH

7
LINQ (Language Integrated Query)
Disponible depuis la version3.5 du .NET Framework, LINQ est un ensemble de mthodes dextension fortement types permettant de raliser des requtes sur des sources de donnes de nature diffrente. Ainsi, LINQ permet de simplifier lcriture et la comprhension des algorithmes de recherche tout en typant fortement votre code. Les mthodes dextensions proposes par LINQ utilisent considrablement les gnriques et les expressions lambda (voir au Chapitre2). Afin de simplifier encore plus lutilisation de ces fonctionnalits, Microsoft a ajout dans la version3.5 de C# des mots-cls supplmentaires, qui seront convertis en appel de mthodes LINQ au moment de la compilation. LINQ permet dinterroger une source de donnes en fonction dun fournisseur LINQ. Le .NET Framework contient nativement un fournisseur appel LINQ To Object, permettant dinterroger des objets implmentant linterface IEnumerable<T>.

184

CHAPITRE 7 LINQ (Language Integrated Query)

Microsoft propose dautres fournisseurs LINQ intgrs la version3.5 du .NET Framework qui sont : LINQ to Entity et Linq to XML. Ils permettent dinterroger respectivement des sources de donnes SQL (SQL Server, Oracle, etc.) et des documents XML. Ce chapitre est entirement consacr LINQ to Object.

Slectionner des objets (projection)


from <variable de porte> in <seq.IEnumerable<T>> select <projection sur la variable de porte>;

La clause from de C# permet de dfinir la source de donnes interroge par la requte. Une variable (appele variable de porte ) doit tre spcifie avant le mot-cl in. Elle sert de rfrence pour chaque lment contenu dans la squence IEnumerable<T> afin dtre utilise dans les autres clauses dune requte LINQ. Durant lexcution, cette variable peut tre vue comme une variable ditration dune boucle foreach. Elle est automatiquement affecte pour chaque lment parcouru de la squence IEnume rable<T> associe. Pour chaque lment contenu dans la variable de porte, la clause select permet de dfinir les lments qui devront tre rcuprs (cette opration est plus communment appele une projection). Lexemple suivant rcupre les lments contenus dans un tableau dentiers de type int.
int[] tableauEntiers = ...; IEnumerable<int> q = from e in tableauEntiers select e;

Le rsultat dune requte LINQ est toujours de type IEnume rable<Projection>, Projection tant le type des donnes

Slectionner des objets (projection) 185

retournes par la clause select. Dans lexemple prcdent, les lments retourns sont de type IEnumerable<int> car la variable de porte e est de type int et la clause select retourne des lmentse. Il est possible de rcuprer uniquement la valeur dune proprit dune variable de porte; lexemple suivant illus tre la rcupration de la longueur des chanes contenues dans un tableau.
string[] tableauChaines = ...; IEnumerable<int> q = from e in tableauChanes select e.Length;

La clause select accepte aussi lappel de mthode. Lexem ple suivant illustre la conversion dentiers contenus dans un tableau en chanes de caractres.
int[] tableauEntiers = ...; IEnumerable<string> q = from e in tableauEntiers select Convert.ToString(e);

Pour rcuprer plusieurs valeurs, il est possible dinstancier une classe existante o dutiliser des types anonymes. Dans le dernier cas, il faudra utiliser le mot-cl var pour rcuprer le rsultat de la requte. Lexemple suivant illustre la rcupration des chanes de caractres et des longueurs associes contenues dans un tableau en crant un type anonyme.
string[] tableauChaines = ...; var q = from e in tableauChanes select new { Longueur = e.Length, Chaine = e};

Dans cet exemple,q est un IEnumerable<T> et T un type anonyme.

186

CHAPITRE 7 LINQ (Language Integrated Query)

Lors de la dfinition dune requte, cette dernire nest pas excute immdiatement. Elle le sera rellement au moment de son parcours via une boucle foreach. Lexemple suivant illustre une requte LINQ sur un tableau afin de rcuprer la longueur et la chane de caractres associe. Ces informations sont rcupres dans une classe anonyme.
string[] tab; tab = new string[] { AAA , B, CC }; var q = from e in tab select new { Longueur = e.Length, Chaine = e.Trim() }; // Parcourir le rsultat de la requte foreach(var info in q) { Console.WriteLine(info.Chaine + - + info.Taille); }

Lutilisation du mot-cl var pour la variable ditration de la boucle foreach est obligatoire, car la variable q est de type IEnumerable<T>, avecT un type anonyme. Le rsultat produit sur la console est le suivant:
AAA-4 B-3 CC-4

Filtrer des objets


from <variable de porte> in <seq.IEnumerable<T>> where <condition> select <projection sur la variable de porte>;

La clause where permet de filtrer des objets contenus dans lobjet IEnumerable<T> en fonction dune condition. Cette

Filtrer des objets 187

dernire peut utiliser la variable de porte dfinie dans la clause from. Une condition doit tre une expression qui renvoie un boolen (exactement comme la pour clause conditionnelleif). Lexemple suivant illustre un filtre dans une requte LINQ permettant de rcuprer les longueurs des prnoms ayant une longueur suprieure ou gale 6caractres.
string[] tab; IEnumerable<int> q; tab = new string[] { Gilles, David, Aurlie }; q = from e in tab where e.Length >= 6 select e.Length; // Parcourir le rsultat de la requte foreach(int longueur in q) { Console.WriteLine(longueur); }

Voici maintenant le mme exemple quivalent avec lutilisation des mthodes dextensions LINQ.
string[] tab; IEnumerable<int> q; tab = new string[] { Gilles, David, Aurlie }; q = tab.Where(e => e.Length >= 6).Select(e => e.Length); // Parcourir le rsultat de la requte foreach(int longueur in q) { Console.WriteLine(longueur); }

Le rsultat produit sur la console est le suivant:


6 <-- Correspond Gilles 7 <-- Correspond Aurlie

188

CHAPITRE 7 LINQ (Language Integrated Query)

Trier des objets


from <variable de porte> in <seq.IEnumerable<T>> orderby <critre de tri> [ascending | descending] [,<autres critres] select <projection sur la variable de porte>;

La clause orderby permet de trier le rsultat dune requte. Les critres de tri doivent utiliser les variables de portes dclares et tre spars par des virgules. Lordre du tri doit tre spcifi pour chaque critre de tri. Pour cela, on utilise les mots-cls ascending ou descending pour trier respectivement par ordre croissant ou dcroissant. Si aucun ordre de tri nest spcifi, le tri par ordre croissant est utilis par dfaut. Lexemple suivant illustre une requte LINQ permettant de rcuprer des prnoms tris par ordre croissant sur la longueur associe et tri ensuite par ordre dcroissant sur le prnom lui-mme.
string[] prnoms; IEnumerable<string> q; prnoms = new string[] { Gilles, Aurlie, Laurent, David }; q = from prnom in prnoms orderby prnom.Length, prnom descending select prnom; foreach (string prnom in q) { Console.WriteLine(prnom); }

Le rsultat obtenu sur la console est le suivant:


David Gilles Laurent Aurlie

Effectuer unejointure 189

Effectuer unejointure
// Jointure sur une condition dgalit from <variable gauche> in <seq.IEnumerable<T1> gauche> join <variable droite> in <seq.IEnumerable<T2> droite> on <cl gauche> equals <cl droite> select <projection sur les variables de porte>; // Jointure sur une condition avec nimporte // quel oprateur from <variable gauche> in <seq.IEnumerable<T1> gauche> from <variable droite> in <seq.IEnumerable<T2> droite> where <cl gauche> <oprateur> <cl droite> select <projection sur les variables de porte>;

La clause join permet de mettre en corrlation deux squences dobjets IEnumerable<T>. Deux variables de porte doivent donc tre dclares afin dtre utilises dans les autres clauses de la requte. La corrlation entre ces deux squences seffectue avec loprateur dgalit en utilisant le mot-cl equals. Les deux oprandes de chaque ct de equals doivent tre une proprit dune variable de porte qui reprsente la cl permettant la corrlation entre les deux squences. Lexemple suivant illustre une jointure entre un tableau contenant des prnoms et un autre tableau contenant des longueurs. La condition de jointure se fait entre lgalit des longueurs des prnoms et les longueurs prsentes dans le second tableau. La requte rcupre tous les couples prnom/longueur qui satisfont la condition de jointure.

190

CHAPITRE 7 LINQ (Language Integrated Query)

string[] prnoms; int[] longueurs; prnoms = new string[] { Gilles, David, Aurlie, Laurent }; longueurs = new int[] { 10, 6, 20, 7 }; var q = from prnom in prnoms join longueur in longueurs on prnom.Length equals longueur select new { Prnom = prnom, Longueur = longueur }; foreach (var c in q) { Console.WriteLine({0} - {1}, c.Prnom, c.Longueur); }

Le rsultat obtenu sur la console est le suivant:


Gilles 6 Aurlie 7 Laurent - 7

Si la condition de jointure doit tre un oprateur diffrent de lgalit (par exemple loprateur suprieur>) ou alors une condition beaucoup plus complexe (avec par exemple des ET logiques), il nest pas possible dutiliser la clause join. Dans ce cas, il faudra utiliser deux clauses from pour rcuprer les deux squences et ajouter une clause where qui dfinit la condition de corrlation entre ces deux squences. Lexemple suivant illustre une jointure entre un tableau contenant des prnoms et un autre tableau contenant des longueurs. La jointure rcupre tous les couples de prnom/longueur dont la longueur des prnoms est suprieure aux longueurs prsentes dans le second tableau dentiers.

Rcuprer lepremier ouledernier objet

191

string[] prnoms; int[] longueurs; prnoms = new string[] { Gilles, David, Aurlie, Laurent }; longueurs = new int[] { 10, 6, 20, 7 }; var q = from prnom in prnoms join longueur in longueurs where prnom.Length >= longueur select new { Prnom = prnom, Longueur = longueur }; foreach (var c in q) { Console.WriteLine({0} - {1}, c.Prnom, c.Longueur); }

Le rsultat obtenu sur la console est le suivant:


Gilles 6 Aurlie 6 Aurlie 7 Laurent 6 Laurent - 7

Rcuprer lepremier ouledernier objet


// Rcuprer le premier objet (from <variable de porte> in <seq.IEnumerable<T>> select <projection sur variable de porte>).First(); // Rcuprer le dernier objet (from <variable de porte> in <seq.IEnumerable<T>> select <projection sur variable de porte>).Last(); // Rcuprer le premier objet ou sa valeur par dfaut // si inexistant

192

CHAPITRE 7 LINQ (Language Integrated Query)

(from <variable de porte> in <seq.IEnumerable<T>> select <projection sur variable de porte>) .FirstOrDefault(); // Rcuprer le premier objet ou sa valeur par dfaut // si inexistant (from <variable de porte> in <seq.IEnumerable<T>> select <projection sur variable de porte>) .LastOrDefault();

Les mthodes dextension First() et Last() permettent de rcuprer respectivement le premier et le dernier lment rsultant dune requte LINQ. Ces mthodes dclenchent une exception de type InvalidOperationException sil nexiste aucun lment dans le rsultat de la requte. Ces mthodes retournent un objet du type des lments spcifis dans le rsultat de la projection de select. Les mthodes dextension FirstOrDefault() et LastOrDe fault() produisent le mme rsultat que First() et Last() mais ne dclenchent pas dexception sil nexiste aucun lment dans le rsultat de la requte. La valeur par dfaut de lobjet est retourne dans ce cas (null si la clause select retourne un type rfrence, la valeur par dfaut dans le cas dun type valeur). Lexemple suivant illustre la rcupration du premier et du dernier prnom commenant par Gi qui se trouvent dans un tableau de chanes de caractres.
string[] prnoms; string q; prnoms = new string[] { Gilles, Aurlie, Gilbert, Laurent }; // Rcuprer le premier prnom q = (from prnom in prnoms where prnom.StartsWith(Gi) select prnom).First();

Compter lenombre dobjets 193

// Afficher le premier prnom Console.WriteLine(q); // Rcuprer le dernier prnom q = (from prnom in prnoms where prnom.StartsWith(Gi) select prnom).Last(); // Afficher le dernier prnom Console.WriteLine(q);

Compter lenombre dobjets


(from <variable de porte> in <seq.IEnumerable<T>> select <projection sur la variable de porte>) .Count();

La mthode dextension Count() permet de compter le nombre dobjets rsultant dune requte LINQ. Cette mthode retourne un entier de type int. Lexemple suivant affiche le nombre de prnoms contenant la lettrel prsents dans le tableau tab.
string[] tab; int q; tab = new string[] { Gilles, David, Aurlie }; q = (from e in tab where e.Contains(l) == true select e).Count(); // Afficher le nombre de prnoms obtenu Console.WriteLine(q);

Le rsultat produit sur la console est le suivant:


2 <-- Correspond Gilles et Aurlie

194

CHAPITRE 7 LINQ (Language Integrated Query)

Effectuer unesomme
(from <variable de porte> in <seq.IEnumerable<T>> select <projection do rsulte un nombre>).Sum();

La mthode dextension Sum() permet deffectuer une somme sur une requte produisant des nombres en sortie. Ces nombres peuvent tre de type int, float, double ou decimal. Il est possible deffectuer la projection des nombres sommer en paramtre de la mthode Sum() laide dune expression lambda (voir la section correspondante au Chapitre2). Lexemple suivant ralise la somme des longueurs des chanes de caractres contenues dans un tableau.
string[] tab; int somme; tab = new string[] { Gilles, David, Aurlie }; somme = (from e in tab select e.Length).Sum(); // Afficher le nombre de prnoms obtenu Console.WriteLine(somme);

Le rsultat produit sur la console est le suivant:


18 <-- 6 + 5 + 7

Grouper des objets


// Groupement dobjets from <variable de porte> in <seq.IEnumerable<T1>> group <variable de porte> by <critre de groupement>;

Grouper des objets 195

// Groupement dobjets suivi dune projection from <variable de porte> in <seq.IEnumerable<T1>> group <variable de porte> by <critres de groupement> into <variable de groupe> select <projection sur la variable de groupe> // Dfinition de linterface IGroupingKey<TCl, T> public interface IGroupingKey<TCl, T>: IEnumerable<T> { TCl Key { get; } }

La clause group by permet de raliser des groupes dobjets suivant un ou plusieurs critres de regroupement. Lensemble de ces critres forme une cl dun groupe. Les requtes se terminant par la clause group by retournent une squence IEnumerable<IGroupingKey<TCl, T>>. Chaque instance contenue dans cette squence correspond un groupe modlis par linterface IGroupingKey<TCl, T>. Cette interface contient une proprit Key permettant de rcuprer la cl du groupe (qui a t dfinie dans la clause group by). IGroupingKey<TCl, T> implmente linterface IEnumerable<T> permettant de parcourir les objets appartenant au mme groupe (ayant la mme cl). Le type gnrique TCl de IGroupingKey<TCl, T> correspond au type des critres de groupement spcifis dans la clause group by. Le type gnrique T, quant lui, correspond aux variables de porte spcifies entre les clauses group et by. Lexemple suivant effectue des groupes sur la premire lettre des prnoms contenus dans un tableau.

196

CHAPITRE 7 LINQ (Language Integrated Query)

string[] prnoms; IEnumerable<IGrouping<char, string>> q; prnoms = new string[] { Gilles, Aurlie, Laurent, Anne, Gilbert, Anne-Laure }; q = from prnom in prnoms group prnom by prnom[0]; // Parcourir chaque groupe foreach (IGrouping<char, string> groupe in q) { Console.WriteLine(Groupe : {0}, groupe.Key); // Parcourir les lments de chaque groupe foreach (string prnom in groupe) { Console.WriteLine(\t {0}, prnom); } Console.WriteLine(); }

Voici le rsultat produit sur la console:


Groupe : G Gilles Gilbert Groupe : A Aurlie Anne Anne-Laure Groupe : L Laurent

Il nest pas ncessaire dutiliser la clause select avec le group by; cependant, si lon souhaite modifier la requte afin de rcuprer dautres informations que les groupes gnrs par group by, on peut alors ajouter la fin de la

Grouper des objets 197

requte une clause select. Dans ce cas, le groupement doit seffectuer dans une variable de groupe (variable locale la requte) laide du mot-cl into qui se situe aprs la clause group by. La projection ralise sur le select ne peut plus se faire partir des variables de porte dclares dans les clauses from, mais uniquement partir de la variable de groupe. Cette dernire correspond une instance dun objet implmentant linterface IGroupingKey<TCl, T> reprsentant le regroupement effectu. IGroupingKey<TCl, T> implmentant linterface IEnumerable<T>, la clause select peut se servir de cette variable afin dutiliser des mthodes dagrgations telles que Sum() ou Count(). Lexemple suivant effectue des groupes sur la premire lettre des prnoms contenus dans un tableau. La premire lettre du groupe ainsi que le nombre de prnoms de chaque groupe sont rcuprs dans un type anonyme.
string[] prnoms; prnoms = new string[] { Gilles, Aurlie, Laurent, Anne, Gilbert, Anne-Laure }; var q = from prnom in prnoms group prnom by prnom[0] into groupe select new { Lettre = groupe.Key, Total = groupe.Count() }; // Parcourir chaque groupe foreach (var groupe in q) { Console.WriteLine({0} (Nb: {1}), groupe.Lettre, groupe.Total); }

Voici le rsultat produit sur la console:


G (Nb : 2) A (Nb : 3) L (Nb : 1)

198

CHAPITRE 7 LINQ (Language Integrated Query)

Dterminer si unesquence contient au moins unobjet


(from <variable de porte> in <sq. IEnumerable<T>> select <projection>).Any();

La mthode dextension Any() retourne true si la squence associe contient au moins un lment. Il est tout fait possible dutiliser cette mthode dans les conditions afin de dterminer lexistence dun lment dans une autre squence. Lexemple suivant illustre lutilisation de la mthode dextension Any() afin de dterminer sil existe au moins un prnom dans le tableau commenant par la lettreG.
string[] tab; bool rsultat; tab = new string[] { Gilles, David, Aurlie }; rsultat = (from e in tab where e.StartsWith(G) == true select e).Any(); // Afficher le rsultat obtenu Console.WriteLine(rsultat);

Dclarer unevariabledeporte
(from <variable de porte> in <seq.IEnumerable<T>> let <nouvelle variable> = <valeur> select <projection sur une variable de porte>);

Le mot-cl let permet de dclarer une variable de porte associe une expression. Comme pour les variables de

Dclarer unevariabledeporte 199

porte dclares dans la clause from, les variables de portes dclares avec let peuvent tre utilises dans toutes les autres clauses de la requte. Les variables de portes dclares avec let doivent tre associes une expression qui ne pourra pas tre modifie par la suite. Ces variables peuvent tre vues comme la dcla ra tion dun alias associe une expression permettant la simplification du code. lexcution, toutes les rfrences des variables de portes seront remplaces par lexpression associe. Lexemple suivant illustre la rcupration des prnoms contenus dans un tableau commenant par la lettreG et ayant une longueur dau moins 4caractres. Une variable de porte longueur est utilise afin de stocker la longueur des prnoms.
string[] tab; tab = new string[] { Gilles, Claude, Gilbert, Gil }; var rsultats = (from e in tab let longueur = e.Length where e.StartsWith(G) == true && longueur >= 4 select new { Nom=e, Longueur=longueur }); foreach (var rsultat in rsultats) { Console.WriteLine({0} ({1}), rsultat.Nom, rsultat.Longueur); }

Voici le rsultat produit sur la console:


Gilles (6) Gilbert (7)

8
Les classes et interfaces de base
Le .NET Framework fournit une bibliothque contenant normment de classes; ce chapitre dtaille donc les classes les plus importantes et les plus utilises.

Laclasse Object
// Dterminer si deux objets sont identiques public virtual bool Equals(object obj); public static bool Equals(object objA, object objB); // Dterminer si les rfrences font rfrence // au mme objet public static bool ReferenceEquals(object objA, object objB); // Retourner une chane de caractres reprsentant // lobjet public virtual string ToString();

202

CHAPITRE 8 Les classes et interfaces de base

La classe Object est la classe de base de toutes les classes du .NET Framework. Elle reprsente la racine de la hirarchie de classes. Si vous crez une classe qui nhrite daucune classe, le compilateur la fera automatiquement hriter de laclasse Object. Le mot-cl object est un raccourci pour la classe System.Object. La classe Object contient des services de base qui peuvent tre utiliss sur nimporte quel type dobjet. La mthode Equals() permet de comparer une instance un autre objet. Cette mthode est marque comme virtual, car vous pouvez la redfinir pour changer son comportement. La mthode static Equals() permet de comparer deux objets, en tenant compte si lune des deux rfrences passes en paramtre est nulle. Si ce nest pas le cas, elle appelle la mthode non statique Equals() sur le premier objet avec comme paramtre le deuxime objet. Par dfaut, avec les types rfrence, la mthode Equals() compare deux rfrences et vrifie si elles font rfrence au mme objet. Dans le cas des types valeur, la mthode Equals() compare tous les champs des deux objets et appelle la mthode Equals() sur chacun des champs. La classe Object contient une mthode static Reference Equals() qui permet de tester si deux rfrences font rfrence un mme objet. La mthode ToString() retourne une reprsentation textuelle dun objet. Par dfaut, elle retourne le nom du type (avec son espace de noms). tant donn quelle est marque comme virtual, il est possible de la redfinir afin de retourner une reprsentation textuelle beaucoup plus vocatrice.

Laclasse Object 203

Astuce La mthode ToString() est trs utile, car elle permet dobtenir trs rapidement, sous forme de chane, une reprsentation textuelle de nimporte quel objet. Nhsitez pas redfinir cette mthode afin de retourner une chane de caractres permettant didentifier un objet (par exemple le numro de scurit social avec le nom et prnom dune Personne).

Lexemple suivant illustre une classe Chien redfinissant certaines mthodes de la classe Object.
public class Chien { private string tatouage; private string nom; public Chien(string tatouage, string nom) { this.tatouage = tatouage; this.nom = nom; } public override bool Equals(object obj) { // Si obj est null retourner false car on compare un // Chien avec une rfrence null if (obj == null) { return false; } Chien c; c = obj as Chien; // Si obj nest pas un Chien, retourner false, car // on compare un Chien avec un autre type dobjet if (c == null)

204

CHAPITRE 8 Les classes et interfaces de base

{ return false; } // Comparer les numros de tatouage return (String.Compare(this.tatouage, c.tatouage) == 0); } }

Voici un exemple qui illustre lutilisation de ces diffrentes mthodes sur des instances de la classe Chien.
Chien cachou, clone, rfrence, iris; bool b; cachou = new Chien(AAZZ33, Cachou); rfrence = cachou; clone = new Chien(AAZZ33, Le clone de Cachou); iris = new Chien(BBCC51, Iris); b b b b b = = = = = cachou.Equals(iris); cachou.Equals(clone); cachou.Equals(33); Object.Equals(cachou, clone); Object.Equals(cachou, null); // // // // // Retourne Retourne Retourne Retourne Retourne false true false true false

b = Object.ReferenceEquals(cachou, clone); // Retourne false b = Object.ReferenceEquals(cachou, rfrence); // Retourne true Console.WriteLine(iris.ToString()); // Affiche Iris

Laclasse Array 205

Laclasse Array
// Nombre total dlments dans un tableau public int Length { get; } // Nombre de dimensions du tableau public int Rank { get; } // Affecter une plage dlments la valeur // par dfaut public static void Clear(Array tab, int dbut, int longueur); // Copier les lments dun tab. dans un autre tableau public static void Copy(Array src, Array dest, int longueur); // Effectuer une action sur chaque lment public static void ForEach<T>(T[] tab, Action<T> action); // Dterminer sil existe un lment correspondant // au prdicat public static bool Exists<T>(T[] tab, Predicate<T> prdicat); // Rechercher un lment correspondant au prdicat public static T Find<T>(T[] tab, Predicate<T> prdicat); // Rechercher tous les lments correspondant // au prdicat public static T[] FindAll<T>(T[] tab, Predicate<T> prdicat); // Rechercher le dernier lment correspondant // au prdicat public static T FindLast<T>(T[] tab, Predicate<T> prdicat); // Rechercher la position dun lment correspondant // au prdicat

206

CHAPITRE 8 Les classes et interfaces de base

public static int FindIndex<T>(T[] tab, Predicate<T> prdicat); // Rechercher la dernire position dun lment // correspondant au prdicat public static int FindLastIndex<T>(T[] tab, Predicate<T> prdicat); // Trier les lments du tableau public static void Sort<T>(T[] tab); public static void Sort<T>(T[] tab, IComparer<T> comparateur); public static void Sort<T>(T[] tab, Comparison<T> comparaison);

La classe System.Array est la classe de base de tous les tableaux. Une fois un tableau dclar, il est possible de rcuprer le nombre total dlments laide de la proprit Length. La classe Array contient des mthodes static permettant deffectuer des copies, des effacements, des recherches et des tris sur les tableaux. Les mthodes de recherche demandent en paramtre un prdicat (un dlgu) qui sera automatiquement appel sur chaque lment afin de vrifier si ce dernier correspond au critre de recherche dfini par le dveloppeur. Il existe des surcharges permettant de spcifier si ncessaire les intervalles o seffectue la recherche. Pour trier des lments dun tableau, il faut que par dfaut, ces lments implmentent linterface IComparable. Les mthodes Sort() se chargent dappeler la mthode Compa reTo() sur chacun de ces lments suivant lalgorithme du tri rapide (quick sort). Si les lments nimplmentent pas linterface IComparable, il est possible dutiliser un comparateur implmentant linterface IComparer.

Laclasse Array

207

Comme pour la recherche, une surcharge de la mthode Sort() permet dutiliser un prdicat (dlgu) prenant en paramtre deux objets et devant retourner un entier pour indiquer lordre de ces deux objets. Les valeurs que doit retourner ce prdicat sont: <0 si le premier objet est infrieur au deuxime; 0 si le premier objet est gal au deuxime; >0 si le premier objet est suprieur au deuxime. Lexemple suivant dfinit une classe Chien contenant son nom et son numro de tatouage ainsi quune mthode pour le faire aboyer. Cette classe implmente linterface IComparable<T> permettant de comparer les chiens suivant leur numro de tatouage.
public class Chien : IComparable<Chien> { private string tatouage; private string nom; public Chien(string tatouage, string nom) { this.tatouage = tatouage; this.nom = nom; } public string Tatouage { get { return this.tatouage; } } public string Nom { get { return this.nom; } }

208

CHAPITRE 8 Les classes et interfaces de base

public void Aboyer() { Console.WriteLine(Waf ! Waf !); } public int CompareTo(Chien other) { return this.tatouage.CompareTo(this.tatouage); } }

Lexemple suivant utilise la classe dclare prcdemment, afin de crer et initialiser un tableau de chiens. Une recher che est effectue sur le chien ayant comme nom Cachou. On effectue ensuite deux tris, lun en utilisant linterface IComparable (implmente prcdemment dans la classe Chien), lautre laide dun prdicat (dlgu anonyme). Etfinalement, on fait aboyer tous les chiens avec la mthode Array.ForEach() laide dune expression lambda (voir la section correspondante au Chapitre2).
Chien[] tab; Chien toutou; tab = new Chien[] { new Chien(AAZZ33, new Chien(BBCC51, new Chien(ABCD16, new Chien(RSTU64, };

Cachou), Iris), Opale), Upsa)

// Rechercher Cachou toutou = Array.Find(tab, delegate(Chien c) { if (c.Nom == Cachou)

Laclasse Enum 209

{ return true; } return false; }); // Trier le tableau laide de IComparable<T> Array.Sort(tab); // Trier le tableau suivant le nom laide // dun prdicat Array.Sort(tab, delegate(Chien c1, Chien c2) { return c1.Nom.CompareTo(c2.Nom); }); // Faire aboyer tous les chiens (avec une // expression lambda) Array.ForEach(tab, (c) => c.Aboyer());

Laclasse Enum
// Rcuprer les noms des constantes dune numration public static string[] GetNames(Type type); // Rcuprer le nom de la constante dune numration // qui a la valeur spcifie public static string GetName(Type type, object valeur); // Indiquer si la valeur dune numration existe public static bool IsDefined(Type type, Object valeur); // Convertir lentier spcifi en un membre // dune numration public static object ToObject(Type type, Object valeur);

210

CHAPITRE 8 Les classes et interfaces de base

// Convertir la reprsentation sous forme de chane du // nom de la constante en un membre dune numration public static object Parse(Type type, string valeur, bool ignorerCasse);

La classe Enum permet de rcuprer des informations sur les classes de type numration (dclares laide du mot-cl enum). La mthode GetNames() permet de rcuprer les noms des diffrents membres contenus dans une numration. La mthode GetName() rcupre quant elle le nom dun membre ayant une valeur spcifie en paramtre. La mthode IsDefined() permet de savoir si la valeur spcifie en paramtre est contenue dans un des membres dune numration. La mthode ToObject() permet de convertir une valeur entire de type int en une valeur membre dune numration. La mthode Parse() retourne la valeur membre dune numration dont le nom est reprsent sous forme de chane de caractres. Lexemple suivant illustre la dclaration dune numration Sexe contenant deux membres, Homme et Femme.
enum Sexe { Homme = 1, Femme = 2 }

Laclasse Enum

211

Le code suivant illustre lutilisation des mthodes de la classe Enum sur lnumration Sexe dclare prcdemment.
Sexe s; string[] noms; string nom; // Affichage des diffrents noms des membres // de lnumration noms = Enum.GetNames(typeof(Sexe)); for (int i = 0; i < noms.Length; i++) { Console.WriteLine(noms[i]); } // Affichage du nom du membre ayant comme valeur 2 nom = Enum.GetName(typeof(Sexe), 2); Console.WriteLine(2 = + nom); // Test si la valeur 3 est dfini dans lnumration // sexe if (Enum.IsDefined(typeof(Sexe), 3) == false) { Console.WriteLine(La valeur 3 nexiste pas !); } // Rcupration du membre de lnumration ayant // la valeur 3 s = (Sexe)Enum.ToObject(typeof(Sexe), 1); Console.WriteLine(1 = + s); // Rcupration du membre de lnumration ayant comme // nom feMMe (sans tenir compte de la casse) s = (Sexe)Enum.Parse(typeof(Sexe), feMMe, true); Console.WriteLine(feMMe = + s);

212

CHAPITRE 8 Les classes et interfaces de base

Laclasse TimeSpan
// Crer une nouvelle instance de TimeSpan public TimeSpan(int heures, int minutes, int secondes); public TimeSpan(int jours, int heures, int minutes, int secondes); public static valeur); public static public static public static public static TimeSpan FromMilliseconds(double TimeSpan TimeSpan TimeSpan TimeSpan FromSeconds(double valeur); FromMinutes(double valeur); FromHours(double valeur); FromDays(double valeur);

// Crer un TimeSpan partir dune chane // de caractres public static TimeSpan Parse(string s); public static bool TryParse(string s, out TimeSpan rsultat); // Composantes dun TimeSpan public int Days { get; } public int Hours { get; } public int Minutes { get; } public int Seconds { get; } public int Milliseconds { get; }

// // // // //

Jours Heures Minutes Secondes Millisecondes

// Nombre total de... public double TotalDays { get; } // ...jours public double TotalHours { get; } // ...heures public double TotalMinutes { get; } // ...minutes public double TotalSeconds { get; } // ...secondes public double TotalMilliseconds { get; } //...ms // Convertir un TimeSpan en une chane de caractres public string ToString();

Laclasse TimeSpan

213

La structure System.TimeSpan permet de reprsenter une dure avec une prcision dune milliseconde. Cette structure est immuable, cest--dire quune fois instancie, ses valeurs ne peuvent plus changer. Il faudra r-instancier de nouveau un TimeSpan pour reprsenter une dure diffrente. La cration dune instance de TimeSpan peut se faire en appelant une des surcharges du constructeur en spcifiant des valeurs aux diffrentes composantes. Il est possible de crer une instance de TimeSpan depuis une certaine quantit dune composante dune dure (par exemple depuis un nombre de minutes). Les mthodes Parse() et TryParse() permettent danalyser et de convertir une chane de caractres en une instance TimeSpan. La mthode Parse() dclenchera une exception si la chane de caractres analyse est incorrecte, alors que la mthode TryParse() retournera false et affectera la dure spcifie en paramtre la valeur de la constante TimeSpan.Zero (00:00:00). La classe TimeSpan contient des mthodes et des oprateurs permettant de raliser des calculs sur des dures. chaque calcul, une nouvelle instance de TimeSpan est cre et retourne. Lexemple suivant illustre lutilisation de la classe TimeSpan.
TimeSpan dure1; TimeSpan dure2; // Crations et affichages dune dure dure1 = TimeSpan.FromSeconds(3600); Console.WriteLine(dure1); // Affiche 01:00:00 dure1 = TimeSpan.FromMinutes(1.5); // 1 min 30s Console.WriteLine(dure1.TotalSeconds); // Affiche 90 dure1 = new TimeSpan(10, 5, 47, 4); Console.WriteLine(dure1); // Affiche 10.05:47:04

214

CHAPITRE 8 Les classes et interfaces de base

dure1 = TimeSpan.FromHours(1); // dure2 = TimeSpan.FromMinutes(30);// dure1 = dure1 + dure2; // Console.WriteLine(dure1); //

1h 30 min 1h + 30 min = 1h30 Affiche 00:01:30

if (TimeSpan.TryParse(04:10:30, out dure1) == true) { Console.WriteLine(dure1.Hours); // Affiche 4 Console.WriteLine(dure1.Minutes); // Affiche 10 Console.WriteLine(dure1.Seconds); // Affiche 30 } else { Console.WriteLine(Mauvais format de la dure); }

Laclasse DateTime
// Crer une nouvelle instance public DateTime(int anne, int public DateTime(int anne, int int heure, int minute, int public DateTime(int anne, int int heure, int minute, int int milliseconde); de DateTime mois, int jours); mois, int jours, seconde); mois, int jours, seconde,

// Crer un DateTime depuis une chane // de caractres public static DateTime Parse(string s); public static bool TryParse(string s, out DateTime rsultat); // Obtenir la public static // Obtenir la public static date daujourdhui DateTime Today { get; } date et lheure daujourdhui DateTime Now { get; }

Laclasse DateTime

215

// Composantes dun DateTime public int Year { get; } public int Month { get; } public int Day { get; } public int Hour { get; } public int Minute { get; } public int Second { get; } public int Millisecond { get; }

// // // // // // //

Anne Mois Jour Heure Minute Seconde Milliseconde

// Obtenir la partie date du DateTime public DateTime Date { get; } // Obtenir la partie heure du DateTime public TimeSpan TimeOfDay { get; } // Calculs sur une date public DateTime AddSeconds(int valeur); public DateTime AddMinutes(int valeur); public DateTime AddHours(int valeur); public DateTime AddDays(int valeur); public DateTime AddMonths(int valeur); public DateTime AddYears(int valeur); public DateTime Add(TimeSpan dure); // Convertir un TimeSpan en une chane de caractres public string ToString(); public string ToString(string format);

La structure System.DateTime permet de reprsenter un instant dans le temps compos dune date et dune heure avec une prcision dune milliseconde. Cette structure est immuable, cest--dire quune fois instancie, ses valeurs ne peuvent plus changer. Il faudra r-instancier de nouveau un DateTime pour reprsenter une date diffrente. La cration dune instance de DateTime peut se faire en appelant une des surcharges du constructeur en donnant des valeurs aux diffrentes composantes. Les proprits Now

216

CHAPITRE 8 Les classes et interfaces de base

et Today permettent de rcuprer respectivement la date+heure et la date uniquement de lordinateur o sexcute le code. Les mthodes Parse() et TryParse() permettent danalyser et convertir une chane de caractres en une instance DateTime. La mthode Parse() dclenchera une exception si la chane de caractres analyse est incorrecte, alors que la mthode TryParse() retournera false et affectera la date spcifie en paramtre la valeur de la constante DateTime.MinValue (01/01/0001 00:00:00). La mthode ToString() permet de retourner linstance DateTime en une chane de caractres. Il est possible de spcifier un format particulier. La classe DateTime contient des mthodes et des oprateurs permettant de raliser des calculs sur des dates en ajoutant des quantits sur une composante ou en ajoutant une dure reprsente par une instance TimeSpan. chaque calcul, une nouvelle instance de TimeSpan est cre et retourne. Lexemple suivant illustre lutilisation de la classe DateTime:
DateTime d; TimeSpan dure; d = DateTime.Now; Console.WriteLine(d.ToString()); // Affiche 14/04/2010 21:23:47 d = new DateTime(2010, 8, 16); Console.WriteLine(d); // Affiche 16/08/2010 00:00:00 date = date.AddYears(2); Console.WriteLine(d); // Affiche 16/08/2012 00:00:00 dure = TimeSpan.FromDays(1.5); d = d + dure;

Laclasse Nullable<T>

217

Console.WriteLine(d);

// Affiche 17/08/2012 12:00:00

if (DateTime.TryParse(02/05/2010 18:02:25, out d) == true) { Console.WriteLine(date.Year); // Affiche 10 Console.WriteLine(date.Month); // Affiche 5 Console.WriteLine(date.Day); // Affiche 2 Console.WriteLine(date.Hour); // Affiche 18 Console.WriteLine(date.Minute); // Affiche 2 Console.WriteLine(date.Second); // Affiche 25 } else { Console.WriteLine(Mauvais format de la dure); }

Laclasse Nullable<T>
// Dclaration de la classe Nullable<T> public struct Nullable<T> where T: struct, new() // Indiquer si la structure contient une valeur public bool HasValue { get; } // Obtenir la valeur contenue dans Nullable<T> // si existante public T Value { get; } // Dclarer un type comme nullable Nullable<<type>> <instance>; <type>? instance; // Version raccourcie

Les types par valeur ne peuvent pas tre null. Par exemple, il est impossible de dfinir une valeur null pour un entier de type int. La structure Nullable<T> permet de reprsenter

218

CHAPITRE 8 Les classes et interfaces de base

des types valeur pouvant avoir la valeur null. Ces types sont appels des types nullables. Pour crer un type nullable, il suffit de dclarer une variable de type Nullable<T> avec comme paramtre de type le type rendre nullable. Il devient alors possible de dfinir cette variable comme null ou ayant une valeur spcifie. Si la variable Nullable<T> nest pas null (ou si la proprit HasValue retourne true), la valeur peut tre obtenue en utilisant la proprit Value. Une variable Nullable<T> peut tre dclare laide du symbole(?) plac juste aprs le type valeur rendre nullable. Lexemple suivant illustre la dclaration et lutilisation dun int nullable.
int? age; age = null; if (age == null) { Console.WriteLine(Aucun ge na t spcifi); } age = 26; if (age != null) { Console.Write(Vous avez :); Console.WriteLine(age.Value); }

Info Lappel de la proprit Value sur type nullable dfinie null dclenchera la leve dune exception de type InvalidOperation Exception.

Linterface IDisposable

219

Linterface IDisposable
// InterfaceIDisposable public interface IDisposable { // Librer les ressources alloues void Dispose(); } using(<classe IDisposable> <instance> = <nouvelle instance>()) { // Code utilisant la classe qui implmente // IDisposable }

Le ramasse-miettes (ou garbage collector) est un processus intgr dans le .NET Framework permettant de librer automatiquement la mmoire lorsquun objet nest plus utilis. Il est cependant trs difficile de prvoir quel moment le ramasse-miettes se mettra fonctionner. Certaines classes contiennent des ressources, telle une connexion une base de donnes quil faut librer ds que lobjet nest plus utilis. Si le ramasse-miettes met du temps se dclencher, la connexion la base de donnes risque dtre libre tardivement. Le.NET Framework contient une interface IDisposable contenant une mthode Dispose() que doivent implmenter les classes disposant de ressources librer explicitement. Avec cette mthode, les utilisateurs de vos classes peuvent demander de manire explicite la libration des ressources utilises par les classes implmentant linterface IDisposable.

220

CHAPITRE 8 Les classes et interfaces de base

Limplmentation de la mthode Dispose() doit respecter les rgles suivantes: La mthode peut tre appele plusieurs fois (mme si les ressources sont dj libres). La mthode ne doit jamais dclencher une exception. Ilfaut utiliser le duo try/finally si ncessaire pour protger le code. Ds le premier appel Dispose(), lobjet ne doit plus tre utilisable. Il faut dclencher pour cela lexception ObjectDisposedException. Lexemple qui suit montre une classe implmentant linter face IDisposable contenant une ressource StreamWriter.
public class EcritureFichier : IDisposable { // Ressource librer private StreamWriter fichier; public EcritureFichier() { this.fichier = new StreamWriter(Fichier.txt); } public void EcrireLigne(string ligne) { // Rgle n 3 // Vrifier que la mthode Dispose() na pas // dj t appele if (this.fichier == null) { throw new ObjectDisposedException( Objet dj libr); } this.fichier.WriteLine(ligne); }

Linterface IDisposable

221

public void Dispose() { // Rgle n1 // Si la ressource na pas t libre, la librer if (this.fichier != null) { // Rgle n2 // Protger la libration de la ressource afin // de ne pas dclencher une exception try { this.fichier.Dispose(); } finally { this.fichier = null; } } } }

Voici maintenant lutilisation de la classe dclare prcdemment.


EcritureFichier f; f = new EcritureFichier(); try { f.EcrireLigne(Blabla !); } finally // Appeler la mthode Dispose() en cas de leve { // ou non dune exception f.Dispose(); }

222

CHAPITRE 8 Les classes et interfaces de base

Le bloc using de C# permet de produire le mme rsultat que prcdemment en protgeant un objet implmentant linterface IDisposable. Lors de la sortie de ce bloc, la mthode Dispose() de lobjet protg est automatiquement appele.
using (EcritureFichier f = new EcritureFichier()) { f.EcritureFichier(); }

Info La mthode Dispose() de lobjet protg par le bloc using sera automatiquement appele en sortie du bloc mme si une exception est dclenche.

Linterface IClonable
public class Object { // Raliser une copie superficielle de lobjet // courant protected object MemberwiseClone(); } // Interface IClonable public interface IClonable { public object Clone(); }

La classe de base Object contient une mthode protge MemberwiseClone() permettant de crer une copie superficielle de lobjet o est appele la mthode.

Linterface IClonable 223

La copie superficielle consiste copier tous les champs non statiques de lobjet. Si le champ est de type valeur, il est copi bit bit. Si le champ est de type rfrence, seule la rfrence est copie, mais lobjet rfrenc ne lest pas. Par exemple, si lon dispose dune classe Moto qui dtient deux rfrences un objet Roue, le clonage dune moto par lutilisation de la mthode MemberwiseClone() provoquera la cration dune nouvelle instance de type Moto ayant comme rfrence les mmes roues! Pour pallier ce problme, il faut mettre en place un mcanisme de copie en profondeur qui consiste cloner un objet et tous ses objets rfrencs. Les objets devant tre clons en profondeur doivent implmenter la mthode Clone() de linterface IClonable. Limplmentation de la mthode Clone() de linterface IClonable consiste tout dabord cloner lobjet lui-mme puis cloner les objets dont lobjet dtient une rfrence. En procdant ainsi, le clonage dun objet consiste cloner lobjet lui-mme ainsi que ses objets rfrencis et cela de manire rcursive. Les objets rfrencs doivent donc eux aussi implmenter linterface ICloneable. Lexemple suivant illustre la dclaration des classes Moto et Roue mettant en uvre le mcanisme de clonage en profondeur.
class Roue : ICloneable { private double pression; public Roue(double pression) { this.pression = pression; } public double Pression { get { return this.pression; }

224

CHAPITRE 8 Les classes et interfaces de base

set { this.pression = value; } } public object Clone() { return this.MemberwiseClone(); } } class Moto : ICloneable { private Roue roueAvant; private Roue roueArrire; public Moto() { this.roueAvant = new Roue(2.1); this.roueArrire = new Roue(1.9); } public Roue RoueArrire { get { return this.roueArrire; } } public Roue RoueAvant { get { return this.roueAvant; } } public object Clone() { Moto m; // Cloner la moto m = (Moto)this.MemberwiseClone();

// Cloner les roues m.roueArrire = (Roue)this.roueArrire.Clone(); m.roueAvant = (Roue)this.roueAvant.Clone();

Linterface IClonable 225

return m; } }

Dans cet exemple, le clonage dune Moto consiste cloner la Moto elle-mme et les deux Roue associes. Lexemple suivant illustre lutilisation du clonage en profon deur de la classe Moto dclare prcdemment. La mthode static Object.ReferencesEquals() est ensuite appele afin de vrifier que les instances des deux Roue de la Moto clone ne sont pas les mmes que celles de la Moto originale.
Moto m1; Moto m2; m1 = new Moto(); // Cloner la moto m2 = (Moto)m1.Clone(); Console.Write(Rfrences identiques (roues avant)? ); Console.WriteLine(Object.ReferenceEquals(m1.RoueAvant, m2.RoueAvant)); Console.Write(Rfrences identiques (roues arr.)? ); Console.WriteLine(Object.ReferenceEquals(m1.RoueArrire, m2.RoueArrire));

Attention Lappel de la mthode Clone() sur un tableau neffectue pas une copie en profondeur des objets contenus dans ce dernier (si les objets sont de type rfrence). Aprs clonage dun tableau, il en rsultera deux tableaux qui feront rfrence aux mmes objets.

226

CHAPITRE 8 Les classes et interfaces de base

Laclasse BitConverter
// Convertir des octets spcifis en leur // reprsentation sous forme de chane hexadcimale public static string ToString(byte[] octets, int dbut, int longueur) // Convertir des types primitifs en octets public static byte[] GetBytes(bool boolen); public static byte[] GetBytes(char caractre); public static byte[] GetBytes(double nombre); public static byte[] GetBytes(int nombre); public static byte[] GetBytes(long nombre); // Convertir des octets en type primitif public static bool ToBoolean(byte[] octets, int index); public static bool ToChar(byte[] octets, int index); public static bool ToDouble(byte[] octets, int index); public static bool ToInt32(byte[] octets, int index); public static bool ToInt64(byte[] octets, int index);

La classe System.BitConverter contient des mthodes static permettant de convertir des types primitifs en tableau doctets (byte[]) et inversement. Pour convertir un type primitif en un tableau doctets, il faut utiliser la mthode GetBytes(). La taille du tableau obtenu dpend du type primitif spcifi. Par exemple, la mthode GetBytes(int) permet de convertir un entier de type int en un tableau de 4octets (32bits). Les mthodes ToBoolean(), ToChar(), ToDouble(), ToInt32() et ToInt64() permettent de convertir des octets en un type primitif. Le nombre doctets doit tre suffisant selon le type primitif sinon une exception sera dclenche. Par exemple, la mthode ToInt32() doit prendre en paramtre un tableau contenant au moins 4 octets (32bits).

Laclasse BitConverter 227

Leparamtre index permet de spcifier partir de quel indice du tableau le type primitif doit tre converti. La mthode ToString() permet de convertir un tableau doctets en une reprsentation sous forme de chane de caractres. Chaque octet est exprim dans sa valeur hexadcimale et est spar par un tiret. Cette mthode est trs utilise lors du dboguage dapplications. Lexemple suivant illustre la conversion dun entier en octets et la conversion deux octets contenus dans un tableau en un caractre.
int entier; byte[] octets; char caractre; entier = 1664; // Convertir entier en octets et afficher sa // reprsentation sous forme de chane octets = BitConverter.GetBytes(entier); Console.Write(Octets : ); Console.WriteLine(BitConverter.ToString(octets, 0, 4));

// Crer un tableau doctets contenant aux indices // 3 et 4 les valeurs 0x47-0x00 octets = new byte[] { 0, 0, 0, 0x47, 0 }; // Rcuprer le caractre (2 octets) contenu // lindice 3 caractre = BitConverter.ToChar(octets, 3); Console.WriteLine(Caractre : + caractre);

Le rsultat affich sur la console est le suivant:


Octets : 80-06-00-00 Caractre : G

228

CHAPITRE 8 Les classes et interfaces de base

Laclasse Buffer
// Obtenir le nombre doctets du tableau spcifi public static int ByteLength(Array tableau); // Copier un nombre spcifi doctets dun tableau // vers un autre tableau public static void BlockCopy(Array source, int sourceDbut, Array destination, int destinationDbut, int longueur)

La classe System.Buffer contient des mthodes static permettant de manipuler des octets dun tableau de type primitif. La mthode ByteLength() retourne le nombre doctets dun tableau contenant des types primitifs. Par exemple, pour un tableau contenant 10entiers de type int (32bits), cette mthode retournera 40octets (104). La mthode BlockCopy() permet de copier un certain nombre doctets dun tableau vers un autre tableau. Les tableaux ne doivent pas tre obligatoirement du mme type. Le nombre doctets copier dans le tableau source est spcifi par le paramtre longueur. Les indices sourceDbut et destinationDbut permettent de spcifier, respectivement, lindice doctet de dbut du tableau source partir duquel la copie sera effectue et lindice doctet de dbut du tableau destination o les octets seront copis. Par exemple, si lon dispose dun tableau source de 2entiers de type int (qui tient alors au total sur 8octets) et que lon souhaite copier le deuxime entier, il faudra alors spcifier4 pour le paramtre sourceDbut afin de dmarrer la copie partir du 5eoctet.

Laclasse Buffer 229

Lexemple suivant illustre la copie de deux entiers de type int contenu dans un tableau doctets. La taille en octets du tableau dentiers est ensuite affiche sur la console.
byte[] octets; int[] entiers; // Cration dun tableau contenant 3 octets + 2 int // + 1 octet octets = new byte[] { 0, 0, 0, 16, 0, 0, 0, 64, 0, 0, 0, 0 }; entiers = new int[2]; // Copier les octets depuis lindice 3 sur // une longueur 8 Buffer.BlockCopy(octets, 3, entiers, 0, 8); Console.WriteLine(Entiers rcuprs : {0}-{1}, entiers[0], entiers[1]); Console.WriteLine(Le tableau tient sur {0} octets, Buffer.ByteLength(entiers))

Le rsultat affich sur la console est le suivant:


Entiers rcuprs : 16-64 Le tableau tient sur 8 octets

9
Les collections
De base, le regroupement dobjets ne peut se faire quavec des tableaux. Mais la notion de collection permet doffrir des structures de donnes beaucoup plus abstraites aux dveloppeurs. Le .NET Framework contient donc un ensemble de classes de collections, que vous pouvez utiliser ou tendre. Depuis la version2.0 de C#, les collections utilisent les gnriques, qui garantissent votre code un typage fort.

Les itrateurs
// Interface IEnumerable public interface IEnumerable { IEnumerator GetEnumerator(); } // Interface gnrique IEnumerable<T> public interface IEnumerable<T>: IEnumerable { IEnumerator<T> GetEnumerator(); }

232

CHAPITRE 9 Les collections

// Interface IEnumerator public interface IEnumerator { // Rtablit litrateur la position initiale void Reset(); // Avance litrateur llment suivant bool MoveNext(); // Obtient llment actuel object Current { get; } } // Interface gnrique IEnumerator<T> public interface IEnumerator<T>: IEnumerator { // Obtient llment actuel T Current { get; } } // Parcours dune collection IEnumerable IEnumerator itrateur; itrateur = <collection IEnumerable>.GetEnumerator(); while (itrateur.MoveNext()) { <type> <variable>; <variable> = itrateur.Current; } // Version condense avec linstruction foreach foreach(<type> <variable> in <collection IEnumerable>) { // Code de la boucle }

Les itrateurs permettent de parcourir de manire abstraite (sans connatre limplmentation relle) une collection. Ce parcours se fait lment par lment et en avant unique-

Les itrateurs 233

ment. Il nest pas possible de repartir en arrire ou de sauter des lments. Par contre, il est possible de rinitialiser le parcours et de revenir au tout dbut. Toutes les collections fournies avec le .NET Framework peuvent tre parcourues laide dun itrateur. Pour quun objet soit itrable laide dun itrateur, il faut que la classe associe implmente linterface IEnumerable. Cette interface contient une mthode GetEnumerator() qui doit retourner une nouvelle instance dun itrateur. Un itrateur est une classe qui implmente linterface IEnumerator et se charge de parcourir (itrer) lobjet o la mthode GetEnumerator() a t appele. Linterface IEnumerator demande dimplmenter une mthode MoveNext() permettant davancer la position de litrateur sur lobjet parcouru. MoveNext() doit retourner true si litrateur se trouve sur un lment ou false si litrateur est arriv la fin. Durant le parcours, la proprit Current doit retourner llment courant o se trouve litrateur. La mthode Reset() permet litrateur de revenir au tout dbut de lobjet parcourir. Un objet qui implmente linterface IEnumerable peut tre parcouru laide de linstruction foreach, ce qui permet de simplifier le code. Si aucun lment nest contenu dans lobjet parcourir, cest--dire que le premier appel MoveNext() retourne false, alors le code contenu dans le foreach nest pas excut. Lors de limplmentation dun litrateur ou dun objet pouvant tre itr, il est ncessaire de respecter les rgles suivantes: La mthode GetEnumerator() de linterface IEnumerable doit toujours retourner une nouvelle instance dun itrateur.

234

CHAPITRE 9 Les collections

Lappel

mthode Reset() de linterface IEnumerator doit placer litrateur avant le premier lment. Un appel MoveNext() est donc obligatoire et la proprit Current doit dclencher une exception de type InvalidOperationException afin de signaler que litrateur se trouve sur aucun lment. Si le retour au dbut nest pas possible, il faut dans ce cas dclencher une exception de type InvalidOperationException. Lors de linstanciation dun itrateur, celui-ci doit se positionner avant le premier lment de lobjet parcourir (quivalent lappel dun Reset()). Il est possible de faire plusieurs appels MoveNext(), mme si litrateur se trouve la fin de lobjet parcouru. Dans ce cas, la proprit Current doit dclencher une exception de type InvalidOperationException pour indiquer quil nexiste aucun lment la position courante de litrateur. Durant le parcours, il ne doit pas tre possible de modifier les lments de lobjet parcouru. Dans ce cas, il faudra dclencher une exception de type InvalidOperationEx ception au prochain appel de MoveNext(). Lexemple suivant illustre une classe Etudiant contenant le nom associ. Ces tudiants sont regroups dans une classe Promotion. La classe Promotion implmente linterface IEnumerable<Etudiant> et retourne une instance dune classe imbrique de type PromotionEnumerator, qui reprsente un itrateur permettant ditrer les tudiants contenus dans la promotion.
class Etudiant { private string nom; public Etudiant(string nom) { this.nom = nom;

Les itrateurs 235

} public string Nom { get { return this.nom; } } } class Promotion : IEnumerable<Etudiant> { private Etudiant[] tudiants; // Nombre dtudiants rels dans le tableau private int nombreEtudiants; // Version permettant de dtecter les changements // lors de lajout dun tudiant private int version; public Promotion() { // Il peut y avoir au maximum 10 tudiants this.tudiants = new Etudiant[10]; } public void Ajouter(Etudiant tudiant) { this.tudiants[this.nombreEtudiants] = tudiant; this.nombreEtudiants++; this.version++; } public IEnumerator<Etudiant> GetEnumerator() { return new PromotionEnumerator(this); } IEnumerator IEnumerable.GetEnumerator() { // Appeler GetEnumerator() implment implicitement

236

CHAPITRE 9 Les collections

return this.GetEnumerator(); } // ... Classe imbrique PromotionEnumerator }

Un champ version est ajout la classe Promotion. Ce champ est incrment chaque ajout dun tudiant. Cela permettra litrateur de contrler sil y a eu des changements dans linstance Promotion associe durant le parcours. Lors de linstanciation de PromotionEnumerator, linstance de la promotion est passe en paramtre au constructeur. Cette instance permettra litrateur davoir accs au contenu de la classe Promotion. Une classe imbrique pouvant avoir accs aux membres privs de la classe conteneur, il est alors possible litrateur PromotionEnumerator davoir accs au tableau et au champ version de Promotion. Linterface gnrique IEnumerable<T> contient deux mthodes GetEnumerator() avec des types de retour diffrents. Il est donc ncessaire dimplmenter lune des deux mthodes de manire explicite. Lexemple qui suit est limplmentation de la classe imbrique PromotionEnumerator qui implmente IEnumerator <Etudiant>.
// La classe PromotionEnumerator est imbrique dans // la classe Promotion class PromotionEnumerator : IEnumerator<Etudiant> { // Index o se trouve positionner litrateur private int? index; // Version de la promotion au moment de la cration // de litrateur private int version;

Les itrateurs 237

// Promotion actuellement parcourue private Promotion promotion; public PromotionEnumerator(Promotion promotion) { this.promotion = promotion; // Rcuprer la version courante de la promotion this.version = promotion.version; } public Etudiant Current { get { if (this.index == null) { throw new InvalidOperationException( Litrateur ne se trouve sur aucun lment); } return this.promotion.tudiants [this.index.Value]; } } // Nest pas utilis public void Dispose() { } object IEnumerator.Current { // Appeler Current implment implicitement get { return this.Current; } }

238

CHAPITRE 9 Les collections

public bool MoveNext() { // Contrler si la promotion na pas chang if (this.version != this.promotion.version) { throw new InvalidOperationException( La promotion a chang); } if (this.index == null) { // Amorcer le parcourt this.index = -1; } if (this.index + 1 == this.promotion.nombreEtudiants) { // On est arriv la fin return false; } // Incrmenter la position (index) // et retourner true. this.index++; return true; } public void Reset() { this.index = null; } }

Voici maintenant un exemple qui utilise litrateur:


Promotion promotion; IEnumerator<Etudiant> itrateur;

Les itrateurs 239

promotion = new Promotion(); promotion.Ajouter(new Etudiant(Gilles)); promotion.Ajouter(new Etudiant(Aurlie)); promotion.Ajouter(new Etudiant(Claude)); // Parcourir tous les tudiants de la promotion itrateur = promotion.GetEnumerator(); while (itrateur.MoveNext() == true) { Console.WriteLine(itrateur.Current.Nom); }

Lexemple prcdent produira sur la console:


Gilles Aurlie Claude

Il est possible dutiliser loprateur foreach pour parcourir tous les tudiants de la Promotion. Cela produira le mme rsultat que prcdemment.
foreach (Etudiant e in promotion) { Console.WriteLine(numrateur.Current.Nom); }

En cas de modification de la promotion durant un parcours laide de litrateur PromotionEnumerateur, une exception de type InvalidOperationException est automatiquement dclenche.
foreach (Etudiant e in promotion) { // Dclenchement dune exception // la prochaine itration promotion.Ajouter(new Etudiant(Laurent)); }

240

CHAPITRE 9 Les collections

Les listes: List<T>


// Crer une liste public List<T>(); // Obtenir le nombre dlments public int Count { get; } // Rcuprer ou modifier llment lindex spcifi public T this[int index] { get; set; } // Ajouter un lment public void Add(T lment); // Insrer un lment public void Insert(int index, T lment); // Supprimer un lment public bool Remove(T lment); public void RemoveAt(int index); // Rechercher la position dun lment public int IndexOf(T lment); public int IndexOf(T lment, int index); public int LastIndexOf(T lment); public int LastIndexOf(T lment, int index); // Rechercher un lment en fonction dun prdicat public T Find(Predicate<T> prdicat); public T FindLast(Predicate<T> prdicat); public List<T> FindAll(Predicate<T> prdicat); // Trier les lments dune liste public void Sort<T>(); public void Sort<T>(IComparer<T> comparateur); public void Sort<T>(Comparison<T> comparaison); // Copier les lments de la liste // vers un nouveau tableau public T[] ToArray(); // Obtenir une plage dlments de la liste public List<T> GetRange(int dbut, int nombre);

Les listes: List<T>

241

Les listes offrent quasiment les mmes services quun tableau la diffrence quelles peuvent contenir un nombre dlments qui peut varier durant lexcution. Les lments dune liste peuvent tre accessibles ou modifiables laide dindexeur. La classe List<T> contient en interne un tableau qui est redimensionn au fur et mesure que lon ajoute des lments. Contrairement au tableau, il est possible dinsrer un lment en plein milieu dune liste grce la mthode Insert(). Les recherches dun ou plusieurs lments sur une liste seffectuent avec les mthodes Find(), FindLast() et FindAll(). Il est possible deffectuer des recherches afin de trouver la position (index) dun lment dans une liste laide des mthodes IndexOf() et LastIndexOf(). Comme pour la classe Array, le tri peut seffectuer laide de limplmentation de linterface IComparable<T> pour les lments de la liste mais aussi laide dun prdicat. Le plus souvent, les listes servent de tableau temporaire dynamique. Elles sont alimentes et modifies pendant le droulement dun algorithme et sont converties en un tableau laide de la mthode ToArray(). Lexemple suivant illustre lutilisation dune liste contenant des nombres. Un tri est dabord effectu, suivi de plusieurs recherches dlments.
List<string> liste; int index; string personne; liste = new List<string>(); // Ajouter des personnes liste.Add(Gilles);

242

CHAPITRE 9 Les collections

liste.Add(Claude); liste.Add(Laurent); // Trier la liste liste.Sort(); // Insrer Aurlie en premire position liste.Insert(0, Aurlie); // Afficher le contenu de la liste for (int i = 0; i < liste.Count; i++) { Console.WriteLine(i + --> + liste[i]); } // Rechercher la position de la chane Gilles index = liste.IndexOf(Gilles); Console.WriteLine(Position de Gilles : + index); // Rechercher la premire personne contenant // la lettre u personne = liste.Find(e => e.Contains(u) == true); Console.WriteLine(index);

Le rsultat produit sur la console sera le suivant:


0 --> Aurlie 1 --> Claude 2 --> Gilles 3 --> Laurent Position de Gilles : 2 Personne trouve : Aurlie

Les dictionnaires: Dictionary<TCl, TValeur> 243

Les dictionnaires: Dictionary<TCl, TValeur>


// Crer un dictionnaire public Dictionary<TCl, TValeur>(); // Obtenir le nombre dlments contenu public int Count { get; } // Obtenir un itrateur des paires cl/valeur public Dictionary<TCl, TValeur>.Enumerator GetEnumerator(); // Obtenir les cls du dictionnaire public Dictionary<TCl, TValeur>.KeyCollection Keys { get; } // Obtenir les valeurs dun dictionnaire public Dictionary<TCl, TValeur>.ValueCollection Values {get;} // Obtenir ou modifier llment associer // la cl spcifie public TValeur this[TCl cl] { get; set; } // Obtenir la valeur associe la cl spcifie public bool TryGetValue(TCl cl, out TValeur valeur); // Dterminer si la cl existe dans le dictionnaire public bool ContainsKey(TCl cl); // Dterminer si une valeur existe dans // le dictionnaire public bool ContainsValue(TValue valeur); // Ajouter un lment dans un dictionnaire public void Add(TCl cl, TValeur valeur); // Supprimer un lment dans un dictionnaire public void Remove(TCl cl);

244

CHAPITRE 9 Les collections

Les dictionnaires sont des collections de paires composes dune cl et dune valeur. Les cls et les valeurs peuvent tre de nimporte quel type (entiers, chanes de caractres, Etudiant, etc.). Le type ne peut changer aprs instanciation du dictionnaire. Les cls doivent tre uniques dans un dictionnaire, mais les doublons sur les valeurs sont autoriss. La classe Dictionary<TCl, TValeur> contient deux paramtres de type qui sont le type des cls et le type des valeurs associes. Lajout dune paire se fait laide de la mthode Add() en spcifiant en paramtre la cl et la valeur associe, mais ellepeut se faire partir de la mthode set de lindexeur. Lasuppression dune paire ne peut se faire qu partir de la cl associe. Les dictionnaires permettent de rcuprer trs rapidement une valeur partir dune cl spcifie. Lindexeur de la classe Dictionary<TCl, TValeur> prend en paramtre la cl de la valeur associe rcuprer. Lappel la mthode get sur une cl inexistante, provoquera la leve dune exception. Il faut dans ce cas utiliser la mthode TryGetValue() permettant de rcuprer si possible la valeur associe une cl spcifie. La classe Dictionary<TCl, TValeur> implmente linterface IEnumerable permettant de parcourir les paires de cl/ valeur contenues dans une instance de KeyValuePair<TCl, TValeur>. Il est possible de parcourir uniquement les cls ou les valeurs en utilisant les collections retournes par les proprits Keys et Values. Lexemple qui suit illustre la cration dun dictionnaire de personnes avec comme cl un identifiant.
Dictionary<int, string> d; string valeur; d = new Dictionary<int, string>();

Les dictionnaires: Dictionary<TCl, TValeur> 245

d.Add(16, Gil); d.Add(64, Aurlie); d.Add(33, Laurent); // Ajout dune personne (cl inexistante) d[51] = Claude; // Correction du prnom Gilles d[16] = Gilles; // Afficher toutes les cls et les valeurs associes foreach (KeyValuePair<int, string> paire in d) { Console.WriteLine(paire.Key + = + paire.Value); } // Essayer de rcuprer la valeur de la cl 51 if (d.TryGetValue(51, out valeur) == true) { Console.WriteLine(Valeur trouve : + valeur); } // Indiquer si le dictionnaire contient la cl 64 Console.WriteLine(Cl 64 existante ? + d.ContainsKey(64)); // Indique si le dictionnaire contient la valeur // Benot Console.Write(Valeur Benot existante ? ); Console.WriteLine(d.ContainsValue(Benot));

Voici maintenant le rsultat produit sur la console:


16=Gilles 64=Aurlie 33=Laurent 51=Claude Valeur trouve : Claude Cl 64 existante? True Valeur Benot existante? False

246

CHAPITRE 9 Les collections

Les piles: Stack<T>


// Crer une pile dobjets public Stack<T>(); // Obtenir le nombre dobjets contenus dans la pile public int Count { get; } // Ajouter un objet en haut de la pile public void Push(T objet); // Retirer et obtenir lobjet en haut de la pile public T Pop(); // Obtenir lobjet en haut de la pile // sans le supprimer public T Peek();

La classe Stack<T> permet de modliser des piles dobjets de type Par dfinition, comme pour une pile dassiettes, on ne peut ajouter un objet quau sommet de la pile. On utilise pour cela la mthode Push(). Il est impossible de retirer ou dacc der un objet situ en plein milieu de la pile. Seule la mthode Pop() permet de rcuprer et de supprimer lobjet situ au sommet de la pile. La mthode Peek() rcupre uniquement lobjet situ au sommet sans le retirer. Lexemple qui suit illustre lutilisation dune pile constitue de prnoms.
Stack<string> s; s = new Stack<string>(); s.Push(Gilles); s.Push(Aurlie); s.Push(Laurent); Console.WriteLine(Nombre de prnoms : + s.Count);

Les files: Queue<T> 247

Console.WriteLine(Prnom au sommet : + s.Peek()); // Suppression du prnom (Laurent) situ au sommet s.Pop(); // Affichage des prnoms restant while (s.Count > 0) { Console.WriteLine(s.Pop()); }

Voici le rsultat obtenu sur la console:


Nombre de prnoms : 3 Prnom au sommet : Laurent Aurlie Gilles

Les files: Queue<T>


// Crer une file dobjets public Queue<T>(); // Obtenir le nombre dobjets contenus dans la file public int Count { get; } // Ajouter un objet public void Enqueue(T // Retirer et obtenir public T Dequeue(); // Obtenir lobjet au // sans le supprimer public T Peek(); la fin de la file objet); lobjet au dbut de la file dbut de la file

La classe Queue<T> permet de modliser des files dobjets. Les files peuvent tre considres limage dune file d attente au guichet dun cinma: cest le premier arriv qui sera le premier servi (FIFO: First In First Out).

248

CHAPITRE 9 Les collections

Lajout dun objet la fin de la file se fait laide de la mthode Enqueue(). linverse, la mthode Dequeue() permet de retirer et rcuprer lobjet se trouvant au dbut de la file. Il est possible de rcuprer lobjet se trouvant au dbut de la file sans le retirer via la mthode Peek(). Lexemple suivant illustre lutilisation dune file constitue de prnoms.
Queue<string> s; s = new Queue<string>(); s.Enqueue(Gilles); s.Enqueue(Aurlie); s.Enqueue(Laurent); Console.WriteLine(Nombre de prnoms : + s.Count); Console.WriteLine(Prnom au sommet : + s.Peek()); // Suppression du prnom (Gilles) situ au dbut // de la file s.Dequeue(); // Affichage des prnoms restant while (s.Count > 0) { Console.WriteLine(s.Dequeue()); }

Voici le rsultat obtenu sur la console:


Nombre de prnoms : 3 Prnom au sommet : Gilles Aurlie Laurent

Initialiser unecollection lorsdesa cration (C#3.0) 249

Initialiser unecollection lorsdesa cration (C#3.0)


// Dclarer une collection <type collection> <instance>; // Crer une collection <instance> = new <type collection>() { <lment1>[, ...] }

Avec C#3.0, il est possible dinitialiser une collection lors de sa cration (comme pour les tableaux). Linitialisation dune collection ncessite que cette dernire dispose dune mthode Add(). Les types des diffrents lments spcifis linitialisation doivent correspondre au type des paramtres de la mthode Add() de la collection. Par exemple, il nest pas possible dinitialiser une liste de chanes de caractres avec des entiers. En effet, la mthode Add() de la classe List<string> prend en paramtre une chane de caractres et non un entier. Lexemple suivant illustre linstanciation et linitialisation dune liste de chanes de caractres contenant des prnoms.
List<string> prnoms; prnoms = new List<string>() { Gilles, Claude };

Voici lquivalent du code prcdent avec plusieurs appels la mthode Add().


List<string> prnoms; prnoms = new List<string>(); prnoms.Add(Gilles); prnoms.Add(Claude);

250

CHAPITRE 9 Les collections

Dans le cas de classe Dictionary<TCl, TValeur>, la mthode Add() prend deux paramtres qui sont la cl et lavaleur associe. Les lments devant tre spcifis linitialisation doivent donc tre des couples de cl/valeur. Lexemple suivant illustre linstanciation et linitialisation dun dictionnaire de type Dictionary<int, string>.
Dictionary<int, string> personnes; personnes = new Dictionary<int, string>() { { 16, Gilles }, { 64, Claude } };

10
Les flux
Les flux peuvent tre vus comme des squences doctets, tels un fichier, un canal de communication rseau ou une zone mmoire. Les flux offrent trois services qui sont la lecture, lcriture et le dplacement de la position courante. Un flux est associ une position courante qui reprsente lemplacement o seront lues ou crites les donnes. Cette position est automatiquement dplace au fur et mesure dune opration de lecture ou dcriture. Certains flux, comme par exemple les canaux de communication rseau, ne supportent pas lopration de dplacement de la position courante. Les flux hritent de la classe abstraite Stream et doivent limplmenter. Ainsi, un code peut lire ou crire des octets sur un flux sans savoir rellement o ils seront lus ou crits. Les oprations de lecture et dcriture manipulent des octets (de type byte). Le .NET Framework contient des classes appeles des lecteurs (reader) et des crivains (writer). Ils permettant de lire et dcrire des types plus abstraits tel que des chanes de caractres ou des entiers et se chargent de raliser la conversion en octets en fonction du codage choisi.

252

CHAPITRE 10 Les flux

Utiliser les flux (Stream)


// Lire un octet public int ReadByte(); // Lire une squence doctets et stocker le rsultat // dans tab public int Read(byte[] tab, int offset, int longueur); // crire un octet public void WriteByte(byte valeur); // crire une squence doctets contenus dans le // tableau tab public void Write(byte[] tab, int offset, int longueur); // Forcer lcriture des donnes se trouvant en // mmoire tampon public void Flush(); // Obtenir la position actuelle du flux en octets public long Position { get; } // Dplacer la position actuelle du flux public long Seek(long offset, SeekOrigin origine); // Fermer le flux (libre les ressources) public void Close(); // ou via limplmentation de linterface IDisposable public void Dispose();

La classe Stream est la classe de base de tous les flux. Elle contient les services de lecture, dcriture et de dplacement de la position courante du flux. Les mthodes Read() et Write() prennent en paramtre un tableau qui contient les donnes lues o crire. Ces octets sont placs ou rcuprs dans le tableau partir dune position et sur une longueur dfinies par les paramtres offset et longueur.

Utiliser les flux defichier (FileStream) 253

Les oprations de lecture et dcriture avancent automatiquement la position actuelle du flux. Cette dernire peut tre rcupre laide de la proprit Position et modifie laide de la mthode Seek() en spcifiant lorigine et loffset du dplacement raliser. Certains types de flux contiennent un mcanisme de mmoire tampon afin damliorer les performances dcriture. La mthode Flush() permet de vider et forcer lcriture des donnes contenues dans la mmoire tampon. Les ressources internes utilises par les flux doivent tre libres explicitement en appelant la mthode Close(). Dans ce cas, la mthode Flush() est automatiquement appele afin dcrire les donnes restantes dans la mmoire tampon. La classe Stream implmente linterface IDisposable ; il est donc possible dappeler la mthode Dispose() qui fait appel la mthode Close() en utilisant la clause using.

Utiliser les flux defichier (FileStream)


// Crer un flux sur un fichier public FileStream(string fichier, FileMode mode);

La classe FileStream reprsente un flux permettant de lire et crire des octets sur un fichier. Le dplacement est autoris sur ce type de flux. Lexemple suivant illustre lutilisation dun flux obtenu en ouvrant un fichier contenant les octets suivants:
43 61 63 68 6F 75

Un octet est lu laide de la mthode ReadByte() et les trois suivants laide de la mthode Read(). La position courante est change pour se situer sur le deuxime octet afin de pouvoir crire un octet via la mthode WriteByte() suivi de trois autres octets via la mthode Write().

254

CHAPITRE 10 Les flux

using (Stream s = new FileStream(Fichier, FileMode.Open)) { byte[] t; t = new byte[3]; // Lecture dun octet Console.WriteLine(Octet lu : {0:X}, s.ReadByte()); // Lecture de 3 octets s.Read(t, 0, 3); Console.WriteLine(Octets lus : {0:X}-{1:X}-{2:X}, t[0], t[1], t[2]); // Se replacer sur le 2e octet Console.WriteLine(Avant dplacement : {0}, s.Position); s.Seek(1, SeekOrigin.Begin); Console.WriteLine(Aprs dplacement : {0}, s.Position); // crire un octet s.WriteByte(0x61); s.Flush(); // crire 4 octets t = new byte[] { 0x73, 0x73, 0x65, 0x72 }; s.Write(t, 0, 4); }

Le rsultat produit sur la console est le suivant:


Octet lu : 43 Octets lus : 61-63-68 Avant dplacement : 4 Aprs dplacement : 1

Le fichier aprs excution du code contient les octets suivants:


43 61 73 73 65 72

Utiliser les flux enmmoire (MemoryStream) 255

Utiliser les flux enmmoire (MemoryStream)


// Crer un flux en mmoire public MemoryStream(); // Crer un flux en mmoire initialis avec // des octets public MemoryStream(byte[] octetsInitiales); // Crer un flux en mmoire dune capacit // spcifie public MemoryStream(int capacit); // Obtenir tous les octets contenus dans le flux public byte[] ToArray();

La classe MemoryStream reprsente un flux permettant de lire ou dcrire des octets dans un tableau (byte[]) en mmoire. Ce tableau est automatiquement agrandi si ncessaire et peut tre obtenu grce la mthode ToArray(). Lexemple suivant illustre la cration et lutilisation dun MemoryStream.
// Cration dun flux mmoire dune capacit // de 10 octets using (MemoryStream s = new MemoryStream(10)) { byte[] t, rsultat; t = new byte[] { 0x43, 0x61, 0x63, 0x68, 0x67, 0x75 }; // criture de 6 octets ! s.Write(t, 0, 6); // Rcupration de tous les octets contenus // dans le flux rsultat = s.ToArray(); for (int i = 0; i < rsultat.Length; i++)

256

CHAPITRE 10 Les flux

{ Console.Write({0:X} , rsultat[i]); } }

Le rsultat produit sur la console est le suivant:


43 61 63 68 67 75

crire surunflux avecStreamWriter


// Crer un crivain StreamWriter sur un flux public StreamWriter(Stream stream); // Crer un crivain StreamWriter sur un flux et // utilisant le codage spcifi public StreamWriter(Stream stream, Encoding codage); // crire un caractre public void Write(char caractre); // crire un rel de type decimal public void Write(decimal rel); // crire un rel de type double public void Write(double rel); // crire un entier public void Write(int entier); // crire une chane de caractres public void Write(string chane); // crire une chane de caractres de mise en forme public void Write(string chane, params object[] args); // crire un saut de ligne public void WriteLine(); // crire un caractre suivi dun saut de ligne public void WriteLine(char caractre); // crire un rel de type decimal suivi dun saut // de ligne public void WriteLine(decimal rel);

crire surunflux avecStreamWriter 257

// crire un rel de type double suivi dun saut // de ligne public void WriteLine(double rel); // crire un entier suivi dun saut de ligne public void WriteLine(int entier); // crire une chane de caractres suivie dun saut // de ligne public void WriteLine(string chane); // crire une chane de caractres de mise en forme // suivie dun saut de ligne public void WriteLine(string chane, params object[] args); // Fermer lcrivain et le flux sous-jacent public void Close(); // ou via limplmentation de linterface IDisposable public void Dispose();

Lcrivain StreamWriter permet dcrire des types de base tels que des entiers, des chanes de caractres, etc. au format texte dans un flux sous-jacent (Stream). Pour ce faire, lune des surcharges de la mthode Write() doit tre utilise. Les surcharges de WriteLine() font appel aux surcharges de la mthode Write() mais elles ajoutent juste aprs un retour la ligne. Lencodage utilis pour convertir ces types de base en texte doit tre spcifi dans le constructeur de StreamWriter. Si aucun encodage nest spcifi, le format UTF-8 est automatiquement utilis. Lexemple suivant illustre lutilisation de lcrivain Stream Writer permettant dcrire du texte dans un flux de fichier.
// Cration dun flux sur un nouveau fichier using (Stream s = new FileStream(Test.txt, FileMode.Create)) { // Cration dun crivain sur le flux cr using (StreamWriter crivain = new StreamWriter(s,

258

CHAPITRE 10 Les flux

Encoding.Unicode))

{ crivain.WriteLine(Bonjour {0} {1}, Gilles, TOURREAU); crivain.Write(Le prix ); crivain.Write(de cet article ); crivain.Write(est de : ); crivain.Write(999.95); crivain.WriteLine( ); } }

Voici le contenu du fichier Test.txt:


Bonjour Gilles TOURREAU Le prix de cet article est de : 999,95

Lire surunflux avecStreamReader


// Crer un lecteur StreamReader sur un flux public StreamReader(Stream stream); // Crer un lecteur StreamReader sur un flux et // utilisant le codage spcifi public StreamReader(Stream stream, Encoding codage); // Lire un nombre spcifi de caractres public int ReadBlock(char[] t, int dbut, int longueur); // Lire et retourner une ligne de caractres public string ReadLine(); // Lire et retourner tous les caractres restant // dans le flux public string ReadToEnd(); // Fermer le lecteur et le flux sous-jacent public void Close(); // ou via limplmentation de linterface IDisposable public void Dispose();

Lire surunflux avecStreamReader 259

Le lecteur StreamReader permet de lire des caractres contenus dans un flux sous-jacent (Stream). Lencodage utilis pour convertir les octets contenus dans le flux sous-jacent doit tre spcifi au moment de la cration du lecteur. Si aucun encodage nest spcifi, le format UTF-8 est automatiquement utilis. La mthode ReadLine() permet de lire une ligne dans le flux sous-jacent. Une ligne correspond tous les caractres compris entre la position actuelle du flux et un caractre de saut de ligne (ce dernier nest pas rcupr). La mthode ReadToEnd() lit tous les caractres compris entre la position actuelle du flux et sa fin. La mthode ReadBlock() permet de lire un nombre de caractres spcifi par le paramtre longueur. Les caractres lus sont placs dans le tableaut la position dbut. Lexemple suivant illustre la lecture du fichier contenant le texte suivant:
Bonjour Gilles TOURREAU ! Programmer avec C#, cest facile!

Voici lexemple qui illustre la lecture de ce fichier.


using (Stream s = new FileStream(Test.txt, FileMode.Open)) { using (StreamReader lecteur = new StreamReader(s, Encoding.Unicode)) { string texte; char[] t; t = new char[10]; // Lire Bonjour lecteur.ReadBlock(t, 0, 7); Console.WriteLine(t, 0, 7); // Lire toute la ligne restante texte = lecteur.ReadLine(); Console.WriteLine(texte);

260

CHAPITRE 10 Les flux

// Lire le reste du fichier texte = lecteur.ReadToEnd(); Console.WriteLine(texte); } }

Voici le rsultat produit sur la console:


Bonjour Gilles TOURREAU ! Programmer avec C#, cest facile !

crire surunflux avecBinaryWriter


// Crer un crivain BinaryWriter sur un flux public BinaryWriter(Stream stream); // Crer un crivain BinaryWriter sur un flux et // utilisant le codage spcifi pour les chanes // de caractres public BinaryWriter(Stream stream, Encoding codage); // crire un caractre public void Write(char caractre); // crire un rel de type decimal public void Write(decimal rel); // crire un rel de type double public void Write(double rel); // crire un entier public void Write(int entier); // crire une chane de caractres public void Write(char[] chane); // crire une chane de caractres prfixe de // sa longueur en octets public void Write(string chane);

crire surunflux avecBinaryWriter

261

// Fermer lcrivain et le flux sous-jacent public void Close(); // ou via limplmentation de linterface IDisposable public void Dispose();

Lcrivain BinaryWriter permet dcrire, laide des surcharges de la mthode Write(), des types de base tels que entiers, chanes de caractres, etc., au format binaire dans un flux sous-jacent (Stream). La surcharge de la mthode Write(String), prenant en paramtre une chane de caractres (string), prfixe la chane crite par sa longueur. Cela permet au lecteur de pouvoir connatre la longueur en octets de la chane decaractres lors de sa lecture. Pour crire une chane de caractres sans la prfixer de sa longueur, il faut utiliser la surcharge Write(char[]). Lencodage utilis pour crire les chanes de caractres doit tre spcifi dans le constructeur de BinaryWriter. Si aucun encodage nest spcifi, le format UTF-8 est automatiquement utilis. Lexemple suivant illustre lutilisation de lcrivain Binary Writer. Cet crivain crit un entier suivi de deux chanes de caractres. La premire est crite avec la surcharge Write(String), la suivante avec la surcharge Write(char[]).
// Cration dun flux sur un nouveau fichier using (Stream s = new FileStream(Test.bin, FileMode.Create)) { // Cration dun crivain sur le flux cr using (BinaryWriter crivain = new BinaryWriter(s, Encoding.Unicode)) { // criture dun entier crivain.Write(0x1664);

262

CHAPITRE 10 Les flux

// criture dune chane de caractres // prfixe par sa longueur crivain.Write(Gilles); // criture dune chane de caractres crivain.Write(TOURREAU.ToCharArray()); } }

Voici le contenu du fichier Test.bin:


64 00 00 00 16 6C 55 55 00 00 0C 47 00 69 00 6C 00 65 00 73 00 54 00 4F 00 52 00 52 00 45 00 41 00

Les caractres crits dans ce fichier sont au format Unicode UTF-16. Ils sont donc cods sur 16bits, soit deux octets. Les quatre premiers octets reprsentent lentier 1664 sur 32bits.Vient ensuite loctet ayant comme valeur 0C soit 12 en dcimal qui correspond la longueur en octets de la chane Gilles , code avec Unicode UTF-16. Les octets restants reprsentent la chane de caractres TOURREAU qui est elle aussi code avec Unicode UTF-16.

Lire unflux avecBinaryReader


// Crer un crivain BinaryReader sur un flux public BinaryReader(Stream stream); // Crer un crivain BinaryReader sur un flux et // utilisant le codage spcifi pour les chanes // de caractres public BinaryReader (Stream stream, Encoding codage); // Lire un caractre public char ReadChar(); // Lire un rel de type decimal public decimal ReadDecimal();

Lire unflux avecBinaryReader 263

// Lire un rel de type double public double ReadDouble(); // Lire un entier de type int public int ReadInt32(); // Lire une chane de caractres public char[] ReadChars(int longueur); // Lire une chane de caractres prfixe // de sa longueur en octets public string ReadString(); // Fermer le lecteur et le flux sous-jacent public void Close(); // ou via limplmentation de linterface IDisposable public void Dispose();

Le lecteur BinaryReader permet de lire des types de base tels que chanes de caractres, entiers, etc. contenus dans un flux. Une chane de caractres peut tre lue directement via la mthode ReadString() si celle-ci est prfixe par sa longueur en octets. Lencodage utilis pour lire les chanes de caractres doit tre spcifi dans le constructeur de BinaryReader. Si aucun encodage nest spcifi, le format UTF-8 est automatiquement utilis. Lexemple suivant illustre la lecture du fichier contenant les octets suivants:
64 00 00 00 16 6C 55 55 00 00 0C 47 00 69 00 6C 00 65 00 73 00 54 00 4F 00 52 00 52 00 45 00 41 00

Les caractres crits dans ce fichier sont au format Unicode UTF-16. Ils sont donc cods sur 16bits, soit deux octets. Les quatre premiers octets reprsentent lentier 1664 en hexadcimal cod sur 32bits (int).Vient ensuite un octet

264

CHAPITRE 10 Les flux

ayant comme valeur 0C soit 12 en dcimal qui correspond la longueur de la chane Gilles qui suit, code avec Unicode UTF-16. Les octets restants reprsentent la chane de caractres TOURREAU, code elle aussi avec Unicode UTF-16. Le code suivant permet de lire ce fichier.
using (Stream s = new FileStream(Test.bin, FileMode.Open)) { // Cration dun crivain sur le flux cr using (BinaryReader lecteur = new BinaryReader(s, Encoding.Unicode)) { int entier; string chane; char[] t; // Lire lentier sur 32-bit entier = lecteur.ReadInt32(); Console.WriteLine(Entier lu : {0:X}, entier); // Lire la chane de caractres Gilles chane = lecteur.ReadString(); Console.WriteLine(Chane lue : + chane); // Lire la chane de caractres TOURREAU t = lecteur.ReadChars(8); Console.Write(Chane lue : ); Console.WriteLine(t); } }

Voici le rsultat affich sur la console:


Entier lu : 1664 Chane lue : Gilles Chane lue : TOURREAU

11
Les fichiers et rpertoires
Manipuler les fichiers (File)
// Copier un fichier public static void Copy(string source, string destination); // Copier un fichier avec crasement si ncessaire public static void Copy(string source, string destination, bool craser); // Supprimer un fichier public static void Delete(string cheminFichier); // Dterminer si un fichier est existant public static bool Exists(string fichier); // Ouvrir un fichier et retourner un FileStream public static FileStream Open(string fichier, FileMode mode, FileAccess accs);

266

CHAPITRE 11 Les fichiers et rpertoires

La classe static File contient des mthodes static permettant de manipuler des fichiers. La mthode static Copy() permet de copier un fichier. Cette mthode dclenche une exception si le fichier destination existe dj. Une surcharge de la mthode Copy() prend en paramtre un boolen permettant dindiquer sil faut craser ou non le fichier de destination si ce dernier existe. La mthode static Delete() permet de supprimer un fichier dont le chemin est spcifi en paramtre. Aucune exception nest dclenche si le fichier nexiste pas. La mthode static Exists() permet de tester lexistence dun fichier. Lexemple suivant illustre le dplacement dun fichier.
string source; string destination; source = @C:\MesDocuments\DocumentWord.docx; destination = @C:\AutreEmplacement\AutreNom.docx; // Vrifier si le fichier destination existe dj if (File.Exists(destination) == true) { Console.WriteLine(Le fichier destination existe !); } // Copier le fichier File.Copy(source, destination); // Supprimer le fichier source File.Delete(source);

La mthode static Open() permet douvrir ou de crer un fichier en fonction du mode et de laccs spcifis en paramtres. vitez dutiliser le mode daccs ReadWrite si vous ne souhaitez pas lire et crire la fois dans un fichier.

Manipuler les fichiers (File) 267

Lemode daccs vous permet de protger vos fichiers contre des failles qui seraient prsentes dans votre application. Les diffrentes valeurs de FileMode sont donnes au Tableau11.1.
Tableau 11.1: Les diffrentes valeurs de FileMode
Valeur Description Ouvre le fichier sil existe et place la position duflux la fin du fichier. Cre un fichier; si celui-ci existe il est remplac. Cre un fichier; si celui-ci existe, une exception est dclenche. Ouvre le fichier; si celui-ci nexiste pas, une exception est dclenche. Ouvre le fichier; si celui-ci nexiste pas, il est automatiquement cr. Ouvre le fichier et efface tout son contenu.

Append Create CreateNew Open OpenOrCreate Truncate

Le Tableau11.2 prsente les diffrentes valeurs de FileAccess.


Tableau 11.2: Les diffrentes valeurs de FileAccess
Valeur Description Ouvre le fichier en lecture uniquement. Ouvre le fichier en lecture et criture. Ouvre le fichier en criture.

Read ReadWrite Write

Lexemple suivant illustre lutilisation de la mthode Open() pour ouvrir un fichier existant afin dy crire des octets.

268

CHAPITRE 11 Les fichiers et rpertoires

using (Stream s = File.Open(@C:\MesDocuments\Document.txt, FileMode.Open, FileAccess.Write)) { byte[] t; t = new byte[] { 16, 64 }; // crire les octets contenus dans t s.Write(octets, 0, 2); }

Manipuler les rpertoires (Directory)


// Crer tous les rpertoires et sous-rpertoires public static DirectoryInfo CreateDirectory( string rp); // Supprimer un rpertoire spcifi public static void Delete(string rpertoire, bool rcursif); // Dterminer si un fichier existe public static bool Exists(string rpertoire); // Obtenir le rpertoire courant de lapplication public static string GetCurrentDirectory(); // Dplacer un rpertoire public static void Move(string source, string destination); // Rcuprer tous les noms des fichiers contenus // dans un rpertoire public static string[] GetFiles(string rpertoire, string patternRecherche, SearchOption options);

Manipuler les rpertoires (Directory) 269

// Rcuprer tous les noms des sous-rpertoires // contenus dans un rpertoire public static string[] GetDirectories( string rpertoire, string patternRecherche, SearchOption options);

La classe static Directory contient des mthodes static permettant de manipuler des rpertoires. Chaque processus (instance dune application) sexcute dans un rpertoire appel plus communment rpertoire de travail, qui peut tre obtenu laide de la mthode GetCurrentDirectory(). Il est possible de faire rfrence ce rpertoire courant dans toutes les mthodes contenues dans la classe Directory en utilisant le rpertoire.\. La mthode CreateDirectory() permet de crer un rpertoire ainsi que tous les sous-rpertoires ncessaires et retourne une instance DirectoryInfo contenant des informations relatives au rpertoire nouvellement cr. La mthode DeleteDirectory() permet de supprimer un rpertoire avec tous ses sous-rpertoires et fichiers inclus si le paramtre rcursif est dfini true. Si le paramtre rcursif est false, le rpertoire doit tre vide sinon une exception sera dclenche. Lexemple suivant illustre la cration et le dplacement dun rpertoire avant sa destruction.
string source; string destination; source = @C:\MesDocuments\Documents Word\Livre; destination = @C:\Autre Rpertoire; // Cration du rpertoire : // C:\MesDocuments\Documents Word\Livre Directory.CreateDirectory(source);

270

CHAPITRE 11 Les fichiers et rpertoires

// Dplacement du rpertoire // C:\MesDocuments\Documents Word\Livre // vers C:\Autre Rpertoire Directory.Move(source, destination); // Suppression du rpertoire // C:\Autre Rpertoire\Livre Directory.Delete(destination);

La mthode GetFiles() retourne un tableau contenant une liste de noms de fichiers avec leur chemin daccs complet, en fonction dun critre de recherche spcifi. Il en est de mme avec les sous-rpertoires en utilisant la mthode GetDirectories(). Le paramtre patternRecherche correspond aux fichiers (ou sous-rpertoires) rechercher. Les caractres jokers tel que* et? peuvent tre utiliss si ncessaire. Le paramtre options de type SearchOption contient deux valeurs permettant dindiquer si la recherche doit se faire soit dans le rpertoire lui-mme uniquement, soit se propager dans les sousrpertoires de manire rcursive.

Tableau 11.3: Les diffrentes valeurs de SearchOption


Valeur Description La recherche doit se faire uniquement dans le rpertoire. La recherche doit se faire dans le rpertoire ainsi que dans tous ses sous-rpertoires.

TopDirectoryOnly

AllDirectories

Manipuler les rpertoires (Directory)

271

Lexemple qui suit illustre la recherche de diffrents fichiers contenus dans cette arborescence de fichiers:
C:\MesDocuments \Livre \GDS C#.docx \Article sur C#.txt \Lettre.docx

Voici trois exemples de recherche de fichiers dans larborescence prcdente.


string[] fichiers; Console.WriteLine(@Recherche de tous les fichiers se terminant par .docx dans le rpertoire C:\MesDocuments\ :); fichiers = Directory.GetFiles(@C:\MesDocuments\,*.docx, SearchOption.TopDirectoryOnly); foreach (string fichier in fichiers) { Console.WriteLine(fichier); } Console.WriteLine(); Console.WriteLine(@Recherche de tous les fichiers contenant C# dans le rpertoire C:\MesDocuments\ et ses sous-rpertoires :); fichiers = Directory.GetFiles(@C:\MesDocuments\, *C#*, SearchOption.TopDirectoryOnly); foreach (string fichier in fichiers) { Console.WriteLine(fichier); } Console.WriteLine(); Console.WriteLine(@Rcupration de tous les fichiers contenu dans C:\MesDocuments\ et ses sous-rpertoires :);

272

CHAPITRE 11 Les fichiers et rpertoires

fichiers = Directory.GetFiles(@C:\MesDocuments\, *, SearchOption.TopDirectoryOnly); foreach (string fichier in fichiers) { Console.WriteLine(fichier); }

Le rsultat produit sur la console sera le suivant:


Recherche de tous les fichiers se terminant par .docx dans le rpertoire C:\MesDocuments : C:\MesDocuments\Lettre.docx Recherche de tous les fichiers contenant C# dans le rpertoire C:\MesDocuments\ et ses sous-rpertoires : C:\MesDocuments\Article sur C#.txt Rcupration de tous les fichiers contenu dans C:\MesDocuments\ et ses sous-rpertoires : C:\MesDocuments\Article sur C#.txt C:\MesDocuments\Lettre.docx

Obtenir des informations surunfichier (FileInfo)


// Crer une instance FileInfo associe un fichier // spcifi public FileInfo(string nomFichier); // Obtenir le chemin daccs complet du fichier public string FullName { get; } // Obtenir la longueur du fichier public int Length { get; } // Obtenir le nom du rpertoire public string DirectoryName { get; }

Obtenir des informations surunfichier (FileInfo) 273

// Obtenir ou dfinir lheure du dernier accs // au fichier public DateTime LastAccessTime { get; set; } // Obtenir ou dfinir lheure de la dernire criture public DateTime LastWriteTime { get; set; } // Obtenir ou dfinir lheure de cration du fichier public DateTime CreationTime { get; set; } // Obtenir ou dfinir les attributs du fichier public FileAttributes Attributes { get; set; }

La classe FileInfo permet de rcuprer et de modifier des informations sur un fichier tel que: son chemin daccs complet (proprit FullName), sa taille (proprit Length), ses date et heure de cration (proprit CreationTime), ses date et heure de modification (proprit LastWrite Time), ses date et heure de dernier accs (proprit LastAccess Time), ses attributs (proprit Attributes), son rpertoire (proprit DirectoryName). Lors de linstanciation de la classe FileInfo, le nom du fichier complet (cest--dire avec le nom du rpertoire) doit tre spcifi en paramtre. Les attributs de la proprit Attributes sont une combinai son des valeurs contenues dans lnumration FileAttri butes, valeurs dcrites au Tableau11.4.

274

CHAPITRE 11 Les fichiers et rpertoires

Tableau 11.4: Les diffrentes valeurs de FileAttributes


Valeur Description Le fichier est en lecture seule. Le fichier est masqu. Le fichier est un fichier systme. Le fichier est un rpertoire. Le fichier est archiv. Le fichier est compress. Le fichier est crypt.

ReadOnly Hidden System Directory Archive Compressed Encrypted

Lexemple suivant illustre lutilisation de la classe FileInfo afin dafficher des informations relatives au fichier C:\Mes documents\GDS-C#.docx.
FileInfo i; i = new FileInfo(@C:\Mes documents\GDS-C#.docx); Console.WriteLine(Chemin complet : {0}, i.FullName); Console.WriteLine(Taille : {0}, i.Length); Console.WriteLine(Rpertoire : {0}, i.DirectoryName); Console.WriteLine(Dernier accs : {0}, i.LastAccessTime); Console.WriteLine(Dernire modification : {0}, i.LastWriteTime); Console.WriteLine(Cration : {0}, info.CreationTime); if ((i.Attributes & FileAttributes.Archive) == FileAttributes.Archive) { Console.WriteLine(Le fichier a t sauvegard !); }

Obtenir des informations surunrpertoire (DirectoryInfo) 275

Obtenir des informations surunrpertoire (DirectoryInfo)


// Crer une instance DirectoryInfo associe // un rpertoire spcifi public DirectoryInfo(string nomRpertoire); // Obtenir le chemin daccs complet du rpertoire public string FullName { get; } // Obtenir le rpertoire parent public DirectoryInfo Parent { get; } // Obtenir la racine du rpertoire public DirectoryInfo Root { get; } // Obtenir ou dfinir lheure du dernier accs // au rpertoire public DateTime LastAccessTime { get; set; } // Obtenir ou dfinir lheure de la dernire criture public DateTime LastWriteTime { get; set; } // Obtenir ou dfinir lheure de cration // du rpertoire public DateTime CreationTime { get; set; } // Obtenir ou dfinir les attributs du rpertoire public FileAttributes Attributes { get; set; }

La classe DirectoryInfo permet de rcuprer et de modifier des informations sur un fichier tel que: son chemin daccs complet (proprit FullName); sa date et heure de cration (proprit CreationTime); sa date et heure de modification (proprit LastWrite Time);

276

CHAPITRE 11 Les fichiers et rpertoires

sa date et heure de dernier accs (proprit LastAccess


Time); ses attributs (proprit Attributes); son rpertoire parent (proprit Parent); sa racine (proprit Root).

Lors de linstanciation de la classe DirectoryInfo, le rpertoire doit tre spcifi en paramtre. Les proprits Parent et Root retournent dautres instances de DirectoryInfo reprsentant respectivement les rpertoires parent et racine du rpertoire associ. Les attributs de la proprit Attributes sont une combinaison des valeurs de lnumration FileAttributes, dcrites au Tableau 11.5.
Tableau 11.5: Les diffrentes valeurs de FileAttributes
Valeur Description Le rpertoire est en lecture seule. Le rpertoire est masqu. Le rpertoire est un fichier systme. Le rpertoire est un rpertoire. Le rpertoire est archiv. Le rpertoire est compress. Le rpertoire est crypt.

ReadOnly Hidden System Directory Archive Compressed Encrypted

Lexemple suivant illustre lutilisation de la classe Direc toryInfo afin dafficher des informations sur le rpertoire C:\Mes documents.

Obtenir des informations surunlecteur (DriveInfo) 277

DirectoryInfo i; i = new DirectoryInfo(@C:\Mes documents\); Console.WriteLine(Chemin complet : {0}, i.FullName); Console.WriteLine(Parent : {0}, i.Parent.FullName); Console.WriteLine(Racine : {0}, i.Root.FullName); Console.WriteLine(Dernier accs : {0}, i.LastAccessTime); Console.WriteLine(Dernire modification : {0}, i.LastWriteTime); Console.WriteLine(Cration : {0}, info.CreationTime); if ((i.Attributes & FileAttributes.Archive) == FileAttributes.Archive) { Console.WriteLine(Le rpertoire a t sauvegard !); }

Obtenir des informations surunlecteur (DriveInfo)


// Crer une instance DriveInfo associe // un lecteur spcifi public DriveInfo(string lecteur); // Rcuprer tous les lecteurs de lordinateur public static DriveInfo[] GetDrives(); // Obtenir la lettre du lecteur public string Name { get; } // Obtenir ou dfinir ltiquette du lecteur public string VolumeLabel { get; set; }

278

CHAPITRE 11 Les fichiers et rpertoires

// Obtenir le nom du systme de fichiers du lecteur public string DriveFormat { get; } // Obtenir le type de lecteur public DriveType DriveType { get; } // Obtenir le volume total despace libre (en octets) public long TotalFreeSpace { get; } // Obtenir la taille totale despace (en octets) public long TotalSize { get; }

La classe DriveInfo permet de rcuprer et de modifier des informations sur un lecteur tel que: son nom (proprit Name); son tiquette de volume (proprit VolumeLabel); son type de systme de fichiers (proprit DriveFormat); son type (proprit DriveType); son volume total despace libre en octets (proprit TotalFreeSpace); sa taille totale en octets(proprit TotalSize). Lors de linstanciation de la classe DriveInfo, la lettre du lecteur doit tre spcifie en paramtre. Il est possible de rcuprer tous les lecteurs actuellement disponibles de lordi nateur actif en utilisant la mthode static GetDrives(). Le type de lecteur obtenu par la proprit DriveType est lune des valeurs de lnumration DriveType, dcrites au Tableau11.6.

Obtenir des informations surunlecteur (DriveInfo) 279

Tableau 11.6: Les diffrentes valeurs de DriveType


Valeur Description Le lecteur est un priphrique de disque optique (CD ou DVD). Le lecteur est un disque fixe. Le lecteur est un lecteur rseau. Le lecteur est un disque RAM. Le lecteur est un priphrique de stockage amovible (lecteur de disquette, cl USB, etc.). Le lecteur est de type inconnu.

CDRom Fixed Network Ram Removable Unknown

Lexemple suivant illustre lutilisation de la classe DriveInfo afin dafficher des informations sur tous les lecteurs prsents sur lordinateur actif.
foreach (DriveInfo l in DriveInfo.GetDrives()) { Console.WriteLine(Nom : {0} , l.Name); Console.WriteLine(Format : {0}, l.DriveFormat); Console.WriteLine(Dispo : {0} Go, l.TotalFreeSpace / 1024 / 1024 / 1024); Console.WriteLine(Taille : {0} Go, l.TotalSize / 1024 / 1024 / 1024); if (l.DriveType == DriveType.Fixed) { Console.WriteLine(Cest un disque dur !); } else { Console.WriteLine(Ce nest pas un disque dur !); } }

12
Les threads
De nos jours, les ordinateurs disposent dune architecture matrielle multiprocesseur permettant dexcuter plusieurs instances dun code en parallle. Cette instance est appele plus communment un thread. Le .NET Frame work contient une classe Thread permettant de crer et manipuler de tels threads. Chaque instance de la classe Thread est charge dexcuter une mthode. Lorsque la mthode est termine, le thread est considr comme termin. Lors du lancement dune application, un thread est automatiquement cr. Ce thread est appel le thread principal et correspond au code qui est excut au dmarrage de votre application (la mthode Main()). La fin de ce thread engendre la fin de lapplication et de tous les threads associs. Cest le systme dexploitation qui soccupe dexcuter et dordonnancer les threads. Il est donc impossible de prvoir lordre dexcution des threads dun lancement un autre dune application. Les threads font partie dune mme application et se partagent donc les mmes ressources (variables, fichiers ouverts, etc.).

282

CHAPITRE 12 Les threads

Certaines ressources ne peuvent tre utilises quavec un seul Thread; pour cela, le .NET Framework offre des mcanismes permettant dordonnancer et de contrler lexcution des threads (on appelle cela la synchronisation des threads). Ces mcanismes sont les moniteurs, les smaphores et les mutex.

Crer et dmarrer unthread


// Crer un Thread public Thread(ThreadStart mthode); public Thread(ParameterizedThreadStart mthode); // Dlgus utiliss par les constructeurs public delegate void ThreadStart(); public delegate void ParameterizedThreadStart( Object objet); // Obtenir ou dfinir le nom du Thread public string Name { get; set; } // Dmarrer un thread public void Start(); public void Start(object objet);

Pour crer un thread, il suffit de crer une nouvelle instance de la classe Thread en spcifiant en paramtre la mthode excuter lors du dmarrage du thread. Les mthodes doivent tre de type ThreadStart ou ParameterizedThread Start. Les mthodes de type ParameterizedThreadStart permettent de recevoir un paramtre de type object qui est spcifi au moment du dmarrage du Thread.
Info Pensez utiliser les dlgus anonymes (ou les expressions lambda) pour crer des mthodes de Thread.

Crer et dmarrer unthread 283

Il est possible voire mme conseill de spcifier un nom aux Thread laide de la proprit Name. Cela permet de diffrencier les Thread entre eux dans les environnements de dveloppement (tel que Visual Studio). Une fois quun Thread est cre, il faut le dmarrer explicitement en appelant lune des surcharges de la mthode Start(). Spcifiez un paramtre la mthode Start() si le Thread fait rfrence une mthode de type Parameteri zedThreadStart. Une fois la mthode Start() appele, la mthode associe est excute en parallle par rapport au code qui a fait appel la mthode Start(). Lexemple suivant illustre la cration dun Thread qui affiche un message et la valeur de son paramtre reu lors de lappel la mthode Start().
static void Main(string[] args) { Thread t; // Cration dun thread t = new Thread(MthodeThread); // Affectation dun nom t.Name = Mon thread; for (int i = 0; i < 10; i++) { if (i == 1) { // Si i=1 alors on dmarre le thread t.Start(1664); } Console.WriteLine(Bonjour !); } }

284

CHAPITRE 12 Les threads

static void MthodeThread(object o) { Console.WriteLine(Bonjour depuis le Thread !); Console.WriteLine(Paramtre reu : {0}, o); }

Voici un exemple dexcution du code prcdent:


Bonjour ! Bonjour ! Bonjour ! Bonjour ! Bonjour depuis le Thread ! Bonjour ! Bonjour ! Bonjour ! Paramtre reu : 1664 Bonjour ! Bonjour ! Bonjour !

Mettre enpause unthread


public static void Thread.Sleep(int nbMillisecondes);

La mthode static Sleep() met en pause le thread actuellement en cours dexcution durant un nombre de millisecondes spcifi. Lorsque le Thread est en pause, il ne consomme aucune ressource processeur. Lexemple qui suit montre comment mettre en pause un Thread durant une seconde.

Attendre lafin dunthread 285

static void Main(string[] args) { Thread t; // Cration dun thread t = new Thread(MthodeThread); // Dmarrer le thread t.Start(); // Faire attendre le thread principal dune seconde Thread.Sleep(1000); Console.WriteLine(Bonjour !); } static void MthodeThread() { Console.WriteLine(Bonjour depuis le Thread !); }

Attendre lafin dunthread


// Attendre public void // Attendre public bool public bool la fin du thread Join(); la fin du thread sur une dure maximale Join(int dureMaxMillisecondes); Join(TimeSpan dureMax);

La mthode Join() de la classe Thread permet dattendre la fin du thread associ. Lorsquun thread attend la fin dun autre thread, il est mis en pause et ne consomme aucune ressource processeur.

286

CHAPITRE 12 Les threads

Tant que le thread attendu nest pas termin, le thread qui a fait appel la mthode Join() reste bloqu. Il est possible de spcifier une dure maximale dattente en millisecondes. Dans ce cas, la mthode Join() retourne false pour indiquer que le Thread attendu est toujours en cours dexcution. Lexemple qui suit illustre lattente dun thread avec une dure de 2secondes au maximum. La dure dexcution du thread est chronomtre laide de la classe Stopwatch du .NET Framework.
static void Main(string[] args) { Thread t; Stopwatch chrono; bool rsultat; // Cration dun thread t = new Thread(MthodeThread); // Crer le chronomtre chrono = new Stopwatch(); // Dmarrer le thread chrono.Start(); t.Start(); // Attendre la fin du thread cre au maximum // 5 secondes rsultat = t.Join(5000); chrono.Stop(); if (rsultat == false) { Console.WriteLine(Le thread a t attendu plus de 5 secondes !); }

Rcuprer lethread encours dexcution 287

Console.WriteLine(Temps dexcution du Thread : {0} ms, chrono.ElapsedMilliseconds); } static void MthodeThread() { Console.WriteLine(Bonjour depuis le Thread !); Console.WriteLine(Attente de 2 secondes); Thread.Sleep(2000); Console.WriteLine(Je viens de me rveiller !); }

Le rsultat produit sur la console est le suivant:


Bonjour depuis le Thread ! Attente de 2 secondes Je viens de me rveiller ! Temps dexcution du Thread : 2013 ms

Si maintenant, on change la valeur de pause de 2secondes 10 dans la mthode MthodeThread(), on obtiendra la sortie suivante sur la console:
Bonjour depuis le Thread ! Attente de 2 secondes Le thread a t attendu plus de 5 secondes ! Temps dexcution du Thread : 5016 ms

Rcuprer lethread encours dexcution


public static Thread CurrentThread { get; set; }

La proprit CurrentThread permet de rcuprer le thread en cours dexcution.

288

CHAPITRE 12 Les threads

Lexemple qui suit montre un exemple de lutilisation de la proprit CurrentThread afin de rcuprer le nom du thread actuellement en cours dexcution.
static void Main(string[] args) { Thread t; // Cration dun thread t = new Thread(MthodeThread); t.Name = Mon thread moi; // Dmarrer le thread t.Start(); // Attendre la fin du thread t.Join(); } static void MthodeThread() { Thread courant; // Obtenir le Thread courant courant = Thread.CurrentThread; Console.WriteLine(Mon nom est : {0}, courant.Name); }

Le rsultat produit sur la console est le suivant:


Mon nom est : Mon thread moi

Crer des variables statiques associes unthread


[ThreadStaticAttribute] public static <type> <nom champ>;

Crer des variables statiques associes unthread 289

Par dfaut, les variables static sont partages et accessibles par tous les Thread. Il est possible de dclarer une variable static unique pour chaque thread en spcifiant lattribut ThreadStaticAttribute. Il ne faut en aucun cas affecter une valeur initiale un champ marqu par lattribut ThreadStaticAttribute (mme avec le constructeur static). Cette initialisation na lieu quune seule fois lors de la premire utilisation de la classe. Aucune autre initialisation ne sera donc faite pour les autres Thread. Cest donc au dveloppeur de se charger dinitialiser la valeur du champ lors de son premier accs par un Thread. Lexemple suivant illustre une classe Compteur ayant une instance unique dans chaque Thread. Linstance est accessible via la proprit Courant. Cette dernire vrifie si le champ static est dj initialis. Dans le cas contraire, une instanciation de la classe Compteur est ralise et le rsultat est ensuite rfrenc par le champ static courant. En spcifiant lattribut ThreadStaticAttribute pour le champ courant, une instance static de Compteur est donc cre pour chaque Thread.
class Compteur { [ThreadStaticAttribute()] private static Compteur courant; private Compteur() { } public int Valeur { get; set; } public static Compteur Courant

290

CHAPITRE 12 Les threads

{ get { // Vrifier si le compteur est dj existant if (courant == null) { courant = new Compteur(); } return courant; } } }

Lutilisation dun tel compteur est trs simple et se fait en une seule ligne de code:
Compteur.Courant.Valeur++;

Cette ligne incrmente le Compteur courant qui est associ au Thread en cours dexcution.

Utilisez les smaphores (Semaphore)


// Crer un smaphore public Semaphore(int valeurInitiale, int maximum); // Crer un smaphore nomm public Semaphore(int valeurInitiale, int maximum, string nom); // Dcrementer le smaphore public void WaitOne(); // Dcrmenter le smaphore avec une attente maximum public bool WaitOne(TimeSpan attenteMaximum);

Utilisez les smaphores (Semaphore)

291

// Incrmenter le smaphore et retourner sa valeur public void Release();

Un smaphore est un objet de type System.Threading. Semaphore qui permet de protger un ensemble dinstructions devant tre excut par un nombre maximal de threads. Cet ensemble dinstructions est appel plus communment une section critique. Un smaphore contient en interne un compteur, qui est initialis au moment de son instanciation grce au paramtre valeurInitiale. Le paramtre maximum indique le nombre maximal de Thread qui peuvent excuter une mme section critique. Il est possible de donner un nom un smaphore; cela permet de partager et dutiliser un mme smaphore entre diffrentes applications (.NET ou non .NET). Le compteur du smaphore doit tre dcrment chaque entre dans la section critique. Si le compteur interne est dj 0, le thread qui a effectu lappel est automatiquement bloqu. Ce dernier sera automatiquement dbloqu lorsquun autre thread incrmentera la valeur du smaphore. Dans le cas contraire, le compteur est dcrment et lexcution du thread se poursuit. La dcrmentation de la valeur du smaphore se fait avec la mthode WaitOne(). Une surcharge permet de spcifier un temps dattente maximum, et renvoie false si le smaphore na pas pu tre acquis par le thread. Lincrmentation de la valeur du smaphore doit se faire lorsquun thread sort de la section critique. Il suffit pour cela dappeler la mthode Release(). Lexemple suivant illustre une mthode SectionCritique() contenue dans une classe ObjetProtg. Cette mthode est protge par un smaphore qui autorise son excution simultane par trois Thread au maximum.

292

CHAPITRE 12 Les threads

class ObjetProtg { private Semaphore smaphore; public ObjetProtg() { this.smaphore = new Semaphore(3, 3); } public void SectionCritique() { Console.WriteLine({0} : Veut entrer dans la section critique, Thread.CurrentThread.Name); this.smaphore.WaitOne(); Thread.Sleep(1000); Console.WriteLine({0} : Excution de la section critique, Thread.CurrentThread.Name); this.smaphore.Release(); Console.WriteLine({0} : Sort de la section critique, Thread.CurrentThread.Name); } }

Le code suivant utilise la classe ObjetProtg dclare prcdemment et se charge de crer, de dmarrer et dattendre cinq Thread. Ces threads appellent la mthode Section Critique() de lobjet ObjetProtg.
Thread[] threads; ObjetProtg objet; objet = new ObjetProtg(); threads = new Thread[5]; // Cration des threads for (int i = 0; i < threads.Length; i++)

Utilisez les smaphores (Semaphore) 293

{ threads[i] = new Thread(objet.SectionCritique); threads[i].Name = String.Format(Thread n {0}, i + 1); } // Dmarrer les threads foreach (Thread thread in threads) { thread.Start(); } // Attendre les threads foreach (Thread thread in threads) { thread.Join(); }

Voici un exemple du rsultat de lexcution du code prcdent sur la console:


Thread Thread Thread Thread Thread Thread Thread Thread Thread Thread Thread Thread Thread Thread Thread n1 n 5 n 4 n 3 n2 n5 n4 n1 n1 n4 n5 n2 n2 n3 n3 : : : : : : : : : : : : : : : Veut entrer dans la section critique Veut entrer dans la section critique Veut entrer dans la section critique Veut entrer dans la section critique Veut entrer dans la section critique Excution de la section critique Excution de la section critique Excution de la section critique Sort de la section critique Sort de la section critique Sort de la section critique Excution de la section critique Sort de la section critique Excution de la section critique Sort de la section critique

Remarquez que la section critique est excute par trois threads au maximum.

294

CHAPITRE 12 Les threads

Utiliser les mutex (Mutex)


// Crer un mutex qui nest pas initialement dtenu // par le thread actuel public Mutex(); // Crer un mutex en spcifiant si le mutex doit tre // initialement dtenu par le thread actuel public Mutex(bool initialementDtenu); // Crer un mutex nomm en spcifiant si le mutex doit // tre initialement dtenu par le thread actuel public Mutex(bool initialementDtenu, string nom); // Obtenir le mutex public void WaitOne(); // Obtenir le mutex avec une attente maximum public bool WaitOne(TimeSpan attenteMaximum); // Librer le mutex public void ReleaseMutex();

Un mutex est un objet de type System.Threading.Mutex qui permet de protger un ensemble dinstructions devant tre excut par un seul thread la fois. Ce procd est appel lexclusion mutuelle et permet de protger un ensemble dinstructions appel section critique. Un mutex est soit libre, soit dtenu par un thread. Il est possible de spcifier lors de sa cration si le mutex doit tre dtenu par le thread courant en utilisant le paramtre initialementDtenu des diffrentes surcharges du constructeur de la classe Mutex. Lacquisition du mutex se fait laide dune des surcharges de WaitOne(). Si un autre thread dtient dj le mutex, alors le thread qui vient de faire la demande se trouve bloqu jusqu ce que celui-ci soit libr.

Utiliser les mutex (Mutex) 295

La libration du mutex se fait laide dun appel la mthode ReleaseMutex(). Lexemple suivant illustre une mthode SectionCritique() contenue dans une classe ObjetProtg. Cette mthode est protge par un mutex qui nautorise son excution que par un seul Thread.
class ObjetProtg { private Mutex mutex; public ObjetProtg() { this.mutex = new Mutex(false); } public void SectionCritique() { Console.WriteLine({0} : Veut entrer dans la section critique, Thread.CurrentThread.Name); this.mutex.WaitOne(); Thread.Sleep(1000); Console.WriteLine({0} : Excution de la section critique, Thread.CurrentThread.Name); this.mutex.ReleaseMutex(); Console.WriteLine({0} : Sort de la section critique, Thread.CurrentThread.Name); } }

Le code suivant utilise la classe ObjetProtg dclare prcdemment et se charge de crer, de dmarrer et dattendre cinq Thread. Ces threads appellent la mthode Section Critique() de lobjet ObjetProtg.

296

CHAPITRE 12 Les threads

Thread[] threads; ObjetProtg objet; objet = new ObjetProtg(); threads = new Thread[5]; // Cration des threads for (int i = 0; i < threads.Length; i++) { threads[i] = new Thread(objet.SectionCritique); threads[i].Name = String.Format(Thread n{0}, i + 1); } // Dmarrer les threads foreach (Thread thread in threads) { thread.Start(); } // Attendre les threads foreach (Thread thread in threads) { thread.Join(); }

Voici un exemple du rsultat de lexcution du code prcdent sur la console:


Thread Thread Thread Thread Thread Thread Thread Thread Thread Thread n2 n1 n3 n4 n2 n2 n5 n1 n1 n3 : : : : : : : : : : Veut entrer dans la section critique Veut entrer dans la section critique Veut entrer dans la section critique Veut entrer dans la section critique Excution de la section critique Sort de la section critique Veut entrer dans la section critique Excution de la section critique Sort de la section critique Excution de la section critique

Utiliser les moniteurs (Monitor) 297

Thread Thread Thread Thread Thread

n3 n4 n4 n5 n5

: : : : :

Sort de la section critique Excution de la section critique Sort de la section critique Excution de la section critique Sort de la section critique

Remarquez que la section critique nest excute chaque fois que par un seul thread.

Utiliser les moniteurs (Monitor)


// Acqurir un verrou exclusif sur lobjet spcifi public static void Enter(object objet); // Essayer dacqurir un verrou exclusif sur // lobjet spcifi public static bool TryEnter(object objet); // Essayer dacqurir un verrou exclusif sur lobjet // spcifi avec une attente maximum public static bool TryEnter(object objet, TimeSpan timeOut); // Librer un verrou exclusif sur lobjet spcifi public static void Exit(object objet); lock(<objet>) { // Section critique }

Les moniteurs permettent de marquer un bloc de code comme section critique par exclusion mutuelle comme avec les mutex. Au lieu de raliser une exclusion mutuelle en utilisant un objet Mutex, lexclusion mutuelle se base sur une instance dun objet existant.

298

CHAPITRE 12 Les threads

Il est fortement recommand de suivre les recommandations suivantes lors de lutilisation des moniteurs: Ne pas utiliser les moniteurs avec des types publics y compris sur lobjet courant (this). Ne pas utiliser les moniteurs avec des chanes de caractres (les chanes de caractres identiques dans tout le processus se partagent les mmes instances). Ne pas utiliser les moniteurs avec typeof(MonType) car le type retourn est une instance unique dans tout le processus pour le type spcifi. Si vous ne disposez pas dun objet permettant dtre utilis avec les moniteurs, vous pouvez instancier et utiliser un objet vide de type Object. Une instance dObject occupe trs peu de place mmoire contrairement une classe hrite. Lacquisition dun verrou sur une instance dun objet se fait avec lappel de la mthode static Enter(). Si le verrou est dj acquis par un autre thread, le thread qui effectue la demande se trouvera bloqu. Ce dernier sera automatiquement dbloqu lorsque le verrou sera libr par le thread qui le dtient. La mthode TryEnter() permet dacqurir un verrou, mais le retour est immdiat. La valeur boolenne retourne indique si le verrou a pu tre acquis. La libration dun verrou sur un objet seffectue en utilisant la mthode Exit().
Astuce Par mesure de scurit, afin de librer le verrou sur une instance dun objet en cas de leve ou non dune exception, protgez sa libration dans un bloc try/finally.

Lexemple suivant illustre une mthode SectionCritique() contenu dans une classe ObjetProtg. Cette mthode est protge par une exclusion mutuelle laide dun moniteur.

Utiliser les moniteurs (Monitor) 299

Le verrou porte sur un objet vide initialement cre dans le constructeur de ObjetProtg
class ObjetProtg { private object bidon; // Objet vide public ObjetProtg() { this.bidon = new object(); } public void SectionCritique() { Console.WriteLine({0} : Veut entrer dans la section critique, Thread.CurrentThread.Name); Monitor.Enter(this.bidon); try { Thread.Sleep(1000); Console.WriteLine({0} : Excution de la section critique, Thread.CurrentThread.Name); } finally { Monitor.Exit(this.bidon); } Console.WriteLine({0} : Sort de la section critique, Thread.CurrentThread.Name); } }

Le code suivant utilise la classe ObjetProtg dclare prcdemment et se charge de crer, de dmarrer et dattendre cinq Thread. Ces threads appellent la mthode Section Critique() de lobjet ObjetProtg.

300

CHAPITRE 12 Les threads

Thread[] threads; ObjetProtg objet; objet = new ObjetProtg(); threads = new Thread[5]; // Cration des threads for (int i = 0; i < threads.Length; i++) { threads[i] = new Thread(objet.SectionCritique); threads[i].Name = String.Format(Thread n{0}, i + 1); } // Dmarrer les threads foreach (Thread thread in threads) { thread.Start(); } // Attendre les threads foreach (Thread thread in threads) { thread.Join(); }

Voici un exemple du rsultat de lexcution du code prcdent sur la console:


Thread Thread Thread Thread Thread Thread Thread Thread Thread Thread Thread n2 n1 n3 n4 n2 n2 n5 n1 n1 n3 n3 : : : : : : : : : : : Veut entrer dans la section critique Veut entrer dans la section critique Veut entrer dans la section critique Veut entrer dans la section critique Excution de la section critique Sort de la section critique Veut entrer dans la section critique Excution de la section critique Sort de la section critique Excution de la section critique Sort de la section critique

Utiliser les moniteurs (Monitor)

301

Thread Thread Thread Thread

n4 n4 n5 n5

: : : :

Excution de la section critique Sort de la section critique Excution de la section critique Sort de la section critique

Remarquez que la section critique est excute chaque fois par un seul thread. La clause lock de C# utilise les mthodes Enter() et Exit() de la classe Monitor sur un objet spcifi en garantissant que le verrou de lobjet sera automatiquement libr en sortie du bloc. Ainsi, il nest plus ncessaire de protger une section critique avec des blocs try/finally. Voici lquivalent de la clause lock en utilisant des blocs try/finally.
Monitor.Enter(<objet>) try { // Section critique } finally { Monitor.Exit(<objet>); }

Le code suivant reprsente la mthode SectionCritique() de lexemple prcdent en utilisant uniquement la clause lock.
public void SectionCritique() { Console.WriteLine({0} : Veut entrer dans la section critique, Thread.CurrentThread.Name); lock(this.bidon); {

302

CHAPITRE 12 Les threads

Thread.Sleep(1000); Console.WriteLine({0} : Excution de la section critique, Thread.CurrentThread.Name); } Console.WriteLine({0} : Sort de la section critique, Thread.CurrentThread.Name); }

Appeler unemthodedefaon asynchrone


// Interface reprsentant ltat dune opration // asynchrone public interface IAsyncResult { // Indique si lopration asynchrone est termine public bool IsCompleted { get; } // Obtient lobjet spcifi en paramtre lors // de lappel de la mthode BeginInvoke() public object AsyncState { get; } } // Dclarer le dlgu de retour dune opration // asynchrone delegate void AsyncCallBack(IAsyncResultat rsultat); IAsyncResult <instance rsultat>; // Appeler la mthode contenue dans la variable <instance rsultat> = <instance dlgue>.BeginInvoke( [paramtres de la mthode], AsyncCallBack retour, object asyncState);

Appeler unemthodedefaon asynchrone 303

// Attendre la fin de lappel de la mthode // asynchrone <rsultat mthode> = <instance dlgue>.EndInvoke( <instance rsultat>)

Le .NET Framework permet dappeler trs facilement une mthode de faon asynchrone dans un autre thread grce aux dlgus. Toute classe de type dlgu contient une mthode Begin Invoke() permettant dappeler une mthode asynchrone. Ainsi, le code qui effectue lappel nest pas bloqu et poursuit son excution en parallle de la mthode invoque. La mthode BeginInvoke() retourne un objet qui implmente linterface IAsyncResult reprsentant ltat de lop ration asynchrone. On y trouve une proprit IsCompleted qui indique si la mthode invoque de manire asynchrone est termine. La mthode BeginInvoke() prend en paramtres les diffrents arguments envoyer en paramtre la mthode associe. Les deux derniers paramtres permettent de spcifier une mthode de type AsyncCallBack qui sera appele la fin de lopration asynchrone. Un objet peut tre spcifi dans le paramtre asyncState, afin dtre rcupr grce la proprit AsyncState de lobjet de type IAsyncResult retourn par lappel de la mthode BeginInvoke(). La mthode EndInvoke() permet dattendre la fin de lappel asynchrone de la mthode. Si ce dernier nest pas termin, le code qui effectue lappel se trouve bloqu jusqu la fin de lopration asynchrone. La mthode EndInvoke() peut tre vue comme lquivalent de la mthode Thread.Join() prsente aux sections prcdentes. La mthode EndInvoke() retourne la valeur retourne parla mthode appele de faon asynchrone.

304

CHAPITRE 12 Les threads

Lexemple suivant illustre la dclaration dun dlgu Opration prenant en paramtre deux entiers de type int et retournant un entier de type int. Une mthode Addition respectant la signature du dlgu Opration est ensuite dclare ainsi quune autre mthode respectant la signature du dlgu AsyncCallBack.
delegate int Opration(int a, int b); static int Addition(int a, int b) { Console.WriteLine(Calcul en cours...); Thread.Sleep(1000); // On simule un calcul important return a + b; } static void CallBack(IAsyncResult ar) { // Afficher le paramtre spcifi Console.WriteLine(ar.AsyncState); }

Le code qui suit illustre lappel de la mthode Addition de faon asynchrone. La mthode CallBack sera automatiquement appele la fin de lappel de la mthode Addition. La chane de caractres Termin! est pass en paramtre la mthode BeginInvoke() afin quelle puisse tre rcupre dans la mthode CallBack grce la proprit AsyncState.

Appeler unemthodedefaon asynchrone 305

Opration opration; IAsyncResult ar; int rsultat; opration = Addition; ar = opration.BeginInvoke(10, 5, CallBack, Termin !); Console.WriteLine(Le calcul se fait en parallle); Console.WriteLine(Jattends la fin du calcul); rsultat = opration.EndInvoke(ar); Console.Write(Le rsultat de laddition est : ); Console.WriteLine(rsultat);

Le rsultat affich sur la console est le suivant:


Le calcul se fait en parallle Jattends la fin du calcul Calcul en cours... Calcul termin ! Le rsultat de laddition est : 15
Info Les mthodes BeginInvoke() et EndInvoke() permettent dappeler de manire simple et abstraite une mthode de faon asynchrone, sans avoir recours la manipulation des Thread.

13
La srialisation
La srialisation est un processus qui consiste convertir un ensemble dinstances de classe en une suite doctets. Cela permet de sauvegarder des instances de classe dans un fichier et/ou de les faire transiter sur un rseau. Lopration inverse, qui consiste rcuprer ces octets, sappelle la dsrialisation. Il est bien videmment possible de crer son propre mcanisme de srialisation. Cependant, le .NET Framework dispose dun ensemble de classes permettant de raliser les processus de srialisation et de dsrialisation en trs peu de lignes de code. Pour srialiser (ou dsrialiser) une classe, deux tapes sont ncessaires: Spcifier explicitement dans la classe les champs (ou les valeurs des proprits) que lon souhaite srialiser. Utiliser un srialiseur: cest cette classe qui permet de srialiser ou de dsrialiser en octets des instances de la classe prcdemment modifie. Ces octets sont crits ou lus le plus souvent sur un flux. Un srialiseur peut srialiser ou dsrialiser des objets au format binaire, mais il existe des srialiseurs (inclus dans le .NET Framework ou provenant dditeurs tiers) permettant de srialiser des objets dans dautres formats, tel XML.

308

CHAPITRE 13 La srialisation

Attention La srialisation consiste convertir tout (ou une partie) des valeurs des attributs dune classe. Le code des mthodes ou des proprits nest pas srialis.

Un srialiseur srialise par dfaut des types primitifs. Si


la classe srialiser contient des champs faisant rfrence dautres types complexes (non primitifs) il faudra alors dfinir ces autres types comme srialisable.

Info Les classes String, DateTime et TimeSpan sont srialisables.

Dclarer uneclasse srialisable avec SerializableAttribute


[SerializableAttribute()] class <nom de la classe> { // Champs srialisables <visibilit> <type du champ> <nom du champ>; // Champs non srialisables [NonSerializedAttribute()] <visibilit> <type du champ> <nom du champ>; }

Pour dfinir une classe qui soit srialisable, il faut faire prcder sa dclaration par lattribut SerializableAttribute. Cet attribut permet de srialiser automatiquement tous les champs de classe. Si certains champs ne doivent pas tre srialisable, il est alors ncessaire de les faire prcder de lattribut NonSerializedAttribute.

Srialiser et dsrialiser unobjet avecBinaryFormatter 309

Info

internal ou public.

Les champs srialisables peuvent tre private, protected,

Lexemple suivant illustre une classe Personne contenant trois champs dont lun nest pas srialisable.
[SerializableAttribute()] class Personne { private int age; private string nom; [NonSerializedAttribute()] private bool estNouveau; }

Srialiser et dsrialiser unobjet avecBinaryFormatter


// Crer une instance de BinaryFormatter public BinaryFormatter(); // Srialiser un objet dans un flux spcifi public void Serialize(Stream flux, object objet); // Dsrialiser un objet contenu dans le flux spcifi public object Deserialize(Stream flux);

Le srialiseur BinaryFormatter permet de srialiser et de dsrialiser des instances dune classe au format binaire dans des flux doctets. Le code suivant reprsente une classe Personne qui sera srialise.

310

CHAPITRE 13 La srialisation

[SerializableAttribute()] class Personne { private int age; private string nom; [NonSerializedAttribute()] private bool estNouveau; public string Nom { get { return this.nom; } set { this.nom = value; } } public int Age { get { return this.age; } set { this.age = value; } } public bool EstNouveau { get { return this.estNouveau; } set { this.estNouveau = value; } } }

Lexemple qui suit, instancie la classe prcdente et affecte27 la proprit Age, Gilles TOURREAU la proprit Nom et true la proprit EstNouveau. Cette instance est ensuite srialise dans un flux mmoire laide de la mthode Serialize(). Le contenu de ce flux mmoire est ensuite rutilis pour effectuer lopration inverse laide de la mthode Deserialize().

Srialiser et dsrialiser unobjet avecBinaryFormatter

311

BinaryFormatter srialiseur; Personne p; srialiseur = new BinaryFormatter(); p = new Personne(); p.Age = 27; p.Nom = TOURREAU Gilles; p.EstNouveau = true; using (MemoryStream ms = new MemoryStream()) { // Srialiser la personne dans le MemoryStream srialiseur.Serialize(ms, p); // Se mettre au tout dbut du flux ms.Position = 0; // Dsrialiser la personne contenue dans // le MemoryStream p = (Personne)srialiseur.Deserialize(ms); Console.WriteLine(Nom : {0}, p.Nom); Console.WriteLine(Age : {0}, p.Age); Console.WriteLine(Est nouveau : {0}, p.EstNouveau); }

Voici le rsultat produit sur la console:


Nom : TOURREAU Gilles Age : 27 Est nouveau : False

Remarquez que la valeur du champ estNouveau est false car elle na pas t srialise. Lors de la dsrialisation, le champ estNouveau ntant pas dsrialis, il aura comme valeur la valeur par dfaut du type bool.

312

CHAPITRE 13 La srialisation

Personnaliser leprocessus desrialisation aveclinterface ISerializable


// Interface ISerializable public interface ISerializable { // Se produit lors de la srialisation void GetObjectData(SerializationInfo info, StreamingContext context); } // Constructeur ajouter dans lobjet pour // la dsrialisation <visibilit> Personne(SerializationInfo info, StreamingContext contexte) { } // Mthodes public void public void public void public void public void public void de srialisation de SerializationInfo AddValue(string nom, bool valeur); AddValue(string nom, char valeur); AddValue(string nom, double valeur); AddValue(string nom, int valeur); AddValue(string nom, object objet); AddValue(string nom, string valeur);

// Mthodes de dsrialisation de SerializationInfo public bool GetBoolean(string nom); public char GetChar(string nom); public double GetDouble(string nom); public object GetObject(string nom); public int GetInt32(string nom); public string GetString(string nom);

Il est possible de personnaliser le processus de srialisation utilis par BinaryFormatter en implmentant linterface ISerializable sur lobjet srialiser.

Personnaliser leprocessus desrialisation aveclinterface ISerializable

313

Info Si vous implmentez linterface ISerializable, Microsoft vous recommande de spcifier quand mme explicitement lattribut SerializedAttribute().

Durant la srialisation, la mthode GetObjectData() est automatiquement appele afin de rcuprer les valeurs srialiser. Ces valeurs doivent tre spcifies lobjet SerializationInfo pass en paramtre, en appelant lune des surcharges de la mthode AddValue(). Cette mthode prend en paramtre un nom qui doit tre associ la valeur afin quelle puisse tre identifiable durant le processus de dsrialisation. Limplmentation de ISerializable impose lajout dun constructeur prenant en paramtre un objet de type SerializationInfo et un autre de type Serialization Context. Ce constructeur est automatiquement appel par le processus de dsrialisation lors de la cration de lobjet. Les valeurs srialises doivent tre rcupres via les mthodes commenant par Get de lobjet SerializationInfo pass en paramtre. Le paramtre nom de ces mthodes permet de rcuprer la valeur associe qui a t spcifie au moment de lappel la mthode GetObjectData().
Info Pour srialiser un objet qui nest pas un type primitif, utilisez la surcharge AddValue(string, Object). Pour la dsrialisation, utilisez la mthode GetObject(string).

En implmentant la mthode ISerializable, vous pouvez crer votre propre logique pour srialiser ou dsrialiser les valeurs dune classe. Limplmentation de linterface ISerializable ne permet pas de modifier le format des donnes srialises.

314

CHAPITRE 13 La srialisation

Lexemple qui suit illustre une classe Personne implmentant linterface ISerializable.
[SerializableAttribute()] class Personne : ISerializable { private int age; private string nom; private bool estNouveau; public string Nom { get { return this.nom; } set { this.nom = value; } } public int Age { get { return this.age; } set { this.age = value; } } public bool EstNouveau { get { return this.estNouveau; } set { this.estNouveau = value; } } public Personne() { } // Constructeur utilis pour la dsrialisation private Personne(SerializationInfo info, StreamingContext contexte) { // Dsrialiser lage this.age = info.GetInt32(a); // Dsrialiser le nom this.nom = info.GetString(n); }

Dclarer uneclasse srialisable avec DataContractAttribute (.NET3.0)

315

public void GetObjectData(SerializationInfo info,


StreamingContext context)

{ // Srialiser la valeur de lage info.AddValue(a, this.age); // Srialiser la valeur du nom info.AddValue(n, this.nom); } }

Info Le constructeur utilis pour la dsrialisation peut tre protected si la classe risque dtre hrite.

Dclarer uneclasse srialisable avec DataContractAttribute (.NET3.0)


[DataContract( Name=<Nom du contrat de donnes>, Namespace=<Espace de noms>)] class <nom de la classe> { // Champs srialisables [DataMember( EmitDefaultValue=<Srialiser la valeur par dfaut> Name=<Nom du champ>, IsRequired=<Est requis> Order=<Numro dordre>)] <visibilit> <champ ou proprit>; }

316

CHAPITRE 13 La srialisation

Lattribut DataContractAttribute permet de dclarer une classe qui implmente un contrat de donnes et qui est srialisable via un srialiseur tel que DataContractSerializer. Les contrats de donnes sont trs utiliss pour lchange de donnes dans WCF (Windows Communication Foundation). Ils sont disponibles depuis la version3.0 du .NET Framework. Le srialiseur DataContractSerializer srialise les contrats de donnes en XML (voir la section suivante). Une classe qui implmente un contrat de donnes doit tre prcde de lattribut DataContractAttribute. Cet attribut prend en paramtre le nom du contrat ainsi quun espace de noms (afin de le diffrencier dautres contrats qui auraient le mme nom). Les champs ou proprits de la classe qui doivent tre srialiss sont prcds de lattribut DataMemberAttribute.
Info La srialisation dune proprit consiste appeler le code contenu dans le get. La dsrialisation dune proprit consiste appeler le code contenu dans le set en affectant la valeur dsrialise.

Les proprits de lattribut DataMemberAttribute permettent de spcifier: le nom du membre srialiser; si un membre est requis (IsRequired) durant la dsrialisation. Si cette valeur est dfinie true et si la valeur du membre est absente, alors une exception est dclenche durant la dsrialisation; si la valeur par dfaut dun membre (EmitDefaultValue) doit tre srialise explicitement. Si cette proprit est dfinie false et que le membre srialiser est dfini sa valeur par dfaut, alors aucune valeur ne sera produite durant la srialisation; lordre (Order) dans lequel se trouvent les membres srialiser.

Srialiser et dsrialiser unobjet avecDataContractSerializer (.NET3.0).

317

Lexemple qui suit illustre lutilisation des attributs Data ContractAttribute et DataMemberAttribute afin de dclarer une classe Personne comme un contrat de donnes.
[DataContractAttribute(Name = personne, Namespace =http://gilles.tourreau.fr/livre/GDSCSharp)] class Personne { [DataMemberAttribute(Name = age, IsRequired = false, EmitDefaultValue = false)] private int age; private string nom; [DataMemberAttribute(Name = nom, IsRequired = true, EmitDefaultValue = true)] public string Nom { get { return this.nom; } set { this.nom = value; } } }

Srialiser et dsrialiser unobjet avecDataContractSerializer (.NET3.0).


// Crer une instance dun srialiseur pour // le type spcifi public DataContractSerializer(Type type); // Srialiser un objet dans le flux spcifi public void WriteObject(Stream flux, object objet); // Dsrialiser un objet contenu dans le flux spcifi public object ReadObject(Stream flux);

318

CHAPITRE 13 La srialisation

Le srialiseur DataContractSerializer permet de srialiser et de dsrialiser des classes de contrat de donnes qui sont dfinies laide de lattribut DataContractAttribute. Les instances des classes sont srialises au format XML. Ce format est trs utilis pour lchange de donnes entre application et surtout dans WCF (Windows Communication Foundation). Le code suivant illustre la dclaration dune classe Personne qui sera ensuite srialise et dsrialise laide de DataContractSerializer.
[DataContractAttribute(Name = personne, Namespace =http://gilles.tourreau.fr/livre/GDSCSharp)] class Personne { [DataMemberAttribute(Name = age, IsRequired = false, EmitDefaultValue = false)] private int age; private string nom; private bool estNouveau; [DataMemberAttribute(Name = nom, IsRequired = true, EmitDefaultValue = true)] public string Nom { get { return this.nom; } set { this.nom = value; } } public int Age { get { return this.age; } set { this.age = value; } } public bool EstNouveau { get { return this.estNouveau; } set { this.estNouveau = value; } } }

Srialiser et dsrialiser unobjet avecDataContractSerializer (.NET3.0).

319

Lexemple qui suit instancie la classe prcdente et affecte0 la proprit Age, Gilles TOURREAU la proprit Nom et true la proprit EstNouveau. Cette instance est ensuite srialise dans un flux mmoire laide de la mthode WriteObject(). Le contenu de ce flux mmoire est ensuite rutilis pour effectuer lopration inverse laide de la mthode ReadObject(). Le contenu srialis est affich en dernier sur la console.
DataContractSerializer srialiseur; Personne p; srialiseur = new DataContractSerializer(typeof(Personne)); p = new Personne(); p.Age = 0; p.Nom = TOURREAU Gilles; p.EstNouveau = true; using (MemoryStream ms = new MemoryStream()) { // Srialiser la personne dans le MemoryStream srialiseur.WriteObject(ms, p); // Se mettre au tout dbut du flux ms.Position = 0; // Dsrialiser la personne contenue dans // le MemoryStream p = (Personne)srialiseur.ReadObject(ms);

Console.WriteLine(Nom : {0}, p.Nom); Console.WriteLine(Age : {0}, p.Age); Console.WriteLine(Est nouveau : {0}, p.EstNouveau); // Afficher le contenu du document XML Console.WriteLine(Encoding.UTF8.GetString( ms.ToArray())); }

320

CHAPITRE 13 La srialisation

Le rsultat produit sur la console est le suivant:


Nom : TOURREAU Gilles Age : 0 Est nouveau : False

Remarquez que la valeur du champ estNouveau est false car il na pas t srialis. Lors de la dsrialisation, le champ estNouveau ntant pas dsrialis, il aura comme valeur la valeur par dfaut du type bool. Voici maintenant le code XML gnr par le srialiseur DataContractSerializer.
<personne xmlns=http://gilles.tourreau.fr/livre/GDSCSharp xmlns:i=http://www.w3.org/2001/XMLSchema-instance> <nom>TOURREAU Gilles</nom> </personne>

Remarquez que le champ age na pas t srialis. tant donn quil tait 0 (valeur par dfaut du type int) et que lattribut DataMemberAttribute associ dfinit la proprit EmitDefaultValue false, alors aucune srialisation nest produite pour ce champ. Vous pouvez constater aussi que les proprits Name des attributs DataContractAttribute et DataMemberAttribute permettent de dfinir les noms des lments XML gnrs durant la srialisation (ou analyss durant la dsrialisation).

14
Lintrospection
Lintrospection permet de parcourir les mtadonnes des types .NET. Ainsi, il est possible par programmation de lister les membres dun type, de connatre sa classe de base, ses interfaces implmentes, ses paramtres de type gnrique, etc. Lintrospection permet aussi dinstancier dynamiquement des types et dutiliser des membres sur ces instances (par exemple linvocation dune mthode). Lintrospection est trs utilise par des outils dexploration de code (Visual Studio par exemple), mais aussi pour utiliser des types sans les connatre lavance. Cest le cas des mcanismes de plugins; les types ne sont pas connus la compilation mais uniquement lexcution. Lutilisation de lintrospection pour lexcution de code (par exemple lappel dune mthode) peut tre coteuse en temps contrairement du code compil. De plus, lintrospection rend le code beaucoup moins typ, plus difficile lire et certaines erreurs doivent tre testes lexcution et non la compilation (par exemple lappel dune mthode inexistante). Lintrospection doit donc tre utilise avec parcimonie. Dans .NET, les types sont contenus dans des conteneurs physiques appels assembly.

322

CHAPITRE 14 Lintrospection

Info Toutes les classes contenant les fonctionnalits dintrospection se trouvent dans lespace de noms System.Reflection. En anglais, le terme introspection est traduit par reflection . Beaucoup de livres et darticles en franais traduisent de manire inadapte ce terme par rflexion.

Rcuprer ladescription duntype


Type <type>; // Obtenir la dclaration dun type partir // dune instance <type> = <instance>.GetType(); // Obtenir la dclaration dun type partir // de son nom <type> = typeof(<nom dun type>); // Obtenir la dclaration dun type partir // dune chane de caractres <type> = Type.GetType(<nom dun type>); // Proprits contenues dans la classe Type // Obtenir le nom du type public string Name { get; } // Obtenir le nom complet de la classe (avec // le namespace) public string FullName { get; } // Obtenir le namespace du type public string Namespace { get; }

Rcuprer ladescription duntype 323

La classe Type du .NET Framework contient toutes les informations sur un type .NET tel quune classe ou une structure. Grce la classe Type, il est possible de rcuprer la liste des constructeurs, mthodes, vnements, proprits et champs contenus dans le type associ. Les proprits Name, Namespace et FullName de la classe Type permettent de rcuprer respectivement le nom, lespace de noms et le nom complet (espace de noms +nom) du type. La mthode GetType() permet de rcuprer une instance de Type qui dcrit le type de linstance o porte la mthode. La mthode GetType() se trouvant dans la classe de base Object, cette mthode est donc accessible par tous les objets. Lexemple suivant illustre lappel de la mthode GetType() sur une chane de caractres. Le nom, lespace de noms et le nom complet du type obtenu sont ensuite affichs sur la console.
Type type; string s; s = Gilles TOURREAU; type = s.GetType(); Console.WriteLine(Name : {0}, type.Name); Console.WriteLine(Namespace : {0}, type.Namespace); Console.WriteLine(Fullname : {0}, type.FullName);

Voici le rsultat produit sur la console correspondant la description de la classe String:


Name : String Namespace : System Fullname : System.String

324

CHAPITRE 14 Lintrospection

La classe Type contient une mthode static GetType() permettant de rcuprer la description dun type partir de son nom. Lexemple qui suit illustre lutilisation de cette mthode:
Type type; type = Type.GetType(System.Int32); Console.WriteLine(Name : {0}, type.Name); Console.WriteLine(Namespace : {0}, type.Namespace); Console.WriteLine(Fullname : {0}, type.FullName);

Voici le rsultat produit sur la console correspondant la description de la classe Int32:


Name : Int32 Namespace : System Fullname : System.Int32

Loprateur typeof permet de rcuprer la description dun type en spcifiant directement le nom de celui-ci. Le nom du type est contrl la compilation (comme pour la dclaration dune variable). Lexemple suivant illustre lutilisation de cet oprateur qui produit le mme rsultat que lexemple prcdent.
Type type; type = typeof(Int32); Console.WriteLine(Name : {0}, type.Name); Console.WriteLine(Namespace : {0}, type.Namespace); Console.WriteLine(Fullname : {0}, type.FullName); Info La mthode GetType() de la classe Object et le mot-cl typeof retournent toujours une instance de classe Type. Il nest donc pas ncessaire de contrler si ces deux oprations retournent une rfrence null.

Rcuprer ladescription dunassembly 325

Rcuprer ladescription dunassembly


Assembly <instance>; // Rcuprer lassembly contenant la mthode // de dmarrage (Main()) <instance> = Assembly.GetEntryAssembly(); // Rcuprer lassembly contenant la mthode en cours // dexcution <instance> = Assembly.GetExecutingAssembly(); // Rcuprer lassembly contenant la mthode qui // a appel la mthode courante <instance> = Assembly.GetCallingAssembly(); // Charger lassembly spcifi <instance> = Assembly.LoadForm(string fichier); // Description de la classe Assembly class Assembly { // Obtenir le nom complet de lassembly public string FullName { get; } // Obtenir lemplacement de lassembly public string Location { get; } // Obtenir tous les types contenus dans lassembly public Type[] GetTypes(); }

Un assembly est un fichier contenant plusieurs classes compiles. Les assemblys portent par dfaut lextension .dll, et les assemblys excutables (par exemple une application console) se terminent par lextension .exe.

326

CHAPITRE 14 Lintrospection

Les assemblys sont reprsents par des instances de la classe Assembly. Trois mthodes static permettent de rcuprer les Assembly actuellement chargs. La mthode static GetEntryAssembly() permet de rcuprer lassembly qui contient la mthode de dmarrage de lapplication (par exemple la mthode static Main() pour une application console). La mthode static GetCallingAssembly() permet de rcuprer lassembly contenant la mthode qui a effectu lappel de la mthode courante. La mthode static GetExecutingAssembly() permet de rcuprer lassembly contenant la mthode en cours dexcution. Il est possible de charger un assembly prsent sur un disque en utilisant la mthode LoadFrom(), en spcifiant en paramtre le chemin complet du fichier charger. Cette mthode ne recharge pas lassembly sil a dj t charg. Dans ce cas, la mthode LoadFrom() retourne linstance de lassembly dj charg. Si lassembly fait rfrence dautres Assembly qui nont pas t chargs, le .NET Framework soccupe de les charger automatiquement. Une fois quune instance de la classe Assembly a t rcupre, il est possible dobtenir le nom et lemplacement de lassembly associ laide des proprits FullName et Location. La mthode GetTypes() permet de retourner un tableau contenant des instances de type Type, reprsentant la description de toutes les classes contenues dans lassembly. Lexemple suivant illustre laffichage des informations sur lassembly en cours dexcution ainsi que les diffrents types quil contient.
Assembly a; // Rcuprer lassembly o se trouve la mthode Main() a = Assembly.GetExecutingAssembly();

Rcuprer et appeler unconstructeur 327

// Afficher les informations sur lassembly Console.WriteLine(a.FullName); Console.WriteLine(a.Location); // Afficher tous les types contenu dans lassembly Console.WriteLine(*****); foreach (Type t in a.GetTypes()) { Console.WriteLine(t.FullName); }

Rcuprer et appeler unconstructeur


// Rcuprer un constructeur particulier dun type ConstructorInfo <constructeur>; <constructeur> = <type>.GetConstructor( <types paramtres>); // Rcuprer tous les constructeurs dun type ConstructorInfo[] <constructeurs>; <constructeurs> = <type>.GetConstructors(); // Appeler le constructeur object <instance>; <instance> = <constructeur>.Invoke(<paramtres>); // Obtenir des informations sur les paramtres ParameterInfo[] <paramtres>; <paramtres> = <constructeur>.GetParameters(); // Proprits contenues dans la classe ParameterInfo // Obtenir le nom du paramtre public string Name { get; } // Obtenir le type du paramtre public Type ParameterType { get; }

328

CHAPITRE 14 Lintrospection

La classe Type contient une mthode GetConstructor() permettant de rcuprer la description dun constructeur du type associ. tant donn quil peut exister plusieurs surcharges de constructeurs, la mthode GetConstructor() prend en paramtre un tableau qui contient les diffrents Type de chaque paramtre. Cela permet au .NET Frame work de trouver et rcuprer la bonne surcharge du constructeur demand. Le constructeur obtenu est dcrit dans la classe ConstructorInfo Toutes les descriptions des constructeurs dun type peuvent tre rcupres laide de la mthode GetConstructors(). La classe ConstructorInfo contient une mthode GetPara meters() permettant de rcuprer un tableau dcri vant la liste des paramtres requis par le constructeur. La description dun paramtre se trouve dans la classe ParameterInfo. Elle contient deux proprits Name et ParameterType permettant de rcuprer respectivement le nom et le type du paramtre dcrit. Lexemple suivant illustre la rcupration du constructeur de la classe Personne prenant en paramtre un type string (le nom de la personne) et un type int (lge de la personne). Une description des paramtres du constructeur est ensuite affiche sur la console. Voici la dfinition de la classe Personne.
class Personne { public Personne() { } public Personne(string nom, int age) { Console.WriteLine(Construction dune personne); Console.WriteLine(Nom = {0} ; age = {1}, nom, age); } }

Rcuprer et appeler unconstructeur 329

Voici maintenant le code permettant de rcuprer le constructeur de la classe Personne.


Type t; ConstructorInfo constructeur; t = typeof(Personne); // Rcupration du constructeur Personne(string, int) constructeur = t.GetConstructor( new Type[] { typeof(string), typeof(int) }); // Affichage de la description des paramtres foreach (ParameterInfo p in constructeur.GetParameters()) { Console.WriteLine(Nom (Type) : {0} ({1}), p.Name, p.ParameterType.FullName); }

Le rsultat produit sur la console est le suivant:


Nom (Type) : nom (System.String) Nom (Type) : age (System.Int32)

Une fois une instance ConstructorInfo obtenue, il est possible dinvoquer le constructeur associ, en utilisant la mthode Invoke(), afin de construire une instance du type associ. La mthode Invoke() prend en paramtre un tableau dobjets contenant les paramtres passer au constructeur et retourne un objet instanci du type associ. Lexemple suivant illustre lappel du constructeur de la classe Personne prenant en paramtre le nom et lge de celui-ci.
Type t; Personne p; ConstructorInfo constructeur; t = typeof(Personne);

330

CHAPITRE 14 Lintrospection

// Rcupration du constructeur Personne(string, int) constructeur = t.GetConstructor( new Type[] { typeof(string), typeof(int) }); // Instanciation dune Personne p = (Personne)constructeur.Invoke( new object[] { TOURREAU, 26 });

Le rsultat produit sur la console est le suivant:


Vous venez de construire une personne Nom = TOURREAU; age = 26

Instancier unobjet partir deson Type


// Instancier un objet partir de son Type object <instance> = Activator.CreateInstance(<type>);

La classe Activator du .NET Framework contient une mthode static CreateInstance() permettant dinstancier un objet en utilisant son constructeur sans paramtre. Cette mthode permet de simplifier lcriture dune instanciation dynamique dun objet, en vitant de rechercher par introspection le constructeur invoquer. Lexemple suivant illustre la cration dune instance de la classe Personne.
Personne p; p = (Personne)Activator.CreateInstance(typeof(Personne));

Rcuprer et appeler unemthode

331

Rcuprer et appeler unemthode


// Obtenir une mthode particulire dun type MethodInfo <mthode>; <mthode> = <type>.GetMethod(<nom>, <types paramtres>); // Obtenir toutes les mthodes dun type MethodInfo[] <mthode>; <mthode> = <type>.GetMethods(); // Proprits contenues dans la classe MethodInfo // Obtenir le type de retour de la mthode public Type ReturnType { get; } // Obtenir le nom de la mthode public string Name { get; } // Appeler la mthode <mthode>.Invoke(<objet>, <paramtres>); // Obtenir des informations sur les paramtres ParameterInfo[] <paramtres>; <paramtres> = <constructeur>.GetParameters(); // Proprits contenues dans la classe ParameterInfo // Obtenir le nom du paramtre public string Name { get; } // Obtenir le type du paramtre public Type ParameterType { get; }

La classe Type contient une mthode GetMethod() permettant de rcuprer la description dune mthode du type associ. tant donn quil peut exister plusieurs surcharges

332

CHAPITRE 14 Lintrospection

dune mthode de mme nom, la mthode GetMethod() prend en paramtre un tableau qui contient les diffrents Type de chaque paramtre. Cela permet au .NET Framework de rcuprer la bonne surcharge de la mthode demande. La mthode obtenue est dcrite dans la classe MethodInfo Toutes les descriptions des mthodes dun type peuvent tre obtenues laide de la mthode GetMethods(). La classe MethodInfo contient une mthode GetParameters() permettant de rcuprer un tableau dcrivant la liste des paramtres requis par la mthode. La description dun paramtre se trouve dans la classe ParameterInfo. Elle contient deux proprits Name et ParameterType permettant de rcuprer respectivement le nom et le type du paramtre dcrit. Lexemple suivant illustre la rcupration de la mthode GetNom() de la classe Personne prenant en paramtre un type string (message afficher). Une description de la mthode ainsi que les paramtres associs sont ensuite affichs sur la console. Voici la dfinition de la classe Personne.
class Personne { private string nom; public Personne(string nom) { this.nom = nom; } public string GetNom(string message) { Console.WriteLine(message, this.nom); return nom; } }

Rcuprer et appeler unemthode 333

Voici maintenant le code permettant de rcuprer la mthode en question de la classe Personne.


Type t; MethodInfo mthode; t = typeof(Personne); // Rcupration de la mthode GetNom(string) mthode = t.GetMethod(GetNom, new Type[] { typeof(string) }); // Affichage des informations sur la mthode Console.WriteLine(Nom : {0}, mthode.Name); Console.WriteLine(Retourne : {0}, mthode.ReturnType.FullName); // Affichage de la description des paramtres foreach (ParameterInfo p in mthode.GetParameters()) { Console.WriteLine(Paramtre (Type) : {0} ({1}), p.Name, p.ParameterType.FullName); }

Le rsultat produit sur la console est le suivant:


Nom : GetNom Retourne : System.String Paramtre (Type) : message (System.String)

Une fois une instance MethodInfo obtenue, il est possible dinvoquer la mthode associe, en utilisant la mthode Invoke(). La mthode Invoke() prend en paramtre lobjet sur lequel sera effectu lappel (null si la mthode est une mthode static) ainsi quun tableau dobjets contenant les paramtres passer la mthode. La mthode Invoke() retourne la valeur retourne par la mthode appele. Lexemple suivant illustre lappel de la mthode GetNom() de la classe Personne prenant en paramtre le message

334

CHAPITRE 14 Lintrospection

afficher. La valeur retourne est rcupre et affiche sur la console.


Type t; MethodInfo mthode; Personne p; string valeurRetour; t = typeof(Personne); // Rcupration de la mthode GetNom(string) mthode = t.GetMethod(GetNom, new Type[] { typeof(string) }); // Cration dune personne p = new Personne(TOURREAU); // Appel de la mthode GetNom() valeurRetour = (string)mthode.Invoke(p, new object[] { Mon nom est : {0} }); Console.WriteLine(Valeur de retour : {0}, valeurRetour);

Le rsultat produit sur la console est le suivant:


Mon nom est : TOURREAU Valeur de retour : TOURREAU

Dfinir et appliquer unattribut


// Dfinir une classe attribut [AttributeUsage(AttributeTargets application, AllowMultiple=true|false)] class <nom attribut>Attribute: Attribute { // Membres de lattribut }

Dfinir et appliquer unattribut 335

// Elments d<application> des attributs: AttributeTargets.Assembly // Assembly AttributeTargets.Class // Classe AttributeTargets.Struct // Structure AttributeTargets.Constructor // Constructeur AttributeTargets.Method // Mthode AttributeTargets.Property // Proprit AttributeTargets.Field // Champ AttributeTargets.Event // vnement AttributeTargets.Interface // Interface AttributeTargets.All // Tout // Appliquer un attribut [<nom attribut>Attribute([<paramtres constructeur>][, <proprit>=<valeur>,...])] // Application dun attribut sur un assembly [assembly: <nom attribut>Attribute( [<paramtres constructeur>] [,<proprit>=<valeur>,...])]

Les attributs en .NET permettent dajouter des mtadonnes aux assembly, classes, structures, mthodes, constructeurs, proprits, champs et vnements. Ces attributs peuvent tre rcuprs durant lexcution laide du mcanisme dintrospection. La cration dun attribut consiste crer une classe qui hrite dAttribute. Il est possible dajouter des proprits dans la classe cre afin de pouvoir rcuprer les valeurs associes au moment de lintrospection. Un attribut sapplique par dfaut tous les lments de programmation du .NET cits prcdemment. Cependant, il est possible de restreindre lutilisation dun attribut sur un ou plusieurs lments de programmation en appliquant lattribut AttributeUsage la classe de lattribut personnalise. Le constructeur de cette classe prend en paramtre

336

CHAPITRE 14 Lintrospection

une ou plusieurs constantes de lnumration Attribute Targets reprsentant les lments de programmation restreindre. Par dfaut, un attribut peut tre appliqu plusieurs fois sur un lment de programmation. Pour appliquer un attribut quune seule fois, il suffit de dfinir true la proprit AllowMultiple de lattribut AttributeUsage. Lexemple suivant illustre la cration dun attribut Valida tionAttribute qui sapplique uniquement aux proprits des types. Cet attribut permet dassocier une proprit un message de validation si la proprit est null. Ce message est spcifi au niveau du constructeur de lattribut. Une proprit en lecture et criture permet de dfinir si ncessaire la longueur minimale de la chane de caractre.
// Lattribut est utilisable uniquement sur // les proprits et il nest pas possible // den spcifier plusieurs [AttributeUsage(AttributeTargets.Property, AllowMultiple = false)] class ValidationAttribute : Attribute { private string message; private int longueurMinimum; public ValidationAttribute(string message) { this.message = message; } public string Message { get { return this.message; } } public int LongueurMinimum {

Dfinir et appliquer unattribut 337

get { return this.longueurMinimum; } set { this.longueurMinimum = value; } } }

Pour appliquer un attribut, il suffit de le spcifier entre crochets avant llment de programmation concern. Lapplication dun attribut produira son instanciation au moment de son introspection. Cette instanciation est ralise en utilisant lun des constructeurs dont les paramtres doivent tre spcifis entre parenthses. Lexemple suivant illustre lapplication de lattribut cr prcdemment dans une proprit Nom. Lattribut Valida tionAttribute contenant un constructeur avec un paramtre, il est donc ncessaire de spcifier ce paramtre lors de lapplication de lattribut.
[ValidationAttribute(Le nom est requis)] public string Nom { get { return this.nom; } set { this.nom = value; } }

Lapplication dun attribut sur un assembly doit tre prcde du mot-cl assembly. Ces attributs sont le plus souvent contenus dans un fichier appel AssemblyInfo.cs, contenant des informations sur un assembly. Lexemple suivant illustre lapplication de lattribut Assem blyVersion sur un assembly:
[assembly: AssemblyVersion(1.0.0.0)]

Un attribut peut contenir des proprits en criture. Ces proprits peuvent tre dfinies au moment de lapplication

338

CHAPITRE 14 Lintrospection

de lattribut en spcifiant le nom de la proprit suivi de sa valeur. Lexemple suivant illustre lapplication de lattribut Valida tionAttribute en dfinissant la valeur10 la proprit LongueurMinimum.
[ValidationAttribute(Le nom est requis, LongueurMinimum=10)] public string Nom { get { return this.nom; } set { this.nom = value; } }

Rcuprer des attributs


// Obtenir les attributs dun objet dintrospection object[] <attributs>; <attributs> = <lment programmation>. GetCustomAttributes( Type typeAttributs, bool attributsHrits);

Pour rcuprer les attributs dun objet dintrospection (par exemple une proprit), il suffit dappeler la mthode GetCustomAttributes() sur llment de programmation concern (par exemple MethodInfo). Cette mthode prend en paramtre une instance Type correspondant au type des attributs rcuprer. Si un objet dintrospection est hrit dans un type, il est possible de spcifier laide du paramtre attributsHrits que les attributs hrits doivent aussi tre rcuprs. La mthode GetCustomAttributes() provoque linstanciation des attributs et retourne tous les attributs correspondant aux paramtres spcifis dans un tableau dobject. Linstanciation dun attribut est ralise quune seule fois durant toute la vie de lapplication.

Rcuprer des attributs 339

Lexemple suivant illustre la rcupration dun attribut ValidationAttribute applique sur une proprit. Les valeurs de ces proprits sont ensuite affiches sur la console. Voici la dfinition de lattribut ValidationAttribute.
[AttributeUsage(AttributeTargets.Property, AllowMultiple = false)] class ValidationAttribute : Attribute { private string message; private int? longueurMinimum; public ValidationAttribute(string message) { this.message = message; } public string Message { get { return this.message; } } public int? LongueurMinimum { get { return this.longueurMinimum; } set { this.longueurMinimum = value; } } }

Voici maintenant un exemple dapplication de lattribut ValidationAttribute.


public class Personne { [ValidationAttribute(Le nom est requis, LongueurMinimum = 10)] public string Nom {

340

CHAPITRE 14 Lintrospection

get { return this.nom; } set { this.nom = value; } } }

Et enfin le code permettant de rcuprer lattribut Vali dationAttribute appliqu la proprit Nom de la classe Personne.
PropertyInfo propritNom; object[] attributs; ValidationAttribute validationAttribute; // Rcupration de la proprit Nom propritNom = typeof(Personne).GetProperty(Nom); // Rcuprer les attributs de la proprit // Nom de type ValidationAttribute attributs = propritNom.GetCustomAttributes( typeof(ValidationAttribute), true); // Vrifier quau moins un attribut a t rcupr if (attributs.Length > 0) { // Vrifier que lattribut rcupr est de type // ValidationAttribute validationAttribute = attributs[0] as ValidationAttribute; if (validationAttribute != null) { Console.WriteLine(Longueur minimum : {0}, validationAttribute.LongueurMinimum); Console.WriteLine(Message : {0}, validationAttribute.Message); } }

Lemot-cl dynamic (C#4.0)

341

Le rsultat produit sur la console est le suivant:


Longueur minimum : 10 Message : Le nom est requis

Lemot-cl dynamic (C#4.0)


dynamic <instance>;

Le mot-cl dynamic permet deffectuer des oprations sur du code qui ne seront pas contrles la compilation mais uniquement lexcution. Par exemple, il est possible dappeler une mthode M() sur une variable dynamic sans connatre lavance lobjet rfrenc. Il nest donc plus ncessaire dintrospecter les types afin dy rechercher et dinvoquer dynamiquement des membres. lexcution, laccs un membre indfini sur une instance dune variable dynamique lve une exception de type RuntimeBinderException.
Attention Lutilisation du mot-cl dynamic, comme pour lintrospection, rend votre code beaucoup moins typ. Ainsi, les erreurs sur les noms des membres devront tre contrles durant lexcution de lapplication et non au moment de sa compilation. vitez donc dabuser de lutilisation du mot-cl dynamic.

Lexemple suivant illustre lutilisation du mot-cl dynamic en faisant appel une mthode Avancer() contenue dans un objet dont le type sera connu lexcution.
class Personne { public void Avancer() { Console.WriteLine(Je marche !);

342

CHAPITRE 14 Lintrospection

} } class Voiture { public void Avancer() { Console.WriteLine(Vrooum !); } }

Le code suivant illustre maintenant lutilisation de ces deux classes laide du mot-cl dynamic.
dynamic o; Console.WriteLine(Voulez-vous crer une Personne ?); if (Console.ReadLine() == O) { o = new Personne(); } else { o = new Voiture(); } o.Avancer();

Dans lexemple prcdent, si lutilisateur rpond O la question, une instance de type Personne est cre sinon une instance de type Voiture lest. Dans tous les cas, la mthode Avancer() est appele sur lobjet instanci. Si maintenant, on change lappel de la mthode Avancer() par:
o.ExistePas();

le code prcdent compilera sans aucun problme, mais lexcution, une erreur de type RuntimeBinderException sera dclenche.

Index
Symboles
^ 25 ^= 25 -= 23 vnement 58 ! 24 != 24 ? condition 13 structure nullable 217 ?? 90 @ 164 * 23 *= 23 / 23 /= 23 \ 164 \\ 164 & 25 && 24 &= 25 % 23 + 23 concatner deux chanes de c aractres 170 += 23 vnement 58 < 24 << 25 <= 24 == 24 => 54 > 24 >= 24 >> 25
| 25 |= 25 || 24

~ 25 $$ : (condition) 13

A
abstract (mot-cl) 118 Accesseur 40, 44 Action<...> (dlgu) 152 Activator (classe) 330 Addition 23 Add() (mthode List<T>) 240 Affecter une variable 8 Anonyme, mthode 52 ANSI (encodage) 180 Any() (mthode, LINQ) 198 Appel, constructeur 38 Appeler constructeur de base 105 mthode 33

344

Arithmtique, oprateur

Arithmtique, oprateur 23 Array (classe) 205 Clear() (mthode) 205 Copy() (mthode) 205 Exists() (mthode) 205 FindAll() (mthode) 205 FindIndex () (mthode) 205 FindLastIndex() (mthode) 205 FindLast() (mthode) 205 Find() (mthode) 205 ForEach() (mthode) 205 Length (proprit) 205 Rank (proprit) 205 Sort() (mthode) 205 ascending (mot-cl) 188 ASCII (encodage) 180 as (mot-cl) 124 Assembly (classe) 325 AsyncCallBack (dlgu) 302 Attribut dfinir 334 introspection 334, 338 rcuprer 338 Attribute (classe) 338 AttributeUsage (attribut) 334

break (mot-cl) boucle 16 switch 13 Buffer (classe) 228 byte (type) 10

C
C# 1 mots-cls 8 Capturer une exception 128 Caractre 163 rcuprer dans une chane 166 case (mot-cl) 13 cast (oprateur) 71, 99, 123 catch (mot-cl) 128, 132 Chane de caractres 163 comparer 167 concatner 170 crer 164 avec StringBuilder 178 dcoder 180 encoder 180 extraire 171 formater 174 longueur 166 rechercher 172 rcuprer un caractre 166 Champ 31 en lecture seule 39 numration 75 char (type) 10, 166 Classe 27, 97 abstraite 118 anonyme 82 dclarer 28 comme srialisable 308 dlgu 50 numration 75, 209 gnrique 143 imbrique 78 instancier 28 introspection 322

INDEX

B
base (mot-cl) 100, 103, 105 BeginInvoke() (mthode) dlgu 302 vnement 57 BinaryFormatter (classe) 309 BinaryReader (classe) 262 BinaryWriter (classe) 260 BitConverter (classe) 226 bool (type) 10 Boucle 16 dowhile 16 for 16 foreach 231 instruction break 16 instruction continue 16 while 16

Dclaration

345

partielle 80 scelle 122 statique 34 Clear() (mthode Array) 205 Clone() (mthode IClonable) 222 Collection dictionnaire 243 file 247 initialiser 249 itrateur 231 liste 240 pile 246 Commentaires 6 Concat (mthode String) 170 Condition if 13 switch 13 Constante 12 numration 75 Constructeur 38 appeler le constructeur de base 105 introspection 327 surcharge 66 ConstructorInfo (classe) 327 continue (mot-cl) 16 Contrainte, paramtre gnrique 149 Contravariance 159 Convertir depuis des octets 226 en octets 226 Copier fichier 265 objet 223 Copy() (mthode Array) 205 Count() (mthode LINQ) 193 Count (proprit Dictionary<TCl, TValeur>) 243 Count (proprit List<T>) 240 Count (proprit Queue<T>) 247 Count (proprit Stack<T>) 246

Covariance 154 CreateInstance() (mthode Activator) 330 Crer rpertoire 268 thread 282 variable 8 CurrentThread (proprit Thread) 287

D
DataContractAttribute (attribut) 315 DataContractSerializer (classe) 317 DataMemberAttribute (attribut) 315 Date (classe DateTime) 214 DateTime (classe) 214 decimal (type) 10 Dclaration champ 31 classe gnrique 143 partielle 80 scelle 122 constante 12 constructeur 38 dlgu 50 numration 75 vnement 57 indexeur 48 interface 112 mthode 33 anonyme 52 dextension 94 gnrique 147 partielle 92 paramtre 62 proprit 40, 44 structure 83 tableau 19 type anonyme 82 variable 8 de porte (LINQ) 198

INDEX

346

Dclencher

Dclencher vnement 57 exception 127 Dcrmentation post-dcrmentation 23 pr-dcrmentation 23 default (mot-cl) gnrique 151 switch 13 delegate (mot-cl) 50, 52 dlgu 57 classe 50 gnrique 152 mthode asynchrone 302 Dequeue() (mthode Queue<T>) 247 descending (mot-cl) 188 Dsrialisation 307 binaire 309 personnaliser 312 XML 315 Dsrialiseur binaire 309 XML 317 Dictionary<TCl, TValeur> (classe) 243 Count (proprit) 243 Dictionnaire 243 DirectoryInfo (classe) 275 Dispose() (mthode IDisposable) 219 Division 23 double (type) 10 do...while (mot-cl) 16 DriveInfo (classe) 277 Dure (classe TimeSpan) 212 dynamic (mot-cl) 341

Encoding (classe) 180 GetBytes() (mthode) 180 GetEncoding() (mthode) 180 GetString() (mthode) 180 EndInvoke() (mthode), dlgu 302 Enqueue() (mthode Queue<T>) 247 Enum (classe) 209 numration 75, 209 enum (mot-cl) 75 Equals() (mthode Object 201 equals (mot-cl) 189 Erreur, gestion 125 Espace de noms 29 ET logique 24 vnement 57 asynchrone 57 event (mot-cl) 57 Exception 125 dclencher 127 propager 136 traiter 128 Exception (classe) 134 GetBaseException() (mthode) 134 InnerException (proprit) 134 Message (proprit) 134 StackTrace (proprit) 134 Exists() (mthode Array) 205 explicit (oprateur) 68 Expression lambda 54 parenthsage 25

INDEX

F
Fichier copier 265 informations 272 ouvrir 265 supprimer 265 File (classe) 265 File dobjets 247 FileInfo (classe) 272

E
chappement, caractre 164 else (mot-cl) 13

Indexeur

347

FileStream (classe) 253 Filtrer, requte LINQ 186 finally (mot-cl) 132 FindAll() (mthode Array) 205 FindAll() (mthode List<T>) 240 FindIndex() (mthode Array) 205 FindLastIndex() (mthode Array) 205 FindLast() (mthode Array) 205 FindLast() (mthode List<T>) 240 Find() (mthode Array) 205 Find() (mthode List<T>) 240 Flags (attribut) 75 float (type) 10 Flux 251 dun fichier 253 crire 256 en binaire 260 lire 258 en binaire 262 mmoire 255 ForEach() (mthode Array) 205 foreach (mot-cl) 184, 231 Formater une chane de caractres 174 Format() (mthode String) 174 for (mot-cl) 16 from (mot-cl) 184 Func<...> (dlgu) 152 fusion null (oprateur) 90

GetBytes() (mthode Encoding) 180 GetCustomAttributes() (mthode) 338 GetEncoding() (mthode Encoding) 180 get (mot-cl) 40, 44 GetRange() (mthode List<T>) 240 GetString() (mthode Encoding) 180 GetType() (mthode Object) 322 group...by (mot-cl) 194

H
Hritage 97 Heure (classe DateTime) 214

I
IAsyncResult (interface) 302 IClonable (interface) 222 Clone() (mthode) 222 Identificateur 7 IDisposable (interface) 219 Dispose() (mthode) 219 IEnumerable (interface) 231 IEnumerable<T> (interface) 184, 231 IEnumerator (interface) 231 IEnumerator<T> (interface) 231 if (mot-cl) 13 IGroupingKey<TCl, T> (interface) 194 Key (proprit) 194 Imbriquer des classes 78 Implmentation interface 113 interface explicite 116 implicit (oprateur) 68 Incrmentation post-incrmentation 23 pr-incrmentation 23 Indexeur 48

INDEX

G
Gnrique 141 classe 143 contrainte 149 contravariance 159 covariance 154 default 151 dlgu 152 mthode 147 GetBaseException() (mthode Exception) 134

348

IndexOf() (mthode List<T>)

IndexOf() (mthode List<T>) 240 IndexOf() (mthode String) 172 in (mot-cl) contravariance 159 LINQ 184 InnerException (proprit Exception) 134 Insert() (mthode List<T>) 240 Instance 27 courante 36 Instanciation dune classe 28 dun objet 46 Interface dclaration 112 implmentation 113 explicite 116 internal (mot-cl) 37 into (mot-cl) 194 Introspection 321 attribut 334, 338 constructeur 327 instancier un objet 330 mthode 331 int (type) 10 ISerializable (interface) 312 is (mot-cl) 123 Itrateur 231

L
Lambda, expression 54 LastIndexOf() (mthode List<T>) 240 LastIndexOf() (mthode String) 172 Lecteur, informations 277 Length (proprit Array) 205 Length (proprit String) 166 let (mot-cl) 198 Librer des ressources 219 LINQ 183 Any() (mthode) 198 compter le nombre dobjets 193 Count() (mthode) 193 dterminer si une squence contient un objet 198 filtrer 186 grouper des objets 194 jointure 189 rcuprer dernier objet 191 premier objet 191 slectionner des objets 184 somme 194 Sum() (mthode) 194 trier 188 variable de porte 184, 198 Liste 240 List<T> (classe) 240 Add() (mthode) 240 Count (proprit) 240 FindAll() (mthode) 240 FindLast() (mthode) 240 Find() (mthode) 240 GetRange() (mthode) 240 IndexOf() (mthode) 240 Insert() (mthode) 240 LastIndexOf() (mthode) 240 RemoveAt() (mthode) 240 Remove() (mthode) 240 Sort() (mthode) 240 ToArray() (mthode) 240 lock (mot-cl) 297 Logique, oprateur 24 long (type) 10

INDEX

J
join (mot-cl) 189 Jointure, LINQ 189

K
Key (proprit) IGroupingKey<TCl, T> (interface) 194

override (mot-cl)

349

M
Main() (mthode) 5 Masquer mthode 106 proprit 109 MemberwiseClone() (mthode Object) 222 Membre 27 statique 34 visibilit 37 MemoryStream (classe) 255 Message (proprit Exception) 134 Mthode 33 abstraite 118 anonyme 52 appel asynchrone 302 dextension 94 gnrique 147 introspection 331 masquer 106 partielle 92 redfinir 100 statique 34 surcharge 60 MethodInfo (classe) 331 Modulo 23 Moniteur 297 Monitor (classe) 297 Multiplication 23 Mutex 294 Mutex (classe) 294

NON logique 24 null 217 Nullable<T> (classe) 217 Value (proprit) 217 null (mot-cl) 28, 90

O
Object (classe) 97, 201 Equals() (mthode) 201 GetType() (mthode) 322 MemberwiseClone() (mthode) 222 ReferenceEquals() (mthode) 201 ToString() (mthode) 201 object (mot-cl) 201 Objet 27 copie, clonage 222 instancier 38, 46, 330 Octet codage 180 conversion 226 flux 251 type byte 11 Oprateur 68 arithmtique 23 as 124 binaire 25 cast 68, 97, 123 ET logique 24 is 123 logique 24 NON logique 24 OU logique 24 surcharge 68 operator (mot-cl) 68 orderby (mot-cl) 188 OU logique 24 out (mot-cl) covariance 154 paramtre 87 Ouvrir un fichier 265 override (mot-cl) 100, 103

INDEX

N
namespace (mot-cl) 29 new (mot-cl) instanciation dune classe 28 masquage dune mthode 106 dune proprit 109 Niveau de visibilit 37

350

Paramtre

P
Paramtre 33 de type 143 facultatif 62 gnrique 143 nomm 64 par rfrence 87 par valeur 87 partial (mot-cl) 80, 92 Peek() (mthode Queue<T>) 247 Peek() (mthode Stack<T>) 246 Pile 246 Polymorphisme 97, 120, 155 Pop() (mthode Stack<T>) 246 private (mot-cl) 37 Programmation oriente objet 27 Projection, LINQ 184 Proprit 40 abstraite 118 get (accesseur) 40, 44 implmente automatiquement 44 indexeur 48 initialiser 46 masquer 109 redfinir 103 set (accesseur) 40, 44 statique 34 protected internal (mot-cl) 37 protected (mot-cl) 37 public (mot-cl) 37 Push() (mthode Stack<T>) 246

R
Rank (proprit Array) 205 readonly (mot-cl) 39 Redfinir mthode 100 proprit 103 ReferenceEquals() (mthode Object) 201 Reflection 321 ref (mot-cl) 87 RemoveAt() (mthode List<T>) 240 Remove() (mthode List<T>) 240 Rpertoire crer 268 informations 275 obtenir le rpertoire courant 268 les fichiers 268 les rpertoires 268 supprimer 268

INDEX

S
sbyte (type) 10 sealed (mot-cl) 122 Smaphore 290 Semaphore (classe) 290 Srialisation 307 binaire 309 personnaliser 312 XML 315 Srialiseur binaire 309 XML 317 SerializableAttribute (attribut) 308 set (mot-cl) 40, 44 short (type) 10 Sleep() (mthode Thread) 284

Q
Queue<T> (classe) 247 Dequeue() (mthode) 247 Enqueue() (mthode) 247 Peek() (mthode) 247

try (mot-cl)

351

Somme 23 requte LINQ 194 Sort() (mthode Array) 205 Sort() (mthode List<T>) 240 Soustraction 23 Stack<T> (classe) 246 Peek() (mthode) 246 Pop() (mthode) 246 Push() (mthode) 246 StackTrace (proprit Exception) 134 Start() (mthode Thread) 282 static (mot-cl) 34 champ (propre chaque thread) 288 mthode dextension 94 Stopwatch (classe) 285 Stream (classe) 252 StreamReader (classe) 258 StreamWriter (classe) 256 StringBuilder (classe) 178 String (classe) 163 Concat (mthode) 170 Format() (mthode) 174 IndexOf() (mthode) 172 LastIndexOf() (mthode) 172 Length (proprit) 166 Substring() (mthode) 171 string (mot-cl) 163 struct (mot-cl) 83 Structure 83 nullable 217 Substring() (mthode String) 171 Sum() (mthode LINQ) 194 Supprimer fichier 265 rpertoire 268 Surcharge constructeur 66 mthode 60 oprateur 68 switch (mot-cl) 13 System 5

T
Tableau 205 copier des octets 228 de caractres 163 dynamique (liste) 240 en escalier 21 multidimensionnel 20 rechercher un lment 205 taille en octets 228 trier 205 unidimensionnel 19 Test 13 this (mot-cl) appel dun autre constructeur 66 instance courante 36 Thread 281 attendre la fin 285 courant 287 crer 282 dmarrer 282 mettre en pause 284 variables statiques 288 Thread (classe) 281 CurrentThread (proprit) 287 Sleep() (mthode) 284 Start() (mthode) 282 ThreadStaticAttribute (attribut) 288 throw (mot-cl) dclencher une exception 127 propager une exception 136 TimeSpan (classe) 212 ToArray() (mthode List<T>) 240 ToString() (mthode Object) 201 Traiter une exception 128 Trier liste 240 requte LINQ 188 tableau 205 try (mot-cl) 128, 132

INDEX

352

Type

Type anonyme 82 gnrique 141 primitif 10 valeur 83 nullable 217 Type (classe) 322 typeof (mot-cl) 322

V
Value (proprit Nullable<T>) 217 ValueType (classe) 83 Variable 8 dune classe 31 de porte (LINQ) 198 var (mot-cl) 10 virtual (mot-cl) 100, 103 Visibilit, niveau de 37

U
uint (type) 10 ulong (type) 10 Unicode (encodage) 180 ushort (type) 10 using (mot-cl) avec IDisposable 219 namespace 29 UTF-8 (encodage) 180 UTF-16 (encodage) 180 UTF-32 (encodage) 180

W
where (mot-cl) 186 while (mot-cl) 16

INDEX

LE GUIDE DE SURVIE

C#
LESSENTIEL DU CODE ET DES CLASSES
Ce Guide de survie est loutil indispensable pour programmer efcacement en C# 2.0, 3.0, 3.5 et 4.0 et manipuler la bibliothque des classes du .NET Framework. Il permettra aux dveloppeurs dj familiers de lalgorithmique ou de la programmation oriente objet de sinitier rapidement aux technologies du .NET Framework.

CONCIS ET MANIABLE
Facile transporter, facile utiliser nis les livres encombrants !

PRATIQUE ET FONCTIONNEL
Plus de 100 squences de codes personnalisables pour programmer du C# oprationnel dans toutes les situations. Gilles Tourreau, architecte .NET et formateur dans une socit de services, intervenant actif sur les forums MSDN, sest vu attribuer ces trois dernires annes le label MVP C# (Most Valuable Professional). Retrouvez-le sur http://gilles.tourreau.fr

Niveau : Intermdiaire Catgorie : Programmation

Pearson Education France 47 bis rue des Vinaigriers 75010 Paris Tl. : 01 72 74 90 00 Fax : 01 42 05 22 17 www.pearson.fr

ISBN : 978-2-7440-4163-1

Das könnte Ihnen auch gefallen