You are on page 1of 514

Sauf mention contraire, le contenu de cet ouvrage est publi sous la licence : Creative Commons BY-NC-SA 2.

0 La copie de cet ouvrage est autorise sous rserve du respect des conditions de la licence Texte complet de la licence disponible sur : http://creativecommons.org/licenses/by-nc-sa/2.0/fr/ Simple IT 2012 - ISBN : 978-2-9535278-6-5

Prface

# .NET est le langage de programmation phare de Microsoft. Il a t dvelopp dans le but de pouvoir crer facilement divers types dapplications en tirant le meilleur des produits et technologies Microsoft.

Les crateurs du langage se sont inspirs des langages existants en sattachant retenir le meilleur de chacun deux. Aussi nest-ce pas tonnant de retrouver un typage fort, une approche oriente objet et une syntaxe rappellant la fois celle du C++ et du Java. C# .NET est apparu en 2000 et depuis, ne cesse dvoluer au rythme des direntes versions du Framework .NET. Le couple C# et Framework .NET englobe les dernires avances des langages de programmation (Generic, Lambda, Infrence de type, Linq. . .). Ces amliorations, fortement inspires des langages dits fonctionnels, font de C# un des langages les plus modernes et aboutis, sans que jamais la productivit et la solidit du code ne soient compromis. Aujourdhui, C# .NET est de plus en plus utilis dans le monde professionnel. Sa puissance et son interoprabilit avec les produits et technologies Microsoft font de lui un langage sr et prenne. Ce langage prsente en outre lintrt de ne pas tre propritaire puisque ses spcications permettent de voir apparatre des initiatives (comme par exemple Mono), le code C# pouvant ainsi tourner sur des distributions Linux. Il est possible de dvelopper toutes sortes dapplications : jeux, applications de gestion, interfaces tactiles, XAML ou applications pour tlphones. Noublions pas le monde embarqu avec le Micro Framework .NET ainsi que le Web avec ASP.NET. Bref, C# est un langage tout terrain, ouvrant une gamme de possibles unique sur la plateforme Microsoft. Ce livre se veut simple et facile daccs. Il allie les connaissances de Nicolas Hilaire, spcialiste de ces technologies et MVP Microsoft 1 , avec celles des crateurs du Site du Zro, rputs depuis de nombreuses annes pour leur approche pdagogique et accessible tous les dbutants. Ce livre est donc tout indiqu pour ceux qui veulent se former facilement la programmation C# .NET. ric Mittelette Responsable des relations techniques avec les dveloppeurs chez Microsoft France
1. Most Valuable Professional, expert en technologies Microsoft.

ii

Avant-propos

uand jai commenc la programmation, javais dix ans et un Atari ST possdant un interprteur GFA Basic. Mes parents mavaient achet un livre contenant des listings recopier et excuter. Si mes souvenirs ne me trahissent pas, il sagissait pour la plupart dapplications permettant de grer le contenu de son frigo ou de sa cave vins. Quelques petits jeux trs simples et peu graphiques venaient agrmenter le lot. Pour faire fonctionner ces programmes, il fallait tout recopier la main (ou plutt au clavier), gnralement quelques centaines de lignes de code. Rgulirement, cela ne fonctionnait pas car je faisais une erreur de copie, inversant des parenthses ou oubliant des mots. part vrier tout le listing ligne par ligne, je navais plus qu passer au listing suivant ! Parfois, mes eorts taient rcompenss mme si je ne comprenais strictement rien ce que je recopiais. Je me rappelle dun superbe labyrinthe en 3 dimensions, quoique mes souvenirs lui rendent certainement un hommage plus en couleur quil ne le mritait ! Ces listings remplis de mots magiques mont donn envie de comprendre comment cela fonctionnait. Jai donc pris mon courage dix doigts et tent de crer mes propres programmes en isolant les parties qui me paraissaient simples. Acher Bonjour comment allez-vous et pouvoir discuter avec lordinateur grce un algorithme de mon cru ont t un de mes premiers souvenirs de programme russi. cette poque recule, il nexistait pas de moyen dapprendre facilement la programmation. Il ny avait pas internet. . . eh oui, cette poque a exist ! Durant mon adolescence jai continu mon apprentissage en essayant dirents langages, comme le C++ ou lassembleur, le turbo pascal et autres joyeusets. La plupart taient inaccessibles, notamment le C++. Quelques livres en bibliothque ont ni dans la mienne mais ils taient tous bien incomprhensibles. . . je me souviens mme dun livre qui promettait de pouvoir crer un jeu facilement . Cela ne devait pas tre si facile que a vu mon air hbt aprs la lecture du livre ! Cela manquait dun Site du Zro o tout est expliqu de zro pour les personnes, comme jai pu ltre, curieuses de se lancer dans le monde magique du dveloppement.

iii

CHAPITRE 0. AVANT-PROPOS

On parle du C# ?
Jy viens ! Cest dans cette optique que jai commenc crire. Pouvoir partager mes connaissances souvent durement acquises et aider ceux qui ont du mal se lancer. Et cest vrai que ce nest pas facile, malgr toute la bonne volont du monde. Sans une mthodologie simple et des explications claires, il nest pas ais de se lancer sans se sentir perdu. Cest l o jespre pouvoir faire quelque chose travers la collection des Livres du Zro. Aprs tous mes essais de jeunesse, mes tudes et mon entre dans le monde du travail, jai acquis une certaine exprience des dirents langages de programmation. Jai pris got lcriture en commenant rdiger des articles avec mon langage prfr de lpoque, le C++. Aujourdhui, cest le C# qui occupe cette place prestigieuse dans mon classement ultra-personnel des langages de programmation ! Cest donc loccasion de pouvoir mettre prot cette volont de partage de connaissances et ce got pour la rdaction, dans un ouvrage permettant dapprendre le C# et qui est destin aux dbutants.

Quallez-vous apprendre en lisant ce livre ?


Nous allons apprendre le langage de programmation C# de faon progressive au cours de cet ouvrage, compos des parties suivantes : 1. Les rudiments du langage C# : nous commencerons par dcouvrir les bases du langage C#. Nous partons vraiment des bases : comment est construite une application informatique ? Quels logiciels dois-je installer ? Quelles sont les instructions de base du C# ? Nous allons dcouvrir tout cela au cours de cette premire partie qui permettra de poser les briques de nos premires applications. 2. Un peu plus loin avec le C# : dans cette partie, nous allons continuer approfondir nos connaissances avec le C#. Nous dcouvrirons les premires interactions avec lutilisateur de nos programmes. Comment lire simplement une saisie clavier ? Comment lire le contenu de la ligne de commande ? Nous dcouvrirons cela, avec en complment des TP pour nous entraner. 3. Le C#, un langage orient objet : ici, les choses srieuses commencent et nous allons voir ce quest la programmation oriente objet et comment le C# rpond ce genre de programmation. Chapitre un peu plus avanc o vous dcouvrirez toute la puissance du langage et o vous vous rendrez compte de ltendue des possibilits du C# ! 4. C# Avanc : forts de nos connaissances acquises prcdemment, nous tudierons des points plus avancs dans cet ultime chapitre. Nous verrons comment accder ecacement aux donnes grce LINQ et comment utiliser une base de donnes avec Entity Framework. Nous verrons galement dautres aspects permettant dtre encore plus ecaces avec vos dveloppements. iv

COMMENT LIRE CE LIVRE ? la n de cet ouvrage, vous aurez acquis toutes les bases vous permettant de vous lancer sans apprhension dans le monde du dveloppement dapplications professionnelles avec le C#. Vous dcouvrirez en bonus un aperu des direntes applications que lon peut raliser avec le C#.

Comment lire ce livre ?


Esprit du livre
Oui, oui, vous avez bien lu, ce livre est pour les dbutants. Pas besoin davoir fait du dveloppement auparavant pour pouvoir lire cet ouvrage ! Je vais donc faire de mon mieux pour dtailler au maximum mes explications, cest promis. Bien sr, il y en a peut-tre parmi vous qui ont dj fait du C, du C++, du Java. . . videmment, si vous avez dj fait du dveloppement informatique, ce sera plus facile pour vous. Attention nanmoins de ne pas aller trop vite : le C# ressemble dautres langages mais il a quand mme ses spcicits ! Nous allons dcouvrir ensemble de nombreuses choses en apprenant dvelopper en C#. Il y aura bien entendu des TP pour vous faire pratiquer, an que vous puissiez vous rendre compte de ce que vous tes capables de faire aprs avoir lu plusieurs chapitres plus thoriques. Nanmoins, je veux que vous soyez actifs ! Ne vous contentez pas de lire passivement mes explications, mme lorsque les chapitres sont plutt thoriques ! Testez les codes et les manipulations au fur et mesure. Essayez les petites ides que vous avez pour amliorer ou adapter lgrement le code. Sortez un peu des sentiers battus du tutoriel : cela vous fera pratiquer et vous permettra de dcouvrir rapidement si vous avez compris ou non le chapitre. Pas dinquitude, si jamais vous bloquez sur quoi que ce soit qui nest pas expliqu dans ce cours, la communaut qui sillonne les forums du Site du Zro saura vous apporter son aide prcieuse.

Suivez lordre des chapitres


Lisez ce livre comme on lit un roman. Il a t conu pour cela. Contrairement beaucoup de livres techniques o il est courant de lire en diagonale et de sauter certains chapitres, il est ici trs fortement recommand de suivre lordre du cours, moins que vous ne soyez dj un peu expriments.

Utilisez les codes web !


An de tirer parti du Site du Zro dont ce livre est issu, celui-ci vous propose ce quon appelle des codes web . Ce sont des codes six chires saisir sur une page du Site du Zro pour tre automatiquement redirig vers un site web sans avoir en recopier ladresse. v

CHAPITRE 0. AVANT-PROPOS Pour utiliser les codes web, rendez-vous sur la page suivante 2 : http://www.siteduzero.com/codeweb.html Un formulaire vous invite rentrer votre code web. Faites un premier essai avec le code ci-dessous : Tester le code web Code web : 123456

Ces codes web ont deux intrts :

ils vous redirigent vers les sites web prsents tout au long du cours, vous permettant ainsi dobtenir les logiciels dans leur toute dernire version ; ils vous permettent de tlcharger les codes sources inclus dans ce livre, ce qui vous vitera davoir recopier certains programmes un peu longs. Ce systme de redirection nous permet de tenir jour le livre que vous avez entre les mains sans que vous ayez besoin dacheter systmatiquement chaque nouvelle dition. Si un site web change dadresse, nous modierons la redirection mais le code web utiliser restera le mme. Si un site web disparat, nous vous redirigerons vers une page du Site du Zro expliquant ce qui sest pass et vous proposant une alternative. En clair, cest un moyen de nous assurer de la prennit de cet ouvrage sans que vous ayez faire quoi que ce soit !

Ce livre est issu du Site du Zro


Cet ouvrage reprend le cours C# du Site du Zro dans une dition revue et corrige, augmente de nouveaux chapitres plus avancs et des notes de bas de page. Il reprend les lments qui ont fait le succs des cours du site, savoir leur approche progressive et pdagogique, leur ton dcontract, ainsi que les TP vous permettant de pratiquer de faon autonome. Ce livre sadresse donc toute personne dsireuse dapprendre les bases de la programmation en C#, que ce soit : par curiosit ; par intrt personnel ; par besoin professionnel.

Remerciements
Je souhaite remercier un certain nombre de personnes qui, de prs ou de loin, ont contribu la naissance de cet ouvrage : ma femme Delphine qui me soutient au quotidien et more chaque jour une raison davancer dans la vie ses cts ;
2. Vous pouvez aussi utiliser le formulaire de recherche du Site du Zro, section Code web .

vi

REMERCIEMENTS Jrmie, mon ami-tmoin-compagnon-de-dev , qui a bien voulu relire mes premiers essais et qui a toujours une nouvelle ide dvelopper ; Anna, Jonathan, Mathieu, Pierre et toute lquipe de Simple IT ; tous les relecteurs et particulirement Julien Patte (alias Orwell), qui ma donn dexcellents conseils ; tous les lecteurs qui ont contribu son amlioration grce leurs commentaires prcieux et leur envie de voir le livre termin. Bonne lecture !

vii

CHAPITRE 0. AVANT-PROPOS

viii

Sommaire

Avant-propos On parle du C# ? . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Quallez-vous apprendre en lisant ce livre ? . . . . . . . . . . . . . . . . . . . . Comment lire ce livre ? . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Ce livre est issu du Site du Zro . . . . . . . . . . . . . . . . . . . . . . . . . Remerciements . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

iii iv iv v vi vi

Les rudiments du langage C#

1
3 4 4 8 8 11 12 12 16 17 19 21 21

1 Introduction au C# Quest-ce que le C# ? . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Comment sont cres les applications informatiques ? . . . . . . . . . . . . . . Excutables ou assemblages ? . . . . . . . . . . . . . . . . . . . . . . . . . . . Quest-ce que le framework .NET ? . . . . . . . . . . . . . . . . . . . . . . . . 2 Crer un projet avec Visual C# 2010 Express Que faut-il pour dmarrer ? . . . . . . . . . . . . . . . . . . . . . . . . . . . . Installer Visual C# 2010 Express . . . . . . . . . . . . . . . . . . . . . . . . . Dmarrer Visual C# 2010 Express . . . . . . . . . . . . . . . . . . . . . . . . Crer un projet . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Analyse de lenvironnement de dveloppement et du code gnr . . . . . . . crire du texte dans notre application . . . . . . . . . . . . . . . . . . . . . . Lexcution du projet . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . ix

SOMMAIRE 3 La syntaxe gnrale du C# crire une ligne de code . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Le caractre de terminaison de ligne . . . . . . . . . . . . . . . . . . . . . . . Les commentaires . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . La compltion automatique . . . . . . . . . . . . . . . . . . . . . . . . . . . . 4 Les variables Quest-ce quune variable ? . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Les dirents types de variables . . . . . . . . . . . . . . . . . . . . . . . . . . Aectations, oprations, concatnation . . . . . . . . . . . . . . . . . . . . . . Les caractres spciaux dans les chanes de caractres . . . . . . . . . . . . . 5 Les instructions conditionnelles Les oprateurs de comparaison . . . . . . . . . . . . . . . . . . . . . . . . . . Linstruction if . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Linstruction switch . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 6 Les blocs de code et la porte dune variable Les blocs de code . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . La porte dune variable . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 7 Les mthodes Crer une mthode . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . La mthode spciale Main() . . . . . . . . . . . . . . . . . . . . . . . . . . . . Paramtres dune mthode . . . . . . . . . . . . . . . . . . . . . . . . . . . . Retour dune mthode . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 8 Tableaux, listes et numrations Les tableaux . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Les listes . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Liste ou tableau ? . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Les numrations . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 9 Utiliser le framework .NET Linstruction using . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . La bibliothque de classes .NET . . . . . . . . . . . . . . . . . . . . . . . . . x 27 28 29 31 31 35 36 38 38 41 45 46 46 49 53 54 54 57 58 59 60 62 67 68 70 72 72 75 76 77

SOMMAIRE Rfrencer une assembly . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Dautres exemples . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 10 TP : bonjour cest le week-end ! Instructions pour raliser le TP . . . . . . . . . . . . . . . . . . . . . . . . . . Correction . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Aller plus loin . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 11 Les boucles La boucle for . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . La boucle foreach . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . La boucle while . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 78 82 85 86 87 88 91 92 95 98

Les instructions break et continue . . . . . . . . . . . . . . . . . . . . . . . . 100 12 TP : calculs en boucle 103

Instructions pour raliser le TP . . . . . . . . . . . . . . . . . . . . . . . . . . 104 Correction . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 105 Aller plus loin . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 107

II

Un peu plus loin avec le C#

109
111

13 Les conversions entre les types

Entre les types compatibles : le casting . . . . . . . . . . . . . . . . . . . . . . 112 Entre les types incompatibles . . . . . . . . . . . . . . . . . . . . . . . . . . . 116 14 Lire le clavier dans la console 119

Lire une phrase . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 120 Lire un caractre . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 121 15 Utiliser le dbogueur 125

quoi a sert ? . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 126 Mettre un point darrt et avancer pas pas . . . . . . . . . . . . . . . . . . . 127 Observer des variables . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 129 Revenir en arrire . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 131 La pile des appels . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 132 xi

SOMMAIRE 16 TP : le jeu du plus ou du moins 135

Instructions pour raliser le TP . . . . . . . . . . . . . . . . . . . . . . . . . . 136 Correction . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 136 Aller plus loin . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 138 17 La ligne de commande Quest-ce que la ligne de commande ? 141 . . . . . . . . . . . . . . . . . . . . . . 142

Passer des paramtres en ligne de commande . . . . . . . . . . . . . . . . . . 142 Lire la ligne de commande . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 144 18 TP : calculs en ligne de commande 147

Instructions pour raliser le TP . . . . . . . . . . . . . . . . . . . . . . . . . . 148 Correction . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 148 Aller plus loin . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 151

III

Le C#, un langage orient objet

155
157

19 Introduction la programmation oriente objet

Quest-ce quun objet ? . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 158 Lencapsulation . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 159 Hritage . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 159 Polymorphisme - Substitution . . . . . . . . . . . . . . . . . . . . . . . . . . . 161 Interfaces . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 162 quoi sert la programmation oriente objet ? . . . . . . . . . . . . . . . . . . 163 20 Crer son premier objet 165

Tous les types C# sont des objets . . . . . . . . . . . . . . . . . . . . . . . . 166 Les classes . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 166 Les mthodes . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 169 Notion de visibilit . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 172 Les proprits . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 174 21 Manipuler des objets 183

Le constructeur . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 184 Instancier un objet . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 186 xii

SOMMAIRE Le mot-cl this . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 189 191

22 La POO et le C#

Des types, des objets, type valeur et type rfrence . . . . . . . . . . . . . . . 192 Hritage . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 194 Substitution . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 207 Polymorphisme . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 211 La conversion entre les objets avec le casting . . . . . . . . . . . . . . . . . . 214 23 Notions avances de POO en C# 221

Comparer des objets . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 222 Les interfaces . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 225 Les classes et les mthodes abstraites . . . . . . . . . . . . . . . . . . . . . . . 233 Les classes partielles . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 236 Classes statiques et mthodes statiques Les types anonymes et le mot-cl var . . . . . . . . . . . . . . . . . . . . . 238 . . . . . . . . . . . . . . . . . . . . . . 244 247 Les classes internes . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 243

24 TP : programmation oriente objet

Instructions pour raliser le TP . . . . . . . . . . . . . . . . . . . . . . . . . . 248 Correction . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 250 Aller plus loin . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 256 Deuxime partie du TP . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 258 Correction . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 259 25 Mode de passage des paramtres une mthode 265

Passage de paramtres par valeur . . . . . . . . . . . . . . . . . . . . . . . . . 266 Passage de paramtres en mise jour . . . . . . . . . . . . . . . . . . . . . . 267 Passage des objets par rfrence . . . . . . . . . . . . . . . . . . . . . . . . . 268 Passage de paramtres en sortie . . . . . . . . . . . . . . . . . . . . . . . . . . 269 26 Les structures 273

Une structure est presque une classe . . . . . . . . . . . . . . . . . . . . . . . 274 quoi sert une structure ? . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 274 Crer une structure . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 275 Passage de structures en paramtres . . . . . . . . . . . . . . . . . . . . . . . 278 xiii

SOMMAIRE Dautres structures ? . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 279 27 Les gnriques 281

Quest-ce que les gnriques ? . . . . . . . . . . . . . . . . . . . . . . . . . . . 282 Les types gnriques du framework .NET . . . . . . . . . . . . . . . . . . . . 282 Crer une mthode gnrique . . . . . . . . . . . . . . . . . . . . . . . . . . . 283 Crer une classe gnrique . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 287 La valeur par dfaut dun type gnrique . . . . . . . . . . . . . . . . . . . . 289 Les interfaces gnriques . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 290 Les restrictions sur les types gnriques . . . . . . . . . . . . . . . . . . . . . 291 Les types nullables . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 295 28 TP : types gnriques 297

Instructions pour raliser la premire partie du TP . . . . . . . . . . . . . . . 298 Correction . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 299 Instructions pour raliser la deuxime partie du TP . . . . . . . . . . . . . . 304 Correction . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 306 Aller plus loin . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 308 Implmenter une interface explicitement . . . . . . . . . . . . . . . . . . . . . 312 29 Les mthodes dextension 315

Quest-ce quune mthode dextension ? . . . . . . . . . . . . . . . . . . . . . 316 Crer une mthode dextension . . . . . . . . . . . . . . . . . . . . . . . . . . 316 Utiliser une mthode dextension . . . . . . . . . . . . . . . . . . . . . . . . . 317 30 Dlgus, vnements et expressions lambdas 321

Les dlgus (delegate) . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 322 Diusion multiple, le multicast . . . . . . . . . . . . . . . . . . . . . . . . . . 325 Les dlgus gnriques Action et Func . . . . . . . . . . . . . . . . . . . . . 327 Les expressions lambdas . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 329 Les vnements . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 330 31 Grer les erreurs : les exceptions 335

Intercepter une exception . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 336 Intercepter plusieurs exceptions . . . . . . . . . . . . . . . . . . . . . . . . . . 340 Lever une exception . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 342 xiv

SOMMAIRE Propagation de lexception . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 343 Crer une exception personnalise Le mot-cl finally . . . . . . . . . . . . . . . . . . . . . . . . 345 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 347 351

32 TP : vnements et mto

Instructions pour raliser le TP . . . . . . . . . . . . . . . . . . . . . . . . . . 352 Correction . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 352 Aller plus loin . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 356

IV

C# avanc

359
361

33 Crer un projet bibliothques de classes Crer un projet de bibliothque de classe

Pourquoi crer une bibliothque de classes ? . . . . . . . . . . . . . . . . . . . 362 . . . . . . . . . . . . . . . . . . . . 363 Proprits de la bibliothque de classe . . . . . . . . . . . . . . . . . . . . . . 365 Gnrer et utiliser une bibliothque de classe . . . . . . . . . . . . . . . . . . 366 Le mot-cl internal . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 368 34 Plus loin avec le C# et .NET Prcisions sur les types et gestion mmoire 371 . . . . . . . . . . . . . . . . . . . 373

Empcher une classe de pouvoir tre hrite . . . . . . . . . . . . . . . . . . . 372 Masquer une mthode . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 377 Le mot-cl yield . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 379 Le formatage de chanes, de dates et la culture . . . . . . . . . . . . . . . . . 384 Les attributs . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 391 La rexion . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 393 35 La conguration dune application 397

Rappel sur les chiers XML . . . . . . . . . . . . . . . . . . . . . . . . . . . . 398 Crer le chier de conguration . . . . . . . . . . . . . . . . . . . . . . . . . . 399 Lecture simple dans la section de conguration prdnie : AppSettings . . . 401 Lecture des chanes de connexion la base de donnes . . . . . . . . . . . . . 403 Crer sa section de conguration depuis un type prdni . . . . . . . . . . . 404 Les groupes de sections . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 407 Crer une section de conguration personnalise . . . . . . . . . . . . . . . . 408 xv

SOMMAIRE Crer une section personnalise avec une collection . . . . . . . . . . . . . . . 409 36 Introduction LINQ 415

Les requtes Linq . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 416 Les mthodes dextension Linq . . . . . . . . . . . . . . . . . . . . . . . . . . 423 Excution dire . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 426 Rcapitulatif des oprateurs de requtes . . . . . . . . . . . . . . . . . . . . . 428 37 Accder aux donnes avec Entity Framework 431

Les bases de donnes et la modlisation . . . . . . . . . . . . . . . . . . . . . 432 Entity Framework et le mapping objet relationnel . . . . . . . . . . . . . . . . 432 Installer et utiliser loutil de gestion de BDD . . . . . . . . . . . . . . . . . . 444 Se connecter la base de donnes, lire et crire . . . . . . . . . . . . . . . . . 453 38 Les tests unitaires 465

Quest-ce quun test unitaire et pourquoi en faire ? . . . . . . . . . . . . . . . 466 Notre premier test . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 466 Le framework de test . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 470 Le framework de simulacre . . . . . . . . . . . . . . . . . . . . . . . . . . . . 479 483

39 Les types dapplications pouvant tre dveloppes en C#

Crer une application Windows avec WPF . . . . . . . . . . . . . . . . . . . . 484 Crer une application web avec ASP.NET . . . . . . . . . . . . . . . . . . . . 484 Crer une application client riche avec Silverlight . . . . . . . . . . . . . . . . 485 Le graphisme et les jeux avec XNA . . . . . . . . . . . . . . . . . . . . . . . . 485 Crer une application mobile avec Windows Phone 7 . . . . . . . . . . . . . . 485 Crer un service web avec WCF . . . . . . . . . . . . . . . . . . . . . . . . . . 486

xvi

Premire partie

Les rudiments du langage C#

Chapitre

1
Dicult :

Introduction au C#

ans ce tout premier chapitre, nous allons dcouvrir ce quest le C#, son histoire et son rapport avec le framework .NET. Dailleurs, vous ne savez pas ce quest un framework ? Ce nest pas grave, tout ceci sera expliqu !

Nous verrons dans ce chapitre ce que sont les applications informatiques et comment des langages de programmation volus comme le C# nous permettent de raliser de telles applications. Et ce nest que le dbut. . . alors ouvrez grands vos yeux, chaussez vos lunettes et explorons ce monde merveilleux !

CHAPITRE 1. INTRODUCTION AU C#

Quest-ce que le C# ?
Le C# est un langage de programmation cr par Microsoft en 2002. Un langage de programmation est un ensemble dinstructions, cest--dire un ensemble de mots qui permettent de construire des applications informatiques. Ces applications informatiques peuvent tre de beaucoup de sortes, par exemple une application Windows, comme un logiciel de traitement de texte, une calculatrice ou encore un jeu de cartes. On les appelle galement des clients lourds. Il est galement possible de dvelopper des applications web, comme un site de-commerce, un intranet, etc. Nous pourrons accder ces applications grce un navigateur internet que lon appelle un client lger. Toujours grce un navigateur internet, nous pourrons dvelopper des clients riches. Ce sont des applications qui se rapprochent dune application Windows mais qui fonctionnent dans un navigateur. Bien dautres types dapplications peuvent tre crites avec le C#, citons encore le dveloppement dapplications mobiles sous Windows phone 7, de jeux ou encore de web services. . . Nous verrons un peu plus en dtail la n de cet ouvrage comment raliser de telles applications ! Le C# est un langage dont la syntaxe ressemble un peu au C++ ou au Java qui sont dautres langages de programmation trs populaires. Le C# est le langage phare de Microsoft. Il fait partie dun ensemble plus important : il est en fait une brique de ce quon appelle le Framework .NET . Gardons encore un peu de suspens sur ce quest le framework .NET, nous dcouvrirons ce que cest un peu plus loin dans ce livre.

Comment sont cres les applications informatiques ?


Une application informatique : quest-ce que cest ?
Comme vous le savez, votre ordinateur excute des applications informatiques pour eectuer des tches. Ce sont des logiciels comme : un traitement de texte ; un navigateur internet ; un jeu vido ; etc.

Votre ordinateur ne peut excuter ces applications informatiques que si elles sont crites dans le seul langage quil comprend, le binaire. Techniquement, le binaire est reprsent par une suite de 0 et de 1, comme vous pouvez le voir la gure 1.1. Il nest bien sr pas raisonnablement possible de raliser une grosse application en binaire, cest pour a quil existe des langages de programmation qui permettent de simplier lcriture dune application informatique. 4

COMMENT SONT CRES LES APPLICATIONS INFORMATIQUES ?

Figure 1.1 Le langage binaire est compos de 0 et de 1

Comment crer des programmes simplement ?


Je vais vous expliquer rapidement le principe de fonctionnement des langages dits traditionnels, comme le C et le C++, puis je vous prsenterai le fonctionnement du C#. Comme le C# est plus rcent, il a t possible damliorer son fonctionnement par rapport au C et au C++ comme nous allons le voir. Langages traditionnels : la compilation Avec des langages traditionnels comme le C et le C++, on crit des instructions simplies, lisibles par un humain comme :
1

printf ( " Bonjour " ) ;

Ce nest pas vraiment du franais, mais cest quand mme beaucoup plus simple que le binaire et on comprend globalement avec cet exemple que lon va acher le mot Bonjour . Bien entendu, lordinateur ne comprend pas ces instructions. Lui, il veut du binaire, du vrai ! Pour obtenir du binaire partir dun code crit en C ou C++, on doit eectuer ce quon appelle une compilation. Le compilateur est un programme qui traduit le code source en binaire excutable. Ce nest pas clair ? Observez donc la gure 1.2 !

Figure 1.2 Compilation traditionnelle dun programme en binaire Cette mthode est ecace et a fait ses preuves. De nombreuses personnes dveloppent toujours en C et C++ aujourdhui. Nanmoins, ces langages ont aussi un certain nombre de dfauts dus leur anciennet. Par exemple, un programme compil (binaire) ne fonctionne que sur la plateforme pour laquelle il a t compil. Cela veut dire que si 5

CHAPITRE 1. INTRODUCTION AU C# vous compilez sous Windows, vous obtenez un programme qui fonctionne sous Windows uniquement (et sur un type de processeur particulier). Impossible de le faire tourner sous Mac OS X ou Linux, moins de le recompiler sous ces systmes dexploitation et deectuer au passage quelques modications (voir gure 1.3).

Figure 1.3 Compilations multiples pour cibler toutes les plateformes Les programmes binaires ont ce dfaut : ils ne fonctionnent que pour un type de machine. Pour les dveloppeurs qui crivent le code, cest assez fastidieux grer. Langages rcents : le code manag Les langages rcents, comme le C# et le Java, rsolvent ce problme de compatibilit tout en ajoutant au langage de nombreuses fonctionnalits apprciables, ce qui permet de raliser des programmes beaucoup plus ecacement. La compilation en C# ne donne pas un programme binaire, contrairement au C et au C++. Le code C# est en fait transform dans un langage intermdiaire (appel CIL ou MSIL) que lon peut ensuite distribuer tout le monde. Ce code, bien sr, nest pas excutable lui-mme, car lordinateur ne comprend que le binaire. Regardez bien la gure 1.4 pour comprendre comment cela fonctionne. Le code en langage intermdiaire (CIL) correspond au programme que vous allez distribuer. Sous Windows, il prend lapparence dun .exe comme les programmes habituels, mais il ne contient en revanche pas de binaire. 6

COMMENT SONT CRES LES APPLICATIONS INFORMATIQUES ?

Figure 1.4 Compilation C# et excution du CIL par le CLR Lorsquon excute le programme CIL, celui-ci est lu par un autre programme (une machine analyser les programmes, appele CLR) qui le compile cette fois en vrai programme binaire. Cette fois, le programme peut sexcuter, ouf ! a complique bien les choses quand mme ! Est-ce bien utile ?

Cela ore beaucoup de souplesse au dveloppeur. Le code en langage intermdiaire (CIL) peut tre distribu tout le monde. Il sut davoir install la machine CLR sur son ordinateur, qui peut alors lire les programmes en C# et les compiler la vole en binaire. Avantage : le programme est toujours adapt lordinateur sur lequel il tourne. Le CLR vrie aussi la scurit du code ; ainsi en C du code mal pens (par exemple une mauvaise utilisation des pointeurs) peut entraner des problmes pour votre PC, ce que vous risquez beaucoup moins avec le C#. De plus, le CLR dispose du JIT debugger qui permet de lancer Visual Studio si une erreur survient dans un programme .NET pour voir ce qui a caus cette erreur. On parle de code manag . Cette complexit ralentit lgrement la vitesse dexcution des programmes (par rapport au C ou au C++), mais la dirence est aujourdhui vraiment ngligeable par rapport aux gains que cela apporte. Donc, en thorie, il est possible dutiliser nimporte quelle application compile en langage intermdiaire partir du moment o il y a une implmentation du CLR disponible. En ralit, il ny a que sous Windows quil existe une implmentation complte du CLR. Il existe cependant une implmentation partielle sous Linux : Mono. Cela veut dire que si votre programme utilise des fonctionnalits qui ne sont pas couvertes par Mono, il ne fonctionnera pas. En conclusion, dans la pratique, le .NET est totalement exploitable sous Windows et ailleurs, non. 7

CHAPITRE 1. INTRODUCTION AU C#

Excutables ou assemblages ?
Jai dit juste au dessus que le C# tait compil en langage intermdiaire et quon le retrouve sous la forme dun .exe comme les programmes habituels. Cest vrai ! (Je ne mens jamais !)

Par contre, cest un peu incomplet. Il est possible de crer des programmes (.exe) qui pourront directement tre excuts par le CLR, mais il est galement possible de crer des bibliothques sous la forme dun chier possdant lextension .dll. On appelle ces deux formes de programme des assemblages, mais on utilise globalement toujours le mot anglais assembly. Les chiers .exe sont des assemblys de processus. Les chiers .dll sont des assemblys de bibliothques. Concrtement, cela signie que le chier .exe servira lancer une application et quune dll pourra tre partage entre plusieurs applications .exe an de rutiliser du code dj crit. Nous verrons un peu plus loin comment ceci est possible. Il est noter quun raccourci est souvent fait avec le terme assembly. On a tendance croire que le mot assembly sert dsigner uniquement les bibliothques dont lextension est .dll.

Quest-ce que le framework .NET ?


Jai commenc vous parler du C# qui tait une brique du framework .NET. Il est temps den savoir un peu plus sur ce fameux framework. Commenons par le commencement : comment cela se prononce ? DOTTE NETTE ou encore POINT NETTE. Je vous accorde que le nom est bizarre. . . ! Surtout que le nom peut tre trompeur. Avec lomniprsence dinternet, son abrviation (net) ou encore des noms de domaines (.net), on pourrait penser que le framework .NET est un truc ddi internet. Que nenni ! Nous allons donc prciser un peu ce quest le framework .NET pour viter les ambiguts. Premire chose savoir, quest-ce quun framework ?

QUEST-CE QUE LE FRAMEWORK .NET ? Pour simplier, on peut dire quun framework est une espce de grosse bote fonctionnalits qui va nous permettre de raliser des applications informatiques de toutes sortes. En fait, cest la combinaison de ce framework et du langage de programmation C# qui va nous permettre de raliser ces applications informatiques. Le framework .NET a t cr par Microsoft en 2002, en mme temps que le C#, qui est principalement ddi la ralisation dapplications fonctionnant dans des environnements Microsoft. Nous pourrons par exemple raliser des programmes qui fonctionnent sous Windows, ou bien des sites web ou encore des applications qui fonctionnent sur tlphone mobile, etc. Disons que la ralisation dune application informatique, cest un peu comme un chantier (je ne dis pas a parce quil y a toujours du retard, mme si cest vrai !). Il est possible de construire direntes choses, comme une maison, une piscine, une terrasse, etc. Pour raliser ces constructions, nous allons avoir besoin de matriaux, comme des briques, de la ferraille, etc. Certains matriaux sont communs toutes les constructions (fer, vis,. . .) et dautres sont spciques certains domaines (pour construire une piscine, je vais avoir besoin dun liner par exemple). On peut voir le framework .NET comme ces matriaux ; cest un ensemble de composants que lon devra assembler pour raliser notre application. Certains sont spciques pour la ralisation dapplications web, dautres pour la ralisation dapplications Windows, etc. Pour raliser un chantier, nous allons avoir besoin doutils an de manipuler les matriaux. Qui envisagerait de tourner une vis avec les doigts ou de poser des parpaings sans les coller avec du mortier ? Cest la mme chose pour une application informatique, pour assembler notre application, nous allons utiliser un langage de programmation : le C#. lheure o jcris ces lignes, le C# est en version 4 ainsi que le framework .NET. Ce sont des versions stables et utilises par beaucoup de personnes. Chaque version intermdiaire a apport son lot dvolutions. Le framework .NET et le C# sont en perptuelle volution, preuve de la dynamique apporte par Microsoft. Cest tout ce quil y a savoir pour linstant ! Nous reviendrons un peu plus en dtail sur le framework .NET dans les chapitres suivants. Pour lheure, il est important de retenir que cest grce au langage de programmation C# et grce aux composants du framework .NET que nous allons pouvoir dvelopper des applications informatiques.

En rsum
Le C# est un langage de programmation permettant dutiliser le framework .NET. Cest le langage phare de Microsoft. Le framework .NET est une norme bote fonctionnalits permettant la cration 9

CHAPITRE 1. INTRODUCTION AU C# dapplications. Le C# permet de dvelopper des applications de toutes sortes, excutables par le CLR qui traduit le MSIL en binaire. Il est possible de crer des assemblys de deux sortes : des assemblys de processus excutables par le CLR et des assemblys de bibliothques.

10

Chapitre

2
Dicult :

Crer un projet avec Visual C# 2010 Express

ans ce chapitre nous allons faire nos premiers pas avec le C#. Nous allons dans un premier temps installer et dcouvrir les outils qui nous seront ncessaires pour raliser des applications informatiques avec le C#. Nous verrons comment dmarrer avec ces outils et la n de ce chapitre, nous serons capables de crer un petit programme qui ache du texte simple et nous aurons commenc nous familiariser avec lenvironnement de dveloppement. Il faut bien commencer par les bases, mais vous verrez comme cela peut tre gratiant darriver enn faire un petit quelque chose. Allez, cest parti !

11

CHAPITRE 2. CRER UN PROJET AVEC VISUAL C# 2010 EXPRESS

Que faut-il pour dmarrer ?


Jespre vous avoir donn envie de dmarrer lapprentissage du C#, cependant, il nous manque encore quelque chose pour pouvoir sereinement attaquer cet apprentissage. Bien sr, vous allez avoir besoin dun ordinateur, mais a priori, vous lavez dj ! Sil nest pas sous Windows, mais sous Linux, vous pouvez utiliser Mono qui va permettre dutiliser le C# sous Linux. Il est tlchargeable via le code web suivant : Tlcharger Mono Code web : 566528
Cependant, Mono nest pas aussi complet que le C# et le framework .NET sous Windows. En lutilisant vous risquez de passer ct de certaines parties de ce livre.

Pour reprendre la mtaphore du chantier, on peut dire quil va galement nous manquer un chef de chantier. Il nest pas forcment ncessaire en thorie, mais dans la pratique il se rvle indispensable pour mener bien son chantier. Ce chef de chantier cest en fait loutil de dveloppement. Il va nous fournir les outils pour orchestrer nos dveloppements. Cest entre autres : un puissant diteur, qui va nous servir crer des chiers contenant des instructions en langage C# ; un compilateur, qui va servir transformer ces chiers en une suite dinstructions comprhensibles par lordinateur, comme nous lavons dj vu ; un environnement dexcution, qui va permettre de faire les actions informatiques correspondantes (acher une phrase, ragir au clic de la souris, etc.) ; cest le CLR dont on a dj parl. Enn, nous aurons besoin dune base de donnes. Nous y reviendrons plus en dtail ultrieurement, mais la base de donnes est un endroit o seront stockes les donnes de notre application. Cest un lment indispensable mesure que lapplication grandit.

Installer Visual C# 2010 Express


Nous avons donc besoin de notre chef de chantier, loutil de dveloppement. Cest un logiciel qui va nous permettre de crer des applications et qui va nous fournir les outils pour orchestrer nos dveloppements. La gamme de Microsoft est riche en outils professionnels de qualit pour le dveloppement, notamment grce Visual Studio. Notez que cet outil de dveloppement se nomme galement un IDE pour Integrated Development Environment ce qui signie Environnement de dveloppement intgr . Nous aurons recours au terme IDE rgulirement. Pour apprendre et commencer dcouvrir lenvironnement de dveloppement, Micro12

INSTALLER VISUAL C# 2010 EXPRESS soft propose gratuitement Visual Studio dans sa version Express. Cest une version allge de lenvironnement de dveloppement qui permet de faire plein de choses, mais avec des outils en moins par rapport aux versions payantes. Rassurez-vous, ces versions gratuites sont trs fournies et permettent de faire tout ce dont on a besoin pour apprendre le C# et suivre ce cours. Pour raliser des applications denvergure, il pourra cependant tre judicieux dinvestir dans loutil complet et ainsi bncier de fonctionnalits complmentaires qui permettent damliorer, de faciliter et dindustrialiser les dveloppements. Pour dvelopper en C# gratuitement et crer des applications Windows, nous allons avoir besoin de Microsoft Visual C# 2010 Express que vous pouvez tlcharger en suivant ce code web : Visual C# 2010 Express Code web : 389622

Pour rsumer :

Visual Studio est la version payante de loutil de dveloppement. Microsoft Visual C# 2010 Express est une version allge et gratuite de Visual Studio, ddie au dveloppement en C#. Exactement ce quil nous faut ! Cliquez sur Visual C# 2010 Express et choisissez la langue qui vous convient. Puis cliquez sur Tlchargez (voir les gures 2.1 et 2.2).

Figure 2.1 Page de tlchargement de Visual C# Express 2010 Une fois lexcutable tlcharg, il ne reste plus qu le lancer et linstallation dmarre. Cliquez sur Suivant pour dmarrer linstallation, ainsi quindiqu la gure 2.3. Vous devez ensuite lire la licence dutilisation du logiciel et laccepter pour pouvoir continuer linstallation. Une application sans donnes, cest plutt rare. Cest un peu comme un site de13

CHAPITRE 2. CRER UN PROJET AVEC VISUAL C# 2010 EXPRESS

Figure 2.2 Tlchargement de Visual C# Express 2010

Figure 2.3 cran daccueil du programme dinstallation de Visual C# Express

14

INSTALLER VISUAL C# 2010 EXPRESS commerce sans produits, un traitement de texte sans chiers ou le site du zro sans tutoriel. On risque de vite sennuyer ! Heureusement, le programme dinstallation nous propose dinstaller Microsoft SQL Server 2008 Express Service Pack 1 . Microsoft propose en version gratuite un serveur de base de donnes allg. Il va nous permettre de crer facilement une base de donnes et de lutiliser depuis nos applications en C#. Nous lavons dj voqu et nous y reviendrons plus en dtail dans un chapitre ultrieur mais une base de donnes est un norme endroit o sont stockes les donnes de notre application. Nous avons galement voqu dans lintroduction quil tait possible de raliser des applications qui ressemblent des applications Windows mais dans un navigateur, que nous avons appel client riche. Silverlight va justement permettre de crer ce genre dapplication. Cochez donc tout pour installer Silverlight et Sql Server et cliquez sur Suivant (voir gure 2.4).

Figure 2.4 Installer Silverlight et Microsoft Sql Server Express 2008 Cliquez ensuite sur Installer en changeant ventuellement le dossier dinstallation. Une fois linstallation termine cliquez sur Quitter. lheure o jcris ces lignes, il existe un service pack pour Visual Studio, le Service pack 1 . Cest un ensemble de corrections qui permettent damliorer la stabilit du logiciel. Je vous invite tlcharger et installer ce pack, disponible via le code web suivant : 15

CHAPITRE 2. CRER UN PROJET AVEC VISUAL C# 2010 EXPRESS Service pack 1 Code web : 974305

Vous voil avec votre copie de Visual C# Express qui va vous permettre de crer des programmes en C# gratuitement et facilement. Linstallation de loutil de dveloppement est termine. Notez que, bien que gratuite, vous aurez besoin denregistrer votre copie de Visual C# Express avant 30 jours. Cest une opration rapide et ncessitant un compte Windows Live. Aprs cela, vous pourrez utiliser Visual C# Express sans retenue. En rsum, nous avons install un outil de dveloppement, Visual C# 2010 dans sa version Express et une base de donnes, SQL Server Express 2008. Nous avons tous les outils ncessaires et nous allons pouvoir dmarrer (enn !) lapprentissage et la pratique du C#.

Dmarrer Visual C# 2010 Express


Nous allons vrier que linstallation de Visual C# Express a bien fonctionn. Et pour ce faire, nous allons le dmarrer et commencer prendre en main ce formidable outil de dveloppement. Il vous semblera srement trs complexe au dbut mais vous allez voir, si vous suivez ce livre pas pas, vous allez apprendre les fonctionnalits indispensables. Elles seront illustres par des copies dcran vous permettant de plus facilement vous y retrouver. force dutiliser Visual C# Express, vous verrez que vous vous sentirez de plus en plus laise et peut-tre oserez-vous aller fouiller dans les menus ? Commencez par dmarrer Visual C# 2010 Express. Le logiciel souvre sur une page de dmarrage (voir la gure 2.5).

Figure 2.5 Premier dmarrage de Visual C# Express 16

CRER UN PROJET Les deux zones entoures de rouge permettent respectivement de crer un nouveau projet et daccder aux anciens projets dj crs. Dans ce deuxime cas, comme je viens dinstaller le logiciel, la liste est vide.

Crer un projet
Commenons par crer un nouveau projet en cliquant dans la zone rouge. Cette commande est galement accessible via le menu Fichier > Nouveau > Projet. Un projet va contenir les lments de ce que lon souhaite raliser. Cela peut tre par exemple une application web, une application Windows, etc. Le projet est aussi un container de chiers et notamment dans notre cas de chiers en langage C# qui vont permettre de construire ce que lon souhaite raliser. Le projet est en fait reprsent par un chier dont lextension est .csproj. Son contenu dcrit les paramtres de conguration correspondant ce que lon souhaite raliser et les chiers qui composent le projet. Crons donc un nouveau projet. La fentre de cration de nouveau projet souvre et nous avons plusieurs possibilits de choix. Nous allons dans un premier temps aller dans Visual C# pour choisir de crer une Application console, comme indiqu la gure 2.6.

Figure 2.6 Crer une application console avec Visual C# Express 17

CHAPITRE 2. CRER UN PROJET AVEC VISUAL C# 2010 EXPRESS noter que si vous navez install que Visual C# Express, vous aurez la mme fentre que moi. Si vous disposez de la version payante de Visual Studio, alors la fentre sera certainement plus garnie. De mme, il y aura plus de choses si vous avez install dautres outils de la gamme Express. Ce que nous faisons ici, cest utiliser ce quon appelle un modle de cration de projet, plus couramment appel par son quivalent anglais template. Si vous naviguez lintrieur des dirents modles, vous pourrez constater que Visual C# nous propose des modles de projets plus ou moins compliqus. Ces modles sont trs utiles pour dmarrer un projet car toute la conguration du projet est dj faite. Le nombre de modles peut tre dirent en fonction de votre version de Visual Studio ou du nombre de versions Express installes. Lapplication console est la forme de projet pouvant produire une application excutable la plus simple. Elle permet de raliser un programme qui va sexcuter dans la console noire qui ressemble une fentre ms-dos, pour les dinosaures comme moi qui ont connu cette poque. . . noter que les projets de type Bibliothque de classes permettent de gnrer des assemblys de bibliothques (.dll). Dans cette console, nous allons notamment pouvoir acher du texte simple. Ce type de projet est parfait pour dmarrer lapprentissage du C# car il y a seulement besoin de savoir comment acher du texte pour commencer. En bas de la fentre de cration de projet, nous avons la possibilit de choisir un nom pour le projet, ici ConsoleApplication1. Changeons le nom de notre application en, par exemple, MaPremiereApplication, dans la zone correspondante. Cliquons sur OK pour valider la cration de notre projet. Visual C# Express cre alors pour nous les chiers composant une application console vide, qui utilise le C# comme langage et que nous avons nomme MaPremiereApplication . Avant toute chose, nous allons enregistrer le projet. dans le menu Fichier > Allons ). Enregistrer (ou utiliser le raccourci bien connu Ctrl + S

Visual C# Express nous ouvre la fentre denregistrement de projet (voir gure 2.7).

Figure 2.7 Enregistrer la solution Nous pouvons donner un nom, prciser un emplacement o nous souhaitons que les 18

ANALYSE DE LENVIRONNEMENT DE DVELOPPEMENT ET DU CODE GNR chiers soient enregistrs et un nom de solution. Une case pr-coche nous propose de crer un rpertoire pour la solution. Cest ce que nous allons faire ! Cliquons maintenant sur Enregistrer. Pour les versions payantes de Visual Studio, le choix de lemplacement et le nom de la solution sont renseigner au moment o lon cre le projet. Une dirence subtile !

Analyse de lenvironnement de dveloppement et du code gnr


Si lon se rend dans lemplacement renseign (ici c:\users\nico\documents\visual studio 2010\Projects), nous pouvons constater que Visual C# Express a cr un rpertoire MaPremiereApplication : cest le fameux rpertoire pour la solution quil nous a propos de crer. Dans ce rpertoire, nous remarquons notamment un chier MaPremiereApplication.sln. Cest ce quon appelle le chier de solution ; il sagit juste dun container de projets qui va nous permettre de visualiser nos projets dans Visual C# Express. En loccurrence, nous navons pour linstant quun projet dans la solution : lapplication MaPremiereApplication, que nous pouvons retrouver dans le sous-rpertoire MaPremiereApplication et qui contient notamment le chier de projet qui sintitule : MaPremiereApplication.csproj. Le chier dcrivant un projet crit en C# est prx par csproj.

Il y a encore un chier digne dintrt dans ce rpertoire, il sagit du chier Program.cs. Les chiers dont lextension est .cs contiennent du code C#, cest dans ce chier que nous allons commencer taper nos premires lignes de code. . . Lensemble des chiers contenant des instructions crites dans un langage de programmation est appel le code source . Par extension, le code correspond des instructions crites dans un langage de programmation. Retournons dans linterface de Visual C# Express, et dtaillons un peu les direntes zones ! Pour suivre mes explications, reportez-vous la gure 2.8. La zone verte numro 1 contient les dirents chiers ouverts sous la forme dun onglet. On voit que, par dfaut, Visual C# nous a cr et ouvert le chier Program.cs. Dans la zone rouge numro 2, cest lditeur de code. Il ache le contenu du chier ouvert. Nous voyons des mots que nous ne comprenons pas encore. Cest du code qui a t automatiquement gnr par Visual C#. Nous pouvons observer que les mots 19

CHAPITRE 2. CRER UN PROJET AVEC VISUAL C# 2010 EXPRESS

Figure 2.8 LIDE de Visual C# Express sont de direntes couleurs. En eet, lditeur Visual C# Express possde ce quon appelle une coloration syntaxique, cest--dire que certains mots-cls sont colors dune couleur dirente en fonction de leur signication ou de leur contexte an de nous permettre de nous y retrouver plus facilement. Comme dans ce livre ! La zone numro 3 en violet est lexplorateur de solutions. Cest ici que lon voit le contenu de la solution sur laquelle nous travaillons en ce moment. En loccurrence, il sagit de la solution MaPremiereApplication qui contient un unique projet MaPremiereApplication. Ce projet contient plusieurs sous lments : 1. Properties : contient des proprits de lapplication, on ne sen occupe pas pour linstant ; 2. Rfrences : contient les rfrences de lapplication, on ne sen occupe pas non plus pour linstant ; 3. Program.cs est le chier qui a t gnr par Visual C# et qui contient le code C#. Il nous intresse fortement ! La zone numro 4 en brun est la zone contenant les proprits de ce sur quoi nous travaillons en ce moment. Ici, nous avons le curseur positionn sur le projet, il ny a pas beaucoup dinformations except le nom du chier de projet. Nous aurons loccasion de revenir sur cette fentre plus tard. La zone numro 5 en jaune nest pas ache au premier lancement, elle contient la liste des erreurs, des avertissements et des messages de notre application. Nous verrons comment lacher un peu plus bas. La zone numro 6 en noir est la barre doutils, elle possde plusieurs boutons que nous pourrons utiliser, notamment pour excuter notre application. 20

CRIRE DU TEXTE DANS NOTRE APPLICATION

crire du texte dans notre application


Allons donc dans la zone 2 rserve ldition de notre chier Program.cs qui est le chier contenant le code C# de notre application. Les mots prsents dans cette zone sont ce quon appelle des instructions de langage. Elles vont nous permettre dcrire notre programme. Nous reviendrons plus loin sur ce que veulent dire les instructions qui ont t gnres par Visual C#. Pour linstant, rajoutons simplement linstruction suivante aprs laccolade ouvrante :
1

Console . WriteLine ( " Hello World !! " ) ;

de manire avoir :
1 2 3 4

static void Main ( string [] args ) { Console . WriteLine ( " Hello World !! " ) ; }

Nous venons dcrire une instruction qui va acher la phrase Hello World ! ! . Pour linstant vous avez juste besoin de savoir a. Nous tudierons ultrieurement quoi cela correspond exactement.

Lexcution du projet
a y est ! Nous avons crit notre premier code qui ache un message trs populaire. Mais pour le moment, a ne fait rien. . . On veut voir ce que a donne ! Comme je vous comprends ! La premire chose faire est de transformer le langage C# que nous venons dcrire en programme excutable. Cette phase sappelle la gnration de la solution sous Visual C#. On lappelle souvent la compilation ou en anglais le build . Allez dans le menu Dboguer et cliquez sur Gnrer la solution (voir gure 2.9). Visual C# lance alors la gnration de la solution et on voit dans la barre des tches en bas gauche quil travaille jusqu nous indiquer que la gnration a russi, ainsi que vous pouvez le voir sur la gure 2.10. Si nous allons dans le rpertoire contenant la solution, nous pouvons voir dans le rpertoire MaPremiereApplication\MaPremiereApplication\bin\Release quil y a deux chiers : MaPremiereApplication.exe MaPremiereApplication.pdb Le premier est le chier excutable, possdant lextension .exe, qui est le rsultat du processus de gnration. Il sagit bien de notre application. Le second est un chier particulier quil nest pas utile de connatre pour linstant, nous allons lignorer ! 21

CHAPITRE 2. CRER UN PROJET AVEC VISUAL C# 2010 EXPRESS

Figure 2.9 Gnrer la solution

Figure 2.10 Gnration russie !

22

LEXCUTION DU PROJET Excutons notre application en lanant le chier excutable depuis lexplorateur de chiers. Dception, nous voyons peine un truc noirtre qui sache et qui se referme immdiatement. Que sest-il pass ? En fait, lapplication sest lance, a ach notre message et sest termine immdiatement. Et tout a un brin trop rapidement. . . a ne va pas tre pratique tout a. Heureusement, Visual C# Express arrive la rescousse. Retournons dans notre IDE prfr. Nous allons ajouter un bouton dans la barre doutils. Javoue ne pas comprendre pourquoi ce bouton est manquant dans linstallation par dfaut. Nous allons remdier ce problme en cliquant sur la petite che qui est ct de la barre doutils, tout droite, et qui nous ouvre le menu droulant permettant dajouter ou supprimer des boutons. Cliquez sur Personnaliser (voir gure 2.11).

Figure 2.11 Personnalisation de la barre doutils Cliquez sur Ajouter une commande, comme indiqu la gure 2.12. Allez dans la catgorie Dboguer et choisissez Excuter sans dbogage puis cliquez sur OK (voir gure 2.13). Enn, fermez la fentre. Comme vous pouvez lobserver sur la gure 2.14, vous avez dsormais un nouveau bouton dans la barre doutils ! Si vous avez la emme dajouter ce bouton, vous pouvez aussi utiliser le raccourci Ctrl + F5 ou bien cliquer sur ce nouveau bouton pour excuter lapplication depuis Visual C#. La console souvre enn, nous dlivrant le message tant attendu (voir gure 2.15). Le message est dsormais visible car Visual C# nous demande dappuyer sur une touche pour que lapplication se termine, ce qui nous laisse donc le temps dapprcier lexcution de notre superbe programme. La console noire que nous voyons sur la gure 2.15 est le rsultat de lexcution de notre programme. Si vous navez pas cr ce programme en mme temps que moi, vous pouvez voir quoi ressemble cette console. 23

CHAPITRE 2. CRER UN PROJET AVEC VISUAL C# 2010 EXPRESS

Figure 2.12 Ajouter une commande la barre doutils

Figure 2.13 Ajouter la commande dexcution sans dbogage

24

LEXCUTION DU PROJET

Figure 2.14 Excuter le projet sans dbogage

Figure 2.15 Notre premire application C# dans la console

25

CHAPITRE 2. CRER UN PROJET AVEC VISUAL C# 2010 EXPRESS Dans la suite du livre, nous utiliserons la syntaxe suivante pour remplacer limage de console du dessus :
Hello World !!

Waouh, a y est, notre premire application en C# ! ! ! Je suis er de nous, mais nous nallons pas en rester l, nous sommes dsormais n pars pour apprendre le C# !

En rsum
Visual C# Express est loutil de dveloppement gratuit de Microsoft permettant de dmarrer avec le C#. Visual Studio est loutil de dveloppement payant de Microsoft permettant dtre ecace dans le dveloppement dapplications .NET. Microsoft SQL Server Express est le moteur de base de donnes utilisable facilement avec Visual C# Express. Lenvironnement de dveloppement nous permet de crer du code C# qui sera contenu dans des projets, qui peuvent tre runis dans une solution.

26

Chapitre

3
Dicult :

La syntaxe gnrale du C#

ous allons aborder ici la syntaxe gnrale du C# dans le cadre dune application console. Nous allons utiliser trs souvent linstruction Console.WriteLine("..."); que nous avons vue au chapitre prcdent et qui est ddie lachage sur la console. Cest une instruction qui va savrer trs pratique pour notre apprentissage car nous pourrons avoir une reprsentation visuelle de ce que nous allons apprendre. Prparez-vous, nous plongeons petit petit dans lunivers du C#. Dans ce chapitre, nous allons nous attaquer la syntaxe gnrale du C# et nous serons capables de reconnatre les lignes de code et de quoi elles se composent.

27

CHAPITRE 3. LA SYNTAXE GNRALE DU C#

crire une ligne de code


Les lignes de code crites avec le langage de dveloppement C# doivent scrire dans des chiers dont lextension est .cs. Nous avons vu dans le chapitre prcdent que nous avons crit dans le chier Program.cs qui est le chier qui a t gnr par Visual C# lors de la cration du projet. Nous y avons notamment ajout une instruction permettant dacher du texte. Les lignes de code C# se lisent et scrivent de haut en bas et de gauche droite, comme un livre normal. Aussi, une instruction crite avant une autre sera en gnral excute avant celle-ci. Attention, chaque ligne de code doit tre correcte dun point de vue syntaxique sinon le compilateur ne saura pas la traduire en langage excutable. Par exemple, si la n de mon instruction, je retire le point-virgule ou si jorthographie mal le mot WriteLine, Visual C# Express me signalera quil y a un problme, comme vous pouvez le voir la gure 3.1.

Figure 3.1 Mise en valeur des erreurs de compilation Visual C# Express met en valeur un manque au niveau de la n de linstruction et il me souligne galement le mot WritLine . Dans la fentre du bas, il mindique quil y a deux erreurs et me donne des prcisions sur celles-ci avec ventuellement des pistes pour rsoudre ces erreurs. 28

LE CARACTRE DE TERMINAISON DE LIGNE Si je tente de lancer mon application (avec le raccourci Ctrl +F5 ), Visual C# Express va tenter de compiler et dexcuter lapplication. Ceci ntant pas possible, il machera un message indiquant quil y a des erreurs (voir gure 3.2).

Figure 3.2 Compilation impossible suite des erreurs Ce sont des erreurs de compilation quil va falloir rsoudre si lon souhaite que lapplication console puisse sexcuter. Nous allons voir dans les chapitres suivants comment crire correctement des instructions en C#. Mais il est important de noter lheure actuelle que le C# est sensible la casse, ce qui veut dire que les majuscules comptent ! Ainsi le mot WriteLine et le mot WriTEline sont deux mots bien distincts et peuvent potentiellement reprsenter deux instructions direntes. Ici, le deuxime mot est incorrect car il nexiste pas. Rappelez-vous bien que la casse est dterminante pour que lapplication puisse compiler.

Le caractre de terminaison de ligne


En gnral, une instruction en code C# scrit sur une ligne et se termine par un point-virgule. Ainsi, linstruction que nous avons vue plus haut :
1

Console . WriteLine ( " Hello World !! " ) ;

se termine au niveau du point-virgule. Il aurait t possible de remplacer le code crit :


1 2 3 4 5 6 7

class Program { static void Main ( string [] args ) { Console . WriteLine ( " Hello World !! " ) ; } }

par :
1

class Program { static void Main ( string [] args ) { Console . WriteLine ( " Hello World !! " ) ;}}

29

CHAPITRE 3. LA SYNTAXE GNRALE DU C# ou encore :


1 2 3 4 5 6 7 8 9 10 11 12

class Program { static void Main ( string [] args ) { Console . WriteLine ( " Hello World !! " } );

En gnral, pour que le code soit le plus lisible possible, on crit une instruction par ligne et on indente le code de faon ce que les blocs soient lisibles. Un bloc de code est dlimit par des accolades : { et }. Nous y reviendrons plus tard.

Indenter signie que chaque ligne de code qui fait partie dun mme bloc de code commence avec le mme retrait sur lditeur. Ce sont soit des tabulations, soit des espaces qui permettent de faire ce retrait. Visual C# Express nous aide pour faire correctement cette indentation quand nous crivons du code. Il galement peut remettre toute la page en forme avec la combinaison de touche : Ctrl + K + Ctrl + D . Dcortiquons prsent cette ligne de code :
1

Console . WriteLine ( " Hello World !! " ) ;

Pour simplier, nous dirons que nous appelons la mthode WriteLine qui permet dcrire une chane de caractres sur la console. Une mthode reprsente une fonctionnalit, crite avec du code, qui est utilisable par dautres bouts de code (par exemple, calculer la racine carre dun nombre ou acher du texte. . .). Linstruction "Hello World ! !" reprsente une chane de caractres et est passe en paramtre de la mthode Console.WriteLine laide des parenthses. La chane de caractres est dlimite par les guillemets. Enn, le point-virgule permet dindiquer que linstruction est termine et quon peut enchaner sur la suivante. Certains points ne sont peut-tre pas encore tout fait clairs, comme ce quest vraiment une mthode, ou comment utiliser des chanes de caractres, mais ne vous inquitez pas, nous allons y revenir plus en dtail dans les chapitres suivants et dcouvrir au fur 30

LES COMMENTAIRES et mesure les arcanes du C#.

Les commentaires
Pour faciliter la comprhension du code ou pour se rappeler un point prcis, il est possible de mettre des commentaires dans son code. Les commentaires sont ignors par le compilateur et nont quune valeur informative pour le dveloppeur. Dans un chier de code C# (.cs), on peut crire des commentaires de deux faons direntes : en commenant son commentaire par /* et en le terminant par */ ce qui permet dcrire un commentaire sur plusieurs lignes ; en utilisant // et tout ce qui se trouve aprs sur la mme ligne est alors un commentaire. Visual C# Express colore les commentaires en vert pour faciliter leur identication.
1 2 3

/* permet d ' afficher du texte sur la console */ Console . WriteLine ( " Hello World !! " ) ; // ne pas oublier le point virgule

noter lignes de code avec le raccourci quon peut commenter plusieurs clavier Ctrl + K + Ctrl + C et dcommenter lignes de code plusieurs avec le raccourci clavier Ctrl + K + Ctrl + U .

La compltion automatique
Visual C# Express est un formidable outil qui nous facilite tout moment la tche, notamment grce la compltion automatique. La compltion automatique est le fait de proposer de complter automatiquement ce que nous sommes en train dcrire en se basant sur ce que nous avons le droit de faire. Par exemple, si vous avez cherch crire linstruction :
1

Console . WriteLine ( " Hello World !! " ) ;

vous avez pu constater que lors de lappui sur la touche C , Visual C# Express nous ache une fentre avec tout ce qui commence par C , ainsi que vous pouvez lobserver la gure 3.3. Au fur et mesure de la saisie, il ane les propositions pour se positionner sur la plus pertinente. Il est possible de valider la proposition en appuyant sur la touche Entre . Non seulement cela nous conomise des appuis de touches, paresseux comme nous sommes, mais cela nous permet galement de vrier la syntaxe de ce que nous crivons et dobtenir galement une mini-aide sur ce que nous essayons dutiliser (voir gure 3.4). 31

CHAPITRE 3. LA SYNTAXE GNRALE DU C#

Figure 3.3 La compltion automatique facilite lcriture du code

Figure 3.4 La compltion automatique sane au fur et mesure de la saisie

32

LA COMPLTION AUTOMATIQUE Ainsi, ni les fautes de frappe qui entranent une erreur de compilation ou les listes de mots-cls dont il faut absolument retenir lorthographe ! De la mme faon, une fois que vous avez ni de saisir Console vous allez saisir le point . et Visual C# Express va nous proposer toute une srie dinstructions en rapport avec le dbut de linstruction (voir gure 3.5).

Figure 3.5 La compltion automatique Nous pourrons ainsi facilement nir de saisir WriteLine et ceci sans erreur dcriture, ni problme de majuscule.

En rsum
Le code C# est compos dune suite dinstructions qui se terminent par un pointvirgule. La syntaxe dun code C# doit tre correcte sinon nous aurons des erreurs de compilation. Il est possible de commenter son code grce aux caractres // , /* et */ . Visual C# Express dispose dun outil puissant qui permet daider complter ses instructions : la compltion automatique.

33

CHAPITRE 3. LA SYNTAXE GNRALE DU C#

34

Chapitre

4
Dicult :

Les variables

ans ce chapitre nous allons apprendre ce que sont les variables et comment ces lments indispensables vont nous rendre bien des services pour traiter de linformation susceptible de changer dans nos programmes informatiques. Nous continuerons en dcouvrant les dirents types de variables et nous ferons nos premires manipulations avec elles. Soyez attentifs ce chapitre, il est vraiment fondamental de bien comprendre quoi servent les variables lors du dveloppement dune application informatique.

35

CHAPITRE 4. LES VARIABLES

Quest-ce quune variable ?


Comme tous les langages de programmation, le C# va pouvoir conserver des donnes grce des variables. Ce sont en fait des blocs de mmoire qui vont contenir des nombres, des chanes de caractres, des dates ou plein dautres choses. Les variables vont nous permettre deectuer des calculs mathmatiques, denregistrer par exemple pour un site, lge du visiteur, de comparer des valeurs, etc. On peut les comparer des petits classeurs possdant une tiquette. On va pouvoir mettre des choses dans ces classeurs, par exemple, je mets 30 dans le classeur tiquet ge de Nicolas et 20 dans le classeur ge de Jrmie . Si je veux connatre lge de Nicolas, je nai qu regarder dans ce classeur pour obtenir 30. Je peux galement remplacer ce quil y a dans mon classeur par autre chose, par exemple changer 30 en 25. Je ne peux par contre pas mettre deux choses dans mon classeur, il na quun seul emplacement. Une variable est reprsente par son nom, caractrise par son type et contient une valeur. Le type correspond ce que la variable reprsente : un entier, une chane de caractres, une date, etc. Par exemple, lge dune personne pourrait tre stock sous la forme dun entier et accessible par la variable age, ce qui scrit en C# :
1

int age ;

On appelle ceci la dclaration de la variable age . Le mot-cl int permet dindiquer au compilateur que la variable age est un entier numrique. int correspond au dbut dinteger qui veut dire entier en anglais. Ici, la variable age na pas t initialise, elle ne pourra pas tre utilise car le compilateur ne sait pas quelle valeur il y a dans la variable age. Pour linitialiser (on parle galement daecter une valeur ) on utilisera loprateur gal ( = ).
1 2

int age ; age = 30 ;

Notre variable age possde dsormais lentier numrique 30 comme valeur. Linitialisation dune variable peut galement se faire au moment de sa dclaration. Ainsi, on pourra remplacer le code prcdent par :
1

int age = 30 ;

Pour dclarer une variable en C#, on commence toujours par indiquer son type (int, ici un entier) et son nom (ici, age). Il faudra imprativement aecter une valeur cette variable avec loprateur = , soit sur la mme instruction que la dclaration, soit un peu plus loin dans le code, mais dans tous les cas, avant lutilisation de cette variable. 36

QUEST-CE QUUNE VARIABLE ? Nous pouvons tout moment demander la valeur contenue dans la variable age, par exemple :
1 2

int age = 30 ; Console . WriteLine ( age ) ; // affiche 30

Il est possible de modier la valeur de la variable nimporte quel moment grce lemploi de loprateur = que nous avons dj vu :
1 2 3 4

int age = 30 ; Console . WriteLine ( age ) ; // affiche 30 age = 20 ; Console . WriteLine ( age ) ; // affiche 20

Vous pouvez nommer vos variables peu prs nimporte comment, quelques dtails prs. Les noms de variables ne peuvent pas avoir le mme nom quun type. Il sera alors impossible dappeler une variable int. Il est galement impossible dutiliser des caractres spciaux, comme des espaces ou des caractres de ponctuation. De mme, on ne pourra pas nommer une variable en commenant par des chires. Il est par contre possible dutiliser des accents dans les noms de variables, cependant ceci nest pas recommand et ne fait pas partie des bonnes pratiques de dveloppement. En eet, il est souvent recommand de nommer ses variables en anglais (langue qui ne contient pas daccents). Vous aurez not que, volontairement, je ne le fais pas dans ce livre an de ne pas ajouter une contrainte supplmentaire lors de la lecture du code. Mais libre vous de le faire ! En gnral, une variable commence par une minuscule et, si son nom reprsente plusieurs mots, on dmarrera un nouveau mot par une majuscule. Par exemple :
1

int ageDuVisiteur ;

Cest ce quon appelle le camel case. Suivant le principe de sensibilit la casse, il faut faire attention car ageduvisiteur et ageDuVisiteur seront deux variables direntes :

1 2 3 4

int ageduvisiteur = 30 ; int ageDuVisiteur = 20 ; Console . WriteLine ( ageduvisiteur ) ; // affiche 30 Console . WriteLine ( ageDuVisiteur ) ; // affiche 20

37

CHAPITRE 4. LES VARIABLES noter un dtail qui peut paratre vident, mais toutes les variables sont rinitialises chaque nouvelle excution du programme. Ds quon dmarre le programme, les classeurs sont vids, comme si on emmnageait dans de nouveaux locaux chaque fois. Il est donc impossible de faire persister une information entre deux excutions du programme en utilisant des variables. Pour ceci, on utilisera dautres solutions, comme enregistrer des valeurs dans un chier ou dans une base de donnes. Nous y reviendrons ultrieurement.

Les dirents types de variables


Nous avons vu juste au-dessus que la variable age pouvait tre un entier numrique grce au mot cl int. Le framework .NET dispose de beaucoup de types permettant de reprsenter beaucoup de choses direntes. Par exemple, nous pouvons stocker une chane de caractres grce au type string.
1

string prenom = " nicolas " ;

ou encore un dcimal avec :


1

decimal s oldeCompteBancaire = 100 ;

ou encore un boolean (qui reprsente une valeur vraie ou fausse) avec


1

bool estVrai = true ;

Il est important de stocker des donnes dans des variables ayant le bon type.

On ne peut par exemple pas stocker le prnom Nicolas dans un entier. Les principaux types de base du framework .NET sont : Vous verrez plus loin quil existe encore dautres types dans le framework .NET et quon peut galement construire les siens.

Aectations, oprations, concatnation


Il est possible deectuer des oprations sur les variables et entre les variables. Nous avons dj vu comment aecter une valeur une variable grce loprateur = .
1 2

int age = 30 ; string prenom = " nicolas " ;

38

AFFECTATIONS, OPRATIONS, CONCATNATION Type byte short int long float double decimal char string bool Description Entier de 0 255 Entier de -32768 32767 Entier de -2147483648 2147483647 Entier de -9223372036854775808 9223372036854775807 Nombre simple prcision de -3,402823e38 3,402823e38 Nombre double prcision de -1,79769313486232e308 1,79769313486232e308 Nombre dcimal convenant particulirement aux calculs nanciers (en raison de ses nombres signicatifs aprs la virgule) Reprsente un caractre Une chane de caractre Une valeur boolenne (vrai ou faux) Note : dans ce paragraphe, je vais vous donner plusieurs exemples daectations. Ces aectations seront faites sur la mme instruction que la dclaration pour des raisons de concision. Mais ces exemples sont videmment fonctionnels pour des aectations qui se situent un endroit dirent de la dclaration. En plus de la simple aectation, nous pouvons galement faire des oprations, par exemple :
1

int resultat = 2 * 3 ;

ou encore
1 2 3

int age1 = 20 ; int age2 = 30 ; int moyenne = ( age1 + age2 ) / 2 ;

Les oprateurs + , * , / ou encore - (que nous navons pas encore utilis) servent bien videmment faire les oprations mathmatiques qui leur correspondent, savoir respectivement laddition, la multiplication, la division et la soustraction. Vous aurez donc srement devin que la variable resultat contient 6 et que la moyenne vaut 25. Il est noter que les variables contiennent une valeur qui ne peut voluer quen aectant une nouvelle valeur cette variable. Ainsi, si jai le code suivant :
1 2 3 4

int age1 = 20 ; int age2 = 30 ; int moyenne = ( age1 + age2 ) / 2 ; age2 = 40 ;

la variable moyenne vaudra toujours 25 mme si jai chang la valeur de la variable age2. En eet, lors du calcul de la moyenne, jai rang dans mon classeur la valeur 25 grce loprateur daectation = et jai referm mon classeur. Le fait de changer la valeur du classeur age2 ninuence en rien le classeur moyenne dans la mesure o il 39

CHAPITRE 4. LES VARIABLES est ferm. Pour le modier, il faudrait rexcuter lopration daectation de la variable moyenne, en crivant nouveau linstruction de calcul, cest--dire :
1 2 3 4 5

int age1 = 20 ; int age2 = 30 ; int moyenne = ( age1 + age2 ) / 2 ; age2 = 40 ; moyenne = ( age1 + age2 ) / 2 ;

Loprateur + peut galement servir concatner des chanes de caractres, par exemple :
1 2 3 4

string codePostal = " 33000 " ; string ville = " Bordeaux " ; string adresse = codePostal + " " + ville ; Console . WriteLine ( adresse ) ; // affiche : 33000 Bordeaux

Dautres oprateurs particuliers existent que nous ne trouvons pas dans les cours de mathmatiques. Par exemple, loprateur ++ qui permet de raliser une incrmentation de 1, ou loprateur -- qui permet de faire une dcrmentation de 1. De mme, les oprateurs que nous avons dj vus peuvent se cumuler loprateur = pour simplier une opration qui prend une variable comme oprande et cette mme variable comme rsultat. Par exemple :
1 2 3 4 5 6

int age age age age age

age = 20 ; = age + 10 ; // age contient 30 ( addition ) = age ++; // age contient 31 ( incr mentation de 1 ) = age - -; // age contient 30 ( d cr mentation de 1 ) += 10 ; // quivalent age = age + 10 ( age contient 40 ) /= 2 ; // quivalent age = age / 2 = > ( age contient 20 )

Comme nous avons pu le voir dans nos cours de mathmatiques, il est possible de grouper des oprations avec des parenthses pour agir sur leurs priorits. Ainsi, linstruction prcdemment vue :
1

int moyenne = ( age1 + age2 ) / 2 ;

eectue bien la somme des deux ges avant de les diviser par 2, car les parenthses sont prioritaires. Cependant, linstruction suivante :
1

int moyenne = age1 + age2 / 2 ;

aurait commenc par diviser age2 par 2 et aurait ajout age1, ce qui naurait plus rien voir avec une moyenne. En eet, la division est prioritaire par rapport laddition. Attention, la division ici est un peu particulire.

Prenons cet exemple : 40

LES CARACTRES SPCIAUX DANS LES CHANES DE CARACTRES


1 2

int moyenne = 5 / 2 ; Console . WriteLine ( moyenne ) ;

Si nous lexcutons, nous voyons que moyenne vaut 2. 2 ? Si je me rappelle bien de mes cours de maths, cest plutt 2.5, non ?

Oui et non. Si nous divisions 5 par 2, nous obtenons bien 2.5. Par contre, ici nous divisons lentier 5 par lentier 2 et nous stockons le rsultat dans lentier moyenne. Le C# ralise en fait une division entire, cest--dire quil prend la partie entire de 2.5, cest--dire 2. De plus, lentier moyenne est incapable de stocker une valeur contenant des chires aprs la virgule. Il ne prendrait que la partie entire. Pour avoir 2.5, il faudrait utiliser le code suivant :
1 2

double moyenne = 5 . 0 / 2 . 0 ; Console . WriteLine ( moyenne ) ;

Ici, nous divisons deux doubles entre eux et nous stockons le rsultat dans un double. (Rappelez-vous, le type de donnes double permet de stocker des nombres virgule.) Le C# comprend quil sagit de double car nous avons ajout un .0 derrire. Sans a, il considre que les chires sont des entiers.

Les caractres spciaux dans les chanes de caractres


En ce qui concerne laectation de chanes de caractres, vous risquez davoir des surprises si vous tentez de mettre des caractres spciaux dans des variables de type string. En eet, une chane de caractres tant dlimite par des guillemets "" , comment faire pour quelle puisse contenir des guillemets ? Cest l quintervient le caractre spcial \ qui sera mettre juste devant le guillemet. Par exemple le code suivant :
1 2

string phrase = " Mon pr nom est \" Nicolas \" " ; Console . WriteLine ( phrase ) ;

achera :
Mon pr nom est " Nicolas "

Si vous avez test par vous-mme linstruction Console.WriteLine et enchan plusieurs instructions qui crivent des lignes, vous avez pu remarquer que nous passions 41

CHAPITRE 4. LES VARIABLES la ligne chaque fois. Cest le rle de linstruction WriteLine qui ache la chane de caractres et passe la ligne la n de la chane de caractres. Nous pouvons faire la mme chose en utilisant le caractre spcial \n . Il permet de passer la ligne chaque fois quil est rencontr. Ainsi, le code suivant :
1 2 3

string phrase = " Mon pr nom est \" Nicolas \" " ; Console . WriteLine ( phrase ) ; Console . WriteLine ( " Passe \ n \ nla \ nligne \ n \ n \ n " ) ;

achera chaque mot prcd du caractre \n sur une ligne dirente :

Mon pr nom est " Nicolas " Passe la ligne

Vous me diriez quon pourrait enchaner les Console.WriteLine et vous auriez raison. Mais les caractres spciaux nous permettent de faire dautres choses comme une tabulation par exemple grce au caractre spcial \t . Le code suivant :
1 2 3

Console . WriteLine ( " Choses faire : " ) ; Console . WriteLine ( " \ t - Arroser les plantes " ) ; Console . WriteLine ( " \ t - Laver la voiture " ) ;

permettra dacher des tabulations :


Choses faire : - Arroser les plantes - Laver la voiture

Nous avons vu que le caractre \ tait un caractre spcial et quil permettait de dire au compilateur que nous voulions lutiliser combin la valeur qui le suit, pour avoir une tabulation ou un retour la ligne. Comment pourrons-nous avoir une chane de caractres qui contienne ce fameux caractre ? Le principe est le mme, il sura de faire suivre ce fameux caractre spcial de luimme :
1 2

string fichier = " c :\\ repertoire \\ fichier . cs " ; Console . WriteLine ( fichier ) ;

La console achera alors les antislashs :


c :\ repertoire \ fichier . cs

Pour ce cas particulier, il est galement possible dutiliser la syntaxe suivante en utilisant le caractre spcial @ devant la chane de caractres : 42

LES CARACTRES SPCIAUX DANS LES CHANES DE CARACTRES


1

string fichier = @ " c :\ repertoire \ fichier . cs " ; // contient : c :\ repertoire \ fichier . cs

Bien sr, nous pouvons stocker des caractres spciaux dans des variables pour faire par exemple :
1 2 3

string sautDeLigne = " \ n " ; Console . WriteLine ( " Passer " + sautDeLigne + " " + sautDeLigne + " la " + sautDeLigne + " ligne " ) ;

Dans ce cas, la variable sautDeLigne peut tre remplace par une espce de variable qui existe dj dans le framework .NET, savoir Environment.NewLine. Ce qui permet davoir le code suivant :
1 2

Console . WriteLine ( " Passer " + Environment . NewLine + " " + Environment . NewLine + " la " + Environment . NewLine + " ligne " ) ;

qui achera :
Passer la ligne

Notez quil est possible de passer la ligne lors de lcriture dune instruction C# comme je lai fait dans le dernier bout de code an damliorer la lisibilit. Noubliez pas que cest le point-virgule qui termine linstruction.

Environment.NewLine ? Une espce de variable ? Quest-ce que cest que cette chose-l ? En fait, je triche un peu sur les mots. Pour faciliter la comprhension, on peut considrer que Environment.NewLine est une variable, au mme titre que la variable sautDeLigne que nous avons dnie. En ralit, cest un peu plus complexe quune variable. Nous dcouvrirons plus loin de quoi il sagit vraiment.

En rsum
Une variable est une zone mmoire permettant de stocker une valeur dun type particulier. Le C# possde plein de types prdnis, comme les entiers (int), les chanes de caractres (string), etc. 43

CHAPITRE 4. LES VARIABLES On utilise loprateur = pour aecter une valeur une variable. Il est possible de faire des oprations entre les variables.

44

Chapitre

5
Dicult :

Les instructions conditionnelles

ans nos programmes C#, nous allons rgulirement avoir besoin de faire des oprations en fonction dun rsultat prcdent. Par exemple, lors dun processus de connexion une application, si le login et le mot de passe sont bons, alors nous pouvons nous connecter, sinon nous acherons une erreur. Il sagit de ce que lon appelle une condition. Elle est value lors de lexcution et en fonction de son rsultat (vrai ou faux) nous ferons telle ou telle chose. Bien que relativement court, ce chapitre est trs important. Nhsitez pas le relire et vous entraner.

45

CHAPITRE 5. LES INSTRUCTIONS CONDITIONNELLES

Les oprateurs de comparaison


Une condition se construit grce des oprateurs de comparaison. On dnombre plusieurs oprateurs de comparaisons, les plus courants sont : Oprateur == != > < >= <= && || ! Description galit Dirence Suprieur Infrieur Suprieur ou gal Infrieur ou gal ET logique OU logique Ngation

Nous allons voir comment les utiliser en combinaison avec les instructions conditionnelles.

Linstruction if
Linstruction if permet dexcuter du code si une condition est vraie 1 . Par exemple :
1 2 3

decimal compteEnBanque = 300 ; if ( compteEnBanque >= 0 ) Console . WriteLine ( " Votre compte est cr diteur " ) ;

Ici, nous avons une variable contenant le solde de notre compte en banque. Si notre solde est suprieur ou gal 0 alors nous achons que le compte est crditeur. Pour acher que le compte est dbiteur, on pourrait tester si la valeur de la variable est infrieure 0 et acher que le compte est dbiteur :
1 2 3 4 5

decimal compteEnBanque = 300 ; if ( compteEnBanque >= 0 ) Console . WriteLine ( " Votre compte est cr diteur " ) ; if ( compteEnBanque < 0 ) Console . WriteLine ( " Votre compte est d biteur " ) ;

Une autre solution est dutiliser le mot-cl else, qui veut dire sinon en anglais. Si la valeur est vraie, alors on fait quelque chose, sinon, on fait autre chose , ce qui se traduit en C# par :
1 2 3

decimal compteEnBanque = 300 ; if ( compteEnBanque >= 0 ) Console . WriteLine ( " Votre compte est cr diteur " ) ;
1. Le mot if signie si en anglais.

46

LINSTRUCTION IF
4 5

else

Console . WriteLine ( " Votre compte est d biteur " ) ;

Il faut bien se rendre compte que linstruction if teste si une valeur est vraie (dans lexemple prcdent la comparaison compteEnBanque >= 0). On a vu rapidement dans les chapitres prcdents quil existait un type de variable qui permettait de stocker une valeur vraie ou fausse : le type bool, autrement appel boolen (boolean en anglais). Ainsi, il sera galement possible de tester la valeur dun boolen. Lexemple prcdent peut aussi scrire :
1 2 3 4 5 6

decimal compteEnBanque = 300 ; bool estCrediteur = ( compteEnBanque >= 0 ) ; if ( estCrediteur ) Console . WriteLine ( " Votre compte est cr diteur " ) ; else Console . WriteLine ( " Votre compte est d biteur " ) ;

Les parenthses autour de linstruction de comparaison sont facultatives, je les ai crites ici pour clairement identier que la variable estCrediteur va contenir une valeur qui est le rsultat de lopration de comparaison compte en banque est suprieur ou gal 0 , en loccurrence vrai. Voici dautres exemples pour vous permettre dapprhender plus prcisment le fonctionnement du type bool :
1 2 3 4 5 6 7

int age = 30 ; bool estAgeDe30Ans = age == 30 ; Console . WriteLine ( estAgeDe30Ans ) ; // affiche True bool estSuperieurA10 = age > 10 ; Console . WriteLine ( estSuperieurA10 ) ; // affiche True bool estDifferentDe30 = age != 30 ; Console . WriteLine ( estDifferentDe30 ) ; // affiche False

Un type bool peut prendre deux valeurs, vrai ou faux, qui scrivent avec les mots-cls true et false.
1 2 3 4 5

bool estVrai = true ; if ( estVrai ) Console . WriteLine ( "C ' est vrai ! " ) ; else Console . WriteLine ( "C ' est faux ! " ) ;

Il est galement possible de combiner les tests grce aux oprateurs de logique conditionnelle, par exemple && qui correspond loprateur ET . Dans lexemple qui suit, nous achons le message de bienvenue uniquement si le login est Nicolas ET que le mot de passe est test . Si lun des deux ne correspond pas, nous irons dans linstruction else.
1 2 3 4

string login = " Nicolas " ; string motDePasse = " test " ; if ( login == " Nicolas " && motDePasse == " test " ) Console . WriteLine ( " Bienvenue Nicolas " ) ;

47

CHAPITRE 5. LES INSTRUCTIONS CONDITIONNELLES


5 6

else

Console . WriteLine ( " Login incorrect " ) ;

Remarquons ici que nous avons utilis le test dgalit ==, ne pas confondre avec loprateur daection =. Cest une erreur classique de dbutant ! Dautres oprateurs de logique existent, nous avons notamment loprateur || qui correspond au OU logique :
1 2 3 4

if ( civilite == " Mme " || civilite == " Mlle " ) Console . WriteLine ( " Vous tes une femme " ) ; else Console . WriteLine ( " Vous tes un homme " ) ;

Lexemple parle de lui-mme ; si la civilit de la personne est Mme ou Mlle, alors nous avons faire une femme. Ici, si la premire condition du if est vraie alors la deuxime ne sera pas value. Cest un dtail ici, mais cela peut savrer important dans certaines situations dont une que nous verrons un peu plus loin. Un autre oprateur trs courant est la ngation que lon utilise avec loprateur ! . Par exemple :
1 2 3 4 5

bool estVrai = true ; if (! estVrai ) Console . WriteLine ( "C ' est faux ! " ) ; else Console . WriteLine ( "C ' est vrai ! " ) ;

Ce test pourrait se lire ainsi : si la ngation de la variable estVrai est vraie, alors on crira Cest faux ! La variable estVrai tant gale true, sa ngation vaut false. Dans cet exemple, le programme nous achera donc linstruction correspondant au else, savoir Cest vrai ! Rappelez-vous, nous avons dit quune instruction se nissait en gnral par un pointvirgule. Comment cela se fait-il alors quil ny ait pas de point-virgule la n du if ou du else ? Et si nous crivions lexemple prcdent de cette faon ?
1 2 3

bool estVrai = true ; if (! estVrai ) Console . WriteLine ( "C ' est faux ! " ) ; else Console . WriteLine ( "C ' est vrai ! " ) ;

Ceci est tout fait valable et permet de voir o sarrte vraiment linstruction grce au point-virgule. Cependant, nous crivons en gnral ces instructions de la premire faon an que celles-ci soient plus lisibles. Vous aurez loccasion de rencontrer dans les chapitres suivants dautres instructions qui ne se terminent pas obligatoirement par un point-virgule. 48

LINSTRUCTION SWITCH Nous verrons dans le chapitre suivant, comment excuter plusieurs instructions aprs une instruction conditionnelle en les groupant dans des blocs de code, dlimits par des accolades : { et }. Remarquons enn quil est possible denchaner les tests de manire traiter plusieurs conditions en utilisant la combinaison else if. Cela donne :
1 2 3 4 5 6 7 8

if ( civilite == " Mme " ) Console . WriteLine ( " Vous tes une femme " ) ; else if ( civilite == " Mlle " ) Console . WriteLine ( " Vous tes une femme non mari e " ) ; else if ( civilite == " M . " ) Console . WriteLine ( " Vous tes un homme " ) ; else Console . WriteLine ( " Je n ' ai pas pu d terminer votre civilit ");

Linstruction switch
Linstruction switch peut tre utilise lorsquune variable prend beaucoup de valeurs. Elle permet de simplier lcriture. Ainsi, linstruction suivante :
1 2 3 4 5 6 7

string civilite = " M . " ; if ( civilite == " M . " ) Console . WriteLine ( " Bonjour monsieur " ) ; if ( civilite == " Mme " ) Console . WriteLine ( " Bonjour madame " ) ; if ( civilite == " Mlle " ) Console . WriteLine ( " Bonjour mademoiselle " ) ;

pourra scrire :
1 2 3 4 5 6 7 8 9 10 11 12 13

string civilite = " M . " ; switch ( civilite ) { case " M . " : Console . WriteLine ( " Bonjour monsieur " ) ; break ; case " Mme " : Console . WriteLine ( " Bonjour madame " ) ; break ; case " Mlle " : Console . WriteLine ( " Bonjour mademoiselle " ) ; break ; }

switch commence par valuer la variable qui lui est passe entre parenthses. Avec le mot-cl case on numre les dirents cas possibles pour la variable et on excute 49

CHAPITRE 5. LES INSTRUCTIONS CONDITIONNELLES les instructions correspondantes jusquau mot-cl break qui signie que lon sort du switch. Nous pouvons galement indiquer une valeur par dfaut en utilisant le mot-cl default, ainsi dans lexemple suivant tout ce qui nest pas M. , Mme ou Mlle donnera lachage dun Bonjour inconnu :
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15

switch ( civilite ) { case " M . " : Console . WriteLine ( " Bonjour break ; case " Mme " : Console . WriteLine ( " Bonjour break ; case " Mlle " : Console . WriteLine ( " Bonjour break ; default : Console . WriteLine ( " Bonjour break ; }

monsieur " ) ; madame " ) ; mademoiselle " ) ; inconnu " ) ;

Nous pouvons galement enchaner plusieurs cas pour quils fassent la mme chose, ce qui reproduit le fonctionnement de loprateur logique OU ( || ). Par exemple, on pourra remplacer lexemple suivant :
1 2 3 4 5 6 7 8 9

string mois = " Janvier " ; if ( mois == " Mars " || mois == " Avril " || mois == " Mai " ) Console . WriteLine ( "C ' est le printemps " ) ; if ( mois == " Juin " || mois == " Juillet " || mois == " Ao t " ) Console . WriteLine ( "C ' est l ' t " ) ; if ( mois == " Septembre " || mois == " Octobre " || mois == " Novembre " ) Console . WriteLine ( "C ' est l ' automne " ) ; if ( mois == " D cembre " || mois == " Janvier " || mois == " F vrier ") Console . WriteLine ( "C ' est l ' hiver " ) ;

par :
1 2 3 4 5 6 7 8 9 10 11

switch ( mois ) { case " Mars " : case " Avril " : case " Mai " : Console . WriteLine ( "C ' est le printemps " ) ; break ; case " Juin " : case " Juillet " : case " Ao t " : Console . WriteLine ( "C ' est l ' t " ) ;

50

LINSTRUCTION SWITCH
12 13 14 15 16 17 18 19 20 21 22 23

break ; case " Septembre " : case " Octobre " : case " Novembre " : Console . WriteLine ( "C ' est l ' automne " ) ; break ; case " D cembre " : case " Janvier " : case " F vrier " : Console . WriteLine ( "C ' est l ' hiver " ) ; break ;

Ce qui allge quand mme lcriture et la rend beaucoup plus lisible !

En rsum
Les instructions conditionnelles permettent dexcuter des instructions seulement si une condition est vrie. On utilise en gnral le rsultat dune comparaison dans une instruction conditionnelle. Le C# possde beaucoup doprateurs de comparaison, comme loprateur dgalit ==, loprateur de supriorit >, dinfriorit <, etc.

51

CHAPITRE 5. LES INSTRUCTIONS CONDITIONNELLES

52

Chapitre

6
Dicult :

Les blocs de code et la porte dune variable

ous avons rgulirement utilis dans le chapitre prcdent les accolades ouvrantes et fermantes : { et }. Nous avons rapidement dit que ces accolades servaient crer des blocs de code.

Lutilisation daccolades implique galement une autre subtilit. Vous lavez vu dans le titre du chapitre, il sagit de la porte dune variable. Regardons prsent comment cela fonctionne.

53

CHAPITRE 6. LES BLOCS DE CODE ET LA PORTE DUNE VARIABLE

Les blocs de code


Les blocs de code permettent de grouper plusieurs instructions qui vont sexcuter dans le mme contexte. Cela peut tre le cas par exemple aprs un if, nous pourrions souhaiter eectuer plusieurs instructions. Par exemple :
1 2 3 4 5 6 7 8 9 10 11

decimal compteEnBanque = 300 ; if ( compteEnBanque >= 0 ) { Console . WriteLine ( " Votre compte est cr diteur " ) ; Console . WriteLine ( " Voici comment ouvrir un livret ... " ) ; } else { Console . WriteLine ( " Votre compte est d biteur " ) ; Console . WriteLine ( "N ' oubliez pas que les frais de d couverts sont de ... " ) ; }

Ici, nous enchanons deux Console.WriteLine en fonction du rsultat de la comparaison de compteEnBanque avec 0. Les blocs de code seront utiles ds quon voudra regrouper plusieurs instructions. Cest le cas pour les instructions conditionnelles mais nous verrons beaucoup dautres utilisations, comme le switch que nous avons vu juste avant, les boucles ou les mthodes que nous allons aborder dans le chapitre suivant.

La porte dune variable


C# . . . porte . . . on se croirait dans un cours de musique ! En fait, la porte dune variable est la zone de code dans laquelle une variable est utilisable. Elle correspond en gnral au bloc de code dans lequel est dnie la variable. Ainsi, le code suivant :
1 2 3 4 5 6 7 8 9 10 11 12 13

static void Main ( string [] args ) { string prenom = " Nicolas " ; string civilite = " M . " ; if ( prenom == " Nicolas " ) { int age = 30 ; Console . WriteLine ( " Votre ge est : " + age ) ; switch ( civilite ) { case " M . " : Console . WriteLine ( " Vous tes un homme de " + age + " ans " ) ; break ;

54

LA PORTE DUNE VARIABLE


14 15 16 17 18 19 20 21 22 23

} } if ( age >= 18 ) { Console . WriteLine ( prenom + " , vous tes majeur " ) ; }

case " Mme " : Console . WriteLine ( " Vous tes une femme de " + age + " ans " ) ; break ;

est incorrect et provoquera une erreur de compilation. En eet, nous essayons daccder la variable age en dehors du bloc de code o elle est dnie. Nous voyons que cette variable est dnie dans le bloc qui est excut lorsque le test dgalit du prnom avec la chane Nicolas est vrai alors que nous essayons de la comparer 18 dans un endroit o elle nexiste plus. Nous pouvons utiliser age dans tout le premier if, et dans les sous-blocs de code, comme cest le cas dans le sous-bloc du switch, mais pas en dehors du bloc de code dans lequel la variable est dnie. Ainsi, la variable prenom est accessible dans le dernier if car elle a t dnie dans un bloc pre. Vous noterez quici, la compltion nous est utile. En eet, Visual C# Express propose de complter le nom de la variable dans un bloc o elle est accessible. Dans un bloc o elle ne lest pas, la compltion ne nous la propose pas. Comme Visual C# Express est malin, si la compltion ne propose pas ce que vous souhaitez, cest que vous ny avez pas le droit ! Cela peut tre parce que la porte ne vous lautorise pas. Pour corriger lexemple prcdent, il faut dclarer la variable age au mme niveau que la variable prenom. Ok, mais alors, pourquoi on ne dclarerait pas tout au dbut une bonne fois pour toutes ? Cela viterait ces erreurs. . . non ? Eh non, ce nest pas possible ! Gnralement, lutilisation de variables accessibles de partout est une mauvaise pratique de dveloppement. Mme si on peut avoir un quivalent en C#, il faut se rappeler que plus une variable est utilise dans la plus petite porte possible, mieux elle sera utilise et plus elle sera pertinente. Essayez donc de dterminer le bloc de code minimal o lutilisation de la variable est adapte.

En rsum
Un bloc de code permet de regrouper des instructions qui commencent par { et qui nissent par }. Une variable dnie lintrieur dun bloc de code aura pour porte ce bloc de code.

55

CHAPITRE 6. LES BLOCS DE CODE ET LA PORTE DUNE VARIABLE

56

Chapitre

7
Dicult :

Les mthodes

lment indispensable de tout programme informatique, une mthode regroupe un ensemble dinstructions, pouvant prendre des paramtres et pouvant renvoyer une valeur. Lors de vos dveloppements, vous allez avoir besoin de crer beaucoup de mthodes. Nous allons dcouvrir les mthodes dans ce chapitre mais nous y reviendrons petit petit tout au long de louvrage et vous aurez ainsi loccasion dapprofondir vos connaissances. Vous pourrez trouver de temps en temps le mot fonction la place du mot mthode . Cela signie la mme chose. Cest une relique du pass correspondant un ancien mode de dveloppement qui sutilise de moins en moins, de mme que le terme procdure qui est encore plus vieux !

57

CHAPITRE 7. LES MTHODES

Crer une mthode


Le but de la mthode est de factoriser du code an dviter davoir rpter sans cesse le mme code et ceci pour deux raisons essentielles : dj parce que lhomme est un tre paresseux qui utilise son intelligence pour viter tout travail inutile ; ensuite parce que si jamais il y a quelque chose corriger dans ce bout de code et sil est dupliqu plusieurs endroits, nous allons devoir faire une correction dans tous ces endroits. Si le code est factoris un unique endroit, nous ferons une unique correction. Oui, oui, encore la paresse mais cela permet aussi dviter doublier un bout de code dans un endroit cach. Ce souci de factorisation est connu comme le principe DRY, qui est lacronyme des mots anglais Dont Repeat Yourself (Ne vous rptez pas). Le but est de ne jamais ( quelques exceptions prs videmment. . .) avoir rcrire la mme ligne de code. Par exemple, imaginons quelques instructions qui soccupent dcrire un message de bienvenue avec le nom de lutilisateur. Le code C# pourrait tre :
1 2 3

Console . WriteLine ( " Bonjour Nicolas " ) ; Console . WriteLine ( " ------- " + Environment . NewLine ) ; Console . WriteLine ( " \ tBienvenue dans le monde merveilleux du C # " );

Dans linstruction Console.WriteLine que nous utilisons rgulirement, WriteLine est une mthode. Si plus tard on veut racher le message de bienvenue, il faudra rcrire ces quatre lignes de code moins que nous utilisions une mthode :
1 2 3 4 5 6

static void AffichageBienvenue () { Console . WriteLine ( " Bonjour Nicolas " ) ; Console . WriteLine ( " ------- " + Environment . NewLine ) ; Console . WriteLine ( " \ tBienvenue dans le monde merveilleux du C#"); }

Dans lexemple prcdent, je dnis une mthode qui sappelle AffichageBienvenue. Linstruction :
1

static void AffichageBienvenue ()

est ce quon appelle la signature de la mthode. Elle nous renseigne sur les paramtres de la mthode et sur ce quelle va renvoyer. Le mot-cl void signie que la mthode ne renvoie rien. Les parenthses vides la n de la signature indiquent que la mthode na pas de paramtre. 58

LA MTHODE SPCIALE MAIN()

Cest la forme de la mthode la plus simple possible.

Le mot-cl static ne nous intresse pas pour linstant, mais sachez quil sert indiquer que la mthode est toujours disponible et prte tre utilise. Dans ce contexte, il est obligatoire. Nous y reviendrons. En dessous de la signature de la mthode, nous retrouvons les accolades. Elles permettent de dlimiter la mthode. Le bloc de code ainsi form constitue ce quon appelle le corps de la mthode . En rsum, pour dclarer une mthode, nous aurons :
1 2 3 4

Signature de la m thode { Corps de la m thode }

Nous pouvons dsormais appeler cette mthode (cest--dire lexcuter), dans notre programme grce son nom. Par exemple, ici je lappelle trs facilement deux fois de suite :
1 2 3 4 5 6 7 8 9 10 11 12

static void Main ( string [] args ) { Af fi ch age Bienvenue () ; Af fi ch age Bienvenue () ; } static void AffichageBienvenue () { Console . WriteLine ( " Bonjour Nicolas " ) ; Console . WriteLine ( " ------- " + Environment . NewLine ) ; Console . WriteLine ( " \ tBienvenue dans le monde merveilleux du C#"); }

Et tout a, sans eort ! Cest quand mme plus simple et plus clair, non ?

La mthode spciale Main()


La signature du premier bloc ci-dessus ne vous rappelle rien ? Mais si, elle ressemble beaucoup celle de la mthode que nous avons cre prcdemment. Il sagit dune mthode spciale, la mthode Main().

Elle a t gnre par Visual C# Express lorsque nous avons cr le projet Console. 59

CHAPITRE 7. LES MTHODES Cette mthode est en fait le point dentre de lapplication, cest--dire que quand le CLR tente dexcuter notre application, il recherche cette mthode an de pouvoir commencer excuter des instructions partir delle. Sil ne la trouve pas, alors, il ne pourra pas excuter notre application. Cest pour cela quil est important que cette mthode soit accessible de partout ; rappelez-vous, cest le rle du mot-cl static que nous aurons loccasion dtudier plus en dtail ultrieurement. Visual C# Express nous garde bien de cette erreur. En eet, si vous supprimez cette mthode (ou que vous enlevez le mot-cl static) et que vous tentez de compiler notre application, vous aurez le message derreur suivant :
Erreur 1 Le programme C :\ Users \ Nico \ Documents \ Visual Studio 2010\ Projects \ C \#\ MaPr emier eAppli catio n \ M a P r e m i er eAppl icatio n \ obj \ x86 \ Release \ Ma Premie reAppl icati on . exe ne contient pas une m thode Main statique appropri e pour un point d entr e

Le message derreur est clair. Il a besoin dune mthode Main() pour dmarrer. Voil pour cette mthode spciale Main(). Elle est indispensable dans tout programme excutable et cest par-l que le programme dmarre. Les lecteurs attentifs auront remarqu que cette mthode possde certains lments dans la signature, entre les parenthses. . . Des paramtres ! Dcouvrons-les dans la prochaine section. . .

Paramtres dune mthode


Super, nous savons crer des mthodes. Nous allons pouvoir crer une mthode qui permet de souhaiter la bienvenue la personne qui vient de se connecter notre application. Si cest Nicolas qui vient de se connecter, nous allons appeler la mthode AffichageBienvenueNicolas(). Si cest Jrmie, nous appellerons la mthode AffichageBienvenueJeremie(), etc.
1 2 3 4 5 6 7 8 9 10 11

static void A f f i c h a ge B i e n v e n u e Ni c o l a s () { Console . WriteLine ( " Bonjour Nicolas " ) ; Console . WriteLine ( " ------- " + Environment . NewLine ) ; Console . WriteLine ( " \ tBienvenue dans le monde merveilleux du C#"); } static void A f f i c h a ge B i e n v e n u e Je r e m i e () { Console . WriteLine ( " Bonjour J r mie " ) ; Console . WriteLine ( " ------- " + Environment . NewLine ) ;

60

PARAMTRES DUNE MTHODE


12 13

Console . WriteLine ( " \ tBienvenue dans le monde merveilleux du C#");

Finalement, ce nest pas si pratique que a. . . Alors que nous venions juste dvoquer le principe DRY, nous nous retrouvons avec deux mthodes quasiment identiques qui ne dirent que dun tout petit dtail. Cest l quinterviennent les paramtres de mthode. Nous lavons voqu au paragraphe prcdent, il est possible de passer des paramtres une mthode. Ainsi, nous pourrons utiliser les valeurs de ces paramtres dans le corps de nos mthodes, les mthodes en deviendront dautant plus gnriques. Dans notre exemple dachage de message de bienvenue, il est vident que le nom de lutilisateur sera un paramtre de la mthode. Les paramtres scrivent lintrieur des parenthses qui suivent le nom de la mthode. Nous devons indiquer le type du paramtre ainsi que le nom de la variable qui le reprsentera au sein de la mthode. Il est possible de passer plusieurs paramtres une mthode, on les sparera avec une virgule. Par exemple :
1 2 3 4 5

static void DireBonjour ( string prenom , int age ) { Console . WriteLine ( " Bonjour " + prenom ) ; Console . WriteLine ( " Vous avez " + age + " ans " ) ; }

Ici, la mthode DireBonjour prend en paramtres une chane de caractres prenom et un entier age. La mthode ache Bonjour ainsi que le contenu de la variable prenom. De mme, juste en dessous, elle ache lge qui a t pass en paramtre. Nous pourrons appeler cette mthode de cette faon, depuis la mthode Main() :
1 2 3 4 5

static void Main ( string [] args ) { DireBonjour ( " Nicolas " , 30 ) ; DireBonjour ( " J r mie " , 20 ) ; }

Ce qui permettra dacher facilement un message de bienvenue personnalis :


Bonjour Nicolas Vous avez 30 ans Bonjour J r mie Vous avez 20 ans

Bien sr, il est obligatoire de fournir en paramtre dune mthode une variable de mme type que le paramtre attendu. Dans le cas contraire, le compilateur est incapable de mettre la donne qui a t passe dans le paramtre. Dailleurs, si vous ne fournissez pas le bon paramtre, vous avez droit une erreur de compilation. 61

CHAPITRE 7. LES MTHODES Par exemple, si vous appelez la mthode avec les paramtres suivants :
1

DireBonjour ( 10 , 10 ) ;

Vous aurez lerreur de compilation suivante :


impossible de convertir de int en string

Il est videmment possible de passer des variables une mthode, cela fonctionne de la mme faon :
1 2

string prenom = " Nicolas " ; DireBonjour ( prenom , 30 ) ;

Nous allons revenir plus en dtail sur ce quil se passe exactement lorsque nous aborderons le chapitre sur le mode de passage des paramtres. Vous voyez, cela ressemble beaucoup ce que nous avons dj fait avec la mthode Console.WriteLine(). Facile, non ? La mthode Console.WriteLine fait partie de la bibliothque du framework .NET. Elle est utilise pour crire des chanes de caractres, des nombres ou plein dautres choses sur la console. Le framework .NET contient normment de mthodes utilitaires de toutes sortes. Nous y reviendrons un peu plus tard. Vous aurez peut-tre remarqu un dtail : nous avons prx toutes nos mthodes du mot-cl static. Jai dit que ctait obligatoire dans notre contexte, pour tre plus prcis, cest parce que la mthode Main() est statique que nous sommes obligs de crer des mthodes statiques. On a dit que la mthode Main() tait obligatoirement statique parce quelle devait tre accessible de partout an que le CLR puisse trouver le point dentre de notre programme. Or, une mthode statique ne peut appeler que des mthodes statiques, cest pour cela que nous sommes obligs (pour linstant) de prxer nos mthodes par le mot-cl static. Nous dcrirons ce que recouvre exactement le mot-cl static dans la partie suivante.

Retour dune mthode


Une mthode peut aussi renvoyer une valeur, par exemple un calcul. Cest souvent dailleurs son utilit premire. On pourrait imaginer par exemple une mthode qui calcule la longueur de lhypotnuse partir des deux cts dun triangle rectangle. Sachant que a2 + b2 = c2 , nous pouvons imaginer une mthode qui prenne en paramtres la longueur des deux cts, fasse la somme de leurs carrs et renvoie la racine carre du rsultat. Cest ce que fait la mthode suivante :
1 2 3 4

static double LongueurHypotenuse ( double a , double b ) { double sommeDesCarres = a * a + b * b ; double resultat = Math . Sqrt ( sommeDesCarres ) ;

62

RETOUR DUNE MTHODE


5 6

return resultat ;

Vous aurez remarqu que la signature de la mthode commence par le mot-cl double, qui indique quelle va nous renvoyer une valeur de type double. Comme on la vu, double a et double b sont deux paramtres de la mthode et sont du type double. La mthode Math.Sqrt est une mthode du framework .NET, au mme titre que la mthode Console.WriteLine, qui permet de renvoyer la racine carre dun nombre. Elle prend en paramtre un double et nous retourne une valeur de type double galement qui correspond la racine carre du paramtre. Cest tout naturellement que nous stockons ce rsultat dans une variable grce loprateur daectation =. la n de la mthode, le mot-cl return indique que la mthode renvoie la valeur la mthode qui la appele. Ici, nous renvoyons le rsultat. Cette mthode pourra sutiliser ainsi :
1 2 3 4 5 6 7

static void Main ( string [] args ) { double valeur = LongueurHypotenuse (1 , 3 ) ; Console . WriteLine ( valeur ) ; valeur = LongueurHypotenuse ( 10 , 10 ) ; Console . WriteLine ( valeur ) ; }

Comme prcdemment, nous utilisons une variable pour stocker le rsultat de lexcution de la mthode :
3 ,16227766016838 14 ,142135623731

Notez quil est galement possible de se passer dune variable intermdiaire pour stocker le rsultat. Ainsi, nous pourrons par exemple crire :
1

Console . WriteLine ( " Le r sultat est : " + LongueurHypotenuse (1 , 3));

Avec cette criture le rsultat renvoy par la mthode LongueurHypotenuse est directement concatn la chane Le rsultat est : et pass en paramtre la mthode Console.WriteLine. Remarquez quon a fait lopration : a*a pour mettre a au carr. On aurait galement pu faire Math.Pow(a, 2) qui permet de faire la mme chose la dirence que Pow permet de mettre la puissance que lon souhaite. Ainsi, Math.Pow(a, 3) permet de mettre a au cube. Il faut savoir que le mot-cl return peut apparatre nimporte quel endroit de la mthode. Il interrompt alors lexcution de celle-ci et renvoie la valeur passe. Ce motcl est obligatoire, sans cela la mthode ne compile pas. Il est galement primordial que tous les chemins possibles dune mthode renvoient quelque chose. Les chemins sont dtermins par les instructions conditionnelles que nous avons vues prcdemment. 63

CHAPITRE 7. LES MTHODES Ainsi, lexemple suivant est correct :


1 2 3 4 5 6 7

static string Conjugaison ( string genre ) { if ( genre == " homme " ) return " " ; else return " e " ; }

car peu importe la valeur de la variable genre, la mthode renverra une chane. Alors que celui-ci :
1 2 3 4 5 6 7 8 9 10

static string Conjugaison ( string genre ) { if ( genre == " homme " ) return " " ; else { if ( genre == " femme " ) return " e " ; } }

est incorrect. En eet, que renvoie la mthode si la variable genre contient autre chose que homme ou femme ? En gnral, Visual C# Express nous indiquera quil dtecte un problme avec une erreur de compilation. Nous pourrons corriger ceci avec, par exemple :
1 2 3 4 5 6 7 8 9 10 11

static string Conjugaison ( string genre ) { if ( genre == " homme " ) return " " ; else { if ( genre == " femme " ) return " e " ; } return " " ; }

noter que "" correspond une chane vide et peut galement scrire string.Empty. Nous avons vu dans le dbut du chapitre quil tait possible de crer des mthodes qui ne retournent rien. Dans ce cas, on peut utiliser le mot-cl return sans valeur qui le suit pour stopper lexcution de la mthode. Par exemple : 64

RETOUR DUNE MTHODE


1 2 3 4 5 6

static void Bonjour ( string prenom ) { if ( prenom == " inconnu " ) return ; Console . WriteLine ( " Bonjour " + prenom ) ; }

Ainsi, si la variable prenom vaut inconnu , alors nous quittons la mthode Bonjour et linstruction Console.WriteLine ne sera pas excute.

En rsum
Une mthode regroupe un ensemble dinstructions pouvant prendre des paramtres et pouvant renvoyer une valeur. Les paramtres dune mthode doivent tre utiliss avec le bon type. Une mthode qui ne renvoie rien est prxe du mot-cl void. Le point dentre dun programme est la mthode statique Main(). Le mot-cl return permet de renvoyer une valeur du type de retour de la mthode, lappelant de cette mthode.

65

CHAPITRE 7. LES MTHODES

66

Chapitre

8
Dicult :

Tableaux, listes et numrations

ans les chapitres prcdents, nous avons pu utiliser les types de base du framework .NET, comme int, string, double, etc. Nous allons dcouvrir ici dautres types qui vont savrer trs utiles dans la construction de nos applications informatiques.

Une fois bien matriss, vous ne pourrez plus vous en passer !

67

CHAPITRE 8. TABLEAUX, LISTES ET NUMRATIONS

Les tableaux
Voici le premier nouveau type que nous allons tudier, le type tableau. En dclarant une variable de type tableau, nous allons en fait utiliser une variable qui contient une suite de variables du mme type. Prenons cet exemple :
1

string [] jours = new string [] { " Lundi " , " Mardi " , " Mercredi " , " Jeudi " , " Vendredi " , " Samedi " , " Dimanche " };

Nous dnissons ici un tableau de chanes de caractres qui contient 7 chanes de caractres : les jours de la semaine. Ne faites pas attention loprateur new pour linstant, nous y reviendrons plus tard ; il permet simplement de crer le tableau. Les crochets [] qui suivent le nom du type permettent de signaler au compilateur que nous souhaitons utiliser un tableau de ce type-l, ici le type string. Un tableau, cest un peu comme une armoire dans laquelle on range des variables. Chaque variable est pose sur une tagre. Pour accder la variable qui est pose sur une tagre, on utilise le nom de larmoire et on indique lindice de ltagre o est stocke la variable, en utilisant des crochets [] :
1 2 3

Console . WriteLine ( jours [ 3 ]) ; // affiche Jeudi Console . WriteLine ( jours [ 0 ]) ; // affiche Lundi Console . WriteLine ( jours [ 10 ]) ; // provoque une erreur d ' ex cution car l ' indice n ' existe pas

Le premier lment du tableau se situe lindice 0 et le dernier se situe lindice taille du tableau 1 , cest--dire 6 dans notre exemple. Si on tente daccder un indice qui nexiste pas, lapplication lvera une erreur. Il est possible de parcourir lensemble dun tableau avec linstruction suivante :
1 2 3 4 5

string [] jours = new string [] { " Lundi " , " Mardi " , " Mercredi " , " Jeudi " , " Vendredi " , " Samedi " , " Dimanche " }; for ( int i = 0 ; i < jours . Length ; i ++) { Console . WriteLine ( jours [ i ]) ; }

Nous parcourons ici les lments de 0 taille-1 et nous achons llment du tableau correspondant lindice en cours. Ce qui nous donne :
Lundi Mardi Mercredi Jeudi Vendredi Samedi Dimanche

Revenons prsent sur la dclaration du tableau : 68

LES TABLEAUX
1

string [] jours = new string [] { " Lundi " , " Mardi " , " Mercredi " , " Jeudi " , " Vendredi " , " Samedi " , " Dimanche " };

Cette criture permet de crer un tableau qui contient 7 lments et daecter une valeur chaque lment du tableau. Il sagit en fait ici dune criture simplie. Cette criture est quivalente celle-ci :
1 2 3 4 5 6 7 8

string [] jours [ 0 ] jours [ 1 ] jours [ 2 ] jours [ 3 ] jours [ 4 ] jours [ 5 ] jours [ 6 ]

jours = new string [ 7 ]; = " Lundi " ; = " Mardi " ; = " Mercredi " ; = " Jeudi " ; = " Vendredi " ; = " Samedi " ; = " Dimanche " ;

qui est beaucoup plus verbeuse, mais dun autre ct, plus explicite. La premire instruction cre un tableau qui peut contenir 7 lments. Cest la taille du tableau, elle ne peut pas changer. Chaque instruction suivante aecte une valeur un indice du tableau. Rappelez-vous, un tableau commence lindice 0 et va jusqu lindice taille 1. Il est possible facilement de faire des oprations sur un tableau, comme un tri. On pourra utiliser la mthode Array.Sort(). Par exemple :
1

Array . Sort ( jours ) ;

Avec cette instruction, le tableau sera class par ordre alphabtique. Vous aurez loccasion de voir dautres mthodes dans des chapitres ultrieurs. Ainsi, le code suivant :
1 2 3 4 5 6

string [] jours = new string [] { " Lundi " , " Mardi " , " Mercredi " , " Jeudi " , " Vendredi " , " Samedi " , " Dimanche " }; Array . Sort ( jours ) ; for ( int i = 0 ; i < jours . Length ; i ++) { Console . WriteLine ( jours [ i ]) ; }

produira un classement alphabtique des entres du tableau :


Dimanche Jeudi Lundi Mardi Mercredi Samedi Vendredi

Ce qui est trs inutile ! Le tableau jours est ce que lon appelle un tableau une dimension. Il est galement possible de crer des tableaux N dimensions, il est cependant assez rare de dpasser 69

CHAPITRE 8. TABLEAUX, LISTES ET NUMRATIONS deux dimensions. Cest surtout utile lorsquon manipule des matrices. Nous ntudierons pas les tableaux plus dune dimension dans ce livre car ils risquent de vraiment peu vous servir dans vos premires applications. Par contre, le type liste , que nous allons voir tout de suite, vous servira abondamment !

Les listes
Un autre type que nous allons beaucoup utiliser est la liste. Nous allons voir comment ce type fonctionne mais sans en faire une tude exhaustive car elle pourrait tre bien longue et ennuyeuse. Regardons cet exemple :
1 2 3 4 5 6 7 8 9 10 11

List < int > chiffres = new List < int >() ; chiffres . Add ( 8 ) ; // chiffres contient chiffres . Add ( 9 ) ; // chiffres contient chiffres . Add ( 4 ) ; // chiffres contient

// cr ation de la liste 8 8, 9 8, 9, 4

chiffres . RemoveAt ( 1 ) ; // chiffres contient 8 , 4 foreach ( int chiffre in chiffres ) { Console . WriteLine ( chiffre ) ; }

La premire ligne permet de crer la liste. Nous reviendrons sur cette instruction un peu plus loin dans ce chapitre. Il sagit dune liste dentiers. Nous ajoutons des entiers la liste grce la mthode Add(). Nous ajoutons en loccurrence les entiers 8, 9 et 4. La mthode RemoveAt() permet de supprimer un lment en utilisant son indice, ici nous supprimons le deuxime entier, cest--dire 9. Comme les tableaux, le premier lment de la liste commence lindice 0.

Aprs cette instruction, la liste contient les entiers 8 et 4. Enn, nous parcourons les lments de la liste grce linstruction foreach. Nous y reviendrons en dtail lors du chapitre sur les boucles. Pour linstant, nous avons juste besoin de comprendre que nous achons tous les lments de la liste, comme ci-dessous :
8 4

Les lecteurs assidus auront remarqu que la construction de la liste est un peu particulire. On observe en eet lutilisation de chevrons <> pour indiquer le type de la liste. 70

LES LISTES Pour avoir une liste dentier, il sut dindiquer le type int lintrieur des chevrons. Ainsi, il ne sera pas possible dajouter autre chose quun entier dans cette liste. Par exemple, linstruction suivante provoque une erreur de compilation :
1 2

List < int > chiffres = new List < int >() ; // cr ation de la liste chiffres . Add ( " cha ne " ) ; // ne compile pas

videmment, on ne peut pas ajouter des choux une liste de carottes ! De la mme faon, si lon souhaite crer une liste de chane de caractres, nous pourrons utiliser le type string lintrieur des chevrons :
1 2 3

List < string > cha nes = new List < string >() ; // cr ation de la liste cha nes . Add ( " cha ne " ) ; // compilation OK cha nes . Add ( 1 ) ; // compilation KO , on ne peut pas ajouter un entier dans une liste de cha nes de caract res

Les listes possdent des mthodes bien pratiques qui permettent toutes sortes doprations. Par exemple, la mthode IndexOf() permet de rechercher un lment dans la liste et de renvoyer son indice.
1 2 3 4 5 6 7 8 9 10

List < string > jours = new List < string >() ; jours . Add ( " Lundi " ) ; jours . Add ( " Mardi " ) ; jours . Add ( " Mercredi " ) ; jours . Add ( " Jeudi " ) ; jours . Add ( " Vendredi " ) ; jours . Add ( " Samedi " ) ; jours . Add ( " Dimanche " ) ; int indice = jours . IndexOf ( " Mercredi " ) ; // indice vaut 2

Nous aurons loccasion de voir dautres utilisations de mthodes de la liste dans les chapitres suivants. La liste que nous venons de voir (List<>) est en fait ce que lon appelle un type gnrique. Nous nallons pas rentrer dans le dtail de ce quest un type gnrique pour linstant, mais il faut juste savoir quun type gnrique permet dtre spcialis par un type concret. Pour notre liste, cette gnricit permet dindiquer de quel type est la liste, une liste dentiers ou une liste de chanes de caractres, etc. Ne vous inquitez pas si tout ceci nest pas parfaitement clair, nous reviendrons plus en dtail sur les gnriques dans un chapitre ultrieur. Le but ici est de commencer se familiariser avec le type List<> que nous utiliserons rgulirement et les exemples que nous verrons permettront dapprhender les subtilits de ce type. noter quil existe galement une criture simplie des listes. En eet, il est possible de remplacer :
1 2 3

List < string > jours = new List < string >() ; jours . Add ( " Lundi " ) ; jours . Add ( " Mardi " ) ;

71

CHAPITRE 8. TABLEAUX, LISTES ET NUMRATIONS


4 5 6 7 8

jours . Add ( " Mercredi " ) ; jours . Add ( " Jeudi " ) ; jours . Add ( " Vendredi " ) ; jours . Add ( " Samedi " ) ; jours . Add ( " Dimanche " ) ;

par
1

List < string > jours = new List < string > { " Lundi " , " Mardi " , " Mercredi " , " Jeudi " , " Vendredi " , " Samedi " , " Dimanche " };

un peu comme pour les tableaux.

Liste ou tableau ?
Vous aurez remarqu que les deux types, tableau et liste, se ressemblent beaucoup. Essayons de voir ce qui les direncie an de savoir quel type choisir entre les deux. En fait, une des grosses dirences est que le tableau peut tre multidimensionnel. Cest un point important mais dans le cadre de vos premires applications, il est relativement rare davoir sen servir. Cela sert le plus souvent dans le dveloppement de jeux ou lorsque lon souhaite faire des calculs 3D. Par contre, le plus important pour nous est que le type tableau est de taille xe alors que la liste est de taille variable. On peut ajouter sans problme un nouvel lment une liste grce la mthode Add(). De mme, on peut supprimer des lments avec la mthode Remove alors quavec un tableau, on peut seulement remplacer les valeurs existantes et il nest pas possible daugmenter sa capacit. Globalement, vous verrez que dans vos applications, vous utiliserez plutt les listes, par exemple pour acher une liste de produits, une liste de clients, etc. Gardez quand mme dans un coin de lesprit les tableaux, ils pourront vous aider dans des situations prcises.

Les numrations
Un type particulier que nous allons galement utiliser est lnumration. Cela correspond comme son nom lindique une numration de valeurs. Par exemple, il pourrait tre trs facile de reprsenter les jours de la semaine dans une numration plutt que dans un tableau. On dnit lnumration de cette faon, grce au mot-cl enum :
1 2 3 4 5

enum Jours { Lundi , Mardi , Mercredi ,

72

LES NUMRATIONS
6 7 8 9 10

Jeudi , Vendredi , Samedi , Dimanche

noter quon ne peut pas dnir cette numration nimporte o, pour linstant, contentons-nous de la dnir en dehors de la mthode Main(). Pour tre tout fait prcis, une numration est un type dont toutes les valeurs dnies sont des entiers. La premire vaut 0, et chaque valeur suivante prend la valeur prcdente augmente de 1. Cest--dire que Lundi vaut 0, Mardi vaut 1, etc. Il est possible de forcer des valeurs toutes ou certaines valeurs de lnumration, les valeurs non forces prendront la valeur prcdente augmente de 1 :
1 2 3 4 5 6 7 8 9 10

enum Jours { Lundi = 5 , // lundi vaut 5 Mardi , // mardi vaut 6 Mercredi = 9 , // mercredi vaut 9 Jeudi = 10 , // jeudi vaut 10 Vendredi , // vendredi vaut 11 Samedi , // samedi vaut 12 Dimanche = 20 // dimanche vaut 20 }

Mais, part pour enregistrer une valeur dans une base de donnes, il est rare de manipuler les numrations comme des entiers car le but de lnumration est justement davoir une liste exhaustive et xe de valeurs constantes. Le code sen trouve plus clair, plus simple et plus lisible. Le fait de dnir une telle numration revient en fait enrichir les types que nous avons notre disposition, comme les entiers ou les chanes de caractres (int ou string). Ce nouveau type sappelle Jours. Nous pourrons dnir une variable du type Jours de la mme faon quavec un autre type :
1

Jours jourDeLaSemaine ;

La seule dirence cest que les valeurs quil est possible daecter notre variable sont ges et font partie des valeurs dnies dans lnumration. Pour pouvoir accder un lment de lnumration, il faudra utiliser le nom de lnumration suivi de loprateur point . , suivi encore de la valeur de lnumration choisie. Par exemple :
1 2

Jours lundi = Jours . Lundi ; Console . WriteLine ( lundi ) ;

soit dans notre programme :


1

class Program

73

CHAPITRE 8. TABLEAUX, LISTES ET NUMRATIONS


2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19

enum Jours { Lundi , Mardi , Mercredi , Jeudi , Vendredi , Samedi , Dimanche } static void Main ( string [] args ) { Jours lundi = Jours . Lundi ; Console . WriteLine ( lundi ) ; }

La console nous achera :


Lundi

Nous pourrons galement nous servir des numrations pour faire des tests de comparaison, comme par exemple :
1 2 3 4

if ( jourDeLaSemaine == Jours . Dimanche || jourDeLaSemaine == Jours . Samedi ) { Console . WriteLine ( " Bon week - end " ) ; }

Sachez que le framework .NET utilise beaucoup les numrations. Il est important de savoir les manipuler. Vous aurez peut-tre remarqu que lorsquon ache la valeur dune numration, la console nous ache le nom de lnumration et non pas sa valeur entire. a peut paratre trange, mais cest parce que le C# fait un traitement particulier dans le cadre dune numration lachage.

En rsum
Un tableau est un type volu pouvant contenir une squence dautres types, comme un tableau dentiers ou un tableau de chanes de caractres. Une liste est un type complexe un peu plus souple que le tableau permettant davoir une liste de nimporte quel type. Une numration sutilise lorsque lon veut crer un type possdant plusieurs valeurs xes, comme les jours de la semaine. 74

Chapitre

9
Dicult :

Utiliser le framework .NET

omme on la dj voqu, le framework .NET est une norme bote outils qui contient beaucoup de mthodes permettant de construire toutes sortes dapplications.

Nous allons avoir besoin rgulirement dutiliser les lments du framework .NET pour raliser nos applications. Il est donc grand temps dapprendre savoir le manipuler ! Rentrons tout de suite dans le vif du sujet.

75

CHAPITRE 9. UTILISER LE FRAMEWORK .NET

Linstruction using
Nous allons sans arrt solliciter la puissance du framework .NET. Par exemple, nous pouvons lui demander de nous donner la date courante. Pour ce faire, on utilisera linstruction :
1

Console . WriteLine ( DateTime . Now ) ;

Ce qui se passe ici, cest que nous demandons notre application lachage de la proprit Now de lobjet DateTime. Nous allons revenir en dtail sur ce que sont des proprits et des objets, considrez pour linstant quils correspondent simplement une instruction qui nous fournit la date du moment :
09/08/2011 23:47:58

En fait, pour accder la date courante, on devrait normalement crire :


1

System . Console . WriteLine ( System . DateTime . Now ) ;

En eet, les objets DateTime et Console se situent dans lespace de nom System. Un espace de nom 1 correspond un endroit o lon range des mthodes et des objets. Il est caractris par des mots spars par des points . . Cest un peu comme des rpertoires, nous pouvons dire que le chier DateTime est rang dans le rpertoire System et, quand nous souhaitons y accder, nous devons fournir lemplacement complet du chier, savoir System.DateTime. Cependant, plutt que dcrire le chemin complet chaque fois, il est possible de dire : OK, maintenant, chaque fois que je vais avoir besoin daccder une fonctionnalit, va la chercher dans lespace de nom System. Si elle sy trouve, utilise-la . Cest ce qui se passe grce linstruction suivante :
1

using System ;

qui a t gnre par Visual C# Express au dbut du chier. Elle indique au compilateur que nous allons utiliser lespace de nom System dans notre page. Ainsi, il nest plus obligatoire de prxer laccs DateTime par System. Cest une espce de raccourci. Pour conserver lanalogie avec les rpertoires et les chiers, on peut dire que nous avons ajout le rpertoire System dans le path. Pour rsumer, linstruction :
1

System . Console . WriteLine ( System . DateTime . Now ) ;

est quivalente aux deux instructions :


1

using System ;

et
1. En anglais, namespace

76

LA BIBLIOTHQUE DE CLASSES .NET


1

Console . WriteLine ( DateTime . Now ) ;

Sachant quelles ne scrivent pas cte cte. En gnral, on met linstruction using en en-tte du chier .cs, comme ce qua fait Visual C# Express lorsquil a gnr le chier. Lautre instruction est positionner lendroit o nous souhaitons quelle soit excute. Si le using System est absent, la compltion automatique de Visual C# Express ne vous proposera pas le mot DateTime. Cest un bon moyen de se rendre compte quil manque la dclaration de lutilisation de lespace de nom. noter que dans ce cas-l, si Visual C# Express reconnat linstruction mais que lespace de nom nest pas inclus, il le propose en soulignant le dbut du mot DateTime dun petit trait bleu et blanc, comme lillustre la gure 9.1.

Figure 9.1 Le trait bleu permet dindiquer que lIDE sait rsoudre limport de lespace de nom Un clic droit sur le mot permettra douvrir un menu droulant, de choisir Rsoudre et dimporter le using correspondant automatiquement (voir gure 9.2).

Figure 9.2 Importer lespace de nom automatiquement

La bibliothque de classes .NET


Vous aurez loccasion de dcouvrir que le framework .NET fourmille despaces de noms contenant plein de mthodes de toutes sortes permettant de faire un peu tout et nimporte quoi. Une vraie caverne dAli Baba ! Parmi les nombreuses fonctionnalits du framework .NET, nous avons une mthode qui nous permet de rcuprer lutilisateur courant (au sens utilisateur Windows ), on pourra par exemple utiliser linstruction suivante : 77

CHAPITRE 9. UTILISER LE FRAMEWORK .NET


1

Console . WriteLine ( System . Environment . UserName ) ;

qui achera lutilisateur Windows dans la console :


Nico

Ou, comme vous le savez dsormais, on pourra utiliser conjointement :


1

using System ;

et
1

Console . WriteLine ( Environment . UserName ) ;

pour produire le mme rsultat. Petit petit vous allez retenir beaucoup dinstructions et despaces de nom du framework .NET mais il est inimaginable de tout retenir. Il existe une documentation rpertoriant tout ce qui compose le framework .NET, cest ce quon appelle la MSDN library. Vous pouvez y accder via le code web suivant : MSDN library Code web : 153597

Par exemple, la documentation de la proprit que nous venons dutiliser est consultable avec le code web suivant : Doc UserName Code web : 563051

Nous avons dsign la caverne dAli Baba par le mot framework .NET . Pour tre plus prcis, la dsignation exacte de cette caverne est : la bibliothque de classes .NET . Le mot framework .NET est en gnral utilis par abus de langage (et vous avez remarqu que jai moi-mme abus du langage !) et reprsente galement cette bibliothque de classes. Nous reviendrons plus tard sur ce quest exactement une classe, pour linstant, vous pouvez voir a comme des composants. Le framework serait donc une bibliothque de composants.

Rfrencer une assembly


a y est, nous savons accder au framework .NET. Mais dailleurs, comment se fait-il que nous puissions accder aux mthodes du framework .NET sans nous poser de question ? Il est magique ce framework ? O se trouve le code qui permet de rcuprer la date du jour ? Judicieuse question. Si on y rchit, il doit falloir beaucoup de code pour renvoyer la date du jour ! Dj, il faut aller la lire dans lhorloge systme, il faut peut-tre ladapter au fuseau horaire, la formater dune faon particulire en fonction de la langue, etc. 78

RFRENCER UNE ASSEMBLY Tout a est dj fait, heureusement, dans la bibliothque de mthodes du framework .NET. Alors, o est-elle cette bibliothque ? Et le reste ?

Dans des assemblys bien sr ! Comme on la vu, les assemblys possdent des fragments de code compils en langage intermdiaire. Sils sont rutilisables, ils se trouvent dans des chiers dont lextension est .dll. Le framework .NET est compos dune multitude dassemblys qui sont installes sur votre systme dexploitation, dans le GAC 2 qui est un endroit o sont stockes ces assemblys an de les rendre accessibles depuis nos programmes. Pour pouvoir utiliser ces assemblys dans notre programme, nous devons indiquer que nous voulons les utiliser. Pour a, il faut les rfrencer dans le projet. Si lon dplie les rfrences dans notre explorateur de solutions, nous pouvons voir que nous avons dj pas mal de rfrences qui ont t ajoutes par Visual C# Express (voir gure 9.3).

Figure 9.3 Les rfrences de notre application console Ce sont des assemblys qui sont trs souvent utilises, cest pour a que Visual C# Express nous les a automatiquement rfrences. Toujours ce souci de nous simplier le travail. Quil est sympa ! Prenons par exemple la rfrence System.Xml. Son nom nous suggre que dedans est runi tout ce quil nous faut pour manipuler le XML. Commenons par taper System.Xml., la compltion automatique nous propose plusieurs choses (voir gure 9.4). On ne sait pas du tout quoi elle sert, mais dclarons par exemple une variable de lnumration ConformanceLevel :
2. GAC est lacronyme de Global Assembly Cache.

79

CHAPITRE 9. UTILISER LE FRAMEWORK .NET

Figure 9.4 Lespace de nom System.Xml est accessible si la rfrence est prsente
1

System . Xml . ConformanceLevel level ;

et compilons. Pas derreur de compilation. Supprimez la rfrence System.Xml. (clic droit, Supprimer) et compilez nouveau, vous verrez que ConformanceLevel est dsormais soulign en rouge, signe quil y a un problme (voir gure 9.5).

Figure 9.5 Erreur de compilation car la rfrence a t supprime Par ailleurs, la compilation provoque lerreur suivante :
Le type ou le nom d espace de noms ConformanceLevel n existe pas dans l espace de noms System . Xml ( une r f rence d assembly est - elle manquante ?)

Loin dtre bte, Visual C# Express nous ache un message derreur plutt explicite. En eet, cette numration est introuvable car elle est dnie dans une assembly qui nest pas rfrence. Cest comme si on nous demandait de prendre le marteau pour enfoncer un clou, mais que le marteau nest pas ct de nous, mais au garage bien rang ! Il faut donc rfrencer le marteau an de pouvoir lutiliser. Pour ce faire, faisons un clic sur Rfrences et ajoutons une rfrence (voir gure 9.6). Ici, nous avons plusieurs onglets (voir gure 9.7). Selon la version de Visual Studio que vous possdez, vous aurez peut-tre une prsentation lgrement dirente. 80

RFRENCER UNE ASSEMBLY

Figure 9.6 Ajouter une rfrence

Figure 9.7 Liste des assemblys prsentes dans le GAC pouvant tre rfrences

81

CHAPITRE 9. UTILISER LE FRAMEWORK .NET Chaque onglet permet dajouter une rfrence une assembly. Longlet .NET permet de rfrencer une assembly prsente dans le GAC, cest ici que nous viendrons chercher les assemblys du framework .NET. Longlet COM permet de rfrencer une dll COM. Vous ne savez pas ce quest COM ? On peut dire que cest lanctre de lassembly. Techniquement, on ne rfrence pas directement une dll COM, mais Visual C# Express gnre ce quon appelle un wrapper permettant dutiliser la dll COM. Un wrapper est en fait une espce de traducteur permettant dutiliser la dll COM comme si ctait une assembly. Mais ne nous attardons pas sur ce point qui ne va pas nous servir, nous qui sommes dbutants. Longlet Projets permet de rfrencer des assemblys qui se trouvent dans notre solution. Dans la mesure o notre solution ne contient quun seul projet, longlet sera vide. Nous verrons plus tard comment utiliser cet onglet lorsque nous ajouterons des assemblys nos solutions. Longlet Parcourir va permettre de rfrencer une assembly depuis un emplacement sur le disque dur. Cela peut-tre une assembly tierce fournie par un collgue, ou par un revendeur, etc. Enn, longlet Rcent, comme son nom le suggre, permet de rfrencer des assemblys rcemment rfrences. Retournons nos moutons, repartons sur longlet .NET et recherchons lassembly que nous avons supprime, savoir System.Xml. Une fois trouve, appuyez sur OK. Maintenant que la rfrence est ajoute, nous pouvons nouveau utiliser cette numration. . . Ouf ! Je ne comprends pas, jai supprim toutes les rfrences, mais jarrive quand mme utiliser la date, le nom de lutilisateur ou la mthode Console.WriteLine. Si jai bien compris, je ne devrais pas pouvoir utiliser des mthodes dont les assemblys ne sont pas rfrences. . . Cest normal ? Eh oui, il existe une assembly qui napparat pas dans les rfrences et qui contient tout le cur du framework .NET. Cette assembly doit obligatoirement tre rfrence, il sagit de mscorlib.dll. Comme elle est obligatoire, elle est rfrence par dfaut ds que lon cre un projet.

Dautres exemples
Il arrivera souvent que vous ayez besoin dune fonctionnalit trouve dans la documentation ou sur internet et quil faille ajouter une rfrence. Ce sera peut-tre une rfrence au framework .NET, des bibliothques tierces ou vous. Nous verrons plus loin comment crer nos propres bibliothques. Dans la documentation MSDN, il est toujours indiqu quelle assembly il faut rfrencer pour utiliser une fonctionnalit. Prenons par exemple la proprit DateTime.Now, la documentation nous dit :

82

DAUTRES EXEMPLES

Espace de noms : System Assembly : mscorlib ( dans mscorlib . dll )

Cela veut donc dire quon utilise la date du jour en utilisant lassembly obligatoire mscorlib et la fonctionnalit se trouve dans lespace de nom System, comme dj vu. Il ny a donc rien faire pour pouvoir utiliser DateTime.Now. En imaginant que nous ayons besoin de faire un programme qui utilise des nombres complexes, vous allez srement avoir besoin du type Complex, trouv dans la documentation adquate, via le code web suivant : Doc complex Code web : 836961

Pour lutiliser, comme indiqu, il va falloir rfrencer lassembly System.Numerics.dll. Rien de plus simple, rptons la procdure pour rfrencer une assembly et allons trouver System.Numerics.dll, comme indiqu la gure 9.8.

Figure 9.8 Rfrencer lassembly System.Numerics Ensuite, nous pourrons utiliser par exemple le code suivant :
1 2 3 4 5 6

Complex c = Complex . One ; Console . WriteLine ( c ) ; Console . WriteLine ( " Partie r elle : " + c . Real ) ; Console . WriteLine ( " Partie imaginaire : " + c . Imaginary ) ; Console . WriteLine ( Complex . Conjugate ( Complex . F r o m P o l a r Coordinates ( 1 .0 , 45 * Math . PI / 180 ) ) ) ;

Sachant quil aura fallu bien sr ajouter : 83

CHAPITRE 9. UTILISER LE FRAMEWORK .NET


1

using System . Numerics ;

permettant dviter de prxer Complex par System.Numerics. Mais a, vous laviez trouv tout seul, nest-ce pas ?
(1 , 0) Partie r elle : 1 Partie imaginaire : 0 (0 ,707106781186548 , -0 ,707106781186547)

En rsum
Le framework .NET est un ensemble dassemblys quil faut rfrencer dans son projet an davoir accs leurs fonctionnalits. On utilise le mot cl using pour inclure un espace de nom comme raccourci dans son programme, ce qui permet de ne pas avoir prxer les types de leurs espaces de noms complets.

84

Chapitre

10
Dicult :

TP : bonjour cest le week-end !

ienvenue dans ce premier TP ! Vous avez pu dcouvrir dans les chapitres prcdents les premires bases du langage C# permettant la construction dapplications. Il est grand temps de mettre en pratique ce que nous avons appris. Cest ici loccasion pour vous de tester vos connaissances et de valider ce que vous appris en ralisant cet exercice.

85

CHAPITRE 10. TP : BONJOUR CEST LE WEEK-END !

Instructions pour raliser le TP


Le but est de crer une petite application qui ache un message dirent en fonction du nom de lutilisateur et du moment de la journe : Bonjour X pour la tranche horaire 9h > 18h, les lundis, mardis, mercredis, jeudis et vendredis ; Bonsoir X pour la tranche horaire 18h > 9h, les lundis, mardis, mercredis, jeudis ; Bon week-end X pour la tranche horaire vendredi 18h > lundi 9h. Peut-tre cela vous parat-il simple, dans ce cas, foncez et ralisez cette premire application tout seuls. Sinon, dcortiquons un peu lnonc de ce TP pour viter dtre perdus ! Pour raliser ce premier TP, vous allez avoir besoin de plusieurs choses. Dans un premier temps, il faut acher le nom de lutilisateur, cest une chose que nous avons dj faite en allant puiser dans les fonctionnalits du framework .NET. Vous aurez besoin galement de rcuprer lheure courante pour la comparer aux tranches horaires souhaites. Vous avez dj vu comment rcuprer la date courante. Pour pouvoir rcuprer lheure de la date courante, il vous faudra utiliser linstruction DateTime.Now.Hour qui renvoie un entier reprsentant lheure du jour. Pour comparer lheure avec des valeurs entires il vous faudra utiliser les oprateurs de comparaison et les instructions conditionnelles que nous avons vues prcdemment. Pour traiter le cas spcial du jour de la semaine, vous aurez besoin que le framework .NET vous indique quel jour nous sommes. Cest le rle de linstruction DateTime.Now.DayOfWeek qui est une numration indiquant le jour de la semaine.Les direntes valeurs sont consultables via le code web suivant : Doc DayOfWeek . Code web : 921325
Pour plus de clart, nous les reprenons ici : Valeur Sunday Monday Tuesday Wednesday Thursday Friday Saturday Traduction Dimanche Lundi Mardi Mercredi Jeudi Vendredi Samedi

Il ne restera plus qu comparer deux valeurs dnumration, comme on la vu dans le chapitre sur les numrations. Voil, vous avez tous les outils en main pour raliser ce premier TP ! Nhsitez pas revenir sur les chapitres prcdents si vous avez un doute sur la syntaxe ou sur les instructions raliser. On ne peut pas apprendre un langage par cur du premier coup. 86

CORRECTION vous de jouer !

Correction
Vous tes autoriss lire cette correction uniquement si vous vous tes arrach les cheveux sur ce TP ! Je vois quil vous en reste, encore un eort ! Si vous avez russi avec brio le TP, vous pourrez galement comparer votre travail au mien. Quoi quil en soit, voici la correction que je propose. Bien videmment, il peut y en avoir plusieurs, mais elle contient les informations ncessaires pour la ralisation de ce TP. Premire chose faire : crer un projet de type console. Jai ensuite ajout le code suivant :
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31

static void Main ( string [] args ) { if ( DateTime . Now . DayOfWeek == DayOfWeek . Saturday || DateTime . Now . DayOfWeek == DayOfWeek . Sunday ) { // nous sommes le week - end Af fi cherBonWeekEnd () ; } else { // nous sommes en semaine if ( DateTime . Now . DayOfWeek == DayOfWeek . Monday && DateTime . Now . Hour < 9 ) { // nous sommes le lundi matin AfficherBonWeekEnd () ; } else { if ( DateTime . Now . Hour >= 9 && DateTime . Now . Hour < 18 ) { // nous sommes dans la journ e AfficherBonjour () ; } else { // nous sommes en soir e if ( DateTime . Now . DayOfWeek == DayOfWeek . Friday && DateTime . Now . Hour >= 18 ) { // nous sommes le vendredi soir AfficherBonWeekEnd () ;

87

CHAPITRE 10. TP : BONJOUR CEST LE WEEK-END !


32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55

} else { } }

AfficherBonsoir () ;

static void AfficherBonWeekEnd () { Console . WriteLine ( " Bon week - end " + Environment . UserName ) ; } static void AfficherBonjour () { Console . WriteLine ( " Bonjour " + Environment . UserName ) ; } static void AfficherBonsoir () { Console . WriteLine ( " Bonsoir " + Environment . UserName ) ; }

Dtaillons un peu ce code : Au chargement du programme (mthode Main) nous faisons les comparaisons adquates. Dans un premier temps, nous testons le jour de la semaine de la date courante (DateTime.Now.DayOfWeek) et nous la comparons aux valeurs reprsentant Samedi et Dimanche. Si cest le cas, alors nous appelons une mthode qui ache le message Bon week-end avec le nom de lutilisateur courant que nous pouvons rcuprer avec Environment.UserName. Si nous ne sommes pas le week-end, alors nous testons lheure de la date courante avec DateTime.Now.Hour. Si nous sommes le lundi matin avant 9h, alors nous continuons acher Bon week-end . Sinon, si nous sommes dans la tranche horaire 9h 18h alors nous pouvons appeler la mthode qui ache Bonjour . Dans le cas contraire, il reste juste vrier que nous ne sommes pas vendredi soir, qui fait partie du week-end, sinon on peut acher le message Bonsoir . Et voil, un bon exercice pour manipuler les conditions et les numrations. . .

Aller plus loin


Ce TP ntait pas trs compliqu, il nous a permis de vrier que nous avions bien compris le principe des if et que nous savions appeler des lments du framework .NET. 88

ALLER PLUS LOIN Nous aurions pu simplier lcriture de lapplication en compliquant en peu les tests avec une combinaison de conditions. Par exemple, on pourrait avoir :
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21

if ( DateTime . Now . DayOfWeek == DayOfWeek . Saturday || DateTime . Now . DayOfWeek == DayOfWeek . Sunday || ( DateTime . Now . DayOfWeek == DayOfWeek . Monday && DateTime . Now . Hour < 9 ) || ( DateTime . Now . DayOfWeek == DayOfWeek . Friday && DateTime . Now . Hour >= 18 ) ) { // nous sommes le week - end Af fi ch erB onWeekEnd () ; } else { // nous sommes en semaine if ( DateTime . Now . Hour >= 9 && DateTime . Now . Hour < 18 ) { // nous sommes dans la journ e AfficherBonjour () ; } else { AfficherBonsoir () ; } }

Le premier test permet de vrier que nous sommes soit samedi, soit dimanche, soit que nous sommes lundi et que lheure est infrieure 9, soit que nous sommes vendredi et que lheure est suprieure 18. Nous avons, pour ce faire, combin les tests avec loprateur logique OU : ||. Remarquons que les parenthses nous permettent dagir sur lordre dvaluation des conditions. Pour que ce soit le week-end, il faut bien sr tre vendredi et que lheure soit suprieure 18 ou lundi et que lheure soit infrieure 9 ou samedi ou dimanche. On pourrait encore simplier en vitant de solliciter chaque fois le framework .NET pour obtenir la date courante. Il sut de stocker la date courante dans une variable de type DateTime. Ce qui donnerait :
1 2 3 4 5 6 7 8 9 10

DateTime dateCourante = DateTime . Now ; if ( dateCourante . DayOfWeek == DayOfWeek . Saturday || dateCourante . DayOfWeek == DayOfWeek . Sunday || ( dateCourante . DayOfWeek == DayOfWeek . Monday && dateCourante . Hour < 9 ) || ( dateCourante . DayOfWeek == DayOfWeek . Friday && dateCourante . Hour >= 18 ) ) { // nous sommes le week - end Af fi ch erB onWeekEnd () ; }

89

CHAPITRE 10. TP : BONJOUR CEST LE WEEK-END !


11 12 13 14 15 16 17 18 19 20 21 22 23

else {

// nous sommes en semaine if ( dateCourante . Hour >= 9 && dateCourante . Hour < 18 ) { // nous sommes dans la journ e AfficherBonjour () ; } else { AfficherBonsoir () ; }

On utilise ici le type DateTime comme le type string ou int. Il sert grer les dates et lheure. Il est lgrement dirent des types que nous avons vus pour linstant, nous ne nous attarderons pas dessus. Nous aurons loccasion de dcouvrir de quoi il sagit dans la partie suivante. Cette optimisation na rien dextraordinaire, mais cela nous vite un appel rpt au framework .NET. Voil pour ce TP. Jespre que vous aurez russi avec brio lexercice. Vous avez pu remarquer que ce ntait pas trop dicile. Il a simplement fallu rchir comment imbriquer correctement nos conditions. Nhsitez pas pratiquer et vous entraner avec dautres problmes de votre cru. Si vous avez la moindre hsitation, vous pouvez relire les chapitres prcdents. Vous verrez que nous aurons loccasion dnormment utiliser ces instructions conditionnelles dans tous les programmes que nous allons crire ! Vous pouvez tlcharger tous les codes sources de cet exercice grce au code web suivant : Copier ce code Code web : 106683

90

Chapitre

11
Dicult :

Les boucles

ous les avons voques rapidement un peu plus tt en parlant des tableaux et des listes. Dans ce chapitre nous allons dcrire plus prcisment les boucles.

Elles sont souvent utilises pour parcourir des lments numrables, comme le sont les tableaux ou les listes. Elles peuvent galement tre utilises pour eectuer la mme action tant quune condition nest pas ralise.

91

CHAPITRE 11. LES BOUCLES

La boucle for
La premire instruction que nous avons aperue est la boucle for. Elle permet de rpter un bout de code tant quune condition est vraie. Souvent cette condition est un compteur. Nous pouvons par exemple acher un message 50 fois avec le code suivant :
1 2 3 4 5

int compteur ; for ( compteur = 0 ; compteur < 50 ; compteur ++) { Console . WriteLine ( " Bonjour C # " ) ; }

Ce qui achera :
Bonjour Bonjour Bonjour [... 50 !] Bonjour Bonjour Bonjour C# C# C# fois en tout , mais abr g pour conomiser du papier ... C# C# C#

Nous dnissons ici un entier compteur. Il est initialis 0 en dbut de boucle (compteur = 0). Aprs chaque excution du bloc de code du for, cest--dire chaque itration, il va acher Bonjour C# . la n de chaque itration, la variable compteur est incrmente (compteur++) et nous recommenons lopration tant que la condition compteur < 50 est vraie. Bien sr, la variable compteur est accessible dans la boucle et change de valeur chaque itration.
1 2 3 4 5

int compteur ; for ( compteur = 0 ; compteur < 50 ; compteur ++) { Console . WriteLine ( " Bonjour C # " + compteur ) ; }

Le code prcdent achera la valeur de la variable aprs chaque bonjour, donc de 0 49. En eet, quand compteur passe 50, la condition nest plus vraie et on passe aux instructions suivantes :
Bonjour C # 0 Bonjour C # 1 Bonjour C # 2 Bonjour C # 3 [... abr g galement ...] Bonjour C # 48 Bonjour C # 49

92

LA BOUCLE FOR En gnral, on utilise la boucle for pour parcourir un tableau. Ainsi, nous pourrons utiliser le compteur comme indice pour accder aux lments du tableau :
1 2 3 4 5 6

string [] jours = new string [] { " Lundi " , " Mardi " , " Mercredi " , " Jeudi " , " Vendredi " , " Samedi " , " Dimanche " }; int indice ; for ( indice = 0 ; indice < 7 ; indice ++) { Console . WriteLine ( jours [ indice ]) ; }

Dans cette boucle, vu qu la premire itration indice vaut 0, nous acherons llment du tableau la position 0, savoir Lundi . litration suivante, indice passe 1, nous achons llment du tableau la position 1, savoir Mardi , et ainsi de suite :
Lundi Mardi Mercredi Jeudi Vendredi Samedi Dimanche

Attention ce que lindice ne dpasse pas la taille du tableau, sinon laccs un indice en dehors des limites du tableau provoquera une erreur lexcution. Pour viter ceci, on utilise en gnral la taille du tableau comme condition de n :
1 2 3 4 5 6

string [] jours = new string [] { " Lundi " , " Mardi " , " Mercredi " , " Jeudi " , " Vendredi " , " Samedi " , " Dimanche " }; int indice ; for ( indice = 0 ; indice < jours . Length ; indice ++) { Console . WriteLine ( jours [ indice ]) ; }

Ici jours.Length renvoie la taille du tableau, savoir 7. Il est trs courant de boucler sur un tableau en passant tous les lments un par un, mais il est possible de changer les conditions de dpart, les conditions de n, et llment qui inue sur la condition de n. Ainsi, lexemple suivant :
1 2 3 4 5 6

int [] chiffres = new int [] { 0 , 1 , 2 , 3 , 4 , 5 , 6 , 7 , 8 , 9 }; for ( int i = 9 ; i > 0 ; i -= 2 ) { Console . WriteLine ( chiffres [ i ]) ; }

Permet de parcourir le tableau de 2 en 2 en commenant par la n : 93

CHAPITRE 11. LES BOUCLES

9 7 5 3 1

Vous avez pu voir que dans cet exemple, nous avons dni lentier i directement dans linstruction for. Cest une commodit qui permet que la variable i nexiste qu lintrieur de la boucle, car sa porte correspond aux accolades qui dlimitent le for. Attention, il est possible de faire un peu tout et nimporte quoi dans ces boucles. Aussi il peut arriver que lon se retrouve avec des bugs, comme des boucles innies. Par exemple, le code suivant :
1 2 3 4

for ( int indice = 0 ; indice < 7 ; indice - -) { Console . WriteLine ( " Test " + indice ) ; }

est une boucle innie. En eet, on modie la variable indice en la dcrmentant. Sauf que la condition de sortie de la boucle est valable pour un indice qui dpasse ou gale la valeur 7, ce qui narrivera jamais. Si on excute lapplication avec ce code, la console va acher linni le mot Test avec son indice. La seule solution pour quitter le programme sera de fermer brutalement lapplication. Lautre solution est dattendre que le programme se termine. . . Mais tu viens de dire que la boucle tait innie ?

Oui cest vrai, mais en fait, ici on se heurte un cas limite du C#. Cest cause de la variable indice, qui est un entier que lon dcrmente. Au dbut il vaut zro, puis -1, puis -2, etc. Lorsque la variable indice arrive la limite infrieure que le type int est capable de grer, cest--dire -2147483648, se produit alors ce quon appelle un dpassement de capacit. Sans rentrer dans les dtails, il ne peut pas stocker un entier plus petit et donc il boucle et repart lentier le plus grand, cest--dire 2147483647. Donc pour rsumer, lindice fait : 94 0 -1 -2 ... -2147483647 -2147483648

LA BOUCLE FOREACH +2147483647 Et comme l, il se retrouve suprieur 7, la boucle se termine !


Test Test Test [... Test Test 0 -1 -2 abr g ...] -2147483647 -2147483648

Donc, techniquement, ce nest pas une boucle innie, mais on a attendu tellement longtemps pour atteindre ce cas limite que cest tout comme. Et surtout, nous tombons sur un cas imprvu. Ici, a se termine plutt bien, mais a aurait pu nir en crash de lapplication. Dans tous les cas, il faut absolument matriser ses conditions de sortie de boucle pour viter la boucle innie, un des cauchemars du dveloppeur !

La boucle foreach
Comme il est trs courant dutiliser les boucles pour parcourir un tableau ou une liste, le C# dispose dun oprateur particulier : foreach que lon peut traduire par pour chaque ; pour chaque lment du tableau faire ceci. . .
1 2 3 4 5

string [] jours = new string [] { " Lundi " , " Mardi " , " Mercredi " , " Jeudi " , " Vendredi " , " Samedi " , " Dimanche " }; foreach ( string jour in jours ) { Console . WriteLine ( jour ) ; }

Cette boucle va nous permettre de parcourir tous les lments du tableau jours. chaque itration, la boucle va crer une chane de caractres jour qui contiendra llment courant du tableau. noter que la variable jour aura une porte gale au bloc foreach. Nous pourrons ainsi utiliser cette valeur dans le corps de la boucle et pourquoi pas lacher comme dans lexemple prcdent :
Lundi Mardi Mercredi Jeudi Vendredi Samedi Dimanche

95

CHAPITRE 11. LES BOUCLES Linstruction foreach fonctionne aussi avec les listes. Par exemple le code suivant :
1 2 3 4 5

List < string > jours = new List < string > { " Lundi " , " Mardi " , " Mercredi " , " Jeudi " , " Vendredi " , " Samedi " , " Dimanche " }; foreach ( string jour in jours ) { Console . WriteLine ( jour ) ; }

nous permettra dacher tous les jours de la semaine contenus dans la liste des jours. Attention, la boucle foreach est une boucle en lecture seule. Cela veut dire quil nest pas possible de modier llment de litration en cours. Par exemple, le code suivant :
1 2 3 4 5

List < string > jours = new List < string > { " Lundi " , " Mardi " , " Mercredi " , " Jeudi " , " Vendredi " , " Samedi " , " Dimanche " }; foreach ( string jour in jours ) { jour = " pas de jour ! " ; }

provoquera lerreur de compilation suivante :


Impossible d assigner jour , car il s agit d un variable d it ration foreach

Notez dailleurs que lquipe de traduction de Visual C# Express a quelques progrs faire. . . ! Si nous souhaitons utiliser une boucle pour changer la valeur de notre liste ou de notre tableau, il faudra passer par une boucle for :
1 2 3 4 5

List < string > jours = new List < string > { " Lundi " , " Mardi " , " Mercredi " , " Jeudi " , " Vendredi " , " Samedi " , " Dimanche " }; for ( int i = 0 ; i < jours . Count ; i ++ ) { jours [ i ] = " pas de jour ! " ; }

Il vous arrivera aussi srement un jour (a arrive tous les dveloppeurs) de vouloir modier une liste en elle-mme lors dune boucle foreach. Cest--dire lui rajouter un lment, en supprimer un autre, etc. Cest galement impossible, car partir du moment o lon rentre dans la boucle foreach, la liste devient non-modiable. Prenons lexemple, combien classique, de la recherche dune valeur dans une liste pour la supprimer. Nous serions tents de faire :
1 2 3

List < string > jours = new List < string > { " Lundi " , " Mardi " , " Mercredi " , " Jeudi " , " Vendredi " , " Samedi " , " Dimanche " }; foreach ( string jour in jours ) {

96

LA BOUCLE FOREACH
4 5 6

if ( jour == " Mercredi " ) jours . Remove ( jour ) ;

ce qui semblerait tout fait correct et en plus, ne provoque pas derreur de compilation. Sauf que si vous excutez lapplication, vous aurez un message derreur :
Exception non g r e : System . I nv a l i d O p e r a t io n E x c e p t i o n : La collection a t modifi e ; l op ration d num ration peut ne pas s ex cuter . System . ThrowHelper . T h r o w I n v a l i d O p e r a t i o n E x c e p t i o n ( Excep tionResource resource ) System . Collections . Generic . List 1. Enumerator . MoveNextRare () System . Collections . Generic . List 1. Enumerator . MoveNext () M a P r e m i e r e A p plica tion . Program . Main ( String [] args ) dans C :\ Users \ Nico \ Documents \ Visual Studio 2010\ Projects \ C #\ M a P r e m i e r e Appl icatio n \ MaP remier eAppli catio n \ Program . cs : ligne 14

Le programme nous indique alors que la collection a t modie et que lopration dnumration peut ne pas sexcuter . Il est donc impossible de faire notre suppression ainsi. Comment tu ferais toi ?

Eh bien, plusieurs solutions existent. Celle qui vient en premier lesprit est dutiliser une boucle for par exemple :
1 2 3 4 5

for ( int i = 0 ; i < jours . Count ; i ++) { if ( jours [ i ]== " Mercredi " ) jours . Remove ( jours [ i ]) ; }

Cette solution est intressante ici, mais elle peut poser un problme dans dautres situations. En eet, vu que nous supprimons un lment de la liste, nous allons nous retrouver avec une incohrence entre lindice en cours et llment que nous essayons datteindre. En eet, lorsque le jour courant est mercredi, lindice i vaut 2. Si lon supprime cet lment, cest jeudi qui va se retrouver en position 2. Et nous allons rater son analyse car la boucle va continuer lindice 3, qui sera vendredi. On peut viter ce problme en parcourant la boucle lenvers :
1 2 3 4 5

for ( int i = jours . Count - 1 ; i >= 0 ; i - -) { if ( jours [ i ] == " Mercredi " ) jours . Remove ( jours [ i ]) ; }

97

CHAPITRE 11. LES BOUCLES Dune manire gnrale, il ne faut pas modier les collections que nous sommes en train de parcourir. Nous ntudierons pas les autres solutions car elles font appel des notions que nous verrons en dtail plus tard. Aprs avoir lu ceci, vous pourriez avoir limpression que la boucle foreach nest pas souple et dicilement exploitable : autant utiliser autre chose. . . Vous verrez lutilisation que non, elle est en fait trs pratique. Il faut simplement connatre ses limites. Voil qui est chose faite ! Nous avons vu que linstruction foreach permettait de boucler sur tous les lments dun tableau ou dune liste. En fait, il est possible de parcourir tous les types qui sont numrables. Nous verrons plus loin ce qui caractrise un type numrable, car pour linstant, cest un secret ! Chuuut. . .

La boucle while
La boucle while est en gnral moins utilise que for ou foreach. Cest la boucle qui va nous permettre de faire quelque chose tant quune condition nest pas vrie. Elle ressemble de loin la boucle for, mais la boucle for se spcialise dans le parcours de tableau tandis que la boucle while est plus gnrique. Par exemple :
1 2 3 4 5 6

int i = 0 ; while ( i < 50 ) { Console . WriteLine ( " Bonjour C # " ) ; i ++; }

Ce code permet dcrire Bonjour C# 50 fois de suite. Ici, la condition du while est value en dbut de boucle. Dans lexemple suivant :
1 2 3 4 5 6 7

int i = 0 ; do { Console . WriteLine ( " Bonjour C # " ) ; i ++; } while ( i < 50 ) ;

La condition de sortie de boucle est value la n de la boucle. Lutilisation du mot-cl do permet dindiquer le dbut de la boucle. Concrtement cela veut dire que dans le premier exemple, le code lintrieur de la boucle peut ne jamais tre excut, si par exemple lentier i est initialis 50. A 98

LA BOUCLE WHILE contrario, on passera au moins une fois dans le corps de la boucle du second exemple, mme si lentier i est initialis 50 car la condition de sortie de boucle est value la n. Les conditions de sortie de boucles ne portent pas forcment sur un compteur ou un indice, nimporte quelle expression peut tre value. Par exemple :
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16

string [] jours = new string [] { " Lundi " , " Mardi " , " Mercredi " , " Jeudi " , " Vendredi " , " Samedi " , " Dimanche " }; int i = 0 ; bool trouve = false ; while (! trouve ) { string valeur = jours [ i ]; if ( valeur == " Jeudi " ) { trouve = true ; } else { i ++; } } Console . WriteLine ( " Trouv l ' indice " + i ) ;

Le code prcdent va rpter les instructions contenues dans la boucle while tant que le boolen trouve sera faux (cest--dire quon va sarrter ds que le boolen sera vrai). Nous analysons la valeur pour lindice i. Si la valeur est celle cherche, alors nous passons le boolen vrai et nous pourrons sortir de la boucle. Sinon, nous incrmentons lindice pour passer au suivant. Attention encore aux valeurs de sortie de la boucle. Si nous ne trouvons pas la chane recherche, alors i continuera sincrmenter ; le boolen ne passera jamais vrai, nous resterons bloqus dans la boucle et nous risquons datteindre un indice qui nexiste pas dans le tableau. Il serait plus prudent que la condition porte galement sur la taille du tableau, par exemple :
1 2 3 4 5 6 7 8 9 10 11 12 13

string [] jours = new string [] { " Lundi " , " Mardi " , " Mercredi " , " Jeudi " , " Vendredi " , " Samedi " , " Dimanche " }; int i = 0 ; bool trouve = false ; while ( i < jours . Length && ! trouve ) { string valeur = jours [ i ]; if ( valeur == " Jeudi " ) { trouve = true ; } else { i ++;

99

CHAPITRE 11. LES BOUCLES


14 15 16 17 18 19

} } if (! trouve ) Console . WriteLine ( " Valeur non trouv e " ) ; else Console . WriteLine ( " Trouv l ' indice " + i ) ;

Ainsi, si lindice est suprieur la taille du tableau, nous sortons de la boucle et nous liminons le risque de boucle innie. Une erreur classique est que la condition ne devienne jamais vraie cause dune erreur de programmation. Par exemple, si joublie dincrmenter la variable i, alors chaque passage de la boucle janalyserai toujours la premire valeur du tableau et je natteindrai jamais la taille maximale du tableau ; condition qui me permettrait de sortir de la boucle. Je ne le rpterai jamais assez : faites bien attention aux conditions de sortie dune boucle !

Les instructions break et continue


Il est possible de sortir prmaturment dune boucle grce linstruction break. Ds quelle est rencontre, elle sort du bloc de code de la boucle. Lexcution du programme continue alors avec les instructions situes aprs la boucle. Par exemple :
1 2 3 4 5 6 7 8 9 10

int i = 0 ; while ( true ) { if ( i >= 50 ) { break ; } Console . WriteLine ( " Bonjour C # " ) ; i ++; }

Le code prcdent pourrait potentiellement produire une boucle innie. En eet, la condition de sortie du while est toujours vraie. Mais lutilisation du mot-cl break nous permettra de sortir de la boucle ds que i atteindra la valeur 50. Certes ici, il surait que la condition de sortie porte sur lvaluation de lentier i. Mais il y a des cas o il pourra tre judicieux dutiliser un break (surtout lors dun dmnagement !). Cest le cas pour lexemple suivant. Imaginons que nous voulions vrier la prsence dune valeur dans une liste. Pour la trouver, on peut parcourir les lments de la liste et une fois trouve, on peut sarrter. En eet, il serait inutile de continuer parcourir le reste des lments : 100

LES INSTRUCTIONS BREAK ET CONTINUE


1 2 3 4 5 6 7 8 9 10

List < string > jours = new List < string > { " Lundi " , " Mardi " , " Mercredi " , " Jeudi " , " Vendredi " , " Samedi " , " Dimanche " }; bool trouve = false ; foreach ( string jour in jours ) { if ( jour == " Jeudi " ) { trouve = true ; break ; } }

Nous nous dispensons ici danalyser les 3 derniers lments de la liste. Il est galement possible de passer litration suivante dune boucle grce lutilisation du mot-cl continue. Prenons lexemple suivant :
1 2 3 4 5 6 7 8

for ( int i = 0 ; i < 20 ; i ++) { if ( i % 2 == 0 ) { continue ; } Console . WriteLine ( i ) ; }

Ici, loprateur % est appel modulo . Il permet dobtenir le reste de la division. Lopration i % 2 renverra donc 0 quand i est pair. Ainsi, ds quun nombre pair est trouv, nous passons litration suivante grce au mot-cl continue. Ce qui fait que nous nacherons que les nombres impairs :
1 3 5 7 9 11 13 15 17 19

Bien sr, il aurait t possible dinverser la condition du if pour nexcuter le Console.WriteLine() que dans le cas o i % 2 != 0. vous de choisir lcriture que vous prfrez en fonction des cas que vous rencontrez.

En rsum
On utilise la boucle for pour rpter des instructions tant quune condition nest pas vrie, les lments de la condition changeant chaque itration. 101

CHAPITRE 11. LES BOUCLES On utilise en gnral la boucle for pour parcourir un tableau, avec un indice qui sincrmente chaque itration. La boucle foreach est utilise pour simplier le parcours des tableaux ou des listes. La boucle while permet de rpter des instructions tant quune condition nest pas vrie. Cest la boucle la plus souple. Il faut faire attention aux conditions de sortie dune boucle an dviter les boucles innies qui font planter lapplication.

102

Chapitre

12
Dicult :

TP : calculs en boucle

a y est, grce au chapitre prcdent, vous devez avoir une bonne ide de ce que sont les boucles. Par contre, vous ne voyez peut-tre pas encore compltement quelles vont vous servir tout le temps. Cest un lment quil est primordial de matriser. Il vous faut donc vous entraner pour tre bien srs davoir tout compris. Cest justement lobjectif de ce deuxime TP. Finie la thorie des boucles, place la pratique en boucle ! Notre but est de raliser des calculs qui vont avoir besoin des for et des foreach. Vous tes prts ? Alors, cest parti !

103

CHAPITRE 12. TP : CALCULS EN BOUCLE

Instructions pour raliser le TP


Le but de ce TP va tre de crer trois mthodes. La premire va servir calculer la somme dentiers conscutifs. Si par exemple je veux calculer la somme des entiers de 1 10, cest--dire 1 + 2 + 3 + 4 + 5 + 6 + 7 + 8 + 9 + 10, je vais appeler cette mthode en lui passant en paramtres 1 et 10, cest--dire les bornes des entiers dont il faut faire la somme. Quelque chose du genre :
1 2

Console . WriteLine ( CalculSommeEntiers (1 , 10 ) ) ; Console . WriteLine ( CalculSommeEntiers (1 , 100 ) ) ;

Sachant que le premier rsultat de cet exemple vaut 55 (1 + 2 + 3 + 4 + 5 + 6 + 7 + 8 + 9 + 10 = 55) et le deuxime 5050. La deuxime mthode acceptera une liste de double en paramtres et devra renvoyer la moyenne des doubles de la liste. Par exemple :
1 2

List < double > liste = new List < double > { 1 .0 , 5 .5 , 9 .9 , 2 .8 , 9 . 6 }; Console . WriteLine ( CalculMoyenne ( liste ) ) ;

Le rsultat de cet exemple vaut 5.76. Enn, la dernire mthode devra dans un premier temps construire une liste dentiers de 1 100 qui sont des multiples de 3 (3, 6, 9, 12,. . .). Dans un second temps, construire une autre liste dentiers de 1 100 qui sont des multiples de 5 (5, 10, 15, 20,. . .). Et dans un dernier temps, il faudra calculer la somme des entiers qui sont communs aux deux listes. . . vous devez bien sr trouver 315 comme rsultat ! Voil, cest vous de jouer. . . Bon, allez, je vais quand mme vous donner quelques conseils pour dmarrer. Vous ntes pas obligs de les lire si vous vous sentez capables de raliser cet exercice tout seul. Vous laurez videmment compris, il va falloir utiliser des boucles. Je ne donnerai pas de conseils pour la premire mthode, cest juste pour vous chauer ! Pour la deuxime mthode, vous allez avoir besoin de diviser la somme de tous les double par la taille de la liste. Vous ne savez sans doute pas comment obtenir cette taille. Le principe est le mme que pour la taille dun tableau et vous laurez sans doute trouv si vous fouillez un peu dans les mthodes de la liste. Toujours est-il que pour obtenir la taille dune liste, on va utiliser liste.Count, avec par exemple :
1

int taille = liste . Count ;

Enn, pour la dernire mthode, vous allez devoir trouver tous les multiples de 3 et de 5. Le plus simple, mon avis, pour calculer tous les multiples de 3, est de faire une boucle qui dmarre 3 et davancer de 3 en 3 jusqu la valeur souhaite. On fait de mme pour les multiples de 5. 104

CORRECTION Ensuite, il sera ncessaire de faire deux boucles imbriques an de dterminer les intersections. Cest--dire parcourir la liste des multiples de 3 et lintrieur de cette boucle, parcourir la liste des multiples de 5. On compare les deux lments ; sils sont gaux, cest quils sont communs aux deux listes. Voil, vous devriez avoir tous les lments en main pour russir ce TP, cest vous de jouer !

Correction
Jimagine que vous avez russi ce TP, non pas sans dicults, mais force de ttonnements, vous avez sans doute russi. Bravo ! Quoi quil en soit, voici la correction que je propose. Premire mthode :
1 2 3 4 5 6 7 8 9

static int Ca lculSommeEntiers ( int borneMin , int borneMax ) { int resulat = 0 ; for ( int i = borneMin ; i <= borneMax ; i ++) { resulat += i ; } return resulat ; }

Ce ntait pas trs compliqu, il faut dans un premier temps construire une mthode qui renvoie un entier et qui accepte deux entiers en paramtres. Ensuite, on boucle grce linstruction for de la borne infrieure la borne suprieure (incluse, donc il faut utiliser loprateur de comparaison <= ) en incrmentant un compteur de 1 chaque itration. Nous ajoutons la valeur du compteur un rsultat, que nous retournons en n de boucle. Seconde mthode :
1 2 3 4 5 6 7 8 9

static double CalculMoyenne ( List < double > liste ) { double somme = 0 ; foreach ( double valeur in liste ) { somme += valeur ; } return somme / liste . Count ; }

Ici, le principe est grosso modo le mme, la dirence est que la mthode retourne un double et accepte une liste de double en paramtres. Ici, nous utilisons la boucle foreach pour parcourir tous les lments que nous ajoutons un rsultat. Enn, nous retournons ce rsultat divis par la taille de la liste. 105

CHAPITRE 12. TP : CALCULS EN BOUCLE Troisime mthode :


1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25

static int Ca l cu lS om m eI nt e rs ec ti o n () { List < int > multiplesDe3 = new List < int >() ; List < int > multiplesDe5 = new List < int >() ; for ( int i = 3 ; i <= 100 ; i += 3 ) { multiplesDe3 . Add ( i ) ; } for ( int i = 5 ; i <= 100 ; i += 5 ) { multiplesDe5 . Add ( i ) ; } int somme = 0 ; foreach ( int m3 in multiplesDe3 ) { foreach ( int m5 in multiplesDe5 ) { if ( m3 == m5 ) somme += m3 ; } } return somme ;

Cette troisime mthode est peut-tre la plus complique. . . On commence par crer nos deux listes de multiples. Comme je vous avais conseill, jutilise une boucle for qui commence 3 avec un incrment de 3. Comme a, je suis sr davoir tous les multiples de 3 dans ma liste. Cest le mme principe pour les multiples de 5, sachant que dans les deux cas, la condition de sortie est quand lindice est suprieur 100. Ensuite, jai mes deux boucles imbriques o je compare les deux valeurs et si elles sont gales, je rajoute la valeur la somme globale que je renvoie en n de mthode. Pour bien comprendre ce quil se passe dans les boucles imbriques, il faut comprendre que nous allons parcourir une unique fois la liste multiplesDe3 mais que nous allons parcourir autant de fois la liste multipleDe5 quil y a dlments dans la liste multipleDe3, cest--dire 33 fois. Ce nest sans doute pas facile de le concevoir ds le dbut, mais pour vous aider, vous pouvez essayer de vous faire lalgorithme dans la tte : on m3 on m5 on on rentre dans la boucle qui parcoure la liste multiplesDe3 ; vaut 3 ; rentre dans la boucle qui parcoure la liste multiplesDe5 ; vaut 5 ; compare 3 5, ils sont dirents ; passe litration suivante de la liste multiplesDe5 ;

106

ALLER PLUS LOIN m5 vaut 10 ; on compare 3 10, ils sont dirents ; de mme jusqu ce quon ait ni de parcourir la liste des multiplesDe5 ; on passe litration suivante de la liste multiplesDe3 ; m3 vaut 6 ; on rentre dans la boucle qui parcoure la liste multiplesDe5 ; m5 vaut 5 ; on compare 6 5, ils sont dirents ; on passe litration suivante de la liste multiplesDe5 ; etc.

Aller plus loin


Vous avez remarqu que dans la deuxime mthode, jutilise une boucle foreach pour parcourir la liste :
1 2 3 4 5 6 7 8 9

static double CalculMoyenne ( List < double > liste ) { double somme = 0 ; foreach ( double valeur in liste ) { somme += valeur ; } return somme / liste . Count ; }

Il est aussi possible de parcourir les listes avec un for et daccder aux lments de la liste avec un indice, comme on le fait pour un tableau. Ce qui donnerait :
1 2 3 4 5 6 7 8 9

static double CalculMoyenne ( List < double > liste ) { double somme = 0 ; for ( int i = 0 ; i < liste . Count ; i ++) { somme += liste [ i ]; } return somme / liste . Count ; }

Notez quon se sert de liste.Count pour obtenir la taille de la liste et quon accde llment courant avec liste[i]. Je reconnais que la boucle foreach est plus explicite, mais cela peut tre utile de connatre le parcours dune liste avec la boucle for. vous de voir. Vous aurez galement not ma technique pour avoir des multiples de 3 et de 5. Il y en a plein dautres. Je pense une en particulier et qui fait appel des notions que nous avons vues auparavant : la division entire et la division de double. 107

CHAPITRE 12. TP : CALCULS EN BOUCLE Pour savoir si un nombre est un multiple dun autre, il sut de les diviser entre eux et de voir sil y a un reste la division. On peut faire cela de deux faons. La premire, en comparant la division entire avec la division double et en vriant que le rsultat est le mme. Si le rsultat est le mme, cest quil ny a pas de chires aprs la virgule :
1 2 3 4 5 6 7

for ( int i = 1 ; i <= 100 ; i ++) { if ( i / 3 == i / 3 . 0 ) multiplesDe3 . Add ( i ) ; if ( i / 5 == i / 5 . 0 ) multiplesDe5 . Add ( i ) ; }

Cette technique fait appel des notions que nous navons pas encore vues : comment se fait-il quon puisse comparer un entier un double ? Nous le verrons un peu plus tard. Lautre solution est dutiliser loprateur modulo que nous avons vu prcdemment qui fait justement a :
1 2 3 4 5 6 7

for ( int i = 1 ; i <= 100 ; i ++) { if ( i % 3 == 0 ) multiplesDe3 . Add ( i ) ; if ( i % 5 == 0 ) multiplesDe5 . Add ( i ) ; }

Lavantage de ces deux techniques est quon peut construire les deux listes de multiples en une seule boucle. Voil, cest ni pour ce TP. Nhsitez pas vous faire la main sur ces boucles car il est fondamental que vous les matrisiez. Faites-vous plaisir, tentez de les repousser dans leurs limites, essayez de rsoudre dautres problmes. . . Limportant, cest de pratiquer. Vous pouvez tlcharger tous les codes sources de cet exercice grce au code web suivant : Copier ce code Code web : 764808

108

Deuxime partie

Un peu plus loin avec le C#

109

Chapitre

13
Dicult :

Les conversions entre les types

ous avons vu que le C# est un langage qui possde plein de types de donnes dirents : entier, dcimal, chane de caractres, etc.

Dans nos programmes, nous allons trs souvent avoir besoin de manipuler des donnes entre elles alors quelles ne sont pas forcment du mme type. Lorsque cela sera possible, nous aurons besoin de convertir un type de donnes en un autre.

111

CHAPITRE 13. LES CONVERSIONS ENTRE LES TYPES

Entre les types compatibles : le casting


Cest lheure de faire la slection des types les plus performants, place au casting ! Le jury est en place, vos SMS pour voter. Ah, on me fait signe quil ne sagirait pas de ce casting-l. . . Le casting est simplement laction de convertir la valeur dun type dans un autre. Plus prcisment, cela fonctionne pour les types qui sont compatibles entre eux, entendez par l qui se ressemblent . Par exemple, lentier et le petit entier se ressemblent. Pour rappel, nous avons : Type short int Description Entier de -32768 32767 Entier de -2147483648 2147483647

Ils ne dirent que par leur capacit. Le short ne pourra pas contenir le nombre 100000 par exemple, alors que lint le pourra. Nous ne lavons pas encore fait, mais le C# nous autorise faire :
1 2

short s = 200 ; int i = s ;

En eet, il est toujours possible de stocker un petit entier dans un grand. Peu importe la valeur de s, i sera toujours mme de contenir sa valeur. Cest comme dans une voiture. Si nous arrivons tenir 5 dans un monospace, nous pourrons facilement tenir 5 dans un bus. Linverse nest pas vrai bien sr. Si nous tenons 100 dans un bus, ce nest pas certain que nous pourrons tenir dans le monospace, mme en se serrant bien. Ce qui fait que si nous faisons :
1 2

int i = 100000 ; short s = i ;

nous aurons lerreur de compilation suivante :


Impossible de convertir implicitement le type int en short . Une conversion explicite existe ( un cast est - il manquant ?)

Eh oui, comment pouvons-nous faire rentrer 100000 dans un type qui ne peut aller que jusqu 32767 ? Le compilateur le sait bien. Vous remarquerez que nous aurons cependant la mme erreur si nous tentons de faire :
1 2

int i = 200 ; short s = i ;

112

ENTRE LES TYPES COMPATIBLES : LE CASTING

Mais pourquoi ? Le short est bien capable de stocker la valeur 200 ?

Oui tout fait, mais le compilateur nous avertit quand mme. Il nous dit : Attention, vous essayez de faire rentrer les personnes du bus dans un monospace, tes-vous bien sr ? Nous avons envie de lui rpondre : Oui, oui, je sais quil y a trs peu de personnes dans le bus et quils pourront rentrer sans aucun problme dans le monospace. Fais-moi conance !

Eh bien, ceci scrit en C# de la manire suivante :


1 2

int i = 200 ; short s = ( short ) i ;

Nous utilisons ce quon appelle un cast explicite.

Pour faire un tel cast, il sut de faire prcder la variable convertir du nom du type souhait entre parenthses. Cela permet dindiquer notre compilateur que nous savons ce que nous faisons, et que lentier tiendra correctement dans le petit entier. Mais attention, le compilateur nous fait conance. Nous sommes le boss ! Cela implique une certaine responsabilit, il ne faut pas faire nimporte quoi. Si nous faisons :
1 2 3 4

int i = 40000 ; short s = ( short ) i ; Console . WriteLine ( s ) ; Console . WriteLine ( i ) ;

nous tentons de faire rentrer un trop gros entier dans ce quest capable de stocker le petit entier. Si nous excutons notre application, nous aurons un rsultat surprenant :
-25536 40000

113

CHAPITRE 13. LES CONVERSIONS ENTRE LES TYPES Le rsultat nest pas du tout celui attendu. Nous avons fait nimporte quoi, le C# nous a punis ! En fait, plus prcisment, il sest pass ce quon appelle un dpassement de capacit. Nous lavons dj vu lors du chapitre sur les boucles. Ici, il sest produit la mme chose. Le petit entier est all sa valeur maximale puis a boucl en repartant de sa valeur minimale. Bref, tout a pour dire que nous obtenons un rsultat inattendu. Il faut donc faire attention ce que lon fait et honorer la conance que nous porte le compilateur en faisant bien attention ce que lon caste. 1 . Dune faon similaire lentier et au petit entier, lentier et le double se ressemblent un peu. Ce sont tous les deux des types permettant de contenir des nombres. Le premier permet de contenir des nombres entiers, le deuxime peut contenir des nombres virgules. Ainsi, nous pouvons crire le code C# suivant :
1 2

int i = 20 ; double d = i ;

En eet, un double est plus prcis quun entier. Il est capable davoir des chires aprs la virgule alors que lentier ne le peut pas. Ce qui fait que le double est entirement capable de stocker toute la valeur dun entier sans perdre dinformation dans cette aectation. Par contre, comme on peut sy attendre, linverse nest pas possible. Le code suivant :
1 2

double prix = 125 . 55 ; int valeur = prix ;

produira lerreur de compilation suivante :


Impossible de convertir implicitement le type double en int . Une conversion explicite existe ( un cast est - il manquant ?)

En eet, un double tant plus prcis quun entier, si nous mettons ce double dans lentier nous allons perdre notamment les chires aprs la virgule. Il restera encore une fois demander au compilateur de nous faire conance : OK, ceci est un double, mais ce nest pas grave, je veux lutiliser comme un entier ! Oui, oui, mme si je sais que je vais perdre les chires aprs la virgule . Ce qui scrit en C# de cette manire, comme nous lavons vu :
1 2

double prix = 125 . 55 ; int valeur = ( int ) prix ; // valeur vaut 125

Nous faisons prcder la variable prix du type dans lequel nous voulons la convertir en utilisant les parenthses.
1. Nous utiliserons souvent cet anglicisme, qui signiera bien sr que nous convertissons des types qui se ressemblent entre eux, pour le plus grand malheur des professeurs de franais.

114

ENTRE LES TYPES COMPATIBLES : LE CASTING En loccurrence, on peut se servir de ce cast pour rcuprer la partie entire dun nombre virgule. Cest plutt sympa comme instruction. Mais noubliez pas que cette opration est possible uniquement avec les types qui se ressemblent entre eux. Par exemple, le cast suivant est impossible :
1 2

string nombre = " 123 " ; int valeur = ( int ) nombre ;

car les deux types sont trop dirents et sont incompatibles entre eux. Mme si la chane de caractres reprsente un nombre. Nous verrons plus loin comment convertir une chane en entier. Pour linstant, Visual C# Express nous gnre une erreur de compilation :
Impossible de convertir le type string en int

Le message derreur est plutt explicite. Il ne nous propose mme pas dutiliser de cast, il considre que les types sont vraiment trop dirents ! Nous avons vu prcdemment que les numrations reprsentaient des valeurs entires. Il est donc possible de rcuprer lentier correspondant une valeur de lnumration grce un cast. Par exemple, en considrant lnumration suivante :
1 2 3 4 5 6 7 8 9 10

enum Jours { Lundi = 5 , // lundi vaut 5 Mardi , // mardi vaut 6 Mercredi = 9 , // mercredi vaut 9 Jeudi = 10 , // jeudi vaut 10 Vendredi , // vendredi vaut 11 Samedi , // samedi vaut 12 Dimanche = 20 // dimanche vaut 20 }

Le code suivant :
1

int valeur = ( int ) Jours . Lundi ; // valeur vaut 5

convertit lnumration en entier. Nous verrons que le cast est beaucoup plus puissant que a, mais pour linstant, nous navons pas assez de connaissances pour tout tudier. Nous y reviendrons dans la partie suivante. 115

CHAPITRE 13. LES CONVERSIONS ENTRE LES TYPES

Entre les types incompatibles


Nous avons vu quil tait facile de convertir des types qui se ressemblent grce au cast. Il est parfois possible de convertir des types qui ne se ressemblent pas mais qui ont le mme sens. Par exemple, il est possible de convertir une chane de caractres qui contient uniquement des chires en un nombre (entier, dcimal, etc.). Cette conversion va nous servir normment car ds quun utilisateur va saisir une information par le clavier, elle sera reprsente par une chane de caractres. Donc si on lui demande de saisir un nombre, il faut tre capable dutiliser sa saisie comme un nombre an de le transformer, de le stocker, etc. Pour ce faire, il existe plusieurs solutions. La plus simple est dutiliser la mthode Convert.ToInt32() disponible dans le framework .NET. Par exemple :
1 2

string chaineAge = " 20 " ; int age = Convert . ToInt32 ( chaineAge ) ; // age vaut 20

Cette mthode, bien que simple dutilisation, prsente un inconvnient dans le cas o la chane ne reprsente pas un entier. ce moment-l, la conversion va chouer et le C# va renvoyer une erreur au moment de lexcution. Par exemple :
1 2

string chaineAge = " vingt ans " ; int age = Convert . ToInt32 ( chaineAge ) ;

Si vous excutez ce bout de code, vous verrez que la console nous ache ce que lon appelle une exception :
Exception non g r e : System . FormatException : Le format de la cha ne d entr e est incorrect . System . Number . StringToNumber ( String str , NumberStyles options , NumberBuffer & number , NumberFormatInfo info , Boolean parseDecimal ) System . Number . ParseInt32 ( String s , NumberStyles style , NumberFormatInfo info ) System . Convert . ToInt32 ( String value ) M a P r e m i e reAp plica tion . Program . Main ( String [] args ) dans C :\ Users \ Nico \ Documents \ Visual Studio 2010\ Projects \ C #\ M a P r e m i er eAppl icatio n \ MaPr emier eAppli catio n \ Program . cs : ligne 14

Il sagit tout simplement dune erreur. Nous aurons loccasion dtudier plus en dtail les exceptions dans les chapitres ultrieurs. Pour linstant, on a juste besoin de voir que ceci ne nous convient pas. Lerreur est explicite : Le format de la chane dentre est incorrect , mais cela se passe au moment de lexcution et notre programme plante lamentablement. Nous verrons dans le chapitre sur les exceptions comment grer les erreurs. 116

ENTRE LES TYPES INCOMPATIBLES En interne, la mthode Convert.ToInt32() utilise une autre mthode pour faire la conversion, il sagit de la mthode int.Parse(). Donc plutt que dutiliser une mthode qui en appelle une autre, nous pouvons nous en servir directement, par exemple :
1 2

string chaineAge = " 20 " ; int age = int . Parse ( chaineAge ) ; // age vaut 20

Bien sr, il se passera exactement la mme chose que prcdemment quand la chane ne reprsentera pas un entier correct. Il existe par contre une autre faon de convertir une chane en entier qui ne provoque pas derreur quand le format nest pas correct et qui nous informe si la conversion sest bien passe ou non, cest la mthode int.TryParse() qui sutilise ainsi :
1 2 3 4 5 6 7 8 9 10

string chaineAge = " ab20cd " ; int age ; if ( int . TryParse ( chaineAge , out age ) ) { Console . WriteLine ( " La conversion est possible , age vaut " + age ) ; } else { Console . WriteLine ( " Conversion impossible " ) ; }

Nous aurons alors :


Conversion impossible

La mthode int.TryParse nous renvoie vrai si la conversion est bonne, et faux sinon. Nous pouvons donc eectuer une action en fonction du rsultat grce aux if/else. On passe en paramtres la chane convertir et la variable o lon veut stocker le rsultat de la conversion. Le mot-cl out signie juste que la variable est modie par la mthode. Les mthodes que nous venons de voir Convert.ToString() ou int.TryParse() se dclinent en gnral pour tous les types de base, par exemple double.TryParse() ou Convert.ToDecimal(), etc.

En rsum
Il est possible, avec le casting, de convertir la valeur dun type dans un autre lorsquils sont compatibles entre eux. Le casting explicite sutilise en prxant une variable par un type prcis entre parenthses. Le framework .NET possde des mthodes permettant de convertir des types incompatibles entre eux sils sont smantiquement proches.

117

CHAPITRE 13. LES CONVERSIONS ENTRE LES TYPES

118

Chapitre

14
Dicult :

Lire le clavier dans la console

ous avons beaucoup crit avec Console.WriteLine(), maintenant il est temps de savoir lire. En loccurrence, il faut tre capable de rcuprer ce que lutilisateur a saisi au clavier. En eet, un programme qui na pas dinteraction avec lutilisateur, ce nest pas vraiment intressant. Vous imaginez un jeu o on ne fait que regarder ? a sappelle une vido, cest intressant aussi, mais ce nest pas pareil ! Aujourdhui, il existe beaucoup dinterfaces de communication que lon peut utiliser sur un ordinateur (clavier, souris, doigts, manettes,. . .). Dans ce chapitre nous allons regarder comment savoir ce que lutilisateur a saisi au clavier dans une application console.

119

CHAPITRE 14. LIRE LE CLAVIER DANS LA CONSOLE

Lire une phrase


Lorsque nous lui en donnerons la possibilit, lutilisateur de notre programme pourra saisir des choses grce son clavier. Nous pouvons lui demander son ge, sil veut quitter lapplication, lui permettre de saisir un mail, etc. Il nous faut donc trouver un moyen lui permettant de saisir des caractres en tapant sur son clavier. Nous pourrons faire cela grce la mthode Console.ReadLine :
1

string saisie = Console . ReadLine () ;

Lorsque notre application rencontre cette instruction, elle se met en pause et attend une saisie de la part de lutilisateur. La saisie sarrte lorsque lutilisateur valide ce quil a crit avec la touche Entre . Ainsi les instructions suivantes :
1 2 3

Console . WriteLine ( " Veuillez saisir une phrase et valider avec la touche \" Entr e \" " ) ; string saisie = Console . ReadLine () ; Console . WriteLine ( " Vous avez saisi : " + saisie ) ;

produiront le message suivant :


Veuillez saisir une phrase et valider avec la touche " Entr e " Bonjour tous Vous avez saisi : Bonjour tous

Vous aurez remarqu que nous stockons le rsultat de la saisie dans une variable de type chane de caractres. Cest bien souvent le seul type que nous aurons notre disposition lors des saisies utilisateurs. Cela veut bien sr dire que si vous avez besoin de manipuler lge de la personne, il faudra la convertir en entier. Par exemple :
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15

bool ageIsValid = false ; int age = -1 ; while (! ageIsValid ) { Console . WriteLine ( " Veuillez saisir votre ge " ) ; string saisie = Console . ReadLine () ; if ( int . TryParse ( saisie , out age ) ) ageIsValid = true ; else { ageIsValid = false ; Console . WriteLine ( "L ' ge que vous avez saisi est incorrect ... " ) ; } } Console . WriteLine ( " Votre ge est de : " + age ) ;

Le programme insistera alors pour que lutilisateur entre un nombre : 120

LIRE UN CARACTRE

Veuillez saisir votre ge abc L ge que vous avez saisi est incorrect ... Veuillez saisir votre ge 25 Votre ge est de : 25

Ce code est facile comprendre maintenant que vous matrisez les boucles et les conditions. Nous commenons par initialiser nos variables. Ensuite, nous demandons lutilisateur de saisir son ge. Nous tentons de le convertir en entier. Si cela fonctionne, on pourra passer la suite, sinon, tant que lge nest pas valide, nous recommenons la boucle. Il est primordial de toujours vrier ce que saisit lutilisateur, cela vous vitera beaucoup derreurs et de plantages intempestifs. On dit souvent quen informatique, il ne faut jamais faire conance lutilisateur !

Lire un caractre
Il peut arriver que nous ayons besoin de ne saisir quun seul caractre. Pour cela, nous allons pouvoir utiliser la mthode Console.ReadKey(). Nous pourrons nous en servir par exemple pour faire une pause dans notre application. Le code suivant rclame lattention de lutilisateur avant de dmarrer un calcul hautement important :
1 2 3 4 5 6 7 8

Console . WriteLine ( " Veuillez appuyer sur une touche pour d marrer le calcul ... " ) ; Console . ReadKey ( true ) ; int somme = 0 ; for ( int i = 0 ; i < 100 ; i ++) { somme += i ; } Console . WriteLine ( somme ) ;

Notez que nous avons pass true en paramtre de la mthode an dindiquer au C# que nous ne souhaitions pas que notre saisie apparaisse lcran. Si le paramtre avait t false et que javais par exemple appuy sur H pour dmarrer le calcul, le rsultat se serait vu prcd dun disgracieux H . . . Dailleurs, comment faire pour savoir quelle touche a t saisie ?

Il sut dobserver le contenu de la variable renvoye par la mthode Console.ReadKey. Elle renvoie en loccurrence une variable du type ConsoleKeyInfo. Nous pourrons 121

CHAPITRE 14. LIRE LE CLAVIER DANS LA CONSOLE tester la valeur Key de cette variable qui est une numration du type ConsoleKey. Par exemple :
1 2 3 4 5 6 7 8 9 10

Console . WriteLine ( " Voulez - vous continuer ( O / N ) ? " ) ; ConsoleKeyInfo saisie = Console . ReadKey ( true ) ; if ( saisie . Key == ConsoleKey . O ) { Console . WriteLine ( " On continue ... " ) ; } else { Console . WriteLine ( " On s ' arr te ... " ) ; }

Nous remarquons grce la compltion automatique que lnumration ConsoleKey possde plein de valeurs, comme illustr la gure 14.1.

Figure 14.1 La structure ConsoleKey contient toutes les touches du clavier Nous comparons donc la valeur ConsoleKey.O qui reprsente la touche O . Notez que le type ConsoleKeyInfo est ce quon appelle une structure et que Key est une proprit de la structure. Nous reviendrons dans la partie suivante sur ce que cela veut vraiment dire. Pour linstant, considrez juste quil sagit dune variable volue qui permet de contenir plusieurs choses. . . Quand mme, je trouve que cest super de pouvoir lire les saisies de lutilisateur ! Cest vital pour toute application qui se respecte. Aujourdhui, dans une application moderne, il est primordial de savoir lire la position de la souris, ragir des clics sur des boutons, ouvrir des fentres, etc. Cest quelque chose quil nest pas possible faire avec les programmes en mode console. Noubliez pas que ces applications sont parfaites pour apprendre le C#, cependant il est inimaginable de raliser une application digne de ce nom avec une application console. Pour ce faire, on utilisera des systmes graphiques adapts, qui sortent du cadre dtude de ce livre. 122

LIRE UN CARACTRE

En rsum
La mthode Console.ReadLine nous permet de lire des informations saisies par lutilisateur au clavier. Il est possible de lire toute une phrase termine par la touche Entre ou seulement un unique caractre.

123

CHAPITRE 14. LIRE LE CLAVIER DANS LA CONSOLE

124

Chapitre

15
Dicult :

Utiliser le dbogueur

ous allons maintenant faire une petite pause. Le C# cest bien, mais notre environnement de dveloppement, Visual C# Express, peut faire beaucoup plus que sa fonction basique dditeur de chiers. Il possde un outil formidable qui va nous permettre dtre trs ecaces dans le dbogage de nos applications et dans la comprhension de leur fonctionnement. Il sagit du dbogueur. Dcouvrons vite quoi il sert et comment il fonctionne !

125

CHAPITRE 15. UTILISER LE DBOGUEUR

quoi a sert ?
Fidle son habitude de nous simplier la vie, Visual C# Express possde un dbogueur. Cest un outil trs pratique qui va permettre dobtenir plein dinformations sur le droulement dun programme. Il va permettre dexcuter les instructions les unes aprs les autres, de pouvoir observer le contenu des variables, de revenir en arrire, bref, de savoir exactement ce qui se passe et surtout pourquoi tel bout de code ne fonctionne pas. Pour lutiliser, il faut que Visual C# Express soit en mode dbogage . Jenen ai pas encore parl. Pour ce faire, il faut excuter lapplication en appuyant sur F5 , ou alors passer par le menu Dboguer > Dmarrer le dbogage, ou encore cliquer sur le petit triangle vert dont licne rappelle un bouton de lecture (voir gure 15.1).

Figure 15.1 Dmarrer le dbogage de lapplication en cliquant sur le triangle vert Pour tudier le dbogueur, reprenons la dernire mthode du prcdent TP :
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24

static void Main ( string [] args ) { Console . WriteLine ( Ca l cu lS o mm eI nt e rs ec ti o n () ) ; } static int Ca l cu lS om m eI nt e rs ec ti o n () { List < int > multiplesDe3 = new List < int >() ; List < int > multiplesDe5 = new List < int >() ; for ( int i = 1 ; i <= 100 ; i ++) { if ( i % 3 == 0 ) multiplesDe3 . Add ( i ) ; if ( i % 5 == 0 ) multiplesDe5 . Add ( i ) ; } int somme = 0 ; foreach ( int m3 in multiplesDe3 ) { foreach ( int m5 in multiplesDe5 ) { if ( m3 == m5 )

126

METTRE UN POINT DARRT ET AVANCER PAS PAS


25 26 27 28 29

} return somme ;

somme += m3 ;

Mettre un point darrt et avancer pas pas


Pour que le programme sarrte sur un endroit prcis et quil nous permette de voir ce qui se passe, il va falloir mettre des points darrt dans notre code. Pour mettre un point darrt, il faut se positionner sur la ligne o nous souhaitons nous arrter, par exemple la premire ligne o nous appelons Console.WriteLine et appuyer sur F9 . Nous pouvons galement cliquer dans la marge gauche pour produire le mme rsultat. Un point rouge sache et indique quil y a un point darrt cet endroit, ainsi que vous pouvez le voir la gure 15.2.

Figure 15.2 Pose dun point darrt Il est possible de mettre autant de points darrt que nous le souhaitons, nimporte quel endroit de notre code. Lanons lapplication avec F5 . Nous pouvons voir que Visual C# Express sarrte sur la ligne prvue en la surlignant en jaune (voir gure 15.3). Nous en protons pour remarquer au niveau du carr rouge, que le dbogueur est en pause et quil est possible, soit de continuer lexcution (triangle), soit de larrter (carr), soit enn de redmarrer le programme depuis le dbut (carr avec une che blanche dedans). Nous pouvons dsormais excuter notre code pas pas, en nous servant des icnes ct ou des raccourcis clavier indiqus la gure 15.4. 127

CHAPITRE 15. UTILISER LE DBOGUEUR

Figure 15.3 Visual C# Express est arrt sur une ligne du code

Figure 15.4 Les boutons permettant davancer pas pas dans lapplication

128

OBSERVER DES VARIABLES Utilisons la touche F10 pour continuer lexcution du code pas pas. suivante La ligne se trouve surligne son tour. Appuyons nouveau sur la touche F10 pour aller linstruction suivante ; il ny en a plus : le programme se termine. Vous me direz : cest bien beau, mais nous ne sommes pas passs dans la mthode CalculSommeIntersection() . Eh oui, cest parce que nous avons utilis la touche F10 qui est le pas pas principal. Pour rentrer dans la mthode, il aurait fallu utiliser la touche F11 qui est le pas pas dtaill. Si nous souhaitons que le programme se poursuive pour jusquau prochain point aller . darrt, il sut dappuyer sur le triangle (play) ou sur F5

Relanons notre programme en mode dbogage, cette fois-ci, lorsque le dbogueur et sarrte sur notre point darrt, appuyons sur F11 pour rentrer dans le corps de la mthode. Voil, nous sommes dans lamthode CalculSommeIntersection(). Continuons appuyer plusieurs fois sur F10 an de rentrer dans le corps de la boucle for.

Observer des variables


ce moment-l du dbogage, si nous passons la souris sur la variable i, qui est lindice de la boucle, Visual C# Express va nous acher une mini-information (voir gure 15.5).

Figure 15.5 Visualisation du contenu dune variable Il nous indique que i vaut 1, ce qui est normal, car nous sommes dans la premire itration de la boucle. Si nous continuons le parcours en appuyant sur F10 plusieurs fois, nous voyons que la valeur de i augmente, conformment ce qui est attendu. Maintenant, mettons un point darrt (avec la touche F9 ) sur la ligne :
1

multiplesDe3 . Add ( i ) ;

et poursuivons lexcution en appuyant sur F5 . Il sarrte au moment o i vaut 3. Appuyons sur F10 pour excuter lajout de i la liste et passons la souris sur la liste (voir gure 15.6). Visual C# Express nous indique que la liste multiplesDe3 a son Count qui vaut 1. Si nous cliquons sur le + pour dplier la liste, nous pouvons voir que 3 a t ajout dans le premier emplacement de la liste (indice 0). Si nous continuons lexcution plusieurs fois, nous voyons que les listes se remplissent au fur et mesure (voir gure 15.7). 129

CHAPITRE 15. UTILISER LE DBOGUEUR

Figure 15.6 Visualisation dune liste

Figure 15.7 Visualisation dun lment de la liste Enlevez le point darrt sur la ligne en appuyant nouveau sur F9 et mettez un nouveau point darrt sur la ligne :
1

int somme = 0 ;

Poursuivez lexcution avec F5 , la boucle est termine, nous pouvons voir la gure 15.8 que les listes sont compltement remplies !

Figure 15.8 Visualiser tous les lments de la liste Grce au dbogueur, nous pouvons voir vraiment tout ce qui nous intresse, cest une des grandes forces du dbogueur et cest un atout vraiment trs utile pour comprendre ce qui se passe dans un programme (en gnral, a se passe mal !). Il est galement possible de voir les variables locales en regardant en bas dans la fentre Variables locales. Dans cette fentre, vous pourrez observer les variables qui sont 130

REVENIR EN ARRIRE couramment accessibles. Il existe galement une fentre Espion qui permet, de la mme faon, de surveiller une ou plusieurs variables prcises (voir gure 15.9).

Figure 15.9 La fentre des variables locales permet de voir le contenu des variables qui sont dans la porte

Revenir en arrire
Nom de Zeus, Marty ! On peut revenir dans le pass ?

Cest presque a, Doc ! Si vous souhaitez rexcuter un bout de code, parce que vous navez pas bien vu ou que vous avez rat linformation quil vous fallait, vous pouvez forcer le dbogueur revenir en arrire dans le programme. Pour cela, vous avez deux solutions. Soit vous faites un clic droit lendroit souhait et vous choisissez llment de menu Dfinir linstruction suivante, comme indiqu la gure 15.10 ; soit vous dplacez avec la souris la petite che jaune sur la gauche qui indique lendroit o nous en sommes, comme lillustre la gure 15.11. Il faut faire attention, car ce retour en arrire nen est pas compltement un. En eet, si vous revenez au dbut de la boucle qui calcule les multiples et que vous continuez lexcution, vous verrez que la liste continue se remplir. la n de la boucle, au lieu de contenir 33 lments, la liste des multiples de 3 en contiendra 66. En eet, aucun moment nous navons vid la liste et donc le fait de rexcuter cette partie de code risque de provoquer des comportements inattendus. Ici, il vaudrait mieux revenir au dbut de la mthode an que la liste soit de nouveau cre. Mme si a ne vous saute pas aux yeux pour linstant, vous verrez lusage que cette possibilit est bien pratique. Dautant plus quand vous aurez bien assimil toutes les notions de porte de variable. Il est important de noter que, bien que puissant, le dbogueur de la version gratuite de Visual Studio est vraiment moins puissant que celui des versions payantes. Il y a un certain nombre de choses que lon ne peut pas faire. 131

CHAPITRE 15. UTILISER LE DBOGUEUR

Figure 15.10 Revenir en arrire dans lapplication

Figure 15.11 La che jaune indique la future ligne de code excuter Mais rassurez-vous, celui-ci est quand mme dj bien avanc et permet de faire beaucoup de choses.

La pile des appels


Une autre fentre importante regarder est la pile des appels. Elle permet dindiquer o nous nous trouvons et par o nous sommes passs pour arriver cet endroit-l. Par exemple, si vous appuyez sur F11 et que vous rentrez dans la mthode CalculSommeIntersection(), vous pourrez voir dans la pile des appels que vous tes dans la mthode CalculSommeIntersection(), qui a t appele depuis la mthode Main() (voir gure 15.12). Si vous cliquez sur la ligne du Main(), Visual C# Express vous ramne automatiquement lendroit o a t fait lappel. Cette ligne est alors surligne en vert pour bien faire la dirence avec le surlignage en jaune qui est vraiment lendroit o se trouve le dbogueur. Cest trs pratique quand on a beaucoup de mthodes qui sappellent les unes la suite des autres (voir gure 15.13). La pile des appels est galement ache lorsquon rencontre une erreur, elle permettra didentier plus facilement o est le problme. Vous lavez vu dans le chapitre sur les conversions entre des types incompatibles. 132

LA PILE DES APPELS

Figure 15.12 La pile des appels

Figure 15.13 Pointer la mthode qui encapsule la ligne courante

133

CHAPITRE 15. UTILISER LE DBOGUEUR

En tout cas, le dbogueur est vraiment un outil forte valeur ajoute. Je ne vous ai prsent que le strict minimum ncessaire et indispensable. Mais croyez-moi, vous aurez loccasion dy revenir !

En rsum
Le dbogueur est un outil trs puissant permettant dinspecter le contenu des variables lors de lexcution dun programme. On peut sarrter un endroit de notre application grce un point darrt. Le dbogueur permet dexcuter son application pas pas et de suivre son volution.

134

Chapitre

16
Dicult :

TP : le jeu du plus ou du moins

aouh , nous augmentons rgulirement le nombre de choses que nous savons. Cest super. Nous commenons tre capables dcrire des applications qui ont un peu plus de panache !

Enn. . . moi, jy arrive ! Et vous ? Cest ce que nous allons vrier avec ce TP. Savoir interagir avec son utilisateur est important. Voici donc un petit TP sous forme de cration dun jeu simple qui va vous permettre de vous entraner. Lide est de raliser le jeu classique du plus ou du moins. . .

135

CHAPITRE 16. TP : LE JEU DU PLUS OU DU MOINS

Instructions pour raliser le TP


Je vous rappelle les rgles. Lordinateur calcule un nombre alatoire et nous devons le deviner. chaque saisie, il nous indique si le nombre saisi est plus grand ou plus petit que le nombre trouver. Une fois trouv, il nous indique en combien de coups nous avons russi trouver le nombre secret. Pour ce TP, vous savez presque tout faire. Il ne vous manque que linstruction pour obtenir un nombre alatoire. La voici, cette instruction permet de renvoyer un nombre compris entre 0 et 100 (exclu). Ne vous attardez pas trop sur sa syntaxe, nous aurons loccasion de comprendre exactement de quoi il sagit dans la partie suivante :
1

int valeurATrouver = new Random () . Next (0 , 100 ) ;

Le principe est grosso modo le suivant : tant quon na pas trouv la bonne valeur, nous devons en saisir une nouvelle. Dans ce cas, la console nous indique si la valeur est trop grande ou trop petite. Il faudra bien sr incrmenter un compteur de coups chaque essai. Noubliez pas de grer le cas o lutilisateur saisit nimporte quoi. Nous ne voudrions pas que notre premier jeu ait un bug qui fasse planter lapplication ! Allez, je vous en ai trop dit. Cest vous de jouer. Bon courage.

Correction
Voici ma correction de ce TP. Bien sr, il existe beaucoup de faons de raliser ce petit jeu. Sil fonctionne, cest que votre solution est bonne. Ma solution fonctionne, la voici :
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18

static void Main ( string [] args ) { int valeurATrouver = new Random () . Next (0 , 100 ) ; int nombreDeCoups = 0 ; bool trouve = false ; Console . WriteLine ( " Veuillez saisir un nombre compris entre 0 et 100 ( exclu ) " ) ; while (! trouve ) { string saisie = Console . ReadLine () ; int valeurSaisie ; if ( int . TryParse ( saisie , out valeurSaisie ) ) { if ( valeurSaisie == valeurATrouver ) trouve = true ; else { if ( valeurSaisie < valeurATrouver ) Console . WriteLine ( " Trop petit ... " ) ;

136

CORRECTION
19 20 21 22 23 24 25 26 27 28

} else

Console . WriteLine ( " Trop grand ... " ) ; } nombreDeCoups ++; Console . WriteLine ( " La valeur saisie est incorrecte , veuillez recommencer ... " ) ;

else

} Console . WriteLine ( " Vous avez trouv en " + nombreDeCoups + " coup ( s ) " ) ;

On commence par obtenir un nombre alatoire avec linstruction que jai fournie dans lnonc. Nous avons ensuite les initialisations de variables. Lentier nombreDeCoups va permettre de stocker le nombre dessai et le boolen trouve va permettre davoir une condition de sortie de boucle. Notre boucle dmarre et ne se terminera quune fois que le boolen trouve sera pass vrai (true). Dans le corps de la boucle, nous demandons lutilisateur de saisir une valeur que nous essayons de convertir en entier. Si la conversion choue, nous lindiquons lutilisateur et nous recommenons notre boucle. Notez ici que je nincrmente pas le nombre de coups, jugeant quil ny a pas lieu de pnaliser le joueur parce quil a mal saisi ou quil a renvers quelque chose sur son clavier juste avant de valider la saisie. Si en revanche, la conversion se passe bien, nous pouvons commencer comparer la valeur saisie avec la valeur trouver. Si la valeur est la bonne, nous passons le boolen vrai, ce qui nous permettra de sortir de la boucle et de passer la suite. Sinon, nous acherons un message pour indiquer si la saisie est trop grande ou trop petite en fonction du rsultat de la comparaison. Dans tous les cas, nous incrmenterons le nombre de coups. Enn, en sortie de boucle, nous indiquerons sa victoire au joueur ainsi que le nombre de coups utiliss pour trouver le nombre secret :
Veuillez saisir un nombre compris entre 0 et 100 ( exclu ) 50 Trop petit ... 75 Trop petit ... 88 Trop grand ... 81 Trop petit ... 85 Trop grand ... 83 Vous avez trouv en 6 coup ( s )

137

CHAPITRE 16. TP : LE JEU DU PLUS OU DU MOINS

Aller plus loin


Il est bien sr toujours possible damliorer le jeu. Nous pourrions par exemple ajouter un contrle sur les bornes de la saisie. Ainsi, si lutilisateur saisit un nombre suprieur ou gal 100 ou infrieur 0, nous pourrions lui rappeler les bornes du nombre alatoire. De mme, la n, plutt que dacher coup(s) , nous pourrions tester la valeur du nombre de coups. Sil est gal 1, on ache coup au singulier, sinon coups au pluriel. La boucle pourrait galement tre lgrement dirente. Plutt que de tester la condition de sortie sur un boolen, nous pourrions utiliser le mot-cl break. De mme, nous pourrions allger lcriture avec le mot-cl continue. Par exemple :
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32

static void Main ( string [] args ) { int valeurATrouver = new Random () . Next (0 , 100 ) ; int nombreDeCoups = 0 ; Console . WriteLine ( " Veuillez saisir un nombre compris entre 0 et 100 ( exclu ) " ) ; while ( true ) { string saisie = Console . ReadLine () ; int valeurSaisie ; if (! int . TryParse ( saisie , out valeurSaisie ) ) { Console . WriteLine ( " La valeur saisie est incorrecte , veuillez recommencer ... " ) ; continue ; } if ( valeurSaisie < 0 || valeurSaisie >= 100 ) { Console . WriteLine ( " Vous devez saisir un nombre entre 0 et 100 exclu ... " ) ; continue ; } nombreDeCoups ++; if ( valeurSaisie == valeurATrouver ) break ; if ( valeurSaisie < valeurATrouver ) Console . WriteLine ( " Trop petit ... " ) ; else Console . WriteLine ( " Trop grand ... " ) ; } if ( nombreDeCoups == 1 ) Console . WriteLine ( " Vous avez trouv en " + nombreDeCoups + " coup " ) ; else Console . WriteLine ( " Vous avez trouv en " + nombreDeCoups + " coups " ) ; }

138

ALLER PLUS LOIN Tout ceci est une question de got. Je prfre personnellement la version prcdente naimant pas trop les break et les continue. Mais aprs tout, chacun fait comme il prfre, limportant est que nous nous amusions crire le programme et y jouer ! Voil, ce TP est termin. Vous avez pu voir nalement que nous tions tout fait capables de raliser de petites applications rcratives. Personnellement, jai commenc mamuser faire de la programmation en ralisant toute sorte de petits programmes de ce genre. Je vous encourage fortement essayer de crer dautres programmes par vous-mmes. Plus vous vous entranerez faire des petits programmes simples et plus vous russirez apprhender les subtilits de ce que nous avons appris. Bien sr, plus tard, nous serons capables de raliser des applications plus compliques. . . Cela vous tente ? Alors continuons la lecture ! Vous pouvez tlcharger tous les codes sources de cet exercice grce au code web suivant : Copier ce code Code web : 694204

139

CHAPITRE 16. TP : LE JEU DU PLUS OU DU MOINS

140

Chapitre

17
Dicult :

La ligne de commande

ous ce nom un peu barbare se cache une fonctionnalit trs prsente dans nos usages quotidiens mais qui a tendance tre masque lutilisateur lambda. Nous allons, dans un premier temps, voir ce quest exactement la ligne de commande et quoi elle sert. Ensuite, nous verrons comment lexploiter dans notre application avec le C#. Notez que ce chapitre nest pas essentiel mais quil pourra srement vous servir plus tard dans la cration de vos applications.

141

CHAPITRE 17. LA LIGNE DE COMMANDE

Quest-ce que la ligne de commande ?


La ligne de commande, cest ce qui nous permet dexcuter nos programmes. Trs prsente lpoque o Windows existait peu, elle a tendance disparatre de nos utilisations. Mais pas le fond de son fonctionnement. En gnral, la ligne de commande sert passer des arguments un programme. Par exemple, pour ouvrir un chier texte avec le bloc-notes (notepad.exe), on peut le faire de deux faons direntes. Soit on ouvre le bloc-notes et on va dans le menu Fichier > Ouvrir puis on va chercher le chier pour louvrir. Soit on utilise la ligne de commande pour louvrir directement. Pour ce faire, il sut dutiliser la commande : notepad c:\test.txt et le chier c:\test.txt souvre directement dans le bloc-notes. Ce quil sest pass derrire, cest que nous avons demand dexcuter le programme notepad.exe avec le paramtre c:\test.txt. Le programme notepad a analys sa ligne de commande, il y a trouv un paramtre et il a ouvert le chier correspondant. Superbe ! Nous allons apprendre faire pareil !

Passer des paramtres en ligne de commande


La premire chose est de savoir comment faire pour passer des paramtres en ligne de commande. Plusieurs solutions existent. On peut, par exemple, excuter la commande que jai crite haut depuis le menu Dmarrer > Excuter ou la combinaison touche plus Windows + R (voir la gure 17.1).

Figure 17.1 Excuter une application avec la ligne de commande On peut galement le faire depuis une invite de commande, en allant dans : Menu Dmarrer > Accessoires > Invite de commande. Ceci nous ouvre une console noire, comme celle que lon connat bien et dans laquelle nous pouvons taper des instructions (voir gure 17.2). Mais nous, ce qui nous intresse surtout, cest de pouvoir le faire depuis Visual C# Express an de pouvoir passer des arguments notre programme. Pour ce faire, on va 142

PASSER DES PARAMTRES EN LIGNE DE COMMANDE

Figure 17.2 Excuter une application depuis linvite de commande aller dans les proprits de notre projet (clic droit sur le projet, Proprits) puis nous allons dans longlet Dboguer et nous voyons une zone de texte permettant de mettre des arguments la ligne de commande. Rajoutons par exemple Bonjour Nico (voir gure 17.3).

Figure 17.3 Modier les arguments de la ligne de commande Voil, maintenant lorsque nous excuterons notre application, Visual C# Express lui passera les arguments que nous avons dnis en paramtre de la ligne de commande. noter que dans ce cas, les arguments Bonjour Nico ne seront valables que lorsque nous excuterons lapplication travers Visual C# Express. videmment, si nous excutons notre application par linvite de commande, nous aurons besoin de repasser les arguments au programme pour quil puisse les exploiter. 143

CHAPITRE 17. LA LIGNE DE COMMANDE Cest bien ! Sauf que pour linstant, a ne nous change pas la vie ! Il faut apprendre traiter ces paramtres. . .

Lire la ligne de commande


On peut lire le contenu de la ligne de commande de deux faons direntes. Vous vous rappelez de la mthode Main() ? On a vu que Visual C# Express gnrait cette mthode avec des paramtres. Eh bien ces paramtres, vous ne devinerez jamais ! Ce sont les paramtres de la ligne de commande. Oh joie !
1 2 3

static void Main ( string [] args ) { }

La variable args est un tableau de chanes de caractres. Sachant que chaque paramtre est dlimit par des espaces, nous retrouverons chacun des paramtres un indice du tableau dirent. Ce qui fait que si jutilise le code suivant :
1 2 3 4

foreach ( string parametre in args ) { Console . WriteLine ( parametre ) ; }

et que je lance mon application avec les paramtres dnis prcdemment, je vais obtenir :
Bonjour Nico

Et voil, nous avons rcupr les paramtres de la ligne de commande, il ne nous restera plus qu les traiter dans notre programme. Comme prvu, nous obtenons deux paramtres. Le premier est la chane de caractres Bonjour , le deuxime est la chane de caractres Nico . Noubliez pas que cest le caractre despacement qui sert de dlimiteur entre les paramtres. Lautre faon de rcuprer la ligne de commande est dutiliser la mthode Environment.GetCommandLineArgs(). Elle renvoie un tableau contenant les paramtres, comme ce qui est pass en paramtres la mthode Main. La seule dirence, cest que dans le premier lment du tableau, nous trouverons le chemin complet de notre programme. Ceci peut tre utile dans certains cas. Si cela na aucun intrt pour vous, il sura de commencer la lecture partir de lindice numro 1. Lexemple suivant, ache toutes les valeurs du tableau :
1 2 3 4

foreach ( string parametre in Environment . GetCommandLineArgs () ) { Console . WriteLine ( parametre ) ; }

144

LIRE LA LIGNE DE COMMANDE Ce qui donne :


C :\ Users \ Nico \ Documents \ Visual Studio 2010\ Projects \ C #\ M a P r e m i e r e Appl icatio n \ MaP remier eAppli catio n \ bin \ Release \ M a P r e m i e r e Appl icatio n . exe Bonjour Nico

Attention : si vous devez accder un indice prcis du tableau, vriez bien que la taille du tableau le permet. Noubliez pas que si le tableau contient un seul lment et que vous essayez daccder au deuxime lment, alors il y aura une erreur. Tu as dit que ctait le caractre despacement qui permettait de dlimiter les paramtres. Jai essay de rajouter le paramtre C:\Program Files\test.txt ma ligne de commande an de changer lemplacement du chier, mais je me retrouve avec deux paramtres au lieu dun seul, cest normal ? Eh oui, il y a un espace entre Program et Files. Lastuce est de passer le paramtre entre guillemets, de cette faon : "C:\Program Files\test.txt". Nous aurons un seul paramtre dans la ligne de commande la place de deux. Comme par exemple la gure 17.4.

Figure 17.4 Modier les arguments de la ligne de commande

En rsum
On peut passer des paramtres une application en utilisant la ligne de commande. Le programme C# peut lire et interprter ces paramtres. Ces paramtres sont spars par des espaces.

145

CHAPITRE 17. LA LIGNE DE COMMANDE

146

Chapitre

18
Dicult :

TP : calculs en ligne de commande

t voici un nouveau TP qui va nous permettre de rcapituler un peu tout ce que nous avons vu. Au programme, des if, des switch, des mthodes, des boucles et. . . de la ligne de commande bien sr.

Grce nos connaissances grandissantes, nous arrivons augmenter la dicult de nos exercices et cest une bonne chose. Vous verrez que dans le moindre petit programme, vous aurez besoin de toutes les notions que nous avons apprises. Allez, ne tranons pas trop et vous de travailler !

147

CHAPITRE 18. TP : CALCULS EN LIGNE DE COMMANDE

Instructions pour raliser le TP


Lobjectif de ce TP est de raliser un petit programme qui permet deectuer une opration mathmatique partir de la ligne de commande. Il va donc falloir crire un programme que lon pourra appeler avec des paramtres. En imaginant que le programme sappelle MonProgramme, on pourrait lutiliser ainsi pour faire la somme de deux nombres : MonProgramme somme 2 5 Ce qui renverra bien sr 7. Pour eectuer une multiplication, nous pourrons faire : MonProgramme multiplication 2 5 et nous obtiendrons 10. Enn, pour calculer une moyenne, nous pourrons faire : MonProgramme moyenne 1 2 3 4 5 ce qui renverra 3. Les rgles sont les suivantes : il doit y avoir deux et seulement deux nombres composant laddition et la multiplication ; la moyenne peut contenir autant de nombres que souhait ; si le nombre de paramtres est incorrect, alors le programme achera un message daide explicite ; si le nombre attendu nest pas un double correct, on achera le message daide ; Plutt simple, non ? Alors, vous de jouer, il ny a pas de subtilit.

Correction
STOP ! Je relve les copies. Jai dit quil ny avait pas de subtilit, mais jai un peu menti en fait. Je lavoue, ctait pour que vous alliez au bout du dveloppement sans aide. Pour vrier que vous avez trouv la subtilit, essayez votre programme avec les paramtres suivants :
MonProgramme addition 5 ,5 2 ,5

Obtenez-vous 8 ?

Ctait peu prs la seule subtilit, il fallait juste savoir quun nombre virgule scri148

CORRECTION vait avec une virgule plutt quavec un point. Si nous crivons 2.5, alors le C# ne sait pas faire la conversion. En vrai, cest un peu plus compliqu que a et nous le verrons dans un prochain chapitre, mais lcriture du nombre virgule est dpendante de ce quon appelle la culture courante que lon peut modier dans les paramtres Horloge, langue et rgion du panneau de conguration de Windows. Si lon change le format de la date, de lheure ou des nombres on change la culture courante, et la faon dont les nombres virgule (et autres) sont grs est dirente. Quoi quil en soit, voici ma correction du TP :
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35

static void Main ( string [] args ) { if ( args . Length == 0 ) { AfficherAide () ; } else { string operateur = args [ 0 ]; switch ( operateur ) { case " addition " : Addition ( args ) ; break ; case " multiplication " : Multiplication ( args ) ; break ; case " moyenne " : Moyenne ( args ) ; break ; default : AfficherAide () ; break ; } } } static void AfficherAide () { Console . WriteLine ( " Utilisez l ' application de la mani re suivante : " ) ; Console . WriteLine ( " MonProgamme addition 2 5 " ) ; Console . WriteLine ( " MonProgamme multiplication 2 5 " ) ; Console . WriteLine ( " MonProgamme moyenne 2 5 10 11 " ) ; }

149

CHAPITRE 18. TP : CALCULS EN LIGNE DE COMMANDE


36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84

static void Addition ( string [] args ) { if ( args . Length != 3 ) { AfficherAide () ; } else { double somme = 0 ; for ( int i = 1 ; i < args . Length ; i ++) { double valeur ; if (! double . TryParse ( args [ i ] , out valeur ) ) { AfficherAide () ; return ; } somme += valeur ; } Console . WriteLine ( " R sultat de l ' addition : " + somme ) ; } } static void Multiplication ( string [] args ) { if ( args . Length != 3 ) { AfficherAide () ; } else { double resultat = 1 ; for ( int i = 1 ; i < args . Length ; i ++) { double valeur ; if (! double . TryParse ( args [ i ] , out valeur ) ) { AfficherAide () ; return ; } resultat *= valeur ; } Console . WriteLine ( " R sultat de la multiplication : " + resultat ) ; } } static void Moyenne ( string [] args ) { double total = 0 ;

150

ALLER PLUS LOIN


85 86 87 88 89 90 91 92 93 94 95 96 97

for ( int i = 1 ; i < args . Length ; i ++) { double valeur ; if (! double . TryParse ( args [ i ] , out valeur ) ) { AfficherAide () ; return ; } total += valeur ; } total = total / ( args . Length - 1 ) ; Console . WriteLine ( " R sultat de la moyenne : " + total ) ;

Disons que cest plus long que dicile. . . Oui, je sais, lors de laddition et de la multiplication, jai parcouru lensemble des paramtres alors que jaurais pu utiliser uniquement args[1] et args[2]. Lavantage, cest que si je supprime la condition sur le nombre de paramtres, alors mon addition pourra fonctionner avec autant de nombres que je le souhaite. . . Vous aurez remarqu que je commence mon parcours lindice 1, do la pertinence de lutilisation de la boucle for plutt que de la boucle foreach. Notez quand mme lutilisation du mot-cl return an de sortir prmaturment de la mthode en cas de problme. videmment, il y a plein de faons de raliser ce TP, la mienne en est une, il y en a dautres. Jespre que vous aurez fait attention au calcul de la moyenne. Nous retirons 1 la longueur du tableau pour obtenir le nombre de paramtres. Il faut donc faire attention lordre des parenthses an que C# fasse dabord la soustraction avant la division, alors que sans a, la division est prioritaire sur la soustraction. Sinon, cest lerreur de calcul assure, indigne de notre superbe application !

Aller plus loin


Ce code est trs bien. Si, si, cest moi qui lai fait ! Sauf quil y a quand mme un petit problme. . . dordre architectural. Il a t plutt simple crire en suivant lnonc du TP mais on peut faire encore mieux. On peut le rendre plus facilement maintenable en lorganisant diremment. Observez le code suivant :
1 2 3 4 5 6

static void Main ( string [] args ) { bool ok = false ; if ( args . Length > 0 ) { string operateur = args [ 0 ];

151

CHAPITRE 18. TP : CALCULS EN LIGNE DE COMMANDE


7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55

switch ( operateur ) { case " addition " : ok = Addition ( args ) ; break ; case " multiplication " : ok = Multiplication ( args ) ; break ; case " moyenne " : ok = Moyenne ( args ) ; break ; }

if (! ok ) AfficherAide () ;

static void AfficherAide () { Console . WriteLine ( " Utilisez l ' application de la mani re suivante : " ) ; Console . WriteLine ( " MonProgamme addition 2 5 " ) ; Console . WriteLine ( " MonProgamme multiplication 2 5 " ) ; Console . WriteLine ( " MonProgamme moyenne 2 5 10 11 " ) ; } static bool Addition ( string [] args ) { if ( args . Length != 3 ) return false ; double somme = 0 ; for ( int i = 1 ; i < args . Length ; i ++) { double valeur ; if (! double . TryParse ( args [ i ] , out valeur ) ) return false ; somme += valeur ; } Console . WriteLine ( " R sultat de l ' addition : " + somme ) ; return true ;

static bool Multiplication ( string [] args ) { if ( args . Length != 3 ) return false ; double resultat = 1 ;

152

ALLER PLUS LOIN


56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80

for ( int i = 1 ; i < args . Length ; i ++) { double valeur ; if (! double . TryParse ( args [ i ] , out valeur ) ) return false ; resultat *= valeur ; } Console . WriteLine ( " R sultat de la multiplication : " + resultat ) ; return true ;

static bool Moyenne ( string [] args ) { double total = 0 ; for ( int i = 1 ; i < args . Length ; i ++) { double valeur ; if (! double . TryParse ( args [ i ] , out valeur ) ) return false ; total += valeur ; } total = total / ( args . Length - 1 ) ; Console . WriteLine ( " R sultat de la moyenne : " + total ) ; return true ; }

Nous nachons laide qu un seul endroit en utilisant le fait que les mthodes renvoient un boolen indiquant si lopration a t possible ou pas. Dune manire gnrale, il est important dessayer de rendre son code le plus maintenable possible, comme nous lavons fait ici. Vous pouvez tlcharger tous les codes sources de cet exercice grce au code web suivant : Copier ce code Code web : 846482

153

CHAPITRE 18. TP : CALCULS EN LIGNE DE COMMANDE

154

Troisime partie

Le C#, un langage orient objet

155

Chapitre

19
Dicult :

Introduction la programmation oriente objet

ans ce chapitre, nous allons essayer de dcrire ce quest la programmation oriente objet (abrge souvent en POO). Je dis bien essayer , car pour tre compltement traite, la POO ncessiterait quon lui ddie un ouvrage entier !

Nous allons tcher daller lessentiel et de rester proche de la pratique. Ce nest pas trs grave si tous les concepts abstraits ne sont pas parfaitement apprhends : il est impossible dapprendre la POO en deux heures. Cela ncessite une longue pratique, beaucoup dempirisme et des approfondissements thoriques. Ce qui est important ici, cest de comprendre les notions de base et de pouvoir les utiliser dans de petits programmes. Aprs cette mise au point, attaquons sans plus tarder la programmation oriente objet !

157

CHAPITRE 19. INTRODUCTION LA PROGRAMMATION ORIENTE OBJET

Quest-ce quun objet ?


Vous avez pu voir prcdemment que jai utilis de temps en temps le mot objet et que le mot-cl new est apparu comme par magie. . . Il a t dicile de ne pas trop en parler et il est temps den savoir un peu plus. Alors, quest-ce quun objet ?

Si on prend le monde rel (si, si, vous allez voir, vous connaissez. . .), nous sommes entours dobjets : une chaise, une table, une voiture, etc. Ces objets forment un tout. Ils possdent des proprits (la chaise possde quatre pieds, elle est de couleur bleue, etc.). Ces objets peuvent faire des actions (la voiture peut rouler, klaxonner, etc.). Ils peuvent galement interagir entre eux (lobjet conducteur dmarre la voiture, lobjet voiture fait tourner lobjet volant, etc.). Il faut bien faire attention distinguer ce quest lobjet et ce quest la dnition dun objet. La dnition de lobjet (ou structure de lobjet) permet dindiquer ce qui compose un objet, cest--dire quelles sont ses proprits, ses actions etc. Comme par exemple, le fait quune chaise ait des pieds ou quon puisse sasseoir dessus. Par contre, lobjet chaise est bien concret. On peut donc avoir plusieurs objets chaise : on parle galement dinstances. Les objets chaise, ce sont bien ceux, concrets, que lon voit devant nous autour de lobjet table, pour dmarrer une partie de belote. On peut faire lanalogie avec notre dictionnaire qui nous dcrit ce quest une chaise. Le dictionnaire dcrit en quoi consiste lobjet, et linstance de lobjet reprsente le concret associ cette dnition. Chaque objet a sa propre vie et dire dun autre. Nous pouvons avoir une chaise bleue, une autre rouge, une autre avec des roulettes, une casse. . . Vous voyez, nalement, la notion dobjet est plutt simple quand on la ramne ce quon connat dj ! Sachant quun objet en programmation, cest comme un objet du monde rel, mais ce nest pas forcment restreint au matriel. Un chien est un objet. Des concepts comme lamour ou une ide sont galement des objets, tandis quon ne dirait pas cela dans le monde rel. En conclusion : La dnition (ou structure) dun objet est un concept abstrait, comme une dnition dans le dictionnaire. Cette dnition dcrit les caractristiques dun objet (la chaise a des pieds, lhomme a des jambes, etc.). Cette dnition est unique, comme une dnition dans le dictionnaire. Un objet ou une instance est la ralisation concrte de la structure de lobjet. On peut avoir de multiples instances, comme les cent voitures sur le parking devant chez moi. Elles peuvent avoir des caractristiques direntes (une voiture bleue, une 158

LENCAPSULATION voiture lectrique, une voiture cinq portes, etc.).

Lencapsulation
Le fait de concevoir une application comme un systme dobjets interagissant entre eux apporte une certaine souplesse et une forte abstraction. Prenons un exemple tout simple : la machine caf du bureau. Nous insrons nos pices dans le monnayeur, choisissons la boisson et nous nous retrouvons avec un gobelet de la boisson commande. Nous nous moquons compltement de savoir comment cela fonctionne lintrieur et nous pouvons compltement ignorer si le caf est en poudre, en grain, comment leau est ajoute, chaue, comment le sucre est distribu, etc. Tout ce qui nous importe, cest que le fait de mettre des sous dans la machine nous donne un caf qui va nous permettre dattaquer la journe. Voil un bel exemple de programmation oriente objet. Nous manipulons un objet MachineACafe qui a des proprits (allume/teinte, prsence de caf, prsence de gobelet,. . .) et qui sait faire des actions (AccepterMonnaie, DonnerCafe,. . .). Et cest tout ce que nous avons besoin de savoir : on se moque du fonctionnement interne, peu importe ce qui se passe lintrieur, notre objet nous donne du caf, point ! Cest ce quon appelle lencapsulation. Cela permet de protger linformation contenue dans notre objet et de le rendre manipulable uniquement par ses actions ou proprits. Ainsi, lutilisateur ne peut accder ni au caf ni au sucre et encore moins la monnaie. Notre objet est ainsi protg et fonctionne un peu comme une bote noire. Lintrt est que si la personne qui entretient la machine met du caf en grain la place du caf soluble, cest invisible pour lutilisateur qui na juste qu se soucier de mettre ses pices dans la machine. Lencapsulation protge donc les donnes de lobjet et son fonctionnement interne.

Hritage
Un autre lment important dans la programmation oriente objet, que nous allons aborder maintenant, est lhritage. Ah bon ? Les objets aussi peuvent mourir et transmettre leur patrimoine ?

Eh bien cest presque comme en droit, part que lobjet ne meurt pas et quil ny a pas de taxe sur lhritage. Cest--dire quun objet dit pre peut transmettre certaines de ses caractristiques un autre objet dit ls . Pour cela, on pourra dnir une relation dhritage entre eux. Sil y a une relation dhritage entre un objet pre et un objet ls, alors lobjet ls 159

CHAPITRE 19. INTRODUCTION LA PROGRAMMATION ORIENTE OBJET hrite de lobjet pre. On dit galement que lobjet ls est une spcialisation de lobjet pre ou quil drive de lobjet pre. En langage plus courant, on peut galement dire que lobjet ls est une sorte dobjet pre. Je ne suis pas sr de comprendre, peux-tu donner des exemples ?

Alors, prenons par exemple lobjet chien et imaginons ses caractristiques tires du monde rel en utilisant lhritage : lobjet chien est une sorte dobjet mammifere ; lobjet mammifere est une sorte dobjet animal ; lobjet animal est une sorte dobjet etreVivant. Chaque pre est un peu plus gnral que son ls. Et inversement, chaque ls est un peu plus spcialis que son pre. Avec lexemple du dessus, un mammifre est un peu plus gnral quun chien, ltre vivant tant encore plus gnral quun mammifre. Il est possible pour un pre davoir plusieurs ls, par contre, linverse est impossible, un ls ne peut pas avoir plusieurs pres. Eh oui, cest triste mais cest comme a, cest le rgne du pre clibataire avec plusieurs enfants charge ! Ainsi, un objet chat peut galement tre un ls de lobjet mammifere. Un objet vegetal peut galement tre ls de lobjet etreVivant. Ce quon peut reproduire sur le schma suivant (voir gure 19.1) o chaque bulle reprsente un objet et chaque che reprsente lhritage entre les objets.

Figure 19.1 Lhritage entre les objets On peut dnir une sorte de hirarchie entre les objets, un peu comme on le ferait avec un arbre gnalogique. La dirence est quun objet hritant dun autre peut obtenir certains ou tous les comportements de lobjet quil spcialise, alors quun enfant nhrite 160

POLYMORPHISME - SUBSTITUTION pas forcment des yeux bleus de sa mre ou du ct bougon de son grand-pre, le hasard de la nature faisant le reste. Pour bien comprendre cet hritage de comportement, empruntons nouveau les exemples au monde rel. Ltre vivant peut, par exemple, faire laction vivre . Le mammifre possde des yeux. Le chien, qui est une sorte dtre vivant et une sorte de mammifre, peut galement faire laction vivre et aura des yeux. Le chat, qui est une autre sorte dtre vivant, peut lui aussi faire laction vivre et aura galement des yeux. On voit bien ici que le chat et le chien hritent des comportements de leurs parents et grands-parents en tant capables de vivre et davoir des yeux. Par contre, laction aboyer est spcique au chien. Ce qui veut dire que ni le chat ni le dauphin ne seront capables daboyer. Il ny a que dans les dessins anims de Tex Avery que ceci est possible ! videmment, il ny a pas de notion dhritage entre le chien et le chat et laction daboyer est dnie au niveau du comportement du chien. Ceci implique galement que seul un objet qui est une sorte de chien, par exemple lobjet labrador ou lobjet chihuahua, pourra hriter du comportement aboyer , car il y a une relation dhritage entre eux. Finalement, cest plutt logique ! Rappelons juste avant de terminer ce paragraphe quun objet ne peut pas hriter de plusieurs objets. Il ne peut hriter que dun seul objet. Le C# ne permet pas ce quon appelle lhritage multiple, a contrario dautres langages comme le C++ par exemple. Voil globalement pour la notion dhritage. Je dis globalement, car il y a certaines subtilits que je nai pas abordes mais ce nest pas trop grave ; vous verrez dans les chapitres suivants comment le C# utilise la notion dhritage et ce quil y a vraiment besoin de savoir. Ne vous inquitez pas si certaines notions sont encore un peu oues, vous comprendrez srement mieux grce la pratique.

Polymorphisme - Substitution
Le mot polymorphisme suggre quune chose peut prendre plusieurs formes. Sous ce terme un peu barbare se cachent plusieurs notions de lorient objet qui sont souvent source derreurs. Je vais volontairement passer rapidement sur certains points qui ne vont pas nous servir pour me concentrer sur ceux qui sont importants pour ce livre. En fait, on peut dire quune manifestation du polymorphisme est la capacit pour un objet de faire une mme action avec dirents types dintervenants. Cest ce quon appelle le polymorphisme ad hoc ou le polymorphisme paramtr . Par exemple, notre objet voiture peut rouler sur la route, rouler sur lautoroute, rouler sur la terre 161

CHAPITRE 19. INTRODUCTION LA PROGRAMMATION ORIENTE OBJET si elle est quipe de pneus adquats, rouler au fond de leau si elle est amphibie, etc. Concrtement ici, je fais interagir un objet voiture avec un objet autoroute ou un objet terre. . . par laction qui consiste rouler. Cela peut paratre anodin dcrit ainsi, mais nous verrons ce que cela implique avec le C#. La substitution est une autre manifestation du polymorphisme. Il sagit de la capacit dun objet ls rednir des caractristiques ou des actions dun objet pre. Prenons par exemple un objet mammifre qui sait faire laction se dplacer . Les objets qui drivent du mammifre peuvent potentiellement avoir se dplacer dune manire dirente. Par exemple, lobjet homme va se dplacer sur ses deux jambes et donc diremment de lobjet dauphin qui se dplacera grce ses nageoires ou bien encore diremment de lobjet homme accident qui va avoir besoin de bquilles pour saider dans son dplacement. Tous ces mammifres sont capables de se dplacer, mais chacun va le faire dune manire dirente. Ceci est donc possible grce la substitution qui permet de rednir un comportement hrit. Ainsi, chaque ls sera libre de rcrire son propre comportement, si celui de son pre ne lui convient pas.

Interfaces
Un autre concept important de la programmation oriente objet est la notion dinterface. Linterface est un contrat que sengage respecter un objet. Il indique en gnral un comportement. Prenons un exemple dans notre monde rel et connu : les prises de courant. Elles fournissent de llectricit 220 V avec deux trous et (souvent) une prise de terre. Peu importe ce quil y a derrire, du courant alternatif de la centrale du coin, un transformateur, quelquun qui pdale. . . nous saurons coup sr que nous pouvons brancher nos appareils lectriques car ces prises sengagent nous fournir du courant alternatif avec le branchement adquat. Elles respectent le contrat ; elles sont branchables . Ce dernier terme est un peu barbare et peut faire mal aux oreilles. Mais vous verrez que suxer des interfaces par un able est trs courant et permet dtre plus prcis sur la smantique de linterface. Cest galement un suxe qui fonctionne en anglais et les interfaces du framework .NET nissent pour la plupart par able . noter que les interfaces ne fournissent quun contrat, elles ne fournissent pas dimplmentation, cest--dire pas de code C#. Ce nest pas clair ? Les interfaces indiquent que les objets qui choisissent de respecter ce contrat auront forcment telle action ou telle caractristique mais elles nindiquent pas comment faire, cest--dire quelles 162

QUOI SERT LA PROGRAMMATION ORIENTE OBJET ? nont pas de code C# associ. Chaque objet respectant cette interface (on parle dobjet implmentant une interface) sera responsable de coder la fonctionnalit associe au contrat. Pour manipuler ces prises, nous pourrons utiliser cette interface en disant : Allez hop, tous les branchables, venez par ici, on a besoin de votre courant ! Peu importe que lobjet implmentant cette interface soit une prise murale ou une prise relie une dynamo, ou autre, nous pourrons manipuler ces objets par leur interface et donc brancher nos prises permettant dalimenter nos appareils. Contrairement lhritage, un objet est capable dimplmenter plusieurs interfaces. Par exemple, une pompe chaleur peut tre chauante et refroidissante 1 . Nous en avons termin avec la thorie sur les interfaces. Il est fort probable que vous ne saisissiez pas encore tout lintrt des interfaces ou ce quelles sont exactement. Nous allons y revenir avec des exemples concrets et vous verrez des utilisations dinterfaces dans le cadre du framework .NET qui vous claireront davantage.

quoi sert la programmation oriente objet ?


Nous avons dcrit plusieurs concepts de la programmation oriente objet mais nous navons pas encore dit quoi elle allait nous servir. En fait, on peut dire que la POO est une faon de dvelopper une application qui consiste reprsenter (on dit galement modliser ) une application informatique sous la forme dobjets, ayant des proprits et pouvant interagir entre eux. La modlisation oriente objet est proche de la ralit ce qui fait quil sera relativement facile de modliser une application de cette faon. De plus, les personnes non-techniques pourront comprendre et ventuellement participer cette modlisation. Cette faon de modliser les choses permet galement de dcouper une grosse application, gnralement oue, en une multitude dobjets interagissant entre eux. Cela permet de dcouper un gros problme en plus petits an de le rsoudre plus facilement. Utiliser une approche oriente objet amliore galement la maintenabilit. Plus le temps passe et plus une application est dicile maintenir. Il devient dicile de corriger des choses sans tout casser ou dajouter des fonctionnalits sans provoquer une rgression par ailleurs. Lorient objet nous aide ici limiter la casse en proposant une approche o les modications internes un objet naectent pas tout le reste du code, grce notamment lencapsulation. Un autre avantage de la POO est la rutilisabilit. Des objets peuvent tre rutiliss ou mme tendus grce lhritage. Cest le cas par exemple de la bibliothque de classes du framework .NET que nous avons dj utilise. Cette bibliothque nous fournit par exemple tous les objets permettant de construire des applications graphiques. Inutile de rinventer toute la mcanique pour grer des fentres dans une application, le framework .NET sait dj faire tout a. Nous avons juste besoin dutiliser un objet fentre,
1. Notez quen franais, nous pourrons galement utiliser le suxe ante . En anglais, nous aurons plus souvent able .

163

CHAPITRE 19. INTRODUCTION LA PROGRAMMATION ORIENTE OBJET dans lequel nous pourrons mettre un objet bouton et un objet zone de texte. Ces objets hritent tous des mmes comportements, comme le fait dtre cliquable ou slectionnable, etc. De mme, des composants tout faits et prts lemploi peuvent tre vendus par des entreprises tierces (systmes de log, contrles utilisateur amliors, etc.). Il faut savoir que la POO, cest beaucoup plus que a et nous en verrons des subtilits plus loin, mais comprendre ce quest un objet est globalement susant pour une grande partie de louvrage.

En rsum
Lapproche oriente objet permet de modliser son application sous la forme dinteractions entre objets. Les objets ont des proprits et peuvent faire des actions. Ils masquent la complexit dune implmentation grce lencapsulation. Les objets peuvent hriter de fonctionnalits dautres objets sil y a une relation dhritage entre eux.

164

Chapitre

20
Dicult :

Crer son premier objet

h , enn un peu de concret et surtout de code. Dans ce chapitre, nous allons appliquer les notions que nous avons vues sur la programmation oriente objet pour continuer notre apprentissage du C#.

Mme si vous navez pas encore apprhend exactement o la POO pouvait nous mener, ce nest pas grave. Les notions saneront au fur et mesure de la lecture du livre. Il est temps pour nous de commencer crer des objets, les faire hriter entre eux, etc. Bref, jouer, grce ces concepts, avec le C#. Vous verrez quavec un peu de pratique tout sclaircira ; vous saisirez lintrt de la POO et comment tre ecace avec le C#.

165

CHAPITRE 20. CRER SON PREMIER OBJET

Tous les types C# sont des objets


a y est, il a ni avec son baratin qui donne mal la tte ? Pourquoi tout ce blabla sur les objets ? Parce que tout dans le C# est un objet. Comme dj dit, une fentre Windows est un objet. Une chane de caractres est un objet. La liste que nous avons vue plus haut est un objet. Nous avons vu que les objets possdaient des caractristiques ; il sagit de proprits. Un objet peut galement faire des actions ; ce sont des mthodes. Suivant ce principe, une chane de caractres est un objet et possde des proprits (par exemple sa longueur). De la mme faon, il sera possible que les chanes de caractres fassent des actions (par exemple se mettre en majuscules). Nous allons voir plus bas quil est videmment possible de crer nos propres objets (chat, chien, etc.) et que grce eux, nous allons enrichir les types qui sont notre disposition. Un peu comme nous avons dj fait avec les numrations. Voyons ds prsent comment faire, grce aux classes.

Les classes
Dans le chapitre prcdent, nous avons parl des objets mais nous avons galement parl de la dnition de lobjet, de sa structure. Eh bien, cest exactement ce quest une classe. Une classe est une manire de reprsenter un objet. Le C# nous permet de crer des classes. Nous avons dj pu voir une classe dans le code que nous avons utilis prcdemment et qui a t gnr par Visual C# Express, la classe Program. Nous ny avons pas fait trop attention, mais voil peu prs quoi elle ressemblait :
1 2 3 4 5 6

class Program { static void Main ( string [] args ) { } }

Cest elle qui contenait la mthode spciale Main() qui sert de point dentre lapplication. Nous pouvons dcouvrir avec des yeux neufs le mot-cl class qui, comme son nom le suggre, permet de dnir une classe, cest--dire la structure dun objet. 166

LES CLASSES Rappelez-vous, les objets peuvent avoir des caractristiques et faire des actions. Ici, cest exactement ce qui se passe. Nous avons dni la structure dun objet Program qui contient une action : la mthode Main(). Vous aurez remarqu au passage que pour dnir une classe, nous utilisons nouveau les accolades permettant de crer un bloc de code qui dlimite la classe. Passons sur cette classe particulire et lanons-nous dans la cration dune classe qui nous servira crer des objets. Par exemple, une classe Voiture. noter quil est possible de crer une classe plusieurs endroits dans le code, mais en gnral, nous utiliserons un nouveau chier, du mme nom que la classe, qui lui sera entirement ddi. Une rgle dcriture commune beaucoup de langages de programmation est que chaque chier doit avoir une seule classe. Faisons un clic droit sur notre projet pour ajouter une nouvelle classe, comme indiqu la gure 20.1.

Figure 20.1 Ajouter une classe Visual C# Express nous ouvre sa fentre permettant de faire lajout dun lment en se positionnant sur llment Classe. Nous pourrons donner un nom cette classe : Voiture (voir gure 20.2). Vous remarquerez que les classes commencent en gnral par une majuscule. Visual C# Express nous gnre le code suivant :
1 2 3 4 5 6 7 8 9 10 11

using using using using

System ; System . Collections . Generic ; System . Linq ; System . Text ;

namespace M a Premie reApp licati on { class Voiture { } }

167

CHAPITRE 20. CRER SON PREMIER OBJET

Figure 20.2 Choix dun modle de chier Classe dans la fentre de slection des modles Nous retrouvons le mot-cl class suivi du nom de notre classe Voiture et les accolades ouvrantes et fermantes permettant de dlimiter notre classe. Notons galement que cette classe fait partie de lespace de nom MaPremiereApplication qui est lespace de nom par dfaut de notre projet. Pour nous simplier le travail, Visual C# Express nous a galement inclus quelques espaces de noms souvent utiliss. Mais pour linstant cette classe ne fait pas grand-chose. Comme cette classe est vide, elle ne possde ni proprits ni actions. Nous ne pouvons absolument rien faire avec, part en crer une instance, cest--dire un objet. Cela se fait grce lutilisation du mot-cl new. Nous y reviendrons plus en dtail plus tard, mais cela donne :
1 2 3 4 5

static void Main ( string [] args ) { Voiture voitureNicolas = new Voiture () ; Voiture voitureJeremie = new Voiture () ; }

Nous avons cr deux instances de lobjet Voiture et nous les stockons dans les variables voitureNicolas et voitureJeremie. Si vous vous rappelez bien, nous aurions logiquement d crire :
1

M a P r e m i e r eAppl icatio n . Voiture voitureNicolas = new M a P r e mier eAppli catio n . Voiture () ;

Ou alors positionner le using qui allait bien, permettant dinclure lespace de nom MaPremiereApplication. 168

LES MTHODES En fait, ici cest superu vu que nous crons les objets depuis la mthode Main(), qui fait partie de la classe Program, faisant partie du mme espace de nom que notre classe.

Les mthodes
Nous venons de crer notre objet Voiture mais nous ne pouvons pas en faire grandchose. Ce qui est bien dommage. a serait bien que notre voiture puisse klaxonner par exemple si nous sommes bloqus dans des embouteillages. Bref, que notre voiture soit capable de faire des actions. Qui dit action dit mthode . Nous allons pouvoir dnir des mthodes faisant partie de notre objet Voiture. Pour ce faire, il sut de crer une mthode, comme nous lavons dj vu, directement dans le corps de la classe :
1 2 3 4 5 6 7

class Voiture { void Klaxonner () { Console . WriteLine ( " Pouet ! " ) ; } }

Notez quand mme labsence du mot-cl static que nous tions obligs de mettre avant. Je vous expliquerai un peu plus loin pourquoi. Ce qui fait que si nous voulons faire klaxonner notre voiture, nous aurons juste besoin dinvoquer la mthode Klaxonner() depuis lobjet Voiture, ce qui scrit :
1 2

Voiture voitureNicolas = new Voiture () ; voitureNicolas . Klaxonner () ;

Cela ressemble beaucoup ce que nous avons dj fait. En fait, nous avons dj utilis des mthodes sur des objets. Rappelez-vous, nous avons utilis la mthode Add() permettant dajouter une valeur une liste :
1 2

List < int > maListe = new List < int >() ; maListe . Add ( 1 ) ;

Comme nous avons un peu plus de notions dsormais, nous pouvons remarquer que nous instancions un objet List (plus prcisment, une liste dentier) grce au mot-cl new et que nous invoquons la mthode Add de notre liste pour lui ajouter lentier 1. Nous avons fait pareil pour obtenir un nombre alatoire, dans une criture un peu plus concise. Nous avions crit :
1

int valeurATrouver = new Random () . Next (0 , 100 ) ;

Ce qui peut en fait scrire :


1 2

Random random = new Random () ; int valeurATrouver = random . Next (0 , 100 ) ;

169

CHAPITRE 20. CRER SON PREMIER OBJET Nous crons un objet du type Random grce new puis nous appelons la mthode Next() qui prend en paramtres les bornes du nombre alatoire que nous souhaitons obtenir (0 tant inclus et 100 exclu). Puis nous stockons le rsultat dans un entier. Eh oui, nous avons manipul quelques objets sans le savoir. . . ! Revenons notre embouteillage et compilons le code nous permettant de faire klaxonner notre voiture :
1 2

Voiture voitureNicolas = new Voiture () ; voitureNicolas . Klaxonner () ;

Impossible de compiler, le compilateur nous indique lerreur suivante :


M a P r e m i e r e Appli catio n . Voiture . Klaxonner () est inaccessible en raison de son niveau de protection

Diantre ! Dj un premier chec dans notre apprentissage de lobjet ! Vous aurez devin grce au message derreur que la mthode Klaxonner() semble inaccessible. Nous expliquerons un peu plus loin de quoi il sagit. Pour linstant, nous allons juste prxer notre mthode du mot-cl public, comme ceci :
1 2 3 4 5 6 7

class Voiture { public void Klaxonner () { Console . WriteLine ( " Pouet ! " ) ; } }

Nous allons y revenir juste aprs, mais le mot-cl public permet dindiquer que la mthode est accessible depuis nimporte o. Excutons notre application et nous obtenons :
Pouet !

Waouh, la premire action dun de nos objets ! Bien sr, ces mthodes peuvent galement avoir des paramtres et renvoyer un rsultat. Par exemple :
1 2 3 4 5 6 7 8 9 10

class Voiture { public bool VitesseAutorisee ( int vitesse ) { if ( vitesse > 90 ) return false ; else return true ; } }

170

LES MTHODES Cette mthode accepte une vitesse en paramtre et si elle est suprieure 90, alors la vitesse nest pas autorise. Cette mthode pourrait galement scrire :
1 2 3 4 5 6 7

class Voiture { public bool VitesseAutorisee ( int vitesse ) { return vitesse <= 90 ; } }

En eet, nous souhaitons renvoyer faux si la vitesse est suprieure 90 et vrai si la vitesse est infrieure ou gale. Donc en fait, nous souhaitons renvoyer la valeur du rsultat de la comparaison dinfriorit ou dgalit de la vitesse 90, cest--dire :
1 2 3 4 5 6 7 8

class Voiture { public bool VitesseAutorisee ( int vitesse ) { bool estVitesseAutorisee = vitesse <= 90 ; return estVitesseAutorisee ; } }

Ce que nous pouvons crire nalement en une seule ligne comme prcdemment. Il est bien sr possible davoir plusieurs mthodes dans une mme classe et elles peuvent sappeler entre elles, par exemple :
1 2 3 4 5 6 7 8 9 10 11 12 13

class Voiture { public bool VitesseAutorisee ( int vitesse ) { return vitesse <= 90 ; } public void Klaxonner () { if (! VitesseAutorisee ( 180 ) ) Console . WriteLine ( " Pouet ! " ) ; }

Quitte rouler une vitesse non autorise, autant faire du bruit ! 171

CHAPITRE 20. CRER SON PREMIER OBJET

Notion de visibilit
Oui, mais le mot-cl public. . . il nous a bien sauv la vie, mais. . . quest-ce donc ? En fait, je lai rapidement voqu et nous nous sommes bien rendu compte que sans ce mot-cl, impossible de compiler car la mthode ntait pas accessible. Le mot-cl public sert indiquer que notre mthode pouvait tre accessible depuis dautres classes ; en loccurrence dans notre exemple depuis la classe Program. Cest-dire que sans ce mot-cl, il est impossible dautres objets dutiliser cette mthode. Pour faire en sorte quune mthode soit inaccessible, nous pouvons utiliser le mot-cl private. Ce mot-cl permet davoir une mthode qui nest accessible que depuis la classe dans laquelle elle est dnie. Prenons lexemple suivant :
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33

class Voiture { public bool Demarrer () { if ( ClesSurLeContact () ) { DemarrerLeMoteur () ; return true ; } return false ; } public void SortirDeLaVoiture () { if ( ClesSurLeContact () ) PrevenirLUtilisateur () ; } private bool ClesSurLeContact () { // faire quelque chose pour v rifier return true ; } private void DemarrerLeMoteur () { // faire quelque chose pour d marrer le moteur } private void PrevenirLUtilisateur () { Console . WriteLine ( " Bip bip bip " ) ; }

172

NOTION DE VISIBILIT
34

Ici seules les mthodes Demarrer() et SortirDeLaVoiture() sont utilisables depuis une autre classe, cest--dire que ce sont les seules mthodes que nous pourrons invoquer, car elles sont publiques. Les autres mthodes sont prives la classe et ne pourront tre utilises qu lintrieur de la classe elle-mme. Les autres classes nont pas besoin de savoir comment dmarrer le moteur ou comment vrier que les cls sont sur le contact, elles nont besoin que de pouvoir dmarrer ou sortir de la voiture. Les mthodes prives sont exclusivement rserves lusage interne de la classe. Notez dailleurs que la compltion automatique nest pas propose pour les mthodes inaccessibles. Il existe dautres indicateurs de visibilits que nous allons rapidement dcrire : Visibilit public protected private internal protected internal Description Accs non restreint Accs depuis la mme classe ou depuis une classe drive Accs uniquement depuis la mme classe Accs restreint la mme assembly Accs restreint la mme assembly ou depuis une classe drive

Les visibilits qui vont le plus vous servir sont reprsentes par les mots cls public et private. Nous verrons que le mot-cl protected va servir un peu plus tard quand nous parlerons dhritage. Notez quinternal pourra tre utilis une fois que nous aurons bien matris toutes les notions. Ces mots-cls sont utilisables avec beaucoup dautres concepts. Nous avons utilis les mthodes pour les illustrer mais ceci est galement valable pour les classes ou les proprits que nous allons dcouvrir juste aprs. Au dbut, nous avons pu dclarer une classe sans prciser de visibilit. . . et pareil pour la premire mthode qui ne compilait pas. . . cest normal ? Oui, il existe des visibilits par dfaut suivant les types dclars. Vous aurez compris par exemple que la visibilit par dfaut dune mthode est prive si lon ne spcie pas le mot-cl. Pour viter tout risque et toute ambigut, il est recommand de toujours indiquer la visibilit ; ce que nous ferons dsormais dans ce livre, maintenant que nous savons de quoi il sagit. 173

CHAPITRE 20. CRER SON PREMIER OBJET

Les proprits
Des objets cest bien. Des actions sur ces objets, cest encore mieux. Il nous manque encore les caractristiques des objets. Cest l quinterviennent les proprits. Sans le savoir, vous avez dj utilis des proprits, par exemple dans le code suivant :
1 2 3 4 5

string [] jours = new string [] { " Lundi " , " Mardi " , " Mercredi " , " Jeudi " , " Vendredi " , " Samedi " , " Dimanche " }; for ( int i = 0 ; i < jours . Length ; i ++) { Console . WriteLine ( jours [ i ]) ; }

Dans la boucle, nous utilisons jours.Length. Nous utilisons en fait la proprit Length du tableau jours , un tableau tant bien sr un objet. Nous avons pu utiliser dautres proprits, par exemple dans linstruction suivante :
1 2 3 4 5

List < string > jours = new List < string > { " Lundi " , " Mardi " , " Mercredi " , " Jeudi " , " Vendredi " , " Samedi " , " Dimanche " }; for ( int i = 0 ; i < jours . Count ; i ++) { Console . WriteLine ( jours [ i ]) ; }

Ici, Count est une proprit de la liste jours . De la mme faon, nous avons la possibilit de crer des proprits sur nos classes pour permettre dajouter des caractristiques nos objets. Par exemple, nous pouvons rajouter les proprits suivantes notre voiture : une couleur, une marque, une vitesse. Il y a plusieurs faons de rajouter des caractristiques un objet. La premire est dutiliser des variables membres.

Les variables membres


Ici en fait, un objet peut avoir une caractristique sous la forme dune variable publique qui fait partie de la classe. Pour ce faire, il sut de dnir simplement la variable lintrieur des blocs de code dlimitant la classe et de lui donner la visibilit public. Rajoutons par exemple une chane de caractres permettant de stocker la couleur de la voiture :
1 2 3 4

public class Voiture { public string Couleur ; }

Notez que jai rajout les visibilits pour la classe et pour la variable. Grce ce code, la chane de caractres Couleur est dsormais une caractristique de la classe Voiture. Nous pourrons lutiliser en faisant suivre notre objet de loprateur 174

LES PROPRITS . suivi du nom de la variable. Ce qui donne :


1 2 3 4

Voiture voitureNicolas voitureNicolas . Couleur Voiture voitureJeremie voitureJeremie . Couleur

= = = =

new Voiture () ; " rouge " ; new Voiture () ; " verte " ;

Cela ressemble beaucoup ce que nous avons dj fait. Nous utilisons le . pour accder aux proprits dun objet. Comme dhabitude, les variables vont pouvoir stocker des valeurs. Ainsi, nous pourrons avoir une voiture rouge pour Nicolas et une voiture verte pour Jrmie. Notez ici quil sagit bien de variables membres et non de vraies proprits. En gnral, les variables dune classe ne doivent jamais tre publiques. Nous utiliserons rarement cette construction.

Les proprits :
Les proprits sont en fait des variables volues. Elles sont mi-chemin entre une variable et une mthode. Prenons cet exemple :
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15

public class Voiture { private int vitessePrivee ; public int Vitesse { get { return vitessePrivee ; } set { vitessePrivee = value ; } } }

Nous pouvons voir que nous dnissons dans un premier temps une variable prive, vitessePrivee de type entier. Comme prvu, cette variable est masque pour les utilisateurs dobjets Voiture. Ainsi, le code suivant :
1 2

Voiture voitureNicolas = new Voiture () ; voitureNicolas . vitessePrivee = 50 ;

provoquera lerreur de compilation dsormais bien connue :


MaP r e m i e r e A p p l icatio n . Voiture . vitessePrivee est inaccessible en raison de son niveau de protection

175

CHAPITRE 20. CRER SON PREMIER OBJET Par contre, nous en avons prot pour dnir la proprit Vitesse, de type int. Pour ceci, nous avons dni une partie de la proprit avec le mot-cl get suivi dun return vitessePrivee. Et juste en dessous, nous avons utilis le mot-cl set, suivi de vitessePrivee = value. Ce que nous avons fait, cest dnir la possibilit de lire la proprit grce au motcl get. Ici, la lecture de la proprit nous renvoie la valeur de la variable prive. De la mme faon, nous avons dni la possibilit daecter une valeur la proprit en utilisant le mot-cl set et en aectant la valeur la variable prive. Les blocs de code dlimits par get et set se comportent un peu comme des mthodes. Ils ont un corps qui est dlimit par des accolades et dans le cas du get, ils doivent renvoyer une valeur du mme type que la proprit. noter que dans le cas du set, value est un mot-cl qui permet de dire : la valeur que nous avons aecte la proprit . Prenons lexemple suivant :
1 2 3

Voiture voitureNicolas = new Voiture () ; voitureNicolas . Vitesse = 50 ; Console . WriteLine ( voitureNicolas . Vitesse ) ;

La premire instruction instancie bien sr une voiture. La deuxime instruction consiste appeler le bloc de code set de Vitesse qui met la valeur 50 dans la pseudo-variable value qui est stocke ensuite dans la variable prive. Lorsque nous appelons la troisime instruction, nous lisons la valeur de la proprit et pour ce faire, nous passons par le get qui nous renvoie la valeur de la variable prive. Ok, cest super, mais dans ce cas, pourquoi passer par une proprit et pas par une variable ? Mme sil parat que les variables ne doivent jamais tre publiques. . . Eh bien parce que dans ce cas-l, la proprit peut faire un peu plus que simplement renvoyer une valeur. Et aussi parce que nous masquons la structure interne de notre classe ceux qui veulent lutiliser. Nous pourrions trs bien envisager daller lire la vitesse dans les structures internes du moteur, ou en faisant un calcul pouss par rapport au coecient du vent et de lge du capitaine (ou plus vraisemblablement en allant lire la valeur en base de donnes). Et ici, nous pouvons tirer parti de la puissance des proprits pour masquer tout a lappelant qui, lui, na besoin que dune vitesse. Par exemple :
1 2 3 4 5 6 7

private int vitessePrivee ; public int Vitesse { get { int v = vitesseDesRoues * rayon * coefficient ; // ce calcul est compl tement farfelu ! M et treAJo urLeC ompteu r () ;

176

LES PROPRITS
8 9 10 11 12 13 14 15 16 17

} set {

A d a p t e r L a V i t e s s e D e s E s s u i e G l a c e s () ; return v ;

// faire la mise jour des variables internes M e t t r eAJo urLeCo mpteu r () ; A d a p t e r L a V i t e s s e D e s E s s u i e G l a c e s () ;

En faisant tout a dans le bloc de code get, nous masquons les rouages de notre classe lutilisateur. Lui, il na besoin que dobtenir la vitesse, sans sencombrer du compteur ou des essuie-glaces. Bien sr, la mme logique peut sadapter au bloc de code set, qui permet daecter une valeur la proprit. Il est galement possible de rendre une proprit en lecture seule, cest--dire non modiable par les autres objets. Il pourra par exemple sembler bizarre de positionner une valeur la vitesse alors quen fait, pour mettre jour la vitesse, il faut appeler la mthode AppuyerPedale(double forceAppui). Pour empcher les autres objets de pouvoir directement mettre jour la proprit Vitesse, il sut de ne pas dclarer le bloc de code set et de ne garder quun get. Par exemple :
1 2 3 4 5 6 7 8 9 10 11 12

public class Voiture { private int vitessePrivee ; public int Vitesse { get { // faire des calculs ... return vitessePrivee ; } } }

Ce qui fait que si nous tentons daecter une valeur la proprit Vitesse depuis une autre classe, par exemple :
1 2

Voiture voitureNicolas = new Voiture () ; voitureNicolas . Vitesse = 50 ;

Nous aurons lerreur de compilation suivante :


La propri t ou l indexeur M aPrem ier eAp plica tion . Voiture . Vitesse ne peut pas tre assign -- il est en lecture seule

177

CHAPITRE 20. CRER SON PREMIER OBJET Le compilateur nous indique donc trs justement quil est impossible de faire cette aectation car la proprit est en lecture seule. Il devient donc impossible pour un utilisateur de cette classe de modier la vitesse de cette faon. De mme, il est possible de dnir une proprit pour quelle soit accessible en criture seule. Il sut de dnir uniquement le bloc de code set :
1 2 3 4 5 6 7 8

private double acceleration ; public double Acceleration { set { acceleration = value ; } }

Ainsi, si nous tentons dutiliser la proprit en lecture, avec par exemple :


1

Console . WriteLine ( voitureNicolas . Acceleration ) ;

Nous aurons lerreur de compilation attendue :


La propri t ou l indexeur M aPrem iereAp plicat ion . Voiture . Acceleration ne peut pas tre utilis dans ce contexte , car il lui manque l accesseur get

Ce bridage daccs des proprits prend tout son sens quand nous souhaitons exposer nos objets dautres consommateurs qui nont aucun intrt connatre la structure interne de notre classe. Cest un des principes de lencapsulation.

Les proprits auto-implmentes


Les proprits auto-implmentes sont une fonctionnalit que nous allons beaucoup utiliser. Il sagit de la dnition dune proprit de manire trs simplie qui va nous servir dans la grande majorit des cas o nous aurons besoin dcrire des proprits. Dans cet ouvrage, nous lutiliserons trs souvent. Ainsi, le code suivant que nous avons dj vu :
1 2 3 4 5 6 7 8 9 10 11 12

private int vitesse ; public int Vitesse { get { return vitesse ; } set { vitesse = value ; } }

178

LES PROPRITS est un cas trs frquent dutilisation de proprits. Nous exposons ici une variable prive travers les proprits get et set. Lcriture du dessus est quivalente la suivante :
1

public int Vitesse { get ; set ; }

Dans ce cas, le compilateur fait le boulot lui-mme, il gnre (dans le code compil) une variable membre qui va servir stocker la valeur de la proprit. Cest exactement pareil, sauf que cela nous simplie grandement lcriture. En plus, nous pouvons encore acclrer son criture en utilisant ce quon appelle des snippets qui sont des extraits de code. Pour les utiliser, il sut de commencer crire un mot et Visual C# nous complte le reste. Un peu comme la compltion automatique sauf que cela fonctionne pour des bouts de code trs rptitifs et trs classiques crire. Commencez par exemple taper prop ; Visual C# nous propose plusieurs extraits de code, comme vous pouvez le voir sur la gure 20.3.

Figure 20.3 Utilisation du snippet de cration de proprit auto-implmente Appuyez sur Tab ou Entre pour slectionner cet extrait de code et appuyez ensuite sur Tab pour que Visual C# gnre lextrait de code correspondant la proprit auto-implmente (voir gure 20.4).

Figure 20.4 Changer le type de la proprit Ici, int est surlign et vous pouvez, si vous le souhaitez, changer le type de la proprit, par exemple string. Appuyez nouveau sur Tab et Visual C# surligne le nom de 179

CHAPITRE 20. CRER SON PREMIER OBJET la proprit, que vous pouvez nouveau changer. Appuyez enn sur Entre pour terminer la saisie et vous aurez une belle proprit auto-implmente (voir gure 20.5).

Figure 20.5 La proprit auto-implmente Vous avez pu remarquer quen commenant taper prop , Visual C# Express vous a propos dautres extraits de code, par exemple propfull qui va gnrer la proprit complte telle quon la vue un peu plus haut. Dautres extraits de code existent, comme propg qui permet de dnir une proprit en lecture seule. En eet, comme au chapitre prcdent, il est possible de dnir des proprits autoimplmentes en lecture seule ou en criture seule avec une criture simplie. Dans le cas des proprits auto-implmentes, il y a cependant une subtilit. Pour avoir de la lecture seule, nous devons indiquer que laectation est prive, comme on peut le voir en utilisant lextrait de code propg . Visual C# Express nous gnre le bout de code suivant :
1

public int Vitesse { get ; private set ; }

En positionnant une proprit dcriture en priv, Visual C# Express autorise la classe Voiture modier la valeur de Vitesse, que ce soit par une mthode ou par une proprit. noter que si nous navions pas mis private set et que nous avions simplement supprim le set, alors la compilation aurait t impossible. En eet, il savre dicile dexploiter une proprit auto-implmente en lecture alors que nous navons pas la possibilit de lui donner une valeur. Ou inversement. Alors, pourquoi nous avoir parl de la possibilit de compltement supprimer la lecture ou lcriture ? Ce nest pas plus intressant de toujours mettre la proprit en private ? Si cest une proprit auto-implmente, videmment que si. Par contre, si nous nutilisons pas une proprit auto-implmente et que nous utilisons une variable membre pour sauvegarder la valeur de la proprit, nous pourrons ventuellement modier ou lire la valeur de la variable partir dune mthode ou dune autre proprit. Bref, maintenant que vous connaissez les deux syntaxes, vous pourrez utiliser la plus adapte votre besoin. 180

LES PROPRITS Voil pour les proprits. noter que quand nous avons beaucoup de proprits initialiser sur un objet, nous pouvons utiliser une syntaxe plus concise. Par exemple, les instructions suivantes :
1 2 3 4 5

Voiture voitureNicolas = new Voiture () ; voitureNicolas . Couleur = " Bleue " ; voitureNicolas . Marque = " Peugeot " ; voitureNicolas . Vitesse = 50 ;

sont quivalentes linstruction :


1

Voiture voitureNicolas = new Voiture { Couleur = " Bleue " , Marque = " Peugeot " , Vitesse = 50 };

Les accolades servent ici initialiser les proprits au mme moment que linstanciation de lobjet. Note : cela parat vident, mais il est bien sr possible daccder aux proprits dune classe depuis une mthode de la mme classe.

En rsum
On utilise des classes pour reprsenter quasiment la plupart des objets. On utilise le mot-cl class pour dnir une classe et le mot-cl new pour linstancier. Une classe peut possder des caractristiques (les proprits) et peut faire des actions (les mthodes).

181

CHAPITRE 20. CRER SON PREMIER OBJET

182

Chapitre

21
Dicult :

Manipuler des objets

aintenant que nous avons bien vu comment dnir des objets, il est temps de savoir les utiliser. Nous allons voir dans ce chapitre quelles sont les subtilits de linstanciation des objets. Vous verrez notamment ce quest un constructeur et quon peut avoir des valeurs nulles pour des objets. Nhsitez pas jouer avec tous ces concepts, il est important dtre laise avec les bases pour pouvoir tre ecace avec les concepts avancs !

183

CHAPITRE 21. MANIPULER DES OBJETS

Le constructeur
Le constructeur est une mthode particulire de lobjet qui permet de faire des choses au moment de la cration dun objet, cest--dire au moment o nous utilisons le motcl new. Il est en gnral utilis pour initialiser des valeurs par dfaut dun objet. Par exemple, si nous voulons que lors de la cration dune voiture, elle ait automatiquement une vitesse, nous pouvons faire :
1 2 3 4 5 6 7 8 9

public class Voiture { public int Vitesse { get ; set ; } public Voiture () { Vitesse = 5 ; }

Le constructeur est en fait une mthode spciale qui a le mme nom que la classe et qui ne possde pas de type de retour. Elle est appele lors de la cration de lobjet, avec new. Pour illustrer le comportement du constructeur, ajoutons une mthode Rouler notre classe, de cette faon :
1 2 3 4 5 6 7 8 9 10 11 12 13 14

public class Voiture { public int Vitesse { get ; set ; } public Voiture () { Vitesse = 5 ; } public void Rouler () { Console . WriteLine ( " Je roule " + Vitesse + " km / h " ) ; }

Que nous pourrons appeler ainsi :


1 2 3 4

Voiture voitureNicolas = new Voiture () ; voitureNicolas . Rouler () ; voitureNicolas . Vitesse = 50 ; voitureNicolas . Rouler () ;

Au moment de linstanciation de lobjet avec new, la vitesse va tre gale 5. Nous faisons rouler la voiture. Puis nous changeons la vitesse pour la passer 50 et nous faisons nouveau rouler la voiture :

184

LE CONSTRUCTEUR

Je roule 5 km / h Je roule 50 km / h

Le constructeur est la premire mthode tre appele lors de la cration dun objet. Cest lendroit appropri pour faire des initialisations, ou pour charger des valeurs, etc. Le constructeur que nous avons vu est ce quon appelle le constructeur par dfaut, car il ne possde pas de paramtres. Il est possible de passer des paramtres un constructeur, pour initialiser des variables de notre classe avec des valeurs. Pour ce faire, nous devons dclarer un constructeur avec un paramtre. Par exemple :
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19

public class Voiture { public int Vitesse { get ; set ; } public Voiture () { Vitesse = 5 ; } public Voiture ( int vitesse ) { Vitesse = vitesse ; } public void Rouler () { Console . WriteLine ( " Je roule " + Vitesse + " km / h " ) ; }

Ainsi, nous pourrons crer un objet voiture en lui prcisant une vitesse par dfaut de cette faon :
1

Voiture voitureNicolas = new Voiture ( 20 ) ;

Aprs ceci, la variable voitureNicolas aura une vitesse de 20. Bien sr, nous pourrions faire la mme chose sans utiliser de constructeur, en aectant une valeur la proprit aprs avoir instanci lobjet. Ce qui fonctionnerait tout fait. Sauf quil ne se passe pas exactement la mme chose. Le constructeur est vraiment appel en premier, ds quon utilise new pour crer un objet. Les proprits sont aectes fatalement aprs, donc tout dpend de ce que lon veut faire. Donc, oui, on pourrait aecter une valeur des proprits pour faire ce genre dinitialisation juste aprs avoir instanci notre objet mais cela nous oblige crire une instruction supplmentaire qui pourrait ne pas paratre vidente ou obligatoire. Une chose est sre avec le constructeur, cest que nous sommes obligs dy passer et ceci, peu importe la faon dont on utilise la classe. Linitialisation devient donc obligatoire, on vite le risque quune proprit soit nulle. 185

CHAPITRE 21. MANIPULER DES OBJETS noter quil est possible de cumuler les constructeurs tant quils ont chacun des paramtres dirents. Dans notre exemple, nous pourrons donc crer des voitures de deux faons direntes :
1 2

Voiture voitureNicolas = new Voiture () ; // vitesse vaut 5 Voiture voitureJeremie = new Voiture ( 20 ) ; // vitesse vaut 20

Il est aussi possible de ne pas dnir de constructeur par dfaut et davoir uniquement un constructeur possdant des paramtres. Dans ce cas, il devient impossible dinstancier un objet sans lui passer de paramtres.

Instancier un objet
Nous allons revenir prsent sur linstanciation dun objet. Comme nous venons de le voir, nous utilisons le mot-cl new pour crer une instance dun objet. Cest lui qui permet la cration dun objet. Il appelle le constructeur correspondant. Si aucun constructeur nexiste, il ne se passera rien de plus quune cration de base. Par exemple :
1

Voiture voitureNicolas = new Voiture () ;

Pour rentrer un peu dans la technique, au moment de linstanciation dun objet, loprateur new cre lobjet et le met une place disponible en mmoire. Cette adresse mmoire est conserve dans la variable voitureNicolas. On dit que voitureNicolas contient une rfrence vers lobjet. Nous verrons un peu plus tard ce que cela implique. Ce principe ressemble un peu au pointeur que nous pourrions trouver dans des langages comme le C ou le C++. Typiquement, si vous savez ce quest un pointeur, vous pouvez vous reprsenter une rfrence comme un pointeur volu. Comme pour les types que nous avons vus plus haut, nous sommes obligs dinitialiser un objet avant de lutiliser. Sinon, Visual C# Express nous gnrera une erreur de compilation. Par exemple, les instructions suivantes :
1 2

Voiture voitureNicolas ; voitureNicolas . Vitesse = 5 ;

provoqueront lerreur de compilation suivante :


Utilisation d une variable locale non assign e voitureNicolas

En eet, Visual C# Express est assez intelligent pour se rendre compte que lon va essayer daccder un objet qui na pas t initialis. Il faut toujours initialiser une variable avant de pouvoir lutiliser. Comme pour les types prcdents, il est possible de dissocier la dclaration dun objet de son instanciation, en crivant les instructions sur plusieurs lignes, par exemple : 186

INSTANCIER UN OBJET
1 2 3

Voiture voitureNicolas ; // des choses voitureNicolas = new Voiture () ;

ceci est possible tant que nous nutilisons pas la variable voitureNicolas avant de lavoir instancie. Les objets peuvent galement avoir une valeur nulle. Ceci est dirent de labsence dinitialisation car la variable est bien initialise et sa valeur vaut nul . Ceci est possible grce lemploi du mot-cl null.
1

Voiture voitureNicolas = null ;

Attention, il est par contre impossible daccder un objet qui vaut null. Eh oui, comment voulez-vous vous asseoir sur une chaise qui nexiste pas ? Eh bien vous vous retrouverez avec les fesses par terre, personne ne vous a indiqu que la chaise nexistait pas. Cest pareil pour notre application, si nous tentons dutiliser une voiture qui nexiste pas, nous aurons droit un beau plantage. Par exemple, avec le code suivant :
1 2

Voiture voitureNicolas = null ; voitureNicolas . Vitesse = 5 ;

vous naurez pas derreur la compilation, par contre vous aurez un message dexception assez explicite :
Exception non g r e : System . Nu ll Ref er enc eE xce pti on : La r f rence d objet n est pas d finie une instance d un objet . M a P r e m i e r e A p plica tion . Program . Main ( String [] args ) dans C :\ Users \ Nico \ Documents \ Visual Studio 2010\ Projects \ C #\ M a P r e m i e r e Appl icatio n \ MaP remier eAppli catio n \ Program . cs : ligne 13

Comme nous lavons dj vu, le programme nous ache une exception ; nous avons dit que ctait simplement une erreur qui faisait planter notre programme. Ce nest pas bien ! Surtout que cela se passe au moment de lexcution. Nous perdons toute crdibilit ! Ici, le programme nous dit que la rfrence dun objet nest pas dnie une instance dun objet. Concrtement, cela veut dire que nous essayons de travailler sur un objet null. Pour viter ce genre derreur lexcution, il faut imprativement instancier ses objets, en utilisant loprateur new, comme nous lavons dj vu. Il nest cependant pas toujours pertinent dinstancier un objet dont on pourrait ne pas avoir besoin. Le C# nous ore donc la possibilit de tester la nullit dun objet. Il sut dutiliser loprateur de comparaison == en comparant un objet au mot-cl null, par exemple :
1 2

string prenom = " Nicolas " ; Voiture voiture = null ;

187

CHAPITRE 21. MANIPULER DES OBJETS


3 4 5 6 7 8 9 10 11 12

if ( prenom == " Nicolas " ) voiture = new Voiture { Vitesse = 50 }; if ( voiture == null ) { Console . WriteLine ( " Vous n ' avez pas de voiture " ) ; } else { voiture . Rouler () ; }

Ainsi, seul Nicolas possdera une voiture et le test de nullit sur lobjet permet dviter une erreur dexcution si le prnom est dirent. Maintenant que vous connaissez le mot-cl null et que vous savez quun objet peut prendre une valeur nulle, nous allons revenir sur un point que jai rapidement abord auparavant. Je ne sais pas si vous vous en rappelez, mais lors de ltude des oprateurs logiques jai parl du fait que loprateur OU ( || ) valuait la premire condition et si elle tait vraie alors il nvaluait pas la suivante, considrant que de toute faon, le rsultat allait tre vrai. Ce dtail prend toute son importance dans le cas suivant :
1 2 3 4

if ( voiture == null || voiture . Couleur == " Bleue " ) { // faire quelque chose }

Dans ce cas, si la voiture est eectivement nulle, alors le fait dvaluer la proprit Couleur de la voiture devrait renvoyer une erreur. Heureusement, le C# avait prvu le coup. Si la premire condition est vraie alors la seconde ne sera pas value, ce qui vitera lerreur. Ainsi, nous sommes srs de navoir aucune voiture bleue. Il est par contre vident quune telle condition utilisant loprateur ET (&&) est une hrsie car pour que la condition soit vraie, le C# a besoin dvaluer les deux oprandes. Et donc si la voiture est nulle, lutilisation dune proprit sur une valeur nulle renverra une erreur. Notons galement que lorsque nous utilisons loprateur ET (&&), si la premire oprande est fausse, alors de la mme faon, il nvalue pas la seconde, car pour que la condition soit vraie il faut que les deux le soient. Ce qui fait quil est galement possible dcrire ce code :
1 2 3 4

if ( voiture != null && voiture . Couleur == " Rouge " ) { // faire autre chose }

qui ne provoquera pas derreur lexcution, mme si voiture vaut null car dans ce cas, le fait que le premier test soit faux vitera le test de lautre partie de lexpression. Vous verrez que vous aurez loccasion dutiliser le mot-cl null rgulirement. 188

LE MOT-CL THIS

Le mot-cl this
Lorsque nous crivons le code dune classe, le mot-cl this reprsente lobjet dans lequel nous nous trouvons. Il permet de clarier ventuellement le code, mais il est gnralement facultatif. Ainsi, pour accder une variable de la classe ou ventuellement une mthode, nous pouvons les prxer par this. . Par exemple, nous pourrions crire notre classe de cette faon :
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21

public class Voiture { public int Vitesse { get ; set ; } public string Couleur { get ; set ; } public Voiture () { this . Vitesse = 5 ; } public void Rouler () { Console . WriteLine ( " Je roule " + this . Vitesse + " km / h "); } public void Accelerer ( int acceleration ) { this . Vitesse += acceleration ; this . Rouler () ; }

Ici, dans le constructeur, nous utilisons le mot-cl this pour accder la proprit Vitesse. Cest la mme chose dans la mthode Rouler. De la mme faon, on peut utiliser this.Rouler() pour appeler la mthode Rouler depuis la mthode Accelerer(). Cest une faon pour la classe de dire : Regardez, avec this, cest "ma variable moi" . Notez bien sr que sans le mot-cl this, notre classe compilera quand mme et sera tout fait fonctionnelle. Ce mot-cl est facultatif mais il peut aider bien faire la dirence entre ce qui appartient la classe et ce qui fait partie des paramtres des mthodes ou dautres objets utiliss. Suivant les personnes, le mot-cl this est soit systmatiquement utilis, soit jamais. Je fais plutt partie des personnes qui ne lutilisent jamais. Il arrive par contre certaines situations o il est absolument indispensable, comme celle-ci, mais en gnral, jessaie dviter ce genre de construction :
1 2 3

public void ChangerVitesse ( int Vitesse ) { this . Vitesse = Vitesse ;

189

CHAPITRE 21. MANIPULER DES OBJETS


4

Vous remarquerez que le paramtre de la mthode ChangerVitesse() et la proprit ou variable membre de la classe ont exactement le mme nom. Ceci est possible ici mais source derreurs, les variables ayant des portes direntes. Il savre que dans ce cas, le mot-cl this est indispensable. On pourra donc viter lambigut en prxant la proprit membre avec le mot-cl this. Je recommande plutt de changer le nom du paramtre, quitte utiliser une minuscule, ce qui augmentera la lisibilit et vitera des erreurs potentielles. En gnral, des conventions de nommage pourront nous viter de nous retrouver dans ce genre de situation. a y est, nous savons instancier et utiliser des objets ! Savoir crer et utiliser ses propres objets est trs important dans un programme orient objet. Vous allez galement avoir besoin trs rgulirement dutiliser des objets tout faits, comme ceux venant de la bibliothque de classe du framework .NET. Nous comprenons dailleurs mieux pourquoi elle sappelle bibliothque de classes . Il sagit bien dun ensemble de classes utilisables dans notre application et nous pourrons instancier les objets relatifs ces classes pour nos besoins. Comme ce que nous avions dj fait auparavant sans trop le savoir, avec lobjet Random par exemple. . . Nhsitez pas relire ce chapitre ainsi que le prcdent si vous navez pas parfaitement compris toutes les subtilits de la cration dobjet. Cest un point important du livre.

En rsum
Les classes possdent une mthode particulire, appele linstanciation de lobjet : le constructeur. Une instance dune classe peut tre initialise avec une valeur nulle grce au mot-cl null. Le mot-cl this reprsente lobjet en cours de la classe.

190

Chapitre

22
Dicult :

La POO et le C#

ans ce chapitre, vous allez vous immerger un peu plus dans les subtilits de la POO en utilisant le C#. Il est temps un peu de tourmenter nos objets et de voir ce quils ont dans le ventre. Ainsi, nous allons voir comment les objets hritent les uns des autres ou comment fonctionnent les dirents polymorphismes. Nous allons galement voir comment tous ces concepts se retrouvent dans le quotidien dun dveloppeur C#.

191

CHAPITRE 22. LA POO ET LE C#

Des types, des objets, type valeur et type rfrence


Ok, je sais maintenant crer des objets, mais je me rappelle quau dbut du livre, nous avons manipul des int et des string et que tu as appel a des types ; et aprs, tu nous dis que tout est objet. . . Tu serais pas en train de raconter nimporte quoi par hasard ? ! Eh bien non, perspicace lecteur ! Prcisons un peu, maintenant que vous avez de meilleures connaissances. Jai bien dit que tout tait objet, je le maintiens, mme sous la torture ! Cest--dire que mme les types simples comme les entiers int ou les chanes de caractres sont des objets. Jen veux pour preuve ce simple exemple :
1 2 3 4 5 6

int a = 10 ; string chaine = a . ToString () ; chaine = " abc " + chaine ; string chaineEnMajuscule = chaine . ToUpper () ; Console . WriteLine ( chaineEnMajuscule ) ; Console . WriteLine ( chaineEnMajuscule . Length ) ;

La variable a est un entier. Nous appelons la mthode ToString() sur cet entier. Mme si nous navons pas encore vu quoi elle servait, nous pouvons supposer quelle eectue une action qui consiste transformer lentier en chane de caractres. Nous concatnons ensuite la chane abc cette chane et nous eectuons une action qui, travers la mthode ToUpper(), met la chane en majuscule. Enn, la mthode Console.WriteLine nous ache ABC10 puis nous ache la proprit Length de la chane de caractres qui correspond bien sr sa taille. Pour crer une chane de caractres, nous utilisons le mot-cl string. Sachez que ce mot-cl est quivalent la classe String (notez la dirence de casse). En crant une chane de caractres, nous avons instanci un objet dni par la classe String. Mais alors, pourquoi utiliser string et non pas String ?

En fait, le mot-cl string est ce quon appelle un alias de la classe String qui se situe dans lespace de nom System. De mme, le mot-cl int est un alias de la structure Int32 qui se situe galement dans lespace de nom System (nous verrons un peu plus loin ce quest vraiment une structure). Ce qui fait que les instructions suivantes :
1 2

int a = 10 ; string chaine = " abc " ;

sont quivalentes celles-ci :


1

System . Int32 a = 10 ;

192

DES TYPES, DES OBJETS, TYPE VALEUR ET TYPE RFRENCE


2

System . String chaine = " abc " ;

En pratique, comme on la dj fait, on utilise plutt les alias que les classes quils reprsentent. Cependant, les entiers, les boolens et autres types simples sont ce quon appelle des types intgrs. Et mme si ce sont des objets part entire (mthodes, proprits,. . . ), ils ont des particularits, notamment dans la faon dont ils sont grs par le framework .NET. On les appelle des types valeur, car les variables de ce type possdent la vraie valeur de ce quon leur aecte a contrario des classes qui sont des types rfrence dont les variables possdent simplement un lien vers un objet en mmoire. Par exemple :
1

int entier = 5 ;

Ici, la variable contient vraiment lentier 5. Alors que pour linstanciation suivante :
1

Voiture voitureNicolas = new Voiture () ;

La variable voitureNicolas contient une rfrence vers lobjet en mmoire. On peut imaginer que le type rfrence est un peu comme si on disait que ma maison se situe au 9 rue des bois . Ladresse a t crite sur un bout de papier et rfrence ma maison qui ne se situe bien sr pas au mme endroit que le bout de papier. Si je veux vraiment voir lobjet maison, il va falloir que jaille voir o cest indiqu sur le bout de papier. Cest ce que fait le type rfrence, il va voir en mmoire ce quil y a vraiment dans lobjet. Alors que le type valeur pourrait ressembler un billet de banque par exemple. Je peux me balader avec, il est marqu 500 dessus (oui, je suis riche !) et je peux payer directement avec sans que le fait de donner le billet implique daller chercher le contenu la banque. Le type valeur contient la vraie valeur qui en gnral est assez petite et facile stocker. Le type rfrence ne contient quun lien vers un plus gros objet stock ailleurs. Cette manire dirente de grer les types et les objets implique plusieurs choses. Dans la mesure o les types valeur possdent vraiment la valeur de ce quon y stocke, une copie de la valeur est eectue chaque fois que lon fait une aectation. Cest possible car ces types sont relativement petits et optimiss. Cela savre impossible pour un objet qui est trop gros et trop complexe. Cest un peu compliqu de copier toute ma maison alors que cest un peu plus simple de recopier ce quil y a sur le bout de papier. Ainsi, lexemple suivant :
1 2 3 4

int a = 5 ; int b = a ; b = 6; Console . WriteLine ( a ) ;

193

CHAPITRE 22. LA POO ET LE C#


5

Console . WriteLine ( b ) ;

achera les valeurs 5 puis 6 ; ce qui est le rsultat que lon attend. En eet, la variable a a t initialise 5. On a ensuite aect a b. La valeur 5 sest copie (duplique) dans la variable b. Puis nous avons aect 6 b. Ce qui parat tout fait logique ! Par contre, lexemple suivant :
1 2 3 4 5 6

Voiture voitureNicolas = new Voiture () ; voitureNicolas . Couleur = " Bleue " ; Voiture voitureJeremie = voitureNicolas ; voitureJeremie . Couleur = " Verte " ; Console . WriteLine ( voitureNicolas . Couleur ) ; Console . WriteLine ( voitureJeremie . Couleur ) ;

achera verte et verte. Quoi ? Nous indiquons que la voiture de Nicolas est bleue. Puis nous disons que celle de Jrmie est verte et quand on demande dacher la couleur des deux voitures, on nous dit quelles sont vertes toutes les deux alors quon croyait que celle de Nicolas tait bleue ? Tout lheure, le fait de changer b navait pas chang la valeur de a. . . Eh oui, ceci illustre le fait que les classes (comme Voiture) sont des types rfrence et ne possdent quune rfrence vers une instance de Voiture. Quand nous aectons voitureNicolas voitureJeremie, nous disons en fait que la voiture de Jrmie rfrence la mme chose que celle de Nicolas. Concrtement, le C# copie la rfrence de lobjet Voiture qui est contenue dans la variable voitureNicolas dans la variable voitureJeremie. Ce sont donc deux variables direntes qui possdent toutes les deux une rfrence vers lobjet Voiture, qui est la voiture de Nicolas. Cest--dire que les deux variables rfrencent le mme objet. Ainsi, la modication des proprits de lun aectera forcment lautre. Inattendu au premier abord, mais nalement, cest trs logique. Comprendre cette dirence entre les types valeur et les types rfrence est important, nous verrons dans les chapitres suivants quels sont les autres impacts de cette dirence. noter galement quil est impossible de driver dun type intgr alors que cest possible, et facile, de driver dune classe. Dailleurs, si nous parlions un peu dhritage ?

Hritage
Nous avons vu pour linstant la thorie de lhritage. Que les objets chiens hritaient des comportements des objets Animaux, que les labradors hritaient des comportements des chiens, etc. 194

HRITAGE Passons maintenant la pratique et crons une classe Animal et une classe Chien qui en hrite. Nous allons crer des classes relativement courtes et nous nous limiterons dans le nombre dactions ou de proprits de celles-ci. Par exemple, nous pourrions imaginer que la classe Animal possde une proprit NombreDePattes qui est un entier et une mthode Respirer qui ache le dtail de laction. Ce qui donne :
1 2 3 4 5 6 7 8 9

public class Animal { public int NombreDePattes { get ; set ; } public void Respirer () { Console . WriteLine ( " Je respire " ) ; }

La classe Chien drive de la classe Animal et peut donc hriter de certains de ses comportements. En loccurrence, la classe Chien hritera de tout ce qui est public ou protg, identis comme vous le savez dsormais par les mots-cls public et protected. Le chien sait galement faire quelque chose qui lui est propre, savoir aboyer. Il possdera donc une mthode supplmentaire. Ce qui donne :
1 2 3 4 5 6 7

public class Chien : Animal { public void Aboyer () { Console . WriteLine ( " Wouaf ! " ) ; } }

On reprsente la notion dhritage en ajoutant aprs la classe le caractre : suivi de la classe mre. Ici, nous avons dni une classe publique Chien qui hrite de la classe Animal. Nous pouvons ds prsent crer des objets Animal et des objets Chien, par exemple :
1 2 3 4 5 6 7

Animal animal = new Animal { NombreDePattes = 4 }; animal . Respirer () ; Console . WriteLine () ; Chien chien = new Chien { NombreDePattes = 4 }; chien . Respirer () ; chien . Aboyer () ;

Si nous excutons ce code, nous nous rendons bien compte que lobjet Chien, bien que nayant pas dni la proprit NombreDePattes ou la mthode Respirer() dans le corps de sa classe, est capable davoir des pattes et de faire laction respirer :
Je respire Je respire Wouaf !

195

CHAPITRE 22. LA POO ET LE C# Il a hrit ces comportements de lobjet Animal, en tout cas, ceux qui sont publics. Rajoutons deux variables membres de la classe Animal :
1 2 3 4 5 6 7 8 9 10 11 12

public class Animal { private bool estVivant ; public int age ; public int NombreDePattes { get ; set ; } public void Respirer () { Console . WriteLine ( " Je respire " ) ; }

Lentier age est public alors que le boolen estVivant est priv. Si nous tentons de les utiliser depuis la classe lle Chien, comme ci-dessous :
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16

public class Chien : Animal { public void Aboyer () { Console . WriteLine ( " Wouaf ! " ) ; } public void Vieillir () { age ++; } public void Naissance () { age = 0 ; estVivant = true ; /* Erreur > ' MaPr emier eAppli catio n . Animal . estVivant ' est inaccessible en raison de son niveau de protection */ }

17 18

nous voyons quil est tout fait possible dutiliser la variable age depuis la mthode Vieillir() alors que lutilisation du boolen estVivant provoque une erreur de compilation. Vous avez bien compris que celui-ci tait inaccessible car il est dni comme membre priv. Pour lutiliser, on pourra le rendre public par exemple. Il existe par contre un autre mot-cl qui permet de rendre des variables, proprits ou mthodes inaccessibles depuis un autre objet tout en les rendant accessibles depuis des classes lles. Il sagit du mot-cl protected. Si nous lutilisons la place de private pour dnir la visibilit du boolen estVivant, nous pourrons nous rendre compte que la classe Chien peut dsormais compiler : 196

HRITAGE
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16

public class Animal { protected bool estVivant ; [... Extrait de code supprim ...] } public class Chien : Animal { [... Extrait de code supprim ...] public void Naissance () { age = 0 ; estVivant = true ; // compilation OK }

Par contre, cette variable est toujours inaccessible depuis dautres classes, comme lest galement une variable prive. Dans notre classe Program, linstruction suivante :
1

chien . estVivant = true ;

provoquera lerreur de compilation que dsormais nous connaissons bien :


Ma P r e m i e r e A p p licati on . Animal . estVivant est inaccessible en raison de son niveau de protection

Le mot-cl protected prend tout son intrt ds que nous avons faire avec lhritage. Nous verrons un peu plus loin dautres exemples de ce mot-cl. Nous avons dit dans lintroduction quun objet B qui drive de lobjet A est une sorte dobjet A. Dans notre exemple du dessus, le Chien est une sorte dAnimal. Cela veut dire que nous pouvons utiliser un chien en tant quanimal. Par exemple, le code suivant :
1

Animal animal = new Chien { NombreDePattes = 4 };

est tout fait correct. Nous disons que notre variable animal, de type Animal est une instance de Chien. Avec cette faon dcrire, nous avons rellement instanci un objet Chien mais celuici sera trait en tant quAnimal. Cela veut dire quil sera capable de Respirer() et davoir des pattes. Par contre, mme si en vrai, notre objet est capable daboyer, le fait quil soit manipul en tant quAnimal nous empche de pouvoir le faire Aboyer. Cela veut dire que le code suivant :
1 2 3

Animal animal = new Chien { NombreDePattes = 4 }; animal . Respirer () ; animal . Aboyer () ; // erreur de compilation

provoquera une erreur de compilation pour indiquer que la classe Animal ne contient aucune dnition pour la mthode Aboyer(). Ce qui est normal, car un animal ne sait pas forcment aboyer. . . 197

CHAPITRE 22. LA POO ET LE C#

Quel est lintrt alors dutiliser le chien en tant quanimal ?

Bonne question. Pour y rpondre, nous allons enrichir notre classe Animal, garder notre classe Chien et crer une classe Chat qui hrite galement dAnimal. Ce pourrait tre :
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35

public class Animal { protected string prenom ; public void Respirer () { Console . WriteLine ( " Je suis " + prenom + " et je respire "); }

public class Chien : Animal { public Chien ( string prenomDuChien ) { prenom = prenomDuChien ; } public void Aboyer () { Console . WriteLine ( " Wouaf ! " ) ; }

public class Chat : Animal { public Chat ( string prenomDuChat ) { prenom = prenomDuChat ; } public void Miauler () { Console . WriteLine ( " Miaou " ) ; }

Nous forons les chiens et les chats avoir un nom, hrit de la classe Animal, grce au constructeur an de pouvoir les identier facilement. Le chat garde le mme principe que le chien, sauf que nous avons une mthode Miauler() la place de la mthode Aboyer(). . . Ce qui est, somme toute, logique ! Lide est de pouvoir utiliser nos chiens et nos chats ensemble comme des animaux, par exemple en utilisant une liste. 198

HRITAGE Pour illustrer ce fonctionnement, donnons vie quelques chiens et quelques chats grce nos pouvoirs de dveloppeur et mettons-les dans une liste :
1 2 3 4 5 6 7 8 9 10 11 12

List < Animal > animaux = new List < Animal >() ; Animal milou = new Chien ( " Milou " ) ; Animal dingo = new Chien ( " Dingo " ) ; Animal idefix = new Chien ( " Id fix " ) ; Animal tom = new Chat ( " Tom " ) ; Animal felix = new Chat ( " F lix " ) ; animaux . Add ( milou ) ; animaux . Add ( dingo ) ; animaux . Add ( idefix ) ; animaux . Add ( tom ) ; animaux . Add ( felix ) ;

Nous avons dans un premier temps instanci une liste danimaux laquelle nous avons rajout 3 chiens et 2 chats, chacun tant considr comme un animal puisquils sont tous des sortes danimaux, grce lhritage. Maintenant, nous navons plus que des animaux dans la liste. Il sera donc possible de les faire tous respirer en une simple boucle :
1 2 3 4

foreach ( Animal animal in animaux ) { animal . Respirer () ; }

Ce qui donne :
Je Je Je Je Je suis suis suis suis suis Milou et je respire Dingo et je respire Id fix et je respire Tom et je respire F lix et je respire

Et voil, cest super simple ! Imaginez le bonheur de No sur son arche quand il a compris que grce la POO, il pouvait faire respirer tous les animaux en une seule boucle ! Quel travail conomis. Peu importe ce quil y a dans la liste, des chiens, des chats, des hamsters, nous savons que ce sont tous des animaux et quils savent tous respirer. Vous avez sans doute remarqu que nous faisons la mme chose dans le constructeur de la classe Chien et dans celui de la classe Chat. Deux fois la mme chose. . . Ce nest pas terrible. Peut-tre y a-t-il un moyen de factoriser tout a ? Eectivement, il est possible galement dcrire nos classes de cette faon :
1 2 3 4 5

public class Animal { protected string prenom ; public Animal ( string prenomAnimal )

199

CHAPITRE 22. LA POO ET LE C#


6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38

{ }

prenom = prenomAnimal ;

public void Respirer () { Console . WriteLine ( " Je suis " + prenom + " et je respire "); }

public class Chien : Animal { public Chien ( string prenomDuChien ) : base ( prenomDuChien ) { } public void Aboyer () { Console . WriteLine ( " Wouaf ! " ) ; }

public class Chat : Animal { public Chat ( string prenomDuChat ) : base ( prenomDuChat ) { } public void Miauler () { Console . WriteLine ( " Miaou " ) ; }

Quest-ce qui change ? Eh bien la classe Animal possde un constructeur qui prend en paramtre un prnom et qui le stocke dans sa variable prive. Cest elle qui fait le travail dinitialisation. Il devient alors possible pour les constructeurs des classes lles dappeler le constructeur de la classe mre an de faire laectation du prnom. Pour cela, on utilise les deux points suivis du mot-cl base qui signie appelle-moi le constructeur de la classe du dessus auquel nous passons la variable en paramtre. Avec cette criture un peu barbare, il devient possible de factoriser des initialisations qui ont un sens pour toutes les classes lles. Dans notre cas, je veux que tous les objets qui drivent dAnimal puissent facilement dnir un prnom. Il faut aussi savoir que si nous appelons le constructeur par dfaut dune classe qui nappelle pas explicitement un constructeur spcialis dune classe mre, alors celuici appellera automatiquement le constructeur par dfaut de la classe dont il hrite. 200

HRITAGE Modions nouveau nos classes pour avoir :


1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35

public class Animal { protected string prenom ; public Animal () { prenom = " Marcel " ; } public void Respirer () { Console . WriteLine ( " Je suis " + prenom + " et je respire "); }

public class Chien : Animal { public void Aboyer () { Console . WriteLine ( " Wouaf ! " ) ; } } public class Chat : Animal { public Chat ( string prenomDuChat ) { prenom = prenomDuChat ; } public void Miauler () { Console . WriteLine ( " Miaou " ) ; }

Ici, la classe Animal met un prnom par dfaut dans son constructeur. Le chien na pas de constructeur et le chat en a un qui accepte un paramtre. Il est donc possible de crer un Chien sans quil ait de prnom mais il est obligatoire den dnir un pour le chat. Sauf que lorsque nous instancierons notre objet chien, il appellera automatiquement le constructeur de la classe mre et tous nos chiens sappelleront Marcel :
1 2 3 4 5

static void Main ( string [] args ) { List < Animal > animaux = new List < Animal >() ; Animal chien = new Chien () ; Animal tom = new Chat ( " Tom " ) ;

201

CHAPITRE 22. LA POO ET LE C#


6 7 8 9 10 11 12 13 14 15 16

Animal felix = new Chat ( " F lix " ) ; animaux . Add ( chien ) ; animaux . Add ( tom ) ; animaux . Add ( felix ) ; foreach ( Animal animal in animaux ) { animal . Respirer () ; }

Ce qui achera :
Je suis Marcel et je respire Je suis Tom et je respire Je suis F lix et je respire

Il est galement possible dappeler un constructeur partir dun autre constructeur. Prenons lexemple suivant :
1 2 3 4 5 6 7 8 9

public class Voiture { private int vitesse ; public Voiture ( int vitesseVoiture ) { vitesse = vitesseVoiture ; }

Si nous souhaitons rajouter un constructeur par dfaut qui initialise la vitesse 10 par exemple, nous pourrons faire :
1 2 3 4 5 6 7 8 9 10 11 12 13 14

public class Voiture { private int vitesse ; public Voiture () { vitesse = 10 ; } public Voiture ( int vitesseVoiture ) { vitesse = vitesseVoiture ; }

Ou encore : 202

HRITAGE
1 2 3 4 5 6 7 8 9 10 11 12 13

public class Voiture { private int vitesse ; public Voiture () : this ( 10 ) { } public Voiture ( int vitesseVoiture ) { vitesse = vitesseVoiture ; }

Ici, lutilisation du mot-cl this, suivi dun entier permet dappeler le constructeur qui possde un paramtre entier au dbut du constructeur par dfaut. Inversement, nous pouvons appeler le constructeur par dfaut dune classe depuis un constructeur possdant des paramtres an de pouvoir bncier des initialisations de celui-ci :
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15

public class Voiture { private int vitesse ; private string couleur ; public Voiture () { vitesse = 10 ; } public Voiture ( string couleurVoiture ) : this () { couleur = couleurVoiture ; }

Puisque nous parlons dhritage, il faut savoir que tous les objets que nous crons ou qui sont disponibles dans le framework .NET hritent dun objet de base. On parle en gnral dun super-objet . Lintrt de driver dun tel objet est de permettre tous les objets davoir certains comportements en commun, mais galement de pouvoir ventuellement tous les traiter en tant quobjet. Notre super-objet est reprsent par la classe Object qui dnit plusieurs mthodes. Vous les avez dj vues si vous avez regard dans la compltion automatique aprs avoir cr un objet. Prenons une classe toute vide, par exemple :
1 2 3

public class ObjetVide { }

Si nous instancions cet objet et que nous souhaitons lutiliser, nous verrons que la compltion automatique nous propose des mthodes que nous navons jamais cres (voir gure 22.1). 203

CHAPITRE 22. LA POO ET LE C#

Figure 22.1 La compltion automatique montre des mthodes de la classe de base Object Nous voyons plusieurs mthodes, comme Equals ou GetHashCode ou GetType ou encore ToString. Comme vous lavez compris, ce sont des mthodes qui sont dnies dans la classe Object. La mthode ToString par exemple permet dobtenir une reprsentation de lobjet sous la forme dune chane de caractres. Cest une mthode qui va souvent nous servir, nous y reviendrons un peu plus tard. Ce super-objet est du type Object, mais on utilise gnralement son alias object. Ainsi, il est possible dutiliser tous nos objets comme des object et ainsi utiliser les mthodes qui sont dnies sur la classe Object. Ce qui nous permet de faire :
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17

static void Main ( string [] args ) { ObjetVide monObjetVide = new ObjetVide () ; Chien chien = new Chien () ; int age = 30 ; string prenom = " Nicolas " ; A f f i c he rRe pr ese nt ati on ( monObjetVide ) ; A f f i c he rRe pr ese nt ati on ( chien ) ; A f f i c he rRe pr ese nt ati on ( age ) ; A f f i c he rRe pr ese nt ati on ( prenom ) ;

private static void A ffi ch erR ep res en tat ion ( object monObjetVide ) { Console . WriteLine ( monObjetVide . ToString () ) ; }

Ce qui ache :
M a P r e m i e r e Appli catio n . ObjetVide M a P r e m i e r e Appli catio n . Chien 30 Nicolas

204

HRITAGE Comme indiqu, la mthode ToString() permet dacher la reprsentation par dfaut dun objet. Vous aurez remarqu quil y a une dirence suivant ce que nous passons. En eet, la reprsentation par dfaut des types rfrence correspond au nom du type, savoir son espace de nom suivi du nom de sa classe. Pour ce qui est des types valeur, la reprsentation contient en gnral la valeur du type, lexception des structures que nous navons pas encore vues et que nous aborderons un peu plus loin. Lintrt dans cet exemple de code est de voir que nous pouvons tout manipuler comme un object. Dune manire gnrale, vous aurez peu loccasion de traiter vos objets en tant quobject car il est vraiment plus intressant de proter pleinement du type, lobject tant peu utilisable. Notez que lhritage de object est automatique. Nul besoin dutiliser la syntaxe dhritage que nous avons dj vue. Jen prote maintenant que vous connaissez la mthode ToString() pour parler dun point qui a peut-tre titill vos cerveaux. Dans la premire partie, nous avions fait quelque chose du genre :
1 2

int vitesse = 20 ; string chaine = " La vitesse est " + vitesse + " km / h " ;

La variable vitesse est un entier. La chane La vitesse est est une chane de caractres. Nous essayons dajouter un entier une chane alors que jai dit quils ntaient pas compatibles entre eux ! Et pourtant cela fonctionne. Eectivement, cest bizarre ! Nous concatnons une chane un entier avec loprateur + et nous concatnons encore une chane. Et si je fais linverse :
1

int vitesse = 20 + " 40 " ;

cela provoque une erreur de compilation. Cest logique, on ne peut pas ajouter un entier et une chane de caractres. Alors pourquoi cela fonctionne dans lautre sens ? Ce qui se passe en fait dans linstruction :
1

string chaine = " La vitesse est " + vitesse + " km / h " ;

cest que le compilateur se rend compte que nous concatnons une chane avec un autre objet, peu importe que ce soit un entier ou un objet complexe. Alors, pour que a fonctionne, il demande une reprsentation de lobjet sous la forme dune chane de caractres. Nous avons vu que ceci se faisait en appelant la mthode ToString() qui est hrite de lobjet racine Object. Linstruction est donc quivalente :
1

string chaine = " La vitesse est " + vitesse . ToString () + " km / h ";

205

CHAPITRE 22. LA POO ET LE C# Dans le cas dun type valeur comme un entier, la mthode ToString() renvoie la reprsentation interne de la valeur, savoir 20 . Dans le cas dun objet complexe, elle aurait renvoy le nom du type de lobjet. Avant de terminer, il est important dindiquer que le C# nautorise pas lhritage multiple. Ainsi, si nous possdons une classe Carnivore et une classe EtreVivant, il ne sera pas possible de faire hriter directement un objet Homme de lobjet Carnivore et de lobjet EtreVivant. Ainsi, le code suivant :
1 2 3 4 5 6 7 8 9 10

public class Carnivore { } public class EtreVivant { } public class Homme : Carnivore , EtreVivant { }

provoquera lerreur de compilation suivante :


La classe M aPrem iereAp plica tion . Homme ne peut pas avoir plusieurs classes de base : MaPre miere Applic ation . Carnivore et EtreVivant

Il est impossible de driver de deux objets en mme temps.

En revanche, et cest pertinent, nous pourrons faire un hritage en cascade an que Carnivore drive de EtreVivant et que Homme drive de Carnivore :
1 2 3 4 5 6 7 8 9 10

public class Carnivore : EtreVivant { } public class EtreVivant { } public class Homme : Carnivore { }

Cependant, il nest pas toujours pertinent doprer de la sorte. Notre Homme pourrait tre la fois Carnivore et Frugivore, cependant cela na pas de sens quun carnivore soit galement frugivore, ou linverse. 206

SUBSTITUTION Tu avais pourtant dit que chaque objet drivait du super-objet Object, mais sil drive dune autre classe comme un chien drive dun animal, a fait bien deux classes dont il drive. . . Eectivement, mais dans ce cas-l, ce nest pas pareil. Comme il est automatique de driver de object, cest comme si on avait le chien qui hrite de animal qui hrite lui-mme de object. Le C# est assez malin pour a !

Substitution
Nous avons vu juste avant lutilisation de la mthode ToString() qui permet dobtenir la reprsentation dun objet sous forme de chane de caractres. En loccurrence, vous conviendrez avec moi que la reprsentation de notre classe Chien nest pas particulirement exploitable. Le nom du type cest bien, mais ce nest pas trs parlant. a serait pas mal que, quand nous demandons dacher un chien, nous obtenions le nom du chien, vous ne trouvez pas ? Cest l quintervient la substitution. Nous en avons parl dans lintroduction la POO, la substitution permet de rednir un comportement dont lobjet a hrit an quil corresponde aux besoins de lobjet ls. Typiquement, ici, la mthode ToString() du super-objet ne nous convient pas et dans le cas de notre chien, nous souhaitons la rednir, en crire une nouvelle version. Pour cet exemple, simplions notre classe Chien an quelle nait quune proprit pour stocker son prnom :
1 2 3 4

public class Chien { public string Prenom { get ; set ; } }

Pour rednir la mthode ToString() nous allons devoir utiliser le mot-cl override qui signie que nous souhaitons substituer la mthode existante an de remplacer son comportement, ce que nous pourrons crire en C# avec :
1 2 3 4 5 6 7 8 9

public class Chien { public string Prenom { get ; set ; } public override string ToString () { return " Je suis un chien et je m ' appelle " + Prenom ; }

Le mot-cl override se met avant le type de retour de la mthode, comme on peut le voir ci-dessus. Si nous appelons dsormais la mthode ToString de notre objet Chien :
1 2

Chien chien = new Chien { Prenom = " Max " }; Console . WriteLine ( chien . ToString () ) ;

207

CHAPITRE 22. LA POO ET LE C# notre programme va utiliser la nouvelle version de la mthode ToString() :
Je suis un chien et je m appelle Max

Et voil un bon moyen dutiliser la substitution, la reprsentation de notre objet est quand mme plus parlante ! Adaptons dsormais cet exemple nos classes. Pour montrer comment faire, reprenons notre classe Chien qui possde une mthode Aboyer() :
1 2 3 4 5 6 7

public class Chien { public void Aboyer () { Console . WriteLine ( " Wouaf ! " ) ; } }

Nous pourrions imaginer de crer une classe ChienMuet qui drive de la classe Chien et qui hrite donc de ses comportements. Mais, que penser dun chien muet qui serait capable daboyer ? Cela na pas de sens ! Il faut donc rednir cette chue mthode. Utilisons alors le mot-cl override comme nous lavons vu pour obtenir :
1 2 3 4 5 6 7

public class ChienMuet : Chien { public override void Aboyer () { Console . WriteLine ( " ... " ) ; } }

Crons un chien muet puis faisons-le aboyer, cela donne :


1 2

ChienMuet pauvreChien = new ChienMuet () ; pauvreChien . Aboyer () ;

Sauf que nous rencontrons un problme. Si nous tentons de compiler ce code, Visual C# Express nous gnre une erreur de compilation :
M a P r e m i e r eAppl icati on . Program . ChienMuet . Aboyer () : ne peut pas substituer le membre h rit MaPr emier eAppli catio n . Program . Chien . Aboyer () , car il n est pas marqu comme virtual , abstract ou override .

En ralit, pour pouvoir crer une mthode qui remplace une autre, il faut quune condition supplmentaire soit vrie : il faut que la mthode remplacer sannonce comme candidate la substitution. Cela veut dire que lon ne peut pas substituer nimporte quelle mthode, mais seulement celles qui acceptent de ltre. Cest le cas pour la mthode ToString que nous avons vue prcdemment. Les concepteurs du framework .NET ont autoris cette ventualit. Heureusement, sinon, nous serions bien embts ! 208

SUBSTITUTION Pour marquer notre mthode Aboyer de la classe Chien comme candidate ventuelle la substitution, il faut la prxer du mot-cl virtual. Ainsi, elle annonce ses futures lles que si elles le souhaitent, elles peuvent rednir cette mthode. Cela se traduit ainsi dans le code :
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15

public class Chien { public virtual void Aboyer () { Console . WriteLine ( " Wouaf ! " ) ; } } public class ChienMuet : Chien { public override void Aboyer () { Console . WriteLine ( " ... " ) ; } }

Dsormais, linstanciation de lobjet est possible et nous pourrons avoir notre code :
1 2

ChienMuet pauvreChien = new ChienMuet () ; pauvreChien . Aboyer () ;

Ce code achera :
...

Parfait ! Tout est rentr dans lordre. Le message derreur, quoique peu explicite, nous mettait quand mme sur la bonne voie. Visual C# Express nous disait quil fallait que la mthode soit marque comme virtual, ce que nous avons fait. Il proposait galement quelle soit marque abstract, nous verrons un peu plus loin ce que a veut dire. Visual C# Express indiquait enn que la mthode pouvait tre marque override. Cela veut dire quune classe lle de ChienMuet peut galement rednir la mthode Aboyer() an quelle colle ses besoins. Elle nest pas marque virtual mais elle est marque override. Par exemple :
1 2 3 4 5 6 7

public class C h i e n M u e t A v e c S y n t h e s e V o c a l e : ChienMuet { public override void Aboyer () { Console . WriteLine ( " bwarf ! " ) ; } }

Il y a encore un dernier point que nous navons pas abord. Il sagit de la capacit pour une classe lle de rednir une mthode tout en conservant la fonctionnalit de 209

CHAPITRE 22. LA POO ET LE C# la mthode de la classe mre. Imaginons notre classe Animal qui possde une mthode Manger() :
1 2 3 4 5 6 7 8 9 10

public class Animal { public virtual void Manger () { Console . WriteLine ( " Mettre les aliments dans la bouche " ) ; Console . WriteLine ( " Mastiquer " ) ; Console . WriteLine ( " Avaler " ) ; Console . WriteLine ( " ... " ) ; } }

Notre classe Chien pourra sappuyer sur le comportement de la mthode Manger() de la classe Animal pour crer sa propre action personnelle. Cela se passe en utilisant nouveau le mot-cl base qui reprsente la classe mre. Nous pourrons par exemple appeler la mthode Manger() de la classe mre an de rutiliser son fonctionnement. Cela donne :
1 2 3 4 5 6 7 8 9

public class Chien : Animal { public override void Manger () { Console . WriteLine ( " R clamer manger au ma tre " ) ; base . Manger () ; Console . WriteLine ( " Remuer la queue " ) ; } }

Dans cet exemple, je fais quelque chose avant dappeler la mthode de la classe mre, puis je fais quelque chose dautre aprs. Maintenant, si nous faisons manger notre chien :
1 2

Chien chien = new Chien () ; chien . Manger () ;

sachera dans la console toute la srie des actions dnies comme tant manger :
R clamer manger au ma tre Mettre les aliments dans la bouche Mastiquer Avaler ... Remuer la queue

Nous voyons bien avec cet exemple comment la classe lle peut rutiliser les mthodes de sa classe mre. noter quon peut galement parler de spcialisation ou de rednition la place de la substitution. 210

POLYMORPHISME

Polymorphisme
Nous avons dit quune manifestation du polymorphisme tait la capacit pour une classe deectuer la mme action sur dirents types dintervenants. Il sagit de la surcharge, appele aussi polymorphisme ad hoc. Concrtement, cela veut dire quil est possible de dnir la mme mthode avec des paramtres en entre dirents. Si vous vous rappelez bien, cest quelque chose que nous avons dj fait sans le savoir. Devinez . . . Oui, cest a, avec la mthode Console.WriteLine. Nous avons pu acher des chanes de caractres, mais aussi des entiers, mme des types double, et plus rcemment des objets. Comment ceci est possible alors que nous avons dj vu quil tait impossible de passer des types en paramtres dune mthode qui ne correspondent pas sa signature ? ! Ainsi, lexemple suivant :
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22

public class Program { static void Main ( string [] args ) { Math math = new Math () ; int a = 5 ; int b = 6 ; int resultat = math . Addition (a , b ) ; double c = 1 . 5 ; double d = 5 . 0 ; resultat = math . Addition (c , d ) ; /* erreur de compilation */

public class Math { public int Addition ( int a , int b ) { return a + b ; } }

provoquera une erreur de compilation lorsque nous allons essayer de passer des variables du type double notre mthode qui prend des entiers en paramtres. Pour que ceci fonctionne, nous allons rendre polymorphe cette mthode en dnissant nouveau cette mme mthode mais en lui faisant prendre des paramtres dentre dirents :
1 2 3 4 5

public class Program { static void Main ( string [] args ) { Math math = new Math () ;

211

CHAPITRE 22. LA POO ET LE C#


6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27

int a = 5 ; int b = 6 ; int resultat = math . Addition (a , b ) ; double c = 1 . 5 ; double d = 5 . 0 ; double resultatDouble = math . Addition (c , d ) ; /* a compile , youpi */

public class Math { public int Addition ( int a , int b ) { return a + b ; } public double Addition ( double a , double b ) { return a + b ; }

Nous avons ainsi crit deux formes direntes de la mme mthode. Une qui accepte des entiers et lautre qui accepte des double. Ce code fonctionne dsormais correctement. Il est bien sr possible dcrire cette mthode avec beaucoup de paramtres de types dirents, mme une classe Chien, en imaginant que le fait dadditionner deux chiens correspond au fait dadditionner leurs nombres de pattes :
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17

public class Math { public int Addition ( int a , int b ) { return a + b ; } public double Addition ( double a , double b ) { return a + b ; } public int Addition ( Chien c1 , Chien c2 ) { return c1 . NombreDePattes + c2 . NombreDePattes ; }

Attention, jai toujours indiqu quil tait possible dajouter une nouvelle forme la mme mthode en changeant les paramtres dentres. Vous ne pourrez pas le faire en 212

POLYMORPHISME changeant uniquement le paramtre de retour. Ce qui fait que cet exemple ne pourra pas compiler :
1 2 3 4 5 6 7 8 9 10 11 12

public class Math { public int Addition ( int a , int b ) { return a + b ; } public double Addition ( int a , int b ) { return a + b ; }

Les deux mthodes acceptent deux entiers en paramtres et renvoient soit un entier, soit un double. Le compilateur ne sera pas capable de choisir quelle mthode utiliser lorsque nous essayerons dappeler cette mthode. Les mthodes doivent se direncier avec les paramtres dentres. Lorsque nous avons plusieurs signatures possibles pour la mme mthode, vous remarquerez que la compltion automatique nous propose alors plusieurs possibilits (voir gure 22.2).

Figure 22.2 La compltion automatique propose plusieurs signatures de la mme mthode Visual C# indique quil a trois mthodes possibles qui sappellent Math.Addition. Pour voir la signature des autres mthodes, il sut de cliquer sur les ches, ou dutiliser les ches du clavier, comme indiqu la gure 22.3. Cest ce qui se passe dans la mthode Console.WriteLine (voir gure 22.4). Nous voyons ici quil existe 19 critures de la mthode WriteLine, la cinquime prenant en paramtre un dcimal. Notez que pour crire plusieurs formes de cette mthode, nous pouvons galement jouer sur le nombre de paramtres. La mthode :
1 2 3 4

public int Addition ( int a , int b , int c ) { return a + b + c ; }

213

CHAPITRE 22. LA POO ET LE C#

Figure 22.3 La compltion automatique prsente plusieurs signatures de la mthode Addition

Figure 22.4 La compltion automatique prsente plusieurs signatures de la mthode Console.WriteLine sera bien une nouvelle forme de la mthode Addition. Nous avons galement vu dans le chapitre sur les constructeurs dune classe quil tait possible de cumuler les constructeurs avec des paramtres dirents. Il sagit nouveau du polymorphisme. Il nous permet de dnir dirents constructeurs sur nos objets.

La conversion entre les objets avec le casting


Nous avons dj vu dans la partie prcdente quil tait possible de convertir les types qui se ressemblent entre eux. Cela fonctionne galement avec les objets. Plus prcisment, cela veut dire que nous pouvons convertir un objet en un autre seulement sil est une sorte de lautre objet. Nous avons vu dans les chapitres prcdents quil sagissait de la notion dhritage. Ainsi, si nous avons dni une classe Animal et que nous dnissons une classe Chien qui hrite de cette classe Animal :
1 2 3 4 5 6 7

public class Animal { } public class Chien : Animal { }

nous pourrons alors convertir le chien en animal dans la mesure o le chien est une 214

LA CONVERSION ENTRE LES OBJETS AVEC LE CASTING sorte danimal :


1 2

Chien medor = new Chien () ; Animal animal = ( Animal ) medor ;

Nous utilisons pour ce faire un cast, comme nous lavons dj fait pour les types intgrs (int, bool, etc.). Il sut de prxer la variable convertir du type entre parenthses dans lequel nous souhaitons le convertir. Ici, nous pouvons convertir facilement notre Chien en Animal. Par contre, il est impossible de convertir un chien en voiture, car il ny a pas de relation dhritage entre les deux. Ainsi les instructions suivantes :
1 2

Chien medor = new Chien () ; Voiture voiture = ( Voiture ) medor ;

provoqueront une erreur de compilation. Nous avons prcdemment utilis lhritage an de mettre des chiens et des chats dans une liste danimaux. Nous avions fait quelque chose du genre :
1 2 3 4 5 6

List < Animal > animaux = new List < Animal >() ; Animal chien = new Chien () ; Animal chat = new Chat () ; animaux . Add ( chien ) ; animaux . Add ( chat ) ;

Il serait plus logique en fait dcrire les instructions suivantes :


1 2 3 4 5 6

List < Animal > animaux = new List < Animal >() ; Chien chien = new Chien () ; Chat chat = new Chat () ; animaux . Add (( Animal ) chien ) ; animaux . Add (( Animal ) chat ) ;

Dans ce cas, nous crons un objet Chien et un objet Chat que nous mettons dans une liste dobjets Animal grce une conversion utilisant un cast. En fait, ce cast est inutile et nous pouvons simplement crire :
1 2 3 4 5 6

List < Animal > animaux = new List < Animal >() ; Chien chien = new Chien () ; Chat chat = new Chat () ; animaux . Add ( chien ) ; animaux . Add ( chat ) ;

La conversion est implicite, comme lorsque nous avions utilis un object en paramtres dune mthode et que nous pouvions lui passer tous les types qui drivent dobject. Nous avions galement vu que nous pouvions traiter les chiens et les chats comme des animaux partir du moment o nous les mettions dans une liste. Avec les objets suivants : 215

CHAPITRE 22. LA POO ET LE C#


1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23

public class Animal { public void Respirer () { Console . WriteLine ( " Je respire " ) ; } } public class Chien : Animal { public void Aboyer () { Console . WriteLine ( " Waouf " ) ; } } public class Chat : Animal { public void Miauler () { Console . WriteLine ( " Miaou " ) ; } }

Nous pouvions utiliser une boucle pour faire respirer tous nos animaux :
1 2 3 4 5 6 7 8 9 10 11

List < Animal > animaux = new List < Animal >() ; Chien chien = new Chien () ; Chat chat = new Chat () ; animaux . Add ( chien ) ; animaux . Add ( chat ) ; foreach ( Animal animal in animaux ) { animal . Respirer () ; }

Mais impossible de faire aboyer le chien, ni miauler le chat. Si vous tentez de remplacer dans la boucle Animal par Chien, avec :
1 2 3 4

foreach ( Chien c in animaux ) { c . Aboyer () ; }

Vous pourrez faire aboyer le premier lment de la liste qui est eectivement un chien, par contre il y aura un plantage au deuxime lment de la liste car il sagit dun chat :
Waouf

216

LA CONVERSION ENTRE LES OBJETS AVEC LE CASTING


Exception non g r e : System . InvalidC astException : Impossible d effectuer un cast d un objet de type Ma Premie reApp licati on . Chat en type Ma Premie reApp licati on . Chien . M a P r e m i e r e A p plica tion . Program . Main ( String [] args ) dans C :\ Users \ Nico \ Documents \ Visual Studio 2010\ Projects \ C #\ M a P r e m i e r e Appl icatio n \ MaP remier eAppli catio n \ Program . cs : ligne 19

Lorsque notre programme a tent de convertir un animal qui est un chat en chien, il nous a fait comprendre quil napprciait que moyennement. Les chiens naiment pas trop les chats dune manire gnrale, alors en plus, un chat qui essaie de se faire passer pour un chien : cest une dclaration de guerre ! Voil pourquoi notre programme a lev une exception. Il lui tait impossible de convertir un Chat en Chien. Il est cependant possible de tester si une variable correspond un objet grce au motcl is. Ce qui nous permettra de faire la conversion adquate et de nous viter une erreur lexcution :
1 2 3 4 5 6 7 8 9 10 11 12 13

foreach ( Animal animal in animaux ) { if ( animal is Chien ) { Chien c = ( Chien ) animal ; c . Aboyer () ; } if ( animal is Chat ) { Chat c = ( Chat ) animal ; c . Miauler () ; } }

Nous testons avec le mot-cl is si lanimal est une instance dun chien ou dun chat. Le code du dessus nous permettra dutiliser dans la boucle lanimal courant comme un chien ou un chat en fonction de ce quil est vraiment, grce au test :
Waouf Miaou

Le fait de tester ce quest vraiment lanimal avant de le convertir est une scurit indispensable pour viter ce genre derreur. Cest linconvnient du cast explicite. Il convient trs bien si nous sommes certains du type dans lequel nous souhaitons en convertir un autre. Par contre, si la conversion nest pas possible, alors nous aurons une erreur. Lorsque nous ne sommes pas certains du rsultat du cast, mieux vaut tester si linstance dun objet correspond bien lobjet lui-mme. Cela peut se faire comme nous lavons vu avec le mot-cl is, mais galement avec un autre cast qui sappelle le cast dynamique. Il se fait en employant le mot-cl as. Ce cast dynamique vrie que lobjet est bien convertible. Si cest le cas, alors il fait un 217

CHAPITRE 22. LA POO ET LE C# cast explicite pour renvoyer le rsultat de la conversion, sinon, il renvoie une rfrence nulle. Le code du dessus peut donc scrire :
1 2 3 4 5 6 7 8 9 10 11 12 13

foreach ( Animal animal in animaux ) { Chien c1 = animal as Chien ; if ( c1 != null ) { c1 . Aboyer () ; } Chat c2 = animal as Chat ; if ( c2 != null ) { c2 . Miauler () ; } }

On utilise le mot-cl as en le faisant prcder de la valeur tenter de convertir et en le faisant suivre du type dans lequel nous souhaitons la convertir. Fonctionnellement, nous faisons la mme chose dans les deux codes. Vous pouvez choisir lcriture que vous prfrez, mais sachez que cest ce dernier qui est en gnral utilis, car il est prconis par Microsoft. De plus, il est un tout petit peu plus performant. Un petit dtail encore. Il est possible de convertir un type valeur, comme un int ou un string en type rfrence en utilisant ce quon appelle le boxing. Rien voir avec le fait de taper sur les types valeur ! Comme nous lavons vu, les types valeur et les types rfrence sont grs diremment par .NET. Aussi, si nous convertissons un type valeur en type rfrence, .NET fait une opration spciale automatiquement. Ainsi le code suivant :
1 2

int i = 5 ; object o = i ; // boxing

eectue un boxing automatique de lentier en type rfrence. Cest ce boxing automatique qui nous permet de manipuler les types valeur comme des object. Cest aussi ce qui nous a permis plus haut de passer un entier en paramtre une mthode qui acceptait un object. En interne, ce qui se passe cest que object se voit attribuer une rfrence vers une copie de la valeur de i. Ainsi, modier o ne modiera pas i. Ce code :
1 2 3 4 5

int i = 5 ; object o = i ; // boxing o = 6; Console . WriteLine ( i ) ; Console . WriteLine ( o ) ;

ache 5 puis 6. Le contraire est galement possible, ce quon appelle lunboxing. Seulement, celui-ci a besoin dun cast explicite an de pouvoir compiler. Cest--dire :
1 2 3

int i = 5 ; object o = i ; // boxing int j = ( int ) o ; // unboxing

218

LA CONVERSION ENTRE LES OBJETS AVEC LE CASTING ici nous reconvertissons la rfrence vers la valeur de o en entier et nous eectuons nouveau une copie de cette valeur pour la mettre dans j. Ainsi le code suivant :
1 2 3 4 5 6 7 8 9

int i = 5 ; object o = i ; // boxing o = 6; int j = ( int ) o ; // unboxing j = 7; Console . WriteLine ( i ) ; Console . WriteLine ( o ) ; Console . WriteLine ( j ) ;

achera en toute logique 5 puis 6 puis 7. noter que ces oprations sont consommatrices de temps, elles sont donc faire le moins possible.

En rsum
Les objets peuvent tre des types valeur ou des types rfrence. Les variables de type valeur possdent la valeur de lobjet, comme un entier. Les variables de type rfrence possdent une rfrence vers lobjet en mmoire. Tous les objets drivent de la classe de base Object. On peut substituer une mthode grce au mot-cl override si elle sest dclare candidate la substitution grce au mot-cl virtual. La surcharge est le polymorphisme permettant de faire varier les types des paramtres dune mme mthode ou leurs nombres. Il est possible grce au cast de convertir un type en un autre type, sils ont une relation dhritage.

219

CHAPITRE 22. LA POO ET LE C#

220

Chapitre

23
Dicult :

Notions avances de POO en C#

ans ce chapitre, nous allons continuer dcouvrir comment nous pouvons faire de lorient objet avec le C#. Nous allons pousser un peu plus loin en dcouvrant les interfaces et en manipulant les classes statiques et abstraites.

la n de ce chapitre, vous serez capables de faire des objets encore plus volus et vous devriez tre capables de crer un vrai petit programme orient objet. . . !

221

CHAPITRE 23. NOTIONS AVANCES DE POO EN C#

Comparer des objets


Nous avons vu dans la premire partie quil tait possible de comparer facilement des types valeur grce aux oprateurs de comparaison. En eet, vu que des variables de ces types possdent directement la valeur que nous lui aectons, on peut facilement comparer un entier avec la valeur 5, ou un entier avec un autre entier. Par contre, cela ne fonctionne pas avec les objets. En eet, nous avons vu que les variables qui reprsentent des instances dobjet contiennent en fait une rfrence vers linstance. Cela na pas vraiment de sens de comparer des rfrences. De plus, en imaginant que je veuille vraiment comparer deux voitures, sur quels critres puis-je dterminer quelles sont gales ? La couleur ? La vitesse ? Sans rien faire, la comparaison en utilisant par exemple loprateur dgalit == permet simplement de vrier si les rfrences pointent vers le mme objet. Pour les exemples de ce chapitre, nous nous baserons sur la classe Voiture suivante :
1 2 3 4 5 6

public class Voiture { public string Couleur { get ; set ; } public string Marque { get ; set ; } public int Vitesse { get ; set ; } }

Ainsi, si nous crivons :


1 2 3 4 5 6 7 8

Voiture voitureNicolas = new Voiture () ; voitureNicolas . Couleur = " Bleue " ; Voiture voitureJeremie = voitureNicolas ; voitureJeremie . Couleur = " Verte " ; if ( voitureJeremie == voitureNicolas ) { Console . WriteLine ( " Les objets r f rencent la m me instance " ); }

Ce code achera la chane Les objets rfrencent la mme instance car eectivement, nous avons aect la rfrence de voitureNicolas voitureJeremie - ce qui implique galement que la modication de la voiture de Jrmie aecte galement la voiture de Nicolas, comme nous lavons dj vu. Par contre, le code suivant :
1 2 3 4 5 6

Voiture voitureNicolas = new Voiture () ; Voiture voitureJeremie = new Voiture () ; if ( voitureJeremie == voitureNicolas ) { Console . WriteLine ( " Les objets r f rencent la m me instance " ); }

nachera videmment rien car ce sont deux instances direntes. 222

COMPARER DES OBJETS Sil savre quil est vraiment pertinent de comparer deux voitures entre elles, il faut savoir que cest quand mme possible. La premire chose faire est de dnir les critres de comparaison. Par exemple, nous navons qu dire que deux voitures sont identiques quand la couleur, la marque et la vitesse sont gales. Je sais, cest un peu irrel, mais cest pour lexemple. Ainsi, nous pourrons par exemple vrier que deux voitures sont gales avec linstruction suivante :
1

2 3 4

if ( voitureNicolas . Couleur == voitureJeremie . Couleur && voitureNicolas . Marque == voitureJeremie . Marque && voitureNicolas . Vitesse == voitureJeremie . Vitesse ) { Console . WriteLine ( " Les deux voitures sont identiques " ) ; }

Comme on la dj vu, on utilise le mot-cl override. Ceci est possible dans la mesure o la classe Object a dni la mthode Equals comme virtuelle, avec le mot-cl virtual. Ce qui donne :
1 2 3 4 5 6 7 8 9 10 11 12 13 14

La comparaison dgalit entre deux objets, cest en fait le rle de la mthode Equals() dont chaque objet hrite de la classe mre Object. part pour les types valeur, le comportement par dfaut de la mthode Equals() est de comparer les rfrences des objets. Seulement, il est possible de dnir un comportement plus appropri pour notre classe Voiture, grce la fameuse spcialisation. Pour plus dinformations sur la mthode Equals(), je vous renvoie au code web suivant : Mthode Equals Code web : 657987

public class Voiture { public string Couleur { get ; set ; } public string Marque { get ; set ; } public int Vitesse { get ; set ; } public override bool Equals ( object obj ) { Voiture v = obj as Voiture ; if ( v == null ) return false ; return Vitesse == v . Vitesse && Couleur == v . Couleur && Marque == v . Marque ; }

Remarquons que la mthode Equals prend en paramtre un object. La premire chose faire est donc de vrier que nous avons rellement une voiture, grce au cast dynamique. Ensuite, il ne reste qu comparer les proprits de linstance courante et de lobjet pass en paramtre. Pour faire une comparaison entre deux voitures, nous pourrons utiliser le code suivant : 223

CHAPITRE 23. NOTIONS AVANCES DE POO EN C#


1 2 3 4 5 6

Voiture voitureNicolas = new Voiture { Vitesse = 10 , Marque = " Peugeot " , Couleur = " Grise " }; Voiture voitureJeremie = new Voiture { Vitesse = 10 , Marque = " Peugeot " , Couleur = " Grise " }; if ( voitureNicolas . Equals ( voitureJeremie ) ) { Console . WriteLine ( " Les objets ont les m mes valeurs dans leurs propri t s " ) ; }

Nos deux voitures sont identiques car leurs marques, leurs couleurs et leurs vitesses sont identiques :
Les objets ont les m mes valeurs dans leurs propri t s

Cest facile de comparer ! Sauf que vous aurez peut-tre remarqu que la compilation de ce code provoque un avertissement. Il ne sagit pas dune erreur, mais Visual C# Express nous informe quil faut faire attention (voir la gure 23.1).

Figure 23.1 La compilation fait apparatre un avertissement Il nous dit que nous avons substitu la mthode Equals() sans avoir redni la mthode GetHashCode(). Nous navons pas besoin ici de savoir quoi sert vraiment la mthode GetHashCode(), mais si lenvie vous prend de vous instruire ce sujet, vous pouvez lire la documentation ocielle disponible via le code web suivant : Mthode GetHashCode() Code web : 785089

Toujours est-il que nous devons rajouter une spcialisation de la mthode GetHashCode(), dont le but est de renvoyer un identiant plus ou moins unique reprsentant lobjet, ce qui donnera :
1 2 3 4 5 6 7 8 9

public class Voiture { public string Couleur { get ; set ; } public string Marque { get ; set ; } public int Vitesse { get ; set ; } public override bool Equals ( object obj ) { Voiture v = obj as Voiture ; if ( v == null ) return false ;

10 11

224

LES INTERFACES
12 13 14 15 16 17 18 19

return Vitesse == v . Vitesse && Couleur == v . Couleur && Marque == v . Marque ;

public override int GetHashCode () { return Couleur . GetHashCode () * Marque . GetHashCode () * Vitesse . GetHashCode () ; }

Nous nous servons du fait que chaque variable de la classe possde dj un identiant obtenu avec la mthode GetHashCode(). En combinant chaque identiant de chaque proprit nous pouvons en crer un nouveau. Ici, la classe est complte et prte tre compare. Elle pourra donc fonctionner correctement avec tous les algorithmes dgalit du framework .NET, comme les cls de hachage. Notez quand mme que devoir substituer ces deux mthodes est une opration relativement rare. Ce quil est important de retenir, cest ce fameux warning. La conclusion tirer est que notre faon de comparer, bien que fonctionnelle pour notre voiture, nest pas parfaite. Pourquoi ? Parce quen ayant substitu la mthode Equals(), nous croyons que la comparaison est bonne sauf que le compilateur nous apprend que ce nest pas le cas. Heureusement quil tait l, ce compilateur ! Comme cest une erreur classique, il est capable de la dtecter. Mais si cest autre chose et quil ne le dtecte pas ? Tout a manque duniformisation, vous ne trouvez pas ? Il faudrait quelque chose qui nous assure que la classe est correctement comparable. Une espce de contrat que lobjet sengagerait respecter pour tre sr que toutes les comparaisons soient valides. Un contrat ? Un truc qui nit par able ? a me rappelle quelque chose a. . . mais oui, les interfaces !

Les interfaces
Une fois nest pas coutume. Plutt que de commencer par tudier le plus simple, nous allons tudier le plus logique puis nous reviendrons sur le plus simple. Cest--dire que nous allons pousser un peu plus loin la comparaison en nous servant des interfaces et nous reviendrons ensuite sur le moyen de crer une interface. Nous avons donc dit quune interface tait un contrat que sengageait respecter un objet. Cest tout fait ce dont on a besoin ici. Notre objet doit sengager fonctionner pour tous les types de comparaison. Il doit tre comparable. Mais comparable ne veut pas forcment dire galit , nous devrions tre aussi capables dindiquer si un objet est suprieur un autre. Pourquoi ? Imaginons que nous possdions un tableau de voitures et que nous souhaitions le trier comme on a vu dans un chapitre prcdent. Pour les entiers ctait une opration plutt simple ; avec la mthode Array.Sort() ils taient automatiquement tris par ordre croissant. Mais dans notre cas, comment un tableau sera capable de 225

CHAPITRE 23. NOTIONS AVANCES DE POO EN C# trier nos voitures ? Nous voici donc en prsence dun cas concret dutilisation des interfaces. Linterface IComparable permet de dnir un contrat de mthodes destines la prise en charge de la comparaison entre deux instances dun objet. Pour la documentation sur cette mthode, je vous renvoie au code web suivant : IComparable Code web : 802146

Une fois ces mthodes implmentes, nous serons certains que nos objets seront comparables correctement. Pour cela, nous allons faire en sorte que notre classe implmente linterface. Reprenons notre classe Voiture avec uniquement ses proprits et faisons lui implmenter linterface IComparable. Pour implmenter une interface, on utilisera la mme syntaxe que pour hriter dune classe, cest--dire quon utilisera les deux points suivis du type de linterface. Ce qui donne :
1 2 3 4 5 6

public class Voiture : IComparable { public string Couleur { get ; set ; } public string Marque { get ; set ; } public int Vitesse { get ; set ; } }

Il est conventionnel de dmarrer le nom dune interface par un I majuscule. Cest le cas de toutes les interfaces du framework .NET. Si vous tentez de compiler ce code, vous aurez un message derreur (voir gure 23.2).

Figure 23.2 Erreur de compilation car linterface nest pas implmente Le compilateur nous rappelle lordre : nous annonons que nous souhaitons respecter le contrat de comparaison, sauf que nous navons pas la mthode adquate ! Eh oui, le contrat indique ce que nous nous engageons faire mais pas la faon de le faire. Limplmentation de la mthode est notre charge. Toujours dans loptique de simplier la tche du dveloppeur, Visual C# Express nous aide pour implmenter les mthodes dun contrat. Faites un clic droit sur linterface et choisissez dans le menu contextuel Implmenter linterface et Implmenter linterface (voir gure 23.3). 226

LES INTERFACES

Figure 23.3 Visual C# Express propose dimplmenter automatiquement linterface Visual C# Express nous gnre le code suivant :
1 2 3 4 5 6 7 8 9 10 11

public class Voiture : IComparable { public string Couleur { get ; set ; } public string Marque { get ; set ; } public int Vitesse { get ; set ; } public int CompareTo ( object obj ) { throw new N o tI mp le m en te dE x ce pt i on () ; }

Il sagit de la signature de la mthode qui nous manque pour respecter le contrat de linterface et dun contenu que nous ne comprenons pas pour linstant. Nous y reviendrons plus tard ; pour linstant, vous navez qu supprimer la ligne :
1

throw new N o tI mp le m en te dE x ce pt i on () ;

Il ne reste plus qu crire le code de la mthode. Pour ce faire, il faut dnir un critre de tri. Trier des voitures na pas trop de sens, aussi nous dirons que nous souhaitons les trier suivant leurs vitesses. Pour respecter correctement le contrat, nous devons respecter la rgle suivante qui se trouve dans la documentation de la mthode de linterface : si une voiture est infrieure une autre, alors nous devons renvoyer une valeur infrieure 0, disons -1 ; si elle est gale, alors nous devons renvoyer 0 ; si elle est suprieure, nous devons renvoyer une valeur suprieure 0, disons 1. Ce qui donne :
1 2 3 4 5 6 7 8 9

public int CompareTo ( object obj ) { Voiture voiture = ( Voiture ) obj ; if ( this . Vitesse < voiture . Vitesse ) return -1 ; if ( this . Vitesse > voiture . Vitesse ) return 1 ; return 0 ; }

227

CHAPITRE 23. NOTIONS AVANCES DE POO EN C# La comparaison seectue entre lobjet courant et un objet qui lui est pass en paramtre. Pour que ce soit un peu plus clair, jai utilis le mot-cl this qui permet de bien identier lobjet courant et lobjet pass en paramtre. Comme il est facultatif, nous pouvons le supprimer. Vous aurez galement remarqu que jutilise un cast explicite avant de comparer. Ceci permet de renvoyer une erreur si jamais lobjet comparer nest pas du bon type. En eet, que devrais-je renvoyer si jamais lobjet quon me passe nest pas une voiture ? Une erreur ! Cest trs bien. Le code est susamment explicite pour que nous comprenions facilement ce que lon doit faire : comparer les vitesses. Il est possible de simplier grandement le code, car pour comparer nos deux voitures, nous eectuons la comparaison sur la valeur dun entier, ce qui est plutt trivial. Dautant plus que lentier, en bon objet comparable, possde galement la mthode CompareTo(). Ce qui fait quil est possible dcrire notre mthode de comparaison de cette faon :
1 2 3 4 5

public int CompareTo ( object obj ) { Voiture voiture = ( Voiture ) obj ; return Vitesse . CompareTo ( voiture . Vitesse ) ; }

En eet, Vitesse tant un type intgr, il implmente dj correctement la comparaison. Cest dailleurs pour a que le tableau dentier que nous avons vu prcdemment a t capable de se trier facilement. En ayant implment cette interface, nous pouvons dsormais trier des tableaux de Voiture :
1

2 3 4 5 6

Voiture [] voitures = new Voiture [] { new Voiture { Vitesse = 100 } , new Voiture { Vitesse = 40 } , new Voiture { Vitesse = 10 } , new Voiture { Vitesse = 40 } , new Voiture { Vitesse = 50 } }; Array . Sort ( voitures ) ; foreach ( Voiture v in voitures ) { Console . WriteLine ( v . Vitesse ) ; }

Ce qui achera :
10 40 40 50 100

Voil pour le tri, mais si je peux me permettre, je trouve que ce code-l nest pas trs esthtique ! Jy reviendrai un peu plus tard. . . 228

LES INTERFACES Nous avons donc implment notre premire interface. Finalement, ce ntait pas si compliqu. Voyons prsent comment crer nos propres interfaces. Une interface se dnit en C# comme une classe, sauf quon utilise le mot-cl interface la place de class. En tant que dbutant, vous aurez rarement besoin de crer des interfaces. Cependant, il est utile de savoir le faire. Par contre, il sera beaucoup plus frquent que vos classes implmentent des interfaces existantes du framework .NET, comme nous venons de le faire. Voyons prsent comment crer une interface et examinons le code suivant :
1 2 3 4 5

public interface IVolant { int Nomb rePropulseurs { get ; set ; } void Voler () ; }

Comme pour les classes, il est recommand de crer les interfaces dans un chier part. Rappelez-vous galement de la convention qui fait que les interfaces doivent commencer par un I majuscule. Nous dnissons ici une interface IVolant qui possde une proprit de type int et une mthode Voler() qui ne renvoie rien. Voil, cest tout simple ! Nous avons cr une interface. Les objets qui choisiront dimplmenter cette interface seront obligs davoir une proprit entire NombrePropulseurs et une mthode Voler() qui ne renvoie rien. Rappelez-vous que linterface ne contient que le contrat et aucune implmentation. Cest--dire que nous ne verrons jamais de corps de mthode dans une interface ni de variables membres ; uniquement des mthodes et des proprits. Un contrat. Notez quand mme quil ne faut pas dnir de visibilit sur les membres dune interface. Nous serons obligs de dnir les visibilits en public sur les objets implmentant linterface. Crons dsormais deux objets Avion et Oiseau qui implmentent cette interface, ce qui donne :
1 2 3 4 5 6 7 8 9 10 11 12 13

public class Oiseau : IVolant { public int NombrePropulseurs { get ; set ; } public void Voler () { Console . WriteLine ( " Je vole gr ce " + NombrePropulseurs + " ailes " ) ; }

public class Avion : IVolant { public int NombrePropulseurs { get ; set ; }

229

CHAPITRE 23. NOTIONS AVANCES DE POO EN C#


14 15 16 17 18

public void Voler () { Console . WriteLine ( " Je vole gr ce " + NombrePropulseurs + " moteurs " ) ; }

Grce ce contrat, nous savons maintenant que nimporte lequel de ces objets saura voler. Il est possible de traiter ces objets comme des objets volants, un peu comme ce que nous avions fait avec les classes mres, en utilisant linterface comme type pour la variable. Par exemple :
1 2

IVolant oiseau = new Oiseau { NombrePropulseurs = 2 }; oiseau . Voler () ;

Nous instancions vraiment un objet Oiseau, mais nous le manipulons en tant que IVolant. Un des intrts dans ce cas sera de pouvoir manipuler des objets qui partagent un mme comportement :
1 2 3 4 5 6 7 8

Oiseau oiseau = new Oiseau { NombrePropulseurs = 2 }; Avion avion = new Avion { NombrePropulseurs = 4 }; List < IVolant > volants = new List < IVolant > { oiseau , avion }; foreach ( IVolant volant in volants ) { volant . Voler () ; }

Ce qui produira :
Je vole gr ce 2 ailes Je vole gr ce 4 moteurs

Grce linterface, nous avons pu mettre dans une mme liste des objets dirents, qui nhritent pas entre eux mais qui partagent une mme interface, cest--dire un mme comportement : IVolant. Pour accder ces objets, nous devrons utiliser leurs interfaces. Il sera possible quand mme de caster nos IVolant en Avion ou en Oiseau, si jamais nous souhaitons rajouter une proprit propre lavion. Par exemple je rajoute une proprit NomDuCommandant mon avion mais qui ne fait pas partie de linterface :
1 2 3 4 5 6 7

public class Avion : IVolant { public int NombrePropulseurs { get ; set ; } public string NomDuCommandant { get ; set ; } public void Voler () { Console . WriteLine ( " Je vole gr ce " + NombrePropulseurs + " moteurs " ) ;

230

LES INTERFACES
8 9

Cela veut dire que lobjet Avion pourra aecter un nom de commandant mais quil ne sera pas possible dy accder par linterface :
1 2

IVolant avion = new Avion { NombrePropulseurs = 4 , NomDuCommandant = " Nico " }; Console . WriteLine ( avion . NomDuCommandant ) ; // erreur de compilation

Lerreur de compilation nous indique que IVolant ne possde pas de dnition pour NomDuCommandant ; ce qui est vrai ! Pour accder au nom du commandant, nous pourrons tenter de caster nos IVolant en Avion. Si le cast est valide, alors nous pourrons accder notre proprit :
1 2 3 4 5 6 7 8 9 10 11 12 13

Oiseau oiseau = new Oiseau { NombrePropulseurs = 2 }; Avion avion = new Avion { NombrePropulseurs = 4 , NomDuCommandant = " Nico " }; List < IVolant > volants = new List < IVolant > { oiseau , avion }; foreach ( IVolant volant in volants ) { volant . Voler () ; Avion a = volant as Avion ; if ( a != null ) { Console . WriteLine ( a . NomDuCommandant ) ; } }

Voil, cest tout simple et a ressemble un peu ce quon a dj vu. Lorsque nous avons abord la boucle foreach, jai dit quelle nous servait parcourir des lments numrables . En disant a, je disais en fait que la boucle foreach fonctionne avec tous les types qui implmentent linterface IEnumerable. Maintenant que vous savez ce quest une interface, vous comprenez mieux ce quoi je faisais vraiment rfrence. Il faut galement noter que les interfaces peuvent hriter entre elles, comme cest le cas avec les objets. Cest--dire que je vais pouvoir dclarer une interface IVolantMotorise qui hrite de linterface IVolant.
1 2 3 4 5 6 7

public interface IVolant { int Nomb rePropulseurs { get ; set ; } void Voler () ; } public interface IVolantMotorise : IVolant

231

CHAPITRE 23. NOTIONS AVANCES DE POO EN C#


8 9 10

{ }

void DemarrerLeMoteur () ;

Ainsi, ma classe Avion qui implmentera IVolantMotorise devra obligatoirement implmenter les mthodes et proprits de IVolant et la mthode de IVolantMotorise :
1 2 3 4 5 6 7 8 9 10 11 12

public class Avion : IVolantMotorise { public void DemarrerLeMoteur () { } public int NombrePropulseurs { get ; set ;} public void Voler () { }

Enn, et nous nous arrterons l pour les interfaces, il est possible pour une classe dimplmenter plusieurs interfaces. Il sura pour cela de sparer les interfaces par une virgule et dimplmenter bien sr tout ce quil faut derrire. Par exemple :
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22

public interface IVolant { void Voler () ; } public interface IRoulant { void Rouler () ; } public class Avion : IVolant , IRoulant { public void Voler () { Console . WriteLine ( " Je vole " ) ; } public void Rouler () { Console . WriteLine ( " Je Roule " ) ; }

232

LES CLASSES ET LES MTHODES ABSTRAITES

Les classes et les mthodes abstraites


Une classe abstraite est une classe particulire qui ne peut pas tre instancie. Concrtement, cela veut dire que nous ne pourrons pas utiliser loprateur new. De la mme faon, une mthode abstraite est une mthode qui ne contient pas dimplmentation, cest--dire pas de code. Pour tre utilisables, les classes abstraites doivent tre hrites et les mthodes rednies. En gnral, les classes abstraites sont utilises comme classes de base pour dautres classes. Ces classes fournissent des comportements mais nont pas vraiment dutilit propre. Ainsi, les classes lles qui en hritent pourront bncier de leurs comportements et devront ventuellement en remplacer dautres. Cest comme une classe incomplte qui ne demande qu tre complte. Si une classe possde une mthode abstraite, alors la classe doit absolument tre abstraite. Linverse nest pas vrai, une classe abstraite peut possder des mthodes non abstraites. Vous aurez rarement besoin dutiliser les classes abstraites en tant que dbutant mais elles pourront vous servir pour combiner la puissance des interfaces lhritage. Bon, voil pour la thorie, passons un peu la pratique ! Rappelez-vous nos chiens et nos chats qui drivent dune classe mre Animal. Nous savons quun animal est capable de se dplacer. Cest un comportement quont en commun tous les animaux. Il est tout fait logique de dnir une mthode SeDeplacer() au niveau de la classe Animal. Sauf quun chien ne se dplace pas forcment comme un dauphin. Lun a des pattes, lautre des nageoires. Et mme si le chien et le chat semblent avoir un dplacement relativement proche, ils ont chacun des subtilits. Le chat a un mouvement plus gracieux, plus flin. Bref, on se rend compte que nous sommes obligs de rednir la mthode SeDeplacer() dans chaque classe lle de la classe Animal. Et puis, de toute faon, sommes-nous vraiment capables de dire comment se dplace un Animal dans labsolu ? La mthode SeDeplacer est une candidate parfaite pour une mthode abstraite. Rappelezvous, la mthode abstraite ne possde pas dimplmentation et chaque classe lle de la classe possdant cette mthode abstraite devra la spcialiser. Cest exactement ce quil nous faut. Pour dclarer une mthode comme tant abstraite, il faut utiliser le mot-cl abstract et ne fournir aucune implmentation de la mthode, cest--dire que la dclaration de la mthode doit se terminer par un point-virgule :
public class Animal { public abstract void SeDeplacer () ; }

1 2 3 4

233

CHAPITRE 23. NOTIONS AVANCES DE POO EN C# Si nous tentons de compiler cette classe, nous aurons lerreur suivante :
M a P r e m i e r eAppl icati on . Animal . SeDeplacer () est abstrait , mais est contenu dans la classe non abstraite M a P r e m i er eAppl icatio n . Animal

Ah oui, cest vrai, on a dit quune classe qui contient au moins une mthode abstraite tait forcment abstraite. Cest le mme principe que pour la mthode, il sut dutiliser le mot-cl abstract devant le mot-cl class :
1 2 3 4

public abstract class Animal { public abstract void SeDeplacer () ; }

Voil, notre classe peut compiler tranquillement. Nous avons galement dit quune classe abstraite pouvait contenir des mthodes concrtes et que ctait dailleurs une des grandes forces de ce genre de classes. En eet, il est tout fait pertinent que des animaux puissent mourir. Et l, a se passe pour tout le monde de la mme faon : le cur arrte de battre et on ne peut plus rien faire. Cest triste, mais cest ainsi. Cela veut dire que nous pouvons crer une mthode Mourir() dans notre classe abstraite qui possde une implmentation. Cela donne :
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15

public abstract class Animal { private Coeur coeur ; public Animal () { coeur = new Coeur () ; } public abstract void SeDeplacer () ; public void Mourir () { coeur . Stop () ; }

Avec une classe Cur qui ne fait pas grand-chose :


1 2 3 4 5 6 7

public class Coeur { public void Battre () { Console . WriteLine ( " Boom boom " ) ; }

234

LES CLASSES ET LES MTHODES ABSTRAITES


8 9 10 11 12

public void Stop () { Console . WriteLine ( " Mon coeur s ' arr te de battre " ) ; }

Comme prvu, il nest pas possible dinstancier un objet Animal. Si nous tentons lopration :
1

Animal animal = new Animal () ;

nous aurons lerreur de compilation suivante :


Impossible de cr er une instance de la classe abstraite ou de l interface MaP remie reAppl icati on . Animal

Par contre, il est possible de crer une classe Chien qui drive de la classe Animal :
1 2 3

public class Chien : Animal { }

Cette classe ne pourra pas compiler dans cet tat car il faut obligatoirement rednir la mthode abstraite SeDeplacer(). Cela se fait en utilisant le mot-cl override, comme on la dj vu. Vous aurez srement remarqu que la mthode abstraite nutilise pas le mot-cl virtual comme cela doit absolument tre le cas lors de la substitution dune mthode dans une classe non-abstraite. Il est ici implicite et ne doit pas tre utilis, sinon nous aurons une erreur de compilation. Et puis cela nous arrange ; un seul mot-cl, cest largement susant ! Nous devons donc spcialiser la mthode SeDeplacer, soit en crivant la mthode la main, soit en utilisant encore une fois notre ami Visual C# Express. Il sut de faire un clic droit sur la classe dont hrite Chien (en loccurrence Animal) et de cliquer sur Implmenter une classe abstraite (voir la gure 23.4).

Figure 23.4 Visual C# Express propose dimplmenter la classe abstraite Visual C# Express nous gnre donc la signature de la mthode substituer :
1

public class Chien : Animal

235

CHAPITRE 23. NOTIONS AVANCES DE POO EN C#


2 3 4 5 6 7

public override void SeDeplacer () { throw new N o tI mp le m en te d Ex ce pt i on () ; }

Il ne reste plus qu crire le corps de la mthode SeDeplacer, par exemple :


1 2 3 4 5 6 7

public class Chien : Animal { public override void SeDeplacer () { Console . WriteLine ( " Waouf ! Je me d place avec mes 4 pattes " ) ; } }

Ainsi, nous pourrons crer un objet Chien et le faire se dplacer puis le faire mourir car il hrite des comportements de la classe Animal. Paix son me.
1 2 3

Chien max = new Chien () ; max . SeDeplacer () ; max . Mourir () ;

Ce code achera :
Waouf ! Je me d place avec mes 4 pattes Mon coeur s arr te de battre

Vous pouvez dsormais vous rendre compte de la vracit de la phrase que jai crite en introduction : [Les classes abstraites] pourront vous servir pour combiner la puissance des interfaces lhritage. En eet, la classe abstraite peut fournir des implmentations alors que linterface ne propose quun contrat. Cependant, une classe concrte ne peut hriter que dune seule classe mais peut implmenter plusieurs interfaces. La classe abstraite est un peu mi-chemin entre lhritage et linterface.

Les classes partielles


Les classes partielles orent la possibilit de dnir une classe en plusieurs fois. En gnral, ceci est utilis pour dnir une classe sur plusieurs chiers, si par exemple la classe devient trs longue. Il pourra ventuellement tre judicieux de dcouper la classe en plusieurs chiers pour regrouper des fonctionnalits qui se ressemblent. On utilise pour cela le mot-cl partial. Par exemple, si nous avons un chier qui contient la classe Voiture suivante : 236

LES CLASSES PARTIELLES


1 2 3 4 5 6

public partial class Voiture { public string Couleur { get ; set ; } public string Marque { get ; set ; } public int Vitesse { get ; set ; } }

Nous pourrons complter sa dnition dans un autre chier pour lui rajouter par exemple des mthodes :
1 2 3 4 5 6 7

public partial class Voiture { public string Rouler () { return " Je roule " + Vitesse + " km / h " ; } }

la compilation, Visual C# Express runit les deux classes en une seule et lobjet fonctionne comme toute autre classe qui ne serait pas forcment partielle. noter quil faut imprativement que les deux classes possdent le mot-cl partial pour que cela soit possible, sinon Visual C# Express gnrera une erreur de compilation :
Modificateur partiel manquant sur la d claration de type M a P r e m i e r e Appl icatio n . Voiture ; une autre d claration partielle de ce type existe

Vous allez me dire que ce nest pas super utile comme fonctionnalit. Et je vous dirai que vous avez raison. Sauf dans un cas particulier. Les classes partielles prennent de lintrt quand une partie du code de la classe est gnre par Visual C# Express. Cest le cas pour la plupart des plateformes qui servent dvelopper de vraies applications. Par exemple ASP.NET pour un site internet, WPF pour une application Windows, Silverlight pour un client riche, etc. Cest aussi le cas lorsque nous gnrons de quoi permettre laccs une base de donnes. Dans ce cas-l, disons pour simplier que Visual C# Express va nous gnrer tout un tas dinstructions pour nous connecter la base de donnes ou pour rcuprer des donnes. Toutes ces instructions seront mises dans une classe partielle que nous pourrons enrichir avec nos besoins. Lintrt est que si nous gnrons une nouvelle version de notre classe, seul le chier gnr sera impact. Si nous avions modi le chier pour enrichir la classe avec nos besoins, nous aurions perdu tout notre travail. Vu que, grce aux classes partielles, ce code est situ dans un autre chier, il nest donc pas perdu. Pour notre plus grand bonheur ! noter que le mot-cl partial peut se combiner sans problme avec dautres mots-cls, comme abstract par exemple que nous venons de voir. 237

CHAPITRE 23. NOTIONS AVANCES DE POO EN C# Il est frquent aussi de voir des classes partielles utilises quand plusieurs dveloppeurs travaillent sur la mme classe. Le fait de sparer la classe en deux chiers permet de travailler sans se marcher dessus. Nous aurons loccasion de voir des classes partielles gnres plus tard dans louvrage.

Classes statiques et mthodes statiques


Nous avons dj vu le mot-cl static en premire partie de ce livre. Il nous a bien encombrs ! Nous nous le sommes trimball pendant un moment, puis il a disparu. Il est temps de revenir sur ce mot-cl an de comprendre exactement de quoi il sagit, maintenant que nous avons plus de notions et que nous connaissons les objets et les classes. Jusqu prsent, nous avons utilis le mot-cl static uniquement sur les mthodes et je lai expliqu vaguement en disant quil servait indiquer que la mthode est toujours disponible et prte tre utilise. Pas trs convaincante mon explication. . . mais comme vous tes polis, vous ne mavez rien dit ! En fait, le mot-cl static permet dindiquer que la mthode dune classe nappartient pas une instance de la classe. Nous avons vu que jusqu prsent, nous devions instancier nos classes avec le mot-cl new pour avoir des objets. Ici, static permet de ne pas instancier lobjet mais davoir accs cette mthode en dehors de tout objet. Nous avons dj utilis beaucoup de mthodes statiques, je ne sais pas si vous avez fait attention, mais maintenant que vous connaissez les objets, la mthode suivante ne vous parat pas bizarre ?
1

Console . WriteLine ( " Bonjour " ) ;

Nous utilisons la mthode WriteLine de la classe Console sans avoir cr dobjet Console. trange. Il sagit, vous laurez devin, dune mthode statique qui est accessible en dehors de toute instance de Console. Dailleurs, la classe entire est une classe statique. Si nous essayons dinstancier la classe Console avec :
1

Console c = new Console () ;

Nous aurons les messages derreurs suivants :


Impossible de d clarer une variable de type static System . Console Impossible de cr er une instance de la classe static System . Console

Nous avons dit que la mthode spciale Main() est obligatoirement statique. Cela permet au CLR qui va excuter notre application de ne pas avoir besoin dinstancier 238

CLASSES STATIQUES ET MTHODES STATIQUES la classe Program pour dmarrer notre application. Il a juste appeler la mthode Program.Main() an de dmarrer notre programme. Comme la mthode Main() est utilisable en dehors de toute instance de classe, elle ne peut appeler que des mthodes statiques. En eet, comment pourrait-elle appeler des mthodes dun objet alors quelle nen a mme pas conscience. Cest pour cela que nous avons t obligs de prxer chacune de nos premires mthodes par le mot-cl static. Revenons nos objets. Ils peuvent contenir des mthodes statiques ou des variables statiques. Si une classe ne contient que des choses statiques alors elle peut devenir galement statique. Une mthode statique est donc une mthode qui ne travaille pas avec les membres (variables ou autres) non statiques de sa propre classe. Rappelez-vous, un peu plus haut, nous avions cr une classe Math qui servait faire des additions, an dillustrer le polymorphisme :
1 2 3 4 5 6 7

public class Math { public int Addition ( int a , int b ) { return a + b ; } }

que nous utilisions de cette faon :


1 2

Math math = new Math () ; int resultat = math . Addition (5 , 6 ) ;

Ici, la mthode addition sert additionner deux entiers. Elle est compltement indpendante de la classe Math et donc des instances de lobjet Math. Nous pouvons donc en faire une mthode statique, pour cela il sura de prxer du mot-cl static son type de retour :
1 2 3 4 5 6 7

public class Math { public static int Addition ( int a , int b ) { return a + b ; } }

Et nous pourrons alors utiliser laddition sans crer dinstance de la classe Math, mais simplement en utilisant le nom de la classe suivi du nom de la mthode statique :
1

int resultat = Math . Addition (5 , 6 ) ;

Exactement comme nous avons fait pour Console.WriteLine. De la mme faon, nous pouvons rajouter dautres mthodes, comme la multiplication : 239

CHAPITRE 23. NOTIONS AVANCES DE POO EN C#


1 2 3 4 5 6 7 8 9 10 11 12

public class Math { public static int Addition ( int a , int b ) { return a + b ; } public static long Multiplication ( int a , int b ) { return a * b ; }

Nous lappellerons de la mme faon :


1

long resultat = Math . Multiplication (5 , 6 ) ;

noter que la classe Math est toujours instanciable mais quil nest pas possible dappeler les mthodes qui sont statiques depuis un objet Math. Le code suivant :
1 2

Math math = new Math () ; long resultat = math . Multiplication (5 , 6 ) ;

provoquera lerreur de compilation :


Le membre M aPrem iereAp plica tion . Math . Multiplication ( int , int ) est inaccessible avec une r f rence d instance ; qualifiez - le avec un nom de type

Ok, mais quoi a sert de pouvoir instancier un objet Math si nous ne pouvons accder aucune de ses mthodes, vu quelles sont statiques ? Absolument rien ! Si une classe ne possde que des membres statiques, alors il est possible de rendre cette classe statique grce au mme mot-cl. Celle-ci deviendra non-instanciable, comme la classe Console :
1 2 3 4 5 6 7

public static class Math { public static int Addition ( int a , int b ) { return a + b ; } }

Ainsi, si nous tentons dinstancier lobjet Math, nous aurons une erreur de compilation. En gnral, les classes statiques servent regrouper des mthodes utilitaires qui partagent une mme fonctionnalit. Ici, la classe Math permettrait de ranger toutes les mthodes du style addition, multiplication, racine carre, etc. 240

CLASSES STATIQUES ET MTHODES STATIQUES Ah, on me fait signe que cette classe existe dj dans le framework .NET et quelle sappelle galement Math. Elle est range dans lespace de nom System. Souvenez-vous, nous lavons utilise pour calculer la racine carre. Cette classe est statique, cest la version aboutie de la classe que nous avons commenc crire. Par contre, ce nest pas parce quune classe possde des mthodes statiques quelle est obligatoirement statique. Il est aussi possible davoir des membres statiques dans une classe qui possde des membres non statiques. Par exemple notre classe Chien, qui possde un prnom et qui sait aboyer :
1 2 3 4 5 6 7 8 9 10 11 12 13 14

public class Chien { private string prenom ; public Chien ( string prenomDuChien ) { prenom = prenomDuChien ; } public void Aboyer () { Console . WriteLine ( " Wouaf ! Je suis " + prenom ) ; }

pourrait possder une mthode permettant de calculer lge dun chien dans le rfrentiel des humains. Comme beaucoup le savent, il sut de multiplier lge du chien par 7 !
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19

public class Chien { private string prenom ; public Chien ( string prenomDuChien ) { prenom = prenomDuChien ; } public void Aboyer () { Console . WriteLine ( " Wouaf ! Je suis " + prenom ) ; } public static int CalculerAge ( int ageDuChien ) { return ageDuChien * 7 ; }

Ici, la mthode CalculerAge() est statique car elle ne travaille pas directement avec une instance dun chien. 241

CHAPITRE 23. NOTIONS AVANCES DE POO EN C# Nous pouvons lappeler ainsi :


1 2 3 4

Chien hina = new Chien ( " Hina " ) ; hina . Aboyer () ; int ag e Re ferentielHomme = Chien . CalculerAge ( 4 ) ; Console . WriteLine ( ageReferentielHomme ) ;

Vous aurez ainsi dans la console :


Waouf ! Je suis Hina 28

Vous me direz quil est possible de faire en sorte que la mthode travaille sur une instance dun objet Chien, ce qui serait peut-tre plus judicieux ici. Il surait de rajouter une proprit Age au Chien et de transformer la mthode pour quelle ne soit plus statique. Ce qui donnerait :
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21

public class Chien { private string prenom ; public int Age { get ; set ; } public Chien ( string prenomDuChien ) { prenom = prenomDuChien ; } public void Aboyer () { Console . WriteLine ( " Wouaf ! Je suis " + prenom ) ; } public int CalculerAge () { return Age * 7 ; }

Que lon pourrait appeler de cette faon :


1 2 3

Chien hina = new Chien ( " Hina " ) { Age = 5 }; int ag e Re ferentielHomme = hina . CalculerAge () ; Console . WriteLine ( ageReferentielHomme ) ;

Ici, cest plus une histoire de conception. Cest vous de dcider, mais sachez que cest possible. Il est galement possible dutiliser le mot-cl static avec des proprits. Imaginons que nous souhaitions avoir un compteur sur le nombre dinstances de la classe Chien. Nous pourrions crer une proprit statique de type entier qui sincrmente chaque fois que lon cre un nouveau chien : 242

LES CLASSES INTERNES


1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17

public class Chien { public static int NombreDeChiens { get ; set ; } private string prenom ; public Chien ( string prenomDuChien ) { prenom = prenomDuChien ; NombreDeChiens ++; } public void Aboyer () { Console . WriteLine ( " Wouaf ! Je suis " + prenom ) ; }

Ici, la proprit NombreDeChiens est statique et sincrmente chaque passage dans le constructeur. Ainsi, si nous crons plusieurs chiens :
1 2 3 4 5

Chien chien1 = new Chien ( " Max " ) ; Chien chien2 = new Chien ( " Hina " ) ; Chien chien3 = new Chien ( " Laika " ) ; Console . WriteLine ( Chien . NombreDeChiens ) ;

Nous pourrons voir combien de fois nous sommes passs dans le constructeur :
3

noter que pour une variable statique, cela se passe de la mme faon quavec les proprits statiques.

Les classes internes


Les classes internes 1 sont un mcanisme qui permet davoir des classes dnies lintrieur dautres classes. Cela peut tre utile si vous souhaitez restreindre laccs dune classe uniquement sa classe mre. Par exemple :
1 2 3

public class Chien { private Coeur coeur = new Coeur () ;


1. Classe interne se dit nested class en anglais.

243

CHAPITRE 23. NOTIONS AVANCES DE POO EN C#


4 5 6 7 8 9 10 11 12 13 14 15 16 17

public void Mourir () { coeur . Stop () ; } private class Coeur { public void Stop () { Console . WriteLine ( " The end " ) ; } }

Ici, la classe Coeur ne peut tre utilise que par la classe Chien car elle est prive. Nous pourrions galement mettre la classe en protected an quune classe drive de la classe Chien puisse galement utiliser la classe Coeur :
1 2 3 4

public class ChienSamois : Chien { private Coeur autreCoeur = new Coeur () ; }

Avec ces niveaux de visibilit, une autre classe comme la classe Chat ne pourra pas se servir de ce cur. Si nous mettons la classe Coeur en public ou internal, elle sera utilisable par tout le monde, comme une classe normale. Dans ce cas, pour nos chats, nous pourrions avoir :
1 2 3 4 5 6 7 8 9

public class Chat { private Chien . Coeur coeur = new Chien . Coeur () ; public void Mourir () { coeur . Stop () ; }

Notez que nous prxons la classe par le nom de la classe qui contient la classe Coeur. Dans ce cas, lintrt dutiliser une classe interne est moindre. Cela permet ventuellement de regrouper les classes de manire smantique, et encore, cest plutt le but des espaces de nom. Voil pour les classes internes. Cest une fonctionnalit qui est peu utilise, mais vous la connaissez dsormais !

Les types anonymes et le mot-cl var


Le mot-cl var est un truc de feignant ! 244

LES TYPES ANONYMES ET LE MOT-CL VAR Il permet de demander au compilateur de dduire le type dune variable au moment o nous la dclarons. Par exemple le code suivant :
1 2

var prenom = " Nicolas " ; var age = 30 ;

est quivalent au code suivant :


1 2

string prenom = " Nicolas " ; int age = 30 ;

Le mot-cl var sert indiquer que nous ne voulons pas nous proccuper de ce quest le type et que cest au compilateur de le trouver. Cela implique quil faut que la variable soit initialise (et non nulle) au moment o elle est dclare an que le compilateur puisse dduire le type de la variable, en loccurrence, il devine quen lui aectant Nicolas , il sagit dune chane de caractres. Je ne recommande pas lutilisation de ce mot-cl car le fait de ne pas mettre le type de la variable fait perdre de la clart au code. Ici, cest facile. On dduit facilement nous aussi que le type de prenom est string et que le type de age est int. Mais cest aussi parce quon est trop fort ! Par contre, si jamais la variable est initialise grce une mthode :
1

var calcul = GetCalcul () ;

il va falloir aller regarder la dnition de la mthode an de savoir le type de retour et connatre ainsi le type de la variable. Des contraintes dont on na pas besoin alors quil est aussi simple dindiquer le vrai type et que cest dautant plus lisible. Mais alors, pourquoi en parler ?

Parce que ce mot-cl peut servir dans un cas prcis, celui des types anonymes. Lorsquil est conjointement utilis avec loprateur new, il permet de crer des types anonymes, cest--dire des types dont on na pas dni la classe au pralable. Une classe sans nom. Cette classe est dduite grce son initialisation :
1

var u ne Pe rso nneAnonyme = new { Prenom = " Nico " , Age = 30 };

Ici nous crons une variable qui contient une proprit Prenom et une proprit Age. Forcment, il est impossible de donner un type cette variable vu quelle na pas de dnition. Cest pour cela quon utilise le mot-cl var. Ici, on sait juste que la variable unePersonneAnonyme possde deux proprits, un prnom et un ge. En interne, le compilateur va gnrer un nom de classe pour ce type anonyme, mais il na pas de sens pour nous. En loccurrence, si nous crivons le code suivant o GetType() (hrit de la classe object) renvoie le nom du type :
1 2

var u ne Pe rso nneAnonyme = new { Prenom = " Nico " , Age = 30 }; Console . WriteLine ( unePersonneAnonyme . GetType () ) ;

245

CHAPITRE 23. NOTIONS AVANCES DE POO EN C# nous aurons dans la console le type de notre variable :
<> f__AnonymousType0 2[ System . String , System . Int32 ]

Ce qui est eectivement sans intrt pour nous ! Jusquici, les types anonymes peuvent sembler ne pas apporter dintrt, tant il est simple de dnir une classe Personne possdant une proprit Prenom et une proprit Age. Mais cela permet dutiliser ces classes comme des classes usage unique lorsque nous ne souhaitons pas nous encombrer dun chier possdant une classe qui va nous servir uniquement un seul endroit. Paresse, souci de clart du code. . . tout ceci est un peu ml dans la cration dun type anonyme. vous de lutiliser quand bon vous semble. Vous verrez plus tard que les types anonymes sont souvent utiliss dans les mthodes dextensions Linq. Nous en reparlerons ! noter que lorsque nous crons un tableau, par exemple :
1

string [] jours = new string [] { " Lundi " , " Mardi " , " Mercredi " , " Jeudi " , " Vendredi " , " Samedi " , " Dimanche " };

il est galement possible de lcrire ainsi :


1

string [] jours = new [] { " Lundi " , " Mardi " , " Mercredi " , " Jeudi " , " Vendredi " , " Samedi " , " Dimanche " };

cest--dire sans prciser le type du tableau aprs le new. Le compilateur dduit le type partir de linitialisation. Ce nest pas un type anonyme en soi, mais le principe de dduction est le mme.

En rsum
Une interface est un contrat que sengage respecter un objet. Il est possible dimplmenter plusieurs interfaces dans une classe. Une classe abstraite est une classe qui possde au moins une mthode ou proprit abstraite. Elle ne peut pas tre instancie. Une classe concrte drivant dune classe abstraite est oblige de substituer les membres abstraits. Il est possible de crer des types anonymes grce loprateur new suivi de la description des proprits de ce type. Les instances sont manipules grce au mot-cl var.

246

Chapitre

24
Dicult :

TP : programmation oriente objet

D
ct.

ans ce TP, nous allons essayer de mettre en pratique ce que nous avons appris en programmation oriente objet avec le C#. Il est dicile davoir un exercice faisant appel toutes les notions que nous avons apprises. Aussi, certaines sont laisses de

Le TP est intressant pour sentraner crer des classes, manipuler lhritage et se confronter des situations un peu direntes de la thorie. Le but de ce TP est de crer une mini application de gestion bancaire o nous pourrons grer des comptes pouvant eectuer des oprations bancaires entre eux. Ne vous attendez pas non plus refaire les applications de la Banque de France ; on est l pour sentraner !

247

CHAPITRE 24. TP : PROGRAMMATION ORIENTE OBJET

Instructions pour raliser le TP


Voici lnonc de la premire partie du TP : dans notre mini application bancaire tous les montants utiliss seront des dcimaux. Nous allons devoir grer des comptes bancaires. Un compte est compos : dun Solde (calcul, mais non modiable) ; dun Propritaire (nom du propritaire du compte) ; dune liste doprations internes permettant de garder lhistorique du compte, non accessible par les autres objets ; dune mthode permettant de Crediter() le compte, prenant une somme en paramtre ; dune mthode permettant de Crediter() le compte, prenant une somme et un compte en paramtres, crditant le compte et dbitant le compte pass en paramtres ; dune mthode permettant de Debiter() le compte, prenant une somme en paramtre ; dune mthode permettant de Debiter() le compte, prenant une somme et un compte bancaire en paramtres, dbitant le compte et crditant le compte pass en paramtre ; dune mthode qui permet dacher le rsum dun compte. Un compte courant est une sorte de compte et se compose : de tout ce qui compose un compte ; dun dcouvert autoris, non modiable, dni louverture du compte. Le rsum dun compte courant ache le solde, le propritaire, le dcouvert autoris ainsi que les oprations sur le compte. Un compte pargne entreprise est une sorte de compte et se compose : de tout ce qui compose un compte ; dun taux dabondement, dni louverture du compte en fonction de lanciennet du salari. Un taux est un double compris entre 0 et 1 (5% = 0.05). Le solde doit tenir compte du taux dabondement, mais labondement nest pas calcul lors des transactions car lentreprise verse labondement quand elle le souhaite. Le rsum dun compte pargne entreprise ache le solde, le propritaire, le taux dabondement ainsi que les oprations sur le compte. Une opration bancaire est compose : dun montant ; dun type de mouvement, crdit ou dbit. Voil ! Vous allez maintenant devoir crer une telle application avec les informations suivantes : le compte courant de Nicolas a un dcouvert autoris de 2000 ; le compte pargne entreprise de Nicolas a un taux de 2% ; le compte courant de Jrmie a un dcouvert autoris de 500 ; 248

INSTRUCTIONS POUR RALISER LE TP Nicolas touche son salaire de 100 (pas cher pay) ! il fait le plein de sa voiture : 50 ; il met de ct sur son compte pargne entreprise la coquette somme de 20 ; il reoit un cadeau de la banque de 100 , car il a ouvert son compte pargne pendant la priode promotionnelle ; il remet ses 20 sur son compte bancaire, car nalement, il ne se sent pas trop en scurit ! Jrmie achte un nouveau PC : 500 ; Jrmie rembourse ses dettes Nicolas : 200 . Lapplication doit indiquer les soldes de chacun de ces comptes :
Solde compte courant de Nicolas : 250 Solde compte pargne de Nicolas : 102 ,00 Solde compte courant de J r mie : -700

Ensuite, nous acherons le rsum du compte courant de Nicolas et de son compte pargne entreprise. Ce qui nous donnera :
R sum du compte de Nicolas ******************************************* Compte courant de Nicolas Solde : 250 D couvert autoris : 2000 Op rations : +100 -50 -20 +20 +200 ******************************************* R sum du compte pargne de Nicolas ##################################### Compte pargne entreprise de Nicolas Solde : 102 ,00 Taux : 0 ,02 Op rations : +20 +100 -20 #####################################

Bon, jai bien expliqu, ce nest pas si compliqu, sauf si bien sr vous navez pas lu ou pas compris les chapitres prcdents. Dans ce cas, nhsitez pas y rejeter un coup dil. Sinon, il sut de bien dcomposer tout en crant les classes les unes aprs les autres. 249

CHAPITRE 24. TP : PROGRAMMATION ORIENTE OBJET Bon courage. . . !

Correction
Ne regardez pas tout de suite la correction ! Prenez votre temps pour faire ce TP, car il est important de savoir bien manipuler les classes. Vous allez faire a trs rgulirement ; a doit devenir un rexe, il faut donc pratiquer ! Toujours est-il que voici ma correction. Le plus dur tait certainement de modliser correctement lapplication. Il y a plusieurs solutions bien sr pour crer ce petit programme, voici celle que je propose. Tout dabord, nous devons manipuler des comptes courants et des comptes pargne entreprise, qui sont des sortes de comptes. On en dduit quun compte courant hrite dun compte. De mme, un compte pargne entreprise hrite galement dun compte. Dailleurs, un compte a-t-il vraiment une raison dtre part entire ? Nous ne crons jamais de compte gnraliste , seulement des comptes spcialiss. Nous allons donc crer la classe Compte en tant que classe abstraite. Ce qui nous donnera les classes suivantes :
1 2 3 4 5 6 7 8 9 10 11

public abstract class Compte { } public class CompteCourant : Compte { } public class Co mp t eE pa r gn eE nt r ep ri se : Compte { }

Nous avons galement dit quun compte tait compos dune liste doprations qui possdent un montant et un type de mouvement. Le type de mouvement pouvant prendre deux valeurs, il parat logique dutiliser une numration :
1 2 3 4 5

public enum Mouvement { Credit , Debit }

La classe dopration tant :


1 2 3

public class Operation { public Mouvement TypeDeMouvement { get ; set ; }

250

CORRECTION
4 5

public decimal Montant { get ; set ; }

Voil pour la base. Ensuite, la classe Compte doit possder un nom de propritaire et un solde qui peut tre redni dans la classe CompteEpargneEntreprise. Ce solde est calcul partir dune liste doprations. Le solde est donc une proprit en lecture seule, virtuelle car ncessitant dtre rednie :
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21

public abstract class Compte { protected List < Operation > listeOperations ; public string Proprietaire { get ; set ; } public virtual decimal Solde { get { decimal total = 0 ; foreach ( Operation operation in listeOperations ) { if ( operation . TypeDeMouvement == Mouvement . Credit ) total += operation . Montant ; else total -= operation . Montant ; } return total ; } }

La liste des oprations est une variable membre, dclare en protected car nous allons en avoir besoin dans la classe CompteCourant qui en hrite, an dacher un rsum des oprations. La proprit Solde nest pas trs complique en soit, il sut de parcourir la liste des oprations et en fonction du type de mouvement, ajouter ou retrancher le montant. Comme cest une proprit en lecture seule, seul le get est dni. Noubliez pas que la liste doit tre initialise avant dtre utilise, sinon nous aurons une erreur. Le constructeur est un endroit appropri pour le faire :
1 2 3 4 5 6 7 8

public { // // //

abstract class Compte [...] [ Code pr c dent enlev pour plus de lisibilit ] [...]

public Compte () {

251

CHAPITRE 24. TP : PROGRAMMATION ORIENTE OBJET


9 10 11

listeOperations = new List < Operation >() ;

La classe doit ensuite possder des mthodes permettant de crditer ou de dbiter le compte, ce qui donne :
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30

public { // // //

abstract class Compte [...] [ Code pr c dent enlev pour plus de lisibilit ] [...]

public void Crediter ( decimal montant ) { Operation operation = new Operation { Montant = montant , TypeDeMouvement = Mouvement . Credit }; listeOperations . Add ( operation ) ; } public void Crediter ( decimal montant , Compte compte ) { Crediter ( montant ) ; compte . Debiter ( montant ) ; } public void Debiter ( decimal montant ) { Operation operation = new Operation { Montant = montant , TypeDeMouvement = Mouvement . Debit }; listeOperations . Add ( operation ) ; } public void Debiter ( decimal montant , Compte compte ) { Debiter ( montant ) ; compte . Crediter ( montant ) ; }

Le principe est de crer une opration et de lajouter la liste des oprations. Lastuce ici consiste rutiliser les mthodes de la classe pour crire les autres formes des mthodes, ce qui simplie le travail et facilitera les ventuelles futures oprations de maintenance. Enn, chaque classe drive de la classe Compte doit acher un rsum du compte. Nous pouvons donc forcer ces classes devoir implmenter cette mthode en utilisant une mthode abstraite :
1 2

public abstract class Compte {

252

CORRECTION
3 4 5 6 7 8

// [...] // [ Code pr c dent enlev pour plus de lisibilit ] // [...] } public abstract void AfficherResume () ;

Voil pour la classe Compte. En toute logique, cest elle qui contient le plus de mthodes an de factoriser le maximum de code commun dans la classe mre. Passons la classe CompteEpargneEntreprise, qui hrite de la classe Compte. Elle possde un taux dabondement qui est dni la cration du compte. Il est donc ici intressant de forcer le positionnement de ce taux lors de la cration de la classe, cest--dire en utilisant un constructeur avec un paramtre :
1 2 3 4 5 6 7 8 9

public class Co m pt eE pa r gn eE nt r ep ri se : Compte { private double tauxAbondement ; public C o m pt e Ep ar g ne En tr e pr is e ( double taux ) { tauxAbondement = taux ; }

Ce taux est utilis pour calculer le solde du compte en faisant la somme de toutes les oprations et en appliquant le taux ; ce qui revient appeler le calcul du solde de la classe mre et lui appliquer le taux. Nous substituons donc la proprit Solde et utilisons le calcul fait dans la classe Compte :
1 2 3 4 5 6 7 8 9 10 11 12 13 14

public { // // //

class Co m pt eE pa r gn eE nt r ep ri se : Compte [...] [ Code pr c dent enlev pour plus de lisibilit ] [...]

public override decimal Solde { get { return base . Solde * ( decimal ) ( 1 + } }

tauxAbondement ) ;

Rien de plus simple, en utilisant le mot-cl base pour appeler le solde de la classe mre. Vous noterez galement que nous avons eu besoin de caster le taux qui est un double an de pouvoir le multiplier un dcimal. Enn, cette classe se doit de fournir une implmentation de la mthode AfficherResume() :
1

public class Co m pt eE pa r gn eE nt r ep ri se : Compte

253

CHAPITRE 24. TP : PROGRAMMATION ORIENTE OBJET


2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24

// [...] // [ Code pr c dent enlev pour plus de lisibilit ] // [...] public override void AfficherResume () { Console . WriteLine ( " ##################################### "); Console . WriteLine ( " Compte pargne entreprise de " + Proprietaire ) ; Console . WriteLine ( " \ tSolde : " + Solde ) ; Console . WriteLine ( " \ tTaux : " + tauxAbondement ) ; Console . WriteLine ( " \ n \ nOp rations : " ) ; foreach ( Operation operation in listeOperations ) { if ( operation . TypeDeMouvement == Mouvement . Credit ) Console . Write ( " \ t + " ) ; else Console . Write ( " \t - " ) ; Console . WriteLine ( operation . Montant ) ; } Console . WriteLine ( " ##################################### "); }

Il sagit dune banale mthode dachage o nous parcourons la liste contenant les oprations et achons le montant en fonction du type de mouvement. Voil pour la classe CompteEpargneEntreprise. Il ne reste plus que la classe CompteCourant qui doit galement driver de Compte :
1 2 3

public class CompteCourant : Compte { }

Un compte courant doit possder un dcouvert autoris, dni la cration du compte courant. Nous utilisons comme avant un constructeur avec un paramtre :
1 2 3 4 5 6 7 8 9

public class CompteCourant : Compte { private decimal decouvertAutorise ; public CompteCourant ( decimal decouvert ) { decouvertAutorise = decouvert ; }

Il ne restera plus qu fournir une implmentation de la mthode dachage du rsum : 254

CORRECTION
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24

public { // // //

class CompteCourant : Compte [...] [ Code pr c dent enlev pour plus de lisibilit ] [...]

public override void AfficherResume () { Console . WriteLine ( " ******************************************* "); Console . WriteLine ( " Compte courant de " + Proprietaire ) ; Console . WriteLine ( " \ tSolde : " + Solde ) ; Console . WriteLine ( " \ tD couvert autoris : " + decouvertAutorise ) ; Console . WriteLine ( " \ n \ nOp rations : " ) ; foreach ( Operation operation in listeOperations ) { if ( operation . TypeDeMouvement == Mouvement . Credit ) Console . Write ( " \ t + " ) ; else Console . Write ( " \t - " ) ; Console . WriteLine ( operation . Montant ) ; } Console . WriteLine ( " ******************************************* "); }

Voil pour nos objets. Cela en fait un petit paquet. Mais ce nest pas ni, il faut maintenant crer des comptes et faire des oprations. Lnonc consiste faire les instanciations suivantes, depuis notre mthode Main() :
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15

CompteCourant compteNicolas = new CompteCourant ( 2000 ) { Proprietaire = " Nicolas " }; C o m p t e E p a r g n e En tr ep r is e compteEpargneNicolas = new C o m p t e E p a r gn eE n tr ep ri s e ( 0 . 02 ) { Proprietaire = " Nicolas " }; CompteCourant compteJeremie = new CompteCourant ( 500 ) { Proprietaire = " J r mie " }; compteNicolas . Crediter ( 100 ) ; compteNicolas . Debiter ( 50 ) ; co m p t e E p a rg n e Nicolas . Crediter ( 20 , compteNicolas ) ; co m p t e E p a rg n e Nicolas . Crediter ( 100 ) ; co m p t e E p a rg n e Nicolas . Debiter ( 20 , compteNicolas ) ; compteJeremie . Debiter ( 500 ) ; compteJeremie . Debiter ( 200 , compteNicolas ) ;

255

CHAPITRE 24. TP : PROGRAMMATION ORIENTE OBJET


16 17

18 19

Console . WriteLine ( " Solde compte courant de " + compteNicolas . Proprietaire + " : " + compteNicolas . Solde ) ; Console . WriteLine ( " Solde compte pargne de " + c o m p t e EpargneNicolas . Proprietaire + " : " + c o m p t e EpargneNicolas . Solde ) ; Console . WriteLine ( " Solde compte courant de " + compteJeremie . Proprietaire + " : " + compteJeremie . Solde ) ; Console . WriteLine ( " \ n " ) ;

Rien dextraordinaire. Il ne reste plus qu acher le rsum des deux comptes demands :
1 2 3 4 5 6 7

Console . WriteLine ( " R sum du compte de Nicolas " ) ; compteNicolas . AfficherResume () ; Console . WriteLine ( " \ n " ) ; Console . WriteLine ( " R sum du compte pargne de Nicolas " ) ; c o m p t e E p a rgneNicolas . AfficherResume () ; Console . WriteLine ( " \ n " ) ;

Nous en avons ni avec la premire partie du TP. Si vous avez bien compris comment construire des classes, comment les faire hriter entre elles et les interfaces, ce TP na pas d vous poser trop de problmes. Il existe bien sr direntes solutions pour rsoudre ce problme, de faon plus ou moins complexe.

Aller plus loin


Il y a plusieurs choses qui me drangent dans ce code. La premire est la rptition. Dans les deux mthodes AfficherResume() des classes respectives : CompteCourant et CompteEpargneEntreprise, on ache les oprations de la mme faon. Si jamais je dois modier ce code (bug ou volution), je devrais le faire dans les deux classes, au risque den oublier une. Il faut donc factoriser ce code. Il existe plusieurs solutions simples de factoriser ce code. La premire est dutiliser une mthode statique pour acher la liste des oprations. Cette mthode pourrait tre situe dans une classe utilitaire qui prend en paramtre la liste des oprations :
1 2 3 4 5 6 7 8 9 10 11

public static class Helper { public static void AfficheOperations ( List < Operation > operations ) { Console . WriteLine ( " \ n \ nOp rations : " ) ; foreach ( Operation operation in operations ) { if ( operation . TypeDeMouvement == Mouvement . Credit ) Console . Write ( " \ t + " ) ; else Console . Write ( " \t - " ) ;

256

ALLER PLUS LOIN


12 13 14 15

Console . WriteLine ( operation . Montant ) ;

Il ne reste plus qu utiliser cette mthode statique depuis nos mthodes AfficherResume(), par exemple pour le compte pargne entreprise :
1 2 3 4 5 6 7 8 9

public override void AfficherResume () { Console . WriteLine ( " # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # " ) ; Console . WriteLine ( " Compte pargne entreprise de " + Proprietaire ) ; Console . WriteLine ( " \ tSolde : " + Solde ) ; Console . WriteLine ( " \ tTaux : " + tauxAbondement ) ; Helper . AfficheOperations ( listeOperations ) ; Console . WriteLine ( " # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # " ) ; }

La deuxime solution est de crer la mthode AfficheOperations() dans la classe abstraite Compte, ce qui permet de saranchir de passer la liste en paramtre vu que la classe Compte la connat dj :
1 2 3 4 5 6 7 8 9 10 11 12

protected void AfficheOperations () { Console . WriteLine ( " \ n \ nOp rations : " ) ; foreach ( Operation operation in listeOperations ) { if ( operation . TypeDeMouvement == Mouvement . Credit ) Console . Write ( " \ t + " ) ; else Console . Write ( " \t - " ) ; Console . WriteLine ( operation . Montant ) ; } }

La mthode a tout intrt tre dclare en protected, an quelle puisse servir aux classes lles mais pas lextrieur. Elle sutilisera de cette faon, par exemple dans la classe CompteEpargneEntreprise :
1 2 3 4 5 6 7 8 9

public override void AfficherResume () { Console . WriteLine ( " # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # " ) ; Console . WriteLine ( " Compte pargne entreprise de " + Proprietaire ) ; Console . WriteLine ( " \ tSolde : " + Solde ) ; Console . WriteLine ( " \ tTaux : " + tauxAbondement ) ; Affic heOperations () ; Console . WriteLine ( " # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # " ) ; }

257

CHAPITRE 24. TP : PROGRAMMATION ORIENTE OBJET Ds que lon peut factoriser du code, il ne faut pas hsiter. Si nous avons demain besoin de crer un nouveau type de compte, nous serons ravis de pouvoir nous servir de mthodes toutes prtes nous simpliant le travail. Il pourrait tre intressant galement dencapsuler lenregistrement dune opration dans une mthode. a permettrait de moins se rpter (mme si ici, le code est vraiment petit) mais surtout de sparer la logique denregistrement dune opration an que cela soit plus facile ultrieurement modier, maintenir ou complexier. Par exemple, via une mthode :
1 2 3 4 5

private void EnregistrerOperation ( Mouvement typeDeMouvement , decimal montant ) { Operation operation = new Operation { Montant = montant , TypeDeMouvement = typeDeMouvement }; listeOperations . Add ( operation ) ; }

Cela permet galement de faire en sorte que le type de mouvement soit un paramtre de la mthode. Vous pouvez tlcharger tous les codes sources de cet exercice grce au code web suivant : Copier ce code Code web : 729727

Deuxime partie du TP
Nous voici maintenant dans la deuxime partie du TP. La banque souhaite proposer un nouveau type de compte, le livret ToutBnf. Dans cette banque, le livret ToutBnf fonctionne comme le compte pargne entreprise. Cest--dire quil accepte un taux en paramtres et applique ce taux au moment de la restitution du solde. La premire ide qui vient lesprit est de crer une classe LivretToutBenef qui hrite de CompteEpargneEntreprise. Mais ceci pose un problme si jamais le compte pargne entreprise doit voluer, et cest justement ce que le directeur de la banque vient de me coner. Donc, il vous interdit juste titre dhriter de ses fonctionnalits. Ce que vous allez donc faire ici, cest de considrer que le fait quun compte puisse faire des bnces soit en fait un comportement qui est fourni au moment o on instancie un compte. Il existe plusieurs comportements dont on doit fournir les implmentations : le comportement de bnce taux xe ; le comportement de bnce alatoire ; le comportement o il ny a aucun bnce. Chaque comportement est une classe qui respecte le contrat suivant :
1 2 3

public interface I Ca lcu lat eu rDe Be nef ice { decimal CalculeBenefice ( decimal solde ) ;

258

CORRECTION
4 5

double Taux { get ; }

crivez donc ces trois classes de comportement ainsi que le livret ToutBnf qui possde un taux xe de 2.75% et qui a t crdit une premire fois de 800 et une seconde fois de 200 . Achez enn son rsum qui devra tenir compte du taux du calculateur de bnce. Rcrivez ensuite la classe CompteCourant de manire ce quelle ait un comportement o il ny a pas de bnce. Enn, la classe CompteEpargneEntreprise subira une volution pour fonctionner avec un comportement de bnce alatoire (tir entre 0 et 1). Cest parti.

Correction
Ici cest un peu plus compliqu. Vous ntes sans doute pas compltement familiers avec la notion dinterface, aussi avant de vous donner la correction, je vais vous donner quelques pistes. Le fait davoir un comportement est nalement trs simple. Il sut davoir un membre priv dans la classe LivretToutBenef du type de linterface. Ce membre priv sera aect la valeur passe en paramtre du constructeur. Cest--dire :
1 2 3 4 5 6 7 8 9

public class LivretToutBenef : Compte { private IC al cul at eur DeB en efi ce calcu lateur DeBen efice ; public LivretToutBenef ( I Cal cu lat eur De Ben ef ice calculateur ) { c a l c u late urDeBe nefic e = calculateur ; }

Dsormais, les oprations devront se faire avec cette variable, calculateurDeBenefice. On aura galement besoin dinstancier au pralable le comportement voulu et de le passer en paramtre du constructeur. Retournez donc tenter de raliser ce TP avant de voir la suite de la correction ! Cest fait ? Alors voici ma correction. Nous avions donc cette interface impose :
1 2 3 4 5

public interface I Ca lcu la teu rDe Be nef ic e { decimal CalculeBenefice ( decimal solde ) ; double Taux { get ; } }

Nous avons besoin dcrire plusieurs classes qui implmentent cette interface. La premire est la classe de bnce taux xe :
1 2 3

public class BeneficeATauxFixe : I Ca lcu la teu rDe Be nef ic e { private double taux ;

259

CHAPITRE 24. TP : PROGRAMMATION ORIENTE OBJET


4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22

public BeneficeATauxFixe ( double tauxFixe ) { taux = tauxFixe ; } public decimal CalculeBenefice ( decimal solde ) { return solde * ( decimal ) ( 1 + taux ) ; } public double Taux { get { return taux ; } }

Nous avons dit quelle devait prendre un taux en paramtre ; le constructeur est lendroit indiqu pour cela. Ensuite, la mthode de calcul est trs simple, il sut dappliquer la formule au solde. Enn, la proprit retourne le taux. La classe suivante est la classe de bnce alatoire. L, pas besoin de paramtres, il suft de tirer le nombre alatoire dans le constructeur grce la mthode NextDouble(), ce qui donne :
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23

public class BeneficeAleatoire : I Ca lcu lat eu rDe Be nef ic e { private double taux ; private Random random ; public BeneficeAleatoire () { random = new Random () ; taux = random . NextDouble () ; } public decimal CalculeBenefice ( decimal solde ) { return solde * ( decimal ) ( 1 + taux ) ; } public double Taux { get { return taux ; } }

260

CORRECTION
24

Enn, il faudra crer la classe avec aucun bnce :


1 2 3 4 5 6 7 8 9 10 11 12 13 14 15

public class AucunBenefice : ICa lc ula te urD eBe ne fic e { public decimal CalculeBenefice ( decimal solde ) { return solde ; } public double Taux { get { return 0 ; } }

Rien de sorcier. L o a se complique un peu, cest pour la classe LivretToutBenef. Elle doit bien sr driver de la classe de base Compte et possder un membre priv de type ICalculateurDeBenefice :
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23

public class LivretToutBenef : Compte { private IC al cul at eur DeB en efi ce calcu lateur DeBen efice ; public LivretToutBenef ( I Cal cu lat eur De Ben ef ice calculateur ) { c a l c u late urDeBe nefic e = calculateur ; } public override decimal Solde { get { decimal solde = base . Solde ; return solde + ca lculat eurDe Benefi ce . CalculeBenefice ( solde ) ; } } public override void AfficherResume () { Console . WriteLine ( " ^^^^^^^^^^^^^^^^^ " ) ; Console . WriteLine ( " Livret ToutB n f de " + Proprietaire ); Console . WriteLine ( " \ tSolde : " + Solde ) ;

261

CHAPITRE 24. TP : PROGRAMMATION ORIENTE OBJET


24 25 26 27 28

Console . WriteLine ( " \ tTaux : " + c alcul ateurD eBenef ice . Taux ) ; AfficheOperations () ; Console . WriteLine ( " ^^^^^^^^^^^^^^^^^ " ) ;

Dans le constructeur, la variable est initialise avec un objet de type ICalculateurDeBenefice. Ensuite, pour calculer le solde, il sut dappeler la mthode CalculeBenefice avec le solde de base en paramtre. De mme, pour faire apparatre le taux dans le rsum, on pourra utiliser la proprit Taux de linterface. Il ne reste qu souscrire un Livret ToutBnf en lui passant un objet de bnce taux xe en paramtre du constructeur, et faire les oprations bancaires demandes. Ce qui donne :
1 2 3 4 5 6 7 8

I C a l c u l a t eur DeB en efi ce beneficeATauxFixe = new Benef iceATauxFixe ( 0 . 275 ) ; LivretToutBenef l ivr etT ou tBe ne fNi co las = new LivretToutBenef ( benef iceATauxFixe ) ; l i v r e t T o u tBe nef Ni col as . Crediter ( 800 ) ; l i v r e t T o u tBe nef Ni col as . Crediter ( 200 ) ; Console . WriteLine ( " R sum du livret ToutB n f " ) ; l i v r e t T o u tBe nef Ni col as . AfficherResume () ; Console . WriteLine ( " \ n " ) ;

Vous aurez donc :


R sum du livret ToutB n f ^^^^^ ^^^^^^^^^^^^ Livret ToutB n f de Solde : 2275 ,000 Taux : 0 ,275 Op rations : +800 +200 ^^^^^ ^^^^^^^^^^^^

Maintenant, nous devons adapter la classe CompteEpargneEntreprise pour quelle puisse fonctionner avec un comportement. Cela devient tout simple et se rapproche beaucoup du livret ToutBnf :
1 2 3 4 5 6

public class Co mp t eE pa r gn eE nt r ep ri se : Compte { private I Cal cul at eur De Ben efi ce calcu lateur DeBen efice ; public C om pt e Ep ar gn e En tr e pr is e ( IC alc ula te urD eB ene fi ce calculateur ) {

262

CORRECTION
7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28

c a l c u late urDeBe nefic e = calculateur ;

public override decimal Solde { get { decimal solde = base . Solde ; return solde + ca lculat eurDe Benefi ce . CalculeBenefice ( solde ) ; } } public override void AfficherResume () { Console . WriteLine ( " ##################################### "); Console . WriteLine ( " Compte pargne entreprise de " + Proprietaire ) ; Console . WriteLine ( " \ tSolde : " + Solde ) ; Console . WriteLine ( " \ tTaux : " + c alcula teurD eBenef ice . Taux ) ; Affic heOperations () ; Console . WriteLine ( " ##################################### "); }

Il faut maintenant changer linstanciation de lobjet CompteEpargneEntreprise en lui passant en paramtre un objet de type bnce alatoire :
1 2

IC a l c u l a t e u r D eB en efi ce beneficeAleatoire = new Benef iceAleatoire () ; C o m p t e E p a r g n e En tr ep r is e compteEpargneNicolas = new C o m p t e E p a r gn eE n tr ep ri s e ( beneficeAleatoire ) { Proprietaire = " Nicolas " };

Il reste le compte courant qui suit le mme principe :


1 2 3 4 5 6 7 8 9 10 11

public class CompteCourant : Compte { private decimal decouvertAutorise ; private IC al cul at eur DeB en efi ce calcu lateur DeBen efice ; public CompteCourant ( decimal decouvert , I C a l c ul ate ur DeB en efi ce calculateur ) { decou vertAutorise = decouvert ; c a l c u late urDeBe nefic e = calculateur ; }

263

CHAPITRE 24. TP : PROGRAMMATION ORIENTE OBJET


12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31

public override decimal Solde { get { decimal solde = base . Solde ; return solde + ca lculat eurDeB enefi ce . CalculeBenefice ( solde ) ; } } public override void AfficherResume () { Console . WriteLine ( " ******************************************* "); Console . WriteLine ( " Compte courant de " + Proprietaire ) ; Console . WriteLine ( " \ tSolde : " + Solde ) ; Console . WriteLine ( " \ tD couvert autoris : " + decouvertAutorise ) ; Console . WriteLine ( " \ tTaux : " + c alcul ateurD eBenef ice . Taux ) ; AfficheOperations () ; Console . WriteLine ( " ******************************************* "); }

Que lon pourra instancier avec un objet de type aucun bnce :


1 2

I C a l c u l a t eur DeB en efi ce aucunBenefice = new AucunBenefice () ; CompteCourant compteNicolas = new CompteCourant ( 2000 , aucunBenefice ) { Proprietaire = " Nicolas " };

Et voil ! Lavantage ici est davoir spar les responsabilits dans direntes classes. Si jamais nous crons un nouveau compte qui est rmunr grce un bnce taux xe, il sura de rutiliser ce comportement et le tour est jou. noter que les trois calculs de la proprit Solde sont identiques, il pourrait tre judicieux de le factoriser dans la classe mre Compte. Ceci implique que la classe mre possde elle-mme le membre protg du type de linterface. Voil pour ce TP. Jespre que vous aurez russi avec brio la cration de toutes les classes et que vous ne vous tes pas perdus dans les mots-cls. Vous verrez que vous aurez trs souvent besoin dcrire des classes dans ce genre an de crer une application. Cest un lment indispensable du C# quil est primordial de matriser. Nhsitez pas faire des variations sur ce TP ou crer dautres petits programmes simples vous permettant de vous entraner ! Vous pouvez tlcharger tous les codes sources de cet exercice grce au code web suivant : Copier ce code Code web : 817413
264

Chapitre

25
Dicult :

Mode de passage des paramtres une mthode

ans les chapitres prcdents, nous avons dcrit comment on pouvait passer des paramtres une mthode. Nous avons galement vu que les types du framework .NET pouvaient tre des types valeur ou des types rfrence. Ceci inuence la faon dont sont traits les paramtres dune mthode. Nous allons ici prciser un peu ce fonctionnement. Dans ce chapitre, je vais illustrer mes propos en utilisant des mthodes statiques crites dans la classe Program, gnre par Visual C# Express. Le but est de simplier lcriture et de ne pas sencombrer dobjets inutiles. videmment, tout ce que nous allons voir fonctionne galement de la mme faon avec les mthodes non statiques prsentes dans des objets.

265

CHAPITRE 25. MODE DE PASSAGE DES PARAMTRES UNE MTHODE

Passage de paramtres par valeur


Au tout dbut de ce livre, nous avons appel des mthodes en passant des types simples (int, string, etc.). Nous avons vu quil sagissait de types valeur qui possdent directement la valeur dans la variable. Lorsque nous passons des types valeur en paramtres dune mthode, nous utilisons un passage de paramtre par valeur. Dcortiquons lexemple suivant :
1 2 3 4 5 6 7 8 9 10 11 12

static void Main ( string [] args ) { int age = 30 ; Doubler ( age ) ; Console . WriteLine ( age ) ; } public static void Doubler ( int valeur ) { valeur = valeur * 2 ; Console . WriteLine ( valeur ) ; }

Nous dclarons dans la mthode Main() une variable age, de type entier, laquelle nous aectons la valeur 30. Nous appelons ensuite la mthode Doubler() en lui passant cette variable en paramtre. Ce quil se passe cest que le compilateur fait une copie de la valeur de la variable age pour la mettre dans la variable valeur de la mthode Doubler(). La variable valeur a une porte gale au corps de la mthode Doubler(). Nous modions ensuite la valeur de la variable valeur en la multipliant par deux. tant donn que la variable valeur a reu une copie de la variable age, cest--dire que le compilateur a dupliqu la valeur 30, le fait de modier la variable valeur ne change en rien la valeur de la variable age :
60 30

Nous passons 30 la mthode Doubler() qui calcule le double de la variable valeur. On ache 60. Lorsquon revient dans la mthode Main(), nous retrouvons la valeur initiale de la variable age. Elle na bien sr pas t modie, car la mthode Doubler() a travaill sur une copie. Rappelez-vous que ceci est possible car les types intgrs sont facilement copiables, car peu volus. 266

PASSAGE DE PARAMTRES EN MISE JOUR

Passage de paramtres en mise jour


Oui, mais si je veux modier la valeur ?

Pour pouvoir modier la valeur du paramtre pass, il faut indiquer que la variable est en mode mise jour . Cela se fait grce au mot-cl ref que nous pourrons utiliser ainsi :
1 2 3 4 5 6 7 8 9 10 11

static void Main ( string [] args ) { int age = 30 ; Doubler ( ref age ) ; Console . WriteLine ( age ) ; } public static void Doubler ( ref int valeur ) { valeur = valeur * 2 ; }

Comme on peut sen douter, ce code ache 60. Le mot-cl ref sutilise avant la dnition du paramtre dans la mthode. Cela implique quil soit galement utilis au moment dappeler la mthode. Vous aurez remarqu que le mot-cl utilis est ref et ressemble beaucoup au mot rfrence . Ce nest videmment pas un hasard ! En fait, avec ce mot-cl nous demandons au compilateur de passer en paramtre une rfrence vers la variable age plutt que den faire une copie. Ainsi, la mthode Doubler() rcupre une rfrence et la variable valeur rfrence alors la mme valeur que la variable age. Ceci implique que toute modication de la valeur rfrence provoque un changement sur la variable source puisque les variables rfrencent la mme valeur. Voil pourquoi la variable est modie aprs le passage dans la mthode Doubler(). Bien sr, il aurait tout fait t possible de faire en sorte que la mthode Doubler() renvoie un entier contenant la valeur passe en paramtre multiplie par 2 :
1 2 3 4 5 6 7 8 9 10 11

static void Main ( string [] args ) { int age = 30 ; age = Doubler ( age ) ; Console . WriteLine ( age ) ; } public static int Doubler ( int valeur ) { return valeur * 2 ; }

267

CHAPITRE 25. MODE DE PASSAGE DES PARAMTRES UNE MTHODE Cest ce que nous avons toujours fait auparavant. Voici maintenant une autre faon de faire qui peut tre bien utile quand il y a plus dune valeur renvoyer.

Passage des objets par rfrence


Cest aussi comme a que cela fonctionne pour les objets. Nous avons vu que les variables qui stockent des objets possdent en fait la rfrence de lobjet. Le fait de passer un objet une mthode quivaut passer la rfrence de lobjet en paramtre. Ainsi, cest comme si on utilisait le mot-cl ref implicitement. Le code suivant :
1 2 3 4 5 6 7 8 9 10 11

static void Main ( string [] args ) { Voiture voiture = new Voiture { Couleur = " Grise " }; Repeindre ( voiture ) ; Console . WriteLine ( voiture . Couleur ) ; } public static void Repeindre ( Voiture voiture ) { voiture . Couleur = " Bleue " ; }

va donc crer un objet Voiture et le passer en paramtre la mthode Repeindre(). Comme Voiture est un type rfrence, il ny a pas de duplication de lobjet et une rfrence est passe la mthode. Lorsque nous modions la proprit Couleur de la voiture, nous modions bien le mme objet que celui prsent dans la mthode Main() :
Bleue

Il est noter quand mme que la variable voiture de la mthode Repeindre est une copie de la variable voiture de la mthode Main(). Elles contiennent toutes les deux une rfrence vers lobjet de type Voiture. Cela veut dire que lon accde bien au mme objet, do le rsultat, mais que les deux variables sont indpendantes. Si nous modions directement la variable, avec par exemple :
1 2 3 4 5 6 7 8 9 10

static void Main ( string [] args ) { Voiture voiture = new Voiture { Couleur = " Grise " }; Repeindre ( voiture ) ; Console . WriteLine ( voiture . Couleur ) ; } public static void Repeindre ( Voiture voiture ) { voiture . Couleur = " Bleue " ;

268

PASSAGE DE PARAMTRES EN SORTIE


11 12

voiture = null ;

alors, ce code continuera fonctionner, car la variable voiture de la mthode Main() ne vaut pas null. Le fait de modier la variable de la mthode Repeindre, qui est une copie, naecte en rien la variable de la mthode Main(). Par contre, elle est aecte si nous utilisons le mot-cl ref. Par exemple le code suivant :
1 2 3 4 5 6 7 8 9 10 11 12

static void Main ( string [] args ) { Voiture voiture = new Voiture { Couleur = " Grise " }; Repeindre ( ref voiture ) ; Console . WriteLine ( voiture . Couleur ) ; } public static void Repeindre ( ref Voiture voiture ) { voiture . Couleur = " Bleue " ; voiture = null ; }

provoquera une erreur. En eet, cette fois-ci, cest bien la rfrence qui est passe nulle et pas une copie de la variable contenant la rfrence. . . Une dirence subtile !

Passage de paramtres en sortie


Enn, le dernier mode de passage est le passage de paramtres en sortie. Il permet de faire en sorte quune mthode force linitialisation dune variable et que lappelant rcupre la valeur initialise. Nous avons dj vu ce mode de passage quand nous avons tudi les conversions. Souvenez-vous de ce code :
1 2 3 4 5 6

string chaine = " 1234 " ; int nombre ; if ( int . TryParse ( chaine , out nombre ) ) Console . WriteLine ( nombre ) ; else Console . WriteLine ( " Conversion impossible " ) ;

La mthode TryParse permet de tenter la conversion dune chane. Elle renvoie vrai ou faux en fonction du rsultat de la conversion et met jour lentier qui est pass en paramtre en utilisant le mot-cl out. Si la conversion russit, alors lentier nombre est initialis avec la valeur de la conversion, calcule dans la mthode TryParse. Nous pouvons utiliser ensuite la variable nombre car le mot-cl out garantit que la variable sera initialise dans la mthode. En eet, si nous prenons lexemple suivant :
1

static void Main ( string [] args )

269

CHAPITRE 25. MODE DE PASSAGE DES PARAMTRES UNE MTHODE


2 3 4 5 6 7 8 9 10

int age = 30 ; int ageDouble ; Doubler ( age , out ageDouble ) ;

public static void Doubler ( int age , out int resultat ) { }

Nous aurons une erreur de compilation :


Le param tre de sortie resultat doit tre assign avant que le contr le quitte la m thode actuelle

En eet, il faut absolument que la variable resultat qui est marque en sortie ait une valeur avant de pouvoir sortir de la mthode. Nous pourrons corriger cet exemple avec :
1 2 3 4 5 6 7 8 9 10 11

static void Main ( string [] args ) { int age = 30 ; int ageDouble ; Doubler ( age , out ageDouble ) ; } public static void Doubler ( int age , out int resultat ) { resultat = age * 2 ; }

Aprs lappel de la mthode Doubler(), ageDouble vaudra donc 60. Oui, mais quel intrt par rapport un return normal ?

Ici, aucun. Cest pertinent quand nous souhaitons renvoyer plusieurs valeurs, comme cest le cas pour la mthode TryParse qui renvoie le rsultat de la conversion et si la conversion sest bien passe. Bien sr, si lon naime pas trop le mot-cl out, il est toujours possible de crer un objet contenant deux valeurs que lon retournera lappelant, par exemple :
1 2 3 4 5 6

public class Program { static void Main ( string [] args ) { string nombre = " 1234 " ; Resultat resultat = TryParse ( nombre ) ;

270

PASSAGE DE PARAMTRES EN SORTIE


7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26

if ( resultat . ConversionOk ) Console . WriteLine ( resultat . Valeur ) ;

public static Resultat TryParse ( string chaine ) { Resultat resultat = new Resultat () ; int valeur ; resultat . ConversionOk = int . TryParse ( chaine , out valeur ); resultat . Valeur = valeur ; return resultat ; } } public class Resultat { public bool ConversionOk { get ; set ; } public int Valeur { get ; set ; } }

Ici, notre mthode TryParse renvoie un objet Resultat qui contient les deux valeurs rsultantes de la conversion.

En rsum
Le type dune variable passe en paramtres dune mthode inuence la faon dont elle est traite. Un passage par valeur eectue une copie de la valeur de la variable et la met dans la variable de la mthode. Un passage par rfrence eectue une copie de la rfrence mais continue de pointer sur le mme objet. On utilise le mot-cl ref pour passer une variable de type valeur une mthode an de la modier.

271

CHAPITRE 25. MODE DE PASSAGE DES PARAMTRES UNE MTHODE

272

Chapitre

26
Dicult :

Les structures

ous allons aborder dans ce chapitre les structures, qui sont une nouvelle sorte dobjets que nous pouvons crer dans des applications C#. Les structures sont presque comme des classes. Elles permettent galement de crer des objets, possdent des variables ou proprits, des mthodes et mme un constructeur, mais avec quelques subtiles dirences. . . Dcouvrons-les ds prsent !

273

CHAPITRE 26. LES STRUCTURES

Une structure est presque une classe


Une des premires dirences entre la classe et la structure est la faon dont .NET gre ces deux objets. Nous avons vu que les classes taient des types rfrence. Les variables de type rfrence ne possdent donc pas la valeur de lobjet mais une rfrence vers cet objet en mmoire. La structure, quant elle, est un type valeur et contient donc directement la valeur de lobjet. Une autre dirence est que la structure, bien qutant un objet, ne peut pas utiliser les principes dhritage. On ne peut donc pas hriter dune structure et une structure ne peut pas hriter des comportements dun objet.

quoi sert une structure ?


Les structures vont tre utiles pour stocker de petits objets amens tre souvent manipuls, comme les int ou les bool que nous avons dj vus. La raison tient en un seul mot : performance. tant gres en mmoire diremment, les structures sont optimises pour amliorer les performances des petits objets. Comme il ny a pas de rfrence, on utilisera directement lobjet sans aller le chercher via sa rfrence. On gagne donc un peu de temps lorsquon a besoin de manipuler ces donnes. Cest tout fait pertinent pour des programmes o la vitesse est dterminante, comme les jeux vido. Donc dans ce cas, autant utiliser tout le temps des structures, non ?

Eh bien non, dj vous vous priveriez de tous les mcanismes dhritage que nous avons vus. Ensuite, si nous surchargeons trop la mmoire avec des structures, loptimisation prvue par .NET risque de se retourner contre nous et notre application pourrait tre plus lente que si nous avions utilis des classes. Dune manire gnrale, et moins de savoir exactement ce que vous faites ou davoir mesur les performances, vous allez utiliser plus gnralement les classes que les structures. Vous pouvez ce sujet lire les recommandations de Microsoft, disponibles grce au code web suivant : Documentation Microsoft . Code web : 520338
274

CRER UNE STRUCTURE

Crer une structure


Pour crer une structure, nous utiliserons le mot-cl struct, comme nous avions utilis le mot-cl class pour crer une classe :
1 2 3 4 5

public struct Personne { public string Prenom { get ; set ; } public int Age { get ; set ; } }

Pour instancier cette structure, nous pourrons utiliser le mot-cl new, comme pour les classes. La dirence est que la variable sera un type valeur, avec les consquences que ce type impose en matire de gestion en mmoire ou de passages par paramtres :
1 2

Personne nicolas = new Personne () { Prenom = " Nicolas " , Age = 30 }; Console . WriteLine ( nicolas . Prenom + " a " + nicolas . Age + " ans " );

Comme nous avons dit, il est impossible quune structure hrite dune autre structure ou dun objet ; sauf bien sr du fameux type de base object, pour qui cest automatique. Une structure hrite donc des quelques mthodes dObject (comme ToString()) que nous pouvons ventuellement spcialiser :
1 2 3 4 5 6 7 8 9 10

public struct Personne { public string Prenom { get ; set ; } public int Age { get ; set ; } public override string ToString () { return Prenom + " a " + Age + " ans " ; }

Et nous pourrons crire :


1 2

Personne nicolas = new Personne () { Prenom = " Nicolas " , Age = 30 }; Console . WriteLine ( nicolas . ToString () ) ;

Qui renverra :
Nicolas a 30 ans

Comme pour les classes, il est possible davoir des constructeurs sur une structure, lexception du constructeur par dfaut qui est interdit. Aussi le code suivant : 275

CHAPITRE 26. LES STRUCTURES


1 2 3 4 5 6

public struct Personne { public Personne () { } }

provoquera lerreur de compilation suivante :


Les structures ne peuvent pas contenir de constructeurs exempts de param tres explicites

Par contre, les autres formes des constructeurs sont possibles, comme :
1 2 3 4 5 6 7 8

public struct Personne { private int age ; public Personne ( int agePersonne ) { age = agePersonne ; } }

Ce constructeur sutilisera comme pour une classe :


1

Personne nicolas = new Personne ( 30 ) ;

Attention, si vous tentez dutiliser des proprits ou des mthodes dans le constructeur dune structure, vous allez avoir un problme ! Par exemple le code suivant :
1 2 3 4 5 6 7 8 9 10 11 12 13

public struct Personne { private int age ; public Personne ( int agePersonne ) { AffecteAge ( agePersonne ) ; } private void AffecteAge ( int agePersonne ) { age = agePersonne ; }

provoquera les erreurs de compilation suivantes :


L objet this ne peut pas tre utilis avant que tous ses champs soient assign s

276

CRER UNE STRUCTURE

Le champ M a P r em iereA pplica tion . Personne . age doit tre totalement assign avant que le contr le soit retourn l appelant

Alors quavec une classe, ce code serait tout fait correct. Pour corriger ceci, il faut absolument initialiser tous les champs avant de faire quoi que ce soit avec lobjet, comme lindique lerreur. Nous pourrons par exemple faire :
1 2 3 4 5 6 7 8 9 10 11 12 13 14

public struct Personne { private int age ; public Personne ( int agePersonne ) { age = 0 ; AffecteAge ( agePersonne ) ; } private void AffecteAge ( int agePersonne ) { age = agePersonne ; }

Ce qui peut sembler tout fait inutile dans ce cas-l. Mais comme le compilateur fait certaines vrications, il sera impossible de compiler un code de ce genre sans que toutes les variables soient initialises explicitement. Par contre, vous aurez un souci si vous utilisez des proprits automatiques. Si nous tentons de faire :
1 2 3 4 5 6 7 8

public struct Personne { public int Age { get ; set ; } public Personne ( int agePersonne ) { Age = agePersonne ; } }

nous nous retrouverons avec la mme erreur de compilation. Pour la corriger, il faudra appeler le constructeur par dfaut de la structure qui va permettre dinitialiser toutes les variables de la classe :
1 2 3 4 5 6

public struct Personne { public int Age { get ; set ; } public Personne ( int agePersonne ) : this () { Age = agePersonne ;

277

CHAPITRE 26. LES STRUCTURES


7 8

Cela se fait comme pour les classes, en utilisant le mot-cl this suivi de parenthses, qui permettra dappeler le constructeur par dfaut. Rappelez-vous que le constructeur par dfaut soccupe dinitialiser toutes les variables dune classe ou dune structure.

Passage de structures en paramtres


Comme il sagit dun type valeur, chaque fois que nous passerons une structure en paramtres dune mthode, une copie de lobjet sera faite. Ainsi, tout fait logiquement, le code suivant :
1 2 3 4 5 6 7 8 9 10 11

static void Main ( string [] args ) { Personne nicolas = new Personne () { Age = 30 }; FaitVieillir ( nicolas ) ; Console . WriteLine ( nicolas . Age ) ; } private static void FaitVieillir ( Personne personne ) { personne . Age ++; }

achera 30, bien que nous modions lge de la personne dans la mthode. Comme nous lavons dj vu, la mthode travaille sur une copie de la structure. Cela veut bien sr dire que si nous souhaitons modier une structure partir dune mthode, nous devrons utiliser le mot-cl ref :
1 2 3 4 5 6 7 8 9 10 11

static void Main ( string [] args ) { Personne nicolas = new Personne () { Age = 30 }; FaitVieillir ( ref nicolas ) ; Console . WriteLine ( nicolas . Age ) ; } private static void FaitVieillir ( ref Personne personne ) { personne . Age ++; }

Ceci vaut pour tous les types valeur. Prenez quand mme garde. Si la structure est trs grosse, le fait den faire une copie chaque utilisation de mthode risque dtre particulirement chronophage et pourra perturber les performances de votre application. 278

DAUTRES STRUCTURES ?

Dautres structures ?
Vous laurez peut-tre devin, mais les entiers que nous avons dj vus et que nous utilisions grce au mot-cl int sont en fait des structures. tant trs souvent utiliss et nayant pas non plus normment de choses stocker, ils sont crs en tant que structures et sont optimiss par .NET pour que nos applications sexcutent de faon optimale ; ce qui est un choix tout fait pertinent. Cest le cas galement pour les bool, les double, etc. noter en revanche que dautres objets, comme la classe String, sont bel et bien des classes. Dune manire gnrale, vous allez crer peu de structures en tant que dbutant. Il sera plus judicieux de crer des classes ds que vous en avez besoin. En eet, plus vos objets sont gros et plus ils auront intrt tre des classes pour viter dtre recopis chaque utilisation. Lutilisation de structures pourra se rvler pertinente dans des situations bien prcises, mais en gnral, il faut bien matriser les rouages du framework .NET pour que les bnces de leur utilisation se fassent ressentir. Dans tous les cas, il sera important de mesurer (avec par exemple des outils de prolage) le gain de temps avant de mettre des structures partout.

En rsum
Les structures sont des types valeur qui sont optimiss par le framework .NET. Une structure est un objet qui ressemble beaucoup une classe, mais qui possde des restrictions. Les structures possdent des proprits, des mthodes, des constructeurs, etc. Il nest pas possible dutiliser lhritage avec les structures.

279

CHAPITRE 26. LES STRUCTURES

280

Chapitre

27
Dicult :

Les gnriques

es gnriques sont une fonctionnalit du framework .NET apparus avec la version 2. Vous vous en souvenez peut-tre, nous avons cit le mot dans le chapitre sur les tableaux et les listes. Ils permettent de crer des mthodes ou des classes qui sont indpendantes dun type. Il est trs important de connatre leur fonctionnement car cest un mcanisme cl qui permet de faire beaucoup de choses, notamment en termes de rutilisabilit et damlioration des performances. Noubliez pas vos tubes daspirine et voyons prsent de quoi il retourne !

281

CHAPITRE 27. LES GNRIQUES

Quest-ce que les gnriques ?


Avec les gnriques, vous pouvez crer des mthodes ou des classes qui sont indpendantes dun type. On les appellera des mthodes gnriques et des types gnriques. Nous en avons dj utilis, rappelez-vous, avec la liste. La liste est une classe comme nous en avons dj vu plein, sauf quelle a la capacit dtre utilise avec nimporte quel autre type, comme les entiers, les chanes de caractres, les voitures. . . Cela permet dviter de devoir crer une classe ListeInt, une classe ListeString, une classe ListeVoiture, etc. On pourra utiliser cette classe avec tous les types grce aux chevrons :
1 2 3

List < string > listeChaine = new List < string >() ; List < int > listeEntier = new List < int >() ; List < Voiture > listeVoiture = new List < Voiture >() ;

Nous indiquons entre les chevrons le type qui sera utilis avec le type gnrique. Oui mais, si nous voulons pouvoir mettre nimporte quel type dobjet dans une liste, il surait de crer une ListeObject ? Puisque tous les objets drivent dobject. . . En fait, cest le choix qui avait t fait dans la toute premire version de .NET. On utilisait lobjet ArrayList qui possde une mthode Add prenant en paramtre un object. Cela fonctionne. Sauf que nous nous trouvions face des limitations. Premirement, nous pouvions mlanger nimporte quel type dobjet dans la liste, des entiers, des voitures, des chiens, etc. Cela devenait une classe fourre-tout et nous ne savions jamais ce quil y avait dans la liste. Deuximement, mme si nous savions quil ny avait que des entiers dans la liste, nous tions obligs de les traiter en tant quobject et donc dutiliser le boxing et lunboxing pour mettre les objets dans la liste ou pour les rcuprer. Cela engendrait donc confusion et perte de performance. Grce aux gnriques, il devenait donc possible de crer des listes de nimporte quel type avec la garantie de savoir exactement quel type nous allions rcuprer dans la liste.

Les types gnriques du framework .NET


Le framework .NET possde beaucoup de classes et dinterfaces gnriques, notamment dans lespace de nom System.Collections.Generic. La liste est la classe gnrique que vous utiliserez srement le plus. Mais beaucoup dautres sont votre disposition. Citons par exemple la classe Queue<> qui permet de grer une le dattente style FIFO 1 :
1. Acronyme de lexpression anglaise First In, First Out : premier entr, premier sorti.

282

CRER UNE MTHODE GNRIQUE


1 2 3 4 5

Queue < int > file = new Queue < int >() ; file . Enqueue ( 3 ) ; file . Enqueue ( 1 ) ; int valeur = file . Dequeue () ; // valeur contient 3 valeur = file . Dequeue () ; // valeur contient 1

Citons encore le dictionnaire dlments qui est une espce dannuaire o lon accde aux lments grce une cl :
1 2 3 4 5

Dictionary < string , Personne > annuaire = new Dictionary < string , Personne >() ; annuaire . Add ( " 06 01 02 03 04 " , new Personne { Prenom = " Nicolas " }) ; annuaire . Add ( " 06 06 06 06 06 " , new Personne { Prenom = " Jeremie " }) ; Personne p = annuaire [ " 06 06 06 06 06 " ]; // p contient la propri t Prenom valant Jeremie

Loin de moi lide de vous numrer toutes les collections gnriques du framework .NET ; le but est de vous montrer rapidement quil existe beaucoup de classes gnriques dans le framework .NET.

Crer une mthode gnrique


Nous commenons cerner lintrt des gnriques. Sachez quil est bien sr possible de crer vos propres classes gnriques ou vos propres mthodes. Commenons par les mthodes, a sera plus simple. Il est globalement intressant dutiliser un type gnrique partout o nous pourrions avoir un object. Dans la premire partie, nous avions cr une mthode AfficheRepresentation() qui prenait un objet en paramtre, ce qui pourrait tre :
1 2 3 4 5 6 7 8 9

public static class Afficheur { public static void Affiche ( object o ) { Console . WriteLine ( " Afficheur d ' objet : " ) ; Console . WriteLine ( " \ tType : " + o . GetType () ) ; Console . WriteLine ( " \ tRepr sentation : " + o . ToString () ) ; } }

Nous avons ici utilis une classe statique permettant dacher le type dun objet et sa reprsentation. Nous pouvons lutiliser ainsi :
1 2

int i = 5 ; double d = 9 . 5 ;

283

CHAPITRE 27. LES GNRIQUES


3 4 5 6 7 8 9

string s = " abcd " ; Voiture v = new Voiture () ; Afficheur . Affiche ( i ) ; Afficheur . Affiche ( d ) ; Afficheur . Affiche ( s ) ; Afficheur . Affiche ( v ) ;

Rappelez-vous, chaque fois quon passe dans cette mthode, lobjet est box en type object quand il sagit dun type valeur. Nous pouvons amliorer cette mthode en crant une mthode gnrique. Regardons ce code :
1 2 3 4 5 6 7 8 9

public static class Afficheur { public static void Affiche <T >( T a ) { Console . WriteLine ( " Afficheur d ' objet : " ) ; Console . WriteLine ( " \ tType : " + a . GetType () ) ; Console . WriteLine ( " \ tRepr sentation : " + a . ToString () ) ; } }

Cette mthode fait exactement la mme chose mais avec les gnriques. Dans un premier temps, la mthode annonce quelle va utiliser un type gnrique reprsent par la lettre T entre chevrons. Il est conventionnel que les types gnriques soient utiliss avec T .

Cela signie que tout type utilis dans cette mthode, dclar avec T, sera du type pass la mthode. Ainsi, la variable a est du type gnrique qui sera prcis lors de lappel cette mthode. Comme a est un objet, nous pouvons appeler la mthode GetType() et la mthode ToString() sur cet objet. Pour acher un objet, nous pourrons faire :
1 2 3 4 5 6 7 8 9

int i = 5 ; double d = 9 . 5 ; string s = " abcd " ; Voiture v = new Voiture () ; Afficheur . Affiche < int >( i ) ; Afficheur . Affiche < double >( d ) ; Afficheur . Affiche < string >( s ) ; Afficheur . Affiche < Voiture >( v ) ;

284

CRER UNE MTHODE GNRIQUE Dans le premier appel, nous indiquons que nous souhaitons acher i dont le type gnrique est int. Tout se passe comme si le CLR crait la surcharge de la mthode Affiche, prenant un entier en paramtre :
1 2 3 4 5 6

public static void Affiche ( int a ) { Console . WriteLine ( " Afficheur d ' objet : " ) ; Console . WriteLine ( " \ tType : " + a . GetType () ) ; Console . WriteLine ( " \ tRepr sentation : " + a . ToString () ) ; }

De mme pour lachage suivant, o lon indique le type double entre les chevrons. Cest comme si le CLR crait la surcharge prenant un double en paramtre :
1 2 3 4 5 6

public static void Affiche ( double a ) { Console . WriteLine ( " Afficheur d ' objet : " ) ; Console . WriteLine ( " \ tType : " + a . GetType () ) ; Console . WriteLine ( " \ tRepr sentation : " + a . ToString () ) ; }

Et ceci pour tous les types utiliss, savoir ici int, double, string et Voiture. noter que dans cet exemple, nous pouvons remplacer les quatre lignes suivantes :
1 2 3 4

Afficheur . Affiche < int >( i ) ; Afficheur . Affiche < double >( d ) ; Afficheur . Affiche < string >( s ) ; Afficheur . Affiche < Voiture >( v ) ;

par :
1 2 3 4

Afficheur . Affiche ( i ) ; Afficheur . Affiche ( d ) ; Afficheur . Affiche ( s ) ; Afficheur . Affiche ( v ) ;

En eet, nul besoin de prciser quel type nous souhaitons traiter ici, le compilateur est assez malin pour le dduire du type de la variable. La variable i tant un entier, il est obligatoire que le type gnrique soit un entier. Il est donc facultatif ici de le prciser. Une fois quil est prcis entre les chevrons, le type gnrique sutilise dans la mthode comme nimporte quel autre type. Nous pouvons avoir autant de paramtres gnriques que nous le voulons dans les paramtres et utiliser le type gnrique dans le corps de la mthode. Par exemple, la mthode suivante :
1 2 3 4 5 6

public static void Echanger <T >( ref T t1 , ref T t2 ) { T temp = t1 ; t1 = t2 ; t2 = temp ; }

285

CHAPITRE 27. LES GNRIQUES permet dchanger le contenu de deux variables entre elles. Cest donc une mthode gnrique puisquelle prcise entre les chevrons que nous pourrons utiliser le type T. En paramtres de la mthode, nous passons deux variables de types gnriques. Dans le corps de la mthode, nous crons une variable du type gnrique qui sert de mmoire temporaire puis nous changeons les rfrences des deux variables. Nous pourrons utiliser cette mthode ainsi :
1 2 3 4 5 6 7 8 9 10 11

int i = 5 ; int j = 10 ; Echanger ( ref i , ref j ) ; Console . WriteLine ( i ) ; Console . WriteLine ( j ) ; Voiture v1 = new Voiture { Couleur = " Rouge " }; Voiture v2 = new Voiture { Couleur = " Verte " }; Echanger ( ref v1 , ref v2 ) ; Console . WriteLine ( v1 . Couleur ) ; Console . WriteLine ( v2 . Couleur ) ;

Qui donnera :
10 5 Verte Rouge

Il est bien sr possible de crer des mthodes prenant en paramtres plusieurs types gnriques dirents. Il sut alors de prciser autant de types dirents entre les chevrons quil y a de types gnriques dirents :
1 2 3 4 5 6 7 8 9 10 11 12 13

static void Main ( string [] args ) { int i = 5 ; int j = 5 ; double d = 9 . 5 ; Console . WriteLine ( EstEgal (i , j ) ) ; Console . WriteLine ( EstEgal (i , d ) ) ;

} public static bool EstEgal <T , U >( T t , U u ) { return t . Equals ( u ) ; }

Ici, la methode EstEgal() prend en paramtres deux types potentiellement dirents. Nous lappelons une premire fois avec deux entiers et ensuite avec un entier et un double. 286

CRER UNE CLASSE GNRIQUE

Crer une classe gnrique


Une classe gnrique fonctionne comme pour les mthodes. Cest une classe o nous pouvons indiquer de 1 N types gnriques. Cest comme cela que fonctionne la liste que nous avons dj beaucoup manipule. En fait, la liste nest quune espce de tableau volu. Nous pourrions trs bien imaginer crer notre propre liste sur ce principe, sachant que cest compltement absurde, car elle sera forcment moins bien que cette classe, mais cest pour ltude. Le principe est davoir un tableau plus ou moins dynamique qui grossit si jamais le nombre dlments devient trop grand pour sa capacit. Pour dclarer une classe gnrique, nous utiliserons nouveau les chevrons la n de la ligne qui dclare la classe :
1 2 3

public class MaListeGenerique <T > { }

Nous allons raliser une implmentation toute basique de cette classe histoire de voir un peu quoi ressemble une classe gnrique. Cette classe na dintrt que pour tudier les gnriques, vous lui prfrerez videmment la classe List<> du framework .NET. Nous avons besoin de trois variables prives. La capacit de la liste, le nombre dlments dans la liste et le tableau gnrique.
1 2 3 4 5 6 7 8 9 10 11 12 13

public class MaListeGenerique <T > { private int capacite ; private int nbElements ; private T [] tableau ; public MaListeGenerique () { capacite = 10 ; nbElements = 0 ; tableau = new T [ capacite ]; }

Remarquez la dclaration du tableau. Elle utilise le type gnrique. Concrtement, cela veut dire que quand nous utiliserons la liste avec un entier, nous aurons un tableau dentiers. Lorsque nous utiliserons la liste avec un objet Voiture, nous aurons un tableau de Voiture, etc. Nous initialisons ces variables membres dans le constructeur, en dcidant compltement arbitrairement que la capacit par dfaut de notre liste est de 10 lments. La dernire ligne instancie le tableau en lui indiquant sa taille. Il reste implmenter lajout dans la liste :
1

public class MaListeGenerique <T >

287

CHAPITRE 27. LES GNRIQUES


2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20

[ Code enlev pour plus de clart ] public void Ajouter ( T element ) { if ( nbElements >= capacite ) { capacite *= 2 ; T [] copieTableau = new T [ capacite ]; for ( int i = 0 ; i < tableau . Length ; i ++) { copieTableau [ i ] = tableau [ i ]; } tableau = copieTableau ; } tableau [ nbElements ] = element ; nbElements ++; }

Il sagit simplement de mettre la valeur que lon souhaite ajouter lemplacement adquat dans le tableau. Nous le mettons en dernire position, cest--dire lemplacement correspondant au nombre dlments. Au dbut, nous avons commenc par vrier si le nombre dlments tait suprieur la capacit du tableau. Si cest le cas, alors nous devons augmenter la capacit du tableau. Jai ici dcid encore compltement arbitrairement que je doublais la capacit. Il ne reste plus qu crer un nouveau tableau de cette nouvelle capacit et copier les lments du premier tableau dans celui-ci. Vous aurez not que le paramtre de la mthode Ajouter est bien du type gnrique. Pour le plaisir, rajoutons enn une mthode permettant de rcuprer un lment dindice donn :
1 2 3 4 5 6 7 8 9

public class MaListeGenerique <T > { [ Code enlev pour plus de clart ] public T ObtenirElement ( int indice ) { return tableau [ indice ]; }

Il sagit juste daccder au tableau pour renvoyer la valeur lindice concern. Llment intressant ici est de constater que le type de retour de la mthode est bien du type gnrique. Cette liste peut sutiliser de la manire suivante :
1 2

MaListeGenerique < int > maListe = new MaListeGenerique < int >() ; maListe . Ajouter ( 25 ) ;

288

LA VALEUR PAR DFAUT DUN TYPE GNRIQUE


3 4 5 6 7 8 9 10 11 12 13

maListe . Ajouter ( 30 ) ; maListe . Ajouter ( 5 ) ; Console . WriteLine ( maListe . ObtenirElement ( 0 ) ) ; Console . WriteLine ( maListe . ObtenirElement ( 1 ) ) ; Console . WriteLine ( maListe . ObtenirElement ( 2 ) ) ; for ( int i = 0 ; i < 30 ; i ++) { maListe . Ajouter ( i ) ; }

Ici, nous utilisons la liste avec un entier, mais elle fonctionnerait tout aussi bien avec un autre type. Nhsitez pas passer en debug dans la mthode Ajouter() pour observer ce qui se passe exactement lors de laugmentation de capacit. Voil comment on cre une classe gnrique ! Une fois quon a compris que le type gnrique sutilise comme nimporte quel autre type, cela devient assez facile. Rappelez-vous, toute classe qui manipule des object est susceptible dtre amliore en utilisant les gnriques.

La valeur par dfaut dun type gnrique


Vous aurez remarqu dans limplmentation de la liste du dessus que si nous essayons dobtenir un lment du tableau un indice qui nexiste pas, nous aurons une erreur. Ce comportement est une bonne chose, car il est important de grer les cas aux limites. En loccurrence ici, on dlgue au tableau la gestion du cas limite. On pourrait envisager de grer nous-mmes ce cas limite en achant un message et en renvoyant une valeur nulle, mais ceci pose un problme. Pour un objet Voiture, qui est un type rfrence, il est tout fait pertinent davoir null ; pour un int, qui est un type valeur, a na pas de sens. Cest l quintervient le mot-cl default. Comme son nom lindique, il renvoie la valeur par dfaut du type. Pour un type rfrence, cest null ; pour un type valeur cest 0. Ce qui donnerait :
1 2 3 4 5 6 7 8 9

public T ObtenirElement ( int indice ) { if ( indice < 0 || indice >= nbElements ) { Console . WriteLine ( "L ' indice n ' est pas bon " ) ; return default ( T ) ; } return tableau [ indice ]; }

289

CHAPITRE 27. LES GNRIQUES

Les interfaces gnriques


Les interfaces peuvent aussi tre gnriques. Dailleurs, a me fait penser que plus haut, je vous ai indiqu quun certain code ntait pas trs esthtique et que jen parlerai plus tard. . . Le moment est venu ! Vous ne vous en souvenez plus ? Petit rappel pour les tourdis : il sagissait du chapitre sur les interfaces o nous avions implment linterface IComparable. Nous souhaitions comparer des voitures entre elles et nous avions obtenu le code suivant :
1 2 3 4 5 6 7 8 9 10 11 12

public class Voiture : IComparable { public string Couleur { get ; set ; } public string Marque { get ; set ; } public int Vitesse { get ; set ; } public int CompareTo ( object obj ) { Voiture voiture = ( Voiture ) obj ; return Vitesse . CompareTo ( voiture . Vitesse ) ; }

Je souhaite pouvoir comparer des voitures entre elles, mais le framework .NET me fournit un object en paramtres de la mthode CompareTo(). Quelle ide ! Comme si je voulais comparer des voitures avec des chats ou des chiens. Cela me force en plus faire un cast. Pourquoi il ne me passe pas directement une Voiture en paramtre ? Vous en avez lintuition ? Un object ! Oui, mais cest un peu lourd manier. . . et je ne parle pas des performances ! Cest l o les gnriques vont voler notre secours. Linterface IComparable date de la premire version du framework .NET. Le C# ne possdait pas encore les types gnriques. Depuis leur apparition, il est possible dimplmenter la version gnrique de cette interface. Pour cela, nous faisons suivre linterface du type que nous souhaitons utiliser entre les chevrons. Cela donne :
1 2 3 4 5 6 7 8 9 10 11

public class Voiture : IComparable < Voiture > { public string Couleur { get ; set ; } public string Marque { get ; set ; } public int Vitesse { get ; set ; } public int CompareTo ( Voiture obj ) { return Vitesse . CompareTo ( obj . Vitesse ) ; }

290

LES RESTRICTIONS SUR LES TYPES GNRIQUES Nous devons toujours implmenter la mthode CompareTo() sauf que nous avons dsormais un objet Voiture en paramtre, ce qui nous vite de le caster.

Les restrictions sur les types gnriques


Une mthode ou une classe gnrique cest bien, mais peut-tre voulons-nous quelles ne fonctionnent pas avec tous les types. Aussi, le C# permet de dnir des restrictions sur les types gnriques. Pour ce faire, on utilise le mot-cl where. Il existe six types de restrictions : Contrainte where T : struct where T : class where T : new() where T : IMonInterface where T : MaClasse where T1 : T2 Description Le type gnrique doit tre un type valeur Le type gnrique doit tre un type rfrence Le type gnrique doit possder un constructeur par dfaut Le type gnrique doit implmenter linterface IMonInterface Le type gnrique doit driver de la classe MaClasse Le type gnrique doit driver du type gnrique T2

Par exemple, nous pouvons dnir une restriction sur une mthode gnrique an quelle naccepte en type gnrique que des types qui implmentent une interface. Soit linterface suivante :
1 2 3 4 5

public interface IVolant { void DeplierLesAiles () ; void Voler () ; }

qui est implmente par deux objets :


1 2 3 4 5 6 7 8 9 10 11 12 13 14 15

public class Avion : IVolant { public void DeplierLesAiles () { Console . WriteLine ( " Je d plie mes ailes m caniques " ) ; } public void Voler () { Console . WriteLine ( "J ' allume le moteur " ) ; }

public class Oiseau : IVolant {

291

CHAPITRE 27. LES GNRIQUES


16 17 18 19 20 21 22 23 24 25

public void DeplierLesAiles () { Console . WriteLine ( " Je d plie mes ailes d ' oiseau " ) ; } public void Voler () { Console . WriteLine ( " Je bas des ailes " ) ; }

Nous pouvons crer une mthode gnrique qui soccupe dinstancier ces objets et dappeler les mthodes de linterface :
1 2 3 4 5 6 7

public static T Creer <T >() where T : IVolant , new () { T t = new T () ; t . DeplierLesAiles () ; t . Voler () ; return t ; }

Ici, la restriction porte sur deux niveaux. Il faut dans un premier temps que le type gnrique implmente linterface IVolant. En outre, il faut quil possde un constructeur, bref quil soit instanciable. Nous pouvons donc utiliser cette mthode de cette faon :
1 2

Oiseau oiseau = Creer < Oiseau >() ; Avion a380 = Creer < Avion >() ;

Nous appelons la mthode Crer() avec le type gnrique Oiseau, qui implmente bien IVolant et qui est aussi instanciable. Grce cela, nous pouvons utiliser loprateur new pour crer notre type gnrique, appeler les mthodes de linterface et renvoyer linstance. Ce qui donne :
Je d plie mes ailes d oiseau Je bas des ailes Je d plie mes ailes m caniques J allume le moteur

Si nous tentons dutiliser la mthode avec un type qui nimplmente pas linterface IVolant, comme :
1

Voiture v = Creer < Voiture >() ;

Nous aurons lerreur de compilation suivante :


Le type M aPrem iereA pplica tion . Voiture ne peut pas tre utilis comme param tre de type T dans le type ou la m thode g n rique Ma Premi ereApp licat ion . Program . Creer <T >() . Il n y a

292

LES RESTRICTIONS SUR LES TYPES GNRIQUES


pas de conversion de r f rence implicite de M a P r e m i e r e Appl icatio n . Voiture en MaP remie reAppl icati on . IVolant .

Globalement, il nous dit que lobjet Voiture nimplmente pas IVolant. Oui, mais dans ce cas, plutt que dutiliser une mthode gnrique, pourquoi la mthode ne renvoie pas IVolant ? Cest une judicieuse remarque, mais elle implique quelques modications de code. En eet, il faudrait indiquer quel type instancier. Nous pourrions par exemple faire :
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24

public enum TypeDeVolant { Oiseau , Avion } public static IVolant Creer ( TypeDeVolant type ) { IVolant volant ; switch ( type ) { case TypeDeVolant . Oiseau : volant = new Oiseau () ; break ; case TypeDeVolant . Avion : volant = new Avion () ; break ; default : return null ; } volant . DeplierLesAiles () ; volant . Voler () ; return volant ; }

Et instancier nos objets de cette faon :


1 2

Oiseau oiseau = ( Oiseau ) Creer ( TypeDeVolant . Oiseau ) ; Avion a380 = ( Avion ) Creer ( TypeDeVolant . Avion ) ;

Ce qui complique un peu les choses et rajoute des casts dont on pourrait volontiers se passer. De plus, si nous crons un nouvel objet qui implmente cette interface, il faudrait tout modier. Avouez quavec les types gnriques, cest quand mme plus propre ! Nous pouvons bien sr avoir des restrictions sur les types gnriques dune classe. 293

CHAPITRE 27. LES GNRIQUES Pour le montrer, nous allons crer une classe dont lobjectif est de disposer de types valeur qui pourraient ne pas avoir de valeur. Pour les types rfrence, il sut dutiliser le mot-cl null. Mais pour les types valeur comme les entiers, nous navons rien pour indiquer que ceux-ci nont pas de valeur. Par exemple :
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24

public class TypeValeurNull <T > where T : struct { private bool aUneValeur ; public bool AUneValeur { get { return aUneValeur ; } } private T valeur ; public T Valeur { get { if ( aUneValeur ) return valeur ; throw new I n v al i d O p e r a t i on E x c e p t i o n () ; } set { aUneValeur = true ; valeur = value ; } }

Ici, nous utilisons une classe possdant un type gnrique qui sera un type valeur, grce la condition where T : struct. Cette classe encapsule le type gnrique pour indiquer avec un boolen si le type a une valeur ou pas. Ne faites pas attention la ligne :
1

throw new I n v al i d O p e r a t i o nE x c e p t i o n () ;

qui permet juste de renvoyer une erreur, nous tudierons les exceptions un peu plus loin. Elle pourra sutiliser ainsi :
1 2 3 4 5 6 7 8 9 10

TypeValeurNull < int > entier = new TypeValeurNull < int >() ; if (! entier . AUneValeur ) { Console . WriteLine ( "l ' entier n ' a pas de valeur " ) ; } entier . Valeur = 5 ; if ( entier . AUneValeur ) { Console . WriteLine ( " Valeur de l ' entier : " + entier . Valeur ) ; }

294

LES TYPES NULLABLES Et si nous souhaitons faire de mme pour un autre type valeur, il ny a rien coder de plus :
1

TypeValeurNull < double > valeur = new TypeValeurNull < double >() ;

Cest quand mme super pratique comme classe ! ! Mais ne rvons pas, cette ide ne vient pas de moi. Cest en fait une fonctionnalit du framework .NET : les types nullables.

Les types nullables


En fait, la classe que nous avons vue au-dessus existe dj dans le framework .NET, et en mieux ! videmment. Elle fait exactement ce que jai dcrit, cest--dire permettre un type valeur davoir une valeur nulle. Elle est mieux faite dans la mesure o elle tire parti de certaines fonctionnalits du framework .NET qui en simplient lcriture. Il sagit de la classe Nullable<>. Aussi, nous pourrons crer un entier pouvant tre null grce au code suivant :
1 2 3 4 5 6 7 8 9 10

Nullable < int > entier = null ; if (! entier . HasValue ) { Console . WriteLine ( "l ' entier n ' a pas de valeur " ) ; } entier = 5 ; if ( entier . HasValue ) { Console . WriteLine ( " Valeur de l ' entier : " + entier . Value ) ; }

Le principe est grosso modo le mme sauf que nous pouvons utiliser le mot-cl null ou aecter directement la valeur lentier en utilisant loprateur daectation, sans passer par la proprit Valeur. Il peut aussi tre compar au mot-cl null ou tre utilis avec loprateur +, etc. Ceci est possible grce certaines fonctionnalits du C# que nous navons pas vues et qui sortent de ltude de ce livre. Cette classe est tellement pratique que le compilateur a t optimis pour simplier son criture. En eet, utiliser Nullable<> est un peu long pour nous autres informaticiens qui sommes des paresseux ! Aussi, lcriture :
1

Nullable < int > entier = null ;

peut se simplier en :
1

int ? entier = null ;

Cest le point dinterrogation qui remplace la dclaration de la classe Nullable<>. 295

CHAPITRE 27. LES GNRIQUES

En rsum
Avec les gnriques, vous pouvez crer des mthodes ou des classes qui sont indpendantes dun type. On les appellera des mthodes gnriques et des types gnriques. On utilise les chevrons <> pour indiquer le type dune classe ou dune mthode gnrique. Les interfaces peuvent aussi tre gnriques, comme linterface IEnumerable<>. Les types nullables constituent un exemple dutilisation trs pratique des classes gnriques.

296

Chapitre

28
Dicult :

TP : types gnriques

h , un peu de pratique histoire de vrier que nous avons bien compris les gnriques. Cest un concept assez facile apprhender mais relativement dicile mettre en uvre. Quand en ai-je besoin ? Comment ?

Voici donc un petit exercice qui va vous permettre dessayer de mettre en uvre une classe gnrique.

297

CHAPITRE 28. TP : TYPES GNRIQUES

Instructions pour raliser la premire partie du TP


Cet exercice va se drouler en deux parties. Dans la premire partie du TP, nous allons raliser une liste chane. Il sagit du grand classique des TP dinformatique en C. Je vous en rappelle le principe. La liste chane permet de naviguer dlment en lment. Quand nous sommes sur le premier lment, le suivant est accessible par sa proprit Suivant. Lorsque nous accdons au suivant, llment prcdent est accessible par la proprit Precedent et le suivant toujours accessible par la proprit Suivant. Sil ny a pas de prcdent ou pas de suivant, llment est null (voir gure 28.1).

Figure 28.1 Description de la liste chane Si on insre un lment la position 1, les autres se dcalent, ainsi quillustr la gure 28.2.

Figure 28.2 Insertion dun nouvel lment dans la liste chane Voil, il faut donc crer une telle liste chane dlments. Le but est bien sr de faire en sorte que llment soit gnrique. Nhsitez pas rchir un peu avant de vous lancer. Cela pourrait paratre un peu simpliste, mais en fait cela occasionne quelques nuds au cerveau. Toujours est-il que je souhaiterais disposer dune proprit en lecture seule permettant daccder au premier lment ainsi quune autre proprit galement en lecture seule 298

CORRECTION permettant daccder au dernier lment. Bien sr, il faut pouvoir naviguer dlment en lment avec des proprits Precedent et Suivant. Il faut videmment une mthode permettant dajouter un lment la n de la liste. Nous aurons galement besoin dune mthode permettant daccder un lment partir de son indice et enn dune mthode permettant dinsrer un lment un indice, dcalant tous les suivants. Voil pour la cration de la classe. Ensuite, notre programme instanciera notre liste chane pour lui ajouter les entiers 5, 10 et 4. Puis nous acherons les valeurs de cette liste en nous basant sur la premire proprit et en naviguant dlment en lment. Nous acherons ensuite les dirents lments en utilisant la mthode daccs un lment par son indice. Enn, nous insrerons la valeur 99 la premire position (position 0), puis la valeur 33 la deuxime position et enn la valeur 30 nouveau la deuxime position. Puis nous acherons tout ce beau monde. Fin de lnonc, ouf ! Pour ceux qui nont pas besoin daide, les explications sont termines. Ouvrez vos Visual C# Express (ou vos Visual Studio si vous tes riches !) et vos claviers. Pour les autres, je vais essayer de vous guider un peu plus en essayant tout de mme de ne pas trop vous donner dindications non plus. En fait, votre liste chane nest pas vraiment une liste, comme pourrait ltre la List<> que nous connaissons. Cette liste chane possde un point dentre qui est le premier lment. Lajout du premier lment est trs simple, il sut de mettre jour une proprit. Pour ajouter llment suivant, il faut en fait brancher la proprit Suivant du premier lment llment que nous sommes en train dajouter. Et inversement, la proprit Precedent de llment que nous souhaitons ajouter sera mise jour avec le premier lment. On se rend compte que llment est un peu plus complexe quun simple type. Nous allons donc avoir une classe gnrique possdant trois proprits (Precedent, Suivant et Valeur). Et nous aurons galement une classe du mme type gnrique possdant la proprit Premier et la proprit Dernier et les mthodes dajout, dobtention de llment et dinsertion. Allez, je vous en ai assez dit. vous de jouer !

Correction
Pas si facile hein ? Mais bon, comme vous tes super entrans, cela na pas d vous poser trop de problmes. Voici la correction que je propose. La premire chose faire est de crer la classe gnrique permettant de stocker un 299

CHAPITRE 28. TP : TYPES GNRIQUES lment :


1 2 3 4 5 6

public class Chainage <T > { public Chainage <T > Precedent { get ; set ; } public Chainage <T > Suivant { get ; set ; } public T Valeur { get ; set ; } }

Cest une classe gnrique toute simple qui possde une valeur du type gnrique et deux proprits du mme type que llment pour obtenir le prcdent ou le suivant. Peut-tre que la plus grande dicult rside, ici, dans le fait de bien modliser la classe qui permet dencapsuler llment. Il faudra ensuite crer la liste gnrique et ses mthodes :
1 2 3

public class ListeChainee <T > { }

La liste chane possde galement un type gnrique. Nous crons sa proprit Premier :
1 2 3 4

public class ListeChainee <T > { public Chainage <T > Premier { get ; private set ; } }

L, cest trs simple, il sagit juste dune proprit en lecture seule stockant le premier lment. Cest la mthode Ajouter() qui permettra de mettre jour cette valeur. Notez quand mme que nous utilisons le type gnrique comme type gnrique de la classe encapsulante. Par contre, pour la proprit Dernier, cest un peu plus compliqu. Pour la retrouver, nous allons parcourir tous les lments partir de la proprit Premier. Ce qui donne :
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17

public class ListeChainee <T > { [... Code supprim pour plus de clart ...] public Chainage <T > Dernier { get { if ( Premier == null ) return null ; Chainage <T > dernier = Premier ; while ( dernier . Suivant != null ) { dernier = dernier . Suivant ; } return dernier ; }

300

CORRECTION
18 19

On parcourt les lments en bouclant sur la proprit Suivant, tant que celle-ci nest pas nulle. Il sagit l dun parcours assez classique o on utilise une variable temporaire qui passe au suivant chaque itration. Nous pouvons prsent crer la mthode Ajouter :
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17

public class ListeChainee <T > { [... Code supprim pour plus de clart ...] public void Ajouter ( T element ) { if ( Premier == null ) { Premier = new Chainage <T > { Valeur = element }; } else { Chainage <T > dernier = Dernier ; dernier . Suivant = new Chainage <T > { Valeur = element , Precedent = dernier }; } }

Cette mthode traite tout dabord le cas du premier lment. Il sagit simplement de mettre jour la proprit Premier. De mme, grce au calcul interne de la proprit Dernier, il sera facile dajouter un nouvel lment en se branchant sur la proprit Suivant du dernier lment. Notez que vu que nous ne la renseignons pas, la proprit Suivant du nouvel lment sera bien null. Pour obtenir un lment un indice donn, il sura de reprendre le mme principe que lors du parcours pour obtenir le dernier lment, sauf quil faudra sarrter au bon moment :
1 2 3 4 5 6 7 8 9 10 11 12

public class ListeChainee <T > { [... Code supprim pour plus de clart ...] public Chainage <T > ObtenirElement ( int indice ) { Chainage <T > temp = Premier ; for ( int i = 1 ; i <= indice ; i ++) { if ( temp == null ) return null ; temp = temp . Suivant ;

301

CHAPITRE 28. TP : TYPES GNRIQUES


13 14 15 16

} return temp ;

Ici, plusieurs solutions. Jai choisi dutiliser une boucle for. Nous aurions trs bien pu garder la boucle while comme pour la proprit Dernier. Enn, il ne reste plus qu insrer un lment :
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22

public class ListeChainee <T > { [... Code supprim pour plus de clart ...] public void Inserer ( T element , int indice ) { if ( indice == 0 ) { Chainage <T > temp = Premier ; Premier = new Chainage <T > { Suivant = temp , Valeur = element }; temp . Precedent = Premier ; } else { Chainage <T > elementAIndice = ObtenirElement ( indice ) ; if ( elementAIndice == null ) Ajouter ( element ) ; else { Chainage <T > precedent = elementAIndice . Precedent ; Chainage <T > temp = precedent . Suivant ; precedent . Suivant = new Chainage <T > { Valeur = element , Precedent = precedent , Suivant = temp }; } } }

23 24 25 26

Nous traitons, dans un premier temps, le cas o lon doit insrer en tte. Il sut de mettre jour la valeur du premier en ayant au pralable dcal ce dernier dun cran. Attention, si Premier est null, nous allons avoir un problme. Dans ce cas, soit nous laissons le problme ; en eet, peut-on vraiment insrer un lment avant les autres sil ny en a pas ? Soit nous grons le cas et dcidons dinsrer llment en tant que Premier :
1 2

public class ListeChainee <T > {

302

CORRECTION
3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27

[... Code supprim pour plus de clart ...] public void Inserer ( T element , int indice ) { if ( indice == 0 ) { if ( Premier == null ) Premier = new Chainage <T > { Valeur = element }; else { Chainage <T > temp = Premier ; Premier = new Chainage <T > { Suivant = temp , Valeur = element }; temp . Precedent = Premier ; } } else { Chainage <T > elementAIndice = ObtenirElement ( indice ) ; if ( elementAIndice == null ) Ajouter ( element ) ; else { Chainage <T > precedent = elementAIndice . Precedent ; Chainage <T > temp = precedent . Suivant ; precedent . Suivant = new Chainage <T > { Valeur = element , Precedent = precedent , Suivant = temp }; } } }

28 29 30 31

Pour les autres cas, si nous tentons dinsrer un indice qui nexiste pas, nous insrons la n en utilisant la mthode Ajouter() existante. Sinon, on intercale le nouvel lment dans la liste en prenant soin de brancher le prcdent sur notre nouvel lment et de brancher le suivant sur notre nouvel lment. Voil pour notre classe. Il reste lutiliser :
1 2 3 4 5 6 7 8 9

static void Main ( string [] args ) { ListeChainee < int > listeChainee = new ListeChainee < int >() ; listeChainee . Ajouter ( 5 ) ; listeChainee . Ajouter ( 10 ) ; listeChainee . Ajouter ( 4 ) ; Console . WriteLine ( listeChainee . Premier . Valeur ) ; Console . WriteLine ( listeChainee . Premier . Suivant . Valeur ) ; Console . WriteLine ( listeChainee . Premier . Suivant . Suivant .

303

CHAPITRE 28. TP : TYPES GNRIQUES


Valeur ) ; Console . WriteLine ( " ************* " ) ; Console . WriteLine ( listeChainee . ObtenirElement ( 0 ) . Valeur ) ; Console . WriteLine ( listeChainee . ObtenirElement ( 1 ) . Valeur ) ; Console . WriteLine ( listeChainee . ObtenirElement ( 2 ) . Valeur ) ; Console . WriteLine ( " ************* " ) ; listeChainee . Inserer ( 99 , 0 ) ; listeChainee . Inserer ( 33 , 2 ) ; listeChainee . Inserer ( 30 , 2 ) ; Console . WriteLine ( listeChainee . ObtenirElement ( 0 ) . Valeur ) ; Console . WriteLine ( listeChainee . ObtenirElement ( 1 ) . Valeur ) ; Console . WriteLine ( listeChainee . ObtenirElement ( 2 ) . Valeur ) ; Console . WriteLine ( listeChainee . ObtenirElement ( 3 ) . Valeur ) ; Console . WriteLine ( listeChainee . ObtenirElement ( 4 ) . Valeur ) ; Console . WriteLine ( listeChainee . ObtenirElement ( 5 ) . Valeur ) ;

10 11 12 13 14 15 16 17 18 19 20 21 22 23 24

Ce qui achera donc :


5 10 4 ************* 5 10 4 ************* 99 5 30 33 10 4

Instructions pour raliser la deuxime partie du TP


Bon, cest trs bien de pouvoir accder un lment par son indice. Mais une liste sur laquelle on ne peut pas faire un foreach, cest quand mme bien dommage. Attaquons dsormais la deuxime partie du TP. Toujours dans loptique de manipuler les gnriques, nous allons faire en sorte que notre liste chane puisse tre parcourue en utilisant un foreach. Nous avons dit plus haut quil susait dimplmenter linterface IEnumerable. En loccurrence, nous allons implmenter sa version gnrique, vu que nous travaillons avec une classe gnrique. Voil le but de ce TP ! 304

INSTRUCTIONS POUR RALISER LA DEUXIME PARTIE DU TP Si vous vous le sentez, allez-y ! Je pense quand mme que vous allez avoir besoin dtre un peu guids car cest une opration assez particulire. Vous laurez devin, il faut que notre liste implmente linterface IEnumerable<T>. Le fait dimplmenter cette interface va vous forcer implmenter deux mthodes GetEnumerator(), la version normale et la version explicite. Sachez ds prsent que les deux mthodes feront exactement la mme chose. Mais, quest-ce quil raconte ? Implmenter une interface explicitement ? On na jamais vu a ! Cest vrai ! Allez, je vous en parle aprs la correction. Pour linstant, cela ne devrait pas vous perturber, car les deux mthodes font exactement la mme chose. En loccurrence, elles renverront un Enumerator personnalis. Il va donc falloir crer cet numrateur qui va soccuper de la mcanique permettant de naviguer dans notre liste. Il sagit dune nouvelle classe qui va devoir implmenter linterface IEnumerator<T>, cest--dire :
1 2 3

public class ListeChaineeEnumerator <T > : IEnumerator <T > { }

Cette interface permet dindiquer que notre numrateur va respecter le contrat lui permettant de fonctionner avec un foreach. Avec cette interface, vous allez devoir implmenter : la la la la la proprit Current ; proprit explicite Current (qui sera la mme chose que la prcdente) ; mthode MoveNext qui permet de passer llment suivant ; mthode Reset, qui permet de revenir au dbut de la liste ; mthode Dispose.

La mthode Dispose est en fait hrite de linterface IDisposable dont hrite linterface IEnumerator<T>. Cest une interface particulire qui ore lopportunit de faire tout ce quil faut pour nettoyer la classe, cest--dire librer les variables qui en auraient besoin. En loccurrence, ici nous naurons rien faire mais il faut quand mme que la mthode soit prsente. Elle sera donc vide. Pour implmenter les autres mthodes, il faut que lnumrateur connaisse la liste quil doit numrer. Il faudra donc que la classe ListeChaineeEnumerator prenne en paramtre de son constructeur la liste numrer. Dans ce constructeur, on initialise la variable membre indice qui contient lindice courant. La proprit Current renverra llment lindice courant. La mthode MoveNext passe llment suivant et renvoie faux sil ny a plus dlments et vrai sinon. Enn la mthode Reset repasse lindice sa valeur initiale. noter que la valeur initiale de lindice est -1, car la boucle foreach commence par appeler la mthode MoveNext qui commence par aller llment suivant, cest--dire 305

CHAPITRE 28. TP : TYPES GNRIQUES llment 0. Il ne reste plus qu vous dire exactement quoi mettre dans les mthodes GetEnumerator de la liste chane, car vous ne trouverez peut-tre pas du premier coup :
1 2 3 4 5 6 7 8 9

public IEnumerator <T > GetEnumerator () { return new ListeChaineeEnumerator <T >( this ) ; } IEnumerator IEnumerable . GetEnumerator () { return new ListeChaineeEnumerator <T >( this ) ; }

Cest vous de jouer pour la suite.

Correction
Encore moins facile ! Tant quon ne la pas fait une premire fois, implmenter linterface IEnumerable est un peu droutant. Aprs, cest toujours pareil. Voici donc ma correction. Tout dabord, la liste chane doit implmenter IEnumerable<T>, ce qui donne :
1 2 3 4 5 6 7 8 9 10 11 12 13 14

public class ListeChainee <T > : IEnumerable <T > { [... Code identique au TP pr c dent ...] public IEnumerator <T > GetEnumerator () { return new ListeChaineeEnumerator <T >( this ) ; } IEnumerator IEnumerable . GetEnumerator () { return new ListeChaineeEnumerator <T >( this ) ; }

L, cest du tout cuit vu que je vous avais donn la solution un peu plus tt . Jespre que vous avez au moins russi a ! Maintenant, il faut donc crer un nouvel numrateur personnalis en lui passant notre liste chane en paramtre. Cet numrateur doit implmenter linterface IEnumerator, ce qui donne :
1 2 3

public class ListeChaineeEnumerator <T > : IEnumerator <T > { }

306

CORRECTION Comme prvu, il faut donc un constructeur qui prend en paramtre la liste chane :
1 2 3 4 5 6 7 8 9 10 11 12 13 14

public class ListeChaineeEnumerator <T > : IEnumerator <T > { private int indice ; private ListeChainee <T > listeChainee ; public L i st eCh ain ee Enu me rat or ( ListeChainee <T > liste ) { indice = -1 ; listeChainee = liste ; } public void Dispose () { }

Cette liste sera enregistre dans une variable membre de la classe. Tant que nous y sommes, nous ajoutons un indice priv que nous initialisons -1, comme dj expliqu. Notez galement que la mthode Dispose() est vide. Il reste implmenter les autres mthodes :
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27

public class ListeChaineeEnumerator <T > : IEnumerator <T > { private int indice ; private ListeChainee <T > listeChainee ; public L i st eCh ain ee Enu me rat or ( ListeChainee <T > liste ) { indice = -1 ; listeChainee = liste ; } public void Dispose () { } public bool MoveNext () { indice ++; Chainage <T > element = listeChainee . ObtenirElement ( indice ) ; return element != null ; } public T Current { get { Chainage <T > element = listeChainee . ObtenirElement ( indice ) ; if ( element == null )

307

CHAPITRE 28. TP : TYPES GNRIQUES


28 29 30 31 32 33 34 35 36 37 38 39 40 41 42

return default ( T ) ; return element . Valeur ;

object IEnumerator . Current { get { return Current ; } } public void Reset () { indice = -1 ; }

Commenons par la mthode MoveNext(). Elle passe lindice suivant et renvoie faux ou vrai, selon quon arrive au bout de la liste ou pas. Noubliez pas que cest la premire mthode qui sera appele dans le foreach, donc pour passer llment suivant, on incrmente lindice pour le positionner llment 0. Cest pour cela que lindice a t initialis -1. On utilise ensuite la mthode existante de la liste pour obtenir llment un indice an de savoir si notre liste peut continuer snumrer. La proprit Current renvoie llment lindice courant, pour cela on utilise lindice pour accder llment courant, en utilisant les mthodes de la liste. Lautre proprit Current fait la mme chose, il sut de lappeler. Enn, la mthode Reset permet de rinitialiser lnumrateur en retournant lindice initial. Finalement, ce nest pas si compliqu que a. Mais il faut avouer que la premire fois, cest un peu droutant ! mon sens, cest un bon exercice pratique. Peut-tre que mes explications ont su vous guider. Sans doute avez-vous d regarder un peu la documentation de IEnumerable sur internet. Peut-tre avez-vous cherch des ressources traitant du mme sujet. Dans tous les cas, devoir implmenter une interface du framework .NET est une situation que vous allez frquemment devoir rencontrer. Il est bon de sy entraner.

Aller plus loin


Vous me direz quil fallait le deviner, quon avait besoin dune classe indpendante qui permettait de grer lnumrateur ! En fait, ce nest pas obligatoire. On peut trs bien faire en sorte que notre classe gre la liste chane et son numrateur. Il sut de faire en sorte que la liste chane implmente galement IEnumerator<T> et de grer la logique lintrieur de la classe. Par contre, ce nest pas recommand. Dune manire gnrale il est bon quune classe nait soccuper que dune seule chose. On appelle a le principe de responsabilit 308

ALLER PLUS LOIN unique 1 . Plus une classe fait de choses, plus une modication impacte les autres choses. Ici, il est judicieux de garder le dcouplage des deux classes. Il y a quand mme un lment que lon peut amliorer dans le code de la correction. En eet, cette liste nest pas extrmement optimise car lorsque nous obtenons un lment, nous reparcourons toute la liste depuis le dbut, notamment dans le cas de la gestion de lnumrateur. Il pourrait tre judicieux qu chaque foreach, nous ne parcourions pas tous les lments et quon vite dappeler continuellement la mthode ObtenirElement(). Une ide ? Cela pourrait se faire en liminant lindice et en utilisant une variable de type Chainage<T>, par exemple :
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38

public class ListeChaineeEnumerator <T > : IEnumerator <T > { private Chainage <T > courant ; private ListeChainee <T > listeChainee ; public L i st eCh ain ee Enu me rat or ( ListeChainee <T > liste ) { courant = null ; listeChainee = liste ; } public void Dispose () { } public bool MoveNext () { if ( courant == null ) courant = listeChainee . Premier ; else courant = courant . Suivant ; } return courant != null ;

public T Current { get { if ( courant == null ) return default ( T ) ; return courant . Valeur ; } } object IEnumerator . Current { get { return Current ; } }
1. En anglais, SRP, pour Single Responsibility Principle.

309

CHAPITRE 28. TP : TYPES GNRIQUES


39 40 41 42 43 44

public void Reset () { courant = null ; }

Ici, cest la variable courant qui nous permet ditrer au fur et mesure de la liste chane. Cest le mme principe que dans la mthode ObtenirElement, sauf quon ne reparcourt pas toute la liste chaque fois. Ici, cette optimisation est ngligeable pour notre utilisation. Elle peut savrer intressante si la liste grossit normment. Dans tous les cas, a ne fait pas de mal daller plus vite ! Remarquons avant de terminer quil est possible de simplier encore la classe grce un mot-cl que nous dcouvrirons dans la partie suivante : yield. Il permet de crer facilement des numrateurs. Ce qui fait que le code complet de la liste chane pourra tre :
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30

public class ListeChainee <T > : IEnumerable <T > { public Chainage <T > Premier { get ; private set ; } public Chainage <T > Dernier { get { if ( Premier == null ) return null ; Chainage <T > dernier = Premier ; while ( dernier . Suivant != null ) { dernier = dernier . Suivant ; } return dernier ; } } public void Ajouter ( T element ) { if ( Premier == null ) { Premier = new Chainage <T > { Valeur = element }; } else { Chainage <T > dernier = Dernier ; dernier . Suivant = new Chainage <T > { Valeur = element , Precedent = dernier }; }

310

ALLER PLUS LOIN


31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67

} public Chainage <T > ObtenirElement ( int indice ) { Chainage <T > temp = Premier ; for ( int i = 1 ; i <= indice ; i ++) { if ( temp == null ) return null ; temp = temp . Suivant ; } return temp ; } public void Inserer ( T element , int indice ) { if ( indice == 0 ) { if ( Premier == null ) Premier = new Chainage <T > { Valeur = element }; else { Chainage <T > temp = Premier ; Premier = new Chainage <T > { Suivant = temp , Valeur = element }; temp . Precedent = Premier ; } } else { Chainage <T > elementAIndice = ObtenirElement ( indice ) ; if ( elementAIndice == null ) Ajouter ( element ) ; else { Chainage <T > precedent = elementAIndice . Precedent ; Chainage <T > temp = precedent . Suivant ; precedent . Suivant = new Chainage <T > { Valeur = element , Precedent = precedent , Suivant = temp }; } } } public IEnumerator <T > GetEnumerator () { Chainage <T > courant = Premier ; while ( courant != null )

68 69 70 71 72 73 74 75

311

CHAPITRE 28. TP : TYPES GNRIQUES


76 77 78 79 80 81 82 83 84 85 86

{ }

yield return courant . Valeur ; courant = courant . Suivant ;

IEnumerator IEnumerable . GetEnumerator () { return GetEnumerator () ; }

Remarquons que nous navons plus besoin de la classe ListeChaineeEnumerator. Limplmentation devient trs facile. Nous reviendrons sur ce mot-cl dans la partie suivante.

Implmenter une interface explicitement


Jen prote ici pour faire un apart sur limplmentation dinterface explicitement. Jai choisi dlibrment de ne pas le mettre dans le chapitre des interfaces car cest un cas relativement rare mais qui se produit justement quand on implmente linterface IEnumerable<T>. Cela vient du fait que linterface IEnumerable, non gnrique, expose une proprit Current. De mme, linterface IEnumerable<T>, gnrique, qui hrite de IEnumerable, expose galement une proprit Current. Il y a donc une ambigut car les deux proprits portent le mme nom, mais ne renvoient pas la mme chose. Ce qui est contraire aux rgles que nous avons dj vues. Pour faire la dirence, il sura de prxer la proprit par le nom de linterface et de ne pas mettre le mot-cl public. Limplmentation explicite a galement un intrt dans le code suivant :
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16

public interface ICarnivore { void Manger () ; } public interface IFrugivore { void Manger () ; } public class Homme : ICarnivore , IFrugivore { public void Manger () { Console . WriteLine ( " Je mange " ) ; }

312

IMPLMENTER UNE INTERFACE EXPLICITEMENT


17 18 19 20 21 22 23 24 25 26 27 28

} class Program { static void Main ( string [] args ) { Homme homme = new Homme () ; homme . Manger () ; (( ICarnivore ) homme ) . Manger () ; (( IFrugivore ) homme ) . Manger () ; } }

Ici, ce code compile car la classe Homme implmente la mthode Manger qui est commune aux deux interfaces. Par contre, il nest pas possible de faire la distinction entre le fait de manger en tant quhomme, en tant que ICarnivore ou en tant que IFrugivore. Ce code achera :
Je mange Je mange Je mange

Si cest le comportement attendu, tant mieux. Si ce nest pas le cas, il va falloir implmenter au moins une des interfaces de manire explicite :
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17

public class Homme : ICarnivore , IFrugivore { public void Manger () { Console . WriteLine ( " Je mange " ) ; } void IFrugivore . Manger () { Console . WriteLine ( " Je mange en tant que IFrugivore " ) ; } void ICarnivore . Manger () { Console . WriteLine ( " Je mange en tant que ICarnivore " ) ; }

Avec ce code, notre exemple achera :


Je mange Je mange en tant que ICarnivore Je mange en tant que IFrugivore

313

CHAPITRE 28. TP : TYPES GNRIQUES Si vous vous rappelez, nous avions vu au moment du chapitre sur les interfaces que Visual C# Express nous proposait de nous aider dans limplmentation de linterface. Par le bouton droit, vous aviez galement accs au sous-menu implmenter linterface explicitement. Vous pouvez vous en servir dans ce cas prcis. Je marrte l sur limplmentation dune interface explicite, mme sil y aurait dautres points voir. Globalement, en situation relle, cela ne vous servira jamais. Voil pour ce TP. Nous avons cr une classe gnrique permettant de grer les listes chanes. Ceci nous a permis de manipuler ces types combien indispensables et de nous entraner la gnricit. Nous en avons mme prot pour voir comment faire en sorte quune classe soit numrable, en implmentant la version gnrique de IEnumerable. Notez bien sr que cette classe est fonctionnellement incomplte. Il aurait t judicieux de rajouter une mthode permettant de supprimer un lment par exemple. noter quune classe qui fait peu prs le mme travail existe dans le framework .NET, elle sappelle LinkedList. Vous trouverez sa documentation via le code web suivant : Documentation LinkedList Code web : 607852

Vous pouvez tlcharger tous les codes sources de cet exercice grce au code web suivant : Copier ce code Code web : 972348

314

Chapitre

29
Dicult :

Les mthodes dextension

n gnral, pour ajouter des fonctionnalits une classe, nous pourrons soit modier le code source de la classe, soit crer une classe drive de notre classe et ajouter ces fonctionnalits.

Dans ce chapitre nous allons voir quil existe un autre moyen pour tendre une classe : ceci est possible grce aux mthodes dextension. Elles sont intressantes si nous navons pas la main sur le code source de la classe ou si la classe nest pas drivable. Partons la dcouverte de ces fameuses mthodes. . .

315

CHAPITRE 29. LES MTHODES DEXTENSION

Quest-ce quune mthode dextension ?


Comme son nom lindique, une mthode dextension permet dtendre une classe en lui rajoutant des mthodes. Cest pratique lorsque nous ne possdons pas le code source dune classe et quil savre dicile de la modier. Dune manire gnrale, lorsquon souhaite modier une classe dont on na pas le code source, on utilise une classe drive ; ce qui est impossible avec des objets qui ne sont pas drivables, comme par exemple les structures telles que int ou double ou galement avec la classe String. En eet, String nest pas drivable. Nous verrons plus tard comment cest possible, mais pour linstant, admettons-le. Si vous ne me croyez pas, vous pouvez toujours tenter de compiler le code suivant :
1 2 3

public class StringEvoluee : String { }

Crer une mthode dextension


Si par exemple nous souhaitons crer une mthode permettant de crypter une chane de caractres dans un format que nous seuls comprenons, il serait judicieux de crer une mthode dans une classe StringCryptee qui drive de String. Comme ceci nest pas possible, la seule chose quil nous reste, cest de crer une mthode statique utilitaire faisant cet encodage :
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22

class Program { static void Main ( string [] args ) { string chaineNormale = " Bonjour tous " ; string chaineCryptee = Encodage . Crypte ( chaineNormale ) ; Console . WriteLine ( chaineCryptee ) ; chaineNormale = Encodage . Decrypte ( chaineCryptee ) ; Console . WriteLine ( chaineNormale ) ; } } public static class Encodage { public static string Crypte ( string chaine ) { return Convert . ToBase64String ( Encoding . Default . GetBytes ( chaine ) ) ; } public static string Decrypte ( string chaine ) {

316

UTILISER UNE MTHODE DEXTENSION


23 24 25

return Encoding . Default . GetString ( Convert . FromBase64String ( chaine ) ) ;

Bon, je vous laccorde, notre encodage secret ne lest pas tant que a ! Il savre que jutilise ici un encodage en base 64, algorithme archiconnu. Mais bon, cest pour lexemple !

Utiliser une mthode dextension


Ces mthodes statiques jouent bien leur rle. Mais il est possible de faire en sorte que ces deux mthodes deviennent des mthodes dextension de la classe String. Il sut dutiliser le mot-cl this devant le premier paramtre de la mthode an de crer une mthode dextension :
1 2 3 4 5 6 7 8 9 10 11 12

public static class Encodage { public static string Crypte ( this string chaine ) { return Convert . ToBase64String ( Encoding . Default . GetBytes ( chaine ) ) ; } public static string Decrypte ( this string chaine ) { return Encoding . Default . GetString ( Convert . FromBase64String ( chaine ) ) ; }

Nous pourrons dsormais utiliser ces mthodes comme si elles faisaient partie de la classe String :
1 2 3 4 5

string chaineNormale = " Bonjour tous " ; string chaineCryptee = chaineNormale . Crypte () ; Console . WriteLine ( chaineCryptee ) ; chaineNormale = chaineCryptee . Decrypte () ; Console . WriteLine ( chaineNormale ) ;

Pas mal non ? De plus, si nous regardons dans la compltion automatique, nous pourrons voir apparatre nos fameuses mthodes (voir gure 29.1). Plutt pratique. videmment, en crant une mthode dextension, nous navons pas accs aux mthodes prives ou variables membres internes la classe. La preuve, les mthodes dextension sont des mthodes statiques qui travaillent hors de toute instance de classe. Ces mthodes doivent donc tre statiques et situes lintrieur dune classe statique. 317

CHAPITRE 29. LES MTHODES DEXTENSION

Figure 29.1 Notre mthode dextension apparat dans la compltion automatique Par contre, il faut faire attention lespace de nom o se situent nos mthodes dextension. Si le using correspondant nest pas inclus, nous ne verrons pas les mthodes dextension. Remarquez que les mthodes dextension fonctionnent aussi avec les interfaces. Plus prcisment, elles viennent tendre toutes les classes qui implmentent une interface. Par exemple, avec deux classes implmentant linterface IVolant :
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20

public interface IVolant { void Voler () ; } public class Oiseau : IVolant { public void Voler () { Console . WriteLine ( " Je vole " ) ; } } public class Avion : IVolant { public void Voler () { Console . WriteLine ( " Je vole " ) ; } }

Si je cre une mthode dextension prenant en paramtres un IVolant, prx par this :
1 2 3 4 5

public static class Extentions { public static void Atterrir ( this IVolant volant ) { Console . WriteLine ( "J ' atterris " ) ;

318

UTILISER UNE MTHODE DEXTENSION


6 7

Je peux ensuite accder cette mthode pour les objets Avion et Oiseau :
1 2 3 4

Avion a = new Avion () ; Oiseau b = new Oiseau () ; a . Atterrir () ; b . Atterrir () ;

noter que le framework .NET se sert de ceci pour proposer un grand nombre de mthodes dextension sur les objets implmentant IEnumerable ; nous les tudierons un peu plus tard.

En rsum
Une mthode dextension permet dtendre une classe en lui rajoutant des mthodes. Il nest pas recommand dutiliser des mthodes dextension lorsquon dispose dj du code source de la classe ou quon peut facilement en crer un type driv. On utilise le mot-cl this en premier paramtre dune classe statique pour tendre une classe.

319

CHAPITRE 29. LES MTHODES DEXTENSION

320

Chapitre

30
Dicult :

Dlgus, vnements et expressions lambdas

ans ce chapitre, nous allons aborder les dlgus, les vnements et les expressions lambdas. Les dlgus et les vnements sont des types du framework .NET que nous navons pas encore vus. Ils permettent dadresser des solutions notamment dans le cadre dune programmation par vnements, comme cest le cas lorsque nous ralisons des applications ncessitant de ragir une action faite par un utilisateur. Nous verrons dans ce chapitre que les expressions lambdas vont de pair avec les dlgus.

321

CHAPITRE 30. DLGUS, VNEMENTS ET EXPRESSIONS LAMBDAS

Les dlgus (delegate)


Les dlgus 1 en C# ne soccupent pas de la classe, ni du personnel ! Ils permettent de crer des variables spciales. Ce sont des variables qui pointent vers une mthode. Cest un peu comme les pointeurs de fonctions en C ou C++, sauf quici on sait exactement ce que lon utilise car le C# est fortement typ. Le dlgu va nous permettre de dnir une signature de mthode et avec lui, nous pourrons pointer vers nimporte quelle mthode qui respecte cette signature. En gnral, on utilise un dlgu quand on veut passer une mthode en paramtre dune autre mthode. Un petit exemple sera sans doute plus parlant quun long discours. Ainsi, le code suivant :
1 2 3 4

public class TrieurDeTableau { private delegate void DelegateTri ( int [] tableau ) ; }

cre un dlgu priv la classe TrieurDeTableau qui permettra de pointer vers des mthodes qui ne retournent rien (void) et qui acceptent un tableau dentiers en paramtre. Cest justement le cas des mthodes TriAscendant() et TriDescendant() que nous allons ajouter la classe (a tombe bien !) :
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15

public class TrieurDeTableau { private delegate void DelegateTri ( int [] tableau ) ; private void TriAscendant ( int [] tableau ) { Array . Sort ( tableau ) ; } private void TriDescendant ( int [] tableau ) { Array . Sort ( tableau ) ; Array . Reverse ( tableau ) ; }

Vous aurez compris que la mthode TriAscendant utilise la mthode Array.Sort pour trier un tableau par ordre croissant. Inversement, la mthode TriDescendant() trie le tableau par ordre dcroissant en triant par ordre croissant et en inversant ensuite le tableau. Il ne reste plus qu crer une mthode dans la classe permettant dutiliser le tri ascendant et le tri descendant, grce notre dlgu :
1

public class TrieurDeTableau


1. En anglais, delegate.

322

LES DLGUS (DELEGATE)


2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22

[... Code supprim pour plus de clart ...] public void DemoTri ( int [] tableau ) { DelegateTri tri = TriAscendant ; tri ( tableau ) ; foreach ( int i in tableau ) { Console . WriteLine ( i ) ; } Console . WriteLine () ; tri = TriDescendant ; tri ( tableau ) ; foreach ( int i in tableau ) { Console . WriteLine ( i ) ; }

Nous voyons ici que dans la mthode DemoTri nous commenons par dclarer une variable du type du dlgu DelegateTri, qui est le dlgu que nous avons cr. Puis nous faisons pointer cette variable vers la mthode TriAscendant(). Nul besoin ici dutiliser les parenthses, mais juste le nom de la mthode. Il sagit juste dune aectation. Nous invoquons ensuite la mthode TriAscendant() travers la variable qui va permettre de trier le tableau par ordre croissant avant dacher son contenu. Cette fois-ci, il faut bien sr utiliser les parenthses car nous invoquons la mthode. Puis nous faisons pointer la variable vers la mthode TriDescendant() qui va nous permettre de faire la mme chose mais avec un tri dcroissant. Nous pouvons appeler cette classe de cette faon :
1 2 3 4 5

static void Main ( string [] args ) { int [] tableau = new int [] { 4 , 1 , 6 , 10 , 8 , 5 }; new TrieurDeTableau () . DemoTri ( tableau ) ; }

Notre code achera au nal les entiers tris par ordre croissant, puis les mmes entiers tris par ordre dcroissant. Ok, mais pourquoi utiliser ce dlgu ? On pourrait trs bien appeler dabord la mthode de tri ascendant et ensuite la mthode de tri descendant. Comme on la toujours fait ! 323

CHAPITRE 30. DLGUS, VNEMENTS ET EXPRESSIONS LAMBDAS Eh bien, lintrt ici est que le dlgu est trs souple et va permettre de rorganiser le code (on parle galement de refactoriser du code). Ainsi, en rajoutant la mthode suivante dans la classe :
1 2 3 4 5 6 7 8

private void TrierEtAfficher ( int [] tableau , DelegateTri methodeDeTri ) { methodeDeTri ( tableau ) ; foreach ( int i in tableau ) { Console . WriteLine ( i ) ; } }

nous pourrons grandement simplier la mthode DemoTri :


1 2 3 4 5 6

public void DemoTri ( int [] tableau ) { TrierEtAfficher ( tableau , TriAscendant ) ; Console . WriteLine () ; TrierEtAfficher ( tableau , TriDescendant ) ; }

Ce qui produira le mme rsultat que prcdemment. Quavons-nous fait ici ? Nous avons utilis le dlgu comme paramtre dune mthode. Ce dlgu est ensuite utilis pour invoquer une mthode que nous aurons passe en paramtres. Cest ce que nous faisons en disant dutiliser la mthode TrierEtAfficher une premire fois avec la mthode TriAscendant() et une deuxime fois avec la mthode TriDescendant(). Plutt pas mal non ? Il est mme possible de dnir la mthode qui sera utilise lintrieur de TrierEtAfficher() sans avoir lcrire compltement dans le corps de la classe. Cela peut tre utile si la mthode nest voue tre utilise que dans cette unique situation et quelle nest jamais appele un autre endroit. Par exemple, plutt que de dnir compltement la mthode TriAscendant(), je pourrais la dnir directement au moment de lappel de la mthode :
1 2 3 4 5 6 7 8 9 10 11 12

public class TrieurDeTableau { private delegate void DelegateTri ( int [] tableau ) ; private void TrierEtAfficher ( int [] tableau , DelegateTri methodeDeTri ) { methodeDeTri ( tableau ) ; foreach ( int i in tableau ) { Console . WriteLine ( i ) ; } }

324

DIFFUSION MULTIPLE, LE MULTICAST


13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30

public void DemoTri ( int [] tableau ) { TrierEtAfficher ( tableau , delegate ( int [] leTableau ) { Array . Sort ( leTableau ) ; }) ; Console . WriteLine () ; TrierEtAfficher ( tableau , delegate ( int [] leTableau ) { Array . Sort ( tableau ) ; Array . Reverse ( tableau ) ; }) ; } }

Ainsi, je naurai plus besoin des mthodes TriAscendant() et TriDescendant(). Le fait de dnir la mthode directement au niveau du paramtre dappel est ce quon appelle utiliser une mthode anonyme . Anonyme car la mthode na pas de nom. Elle na de vie qu cet endroit-l. La syntaxe est un peu particulire, mais au lieu dutiliser une variable de type delegate qui pointe vers une mthode, cest comme si on crivait directement la mthode. On utilise le mot-cl delegate suivi de la dclaration du paramtre. videmment, le dlgu anonyme doit respecter la signature de DelegateTri que nous avons dni plus haut. Enn, nous faisons suivre avec un bloc de code qui correspond au corps de la mthode anonyme. noter que le fait dutiliser le mot-cl delegate revient en fait crer une classe qui drive de System.Delegate et qui implmente la logique de base dun dlgu. Le C# nous masque tout ceci an dtre le plus ecace possible.

Diusion multiple, le multicast


Il faut galement savoir que le dlgu peut tre multicast, cela veut dire quil peut pointer vers plusieurs mthodes. Amliorons le premier exemple :
1 2 3 4 5

public class TrieurDeTableau { private delegate void DelegateTri ( int [] tableau ) ; private void TriAscendant ( int [] tableau )

325

CHAPITRE 30. DLGUS, VNEMENTS ET EXPRESSIONS LAMBDAS


6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31

Array . Sort ( tableau ) ; foreach ( int i in tableau ) { Console . WriteLine ( i ) ; } Console . WriteLine () ;

private void TriDescendant ( int [] tableau ) { Array . Sort ( tableau ) ; Array . Reverse ( tableau ) ; foreach ( int i in tableau ) { Console . WriteLine ( i ) ; } } public void DemoTri ( int [] tableau ) { DelegateTri tri = TriAscendant ; tri += TriDescendant ; tri ( tableau ) ; }

Ici, jutilise Console.WriteLine directement dans chaque mthode de tri an de bien voir le rsultat du tri du tableau. Limportant est de voir que dans la mthode DemoTri, je commence par crer un dlgu que je fais pointer vers la mthode TriAscendant. Puis jajoute ce dlgu, avec loprateur +=, une nouvelle mthode, savoir TriDescendant. Dsormais, le fait dinvoquer le dlgu va invoquer les deux mthodes ; ce qui produira en sortie :
1 4 5 6 8 10 10 8 6 5 4 1

Ce dtail prend toute son importance avec les vnements que nous verrons plus loin. 326

LES DLGUS GNRIQUES ACTION ET FUNC noter que le rsultat de ce code est videmment identique en utilisant les mthodes anonymes :
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22

public void DemoTri ( int [] tableau ) { DelegateTri tri = delegate ( int [] leTableau ) { Array . Sort ( leTableau ) ; foreach ( int i in tableau ) { Console . WriteLine ( i ) ; } Console . WriteLine () ; }; tri += delegate ( int [] leTableau ) { Array . Sort ( tableau ) ; Array . Reverse ( tableau ) ; foreach ( int i in tableau ) { Console . WriteLine ( i ) ; } }; tri ( tableau ) ; }

Il faut quand mme remarquer que lordre dans lequel sont appeles les mthodes nest pas garanti et ne dpend pas forcment de lordre dans lequel nous les avons ajoutes au dlgu.

Les dlgus gnriques Action et Func


Cest trs bien tout a, mais cela veut dire qu chaque fois que je vais avoir besoin dutiliser un dlgu, je vais devoir crer un nouveau type en utilisant le mot-cl delegate ? Pas forcment, cest l quinterviennent les dlgus gnriques Action et Func. Action est un dlgu qui permet de pointer vers une mthode qui ne renvoie rien et qui peut accepter jusqu 16 types dirents. Cela veut dire que le code prcdent peut tre remplac par :
1 2 3 4

public class TrieurDeTableau { private void TrierEtAfficher ( int [] tableau , Action < int [] > methodeDeTri ) {

327

CHAPITRE 30. DLGUS, VNEMENTS ET EXPRESSIONS LAMBDAS


5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27

methodeDeTri ( tableau ) ; foreach ( int i in tableau ) { Console . WriteLine ( i ) ; }

public void DemoTri ( int [] tableau ) { TrierEtAfficher ( tableau , delegate ( int [] leTableau ) { Array . Sort ( leTableau ) ; }) ; Console . WriteLine () ; TrierEtAfficher ( tableau , delegate ( int [] leTableau ) { Array . Sort ( tableau ) ; Array . Reverse ( tableau ) ; }) ;

Notez que la dirence se situe au niveau du paramtre de la mthode TrierEtAfficher qui prend un Action<int[]>. En fait, cela est quivalent crer un dlgu qui ne renvoie rien et qui prend un tableau dentiers en paramtre. Si notre mthode avait deux paramtres, il aurait su dutiliser la forme de Action avec plusieurs paramtres gnriques, par exemple Action<int[], string> pour avoir une mthode qui ne renvoie rien et qui prend un tableau dentiers et une chane de caractres en paramtres. Lorsque la mthode renvoie quelque chose, on peut utiliser le dlgu Func<T>, sachant quici, cest le dernier paramtre gnrique qui sera du type de retour du dlgu. Par exemple :
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16

public class Operations { public void DemoOperations () { double division = Calcul ( delegate ( int a , int b ) { return ( double ) a / ( double ) b ; }, 4, 5); double puissance = Calcul ( delegate ( int a , int b ) { return Math . Pow (( double )a , ( double ) b ) ; }, 4, 5); Console . WriteLine ( " Division : " + division ) ; Console . WriteLine ( " Puissance : " + puissance ) ;

328

LES EXPRESSIONS LAMBDAS


17 18 19 20 21 22 23

} private double Calcul ( Func < int , int , double > methodeDeCalcul , int a , int b ) { return methodeDeCalcul (a , b ) ; }

Ici, dans la mthode Calcul, on utilise le dlgu Func pour indiquer que la mthode prend deux entiers en paramtres et renvoie un double. Si nous utilisons cette classe avec le code suivant :
1 2 3 4 5 6 7

class Program { static void Main ( string [] args ) { new Operations () . DemoOperations () ; } }

nous aurons :
Division : 0 ,8 Puissance : 1024

Les expressions lambdas


Non, il ne sagit pas dune expression qui danse la lambada, mais dune faon simplie dcrire les dlgus que nous avons vus au-dessus ! Ainsi, le code suivant :
1 2 3 4

DelegateTri tri = delegate ( int [] leTableau ) { Array . Sort ( leTableau ) ; };

peut galement scrire de cette faon :


1 2 3 4

DelegateTri tri = ( leTableau ) = > { Array . Sort ( leTableau ) ; };

Cette syntaxe est particulire. La variable leTableau permet de spcier le paramtre dentre de lexpression lambda. Ce paramtre est crit entre parenthses. Ici, pas besoin dindiquer son type vu quil est connu par la signature associe au dlgu. On utilise ensuite la che => pour dnir lexpression lambda qui sera utilise. Elle scrit de la mme faon quune mthode, dans un bloc de code. 329

CHAPITRE 30. DLGUS, VNEMENTS ET EXPRESSIONS LAMBDAS Lexpression lambda (leTableau) => se lit : leTableau conduit . Dans le corps de la mthode, nous voyons que nous utilisons la variable leTableau de la mme faon que prcdemment. Dans ce cas prcis, il est encore possible de raccourcir lcriture car la mthode ne contient quune seule ligne ; on pourra alors lcrire de cette faon :
1

TrierEtAfficher ( tableau , leTableau = > Array . Sort ( leTableau ) ) ;

Sil y a un seul paramtre lexpression lambda, on peut omettre les parenthses. Quand il y a deux paramtres, on les spare par une virgule. noter quon nindique nulle part le type de retour, sil y en a un. Notre expression lambda remplaant le calcul de la division peut donc scrire ainsi :
1 2 3 4

double division = Calcul (( a , b ) = > { return ( double ) a / ( double ) b ; }, 4, 5);

Lorsque linstruction possde une unique ligne, on peut encore en simplier lcriture ; ce qui donne :
1

double division = Calcul (( a , b ) = > ( double ) a / ( double )b , 4 , 5 ) ;

Pourquoi tout ce blabla sur les delegate et les expressions lambdas ?

Pour deux raisons : Parce que les dlgus sont la base des vnements. cause des mthodes dextension LINQ. Nous parlerons dans la partie suivante des mthodes dextension LINQ. Quant aux vnements, explorons-les ds maintenant !

Les vnements
Les vnements sont un mcanisme du C# permettant une classe dtre notie dun changement. Par exemple, on peut vouloir sabonner un changement de prix dune voiture. La base des vnements est le dlgu. On pourra stocker dans un vnement un ou plusieurs dlgus qui pointent vers des mthodes respectant la signature de lvnement. Un vnement est dni grce au mot-cl event. Prenons cet exemple : 330

LES VNEMENTS
1 2 3 4 5 6 7 8 9 10 11 12 13

public class Voiture { public delegate void D e l e g a t e D e C h a n g e m e n t D e P r i x ( decimal nouveauPrix ) ; public event D e l e g a t e D e C h a n g e m e n t D e P r i x ChangementDePrix ; public decimal Prix { get ; set ; } public void PromoSurLePrix () { Prix = Prix / 2 ; if ( ChangementDePrix != null ) ChangementDePrix ( Prix ) ; }

Dans la classe Voiture, nous dnissons un dlgu qui ne retourne rien et qui prend en paramtre un dcimal. Nous dnissons ensuite un vnement bas sur ce dlgu, avec, comme nous lavons vu, lutilisation du mot-cl event. Enn, dans la mthode de promotion, aprs un changement de prix (division par 2), nous notions les ventuels objets qui se seraient abonns cet vnement en invoquant lvnement et en lui fournissant en paramtre le nouveau prix. noter que nous testons dabord sil y a un abonn lvnement (en testant sil est dirent de null) avant de le lever. Pour sabonner cet vnement, il sut dutiliser le code suivant :
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23

class Program { static void Main ( string [] args ) { new DemoEvenement () . Demo () ; } } public class DemoEvenement { public void Demo () { Voiture voiture = new Voiture { Prix = 10000 }; Voiture . D e l e g a t e D e C h a n g e m e n t D e P r i x d e le g a te C h an g e me n t De P r ix = v o i t ur e _ Ch a n ge m e nt D e Pr i x ; voiture . ChangementDePrix += d e l eg a t eC h a ng e m en t D eP r i x ; } voiture . PromoSurLePrix () ;

private void v o it u r e_ C h an g e me n t De P r ix ( decimal nouveauPrix ) { Console . WriteLine ( " Le nouveau prix est de : " +

331

CHAPITRE 30. DLGUS, VNEMENTS ET EXPRESSIONS LAMBDAS


nouveauPrix ) ;

24 25

Nous crons une voiture, et nous crons un dlgu du mme type que lvnement. Nous le faisons pointer vers une mthode qui respecte la signature du dlgu. Ainsi, chaque changement de prix, la mthode voiture_ChangementDePrix va tre appele et le paramtre nouveauPrix possdera le nouveau prix qui vient dtre calcul. Appelons la promotion en invoquant la mthode ChangementDePrix(). Nous pouvons nous rendre compte que lapplication nous ache le nouveau prix qui est lancien divis par 2. Lorsque nous commenons crire le code qui va permettre de nous abonner lvnement, la compltion automatique nous propose facilement de crer une mthode qui respecte la signature de lvnement. Il sut de saisir lvnement, dajouter ensuite un += et dappuyer sur la touche Tab pour que Visual C# Express nous propose de tout insrer automatiquement (voir la gure 30.1).

Figure 30.1 Compltion automatique dvnement Ce qui gnre le code suivant :


1

voiture . ChangementDePrix += new Voiture . D e l e g a t e D e C h a n g e m e n t D e P r i x ( v o i tu r e _C h a ng e m en t D eP r i x ) ;

ainsi que la mthode :


1 2 3 4

void v o i t u r e _C h a ng e m en t D eP r i x ( decimal nouveauPrix ) { throw new N o tI mp le m en te d Ex ce pt i on () ; }

On peut aisment simplier labonnement avec :


1

voiture . ChangementDePrix += v o i tu r e _C h a ng e m en t D eP r i x ;

comme on la dj vu. Notez que vous pouvez galement rajouter la visibilit private sur la mthode gnre an que cela soit plus explicite.
1

private void v o it u r e_ C h an g e me n t De P r ix ( decimal nouveauPrix )

332

LES VNEMENTS
2 3

{ }

Lutilisation du += permet dajouter un nouveau dlgu lvnement. Il sera ventuellement possible dajouter un autre dlgu avec le mme oprateur ; ainsi deux mthodes seront dsormais noties en cas de changement de prix. Inversement, il est possible de se dsabonner dun vnement en utilisant loprateur -=. Les vnements sont largement utiliss dans les applications ayant recours au C#, comme les applications clients lourds dveloppes avec WPF par exemple. Ce sont des applications comme un traitement de texte ou un navigateur internet. Par exemple, lorsque lon clique sur un bouton, un vnement est lev. Ces vnements utilisent en gnral une construction base du dlgu EventHandler ou sa version gnrique EventHandler<>. Ce dlgu accepte deux paramtres. Le premier de type object qui reprsente la source de lvnement, cest--dire lobjet qui a lev lvnement. Le second est une classe qui drive de la classe de base EventArgs. Rcrivons notre exemple avec ce nouveau handler. Nous avons donc besoin en premier lieu dune classe qui drive de la classe EventArgs :
1 2 3 4

public class C h a n g e m en t D e P r i x E v en t A r g s : EventArgs { public decimal Prix { get ; set ; } }

Plus besoin de dclaration de dlgu, nous utilisons directement EventHandler dans notre classe Voiture :
1 2 3 4 5 6 7 8 9 10 11 12

public class Voiture { public event EventHandler < ChangementDePrixEventArgs > ChangementDePrix ; public decimal Prix { get ; set ; } public void PromoSurLePrix () { Prix = Prix / 2 ; if ( ChangementDePrix != null ) ChangementDePrix ( this , new C h a n g em e n t D e P r i x Ev e n t A r g s { Prix = Prix }) ; }

Et notre dmo devient :


1 2 3 4 5

class Program { static void Main ( string [] args ) { new DemoEvenement () . Demo () ;

333

CHAPITRE 30. DLGUS, VNEMENTS ET EXPRESSIONS LAMBDAS


6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24

public class DemoEvenement { public void Demo () { Voiture voiture = new Voiture { Prix = 10000 }; voiture . ChangementDePrix += v o i tu r e _C h a ng e m en t D eP r i x ; } voiture . PromoSurLePrix () ;

private void v o it u r e_ C h an g e me n t De P r ix ( object sender , C h a n g em e n t D e P r i x E ve n t A r g s e ) { Console . WriteLine ( " Le nouveau prix est de : " + e . Prix ) ; }

Remarquez que la mthode voiture_ChangementDePrix prend dsormais deux paramtres. Le premier reprsente lobjet Voiture ; si nous en avions besoin, nous pourrions lutiliser avec un cast adquat. Le second reprsente lobjet contenant le prix de la voiture en promotion. noter que, de manire gnrale, nous allons surtout utiliser les vnements dnis par le framework .NET. Il sera donc assez rare davoir en dnir un soi-mme.

En rsum
Les dlgus permettent de crer des variables pointant vers des mthodes. Les dlgus sont la base des vnements. On utilise les expressions lambdas pour simplier lcriture des dlgus. Les vnements sont un mcanisme du C# permettant une classe dtre notie dun changement.

334

Chapitre

31
Dicult :

Grer les erreurs : les exceptions

ous avons parl rapidement des erreurs dans nos applications C# en disant quil sagissait dexceptions. Cest le moment den savoir un peu plus et surtout dapprendre les grer ! Dans ce chapitre, nous allons apprendre comment crer et intercepter une exception.

335

CHAPITRE 31. GRER LES ERREURS : LES EXCEPTIONS

Intercepter une exception


Rappelez-vous de ce code :
1 2

string chaine = " dix " ; int valeur = Convert . ToInt32 ( chaine ) ;

Si nous lexcutons, nous aurons un message derreur :


Exception non g r e : System . FormatException : Le format de la cha ne d entr e est incorrect . System . Number . StringToNumber ( String str , NumberStyles options , NumberBuffer & number , NumberFormatInfo info , Boolean parseDecimal ) System . Number . ParseInt32 ( String s , NumberStyles style , NumberFormatInfo info ) System . Convert . ToInt32 ( String value ) M a P r e m i e reAp plica tion . Program . Main ( String [] args ) dans C :\ Users \ Nico \ Documents \ Visual Studio 2010\ Projects \ C #\ M a P r e m i er eAppl icatio n \ MaPr emier eAppli catio n \ Program . cs : ligne 14

Lapplication nous ache un message derreur et plante lamentablement, produisant par la mme occasion un rapport derreur. Ce qui se passe en fait, cest que lors de la conversion, si le framework .NET narrive pas convertir correctement la chane de caractres en entier, il lve une exception. Cela veut dire quil informe le programme quil rencontre un cas limite qui ncessite dtre gr. Si ce cas limite nest pas gr, alors lapplication plante et cest le CLR qui intercepte lerreur et fait produire un rapport au systme dexploitation. Pourquoi une exception et pas un message derreur ?

Lintrt des exceptions est quelles sont types. Cen est ni des codes derreurs incomprhensibles. Cest le type de lexception, cest--dire sa classe, qui va nous permettre didentier le problme. Pour viter le plantage de lapplication, nous devons grer ces cas limites et intercepter les exceptions. Pour ce faire, il faut encadrer les instructions pouvant atteindre des cas limites avec le bloc dinstruction try...catch, par exemple :
1 2 3 4 5 6 7 8

try {

} catch ( Exception ) {

string chaine = " dix " ; int valeur = Convert . ToInt32 ( chaine ) ; Console . WriteLine ( " Ce code ne sera jamais affich " ) ;

336

INTERCEPTER UNE EXCEPTION


9 10

Console . WriteLine ( " Une erreur s ' est produite dans la tentative de conversion " ) ;

Si nous excutons ce bout de code, lapplication ne plantera plus et achera quune erreur sest produite. . . Nous avons attrap lexception leve par la mthode de conversion grce au motcl catch. Cette construction nous permet de surveiller lexcution dun bout de code, situ dans le bloc try et, sil y a une erreur, alors nous interrompons son excution pour traiter lerreur en allant dans le bloc catch. La suite du code dans le try, savoir lachage de la ligne avec Console.WriteLine, ne sera jamais excut, car lorsque la conversion choue, il saute directement au bloc catch. Inversement, il est possible de ne jamais passer dans le bloc catch si les instructions ne provoquent pas derreur :
1 2 3 4 5 6 7 8 9 10

try {

} catch ( Exception ) { Console . WriteLine ( " Nous ne passons jamais ici ... " ) ; }

string chaine = " 10 " ; int valeur = Convert . ToInt32 ( chaine ) ; Console . WriteLine ( " Conversion OK " ) ;

Ainsi le code ci-dessus achera uniquement que la conversion est bonne et, en toute logique, il ne passera pas dans le bloc de traitement derreur, car il ny en a pas eu. Il est possible dobtenir des informations sur lexception en utilisant un paramtre dans le bloc catch. Ce paramtre est une variable du type Exception, qui dans lexemple suivant sappelle ex :
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15

try {

} catch ( Exception ex ) { Console . WriteLine ( " Il y a un eu une erreur , plus d ' informations ci - dessous : " ) ; Console . WriteLine () ; Console . WriteLine ( " Message d ' erreur : " + ex . Message ) ; Console . WriteLine () ; Console . WriteLine ( " Pile d ' appel : " + ex . StackTrace ) ; Console . WriteLine () ; Console . WriteLine ( " Type de l ' exception : " + ex . GetType () ) ; }

string chaine = " dix " ; int valeur = Convert . ToInt32 ( chaine ) ;

Ici, nous achons le message derreur, la pile dappel et le type de lexception, ce qui donne le message suivant : 337

CHAPITRE 31. GRER LES ERREURS : LES EXCEPTIONS

Il y a un eu une erreur , plus d informations ci - dessous : Message d erreur : Le format de la cha ne d entr e est incorrect . Pile d appel : System . Number . StringToNumber ( String str , NumberStyles options , NumberBuffer & number , NumberFormatInfo info , Boolean parseDecimal ) System . Number . ParseInt32 ( String s , NumberStyles style , NumberFormatInfo info ) System . Convert . ToInt32 ( String value ) M a P r e m i e reAp plica tion . Program . Main ( String [] args ) dans C :\ Users \ Nico \ Documents \ Visual Studio 2010\ Projects \ C #\ M a P r e m i er eAppl icatio n \ MaPr emier eAppli catio n \ Program . cs : ligne 16 Type de l exception : System . FormatException

Dune manire gnrale, la mthode ToString() de lexception fournit des informations susantes pour identier lerreur :
1 2 3 4 5 6 7 8 9

try {

} catch ( Exception ex ) { Console . WriteLine ( " Il y a un eu une erreur : " + ex . ToString () ) ; }

string chaine = " dix " ; int valeur = Convert . ToInt32 ( chaine ) ;

Ce code achera :
Il y a un eu une erreur : System . FormatException : Le format de la cha ne d entr e est incorrect . System . Number . StringToNumber ( String str , NumberStyles options , NumberBuffer & number , NumberFormatInfo info , Boolean parseDecimal ) System . Number . ParseInt32 ( String s , NumberStyles style , NumberFormatInfo info ) System . Convert . ToInt32 ( String value ) M a P r e m i e reAp plica tion . Program . Main ( String [] args ) dans C :\ Users \ Nico \ Documents \ Visual Studio 2010\ Projects \ C #\ M a P r e m i er eAppl icatio n \ MaPr emier eAppli catio n \ Program . cs : ligne 16

Les exceptions peuvent tre de beaucoup de formes. Ici nous remarquons que lexception est de type System.FormatException. Cette exception est utilise en gnral lorsque 338

INTERCEPTER UNE EXCEPTION le format dun paramtre ne correspond pas ce qui est attendu. En loccurrence ici, nous attendons un paramtre de type chane de caractres qui reprsente un entier. Cest une exception spcique qui est ddie un type derreur prcis. Il faut savoir que comme beaucoup dautres objets du framework .NET, les exceptions spciques drivent dune classe de base, savoir la classe Exception, dont vous pouvez trouver la documentation via le code web suivant : Exceptions Code web : 368326

Il existe une hirarchie entre les exceptions. Globalement, deux grandes familles dexceptions existent : ApplicationException et SystemException. Elles drivent toutes les deux de la classe de base Exception. La premire est utilise lorsque des erreurs rcuprables sur des applications apparaissent ; la seconde est utilise pour toutes les exceptions gnres par le framework .NET. Par exemple, lexception que nous avons vue, FormatException drive directement de SystemException, qui drive elle-mme de la classe Exception. Le framework .NET dispose de beaucoup dexceptions correspondant beaucoup de situations. Notons encore au passage une autre exception bien connue des dveloppeurs qui est la NullReferenceException. Elle se produit lorsquon essaie daccder un objet qui vaut null. Par exemple :
1 2

Voiture voiture = null ; voiture . Vitesse = 10 ;

Vous aurez remarqu que dans la construction suivante :


1 2 3 4 5 6 7 8 9

try {

} catch ( Exception ex ) { Console . WriteLine ( " Il y a un eu une erreur : " + ex . ToString () ) ; }

string chaine = " dix " ; int valeur = Convert . ToInt32 ( chaine ) ;

nous voyons que le bloc catch prend en paramtre la classe de base Exception. Cela veut dire que nous souhaitons intercepter toutes les exceptions qui drivent de Exception ; cest--dire en fait toutes les exceptions, car toute exception drive forcment de la classe Exception. Cest utile lorsque nous voulons attraper toutes les exceptions. Mais savons-nous forcment quoi faire dans le cas de toutes les erreurs ? Il est possible dtre plus prcis an de nattraper quun seul type dexception. Il sut de prciser le type de lexception attendue comme paramtre du catch. Par exemple le code suivant nous permet dintercepter toutes les exceptions du type FormatException :
1 2 3

try {

string chaine = " dix " ;

339

CHAPITRE 31. GRER LES ERREURS : LES EXCEPTIONS


4 5 6 7 8 9

int valeur = Convert . ToInt32 ( chaine ) ; } catch ( FormatException ex ) { Console . WriteLine ( ex ) ; }

Cela veut par contre dire que si nous avons une autre exception ce moment-l, du style NullReferenceException, elle ne sera pas attrape ; ce qui fait que le code suivant va planter :
1 2 3 4 5 6 7 8 9

try {

} catch ( FormatException ex ) { Console . WriteLine ( " Erreur de format : " + ex ) ; }

Voiture v = null ; v . Vitesse = 10 ;

En eet, nous demandons uniquement la surveillance de lexception FormatException. Ainsi, lexception NullReferenceException ne sera pas attrape.

Intercepter plusieurs exceptions


Pour attraper les deux exceptions, il est possible denchaner les blocs catch avec des paramtres dirents :
1 2 3 4 5 6 7 8 9 10 11 12

try {

// code provoquant une exception } catch ( FormatException ex ) { Console . WriteLine ( " Erreur de format : " + ex ) ; } catch ( N u l lRe fe ren ce Exc ept io n ex ) { Console . WriteLine ( " Erreur de r f rence nulle : " + ex ) ; }

Dans ce code, cela veut dire que si le code surveill provoque une FormatException, alors nous acherons le message Erreur de format. . . ; sil provoque une NullReferenceException, alors nous acherons le message Erreur de rfrence nulle. . . . Notez bien que nous ne pouvons rentrer chaque fois que dans un seul bloc catch. Une autre solution serait dattraper une exception plus gnraliste par exemple SystemException dont drive FormatException et NullReferenceException : 340

INTERCEPTER PLUSIEURS EXCEPTIONS


1 2 3 4 5 6 7 8

try {

// code provoquant une exception } catch ( SystemException ex ) { Console . WriteLine ( " Erreur syst me : " + ex ) ; }

Par contre, le code prcdent attrape toutes les exceptions drivant de SystemException. Cest le cas de FormatException et NullReferenceException, mais cest aussi le cas pour beaucoup dautres exceptions. Lorsquon surveille un bloc de code, on commence en gnral par surveiller les exceptions les plus nes possible et on remonte en considrant les exceptions les plus gnrales, jusqu terminer par la classe Exception :
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20

// code provoquant une exception } catch ( FormatException ex ) { Console . WriteLine ( " Erreur de format : " + ex ) ; } catch ( N u l l R e fe ren ceE xc ept io n ex ) { Console . WriteLine ( " Erreur de r f rence nulle : " + ex ) ; } catch ( SystemException ex ) { Console . WriteLine ( " Erreur syst me autres que FormatException et Nu llR ef ere nc eEx cep ti on : " + ex ) ; } catch ( Exception ex ) { Console . WriteLine ( " Toutes les autres exceptions : " + ex ) ; }

try {

chaque excution, cest le bloc catch qui se rapproche le plus de lexception leve qui est utilis. Cest un peu comme une opration conditionnelle. Le programme vrie dans un premier temps que lexception nest pas une FormatException. Si ce nest pas le cas, il vriera quil na pas faire une NullReferenceException. Ensuite, il vriera quil ne sagit pas dune SystemException. Enn, il interceptera toutes les exceptions dans le dernier bloc de code, car Exception tant la classe mre, toutes les exceptions sont interceptes avec ce type. noter quil est possible dimbriquer les try...catch si cela savre pertinent. Par exemple :
1 2 3

string saisie = Console . ReadLine () ; try {

341

CHAPITRE 31. GRER LES ERREURS : LES EXCEPTIONS


4 5 6 7 8 9 10 11 12 13 14 15 16 17 18

} catch ( FormatException ) { try { double d = Convert . ToDouble ( saisie ) ; Console . WriteLine ( " La saisie est un double " ) ; } catch ( FormatException ) { Console . WriteLine ( " La saisie n ' est ni un entier , ni un double " ) ; } }

int entier = Convert . ToInt32 ( saisie ) ; Console . WriteLine ( " La saisie est un entier " ) ;

Ce code nous permet de tester si la saisie est un entier. Si une exception se produit, alors nous tentons de le convertir en double. Sil y a encore une exception, alors nous achons un message indiquant que les deux conversions ont chou.

Lever une exception


Il est possible de dclencher soi-mme la leve dune exception. Cest utile si nous considrons que notre code a atteint un cas limite, quil soit fonctionnel ou technique. Pour lever une exception, nous utilisons le mot-cl throw, suivi de linstance dune exception. Imaginons par exemple une mthode permettant de calculer la racine carre dun double. Nous pouvons obtenir un cas limite lorsque nous tentons de passer un double ngatif :
1 2 3 4 5 6

public static double RacineCarree ( double valeur ) { if ( valeur <= 0 ) throw new A r g u m e n t O u t O f R a n g e E x c e p t i o n ( " valeur " , " Le param tre doit tre positif " ) ; return Math . Sqrt ( valeur ) ; }

Ici, nous instancions une ArgumentOutOfRangeException en utilisant un constructeur deux paramtres. Ceux-ci permettent dindiquer le nom du paramtre ainsi que le message derreur. Cette exception est une exception du framework .NET utilise pour indiquer quun paramtre est en dehors des plages de valeurs autorises. Cest exactement ce quil nous faut ici. Puis, nous levons lexception avec le mot-cl throw. Nous pouvons utiliser la mthode ainsi :
1 2 3

static void Main ( string [] args ) { try

342

PROPAGATION DE LEXCEPTION
4 5 6 7 8 9 10 11

double racine = RacineCarree ( - 5 ) ; } catch ( Exception ex ) { Console . WriteLine ( " Impossible d ' effectuer le calcul : " + ex . Message ) ; }

Ce qui ache :
Impossible d effectuer le calcul : Le param tre doit tre positif Nom du param tre : valeur

Il aurait t possible de lever une exception plus gnrique avec la classe de base Exception :
1

throw new Exception ( " Le param tre doit tre positif " ) ;

Mais noubliez pas que plus lexception est nement type, plus elle sera facile traiter prcisment. Cela permet dviter dattraper toutes les exceptions dans le mme catch avec la classe de base Exception. noter que lorsque notre programme rencontre le mot-cl throw, il sarrte pour partir dans le bloc try...catch correspondant (sil existe). Cela signie quune mthode qui doit renvoyer un paramtre pourra compiler si son chemin se termine par une leve dexception, comme cest le cas pour le calcul de la racine carre.

Propagation de lexception
Il est important de noter que lorsquun bout de code se situe dans un bloc try...catch, tout le code qui est dessous est surveill, mme sil y a plusieurs mthodes qui sappellent les unes les autres. Par exemple, si nous appelons depuis la mthode Main() une Methode1(), qui appelle une Methode2(), qui appelle une Methode3(), qui lve une exception, alors nous serons capables de lintercepter depuis la mthode Main() avec un try...catch :
1 2 3 4 5 6 7 8 9

static void Main ( string [] args ) { try { Methode1 () ; } catch ( N o tI m pl em en t ed Ex ce p ti on ) { Console . WriteLine ( " On intercepte l ' exception de la m thode 3 " ) ;

343

CHAPITRE 31. GRER LES ERREURS : LES EXCEPTIONS


10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26

public static void Methode1 () { Methode2 () ; } public static void Methode2 () { Methode3 () ; } public static void Methode3 () { throw new N o tI mp le m en te d Ex ce pt i on () ; }

noter quune NotImplementedException est une exception utilise pour indiquer quun bout de code na pas encore t implment. Il est galement possible dattraper une exception, de la traiter et de choisir quelle continue se propager. Par exemple, imaginons que nous avons un bloc try...catch qui nous permet de surveiller tout notre programme et que nous ayons surveiller un bout de code ailleurs dans le programme qui peut produire une situation limite :
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23

static void Main ( string [] args ) { try { MaMethode () ; } catch ( Exception ex ) { // ici , on intercepte toutes les erreurs possibles en indiquant qu ' un probl me inattendu s ' est produit Console . WriteLine ( "L ' application a rencontr un probl me , un mail a t envoy l ' administrateur ... " ) ; E n v o y e r E x c e p t i o n A d m i n i s t r a t e u r ( ex ) ; } } public static void MaMethode () { try { Console . WriteLine ( " Veuillez saisir un entier : " ) ; string chaine = Console . ReadLine () ; int entier = Convert . ToInt32 ( chaine ) ; } catch ( FormatException )

344

CRER UNE EXCEPTION PERSONNALISE


24 25 26 27 28 29 30 31 32

Console . WriteLine ( " La saisie n ' est pas un entier " ) ; } catch ( Exception ex ) { E n r e g i s t r e r E r r e u r D a n s U n F i c h i e r D e L o g ( ex ) ; throw ; }

Jai une saisie faire et convertir en entier. Si la conversion choue, je suis capable de lindiquer lutilisateur en surveillant la FormatException. Par contre, si une exception inattendue se produit, je souhaite pouvoir faire quelque chose, en loccurrence enregistrer lexception dans un chier ; mais comme cest un cas limite non prvu je souhaite que lexception continue se propager an quelle soit attrape par le bloc try...catch qui permettra denvoyer un mail ladministrateur. Dans ce cas, jutilise un catch gnraliste pour traiter les exceptions inattendues an de loguer lexception, puis jutilise directement le mot-cl throw an de permettre de relever lexception.

Crer une exception personnalise


Grce au typage fort des exceptions, il est pratique dutiliser un type dexception pour reconnatre un cas limite, comme une erreur de conversion ou une exception de rfrence nulle. Nous allons pouvoir utiliser certaines de ces exceptions pour nos besoins, comme ce que nous avions fait avec lexception ArgumentOutOfRangeException. Bien sr, il est possible de crer nous-mmes nos exceptions an de lever nos propres exceptions correspondant des cas limites fonctionnels ou techniques. Par exemple, imaginons un site de-commerce qui ache une page correspondant au descriptif dun produit an de pouvoir le commander. Nous chargeons le produit. Si le produit nest plus en stock alors il peut tre judicieux de lever une exception an que le site puisse grer ce cas limite et acher un message en consquence. Crons donc notre exception personnalise : ProduitNonEnStockException. . . Pour ce faire, il sut de crer une classe qui drive de la classe de base Exception :
1 2 3

public class P r o d u i t N o n E n S t o c k E x c e p t i o n : Exception { }

Qui pourra tre utilise ainsi :


1 2 3 4 5 6 7

static void Main ( string [] args ) { try { ChargerProduit ( " TV HD " ) ; } catch ( P r o d u i t N o n E n S t o c k E x c e p t i o n ex )

345

CHAPITRE 31. GRER LES ERREURS : LES EXCEPTIONS


8 9 10 11 12 13 14 15 16 17 18 19

{ } }

Console . WriteLine ( " Erreur : " + ex . Message ) ;

public static Produit ChargerProduit ( string nomProduit ) { Produit produit = new Produit () ; // remplacer par le chargement du produit if ( produit . Stock <= 0 ) throw new P r o d u i t N o n E n S t o c k E x c e p t i o n () ; return produit ; }

Il serait intressant de pouvoir rendre lexception plus explicite en modiant par exemple la proprit message de lexception. Pour ce faire, il sut dutiliser la surcharge du constructeur prenant une chane de caractres en paramtre an de pouvoir mettre jour la proprit Message (qui est en lecture seule) :
1 2 3 4 5 6

public class P r o d u i t N o n E n S t o c k E x c e p t i o n : Exception { public P r o d u i t N o n E n S t o c k E x c e p t i o n () : base ( " Le produit n ' est pas en stock " ) { } }

Nous pouvons galement crer un constructeur qui prend le nom du produit en paramtre an de rendre le message encore plus prcis :
1 2 3 4 5 6

public class P r o d u i t N o n E n S t o c k E x c e p t i o n : Exception { public P r o d u i t N o n E n S t o c k E x c e p t i o n ( string nomProduit ) : base ( " Le produit " + nomProduit + " n ' est pas en stock " ) { } }

Que nous pourrons utiliser ainsi :


1 2 3 4 5 6 7

public static Produit ChargerProduit ( string nomProduit ) { Produit produit = new Produit () ; // remplacer par le chargement du produit if ( produit . Stock <= 0 ) throw new P r o d u i t N o n E n S t o c k E x c e p t i o n ( nomProduit ) ; return produit ; }

Ce qui donne une belle leve dexception :


Erreur : Le produit TV HD n est pas en stock

346

LE MOT-CL FINALLY noter que pour construire cette exception personnalise, nous avons driv de la classe de base Exception. Il aurait t galement possible de driver de la classe ApplicationException pour conserver une hirarchie cohrente dexceptions.

Le mot-cl finally
Nous avons vu que lorsquun code est surveill dans un bloc try...catch, on sortait du bloc try si jamais une exception tait leve pour atterrir dans le bloc catch. Cela veut dire quil est impossible de garantir quune instruction sera excute dans notre code, si jamais une exception nous fait sortir du bloc. Cest l quintervient le mot-cl finally. Il permet dindiquer que dans tous les cas, un code doit tre excut, quune exception intervienne ou pas. Par exemple :
1 2 3 4 5 6 7 8 9 10 11 12 13 14

try {

} catch ( FormatException ) { Console . WriteLine ( " Vous avez saisi autre chose qu ' un entier "); } finally { Console . WriteLine ( " Merci d ' avoir saisi quelque chose " ) ; }

string saisie = Console . ReadLine () ; int valeur = Convert . ToInt32 ( saisie ) ; Console . WriteLine ( " Vous avez saisi un entier " ) ;

Nous demandons une saisie utilisateur. Nous tentons de convertir cette saisie en entier. Si la conversion fonctionne, nous restons dans le bloc try :
111 Merci d avoir saisi quelque chose

Si la conversion lve une FormatException, le texte ach dans la console sera dirent :
abc Vous avez saisi autre chose qu un entier Merci d avoir saisi quelque chose

Dans les deux cas, nous passons obligatoirement dans le bloc finally pour acher le remerciement. Le bloc finally est utile par exemple quand il sagit de librer la mmoire, denregistrer des donnes, dcrire dans un chier de log, etc. 347

CHAPITRE 31. GRER LES ERREURS : LES EXCEPTIONS Il est important de remarquer que quelle que soit la construction, le bloc finally est toujours excut. Ainsi, mme si on relve une exception dans le bloc catch :
1 2 3 4 5 6 7 8 9 10 11 12

try {

Convert . ToInt32 ( " ppp " ) ; } catch ( FormatException ) { throw new N o tI mp le m en te d Ex ce pt i on () ; } finally { Console . WriteLine ( " Je suis quand m me pass ici " ) ; }

nous acherons toujours notre message. . . Mme lorsque nous souhaitons sortir dune mthode avec le mot-cl return :
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20

static void Main ( string [] args ) { MaMethode () ; } private static void MaMethode () { try { Convert . ToInt32 ( " ppp " ) ; } catch ( FormatException ) { return ; } finally { Console . WriteLine ( " Je suis quand m me pass ici " ) ; } }

Ici, le bloc finally est quand mme excut. Dans ce chapitre, nous avons vu comment grer les cas limites en utilisant le mcanisme des exceptions. Vous avez pu voir quil est trs pratique de grer les exceptions grce leurs types et quil est nalement trs facile de crer nos propres exceptions. Attention : la gestion des exceptions ne doit pas masquer les bugs. Vous ne devez pas entourer tout votre code de blocs try. . .catch et vous fliciter quil ny ait aucun bug. Cest au contraire une opportunit pour les identier et produire un rapport pour les corriger ultrieurement, par exemple en envoyant un e-mail avec le contenu du bug aux dveloppeurs. 348

LE MOT-CL FINALLY

En rsum
Les exceptions permettent de grer les cas limites dune mthode. On utilise le bloc try...catch pour encapsuler un bout de code surveiller. Il est possible de crer des exceptions personnalises en drivant de la classe de base Exception. On peut lever tout moment une exception grce au mot-cl throw. Les exceptions ne doivent pas servir masquer les bugs.

349

CHAPITRE 31. GRER LES ERREURS : LES EXCEPTIONS

350

Chapitre

32
Dicult :

TP : vnements et mto

ienvenue dans le dernier TP de cette partie. Tenez bon, aprs on change de domaine pour aborder dautres notions. Dans ce TP, nous allons pratiquer les vnements. Le but est de savoir en crer un et de pouvoir sy abonner pour tre noti dune information. Vous tes prts ? Alors cest parti !

351

CHAPITRE 32. TP : VNEMENTS ET MTO

Instructions pour raliser le TP


Nous allons raliser ici un mini simulateur de mto qui sera utilis par un statisticien an den faire des statistiques (logique !). Bon, cest le contexte, mais cest juste un exemple. Nesprez pas non plus raliser un vrai simulateur mto dans ce TP ! Bref : nous devons crer un simulateur de mto. Lorsque celui-ci est dmarr, il gnre autant de nombres alatoires que demand, des nombres entre 0 et 100. Si le nombre alatoire est infrieur 5, alors a veut dire que le temps est au soleil. Sil est suprieur ou gal 5 et infrieur 50, alors nous aurons des nuages. Sil est suprieur ou gal 50 et infrieur 90, alors nous aurons de la pluie. Sinon, nous aurons de lorage. Un vnement sera lev chaque changement de temps. Le but de notre statisticien est de sabonner aux vnements du simulateur mto an de compter le nombre de fois o il a fait soleil et le nombre de fois o le temps a chang. Il achera ensuite son rapport en indiquant ces deux rsultats et le pourcentage de fois o il a fait soleil (je veux bien que ce pourcentage soit un entier). Cest tout pour lnonc ! Maintenant, vous avez assez de connaissances pour que je ne dtaille pas plus. Bon courage !

Correction
Allez, cest parti pour la correction. Tout dabord, nous devons crer notre simulateur mto. Il sera reprsent par une classe :
1 2 3

public class SimulateurMeteo { }

Nous aurons galement besoin de quelque chose pour reprsenter le temps : soleil, nuage, pluie et orage. Une numration semble approprie :
1 2 3 4 5 6 7

public enum Temps { Soleil , Nuage , Pluie , Orage }

Enn, nous aurons notre statisticien :


1 2 3

public class Statisticien { }

Commenons par le simulateur de mto. Nous aurons besoin de plusieurs variables membres an de stocker notre gnrateur de nombres alatoires, le dernier temps quil a fait et le nombre de rptitions. Le nombre de rptitions pourra tre un paramtre du constructeur : 352

CORRECTION
1 2 3 4 5 6 7 8 9 10 11 12 13

public class SimulateurMeteo { private Temps ? ancienTemps ; private int nombreDeRepetitions ; private Random random ; public SimulateurMeteo ( int nombre ) { random = new Random () ; ancienTemps = null ; n om b r eDeRepetitions = nombre ; }

tant donn que nous allons dterminer plusieurs nombres alatoires, il est pertinent de ne pas rallouer chaque fois le gnrateur de nombre alatoire. Cest pour cela quon le cre une unique fois dans le constructeur de la classe. Crons maintenant une mthode permettant de dmarrer le simulateur et codons les rgles mtier du simulateur :
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26

public class SimulateurMeteo { // [...] Code supprim pour plus de clart [...] public void Demarrer () { for ( int i = 0 ; i < nombreDeRepetitions ; i ++) { int valeur = random . Next (0 , 100 ) ; if ( valeur < 5 ) GererTemps ( Temps . Soleil ) ; else { if ( valeur < 50 ) GererTemps ( Temps . Nuage ) ; else { if ( valeur < 90 ) GererTemps ( Temps . Pluie ) ; else GererTemps ( Temps . Orage ) ; } } } }

Cest trs simple, on boucle sur le nombre de rptitions. Un nombre alatoire est dtermin chaque itration. La mthode GererTemps prend en paramtre le temps dtermin partir du nombre alatoire. Cest cette mthode GererTemps qui aura pour but de lever un vnement quand le temps change : 353

CHAPITRE 32. TP : VNEMENTS ET MTO


1 2 3 4 5 6 7 8 9 10 11 12 13 14

public class SimulateurMeteo { public delegate void IlFaitBeauDelegate ( Temps temps ) ; public event IlFaitBeauDelegate QuandLeTempsChange ; // [...] Code supprim pour plus de clart [...] private void GererTemps ( Temps temps ) { if ( ancienTemps . HasValue && ancienTemps . Value != temps && QuandLeTempsChange != null ) QuandLeTempsChange ( temps ) ; ancienTemps = temps ; }

Ici, jai choisi de crer un seul vnement quand le temps change et de lui indiquer le temps quil fait en paramtre. Nous avons donc besoin dun dlgu qui prend un Temps en paramtre et qui ne renvoie rien - cest dailleurs souvent le cas des vnements. Puis nous avons besoin dun vnement du type du dlgu. Ensuite, si le temps a chang et que quelquun sest abonn lvnement, alors nous levons lvnement. Il ne reste plus qu remplir notre classe Statisticien. Cette classe a besoin de travailler sur une instance de la classe SimulateurMeteo ; nous pouvons donc lui en passer une dans les paramtres du constructeur. Nous utiliserons galement des variables membres prives permettant de stocker ses analyses :
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22

public class Statisticien { private SimulateurMeteo simulateurMeteo ; private int n o m b r e D e F o i s O u L e T e m p s A C h a n g e ; private int n o m b r e D e F o i s O u I l A F a i t S o l e i l ; public Statisticien ( SimulateurMeteo simulateur ) { simulateurMeteo = simulateur ; nombreDeFoisOuLeTempsAChange = 0; nombreDeFoisOuIlAFaitSoleil = 0; } public void DemarrerAnalyse () { simulateurMeteo . QuandLeTempsChange += simulateurMeteo_QuandLeTempsChange ; simulateurMeteo . Demarrer () ; } public void AfficherRapport () { Console . WriteLine ( " Nombre de fois o le temps a chang : " + nombreDeFoisOuLeTempsAChange );

354

CORRECTION
23 24

25 26 27 28 29 30 31 32 33

Console . WriteLine ( " Nombre de fois o il a fait soleil : " + nombreDeFoisOuIlAFaitSoleil ); Console . WriteLine ( " Pourcentage de beau temps : " + n o m b r e D e F o i s O u I l A F a i t S o l e i l * 100 / nombreDeFoisOuLeTempsAChange + " %");

private void s i m u l a t e u r M e t e o _ Q u a n d L e T e m p s C h a n g e ( Temps temps ) { if ( temps == Temps . Soleil ) n o m b r e D e F o i s O u I l A F a i t S o l e i l ++; n o m b r e D e F o i s O u L e T e m p s A C h a n g e ++; }

Notons que dans la mthode DemarrerAnalyse, nous nous abonnons lvnement de changement de temps. La mthode qui est appele lors de la notication est trs simple, elle incrmente les compteurs. Enn, lachage du rapport est trivial. Ici, comme nous navons que des entiers, la division produira un entier galement. Il ne reste plus qu faire fonctionner nos objets :
1 2 3 4 5 6 7 8 9 10

class Program { static void Main ( string [] args ) { SimulateurMeteo simulateurMeteo = new SimulateurMeteo ( 1000 ) ; Statisticien statisticien = new Statisticien ( simulateurMeteo ) ; statisticien . DemarrerAnalyse () ; statisticien . AfficherRapport () ; } }

Ici, je travaille sur 1000 rptitions. Voici le rsultat que jobtiens lexcution de lapplication :
Nombre de fois o le temps a chang : 646 Nombre de fois o il a fait soleil : 55 Pourcentage de beau temps : 8 %

videmment, vu que nous travaillons avec des nombres alatoires, chacun aura un rsultat dirent. Et voil, cest termin pour ce TP. Notre application est fonctionnelle ! Termin ? hmm. . . pas tout fait, allons un peu plus loin. 355

CHAPITRE 32. TP : VNEMENTS ET MTO

Aller plus loin


En ltat, ce code est fonctionnel. Cest parfait. Mais que se passe-t-il si nous dmarrons plusieurs fois lanalyse ? Nous pouvons essayer :
1 2 3 4 5 6 7 8 9 10 11 12 13

static void Main ( string [] args ) { SimulateurMeteo simulateurMeteo = new SimulateurMeteo ( 1000 ) ; Statisticien statisticien = new Statisticien ( simulateurMeteo ) ; statisticien . DemarrerAnalyse () ; statisticien . AfficherRapport () ; statisticien . DemarrerAnalyse () ; statisticien . AfficherRapport () ; statisticien . DemarrerAnalyse () ; statisticien . AfficherRapport () ;

Ce qui achera :
Nombre de fois Nombre de fois Pourcentage de Nombre de fois Nombre de fois Pourcentage de Nombre de fois Nombre de fois Pourcentage de o le temps a chang : 635 o il a fait soleil : 47 beau temps : 7 % o le temps a chang : 1917 o il a fait soleil : 163 beau temps : 8 % o le temps a chang : 3879 o il a fait soleil : 322 beau temps : 8 %

Les valeurs augmentent. . . alors quelles ne devraient pas. Eh oui, nous ne rinitialisons pas les entiers permettant de stocker les statistiques. Ce nest pas forcment un bug. Ici, comme je navais rien dit dans lnonc, il peut paratre pertinent de continuer incrmenter ces valeurs, comme a, je peux travailler sur une moyenne. Bon, disons que nous souhaitons les rinitialiser chaque fois ; il sut alors de remettre zro les compteurs dans la mthode :
1 2 3 4 5 6 7

public void DemarrerAnalyse () { nombreDeFoisOuLeTempsAChange = 0; nombreDeFoisOuIlAFaitSoleil = 0; simulateurMeteo . QuandLeTempsChange += simulateurMeteo_QuandLeTempsChange ; simulateurMeteo . Demarrer () ; }

Cependant, les compteurs augmentent toujours, moins vite, mais quand mme ! Je suis sr que vous laviez devin et que vous navez pas fait lerreur. En fait, cela vient 356

ALLER PLUS LOIN de labonnement lvnement. Chaque fois que nous dmarrons lanalyse, nous nous rabonnons lvnement. Comme lvnement est multidiusion, nous rajoutons en fait chaque fois un appel la mthode avec le +=. Ce qui veut dire qu la deuxime fois, nous appellerons la mthode deux fois, ce qui fera doubler les rsultats. la troisime fois, ils triplent. . . Nous avons donc une erreur. Soit il faut sabonner une seule fois lvnement, soit il faut se dsabonner la n de lanalyse, quand ce nest plus utile de recevoir lvnement. Cest cette dernire mthode que je vais vous montrer. Il sut de faire :
1 2 3 4 5 6 7 8

public void DemarrerAnalyse () { nombreDeFoisOuLeTempsAChange = 0; nombreDeFoisOuIlAFaitSoleil = 0; simulateurMeteo . QuandLeTempsChange += simulateurMeteo_QuandLeTempsChange ; simulateurMeteo . Demarrer () ; simulateurMeteo . QuandLeTempsChange -= simulateurMeteo_QuandLeTempsChange ; }

On utilise loprateur -= pour enlever la mthode du dlgu. Dune manire gnrale, il est bienvenu de se dsabonner dun vnement lorsque lon sait quon ne va plus sen servir. Cela permet dviter dencombrer la mmoire qui ne saurait pas forcment se librer toute seule. Je nen dis pas plus car ceci est un concept avanc de gestion de mmoire. Gardez seulement lesprit que si on a lopportunit de se dsabonner dun vnement, il faut le faire. Enn, nous pouvons simplier notre code en ne crant pas notre dlgu. Eectivement, dans la mesure o celui-ci possde un seul paramtre, il est possible de le remplacer par le dlgu Action<T>. Il faut juste supprimer la dclaration du dlgu et remplacer la dclaration de lvnement par :
1 2 3 4

public class SimulateurMeteo { public event Action < Temps > QuandLeTempsChange ; }

Vous pouvez tlcharger tous les codes sources de cet exercice grce au code web suivant : Copier ce code Code web : 179137

357

CHAPITRE 32. TP : VNEMENTS ET MTO

358

Quatrime partie

C# avanc

359

Chapitre

33
Dicult :

Crer un projet bibliothques de classes

our linstant, nous navons cr que des projets de type application console. Il existe plein dautres modles de projet. Un des plus utiles est le projet permettant de crer des bibliothques de classes. Il sagit dun projet qui va permettre de contenir des classes que nous pourrons utiliser dans des applications. Exactement comme la bibliothque de classes du framework .NET qui nous permet dutiliser la classe Math ou les exceptions, ou plein dautres choses. . . Ce type de projet permet donc de crer une assembly sous la forme dun chier avec lextension .dll. Ces assemblys sont donc des bibliothques qui seront utilisables par nimporte quelle application utilisant un langage compatible avec le framework .NET.

361

CHAPITRE 33. CRER UN PROJET BIBLIOTHQUES DE CLASSES

Pourquoi crer une bibliothque de classes ?


Globalement pour deux raisons que nous allons dtailler : Rutilisabilit. Architecture. Rutilisabilit : Comme indiqu en introduction, le projet de type bibliothque de classes permet dobtenir des assemblys avec lextension .dll. Nous pouvons y mettre tout le code C# que nous voulons, notamment des classes qui auront un intrt tre utilises plusieurs endroits ou partages par plusieurs applications. Cest le cas des assemblys du framework .NET. Elles possdent plein de code trs utile que nous aurons avantage utiliser pour crer nos applications. De la mme faon, nous allons pouvoir crer des classes qui pourront tre rutilises plusieurs endroits. Comme notre classe permettant de grer les listes chanes. Nimporte quel programme qui utilise des listes chanes aura intrt ne pas tout rinventer et simplement rutiliser ce code prt lemploi. Il sura pour ce faire de rutiliser cette classe en rfrenant lassembly, comme nous lavons dj vu. Architecture : Lautre avantage dans la cration de bibliothques de classes est de pouvoir architecturer son application de manire faciliter sa mise en place, sa maintenabilit et lvolutivit du code. Larchitecture informatique cest comme larchitecture dune maison. Si nous mettons la douche au mme endroit que le compteur lectrique, ou que la machine laver est juste ct de la chambre coucher, cela peut poser des problmes. De mme, que penser dun architecte qui place les toilettes ct du lit ? Une maison mal architecture est une maison o il ne fait pas bon vivre. De mme, une application mal architecture est une application dont il ne fait pas bon faire la maintenance applicative ! Architecturer son application est important surtout si lapplication est grosse. Par exemple, une bonne pratique dans une application informatique est la dcomposition en couches. Nous nallons pas faire ici un cours darchitecture, mais le but est de sparer les composantes de lapplication. Un grand nombre dapplications de gestion est compos dune couche de prsentation, dune couche mtier et dune couche daccs aux donnes, les couches communiquant entre elles. Le but est de limiter les modications dune couche lors de la modication dune autre. Si toutes les couches taient mlanges alors chaque modication impliquerait une srie de modications en chane. On peut faire une analogie avec un plat de lasagnes et un plat de spaghettis. Il est dicile de toucher un spaghetti sans toucher les autres. Cependant, il pourrait tre envisageable de toucher la couche du dessus du plat de lasagnes pour rajouter un peu de fromage sans perturber ce quil y a dessous. Miam ! Il est donc intressant davoir une bibliothque de classes qui gre laccs aux donnes, 362

CRER UN PROJET DE BIBLIOTHQUE DE CLASSE une autre qui gre les rgles mtier et une autre qui soccupe dacher le tout. Je marrte l sur larchitecture, vous aurez loccasion de vous y confronter bien assez tt !

Crer un projet de bibliothque de classe


Pour crer une bibliothque de classes, on utilise lassistant de cration de nouveau projet (Fichier > Nouveau > Nouveau projet), comme on la fait pour une application console sauf quici, on utilisera le modle Bibliothque de classes (voir la gure 33.1). Ne le faisons pas encore.

Figure 33.1 Choix dun projet de type Bibliothque de classes Si nous faisons cela, Visual C# Express va nous crer une nouvelle solution contenant le projet de bibliothques de classes. Cest possible sauf quen gnral, on ajoute une bibliothque de classes pour quelle serve dans le cadre dune application. Cela veut dire que nous pouvons ajouter ce nouveau projet notre solution actuelle. Ainsi, celle-ci contiendra deux projets : Lapplication console, qui sera excutable par le systme dexploitation. La bibliothque de classes, qui sera utilise par lapplication. Pour ce faire, on peut faire un clic droit sur notre solution et faire Ajouter > Nouveau Projet, comme indiqu la gure 33.2. Ici, on choisit le projet de type bibliothque de classes, sans oublier de lui donner un nom, par exemple MaBibliotheque (voir gure 33.3). Visual C# Express nous cre notre projet et lajoute la solution, comme on peut le voir dans lexplorateur de solutions (voir gure 33.4). 363

CHAPITRE 33. CRER UN PROJET BIBLIOTHQUES DE CLASSES

Figure 33.2 Ajout dun nouveau projet la solution

Figure 33.3 Cration dun projet Bibliothque de classes appel MaBibliotheque

Figure 33.4 Lexplorateur de solution ache les deux projets contenus dans la solution 364

PROPRITS DE LA BIBLIOTHQUE DE CLASSE Il le gnre avec un chier Class1.cs que nous pouvons supprimer. Remarquez que lapplication console est en gras. Cela veut dire que cest ce projet que Visual C# Express va tenter de dmarrer lorsque nous utiliserons les touches Ctrl + F5 . Si ce nest pas le cas, il peut tenter de dmarrer la bibliothque, ce qui na pas de sens vu quelle na pas de mthode Main(). Dans ce cas, vous pourrez le changer en cliquant droit sur le projet console et en choisissant Dfinir comme projet de dmarrage. Dans ce projet, nous pourrons dsormais crer nos classes en ajoutant de nouvelles classes au projet, comme on la dj fait avec lapplication console. Ajoutons par exemple la classe suivante :
1 2 3 4 5 6 7 8 9 10

namespace MaBibliotheque { public class Bonjour { public void DireBonjour () { Console . WriteLine ( " Bonjour depuis la biblioth que MaBibliotheque " ) ; } } }

Proprits de la bibliothque de classe


Ouvrons la fentre des proprits de notre projet, en faisant un clic droit sur le projet, puis Proprits (voir la gure 33.5).

Figure 33.5 Le nom de lassembly et lespace de noms par dfaut dans les proprits du projet Nous pouvons voir direntes informations et notamment dans longlet Application 365

CHAPITRE 33. CRER UN PROJET BIBLIOTHQUES DE CLASSES que le projet possde un nom dassembly, qui correspond ici au nom du projet ainsi quun espace de noms par dfaut, qui correspond galement au nom du projet. Le nom de lassembly servira identier lassembly, alors que lespace de noms par dfaut sera celui donn lorsque nous ajouterons une nouvelle classe (ou autre) notre projet. Cet espace de noms pourra tre chang, mais en gnral on garde un nom cohrent avec les arborescences de notre projet. Notons au passage quil y a une option permettant dindiquer la version du framework .NET que nous souhaitons utiliser. Ici, nous gardons la version 4.

Gnrer et utiliser une bibliothque de classe


Nous pouvons alors compiler cette bibliothque de classes, soit individuellement, soit en compilant tout le projet. Rendons-nous dans le rpertoire o nous avons sauvegard notre bibliothque. Nous voyons dans le rpertoire de sortie (chez moi : C:\Users\Nico-PC\Documents\Visual Studio 2010\Projects\C#\MaPremiereApplication\MaBibliotheque\bin\Release) quil y a un chier du nom de notre projet dont lextension est .dll. Cest notre bibliothque de classes (mme sil ny a quune classe dedans !). Elle possde le mme nom que celui que nous avons vu dans les proprits du projet. Jen prote pour vous faire remarquer qu son ct, il y a galement un chier du mme nom avec lextension .pdb. Je peux enn vous rvler de quoi il sagit. Ce chier contient les informations de dbogages, utiles lorsque nous dboguons notre application. Sans ce chier, il est impossible de dboguer lintrieur du code de notre classe. Revenons lassembly gnre. Cest cette dll quil faudra rfrencer dans notre projet, comme nous lavons vu au chapitre sur le rfrencement dassemblys. Si vous avez un doute, nhsitez pas retourner le consulter. Ainsi, nous serons en mesure dutiliser la classe de notre bibliothque. Rappelez-vous, pour rfrencer une assembly, nous faisons un clic droit sur les rfrences du projet et slectionnons Ajouter une rfrence. Nous pourrons rfrencer notre bibliothque de deux manires ; soit en utilisant longlet Parcourir, qui va nous permettre de pointer directement le chier dll contenant nos classes (voir gure 33.6) ; soit en rfrenant directement le projet de bibliothque de classes qui se trouve dans notre solution en utilisant longlet Projet (voir gure 33.7). Cest ce choix qui doit tre privilgi lorsque notre solution contient les projets rfrencer. Gnralement, vos bibliothques vont voluer en mme temps que votre programme, donc il est judicieux de les avoir dans la mme solution que lapplication. Nous pourrons donc faire des rfrences projet. Si cependant les bibliothques sont stables et ne sont pas amenes voluer, alors vous pourrez les rfrencer directement partir des dll ; vous y gagnerez en temps de compilation. Utilisons dsormais notre classe avec le code suivant :
1 2

MaBibliotheque . Bonjour bonjour = new MaBibliotheque . Bonjour () ; bonjour . DireBonjour () ;

366

GNRER ET UTILISER UNE BIBLIOTHQUE DE CLASSE

Figure 33.6 Rfrencement direct de lassembly

Figure 33.7 Rfrencement du projet

367

CHAPITRE 33. CRER UN PROJET BIBLIOTHQUES DE CLASSES Vous pouvez aussi, bien sr, inclure lespace de noms MaBibliotheque pour viter davoir prxer la classe. En gnral, lespace de noms dune bibliothque est dirent de celui de lapplication. Notez que pour quune classe, comme la classe Bonjour, puisse tre utilise par une application rfrenant son assembly, elle doit tre dclare en public an quelle soit visible par tout le monde.

Le mot-cl internal
Nous avons dj vu ce mot-cl lorsque nous avons parl de visibilit avec notamment les autres mots-cls public, private et protected. Cest avec les assemblys que le mot-cl internal prend tout son sens. Il permet dindiquer quune classe, mthode ou proprit ne sera accessible qu lintrieur dune assembly. Cela permet par exemple quune classe soit utilisable par toutes les autres classes de cette assembly mais quelle ne puisse pas tre utilise par une application rfrenant lassembly. Crons par exemple dans notre bibliothque de classes les trois classes suivantes :
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30

public class Client { private string login ; public string Login { get { return login ; } } private string motDePasse ; public string MotDePasse { get { return motDePasse . Crypte () ; } } public Client ( string loginClient , string motDePasseClient ) { login = loginClient ; motDePasse = motDePasseClient ; }

public static class Generateur { public static string O b t en i r Id e n ti f i an t U ni q u e () { Random r = new Random () ; string chaine = string . Empty ; for ( int i = 0 ; i < 10 ; i ++) { chaine += r . Next (0 , 100 ) ;

368

LE MOT-CL INTERNAL
31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47

} return chaine . Crypte () ;

internal static class Encodage { internal static string Crypte ( this string chaine ) { return Convert . ToBase64String ( Encoding . Default . GetBytes ( chaine ) ) ; } internal static string Decrypte ( this string chaine ) { return Encoding . Default . GetString ( Convert . FromBase64String ( chaine ) ) ; }

Vous pouvez copier ce code en utilisant le code web suivant : Copier ce code Code web : 888787
Les deux premires sont des classes publiques qui peuvent tre utilises depuis nimporte o, par exemple depuis le programme principal :
1 2 3 4 5 6 7 8 9 10

class Program { static void Main ( string [] args ) { Client client = new Client ( " Nico " , " 12345 " ) ; Console . WriteLine ( client . MotDePasse ) ; Console . WriteLine ( Generateur . O b t en i r I de n t if i a nt U n iq u e () );

Par contre, la classe Encodage nest accessible que pour les deux autres classes, car elles sont dans la mme assembly. Cela veut dire que si nous tentons de lutiliser depuis notre mthode Main(), nous aurons des erreurs de compilation :
1 2 3 4 5 6

static void Main ( string [] args ) { string chaine = " 12345 " . Crypte () ; } Encodage . Crypte ( " 12345 " ) ;

369

CHAPITRE 33. CRER UN PROJET BIBLIOTHQUES DE CLASSES Cet exemple permet dillustrer lintrt, qui nest pas toujours vident, du mot-cl internal. noter quil existe enn le mot-cl protected internal qui permet dindiquer que des lments sont accessibles un niveau internal pour les classes dune mme assembly mais protected pour les autres assemblys, ce qui permet dappliquer les principes de substitutions ou dhritage. Voil, vous avez vu comment crer une bibliothque de classes ! Nhsitez pas crer ce genre de projet an dy mettre toutes les classes qui peuvent tre utilises par plusieurs applications. Comme a, il sut dune simple rfrence pour accder au code qui y est contenu. Vous vous en servirez galement pour mieux architecturer vos applications, le code sen trouvera plus clair et plus maintenable. La traduction en anglais de bibliothque est library , vous verrez souvent ce mot-l sur internet. Vous le verrez galement mal francis en librairie , ce qui est videmment une erreur !

En rsum
Un projet de bibliothque de classes permet de regrouper des classes pouvant tre utilises entre plusieurs applications. Les bibliothques de classes gnrent des assemblys avec lextension .dll. Elles permettent galement de mieux architecturer un projet.

370

Chapitre

34
Dicult :

Plus loin avec le C# et .NET

aintenant que nous en savons plus, nous allons pouvoir aborder quelques notions qui me paraissent importantes et que nous navons pas encore traites. Ce sera loccasion dapprofondir nos connaissances et de comprendre un peu mieux certains points qui auraient pu vous paratre douteux. Nous allons voir des instructions C# supplmentaires qui vont nous permettre de complter nos connaissances en POO. Nous en proterons galement pour dtailler comment le framework .NET gre les types en mmoire. Vous en saurez plus sur le formatage des chanes et aurez un aperu du ct obscur du framework .NET, la rexion ! Bref, tout plein de nouvelles cordes nos arcs nous permettant dtre de plus en plus ecace avec le C#. Ces nouveaux concepts vous serviront sans doute moins souvent, mais sont importants connatre.

371

CHAPITRE 34. PLUS LOIN AVEC LE C# ET .NET

Empcher une classe de pouvoir tre hrite


Parmi ces nouveaux concepts, nous allons voir comment il est possible de faire en sorte quune classe ne puisse pas tre hrite. Rappelez-vous, un moment, jai dit quon ne pouvait pas crer une classe qui spcialise la classe String. Cest quoi ce mystre ? Nous, quand nous crons une classe, nimporte qui peut en hriter. Pourquoi pas la classe String ? Cest parce que je vous avais cach le mot-cl sealed. Il permet dempcher la classe dtre hrite. Tout simplement ! Pourquoi vouloir empcher dtendre une classe en la drivant ? Pour plusieurs raisons, mais gnralement nous allons recourir ce mot-cl lorsquune classe est trop spcique une mthode ou une bibliothque et que lon souhaite empcher quelquun de pouvoir obtenir du code instable en tendant son fonctionnement. Cest typiquement le cas pour la classe String. Il y a beaucoup de classes dans le framework .NET qui sont marques comme sealed an dviter que lon puisse faire nimporte quoi. Il faut par contre faire attention car lemploi de ce mot-cl restreint normment la faon dont on pourrait employer cette classe. Pour dclarer une classe sealed 1 il sut de faire prcder le mot-cl class du mot-cl sealed :
1 2 3

public sealed class C l a ss e I mp o s s ib l e AD e r iv e r { }

Ainsi, toute tentative dhritage :


1 2 3

public class MaClasse : C l as s e Im p o ss i b le A D er i v er { }

se soldera par une erreur de compilation dshonorante :


impossible de d river du type sealed Ma Premi ereApp licat ion . ClasseImpossibleADeriver

Le mot-cl sealed fonctionne galement pour les mthodes, dans ce cas, cela permet dempcher la substitution dune mthode. Reprenons notre exemple du chien muet :
1 2 3 4 5 6 7

public class Chien { public virtual void Aboyer () { Console . WriteLine ( " Wouf " ) ; } }
1. Ce qui signie scelle , en anglais.

372

PRCISIONS SUR LES TYPES ET GESTION MMOIRE


8 9 10 11 12 13 14 15

public class ChienMuet : Chien { public sealed override void Aboyer () { Console . WriteLine ( " ... " ) ; } }

Ici, nous marquons la mthode comme sealed, ce qui fait quune classe qui drive de ChienMuet ne sera pas capable de remplacer la mthode permettant daboyer. Le code suivant :
1 2 3 4 5 6 7

public class Ch i en Av ec S yn th es e Vo ca le : ChienMuet { public override void Aboyer () { Console . WriteLine ( " Bwarf " ) ; } }

entranera lerreur de compilation :


Ma P r e m i e r e A p p licati on . Ch ie nA v ec Sy nt h es eV oc a le . Aboyer () : ne peut pas se substituer un membre h rit M a P r e m i e r e Appl icatio n . ChienMuet . Aboyer () , car il est sealed

Voil pour ce mot-cl, utiliser en connaissance de cause !

Prcisions sur les types et gestion mmoire


Nous avons vu beaucoup de types dirents au cours des chapitres prcdents. Nous avons vu les structures, les classes, les numrations, les dlgus, etc. Certains sont des types valeur et dautres des types rfrence ; ils sont tous des objets. Voici la gure 34.1 un petit schma rcapitulatif des types que nous avons dj vus. Nous avons dit brivement quils taient grs diremment par le framework .NET et que les variables de type valeur contenaient directement la valeur, alors que les variables de type rfrence possdent un lien vers un objet en mmoire. Ce qui se passe en fait quand nous dclarons une variable de type valeur, cest que nous crons directement la valeur en mmoire dans ce quon appelle la pile . Cest une zone mmoire (limite) o il est trs rapide de mettre et de chercher des valeurs. De plus, cette mmoire na pas besoin dtre libre par le ramasse-miettes (que nous verrons un peu plus loin). Cest pour cela que pour des raisons de performances on peut tre amen choisir les types valeur. Par exemple lorsque nous faisons ce code :
1 2

int age = 10 ; double pourcentage = 5 . 5 ;

373

CHAPITRE 34. PLUS LOIN AVEC LE C# ET .NET

Figure 34.1 Rcapitulatif des types Voici ce quil se passe : la variable age est une adresse mmoire contenant la valeur 10. Cest la mme chose pour la variable pourcentage sauf que lemplacement mmoire est plus important car on peut stocker plus de choses dans un double que dans un int. Je vous renvoie la gure 34.2 pour une explication en images ! En ce qui concerne les types rfrence, lorsque nous instancions par exemple une classe, le C# instancie la variable maVoiture dans la pile et lui met dedans une rfrence vers le vrai objet qui lui est allou sur une zone mmoire de capacit plus importante, mais accs plus lent, que lon appelle le tas . Comme il est gr par le framework .NET, on lappelle le tas manag .

Figure 34.2 Les types valeur sont stocks sur la pile Si nous avons le code suivant :
1 2 3 4 5 6 7

public class Voiture { public int Vitesse { get ; set ; } public string Marque { get ; set ; } } Voiture maVoiture = new Voiture { Vitesse = 10 , Marque = "

374

PRCISIONS SUR LES TYPES ET GESTION MMOIRE


Peugeot " };

nous aurons en mmoire le processus que vous pouvez observer la gure 34.3.

Figure 34.3 Les types rfrence ont leur rfrence alloue sur la pile et leur objet allou sur le tas manag noter que lorsquune variable sort de sa porte et quelle nest plus utilisable par qui que ce soit, alors la case mmoire sur la pile est marque comme de nouveau libre. Voil grosso modo comment est gre la mmoire. Il manque encore un lment fondamental du mcanisme de gestion mmoire : le ramasse-miettes. Nous en avons dj parl brivement, le ramasse-miettes sert librer la mmoire qui nest plus utilise dans le tas manag. Il y aurait de quoi crire un gros tutoriel rien que sur son fonctionnement, aussi nous allons expliquer rapidement comment il fonctionne sans trop rentrer dans les dtails. Le ramasse-miettes est souvent dnomm par sa traduction anglaise, le garbage collector. Pourquoi librer la mmoire ? On a vu que quand une variable sort de porte, alors la case mmoire sur la pile est marque comme nouveau libre. Cest trs bien avec les types valeur qui sont uniquement sur la pile. Mais avec les types rfrence ? Que donne le code suivant lorsque nous quittons la mthode CreerVoiture ?
1 2 3 4 5 6 7 8 9 10 11 12 13

public class Voiture { public int Vitesse { get ; set ; } public string Marque { get ; set ; } } static void Main ( string [] args ) { CreerVoiture () ; } public static void CreerVoiture () {

375

CHAPITRE 34. PLUS LOIN AVEC LE C# ET .NET


14 15

Voiture maVoiture = new Voiture { Vitesse = 10 , Marque = " Peugeot " };

Nous avons dit que la mmoire sur la pile redevenait libre. La rfrence vers lobjet est donc casse. Cependant, la mmoire sur le tas est toujours occupe (voir gure 34.4).

Figure 34.4 Lobjet est toujours prsent sur le tas, mais nest plus rfrenc Cela veut dire que notre objet est toujours en mmoire sauf que la variable maVoiture nexiste plus et donc, ne rfrence plus cet objet. Dans un langage comme le C++, nous aurions t obligs de vider explicitement la mmoire rfrence par la variable. En C#, pas besoin ! Cest le ramasse-miettes qui le fait pour nous. Encore un truc de fainant a ! En fait, cest plutt une scurit quun truc de fainant. Cest la garantie que la mmoire utilise par les objets qui nexistent plus est vide et, du coup, redevient disponible pour de nouveaux objets. Grce au ramasse-miettes, nous vitons ce que lon appelle les fuites mmoire. Cest super a mais il passe quel moment ce ramasse-miettes ?

La rponse est simple : on ne sait pas quand il passe. En fait, il passe quand il a besoin de mmoire ou quand lapplication semble ne rien faire. videmment, lorsque le ramassemiettes passe, les performances de lapplication sont un petit peu pnalises. Cest pour cela que lalgorithme de passage du ramasse-miettes est optimis pour essayer de dranger le moins possible lapplication, lors des moments dinactivit par exemple. Par contre, quand il y a besoin de mmoire, il ne se pose pas la question : il passe quand mme. Il y aurait beaucoup de choses dire encore sur ce mcanisme mais arrtons-nous l. Retenons que le ramasse-miettes est un superbe outil qui nous permet de garantir lintgrit de notre mmoire et quil seorce de nous embter le moins possible. Il est important de librer les ressources dont on na plus besoin quand cest possible, par exemple en se dsabonnant dun vnement ou lorsque nous avons utilis des ressources natives (ce que nous ne verrons pas dans ce livre) ou lors daccs des chiers ou des bases de donnes. 376

MASQUER UNE MTHODE

Masquer une mthode


Dans la partie prcdente sur la substitution, nous avons vu quon pouvait substituer une mthode grce au mot-cl override. Cette mthode devait dailleurs stre dclare comme candidate cette substitution grce au mot-cl virtual. Rappelez-vous de ce code :
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15

public class Chien { public virtual void Aboyer () { Console . WriteLine ( " Wouaf ! " ) ; } } public class Caniche : Chien { public override void Aboyer () { Console . WriteLine ( " Wiiff " ) ; } }

Si nous instancions des chiens et des caniches de cette faon :


1 2 3 4 5 6 7 8 9 10

static void Main ( string [] args ) { Chien chien = new Chien () ; Caniche caniche = new Caniche () ; Chien c a n i c h eT r a i t e C o m m eU n C h i e n = new Caniche () ; chien . Aboyer () ; caniche . Aboyer () ; c a n i c h e T r a i t e C o m m eU n C h i e n . Aboyer () ;

Nous aurons :
Wouaf ! Wiiff Wiiff

En eet, le premier objet est un chien et les deux suivants sont des caniches. Grce la substitution, nous faisons aboyer notre chien, notre caniche et notre caniche qui se fait manipuler comme un chien. Il est possible de masquer des mthodes qui ne sont pas forcment marques comme virtuelles grce au mot-cl new. ce moment-l, la mthode ne se substitue pas la mthode drive mais la masque pour les types drivs. Voyons cet exemple :
1

public class Chien

377

CHAPITRE 34. PLUS LOIN AVEC LE C# ET .NET


2 3 4 5 6 7 8 9 10 11 12 13 14 15

public void Aboyer () { Console . WriteLine ( " Wouaf ! " ) ; }

public class Caniche : Chien { public new void Aboyer () { Console . WriteLine ( " Wiiff " ) ; } }

Remarquons que la mthode Aboyer() de la classe Chien nest pas marque virtual et que la mthode Aboyer de la classe Caniche est marque avec le mot-cl new. Il ne sagit pas ici de substitution. Il sagit juste de deux mthodes qui portent le mme nom, mais la mthode Aboyer() de la classe Caniche se charge de masquer la mthode Aboyer() de la classe mre. Ce qui fait que les prcdentes instanciations :
1 2 3 4 5 6 7 8 9 10

static void Main ( string [] args ) { Chien chien = new Chien () ; Caniche caniche = new Caniche () ; Chien c a n i c h eT r a i t e C o m m e Un C h i e n = new Caniche () ; chien . Aboyer () ; caniche . Aboyer () ; c a n i c he T r a i t e C o m m eU n C h i e n . Aboyer () ;

produiront cette fois-ci :


Wouaf ! Wiiff Wouaf !

Cest le dernier cas qui peut surprendre. Rappelez-vous, il ne sagit pas dun remplacement de mthode cette fois-ci, et donc le fait de manipuler le caniche en tant que Chien ne permet pas de remplacer le comportement de la mthode Aboyer(). Dans ce cas, on appelle bien la mthode Aboyer correspondant au type Chien, ce qui a pour eet dacher Wouaf ! . Si nous navions pas utilis le mot-cl new, nous aurions eu le mme comportement mais le compilateur nous aurait avertis de ceci grce un avertissement :
M a P r e m i e r eAppl icati on . Caniche . Aboyer () masque le membre h rit M a P r e mie reApp licati on . Chien . Aboyer () . Utilisez le mot - cl new si le masquage est intentionnel .

378

LE MOT-CL YIELD Il nous prcise que nous masquons eectivement la mthode de la classe mre et que si cest fait exprs, alors il faut lindiquer grce au mot-cl new an de lui montrer que nous savons ce que nous faisons. Notez quand mme que dans la majorit des cas, en POO, ce que vous voudrez faire, cest bien substituer la mthode et non la masquer. Cest le mme principe pour les variables, il est galement possible de masquer une variable, quoiquen gnral, cela rete plutt une erreur dans le code. Si nous faisons :
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19

public class Chien { protected int age ; } public class Caniche : Chien { private int age ; public Caniche () { age = 0 ; } public override string ToString () { return "J ' ai " + age + " ans " ; }

Alors le compilateur nous indique que la variable age de la classe Caniche masque la variable age initialement hrite de la classe Chien. Si cest intentionnel, il nous propose de la marquer avec le mot cl new. Sinon, cela nous permet de constater que cette variable est peut-tre inutile. . . (srement dailleurs !)

Le mot-cl yield
Nous avons vu dans le TP sur les gnriques comment crer un numrateur. Cest une tche un peu lourde qui ncessite pas mal de code. Oublions un instant que la classe String est numrable et crons pour lexemple une classe qui permet dnumrer une chane de caractres. Nous aurions pu faire quelque chose comme a :
1 2 3 4 5 6 7 8 9

public class ChaineEnumerable : IEnumerable < char > { private string chaine ; public ChaineEnumerable ( string valeur ) { chaine = valeur ; } public IEnumerator < char > GetEnumerator ()

379

CHAPITRE 34. PLUS LOIN AVEC LE C# ET .NET


10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55

{ }

return new ChaineEnumerateur ( chaine ) ;

IEnumerator IEnumerable . GetEnumerator () { return new ChaineEnumerateur ( chaine ) ; }

public class ChaineEnumerateur : IEnumerator < char > { private string chaine ; private int indice ; public ChaineEnumerateur ( string valeur ) { indice = -1 ; chaine = valeur ; } public char Current { get { return chaine [ indice ]; } } public void Dispose () { } object IEnumerator . Current { get { return Current ; } } public bool MoveNext () { indice ++; return indice < chaine . Length ; } public void Reset () { indice = -1 ; }

Vous pouvez copier ce code en utilisant le code web suivant : Copier ce code Code web : 740399
380

LE MOT-CL YIELD Nous avons une classe ChaineEnumerable qui encapsule une chane de caractres de type string et une classe ChaineEnumerateur qui permet de parcourir une chane et de renvoyer chaque itration un caractre, reprsent par le type char 2 . Rien de bien sorcier, mais un code plutt lourd. Regardons prsent le code suivant :
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21

public class ChaineEnumerable : IEnumerable < char > { private string chaine ; public ChaineEnumerable ( string valeur ) { chaine = valeur ; } public IEnumerator < char > GetEnumerator () { for ( int i = 0 ; i < chaine . Length ; i ++) { yield return chaine [ i ]; } } IEnumerator IEnumerable . GetEnumerator () { return GetEnumerator () ; }

Il fait la mme chose, mais dune manire bien simplie. Nous remarquons lapparition du mot-cl yield. Il permet de crer facilement des numrateurs. Combin au mot-cl return, il renvoie un lment dune collection et passe llment suivant. Il peut tre combin au mot-cl break galement pour permettre darrter lnumration. Ce qui veut dire que nous aurions pu crire la mthode GetEnumerator() de cette faon :
1 2 3 4 5 6 7 8 9 10 11

public IEnumerator < char > GetEnumerator () { int i = 0 ; while ( true ) { if ( i == chaine . Length ) yield break ; yield return chaine [ i ]; i ++; } }

Ce qui est plus laid, je le conois ! mais qui permet dillustrer le mot-cl yield break.
2. Le type char est un alias de la structure System.Char.

381

CHAPITRE 34. PLUS LOIN AVEC LE C# ET .NET Le mot-cl yield permet galement de renvoyer un IEnumerable (ou sa version gnrique), ainsi un peu btement, on pourrait faire :
1 2 3 4 5 6 7 8 9 10 11 12 13 14

static void Main ( string [] args ) { foreach ( string prenom in Obten irLis teDePr enoms () ) { Console . WriteLine ( prenom ) ; } } public static IEnumerable < string > Ob tenirL isteD ePreno ms () { yield return " Nicolas " ; yield return " J r mie " ; yield return " Delphine " ; }

Cet exemple va surtout me permettre dillustrer ce qui se passe exactement grce au mode debug. Mettez un point darrt au dbut du programme et lancez-le en mode debug (voir la gure 34.5).

Figure 34.5 Le programme est en pause sur le point darrt avant lnumration Appuyez sur F11 plusieurs fois pour rentrer dans la mthode ObtenirListeDePrenoms, nous voyons lindicateur se dplacer sur les direntes parties de linstruction foreach. Puis nous arrivons sur la premire instruction yield return (voir la gure 34.6). Appuyons nouveau sur F11 et nous pouvons constater que nous sortons de la mthode et que nous rentrons nouveau dans le corps de la boucle foreach qui va nous acher le premier prnom. Continuons les F11 jusqu repasser sur le mot-cl in et rentrer nouveau dans la mthode ObtenirListeDePrenoms() et nous pouvons constater que nous retournons directement au deuxime mot-cl yield, ainsi que vous pouvez le voir la gure 34.7. Et ainsi jusqu ce quil ny ait plus rien dans la mthode ObtenirListeDePrenoms() et que, du coup, le foreach se termine. la premire lecture, ce nest pas vraiment ce comportement que nous aurions attendu. . . 382

LE MOT-CL YIELD

Figure 34.6 Le programme atteint linstruction yield juste aprs le foreach

Figure 34.7 Le programme atteint la seconde instruction yield lors de la deuxime itration

383

CHAPITRE 34. PLUS LOIN AVEC LE C# ET .NET Voil pour le principe du yield return. Notez quun yield break nous aurait fait sortir directement de la boucle foreach. Le mot-cl yield participe ce quon appelle lexcution dire. On nvalue la mthode ObtenirListeDePrenoms() quau moment o on parcoure le rsultat avec la boucle foreach, ce qui pourrait paratre surprenant. Par exemple, si nous faisons :
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16

public static IEnumerable < string > Ob tenirL isteD ePreno ms () { yield return " Nicolas " ; yield return " J r mie " ; yield return " Delphine " ; } static void Main ( string [] args ) { IEnumerable < string > prenoms = Obten irList eDePr enoms () ; Console . WriteLine ( " On fait des choses ... " ) ; foreach ( string prenom in prenoms ) { Console . WriteLine ( prenom ) ; } }

et que nous mettons un point darrt sur le Console.WriteLine et un autre dans la mthode ObtenirListeDePrenoms(), on sarrtera dabord sur le point darrt de la ligne o est situ le Console.WriteLine et nous passerons ensuite dans le point darrt positionn dans la mthode ObtenirListeDePrenoms(). En eet, on ne passera dans la mthode que lorsquon itrera explicitement sur les prnoms grce au foreach. Nous reviendrons sur cette excution dire un peu plus loin avec le mot-cl yield.

Le formatage de chanes, de dates et la culture


Pour linstant, lorsque nous avons eu besoin de mettre en forme des chanes de caractres, nous avons utilis la mthode Console.WriteLine avec en paramtres des chanes concatnes entre elles grce loprateur +. Par exemple :
1 2

int age = 30 ; Console . WriteLine ( "J ' ai " + age + " ans " ) ;

Il est important de savoir quil est possible de formater la chane un peu plus simplement et surtout beaucoup plus ecacement. Ceci est possible grce la mthode string.Format. Le code prcdent peut par exemple tre remplac par :
1

int age = 30 ;

384

LE FORMATAGE DE CHANES, DE DATES ET LA CULTURE


2 3

string chaine = string . Format ( "J ' ai { 0 } ans " , age ) ; Console . WriteLine ( chaine ) ;

La mthode string.Format accepte en premier paramtre un format de chane. Ici, nous lui indiquons une chane de caractres avec au milieu un caractre spcial : {0}. Il permet de dire : remplace-moi le {0} avec le premier paramtre qui va suivre , en loccurrence la variable age. Si nous avons plusieurs valeurs, il est possible dutiliser les caractres spciaux {1}, puis {2}, etc. Chaque nombre reprsente ici lindice correspondant au paramtre. Par exemple :
1 2 3

double valeur1 = 10 . 5 ; double valeur2 = 4 . 2 ; string chaine = string . Format ( " { 0 } multipli par { 1 } s ' crit \"{ 0 } * { 1 }\" et donne comme r sultat : { 2 } " , valeur1 , valeur2 , valeur1 * valeur2 ) ; Console . WriteLine ( chaine ) ;

Ce qui donne :
10 ,5 multipli par 4 ,2 s crit "10 ,5 * 4 ,2" et donne comme r sultat : 44 ,1

Vous avez pu remarquer quil est possible dutiliser le mme paramtre plusieurs endroits. La mthode Console.WriteLine dispose aussi de la capacit de formater des chanes de caractres. Ce qui veut dire que le code prcdent peut scrire :
1 2 3

double valeur1 = 10 . 5 ; double valeur2 = 4 . 2 ; Console . WriteLine ( " { 0 } multipli par { 1 } s ' crit \"{ 0 } * { 1 }\" et donne comme r sultat : { 2 } " , valeur1 , valeur2 , valeur1 * valeur2 ) ;

Ce qui revient absolument au mme. Parlons culture dsormais. Nayez pas peur, rien voir avec des questions pour des champions ! La culture en informatique correspond tout ce qui a trait aux dirences entre les langues et les paramtres locaux. Par exemple, en France et aux tats-Unis, les dates scrivent diremment. Pour le jour de Nol 2011, voici ce que a donne : en France : le 25/12/2011 ; aux tats-Unis : the 12/25/2011. Le mois et le jour sont donc inverss. De la mme faon, il existe des dirences lorsque nous crivons les nombres virgule : en France : 123,45 ; aux tats-Unis : 123.45. Un point la place dune virgule, subtile dirence. On a donc souvent des dirences entre les langues. Et mme plus, on peut avoir des dirences entre les langues en fonction de lendroit o elles sont parles. Citons par exemple le franais de France et le franais du Canada. Il existe donc au sein du systme dexploitation tout une liste de 385

CHAPITRE 34. PLUS LOIN AVEC LE C# ET .NET rgions reprsentes par un couple de lettres. Par exemple, le franais est reprsent par les lettres fr . Et mme plus prcisment, le franais de France est reprsent par fr-FR alors que celui du Canada est reprsent par fr-CA . Cest ce que lon retrouve dans les paramtres de notre systme dexploitation lorsquon va (sous Windows 7) dans le panneau de conguration, Horloge langue et rgion, comme vous pouvez le voir la gure 34.8.

Figure 34.8 Choisir Horloge, langue et rgion dans le panneau de conguration Si vous allez dans Modifier le format de la date, de lheure ou des nombres (voir gure 34.9), vous pouvez adapter le format celui que vous prfrez (voir la gure 34.10). Bref, ce choix, nous le retrouvons dans notre application si nous rcuprons la culture courante :
1

Console . WriteLine ( System . Threading . Thread . CurrentThread . CurrentCulture ) ;

Avec ceci, nous aurons :


fr - FR

Ce qui est intressant, cest que le formatage des chires ou des dates change en fonction de la culture. Ainsi, si nous utilisons le franais de France, pour le code suivant :
1 2 3

public static void Main ( string [] args ) { Affiche () ;

386

LE FORMATAGE DE CHANES, DE DATES ET LA CULTURE

Figure 34.9 On peut modier ici le format de la date

Figure 34.10 Slection de la langue pour le format

387

CHAPITRE 34. PLUS LOIN AVEC LE C# ET .NET


4 5 6 7 8 9 10 11 12 13 14 15

} public static void Affiche () { Console . WriteLine ( System . Threading . Thread . CurrentThread . CurrentCulture ) ; decimal dec = 5 . 5M ; double dou = 4 . 8 ; DateTime date = new DateTime ( 2011 , 12 , 25 ) ; Console . WriteLine ( " D cimal : { 0 } " , dec ) ; Console . WriteLine ( " Double : { 0 } " , dou ) ; Console . WriteLine ( " Date : { 0 } " , date ) ; }

nous aurons :
fr - FR D cimal : 5 ,5 Double : 4 ,8 Date : 25/12/2011 00:00:00

Il est possible de changer la culture courante de notre application, si par exemple nous souhaitons quelle soit multiculture, en utilisant le code suivant :
1

System . Threading . Thread . CurrentThread . CurrentCulture = new CultureInfo ( " en - US " ) ;

Ici par exemple, jindique mon application que je souhaite travailler avec la culture des tats-Unis. Ainsi, le code prcdent, en changeant juste la culture :
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16

public static void Main ( string [] args ) { System . Threading . Thread . CurrentThread . CurrentCulture = new CultureInfo ( " en - US " ) ; Affiche () ; } public static void Affiche () { Console . WriteLine ( System . Threading . Thread . CurrentThread . CurrentCulture ) ; decimal dec = 5 . 5M ; double dou = 4 . 8 ; DateTime date = new DateTime ( 2011 , 12 , 25 ) ; Console . WriteLine ( " D cimal : { 0 } " , dec ) ; Console . WriteLine ( " Double : { 0 } " , dou ) ; Console . WriteLine ( " Date : { 0 } " , date ) ; }

produira un rsultat lgrement dirent : 388

LE FORMATAGE DE CHANES, DE DATES ET LA CULTURE

en - US D cimal : 5.5 Double : 4.8 Date : 12/25/2011 12:00:00 AM

Il sagit exactement de la mme information, mais formate diremment. noter quil existe encore une autre information de culture, savoir la langue du systme dexploitation que lon retrouve dans la culture de linterface graphique, dans la proprit :
1

Console . WriteLine ( System . Threading . Thread . CurrentThread . CurrentUICulture ) ;

Il sagit de la CurrentUICulture, ne pas confondre avec la CurrentCulture. Correspondant la langue du systme dexploitation, nous nous en servirons plus volontiers pour rendre une application multilingue. Pour nir sur le formatage des chanes, il est possible dutiliser des caractres spciaux enrichis pour changer le formatage des types numriques ou de la date. Par exemple, il est possible dacher la date sous une forme dirente avec :
1 2

DateTime date = new DateTime ( 2011 , 12 , 25 ) ; Console . WriteLine ( " La date est { 0 } " , date . ToString ( " dd - MM - yyyy " ));

Ici, dd sert acher le jour, MM le mois et yyyy lanne sur quatre chires, ce qui donne :
La date est 25 -12 -2011

Le fonctionnement est similaire pour les numriques ; ce qui nous permet par exemple dacher un nombre de direntes manires. Ici par exemple, jutilise la notation scientique :
1 2

Il y a beaucoup doptions direntes que vous pouvez retrouver dans la documentation Microsoft, via le code web suivant : DateTime Code web : 781180

double valeur = 123 . 45 ; Console . WriteLine ( " Format scientifique : { 0 : e } " , valeur ) ;

Ce qui donne :
Format scientifique : 1 ,234500 e +002

Pour les nombres, il existe dirents formatages que lon peut retrouver dans le tableau ci-aprs. 389

CHAPITRE 34. PLUS LOIN AVEC LE C# ET .NET Format {0 :c} {0 :d} {0 {0 {0 {0 :e} :f} :g} :n} Description Monnaie Dcimal Notation scientique Virgule ottante Gnral Nombre avec formatage pour les milliers Aller-retour Hexadcimal Remarque Attention, la console ache mal le caractre Ne fonctionne quavec les types sans virgules Valeur par dfaut Permet de garantir que la valeur numrique convertie en chane pourra tre convertie nouveau en valeur numrique identique Ne fonctionne quavec les types sans virgules

{0 :r} {0 :x}

On peut galement avoir du formatage grce la mthode ToString(). Il sut de passer le format en paramtres, comme ici, o cela me permet dacher uniquement le nombre de chires aprs la virgule qui mintressent ou un nombre des chires signicatifs :
1 2

double valeur = 10 . 0 / 3 ; Console . WriteLine ( " Avec 3 chiffres significatifs et 3 chiffres apr s la virgule : { 0 } " , valeur . ToString ( " 000 .### " ) ) ;

Qui donne :
Avec 3 chiffres significatifs et 3 chiffres apr s la virgule : 003 ,333

Mettre le format dans la mthode ToString() fonctionne aussi pour la date :


1 2

DateTime date = new DateTime ( 2011 , 12 , 25 ) ; Console . WriteLine ( date . ToString ( " MMMM " ) ) ;

Ici, je nache que le nom du mois :


d cembre

Voil pour le formatage des chanes de caractres. Lister tous les formatages possibles serait une tche bien ennuyeuse. Vous aurez loccasion de rencontrer dautres formatages qui permettent de faire un peu tout et nimporte quoi. Vous pourrez retrouver la plupart des formatages partir de ce code web : Formatages Code web : 500571
390

LES ATTRIBUTS

Les attributs
Dans la liste des choses qui vont vous servir, on peut ajouter les attributs. Ils sont utiliss pour fournir des informations complmentaires sur une mthode, une classe, des variables, etc. Ils vont nous servir rgulirement dans le framework .NET et nous aurons mme la possibilit de crer nos propres attributs. Un attribut est dclar entre crochets avec le nom de sa classe. Il peut se mettre au-dessus dune classe, dune mthode, dune proprit, etc. Par exemple, dans le code ci-dessous, je positionne lattribut Obsolete au-dessus de la dclaration dune mthode :
1 2 3 4 5

[ Obsolete ( " Utilisez plut t la m thode ToString () pour avoir une repr sentation de l ' objet " ) ] public void Affiche () { // ... }

Ici, jutilise un attribut existant du framework .NET, lattribut Obsolete qui permet dindiquer quune mthode est obsolte et quil vaudrait mieux ne pas lutiliser. En gnral, il est possible de fournir une information permettant de dire pourquoi la mthode est obsolte et par quoi il faut la remplacer. Ainsi, si nous tentons dutiliser cette mthode :
1 2 3 4 5 6 7 8 9 10 11 12 13

public class Program { static void Main ( string [] args ) { new Program () . Affiche () ; } [ Obsolete ( " Utilisez plut t la m thode ToString () pour avoir une repr sentation de l ' objet " ) ] public void Affiche () { // ... }

Le compilateur nous achera lavertissement suivant :


Ma P r e m i e r e A p p licati on . Program . Affiche () est obsol te : Utilisez plut t la m thode ToString () pour avoir une repr sentation de l objet

Cet attribut est un exemple, il en existe beaucoup dans le framework .NET et vous aurez loccasion den voir dautres dans les chapitres suivants. Remarquons que la classe sappelle ObsoleteAttribute et peut sutiliser soit suxe par Attribute, soit sans, et dans ce cas, ce suxe est ajout automatiquement. Grce lhritage, nous allons nous aussi pouvoir dnir nos propres attributs. Il sut pour cela de driver de la classe de base Attribute. Par exemple, dans sa forme la plus 391

CHAPITRE 34. PLUS LOIN AVEC LE C# ET .NET simple :


1 2 3

public class D e s c r i p t i o n C l a s s e A t t r i b u t e : Attribute { }

Nous pourrons alors utiliser notre attribut sur une classe :


1 2 3 4

[ Descript ionClasse ] public class Chien { }

Super, mme si a ne nous sert encore rien ! Problme, jai appel mon attribut DescriptionClasse et je peux quand mme lutiliser sur une mthode :
1 2 3 4 5 6 7 8

[ Descript ionClasse ] public class Chien { [ DescriptionClasse ] public void Aboyer () { } }

Heureusement, il est possible dindiquer des restrictions sur les attributs en indiquant par exemple quils ne sont valables que pour les classes. Cela se fait avec. . . un attribut !
1 2 3 4

[ AttributeUsage ( AttributeTargets . Class , AllowMultiple = true ) ] public class D e s c r i p t i o n C l a s s e A t t r i b u t e : Attribute { }

Ici par exemple, jindique que mon attribut nest valable que sur les classes et quil est possible de le mettre plusieurs fois sur la classe. Le code prcdent o lattribut est positionn sur une mthode ne compilera donc plus, avec lerreur de compilation suivante :
L attribut DescriptionClasse n est pas valide dans ce type de d claration . Il n est valide que dans les d clarations class .

Ce qui nous convient parfaitement ! Il existe dautres attributs qui permettent par exemple de nautoriser des attributs que sur les mthodes. Il est intressant de pouvoir fournir des informations complmentaires notre attribut. Pour cela, on peut rajouter des proprits, ou avoir une surcharge complmentaire du constructeur, comme on la fait pour le message de lattribut Obsolete : 392

LA RFLEXION
1 2 3 4 5 6 7 8 9 10 11 12 13 14

[ AttributeUsage ( AttributeTargets . Class , AllowMultiple = true ) ] public class D e s c r i p t i o n C l a s s e A t t r i b u t e : Attribute { public string Description { get ; set ; } public D e s c r i p t i o n C l a s s e A t t r i b u t e () { } public D e s c r i p t i o n C l a s s e A t t r i b u t e ( string description ) { Description = description ; }

Ce qui nous permettra dutiliser notre attribut de ces deux faons :


1 2 3 4 5

[ Descript ionClasse ( Description = " Cette classe correspond un chien " ) ] [ Descript ionClasse ( " Elle d rive de la classe Animal " ) ] public class Chien : Animal { }

Cest trs bien ces attributs, mais nous ne sommes pas encore capables de les exploiter. Voyons maintenant comment le faire.

La rexion
La rexion en C# ne donne pas mal la tte, quoique. . . ! Cest une faon de faire de lintrospection sur nos types de manire obtenir des informations sur eux, cest-dire sur les mthodes, les proprits, etc. La rexion permet galement de charger dynamiquement des types et de faire de lintrospection sur les assemblys. Cest un mcanisme assez complexe et plutt coteux en terme de temps. Le point de dpart est la classe Type, qui permet de dcrire nimporte quel type. Le type dune classe peut tre obtenu grce au mot-cl typeof :
1

Type type = typeof ( string ) ;

Cet objet Type nous permet dobtenir plein dinformations sur nos classes au moment de lexcution. Par exemple si le type est une classe, cest--dire ni un type valeur, ni une interface, alors le code suivant :
1 2

Type type = typeof ( string ) ; Console . WriteLine ( type . IsClass ) ;

nous renverra true. En eet, la proprit IsClass permet dindiquer si le type est une classe. Nous pouvons aussi obtenir le nom de mthodes grce GetMethods() : 393

CHAPITRE 34. PLUS LOIN AVEC LE C# ET .NET


1 2 3 4 5

Type type = typeof ( string ) ; foreach ( MethodInfo infos in type . GetMethods () ) { Console . WriteLine ( infos . Name ) ; }

Je vous renvoie la gure 34.11 pour le rsultat dans la console.

Figure 34.11 Achage du nom des mthodes Pas trs exploitable comme rsultat, mais bon, cest pour lexemple ! Il y a encore pleins dinfos dans cet objet Type, qui permettent dobtenir ce que nous voulons. Cela est possible grce aux mtadonnes qui sont des informations de description des types. Vous me voyez venir. . . eh oui, nous allons pouvoir obtenir nos attributs. Pour connatre la liste des attributs dun type, on peut utiliser la mthode statique Attribute.GetCustomAttributes(), en lui passant en paramtre le System.Type qui est hrit de la classe abstraite MemberInfo. Si lon souhaite rcuprer un attribut particulier, on peut le passer en paramtre de la mthode :
1

Attribute [] lesAttributs = Attribute . GetCustomAttributes ( type , typeof ( D e s c r i p t i o n C l a s s e A t t r i b u t e ) ) ;

Ceci va nous permettre de rcuprer les attributs de notre classe et en loccurrence, dobtenir leurs descriptions. Cest ce que permet le code suivant :
1 2 3 4 5 6 7 8 9

public class Program { static void Main ( string [] args ) { new DemoAttributs () . Demo () ; } }

394

LA RFLEXION
10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26

public class DemoAttributs { public void Demo () { Animal animal = new Animal () ; Chien chien = new Chien () ; VoirDescription ( animal ) ; VoirDescription ( chien ) ;

27 28 29 30 31 32 33 34 35 36 37 38

public void VoirDescription <T >( T obj ) { Type type = typeof ( T ) ; if (! type . IsClass ) return ; Attribute [] lesAttributs = Attribute . G etCustomAttributes ( type , typeof ( DescriptionClasseAttribute )); if ( lesAttributs . Length == 0 ) Console . WriteLine ( " Pas de description pour la classe " + type . Name + " \ n " ) ; else { Console . WriteLine ( " Description pour la classe " + type . Name ) ; foreach ( D e s c r i p t i o n C l a s s e A t t r i b u t e attribut in lesAttributs ) { Console . WriteLine ( " \ t " + attribut . Description ) ; } } }

Nous commenons par instancier un objet animal et un objet chien. Puis nous demandons leurs descriptions. La mthode gnrique VoirDescription() soccupe dans un premier temps dobtenir le type de la classe grce typeof. On soctroie une vrication permettant de nous assurer que le type est bien une classe. Ceci permet dviter daller plus loin car de toute faon, notre attribut ne sapplique quaux classes. Puis nous utilisons la mthode GetCustomAttributes pour obtenir les attributs de type DescriptionClasseAttribute. Sil y en a, alors on les ache. Ce qui donne :
Pas de description pour la classe Animal Description pour la classe Chien Elle d rive de la classe Animal Cette classe correspond un chien

395

CHAPITRE 34. PLUS LOIN AVEC LE C# ET .NET Voil pour les attributs, vous aurez loccasion de rencontrer des attributs du framework .NET un peu plus loin dans ce tutoriel, mais globalement, il est assez rare davoir crer soi-mme ses attributs. En ce qui concerne la rexion, il faut bien comprendre que le sujet est beaucoup plus vaste que a. Nous avons cependant vu ici un aperu de ce que permet de faire la rexion, en illustrant le mcanisme dintrospection sur les attributs.

En rsum
Il est possible dempcher une classe dtre drive grce au mot-cl sealed. Le garbage collector est le mcanisme permettant de librer la mmoire alloue sur le tas manag qui nest plus rfrence dans une application. Le mot-cl yield permet de crer des numrateurs facilement et participe au mcanisme dexcution dire. Le framework .NET gre les dirents formats des chanes grce la culture de lapplication. La rexion est un mcanisme dintrospection sur les types permettant dobtenir des informations sur ces derniers lors de lexcution.

396

Chapitre

35
Dicult :

La conguration dune application

ans le cycle de vie dune application, il est frquent que de petites choses changent. Imaginons que mon application doive se connecter un serveur FTP pour sauvegarder les donnes sur lesquelles jai travaill. Pendant mes tests, eecuts en local, mon serveur FTP pourrait avoir ladresse ftp://localhost avec un login et un mot de passe test . videmment, lors de lutilisation relle de mon application, ladresse changera, et le login et le mot de passe varieront en fonction de lutilisateur. Il faut tre capable de changer facilement ces paramtres sans avoir modier le code ni recompiler lapplication.

Cest l quinterviennent les chiers de conguration : ils permettent de stocker toutes ces petites choses qui servent faire varier notre application. Pour les clients lourds, comme nos applications console, il sagit du chier app.config : un chier XML qui contient la conguration de notre application. Il stocke des chanes de connexion une base de donnes, une url de service web, des prfrences utilisateurs, etc. Bref, tout ce qui va permettre de congurer notre application !

397

CHAPITRE 35. LA CONFIGURATION DUNE APPLICATION

Rappel sur les chiers XML


Vous ne connaissez pas les chiers XML ? Si vous voulez en savoir plus, nhsitez pas faire un petit tour sur internet, cest un format trs utilis dans linformatique. Pour faire court, le XML est un langage de balise, un peu comme le HTML, o lon dcrit de linformation. Les balises sont des valeurs entoures de < et > qui dcrivent la smantique de la donne. Par exemple :
1

< prenom > Nicolas </ prenom >

La balise <prenom> est ce quon appelle une balise ouvrante, cela signie que ce qui se trouve aprs (en loccurrence la chane Nicolas ) fait partie de cette balise jusqu ce que lon rencontre la balise fermante </prenom> qui est comme la balise ouvrante lexception du / prcdant le nom de la balise. Le XML est un chier facile lire par nous autres humains. On en dduit assez facilement que le chier contient la chane Nicolas et quil sagit smantiquement dun prnom. Une balise peut contenir des attributs permettant de donner des informations sur la donne. Les attributs sont entours de guillemets : "" et font partie de la balise. Par exemple :
1

< client nom = " Nicolas " age = " 30 " > </ client >

Ici, la balise client possde un attribut nom ayant la valeur Nicolas et un attribut age ayant la valeur 30 . Encore une fois, cest trs facile lire pour un humain. Il est possible que la balise nait pas de valeur, comme cest le cas dans lexemple cidessus. On peut dans ce cas-l remplacer la balise ouvrante et la balise fermante par cet quivalent :
1

< client nom = " Nicolas " age = " 30 " / >

Enn, et nous allons terminer notre aperu rapide du XML avec ce dernier point, il est important de noter que le XML peut imbriquer ses balises et quil peut ne possder quun seul lment racine, ce qui nous permet davoir une hirarchie de donnes. Par exemple nous pourrons avoir :
1 2 3 4 5 6 7 8 9 10

< listesDesClient > < client type = " Particulier " > < nom > Nicolas </ nom > < age > 30 </ age > </ client > < client type = " Professionel " > < nom >J r mie </ nom > < age > 40 </ age > </ client > </ listesDesClient >

On voit tout de suite que le chier dcrit une liste de deux clients. Nous en avons un qui est un particulier, qui sappelle Nicolas et qui a 30 ans alors que lautre est un professionnel, prnomm Jrmie et qui a 40 ans. 398

CRER LE FICHIER DE CONFIGURATION

Crer le chier de conguration


Pourquoi utiliser un chier de conguration ? Pour viter de mettre des valeurs en dur dans le code. Imaginons que nous utilisions une url de service web dans notre application, si lurl change, on aimerait ne pas avoir recompiler le code. Pour viter dutiliser la base de registre comme cela a beaucoup t fait dans les premires versions de Windows et qui oblige donner les droits de modication sur celle-ci. Ces chiers permettent davoir des informations de conguration, potentiellement types. De plus, le framework .NET dispose de mthodes pour y accder facilement. Lintrt dutiliser un chier XML plutt quun chier binaire est que ce chier est lisible et comprhensible facilement. On peut galement le modier la main sans un systme volu permettant de faire des modications. Voil plein de raisons pour lesquelles on va utiliser ces chiers. Pour ajouter un chier de conguration : faisons un clic droit sur le projet : Ajouter > Nouvel lment, comme lillustre la gure 35.1.

Figure 35.1 Ajout du chier de conguration Choisissons le modle de chier Fichier de configuration de lapplication (voir gure 35.2). Conservez son nom et validez sa cration. Ce chier est presque vide :
1 2 3

<? xml version = " 1 . 0 " encoding = " utf - 8 " ? > < configuration > </ configuration >

Il possde sur sa premire ligne un marqueur permettant didentier le chier comme tant un chier XML et une balise ouvrante conguration, suivie de sa balise fermante. Cest la structure de base du chier de conguration. Nous mettrons nos sections de conguration lintrieur de cette balise. Nous retrouvons notre chier de conguration dans notre projet et nous pouvons le voir dans lexplorateur de documents, ainsi que lillustre la gure 35.3. 399

CHAPITRE 35. LA CONFIGURATION DUNE APPLICATION

Figure 35.2 Choix et nommage du chier de conguration

Figure 35.3 Le chier de conguration est prsent dans le projet

400

LECTURE SIMPLE DANS LA SECTION DE CONFIGURATION PRDFINIE : APPSETTINGS Compilons notre application et rendons-nous dans le rpertoire o le chier excutable est gnr. Nous y trouvons galement un chier qui porte le mme nom que notre excutable et dont lextension est .exe.config. Cest bien notre chier de conguration. Visual C# Express le dploie au mme endroit que notre application et lui donne un nom qui va lui permettre dtre exploit par notre application. Pour tre utilisables, ces chiers doivent se situer dans le mme rpertoire que lexcutable.

Lecture simple dans la section de conguration prdnie : AppSettings


Avoir un chier de conguration, cest bien. Mais il faut savoir lire lintrieur si nous souhaitons lexploiter. Il existe plusieurs faons dindiquer des valeurs de conguration, nous les dcouvrirons tout au long de ce chapitre. La plus simple est dutiliser la section AppSettings. Cest une section o lon peut mettre, comme son nom le suggre aux anglophones, les proprits de lapplication. Pour cela, on utilisera un systme de cl ou valeur pour stocker les informations. Par exemple, modiez le chier de conguration pour avoir :
1 2 3 4 5 6

< configuration > < appSettings > < add key = " prenom " value = " nicolas " / > < add key = " age " value = " 30 " / > </ appSettings > </ configuration >

En voyant ce chier de conguration, un tre humain comprendra assez facilement quil y a deux paramtres de conguration. Un premier qui est le prnom et qui va valoir Nicolas. Un deuxime qui est lge et qui sera de 30. Le savoir en tant qutre humain, cest bien. Pouvoir y accder dans notre programme informatique, cest mieux ! Voyons comment faire. La premire chose faire est de rfrencer, si ce nest dj fait, lassembly qui contient toutes les classes permettant de grer la conguration. Elle sappelle (sans surprise) System.Configuration (voir gure 35.4). An daccder au chier de conguration, nous allons devoir utiliser la classe statique ConfigurationManager. Pour accder aux informations contenues dans la section AppSettings, nous utiliserons sa proprit AppSettings. Et nous pourrons accder aux lments de la conguration en utilisant loprateur dindexation : []. Ce qui donne :
1 2 3 4

string prenom = ConfigurationManager . AppSettings [ " prenom " ]; string age = ConfigurationManager . AppSettings [ " age " ]; Console . WriteLine ( " Pr nom : " + prenom + " / Age : " + age ) ;

On peut galement utiliser des index numriques pour y accder, mais je trouve que a manque vraiment de clart :
1 2

string prenom = ConfigurationManager . AppSettings [ 0 ]; string age = ConfigurationManager . AppSettings [ 1 ];

401

CHAPITRE 35. LA CONFIGURATION DUNE APPLICATION

Figure 35.4 Ajout de la rfrence lassembly System.Configuration


3 4

Console . WriteLine ( " Pr nom : " + prenom + " / Age : " + age ) ;

De plus, cela oblige connatre lordre dans lequel les cls ont t mises dans le chier. Il est possible aussi de boucler sur toutes les valeurs contenues dans la section :
1 2 3 4

foreach ( string cle in ConfigurationManager . AppSettings ) { Console . WriteLine ( " Cl : " + cle + " / Valeur : " + C o nfigurationManager . AppSettings [ cle ]) ; }

La proprit AppSettings est en fait du type NameValueCollection. Il sagit dune collection dlments de type chane de caractres qui sont accessibles partir dune cl de type chane de caractres galement. Une valeur est donc associe une cl. Pour plus dinformations sur le type NameValueCollection, je vous invite consulter la documentation en ligne, via le code web suivant : NameValueCollection Code web : 357840
noter que la casse de la cl nest pas importante pour accder la valeur associe la cl. Le code suivant :
1

string prenom = ConfigurationManager . AppSettings [ " PRENOM " ];

renverra bien notre valeur de conguration. Cependant, si la valeur nexiste pas, nous obtiendrons la valeur null dans la chane de caractres. Il est important de remarquer que nous rcuprons des chanes et que nous aurons besoin potentiellement de faire une 402

LECTURE DES CHANES DE CONNEXION LA BASE DE DONNES conversion pour manipuler lge en tant quentier. Rien ne vous empche dcrire une petite mthode dextension maintenant que vous savez faire :
1 2 3 4 5 6 7 8

public static class C o n f i g u r a t i o n M a n a g e r E x t e n s i o n s { public static int ObtenirValeurEntiere ( this N am e ValueCollection appSettings , string cle ) { string valeur = appSettings [ cle ]; return Convert . ToInt32 ( valeur ) ; } }

Que nous pourrons utiliser avec :


1

int age = C o n figurationManager . AppSettings . ObtenirValeurEntiere ( " age " ) ;

Lecture des chanes de connexion la base de donnes


Les chanes de connexion reprsentent un type de conguration particulier. Elles vont servir pour les applications ayant besoin de se connecter une base de donnes. On va y stocker tout ce dont on a besoin, comme le nom du serveur ou les identiants pour sy connecter. . . Nous y reviendrons en dtail plus tard, mais regardons la conguration suivante :
1 2 3 4 5 6 7 8

< configuration > < conn ectionStrings > < add name = " MaConnection " providerName = " System . Data . SqlClient " connectionString = " Data Source =.\ SQLEXPRESS ; Initial Catalog = Base1 ; Integrated Security = true " / > < add name = " MaConnection2 " providerName = " System . Data . SqlClient " connectionString = " Data Source =.\ SQLEXPRESS ; Initial Catalog = Base2 ; Integrated Security = true " / > </ conn ectionStrings > </ configuration >

Nous dnissons ici deux chanes de connexion qui permettent de se connecter en authentication Windows (Integrated Security=true) sur un serveur hberg en local (.\SQLEXPRESS) dont le nom est SQLEXPRESS, qui utilisent SQL SERVER (providerName="System.Data.SqlClient"), qui pointent sur les bases de donnes Base1 et Base2, identies chacune par les noms MaConnection et MaConnection2. Pour obtenir les informations de conguration individuellement, il faudra utiliser la proprit ConnectionStrings de la classe ConfigurationManager en y accdant par leurs noms : 403

CHAPITRE 35. LA CONFIGURATION DUNE APPLICATION


1 2 3 4

C o n n e c t i o n St r i ng S e tt i n gs chaineConnexion = ConfigurationManager . ConnectionStrings [ " MaConnection " ]; Console . WriteLine ( chaineConnexion . Name ) ; Console . WriteLine ( chaineConnexion . ConnectionString ) ; Console . WriteLine ( chaineConnexion . ProviderName ) ;

Nous pourrons toutes les obtenir avec une boucle foreach :


1 2 3 4

foreach ( C o n ne c t io n S tr i n gS e t ti n g s valeur in C o n f i g urationManager . ConnectionStrings ) { Console . WriteLine ( valeur . ConnectionString ) ; }

Nous utiliserons les chanes de connexion dans le chapitre sur laccs aux donnes.

Crer sa section de conguration depuis un type prdni


Il est possible de crer sa propre section de conguration partir dun type prdni. Par exemple pour crer une section du mme genre que la section appSettings, qui utilise une paire cl / valeur, on peut utiliser un DictionarySectionHandler. Il existe plusieurs types prdnis, que nous allons tudier ci-dessous. Le type DictionarySectionhandler DictionarySectionhandler est une classe qui fournit les informations de conguration des paires cl / valeur dune section de conguration. Oui mais si tu nous dis que cest un systme de cl / valeur, cest comme pour les AppSettings que nous avons vus. Pourquoi utiliser une section particulire DictionarySectionhandler alors ? Lintrt de pouvoir faire des sections particulires est dorganiser smantiquement son chier de conguration, pour dcouper logiquement son chier au lieu davoir tout dans la mme section. Regardons cette conguration :
1 2 3 4 5 6 7 8 9

< configuration > < configSections > < section name = " In f or ma ti o ns Ut il i sa te u r " type = " System . Configuration . Di c t io n a ry S e ct i o nH a n dl e r " / > </ configSections > < I n f o r m a ti on s Ut il is a te ur > < add key = " login " value = " nico " / > < add key = " motdepasse " value = " 12345 " / > < add key = " age " value = " 30 " / > </ I n f o r m a ti on sU t il is a te ur >

404

CRER SA SECTION DE CONFIGURATION DEPUIS UN TYPE PRDFINI


10

</ configuration >

La premire chose voir est que nous indiquons notre application que nous dnissons une section de conguration du type DictionarySectionHandler et qui va sappeler InformationsUtilisateur. Cela permet ensuite de dnir notre propre section InformationsUtilisateur, qui ressemble beaucoup la section AppSettings sauf quici, on se rend tout de suite compte quil sagit dinformations utilisateurs. Pour accder notre section depuis notre programme, nous devrons utiliser le code suivant :
1

Hashtable section = ( Hashtable ) ConfigurationManager . GetSection ( " I n f o r m a t io ns U ti li sa t eu r " ) ;

On utilise la mthode GetSection de la classe ConfigurationManager pour obtenir la section dont nous passons le nom en paramtre. On reoit en retour une table de hachage ( HashTable ) et nous pourrons lutiliser de cette faon pour obtenir les valeurs de nos cls :
1 2 3

Console . WriteLine ( section [ " login " ]) ; Console . WriteLine ( section [ " MOTDEPASSE " ]) ; Console . WriteLine ( section [ " age " ]) ;

Nous pouvons boucler sur les valeurs de la section en utilisant le code suivant :
1 2 3 4

foreach ( DictionaryEntry d in section ) { Console . WriteLine ( " Cl : " + d . Key + " / Valeur : " + d . Value ) ; }

Le type NameValueSectionHandler La section de type NameValueSectionHandler ressemble beaucoup la section prcdente. Observons la conguration suivante :
1 2 3 4 5 6 7 8 9 10

< configuration > < configSections > < section name = " In fo r ma ti o ns Ut il i sa te ur " type = " System . Configuration . N a me Va lu e Se ct io n Ha nd l er " / > </ configSections > < I n f o r m a t i o n sU t il is a te ur > < add key = " login " value = " nico " / > < add key = " motdepasse " value = " 12345 " / > < add key = " age " value = " 30 " / > </ I n f o r m a t i on sU t il is at e ur > </ configuration >

Cest la mme que prcdemment, lexception du type de la section, qui cette fois-ci est NameValueSectionHandler. Ce qui implique que nous obtenons un type dirent en retour de lappel la mthode GetSection, savoir un NameValueCollection : 405

CHAPITRE 35. LA CONFIGURATION DUNE APPLICATION


1

N am e V al u eCollection section = ( NameValueCollection ) C o n f i g urationManager . GetSection ( " I nf or ma t io ns Ut i li sa t eu r " ) ;

La rcupration des informations de conguration se fait de la mme faon :


1 2 3

Console . WriteLine ( section [ " login " ]) ; Console . WriteLine ( section [ " MOTDEPASSE " ]) ; Console . WriteLine ( section [ " age " ]) ;

Ou encore avec une boucle pour toutes les rcuprer :


1 2 3 4

foreach ( string cle in section ) { Console . WriteLine ( " Cl : " + cle + " / Valeur : " + section [ cle ]) ; }

vous de voir laquelle des deux mthodes vous prfrez, mais dans tous les cas, il faudra fonctionner avec un systme de cl / valeur. Le type SingleTagSectionHandler Ce troisime type permet de grer une section dirente des deux prcdentes. Il sera possible davoir autant dattributs que lon souhaite dans la section. Prenez par exemple cette conguration :
1 2 3 4 5 6

< configuration > < configSections > < section name = " MonUtilisateur " type = " System . Configuration . S i n gl eT a gS ec t io nH an d le r " / > </ configSections > < MonUtilisateur prenom = " Nico " age = " 30 " adresse = " 9 rue des bois " / > </ configuration >

Nous voyons que je peux mettre autant dattributs que je le souhaite. Par contre, il ne sera possible de faire apparatre la section MonUtilisateur quune seule fois, alors que dans les sections prcdentes, nous avions une liste de cl / valeur. Nous pourrons rcuprer notre conguration avec le code suivant :
1 2 3 4

Hashtable section = ( Hashtable ) ConfigurationManager . GetSection ( " MonUtilisateur " ) ; Console . WriteLine ( section [ " prenom " ]) ; Console . WriteLine ( section [ " age " ]) ; Console . WriteLine ( section [ " adresse " ]) ;

Attention par contre, cette fois-ci la casse est importante pour obtenir la valeur de notre attribut. Notons notre boucle habituelle permettant de retrouver tous les attributs de notre section : 406

LES GROUPES DE SECTIONS


1 2 3 4

foreach ( DictionaryEntry d in section ) { Console . WriteLine ( " Attribut : " + d . Key + " / Valeur : " + d . Value ) ; }

Voil pour les sections utilisant un type prdni.

Les groupes de sections


Super, nous savons dnir des sections de conguration. Elles nous permettent dorganiser un peu mieux notre chier de conguration. Par contre, si les sections se multiplient, cela va nouveau tre le bazar. Heureusement, les groupes de sections sont l pour remettre de lordre. Comme son nom lindique, un groupe de sections va permettre de regrouper plusieurs sections. Le but est de clarier le chier de conguration. Regardons lexemple suivant :
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16

< configuration > < configSections > < sectionGroup name = " Utilisateur " > < section name = " ParametreConnexion " type = " System . Configuration . S i ng le Ta g Se ct io n Ha nd l er " / > < section name = " InfoPersos " type = " System . Configuration . D i c t i o na r y Se c t io n H an d l er " / > </ sectionGroup > </ configSections > < Utilisateur > < P ar am etr eConnexion Login = " Nico " MotDePasse = " 12345 " Mode = " Authentification Locale " / > < InfoPersos > < add key = " prenom " value = " Nicolas " / > < add key = " age " value = " 30 " / > </ InfoPersos > </ Utilisateur > </ configuration >

Nous voyons ici que jai dni un groupe qui sappelle Utilisateur, en utilisant la balise sectionGroup, contenant deux sections de conguration. Remarquons plus bas le contenu des sections et nous remarquons que la balise <Utilisateur> contient nos sections de conguration comme prcdemment. Pour obtenir nos valeurs de conguration, la seule chose qui change est la faon de charger la section. Ici, nous mettons le nom de la section prcde du nom du groupe. Ce qui donne :
1 2

Hashtable section1 = ( Hashtable ) ConfigurationManager . GetSection ( " Utilisateur / ParametreConnexion " ) ; Hashtable section2 = ( Hashtable ) ConfigurationManager . GetSection ( " Utilisateur / InfoPersos " ) ;

407

CHAPITRE 35. LA CONFIGURATION DUNE APPLICATION Aprs, la faon de rcuprer les valeurs de conguration de chaque section reste la mme. Avouez que cest quand mme plus clair non ?

Crer une section de conguration personnalise


Nous allons tudier rapidement comment crer des sections de conguration personnalises. Pour cela, il faut crer une section en drivant de la classe ConfigurationSection. Cette classe permet de reprsenter une section dun chier de conguration. Donc, en toute logique, nous pouvons lenrichir avec nos proprits. Il sut pour cela de dcorer nos propres proprits avec lattribut ConfigurationProperty. Ce qui donne :
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16

public class PersonneSection : ConfigurationSection { [ C o n f i gurat ionPro perty ( " age " , IsRequired = true ) ] public int Age { get { return ( int ) this [ " age " ]; } set { this [ " age " ] = value ; } } [ C o n f i gurat ionPro perty ( " prenom " , IsRequired = true ) ] public string Prenom { get { return ( string ) this [ " prenom " ]; } set { this [ " prenom " ] = value ; } }

Le grand intrt ici est de pouvoir typer les proprits. Ainsi, nous pouvons avoir une section de conguration qui travaille avec un entier par exemple. Tout est fait par la classe mre ici et il sut dutiliser ses proprits indexes en y accdant par son nom. Pour que notre section personnalise soit reconnue, il faut la dclarer avec notre nouveau type :
1 2 3

< configSections > < section name = " PersonneSection " type = " Ma Premie reApp licati on . PersonneSection , MaPre miereA pplica tion " / > </ configSections >

Le nom du type est constitu du nom complet du type (espace de noms + nom de la classe) suivi dune virgule et du nom de lassembly. Ici, lespace de noms est le mme que lassembly, car jai cr mes classes la racine du projet. Si vous avez un doute, vous devez vrier lespace de noms dans lequel est dclare la classe. Ensuite, nous pourrons dnir notre section, ce qui donne au nal :
1 2 3

< configuration > < configSections > < section name = " PersonneSection " type = " M aPr emier eAppli catio n . PersonneSection ,

408

CRER UNE SECTION PERSONNALISE AVEC UNE COLLECTION


M a P r e mier eAppl icatio n " / > </ configSections > < PersonneSection prenom = " nico " age = " 30 " / > </ configuration >

4 5 6

Pour accder aux informations contenues dans la section, il faudra charger la section comme dhabitude :
1 2

PersonneSection section = ( PersonneSection ) ConfigurationManager . GetSection ( " PersonneSection " ) ; Console . WriteLine ( section . Prenom + " a " + section . Age + " ans " );

Ce qui est intressant de remarquer ici, cest quon accde directement nos proprits via notre section personnalise. Ce qui est une grande force et permet de travailler avec un entier et une chane de caractres. Il faudra faire attention deux choses ici. La premire est la casse. Comme on la vu dans le code, le nom est crit en minuscule. Il faudra tre cohrent entre le nom indiqu dans lattribut ConfigurationProperty, celui indiqu en paramtre de loprateur dindexation et celui crit dans le chier de conguration. Tout doit tre orthographi de la mme faon, sinon nous aurons une exception :
Exception non g r e : System . Configuration . C o n f i g u r a t i o n E r r o r s E x c e p t i o n : Attribut PRENOM non reconnu . Notez que les noms d attributs respectent la casse . [ ... ] \ M a P r e m i e r e Appl icatio n \ Program . cs : ligne 14

De mme, si nous ne saisissons pas une valeur entire dans lattribut age, il va y avoir un problme de conversion :
Exception non g r e : System . Configuration . C o n f i g u r a t i o n E r r o r s E x c e p t i o n : La valeur de la propri t age ne peut pas tre analys e . L erreur est : trente n est pas une valeur valide pour Int32 . [ ... ] \ M aPremi ereAp plicat ion \ Program . cs : ligne 14

Crer une section personnalise avec une collection


Dans le paragraphe du dessus, on constate quon ne peut dnir quun lment dans notre section. Il pourrait tre intressant dans certains cas davoir une section personnalise qui puisse contenir plusieurs lments, par exemple pour avoir une liste de personnes. Pour ce faire, on utilisera la classe ConfigurationPropertyCollection. La premire chose est de crer un lment en drivant de la classe ConfigurationElement. Cet lment va ressembler beaucoup ce quon a fait pour crer une section personnalise : 409

CHAPITRE 35. LA CONFIGURATION DUNE APPLICATION


1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30

public class ClientElement : ConfigurationElement { private static readonly C o n f i g u r a t i o n P r o p e r t y C o l l e c t i o n _proprietes ; private static readonly Conf igura tionPr opert y age ; private static readonly Conf igura tionPr opert y prenom ; static ClientElement () { prenom = new Co nfigu ration Proper ty ( " prenom " , typeof ( string ) , null , C o n f i g u r a t i o n P r o p e r t y O p t i o n s . IsKey ) ; age = new Confi gurati onPro perty ( " age " , typeof ( int ) , null , C o n f i g u r a t i o n P r o p e r t y O p t i o n s . IsRequired ) ; _proprietes = new C o n f i g u r a t i o n P r o p e r t y C o l l e c t i o n { prenom , age }; } public string Prenom { get { return ( string ) this [ " prenom " ]; } set { this [ " prenom " ] = value ; } } public int Age { get { return ( int ) this [ " age " ]; } set { this [ " age " ] = value ; } } protected override C o n f i g u r a t i o n P r o p e r t y C o l l e c t i o n Properties { get { return _proprietes ; } }

Ici, je dnis deux proprits, Prenom et Age, qui me permettent bien sr dy stocker un prnom et un ge qui sont respectivement une chane de caractres et un entier. noter que nous avons besoin de dcrire ces proprits dans le constructeur statique de la classe. Pour cela, il faut lui indiquer son nom, cest--dire la chane qui sera utilise comme attribut dans llment de la section de conguration. Ensuite, nous lui indiquons son type ; pour cela on utilise le mot-cl typeof qui permet justement de renvoyer le type (dans le sens objet Type) dun type. Enn nous lui indiquons une option, par exemple le prnom sera la cl de mon lment (qui est une valeur unique et obligatoire saisir) et lge, qui sera un lment obligatoire saisir galement. Ensuite, nous avons besoin dutiliser cette classe travers une collection dlments. Pour ce faire, il faut crer une classe qui drive de ConfigurationElementCollection :
1

public class Cl ie n tE le m en tC ol l ec ti on : ConfigurationElementCollection

410

CRER UNE SECTION PERSONNALISE AVEC UNE COLLECTION


2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48

public override C o n f i g u r a t i o n E l e m e n t C o l l e c t i o n T y p e CollectionType { get { return C o n f i g u r a t i o n E l e m e n t C o l l e c t i o n T y p e . BasicMap ; } } protected override string ElementName { get { return " Client " ; } } protected override C o n f i g u r a t i o n P r o p e r t y C o l l e c t i o n Properties { get { return new C o n f i g u r a t i o n P r o p e r t y C o l l e c t i o n () ; } } public ClientElement this [ int index ] { get { return ( ClientElement ) BaseGet ( index ) ; } set { if ( BaseGet ( index ) != null ) { BaseRemoveAt ( index ) ; } BaseAdd ( index , value ) ; } } public new ClientElement this [ string nom ] { get { return ( ClientElement ) BaseGet ( nom ) ; } } public void Add ( ClientElement item ) { BaseAdd ( item ) ; } public void Remove ( ClientElement item ) { BaseRemove ( item ) ; } public void RemoveAt ( int index ) { BaseRemoveAt ( index ) ; }

411

CHAPITRE 35. LA CONFIGURATION DUNE APPLICATION


49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64

public void Clear () { BaseClear () ; } protected override ConfigurationElement CreateNewElement () { return new ClientElement () ; } protected override object GetElementKey ( C o nfigurationElement element ) { return (( ClientElement ) element ) . Prenom ; }

Vous pouvez copier ce code grce au code web suivant : Copier ce code Code web : 854375
Ces classes ont toujours la mme structure. Ce qui est important de voir est quon utilise lintrieur la classe ClientElement pour indiquer le type de la collection. Nous indiquons galement le nom de la balise qui sera utilise dans le chier de conguration, cest la chane Client que renvoie la proprit ElementName. Enn, jai la possibilit de dnir ma cl en substituant la mthode GetElementKey. Le reste des mthodes appellent les mthodes de la classe mre. Enn, il faut crer notre section personnalise, qui drive comme dhabitude de ConfigurationSection :
1 2 3 4 5 6 7 8

public class ListeClientSection : ConfigurationSection { private static readonly C o n f i g u r a t i o n P r o p e r t y C o l l e c t i o n proprietes ; private static readonly Conf igura tionPr opert y liste ; static ListeClientSection () { liste = new Conf igurat ionPr operty ( string . Empty , typeof ( C li en tE l em en t Co ll ec t io n ) , null , C o n f i g u r a t i o n P r o p e r t y O p t i o n s . IsRequired | C o n f i g u r a t i o n P r o p e r t y O p t i o n s . IsDefaultCollection ) ; proprietes = new C o n f i g u r a t i o n P r o p e r t y C o l l e c t i o n { liste }; } public C li en t El em en t Co ll e ct io n Listes { get { return ( Cl i en tE le m en tC o ll ec ti o n ) base [ liste ]; }

9 10 11 12 13 14

412

CRER UNE SECTION PERSONNALISE AVEC UNE COLLECTION


15 16 17 18 19 20 21 22 23 24 25 26

} public new ClientElement this [ string nom ] { get { return Listes [ nom ]; } } protected override C o n f i g u r a t i o n P r o p e r t y C o l l e c t i o n Properties { get { return proprietes ; } }

Notons dans cette classe comment nous utilisons loprateur dindexation pour renvoyer un lment partir de sa cl et renvoyer la liste des lments. Maintenant, nous pouvons crire notre conguration :
1 2 3

4 5 6 7 8 9

< configuration > < configSections > < section name = " ListeClientSection " type = " M a P r e mier eAppl icatio n . ListeClientSection , M a P r e mier eAppl icatio n " / > </ configSections > < L is te Cli en tSection > < Client prenom = " Nicolas " age = " 30 " / > < Client prenom = " J r mie " age = " 20 " / > </ L is teC li entSection > </ configuration >

Nous avons besoin nouveau de dnir la section en indiquant son type. Puis nous pouvons crer notre section et positionner notre liste de clients. Pour accder cette section, nous pouvons charger notre section comme avant avec la mthode GetSection :
1

Li st eC lie nt Se ction section = ( ListeClientSection ) C o n f i g u r a tionManager . GetSection ( " ListeClientSection " ) ;

Puis nous pouvons itrer sur les lments de notre section :


1 2 3 4

foreach ( ClientElement clientElement in section . Listes ) { Console . WriteLine ( clientElement . Prenom + " a " + clientElement . Age + " ans " ) ; }

Ou bien, nous pouvons accder un lment partir de sa cl :


1 2 3 4

ClientElement elementNicolas = section [ " Nicolas " ]; Console . WriteLine ( elementNicolas . Prenom + " a " + elementNicolas . Age + " ans " ) ; ClientElement elementJeremie = section [ " J r mie " ];

413

CHAPITRE 35. LA CONFIGURATION DUNE APPLICATION


5

Console . WriteLine ( elementJeremie . Prenom + " a " + elementJeremie . Age + " ans " ) ;

Ce qui donnera :
Nicolas a 30 ans J r mie a 20 ans

Voil pour la section personnalise avec une liste dlments. Cette partie tait peuttre un peu plus complique, mais retenons que la structure est souvent la mme, il sera facile dadapter vos sections partir de ce code. Nous avons dcouvert plusieurs faons de stocker des paramtres utilisables par notre application. Il faut savoir que beaucoup de composants du framework .NET sont intimement lis ce chier de conguration, comme une application web cre avec ASP.NET ou lorsquon utilise des services web. Il est important de remarquer que ce chier est un chier de conguration dapplication. Il y a dautres endroits du mme genre pour stocker de la conguration pour les applications .NET, comme le machine.config qui est un chier de conguration partag par toutes les applications de la machine. Il y a un hritage entre les dirents chiers de conguration. Si lon dnit une conguration au niveau machine (dans le machine.config), il est possible de la rednir pour notre application (app.config). En gnral, le chier machine.config se trouve dans le rpertoire dinstallation du framework .NET, cest--dire dans un sous rpertoire du systme dexploitation, dpendant de la version du framework .NET installe. Chez moi par exemple, il se trouve dans le rpertoire : C:\Windows\Microsoft.NET\Framework\v4.0.30319\Config. Enn, remarquons quil est possible de faire des modications du chier de conguration directement depuis le code de notre application. Cest un point qui est rarement utilis et que jai choisi de ne pas prsenter pour que le chapitre ne soit pas trop long !

En rsum
Les chiers de conguration sont des chiers XML qui possdent les paramtres de conguration de notre application. Pour les applications console, ils sappellent app.config. On peut dnir toutes sortes de valeurs de conguration grce aux sections prdnies ou en ajoutant son propre type de section personnalise.

414

Chapitre

36
Dicult :

Introduction LINQ

INQ signie Language INtegrated Query. Cest un ensemble dextensions du langage permettant de faire des requtes sur des donnes en faisant abstraction de leur type. Il permet dutiliser facilement un jeu dinstructions supplmentaires an de ltrer des donnes, faire des slections, etc. Il existe plusieurs domaines dapplications pour LINQ :

Linq To Entities ou Linq To SQL qui utilisent ces extensions de langage sur les bases de donnes. Linq To XML qui utilise ces extensions de langage pour travailler avec les chiers XML. Linq To Object qui permet de travailler avec des collections dobjets en mmoire. Ltude de LINQ ncessiterait un livre en entier, aussi nous allons nous concentrer sur la partie qui va le plus nous servir en tant que dbutant et qui va nous permettre de commencer travailler simplement avec cette nouvelle syntaxe, savoir Linq To Object. Il sagit dextensions permettant de faire des requtes sur les objets en mmoire et notamment sur toutes les listes ou collections. En fait, sur tout ce qui implmente IEnumerable<>.

415

CHAPITRE 36. INTRODUCTION LINQ

Les requtes Linq


Les requtes Linq proposent une nouvelle syntaxe permettant dcrire des requtes qui ressemblent de loin des requtes SQL. Pour ceux qui ne connaissent pas le SQL, il sagit dun langage permettant de faire des requtes sur les bases de donnes. Pour utiliser ces requtes, il faut ajouter lespace de noms adquat, savoir :
1

using System . Linq ;

Ce using est en gnral inclus par dfaut lorsquon cre un nouveau chier. Jusqu maintenant, si nous voulions acher les entiers dune liste dentiers qui sont strictement suprieurs 5, nous aurions fait :
1 2 3 4 5 6 7 8

List < int > liste = new List < int > { 4 , 6 , 1 , 9 , 5 , 15 , 8 , 3 }; foreach ( int i in liste ) { if ( i > 5 ) { Console . WriteLine ( i ) ; } }

Grce Linq To Objet, nous allons pouvoir ltrer en amont la liste an de ne parcourir que les entiers qui nous intressent, en faisant :
1 2 3 4 5 6 7 8

List < int > liste = new List < int > { 4 , 6 , 1 , 9 , 5 , 15 , 8 , 3 }; IEnumerable < int > requeteFiltree = from i in liste where i > 5 select i ; foreach ( int i in requeteFiltree ) { Console . WriteLine ( i ) ; }

ce qui donnera :
6 9 15 8

Nous avons ici cr une requte Linq qui contient des mots-cls, comme from, in, where et select. Ici, cette requte veut dire que nous partons (from) de la liste liste en analysant chaque entier de la liste dans (in) la variable i o (where) i est suprieur 5. Dans ce cas, lentier est slectionn comme faisant partie du rsultat de la requte grce au mot-cl select, qui fait un peu oce de return. Cette requte renvoie un IEnumerable<int>. Le type gnrique est ici le type int car cest le type de la variable i qui est retourn par le select. Le fait de renvoyer un IEnumerable<> va permettre potentiellement de rutiliser le rsultat de cette requte pour un ltre successif ou une expression dirente. En eet, Linq travaille sur des IEnumerable<>. Nous pourrions 416

LES REQUTES LINQ par exemple ordonner cette liste par ordre croissant grce au mot-cl orderby. Cela donnerait :
1 2 3 4 5 6 7 8 9 10 11

List < int > liste = new List < int > { 4 , 6 , 1 , 9 , 5 , 15 , 8 , 3 }; IEnumerable < int > requeteFiltree = from i in liste where i > 5 select i ; IEnumerable < int > requeteOrdonnee = from i in requeteFiltree orderby i select i ; foreach ( int i in requeteOrdonnee ) { Console . WriteLine ( i ) ; }

qui pourrait galement scrire en une seule fois avec :


1 2 3 4 5 6 7 8 9

List < int > liste = new List < int > { 4 , 6 , 1 , 9 , 5 , 15 , 8 , 3 }; IEnumerable < int > requete = from i in liste where i > 5 orderby i select i ; foreach ( int i in requete ) { Console . WriteLine ( i ) ; }

Et nous aurons :
6 8 9 15

Lintrt est que grce ces syntaxes, nous pouvons combiner facilement plusieurs ltres et construire des requtes plus ou moins complexes. Par exemple, imaginons des clients :
1 2 3 4 5 6

public class Client { public int Identifiant { get ; set ; } public string Nom { get ; set ; } public int Age { get ; set ; } }

dont ou voudrait savoir sils sont majeurs, puis quon voudrait trier par Age puis par Nom, nous pourrions faire :
1 2 3 4

List < Client > listeClients = new List < Client > { new Client { Identifiant = 1 , Nom = " Nicolas " , Age = 30 } , new Client { Identifiant = 2 , Nom = " J r mie " , Age = 20 } ,

417

CHAPITRE 36. INTRODUCTION LINQ


5 6 7 8 9 10 11 12 13 14 15 16 17

};

new Client { Identifiant = 3 , Nom = " Delphine " , Age = 30 } , new Client { Identifiant = 4 , Nom = " Bob " , Age = 10 }

IEnumerable < string > requete = from client in listeClients where client . Age > 18 orderby client . Age , client . Nom select client . Nom ; foreach ( string prenom in requete ) { Console . WriteLine ( prenom ) ; }

Ce qui donnera :
J r mie Delphine Nicolas

Notez ici que mon select renvoie le nom du client, qui est un string. Il est donc normal que ma requte renvoie un IEnumerable<string> car jai choisi quelle ne slectionne que les noms qui sont de type string. Il est assez frquent de construire des objets anonymes lintrieur dune requte. Par exemple, plutt que de renvoyer uniquement le nom du client, je pourrais galement renvoyer lge, mais comme je nai pas besoin de lidentiant, je peux crer un objet anonyme juste avec ces deux proprits :
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16

List < Client > listeClients = new List < Client > { new Client { Identifiant = 1 , Nom = " Nicolas " , Age = 30 } , new Client { Identifiant = 2 , Nom = " J r mie " , Age = 20 } , new Client { Identifiant = 3 , Nom = " Delphine " , Age = 30 } , new Client { Identifiant = 4 , Nom = " Bob " , Age = 10 } , }; var requete = from client in listeClients where client . Age > 18 orderby client . Age , client . Nom select new { client . Nom , client . Age }; foreach ( var obj in requete ) { Console . WriteLine ( " { 0 } a { 1 } ans " , obj . Nom , obj . Age ) ; }

Et nous aurons :
J r mie a 20 ans Delphine a 30 ans Nicolas a 30 ans

418

LES REQUTES LINQ Mon objet anonyme contient ici juste une proprit Nom et une proprit Age. noter que je suis oblig ce moment-l dutiliser le mot-cl var pour dnir la requte, car je nai pas de type donner cette requte. De mme, dans le foreach je dois utiliser le mot-cl var pour dnir le type anonyme. Les requtes peuvent tre de plus en plus compliques, comme faisant des jointures. Par exemple, ajoutons une classe Commande :
1 2 3 4 5 6

public class Commande { public int Identifiant { get ; set ; } public int IdentifiantClient { get ; set ; } public decimal Prix { get ; set ; } }

Je peux crer des commandes pour des clients :


1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17

List < Client > listeClients = new List < Client > { new Client { Identifiant = 1 , Nom = " Nicolas " , Age = 30 } , new Client { Identifiant = 2 , Nom = " J r mie " , Age = 20 } , new Client { Identifiant = 3 , Nom = " Delphine " , Age = 30 } , new Client { Identifiant = 4 , Nom = " Bob " , Age = 10 } , }; List < Commande > listeCommandes { new Commande { Identifiant = 150 . 05M } , new Commande { Identifiant = 30M } , new Commande { Identifiant = 99 . 99M } , new Commande { Identifiant = 100M } , new Commande { Identifiant = 80M } , new Commande { Identifiant = 10M } , }; = new List < Commande > = 1 , IdentifiantClient = 1 , Prix = 2 , IdentifiantClient = 2 , Prix = 3 , IdentifiantClient = 1 , Prix = 4 , IdentifiantClient = 1 , Prix = 5 , IdentifiantClient = 3 , Prix = 6 , IdentifiantClient = 3 , Prix

Et grce une jointure, rcuprer avec ma requte le nom du client et le prix de sa commande :
1 2 3 4 5 6 7 8

var liste = from commande in listeCommandes join client in listeClients on commande . IdentifiantClient equals client . Identifiant select new { client . Nom , commande . Prix }; foreach ( var element in liste ) { Console . WriteLine ( " Le client { 0 } a pay { 1 } " , element . Nom , element . Prix ) ; }

419

CHAPITRE 36. INTRODUCTION LINQ Ce qui donne :


Le Le Le Le Le Le client client client client client client Nicolas a pay 150 ,05 J r mie a pay 30 Nicolas a pay 99 ,99 Nicolas a pay 100 Delphine a pay 80 Delphine a pay 10

On utilise le mot-cl join pour faire la jointure entre les deux listes puis on utilise le mot-cl on et le mot-cl equals pour indiquer sur quoi on fait la jointure. noter que ceci peut se raliser en imbriquant galement les from et en ltrant sur lgalit des identiants clients :
1 2 3 4 5 6 7 8 9

var liste = from commande in listeCommandes from client in listeClients where client . Identifiant == commande . IdentifiantClient select new { client . Nom , commande . Prix }; foreach ( var element in liste ) { Console . WriteLine ( " Le client { 0 } a pay { 1 } " , element . Nom , element . Prix ) ; }

Il est intressant de pouvoir regrouper les objets qui ont la mme valeur. Par exemple pour obtenir toutes les commandes, groupes par client, on fera :
1 2 3 4 5 6 7 8 9 10 11

var liste = from commande in listeCommandes group commande by commande . IdentifiantClient ; foreach ( var element in liste ) { Console . WriteLine ( " Le client : { 0 } a r alis { 1 } commande ( s ) " , element . Key , element . Count () ) ; foreach ( Commande commande in element ) { Console . WriteLine ( " \ tPrix : { 0 } " , commande . Prix ) ; } }

Ici, cela donne :


Le client : 1 a r alis 3 commande ( s ) Prix : 150 ,05 Prix : 99 ,99 Prix : 100 Le client : 2 a r alis 1 commande ( s ) Prix : 30 Le client : 3 a r alis 2 commande ( s )

420

LES REQUTES LINQ


Prix : 80 Prix : 10

Il est possible de cumuler le group by avec notre jointure prcdente histoire davoir galement le nom du client :
1 2 3 4 5 6 7

var liste = from commande in listeCommandes join client in listeClients on commande . IdentifiantClient equals client . Identifiant group commande by new { commande . IdentifiantClient , client . Nom }; foreach ( var element in liste ) { Console . WriteLine ( " Le client { 0 } ({ 1 }) a r alis { 2 } commande ( s ) " , element . Key . Nom , element . Key . IdentifiantClient , element . Count () ) ; foreach ( Commande commande in element ) { Console . WriteLine ( " \ tPrix : { 0 } " , commande . Prix ) ; } }

8 9 10 11 12

Et nous obtenons :
Le client Nicolas (1) a r alis 3 commande ( s ) Prix : 150 ,05 Prix : 99 ,99 Prix : 100 Le client J r mie (2) a r alis 1 commande ( s ) Prix : 30 Le client Delphine (3) a r alis 2 commande ( s ) Prix : 80 Prix : 10

noter que le group by termine la requte, un peu comme le select. Ainsi, si lon veut slectionner quelque chose aprs le group by, il faudra utiliser le mot-cl into et la syntaxe suivante :
1 2 3 4 5 6 7 8 9

var liste = from commande in listeCommandes join client in listeClients on commande . IdentifiantClient equals client . Identifiant group commande by new { commande . IdentifiantClient , client . Nom } into commandesGroupees select new { commandesGroupees . Key . IdentifiantClient , commandesGroupees . Key . Nom , NombreDeCommandes = commandesGroupees . Count ()

421

CHAPITRE 36. INTRODUCTION LINQ


10 11 12 13 14

}; foreach ( var element in liste ) { Console . WriteLine ( " Le client { 0 } ({ 1 }) a r alis { 2 } commande ( s ) " , element . Nom , element . IdentifiantClient , element . NombreDeCommandes ) ; }

15

Avec pour rsultat :


Le client Nicolas (1) a r alis 3 commande ( s ) Le client J r mie (2) a r alis 1 commande ( s ) Le client Delphine (3) a r alis 2 commande ( s )

Lintrt dutiliser le mot-cl into est galement de pouvoir enchaner avec une autre jointure ou un autre ltre permettant de continuer la requte. Il est galement possible dutiliser des variables lintrieur des requtes grce au mot-cl let. Cela va nous permettre de stocker des rsultats temporaires pour les rutiliser ensuite :
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17

var liste = from commande in listeCommandes join client in listeClients on commande . IdentifiantClient equals client . Identifiant group commande by new { commande . IdentifiantClient , client . Nom } into commandesGroupees let total = commandesGroupees . Sum ( c = > c . Prix ) where total > 50 orderby total select new { commandesGroupees . Key . IdentifiantClient , commandesGroupees . Key . Nom , NombreDeCommandes = commandesGroupees . Count () , PrixTotal = total }; foreach ( var element in liste ) { Console . WriteLine ( " Le client { 0 } ({ 1 }) a r alis { 2 } commande ( s ) pour un total de { 3 } " , element . Nom , element . IdentifiantClient , element . NombreDeCommandes , element . PrixTotal ) ; }

18

Par exemple, ici jutilise le mot-cl let pour stocker le total dune commande groupe dans la variable total (nous verrons la mthode Sum() un tout petit peu plus bas), ce qui me permet ensuite de ltrer avec un where pour obtenir les commandes dont le total est suprieur 50 et de les trier par ordre de prix croissant. Ce qui donne : 422

LES MTHODES DEXTENSION LINQ

Le client Delphine (3) a r alis 2 commande ( s ) pour un total de 90 Le client Nicolas (1) a r alis 3 commande ( s ) pour un total de 350 ,04

Nous allons nous arrter l pour cet aperu des requtes LINQ. Nous avons pu voir que le C# dispose dun certain nombre de mots-cls qui permettent de manipuler nos donnes de manire trs puissante mais dune faon un peu inhabituelle. Cette faon dcrire des requtes LINQ sappelle en anglais la sugar syntax, que lon peut traduire par sucre syntaxique . Il dsigne de manire gnrale les constructions dun langage qui facilitent la rdaction du code sans modier lexpressivit du langage. Voyons prsent ce quil y a derrire cette jolie syntaxe.

Les mthodes dextension Linq


En fait, toute la sugar syntax que nous avons vue prcdemment repose sur un certain nombre de mthodes dextension qui travaillent sur les types IEnumerable<T>. Par exemple, la requte suivante :
1 2 3 4 5 6 7 8

List < int > liste = new List < int > { 4 , 6 , 1 , 9 , 5 , 15 , 8 , 3 }; IEnumerable < int > requeteFiltree = from i in liste where i > 5 select i ; foreach ( int i in requeteFiltree ) { Console . WriteLine ( i ) ; }

scrit vritablement :
1 2 3 4 5 6

List < int > liste = new List < int > { 4 , 6 , 1 , 9 , 5 , 15 , 8 , 3 }; IEnumerable < int > requeteFiltree = liste . Where ( i = > i > 5 ) ; foreach ( int i in requeteFiltree ) { Console . WriteLine ( i ) ; }

Nous utilisons la mthode dextension Where() en lui fournissant une expression lambda servant de prdicat pour ltrer la liste. Cest de cette faon que le compilateur traduit la sugar syntax. Ce nest donc quune faon plus lgante dutiliser ces mthodes dextension. Chaque mthode dextension renvoie un IEnumerable<T> ce qui permet denchaner facilement les ltres successifs. Par exemple, rajoutons une date et un nombre darticles notre classe Commande : 423

CHAPITRE 36. INTRODUCTION LINQ


1 2 3 4 5 6 7 8

public class Commande { public int Identifiant { get ; set ; } public int IdentifiantClient { get ; set ; } public decimal Prix { get ; set ; } public DateTime Date { get ; set ; } public int NombreArticles { get ; set ; } }

Avec la requte suivante :


1 2 3 4 5

IEnumerable < Commande > commandesFiltrees = listeCommandes . Where ( commande = > commande . Prix > 100 ) . Where ( commande = > commande . NombreArticles > 10 ) . OrderBy ( commande = > commande . Prix ) . ThenBy ( commande = > commande . DateAchat ) ;

nous pouvons obtenir les commandes dont le prix est suprieur 100, o le nombre darticles est suprieur 10, tries par prix puis par date dachat. De plus, ces mthodes dextension font beaucoup plus de choses que ce que lon peut faire avec la sugar syntax. Il existe pas mal de mthodes intressantes, que nous ne pourrons pas toutes tudier. Regardons par exemple la mthode Sum() (qui a t utilise dans le paragraphe prcdent) qui permet de faire la somme des lments dune liste ou la mthode Average() qui permet den faire la moyenne :
1 2 3

List < int > liste = new List < int > { 4 , 6 , 1 , 9 , 5 , 15 , 8 , 3 }; Console . WriteLine ( " Somme : { 0 } " , liste . Sum () ) ; Console . WriteLine ( " Moyenne : { 0 } " , liste . Average () ) ;

Qui nous renvoie dans ce cas :


Somme : 51 Moyenne : 6 ,375

Tout est dj fait, cest pratique ! videmment, les surcharges de ces deux mthodes dextension ne fonctionnent quavec des types int ou double ou decimal. . . Qui envisagerait de faire une moyenne sur une chane ? Par contre, il est possible de dnir une expression lambda dans la mthode Sum() an de faire la somme sur un lment dun objet, comme le prix de notre commande :
1

decimal prixTotal = listeCommandes . Sum ( commande = > commande . Prix ) ;

Dautres mthodes sont bien utiles. Par exemple la mthode dextension Take() nous permet de rcuprer les N premiers lments dune liste :
1

IEnumerable < Client > extrait = listeClients . OrderByDescending ( client = > client . Age ) . Take ( 5 ) ;

Ici, je trie dans un premier temps ma liste par ge dcroissant, et je prends les 5 premiers. Ce qui signie que je prends les 5 plus vieux clients de ma liste. Et sil ny 424

LES MTHODES DEXTENSION LINQ en a que 3 ? eh bien il prendra uniquement les 3 premiers ! Suivant le mme principe, on peut utiliser la mthode First() pour obtenir le premier lment dune liste :
1 2

List < int > liste = new List < int > { 4 , 6 , 1 , 9 , 5 , 15 , 8 , 3 }; int premier = liste . Where ( i = > i > 5 ) . First () ;

Jobtiens alors le premier lment de la liste qui est strictement suprieur 5. noter que le ltre peut galement se faire dans lexpression lambda de la mthode First() :
1

int premier = liste . First ( i = > i > 5 ) ;

Ce qui revient exactement au mme. Attention, sil ny a aucun lment dans la liste, alors la mthode First() lve lexception :
Exception non g r e : System . I nv a l i d O p e r a t io n E x c e p t i o n : La s quence ne contient aucun l ment .

Il est possible dans ce cas-l dviter une exception avec la mthode FirstOrDefault() qui renvoie la valeur par dfaut du type de la liste (0 si cest un type valeur, null si cest un type rfrence) :
1 2 3

Client nicolas = listeClients . FirstOrDefault ( client = > client . Nom == " Nicolas " ) ; if ( nicolas == null ) Console . WriteLine ( " Client non trouv " ) ;

Ici, je cherche le premier des clients dont le nom est Nicolas. Sil nest pas trouv, alors FirstOrDefault() me renvoie null, sinon, il me renvoie bien sr le bon objet Client. Dans le mme genre, nous pouvons compter grce la mthode Count() le nombre dlments dune source de donnes suivant un critre :
1

int n o m b r e C l i entsMajeurs = listeClients . Count ( client = > client . Age >= 18 ) ;

Ici, jobtiendrai le nombre de clients majeurs dans ma liste. De la mme faon quavec la sugar syntax, il est possible de faire une slection prcise des donnes que lon souhaite extraire, grce la mthode Select() :
1

var requete = listeClients . Where ( client = > client . Age >= 18 ) . Select ( client = > new { client . Age , client . Nom }) ;

Cela me permettra dobtenir une requte contenant les clients majeurs. noter que seront retourns des objets anonymes possdant une proprit Age et une proprit Nom. Bien sr, nous retrouverons nos jointures avec la mthode dextension Join() ou les groupes avec la mthode GroupBy(). Il existe beaucoup de mthodes dextension et il nest pas envisageable dans ce livre de toutes les dcrire. Je vais nir en vous parlant des mthodes ToList() et ToArray() qui, comme leurs noms le suggrent, permettent de forcer la requte tre mise dans une liste ou dans un tableau :
1

List < Client > lesPlusVieuxClients = listeClients . Order ByDescending ( client = > client . Age ) . Take ( 5 ) . ToList () ;

425

CHAPITRE 36. INTRODUCTION LINQ ou


1

Client [] lesPlusVieuxClients = listeClients . OrderByDescending ( client = > client . Age ) . Take ( 5 ) . ToArray () ;

Plutt que davoir un IEnumerable<>, nous obtiendrons cette fois-ci une List<> ou un tableau. Le fait dutiliser ces mthodes dextension a des consquences que nous allons dcrire.

Excution dire
Les mthodes dextension LINQ ou sa syntaxe sucre cest bien joli, mais quel est lintrt de sen servir plutt que dutiliser des boucles foreach, des if ou dautres choses ? Dj, parce quil y a plein de choses dj toutes faites : la somme, la moyenne, la rcupration de N lments, etc. Mais aussi pour une autre raison plus importante : lexcution dire. Nous en avons dj parl, lexcution dire est possible grce au mot-cl yield. Les mthodes dextensions Linq utilisent fortement ce principe. Cela veut dire que lorsque nous construisons une requte, elle nest pas excute tant que lon nitre pas sur le contenu de la requte. Ceci permet de stocker la requte, dempiler ventuellement des ltres ou des jointures et de ne pas calculer le rsultat tant quon nen a pas explicitement besoin. Ainsi, imaginons que nous souhaitions trier une liste dentiers. Avant cela, nous aurions fait :
1 2 3 4 5 6 7 8 9

List < int > liste = new List < int > { 4 , 6 , 1 , 9 , 5 , 15 , 8 , 3 }; liste . Sort () ; liste . Add ( 7 ) ; foreach ( int i in liste ) { Console . WriteLine ( i ) ; }

Ce qui aurait ach en toute logique la liste trie puis, la n, lentier 7 rajout, cest--dire :
1 3 4 5 6 8 9 15 7

426

EXCUTION DIFFRE Avec Linq, nous allons pouvoir crire :


1 2 3 4 5 6 7 8 9

List < int > liste = new List < int > { 4 , 6 , 1 , 9 , 5 , 15 , 8 , 3 }; var requete = liste . OrderBy ( e = > e ) ; liste . Add ( 7 ) ; foreach ( int i in requete ) { Console . WriteLine ( i ) ; }

Et si nous excutons ce code, nous aurons :


1 3 4 5 6 7 8 9 15

Bien que nous ayons ajout la valeur 7 aprs avoir tri la liste avec OrderBy, on se rend compte que tous les entiers sont quand mme tris lorsque nous les achons. En eet, la requte na t excute quau moment du foreach. Ceci implique donc que le tri va tenir compte de lajout du 7 la liste. La requte est construite en mmorisant les conditions comme notre OrderBy, mais cela fonctionne galement avec un where, et tout ceci nest excut que lorsquon le demande explicitement ; cest--dire avec un foreach dans ce cas-l. En fait, tant que le C# nest pas oblig de parcourir les lments numrables alors il ne le fait pas. Ce qui permet denchaner les ventuelles conditions et dviter les parcours inutiles. Par exemple, dans le cas ci-dessous, il est inutile dexcuter le premier ltre :
1 2 3 4

List < int > liste = new List < int > { 4 , 6 , 1 , 9 , 5 , 15 , 8 , 3 }; IEnumerable < int > requete = liste . Where ( i = > i > 5 ) ; // plein de choses qui n ' ont rien voir avec la requete requete = requete . Where ( i = > i > 10 ) ;

En eet, le deuxime ltre a tout intrt tre combin au premier an dtre simpli. Et encore, ici, on nutilise mme pas la requte, il y a encore moins dintrt eectuer nos ltres si nous ne nous servons pas du rsultat. Ceci peut paratre inattendu, mais cest trs important dans la faon dont Linq sen sert an doptimiser ses requtes. Ici, le parcours en mmoire pourrait paratre peu coteux, mais dans la mesure o Linq doit fonctionner aussi bien avec des objets, quavec des bases de donnes ou du XML (ou autres. . .), cette optimisation prend tout son sens. Le matre mot est la performance, primordial quand on accde aux bases de donnes. Cette excution dire est garde pour le plus tard possible. Cest--dire que le fait 427

CHAPITRE 36. INTRODUCTION LINQ de parcourir notre boucle va obligatoirement entraner lvaluation de la requte an de pouvoir retourner les rsultats cohrents. Il en va de mme pour certaines autres oprations, comme la mthode Sum(). Comment pourrions-nous faire la somme de tous les lments si nous ne les parcourons pas ? Cest aussi le cas pour les mthodes ToList() et ToArray(). Par contre, ce nest pas le cas pour les mthodes Where, ou Take, etc. Il est important de connatre ce mcanisme. Lexcution dire est trs puissante et connatre son fonctionnement permet de savoir exactement ce que nous faisons et pourquoi nous pouvons obtenir parfois des rsultats tranges.

Rcapitulatif des oprateurs de requtes


Pour terminer avec Linq, voici un tableau rcapitulatif des dirents oprateurs de requte. Nous ne les avons pas tous tudis ici car cela serait bien vite lassant. Mais grce leurs noms et leurs types, il est assez facile de voir quoi ils servent an de les utiliser dans la construction de nos requtes. Type Tri des donnes Oprations ensemblistes Filtrage des donnes Oprations de quanticateur Oprations de projection Partitionnement des donnes Oprations de jointure Regroupement de donnes Oprations de gnration Oprations dgalit Oprations dlment Oprateur de requte OrderBy, OrderByDescending, ThenBy, ThenByDescending, Reverse Distinct, Except, Intersect, Union OfType, Where All, Any, Contains Select, SelectMany Skip, SkipWhile, Take, TakeWhile Join, GroupJoin GroupBy, ToLookup DefaultIfEmpty, Empty, Range, Repeat SequenceEqual ElementAt, ElementAtOrDefault, First, FirstOrDefault, Last, LastOrDefault, Single, SingleOrDefault AsEnumerable, AsQueryable, Cast, OfType, ToArray, ToDictionary, ToList, ToLookup Concat Aggregate, Average, Count, LongCount, Max, Min, Sum Excution dire Oui Oui Oui Non Oui Oui Oui Oui Oui Non Non

Conversion de types de donnes Oprations de concatnation Oprations dagrgation

Non Oui Non

428

RCAPITULATIF DES OPRATEURS DE REQUTES Nhsitez pas consulter la documentation de ces mthodes dextension ou aller voir des exemples sur internet. Il y a beaucoup de choses faire avec ces mthodes. Il est important galement de bien savoir les matriser an dviter les problmes de performance. En eet, lvaluation systmatique des expressions peut tre coteuse, surtout quand le tout est imbriqu dans des boucles. utiliser judicieusement ! Voil pour ce petit aperu de Linq. Rappelez-vous bien que Linq est une abstraction qui permet de manipuler des sources de donnes direntes. Nous avons vu son utilisation avec les objets implmentant IEnumerable<>, avec ce quon appelle Linq To Objects. Il est possible de faire du Linq en allant manipuler des donnes en base de donnes, on utilisera pour cela Linq To SQL ou Linq To Entity. De mme, il est possible de manipuler les chiers XML avec Linq To XML. Linq apporte des mthodes dextension et une syntaxe complmentaire an dtre ecace avec la manipulation de sources de donnes. Sachez enn quil est possible de requter nimporte quelle source de donnes partir du moment o un connecteur spcique a t dvelopp. Cela a t fait par exemple pour interroger Google ou Amazon, mais aussi pour requter sur active directory, ou JSON, etc.

En rsum
Linq consiste en un ensemble dextensions du langage permettant de faire des requtes sur des donnes en faisant abstraction de leur type. Il existe plusieurs domaines dapplications de Linq, comme Linq to Object, Linq to Sql, etc. La sugar syntax ajoute des mots-cls qui permettent de faire des requtes qui ressemblent aux requtes faites avec le langage SQL. Derrire cette syntaxe se cache un bon nombre de mthodes dextension qui tirent parti des mcanismes dexcution dire.

429

CHAPITRE 36. INTRODUCTION LINQ

430

Chapitre

37
Dicult :

Accder aux donnes avec Entity Framework

ous allons voir dans ce chapitre comment connecter nos applications une base de donnes. Bien quil soit possible de travailler avec quasiment nimporte quelle base de donnes (oracle, mysql,. . .), le framework .NET ore toute sa puissance en fonctionnant avec SQL Server. Si vous vous souvenez, lors de linstallation de Visual C# Express, nous avons galement install Microsoft SQL Server 2008 express Service Pack 1. SQL Server 2008 Express est un moteur de base de donnes light, idal pour travailler en local sur son PC. Dans un environnement de production, nous aurons tout intrt travailler avec la version complte de SQL Server, mais pour ce livre, la version express est amplement susante. En plus. . . elle est gratuite !

431

CHAPITRE 37. ACCDER AUX DONNES AVEC ENTITY FRAMEWORK

Les bases de donnes et la modlisation


Une base de donnes est un gros espace de stockage structur qui permet denregistrer ecacement de trs grandes quantits dinformations. Cest un lment incontournable dans tout systme informatique. Dans une base de donnes, on peut stocker des informations unitaires, comme le nom dun client, son ge, son adresse, etc. Ce sont les champs. Ces champs sont regroups smantiquement dans des tables. Par exemple, la table des clients contient les informations relatives au client Nicolas, au client Jrmie, etc. Ces informations sont appeles des enregistrements. Lorsquon reprsentera les donnes de notre application, nous allons avoir besoin de plusieurs tables. Par exemple, pour reprsenter les donnes dune application de commerce, nous pouvons avoir une table Produits , une table Rayons , une table Clients , une table Commandes , etc. Il peut y avoir des relations entre les tables, par exemple un rayon peut contenir de 0 N produits, un client peut passer de 0 N commandes, etc. On appelle ces bases de donnes des bases de donnes relationnelles. Nous pouvons lire le contenu des tables et insrer de nouvelles valeurs grce au langage SQL. Nous allons trs peu nous en servir mais il est la base de tout requtage en base de donnes. Nous nallons pas faire ici de cours sur le SQL mais nous allons lutiliser certains endroits, par souci de simplicit. La premire chose faire est de rchir au modle de donnes dont on aura besoin dans notre application. Pour ce chapitre, nous allons prendre pour exemple une application de commerce, dans le genre site de-commerce, spcialise dans la cration dapplications console. Nous allons commencer par modliser les rayons et les produits. On a dit quun rayon pouvait tre compos de 0 N produits. De mme, un produit peut appartenir 0 ou N rayons. Un rayon possde un identiant, un nom et une description. Un produit possde un identiant, un nom, un prix, un stock et une url vers son image (voir la gure 37.1).

Figure 37.1 On appelle ce schma un modle conceptuel de donnes

Entity Framework et le mapping objet relationnel


Finalement, la modlisation que nous venons de faire ressemble beaucoup la modlisation oriente objet que nous commenons matriser. On pourrait trs bien avoir des 432

ENTITY FRAMEWORK ET LE MAPPING OBJET RELATIONNEL objets Produit, des objets Rayon. Un rayon pourrait contenir des produits, etc. Ne pourrions-nous pas essayer de reprsenter la base de donnes sous la forme dun modle orient objet plutt quun modle relationnel ? Cest le principe de ce que lon appelle un ORM 1 que lon traduit en franais par mapping objet-relationnel . LORM est un outil qui permet de gnrer une couche daccs aux donnes oriente objet partir dune base de donnes relationnelle en dnissant des correspondances entre cette base de donnes et des objets. Il existe plusieurs outils dORM pour .NET permettant de gnrer des objets C#, comme nhibernate, Entity Framework, etc. Nous allons utiliser ici Entity Framework. Cest lORM de Microsoft. Il est totalement intgr Visual C# Express et aux autres versions. Son travail consiste, entre autres, : modliser ses donnes et gnrer la base correspondante ; gnrer un modle partir dune base de donnes existante ; grer tous les accs la base de donnes (lecture, criture, suppression,. . .). Le grand intrt est que nous allons travailler directement avec des objets et cest lui qui soccupera de tout ce qui est persistance dans la base de donnes. Allez, ni le blabla, passons son utilisation ! Commenons par crer une application console MonApplicationBaseDeDonnes, ainsi quindiqu la gure 37.2.

Figure 37.2 Cration de lapplication console prsent, ajoutons un nouvel lment de type ADO.NET Entity Data Model notre projet, que lon va appeler ModelCommerce.edmx (voir gure 37.3). Cest ce type de chier qui va nous permettre de modliser nos donnes. Lassistant souvre et nous choisissons Modle vide, comme la gure 37.4. On voit apparatre une nouvelle fentre intitule Entity Data Model Designer. Cette fentre designer va nous permettre de modliser nos donnes. Elle nous donne galement accs la bote outils, ainsi que vous pouvez le voir la gure 37.5. Lorsque nous accdons la bote outils, nous pouvons voir plusieurs lments. Celui qui nous intresse est lentit (voir gure 37.6).
1. Cest lacronyme de lexpression anglaise : object relationnal mapping.

433

CHAPITRE 37. ACCDER AUX DONNES AVEC ENTITY FRAMEWORK

Figure 37.3 Ajout dun chier ADO.NET Entity Data Model

Figure 37.4 Choix dun modle vide

Figure 37.5 La bote outil est accessible sur la gauche 434

ENTITY FRAMEWORK ET LE MAPPING OBJET RELATIONNEL

Figure 37.6 Ajout dune entit sur le designer Nous allons pouvoir modliser nos entits en les faisant glisser sur le designer. Comme lillustre la gure 37.7, nous la voyons apparatre sur le designer.

Figure 37.7 Lentit apparat sur le designer Nous pouvons renommer cette entit, soit en allant modier la proprit Nom dans la fentre de proprits, soit en cliquant directement sur le nom dans le designer. Appelons cette entit Rayon. Nous pouvons constater que cette entit est gnre avec une proprit par dfaut : ID. Cest lidentiant de notre rayon. Cet identiant possde des proprits que nous pouvons voir dans la fentre de proprits (voir gure 37.8). Nous pouvons par exemple voir (ou modier) son nom (ID) et son type (Int32, qui est lquivalent de int). Nous voyons galement que cette proprit est la cl dentit. Ce qui veut dire que cest ce qui va nous permettre didentier notre rayon de manire unique (cest un peu plus complexe que a, mais retenons ce point). Nous pouvons ajouter une nouvelle proprit lentit ; pour cela il sut de faire un clic droit sur lentit et dajouter une proprit scalaire (voir gure 37.9). Une nouvelle proprit apparat dans notre entit Rayon (voir gure 37.10). Nous pouvons la renommer en Nom. Nous pouvons voir sur la gure 37.11 quelle est par dfaut du type String, ce qui nous va trs bien. 435

CHAPITRE 37. ACCDER AUX DONNES AVEC ENTITY FRAMEWORK

Figure 37.8 Les proprits de la proprit ID

Figure 37.9 Ajout dune proprit scalaire

Figure 37.10 Changement du nom de la proprit 436

ENTITY FRAMEWORK ET LE MAPPING OBJET RELATIONNEL

Figure 37.11 Le nom et le type de la proprit Rajoutons une troisime proprit Description, toujours de type String mais qui pourra tre nulle, il sut de dclarer la proprit Nullable True (voir la gure 37.12).

Figure 37.12 La proprit peut tre nulle a y est, notre entit Rayon est modlise ! Rajoutons maintenant une nouvelle entit, Produit, qui possde galement un identiant, un nom de type String et un prix de type Decimal. Pour mettre le type Decimal, il sut de changer le type dans la fentre des proprits (voir la gure 37.13). Ensuite, rajoutons une proprit Stock de type Int32 et une proprit UrlImage de type String. Nous obtenons deux superbes entits (voir la gure 37.14). Il est temps de relier les entits entre elles grce une association : faites un clic droit sur lentit Rayon et ajoutez une association, comme indiqu la gure 37.15. 437

CHAPITRE 37. ACCDER AUX DONNES AVEC ENTITY FRAMEWORK

Figure 37.13 Changement du type de la proprit

Figure 37.14 Les deux entits de notre modle

Figure 37.15 Ajout dune association entre les deux entits 438

ENTITY FRAMEWORK ET LE MAPPING OBJET RELATIONNEL Un nouvel cran souvre qui permet de dnir lassociation. Indiquons que la multiplicit est plusieurs sur les deux entits, ce qui permet de dire quun rayon peut contenir de 0 N produits et inversement, un produit peut tre contenu dans 0 N rayons. Notons au passage que les choix possibles sont 1, 0 ou 1 et plusieurs. Cest ce qui nous permet dindiquer la cardinalit de nos relations. Changez ensuite le nom des proprits de navigation en rajoutant un s Produit et Rayon, comme indiqu la gure 37.16.

Figure 37.16 Changement des caractristiques de lassociation Le designer est mis jour avec la relation et on peut voir apparatre des proprits de navigation dans les entits. Lentit Rayon a une proprit Produits, ce qui va permettre dobtenir la liste des produits dun Rayon. De mme, lentit Produit possde une proprit de navigation Rayons qui va permettre dobtenir la liste des rayons qui contiennent le produit (voir la gure 37.17). Nous allons encore faire une petite modication ce modle. Slectionnez lentit Rayon. Nous pouvons voir dans ses proprits que le nom du jeu dentit vaut RayonJeu, modiez-le en Rayons, comme indiqu la gure 37.18. Faites pareil pour lentit Produit : changez ProduitJeu en Produits. Voil, tout a, cest notre modle ! Il faut maintenant faire en sorte que notre base de donnes soit cohrente avec le modle. Il sut de faire un clic droit sur le designer et de choisir de gnrer la base de donnes partir du modle (voir la gure 37.19). 439

CHAPITRE 37. ACCDER AUX DONNES AVEC ENTITY FRAMEWORK

Figure 37.17 La liaison entre les deux entits

Figure 37.18 Modication du nom du jeu de donnes

Figure 37.19 Gnration de la base de donnes partir du modle

440

ENTITY FRAMEWORK ET LE MAPPING OBJET RELATIONNEL Une nouvelle fentre souvre nous permettant de choisir notre source de donnes. Celleci tant vide, cliquez sur Nouvelle connexion (voir la gure 37.20).

Figure 37.20 Ajouter une nouvelle connexion Une nouvelle fentre apparat nous permettant de choisir notre source de donnes (voir la gure 37.21).

Figure 37.21 Choix dun chier de base de donnes 441

CHAPITRE 37. ACCDER AUX DONNES AVEC ENTITY FRAMEWORK Attention, ici dans la version express de Visual C#, il nest possible de choisir que parmi deux options : un chier de base de donnes ou Microsoft SQL Server compact. Dans dautres versions express (notamment la version permettant de faire du dveloppement web) et dans les versions payantes de Visual Studio, il est possible de choisir directement un serveur de base de donnes. Cela aurait t plus pratique. Tant pis, nous allons faire avec ; choisissons le chier de base de donnes. Il faut lui donner un emplacement et un nom, par exemple dans le rpertoire des projets, je lappelle basecommerce.mdf. Ensuite, pour pouvoir sy connecter, nous utiliserons lauthentication Windows (voir la gure 37.22).

Figure 37.22 Slection de la base de donnes et du type de connexion Au moment de la validation, il nous est demand si lon souhaite crer le chier de base de donnes. Rpondez oui ! Puis nous arrivons sur un rcapitulatif et nous voyons en bas la chane de connexion la base de donnes, comme lillustre la gure 37.23. Nous pouvons choisir denregistrer les paramtres de connexion, ou bien de ne pas le faire, en cochant ou dcochant cette case. Dans tous les cas, cette chane de connexion ne nous servira pas en ltat. Cliquez sur suivant. Le designer dEntity Framework nous a nalement cr un chier contenant des instructions SQL quil nous propose denregistrer. Ces instructions SQL vont permettre de gnrer les tables de la base de donnes (voir la gure 37.24). Nous allons revenir sur ces instructions. Ce chier souvre galement dans Visual C# Express (voir la gure 37.25). 442

ENTITY FRAMEWORK ET LE MAPPING OBJET RELATIONNEL

Figure 37.23 La chane de connexion la base de donnes gnre par Visual C# Express

Figure 37.24 Visual C# Express nous gnre un script SQL

443

CHAPITRE 37. ACCDER AUX DONNES AVEC ENTITY FRAMEWORK

Figure 37.25 Le script SQL souvre dans Visual C# Express Nous en avons termin pour linstant avec le designer.

Installer et utiliser loutil de gestion de BDD


Nous avons le serveur de base de donnes, qui a t install en mme temps que Visual C# Express. Nous avons le script permettant de gnrer le modle de donnes. Il nous manque un outil permettant de crer la base de donnes et dexcuter le script. Cest loutil de gestion de base de donnes. Je vous laisse utiliser le code web suivant pour installer Microsoft SQL Server 2008 Management Studio Express : Microsoft SQL Server Code web : 225473

Notez quil ne sagit que des outils puisque nous avons dj install un serveur de base de donnes. Il existe cependant dautres installations qui cumulent le serveur de base de donnes ainsi que les outils. Tlchargez linstalleur qui vous convient et excutez-le. Puis choisissez Installation et poursuivez. Choisissez ensuite lajout de fonctionnalits une installation existante, comme indiqu la gure 37.26.

On poursuit linstallation, puis nous arrivons sur le choix des lments installer (voir la gure 37.27). Mme si le nom est trompeur, nous devons eectuer une nouvelle installation de SQL Server 2008. Ce nest pas tout fait une installation dune nouvelle instance car nous avons dj une instance installe. Nous pouvons le voir sur la copie dcran, o linstance existante dj installe sappelle SQLEXPRESS. Retenons bien le nom de cette instance, 444

INSTALLER ET UTILISER LOUTIL DE GESTION DE BDD

Figure 37.26 Choix dune nouvelle installation

Figure 37.27 Choix du type dinstallation

445

CHAPITRE 37. ACCDER AUX DONNES AVEC ENTITY FRAMEWORK il nous servira un peu plus loin. Puis nous arrivons sur lcran suivant qui nous indique que nous allons ajouter loutil de gestion de base (voir la gure 37.28).

Figure 37.28 Fentre de slection de composants installer Et voil, nous avons termin cette installation ! Maintenant, nous pouvons enn dmarrer SQL Server Management Studio ! Au dmarrage, il nous demande de nous connecter notre instance de base de donnes. Par dfaut, linstance sappelle SQLEXPRESS, comme nous lavons vu, et nous pouvons nous y connecter en la prxant par le nom de notre machine ou bien simplement en utilisant le point . , ce qui donne .\SQLEXPRESS. Conservez lauthentication Windows (voir la gure 37.29). Vous arrivez dans loutil et vous pouvez voir dans lexplorateur dobjets gauche quil ny a pas (encore) de base de donnes (voir la gure 37.30). Nous allons devoir crer notre base de donnes. Faites un clic droit sur le dossier Base de donnes et choisissez Nouvelle base de donnes. Donnez-lui le mme nom que le chier de base de donnes que nous avions prcdemment cr : basecommerce. Validez : la base de donnes est cre ! Nous la voyons apparatre dans lexplorateur dobjets (voir la gure 37.31) et nous voyons galement quil ny a pas de tables dedans. Cliquons maintenant sur Nouvelle requte, une fentre vide souvre o nous allons coller le contenu du chier SQL qui a t gnr par le designer de Visual C# Express. Parlons un peu du contenu de ce script. Remarquons dj que les commentaires sont prxs par --, mais, malins comme nous sommes, nous les aurions reconnus, en plus ils sont en vert ! Ensuite, la ligne suivante permet dindiquer que nous allons nous positionner sur la base basecommerce :
1

USE [ basecommerce ];

446

INSTALLER ET UTILISER LOUTIL DE GESTION DE BDD

Figure 37.29 Fentre de connexion la base de donnes

Figure 37.30 Il ny a aucune base de donnes

447

CHAPITRE 37. ACCDER AUX DONNES AVEC ENTITY FRAMEWORK

Figure 37.31 Il ny a pas de tables dans la base de donnes que nous avons cre Si jamais vous navez pas donn le mme nom la base de donnes, cest ici quil faut le changer. Allons un peu plus bas, nous voyons linstruction :
1 2 3 4 5

CREATE TABLE [ dbo ].[ Rayons ] ( [ ID ] int IDENTITY (1 , 1 ) NOT NULL , [ Nom ] nvarchar ( max ) NOT NULL , [ Description ] nvarchar ( max ) NULL );

qui permet de crer la table contenant les rayons, suivi du mme genre dinstruction qui permet de crer la table contenant les produits. Sans trop nous attarder dessus, nous pouvons voir la syntaxe permettant de crer la table (avec CREATE TABLE) et la syntaxe permettant de crer les champs de la table, ainsi que leurs types. Aprs la cration des tables, et comme lindiquent les commentaires pour les anglophones, la suite est une histoire de cl primaire et de cl trangre. Cl primaire ? Cl trangre ? Cest quoi a ?

Ce sont des notions de base de donnes. Sans trop entrer dans les dtails, je vais vous expliquer rapidement de quoi il sagit. Une cl primaire est une contrainte dunicit qui permet didentier de manire unique un enregistrement dans une table. La cl primaire correspond dans notre cas lidentiant dun rayon ou lidentiant dun produit dans leurs tables respectives. noter quelles ont une proprit complmentaire, savoir un auto-incrment. Cest--dire que cest SQL Server qui va soccuper de numroter automatiquement ces identiants, en les incrmentant chaque insertion. Une cl trangre est une contrainte qui garantit lintgrit rfrentielle entre 448

INSTALLER ET UTILISER LOUTIL DE GESTION DE BDD deux tables. Elle identie une colonne dune autre table. Cela permet de faire des liens smantiques entre les tables. Vous navez pas besoin de savoir exactement ce quil se passe dans ce script SQL. Nous le regardons vite fait pour la culture, mais il faut juste savoir lexcuter an quil nous cre les tables. Ce petit apart termin, retournons dans SQL Server Management Studio et collons-y notre requte. Il ne reste plus qu excuter le script en cliquant sur le bouton Excuter, comme indiqu la gure 37.32.

Figure 37.32 Excuter la requte Comme tout sest bien pass, nous pouvons rafrachir lexplorateur dobjets et constater que les nouvelles tables sont cres (voir la gure 37.33). Maintenant, nous avons besoin de donnes dans ces tables. Il y a plusieurs faons de faire. La premire est dutiliser le designer de SQL Server Management Studio, la seconde serait dutiliser un script SQL, la troisime serait dutiliser du code C#. Regardons la premire solution et faisons un clic droit sur la table produit pour modier les 200 lignes du haut . Nous pouvons ensuite insrer des valeurs, comme lillustre la gure 37.34. Ne le faites pas, car pour vous viter du travail, je lai fait pour vous grce la deuxime mthode, le script SQL :
1 2 3 4 5 6 7 8 9 10 11 12 13

INSERT INTO VALUES INSERT INTO VALUES INSERT INTO VALUES INSERT INTO VALUES INSERT INTO VALUES INSERT INTO VALUES INSERT INTO

[ Produits ] ([ Nom ] ,[ Prix ] ,[ Stock ] ,[ UrlImage ]) ( ' T l HD ' , 299 , 50 , ' tele . jpg ' ) [ Produits ] ([ Nom ] ,[ Prix ] ,[ Stock ] ,[ UrlImage ]) ( ' Console de jeux ' , 150 , 25 , ' console . jpg ' ) [ Produits ] ([ Nom ] ,[ Prix ] ,[ Stock ] ,[ UrlImage ]) ( ' Canap ' , 400 , 10 , ' canape . jpg ' ) [ Produits ] ([ Nom ] ,[ Prix ] ,[ Stock ] ,[ UrlImage ]) ( ' Cuisini re ' , 280 , 20 , ' cuisiniere . jpg ' ) [ Produits ] ([ Nom ] ,[ Prix ] ,[ Stock ] ,[ UrlImage ]) ( ' Bouilloire ' , 19 , 100 , ' bouilloire . jpg ' ) [ Produits ] ([ Nom ] ,[ Prix ] ,[ Stock ] ,[ UrlImage ]) ( ' Lit 2 places ' , 149 , 15 , ' lit . jpg ' ) [ Produits ] ([ Nom ] ,[ Prix ] ,[ Stock ] ,[ UrlImage ])

449

CHAPITRE 37. ACCDER AUX DONNES AVEC ENTITY FRAMEWORK

Figure 37.33 Les tables ont correctement t cres

Figure 37.34 Ajout de donnes par linterface de SQL Server Management Studio

450

INSTALLER ET UTILISER LOUTIL DE GESTION DE BDD


14 15 16 17 18 19 20 21 22

VALUES INSERT INTO VALUES INSERT INTO VALUES INSERT INTO VALUES INSERT INTO VALUES

( ' Pull ' , 39 . 99 , 25 , ' pull . jpg ' ) [ Produits ] ([ Nom ] ,[ Prix ] ,[ Stock ] ,[ UrlImage ]) ( ' T - shirt ' , 19 . 99 , 20 , ' tshirt . jpg ' ) [ Produits ] ([ Nom ] ,[ Prix ] ,[ Stock ] ,[ UrlImage ]) ( ' Pyjama ' , 15 . 15 , 4 , ' pyjama . jpg ' ) [ Produits ] ([ Nom ] ,[ Prix ] ,[ Stock ] ,[ UrlImage ]) ( ' Tablette PC ' , 350 , 44 , ' tablette . jpg ' ) [ Produits ] ([ Nom ] ,[ Prix ] ,[ Stock ] ,[ UrlImage ]) ( ' Smartphone ' , 319 . 99 , 40 , ' smartphone . jpg ' )

Il vous sut dexcuter ce script pour insrer les donnes. Nous nallons pas dtailler la syntaxe de ce script, mais il est quand mme assez facile lire comme a. noter que nous navons pas besoin dindiquer didentiant car il est auto-incrment par SQL Server. Crons maintenant des rayons, avec la mme technique :
1 2 3 4 5 6 7 8 9 10

INSERT INTO [ Rayons ] ([ Nom ] ,[ Description ]) VALUES ( ' Salon ' , ' Tout ce qu ' ' on trouve dans un salon ' ) INSERT INTO [ Rayons ] ([ Nom ] ,[ Description ]) VALUES ( ' Cuisine ' , ' Venez d couvrir l ' ' univers de la cuisine ' ) INSERT INTO [ Rayons ] ([ Nom ] ,[ Description ]) VALUES ( ' Dormir ' , null ) INSERT INTO [ Rayons ] ([ Nom ] ,[ Description ]) VALUES ( ' Hi - Tech ' , ' Les produits hi - tech ... ' ) INSERT INTO [ Rayons ] ([ Nom ] ,[ Description ]) VALUES ( ' V tements ' , null )

Vous pouvez copier ces codes grce au code web suivant : Copier ces codes Code web : 174353
Nous pouvons voir le contenu de ces tables en faisant un clic droit, puis Slectionner les 1000 lignes du haut, comme illustr la gure 37.35.

Figure 37.35 Acher les premires lignes de la table Ce qui permet de voir la table Produits (voir la gure 37.36). 451

CHAPITRE 37. ACCDER AUX DONNES AVEC ENTITY FRAMEWORK

Figure 37.36 Les premires lignes de la table Produits Il ny a plus qu relier les produits et les rayons. Pour cela, il faut relier les identiants entre eux. Par exemple, avec le script suivant jindique que le rayon Salon (identiant 1) contient la tl HD (identiant 1), la console de jeux (identiant 2), le canap (identiant 3), la tablette PC (identiant 10). Jindique galement que le rayon Cuisine (identiant 2) contient la cuisinire (identiant 4), ainsi que la bouilloire (identiant 5). Et ainsi de suite. . . :
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27

INSERT INTO VALUES INSERT INTO VALUES INSERT INTO VALUES INSERT INTO VALUES INSERT INTO VALUES INSERT INTO VALUES INSERT INTO VALUES INSERT INTO VALUES INSERT INTO VALUES INSERT INTO VALUES INSERT INTO VALUES INSERT INTO VALUES INSERT INTO VALUES INSERT INTO

[ RayonProduit ] (1 , 1 ) [ RayonProduit ] (1 , 2 ) [ RayonProduit ] (1 , 3 ) [ RayonProduit ] (1 , 10 ) [ RayonProduit ] (2 , 4 ) [ RayonProduit ] (2 , 5 ) [ RayonProduit ] (3 , 3 ) [ RayonProduit ] (3 , 6 ) [ RayonProduit ] (3 , 9 ) [ RayonProduit ] (4 , 1 ) [ RayonProduit ] (4 , 2 ) [ RayonProduit ] (4 , 10 ) [ RayonProduit ] (4 , 11 ) [ RayonProduit ]

([ Rayons_ID ] ,[ Produits_ID ]) ([ Rayons_ID ] ,[ Produits_ID ]) ([ Rayons_ID ] ,[ Produits_ID ]) ([ Rayons_ID ] ,[ Produits_ID ]) ([ Rayons_ID ] ,[ Produits_ID ]) ([ Rayons_ID ] ,[ Produits_ID ]) ([ Rayons_ID ] ,[ Produits_ID ]) ([ Rayons_ID ] ,[ Produits_ID ]) ([ Rayons_ID ] ,[ Produits_ID ]) ([ Rayons_ID ] ,[ Produits_ID ]) ([ Rayons_ID ] ,[ Produits_ID ]) ([ Rayons_ID ] ,[ Produits_ID ]) ([ Rayons_ID ] ,[ Produits_ID ]) ([ Rayons_ID ] ,[ Produits_ID ])

452

SE CONNECTER LA BASE DE DONNES, LIRE ET CRIRE


28 29 30 31 32

VALUES INSERT INTO VALUES INSERT INTO VALUES

(5 , 7 ) [ RayonProduit ] ([ Rayons_ID ] ,[ Produits_ID ]) (5 , 8 ) [ RayonProduit ] ([ Rayons_ID ] ,[ Produits_ID ]) (5 , 9 )

Vous pouvez copier ce code grce au code web suivant : Copier ce code Code web : 921140
Voil, notre base de donnes est prte. Nous allons pouvoir utiliser cette base et ses donnes dans notre code C# !

Se connecter la base de donnes, lire et crire


Il est temps de se connecter notre base de donnes depuis notre application C#. Pour cela, nous avons besoin de la chane de connexion la base de donnes. Nous avons dj parl de la chane de connexion, elle contient toutes les informations ncessaires pour se connecter la base de donnes ; savoir le nom du serveur, le nom de la base, les identiants de connexion, le type de connexion, etc. Nous en avons galement vu un aperu lorsque nous avons utilis lassistant de gnration de modle sauf que je vous ai indiqu que cette chane de connexion nallait pas tre bonne pour nos besoins. En eet, nous avons besoin quelle pointe vers notre serveur de base de donnes et pas vers le chier temporaire que nous avons cr pour les besoins de lassistant. Cette chane de connexion a toute sa place dans le chier de conguration de lapplication, que nous avons dj tudi. Si vous ne lavez pas dj ajout, il est temps de le faire. Et vous pouvez le remplir avec la conguration suivante :
1 2 3 4

5 6

<? xml version = " 1 . 0 " encoding = " utf - 8 " ? > < configuration > < conn ectionStrings > < add name = " NotreBaseDeDonnees " connectionString = " metadata = res ://*/ ModelCommerce . csdl | res ://*/ ModelCommerce . ssdl | res ://*/ ModelCommerce . msl ; provider = System . Data . SqlClient ; provider connection string = ' data source =.\ SQLEXPRESS ; Initial Catalog = basecommerce ; integrated security = True ' " providerName = " System . Data . EntityClient " / > </ conn ectionStrings > </ configuration >

Nous indiquons ici que notre chane de connexion va tre accessible par le nom NotreBaseDeDonnees. Il y a plein dinformations dans lattribut connectionString, mais ce qui nous intresse surtout, cest dindiquer la source de donnes ( savoir : data source=.\SQLEXPRESS) ce qui va nous permettre dindiquer que notre serveur est accessible cette adresse, puis le nom de la base que nous avons cre (Initial Catalog=basecommerce) et enn dindiquer que nous utilisons lauthentication Windows (integrated security=True). Le reste permet de donner des informations de 453

CHAPITRE 37. ACCDER AUX DONNES AVEC ENTITY FRAMEWORK description du modle. Enn, nous indiquons que nous utilisons les mthodes dEntity Framework pour laccs aux donnes, travers System.Data.EntityClient. Bref, beaucoup de ces informations sont issues de la chane de connexion gnre par Visual C# Express ; nous avons simplement chang le mode de connexion pour quil corresponde nos besoins. Maintenant, nous allons pouvoir accder la chane de connexion avec le ConfigurationManager que nous connaissons bien dsormais :
1

string chaineConnexion = ConfigurationManager . ConnectionStrings [ " No tr eBaseDeDonnees " ]. ConnectionString ;

Vous navez bien sr pas oubli de rfrencer lassembly System.Configuration ! Retournons dans notre designer et cliquons dessus pour observer les proprits du modle. Nous allons modier le nom du conteneur dentits pour y mettre un nom un peu plus parlant, savoir BaseDeDonnees (voir la gure 37.37).

Figure 37.37 Modication du nom du conteneur dentits Cest le point dentre de notre accs aux donnes. Il sagit en fait dune classe qui a t gnre par le designer dEntity Framework. Une classe gnre ? O a ?

Dans lexplorateur de solutions, en dpliant le chier ModelCommerce.edmx. Il sagit dun chier intitul ModelCommerce.Designer.cs qui contient la dnition des classes gnres et toute la logique permettant daccder la base de donnes. Nous pouvons louvrir, mais le code est assez verbeux et nous risquons de nous perdre. Faisons conance Visual C# Express, nous allons utiliser son code gnr les yeux ferms ! Notez quand mme que le code gnr possde des classes prxes par le mot-cl partial. Je vais y revenir plus loin. Vous ne vous en rendez peut-tre pas encore compte, mais loutil dORM Entity Framework nous simplie normment la tche (et je pse mes mots, normment !), 454

SE CONNECTER LA BASE DE DONNES, LIRE ET CRIRE car non seulement il gnre toutes les classes reprsentant les donnes en base (comme la classe Produit ou la classe Rayon) mais il soccupe galement de nous simplier la cration, la lecture ou la modication des donnes en base. Si nous avions d le faire la main comme ctait le cas avant lutilisation dORMs, cela aurait mrit une centaine de pages supplmentaires dexplications et de code comprendre. L, nous nous positionnons en tant quutilisateur de ces classes gnres et vous allez voir que cest facile utiliser ; vous nimaginez pas le plaisir que cest de constater que lORM a travaill pour nous ! Nous pouvons dsormais instancier la classe BaseDeDonnees gnre en lui passant en paramtre la chane de connexion :
1 2

string chaineConnexion = ConfigurationManager . ConnectionStrings [ " No tr eBa seDeDonnees " ]. ConnectionString ; BaseDeDonnees baseDeDonnees = new BaseDeDonnees ( chaineConnexion );

Vous voil connects la base de donnes. Nous allons pouvoir utiliser les objets que Visual C# Express a gnrs travers cette variable de type BaseDeDonnees, comme par exemple la proprit Rayons qui nous permet daccder aux rayons de notre base :
1 2 3 4

foreach ( Rayon rayon in baseDeDonnees . Rayons ) { Console . WriteLine ( " { 0 } ({ 1 }) " , rayon . Nom , rayon . Description ); }

Ici, nous pouvons parcourir la liste des rayons avec un foreach car la proprit Rayons est du type ObjectSet<> qui implmente IEnumerable<>. Ce qui donne :
Salon ( Tout ce qu on trouve dans un salon ) Cuisine ( Venez d couvrir l univers de la cuisine ) Dormir () Hi - Tech ( Les produits hi - tech ...) V tements ()

De mme, nous pouvons parcourir tous les produits grce la proprit Produits :
1 2 3 4

foreach ( Produit produit in baseDeDonnees . Produits ) { Console . WriteLine ( " { 0 } : { 1 } " , produit . Nom , produit . Prix ) ; }

Ce qui donne :
T l HD : 299 Console de jeux : 150 Canap : 400 Cuisini re : 280 Bouilloire : 19

455

CHAPITRE 37. ACCDER AUX DONNES AVEC ENTITY FRAMEWORK


Lit 2 places : 149 Pull : 40 T - shirt : 20 Pyjama : 15 Tablette PC : 350 Smartphone : 320

Et ce qui est formidable, cest qutant donn que la proprit Produits est numrable, nous allons pouvoir y faire toutes les requtes LINQ que nous le souhaitons, par exemple :
1 2 3 4 5 6 7 8

IEnumerable < Produit > produits = from produit in baseDeDonnees . Produits where produit . Prix > 150 orderby produit . Prix descending select produit ; foreach ( Produit produit in produits ) { Console . WriteLine ( " { 0 } : { 1 } " , produit . Nom , produit . Prix ) ; }

Nous obtenons tous les produits dont le prix est suprieur 150, tris par prix dcroissant :
Canap : 400 Tablette PC : 350 Smartphone : 320 T l HD : 299 Cuisini re : 280

Pratique ! Tout le SQL ncessaire pour renvoyer cette liste de produits ltre a t gnr par Entity Framework. Nous navons rien faire dautre que dutiliser le C#. Et voil. Avouez que cest quand mme super simple, non ? Avouez galement que, si vous avez lhabitude de tout faire la main dans un autre langage de programmation, vous tes merveills ! Jexagre peut-tre un peu, mais Entity Framework nous fait gagner un temps considrable au dveloppement ainsi que tout au long de la vie de lapplication. Merci lui de nous avoir gnr tout le code adquat. propos de gnration de code, souvenez-vous que les classes gnres sont partielles. Nous en avons dj parl dans le chapitre ddi, mais je vous rappelle le but ici. Il sagit de permettre dajouter des fonctionnalits la classe sans avoir modier le chier ModelCommerce.Designer.cs. En eet, chaque fois que nous faisons une modication sur notre modle (ajout dentit, changement de nom, etc.), il rgnre toutes les classes de ce chier. Si nous avions modi des choses la main dedans, elles vont disparatre. . . Le mot-cl partial nous ore lopportunit dajouter des fonctionnalits la classe depuis un autre chier. Nous pouvons en proter pour rajouter nos propres mthodes, par exemple une mthode qui renvoie les produits dont le prix est suprieur un prix pass en paramtre. Il sut de 456

SE CONNECTER LA BASE DE DONNES, LIRE ET CRIRE dclarer une classe partielle du mme nom que la classe BaseDeDonnees, situe dans le mme espace de noms et de rajouter des mthodes. Par exemple :
1 2 3 4 5 6 7 8 9

public partial class BaseDeDonnees { public IEnumerable < Produit > ProduitsPlusCherQue ( decimal prix ) { return from produit in Produits where produit . Prix > prix select produit ; } }

Nous pourrons donc utiliser cette mthode de cette faon :


1 2 3 4

foreach ( Produit produit in baseDeDonnees . ProduitsPlusCherQue ( 200 ) ) { Console . WriteLine ( " { 0 } : { 1 } " , produit . Nom , produit . Prix ) ; }

Ce qui donnera :
T l HD : 299 Canap : 400 Cuisini re : 280 Tablette PC : 350 Smartphone : 320

Remarquons que chaque Rayon possde galement une proprit Produits ; cest la proprit de navigation que nous avons renomme prcdemment. Entity Framework a donc compris quil y avait une relation entre les rayons et les produits et il permet daccder aux produits qui font partie du rayon, grce cette proprit. Ainsi, nous pouvons crire un code, comme le suivant, qui accde la proprit Produits dun rayon et permet dacher la liste de tous les produits de chaque rayon :
1 2 3 4 5 6 7 8

foreach ( Rayon rayon in baseDeDonnees . Rayons ) { Console . WriteLine ( " { 0 } ({ 1 }) " , rayon . Nom , rayon . Description ); foreach ( Produit produit in rayon . Produits ) { Console . WriteLine ( " \ t { 0 } : { 1 } " , produit . Nom , produit . Prix ) ; } }

Sauf quici nous rencontrons un problme. Si nous excutons ce bout de code, nous aurons lexception suivante : 457

CHAPITRE 37. ACCDER AUX DONNES AVEC ENTITY FRAMEWORK

Exception non g r e : System . Data . E n t i t y C o m m a n d E x e c u t i o n E x c e p t i o n : Une erreur s est produite lors de l ex cution de la d finition de la commande . Pour plus de d tails , consultez l exception interne . ---> System . I n v a l i d O p e r a t i o n Ex c e p t i o n : Un DataReader associ cette Command est d j ouvert . Il doit d abord tre ferm . [...]

Pourquoi un tel problme ?

En fait, cela vient de la faon dont sont rcupres les donnes. Lorsque nous accdons la proprit Rayons, Entity Framework gnre une requte en base de donnes pour rcuprer la liste des rayons. Puis lintrieur de la boucle, lorsque nous accdons la proprit Produits dun rayon, il gnre nouveau une requte pour rcuprer les produits de ce rayon. Il y a donc deux connexions la base de donnes en mme temps, et a, il ne sait pas le faire par dfaut. Il y a plusieurs faons de corriger le problme. La premire est de changer la chane de connexion en rajoutant une directive permettant de prciser quon autorise laccs multiple, savoir :
1

m u l t i p l e a c ti v e re s u lt s e ts = True ;

La chane de connexion devient donc :


1

< add name = " NotreBaseDeDonnees " connectionString = " metadata = res ://*/ ModelCommerce . csdl | res ://*/ ModelCommerce . ssdl | res ://*/ ModelCommerce . msl ; provider = System . Data . SqlClient ; provider connection string = ' data source =.\ SQLEXPRESS ; Initial Catalog = basecommerce ; integrated security = True ; m u l t i p l e a ct i v er e s u lt s e ts = True ; ' " providerName = " System . Data . EntityClient " / >

Avec ce changement, si nous excutons ce code, nous aurons :


Salon ( Tout ce qu on trouve dans un salon ) T l HD : 299 Console de jeux : 150 Canap : 400 Tablette PC : 350 Cuisine ( Venez d couvrir l univers de la cuisine ) Cuisini re : 280 Bouilloire : 19 Dormir () Canap : 400 Lit 2 places : 149 Pyjama : 15

458

SE CONNECTER LA BASE DE DONNES, LIRE ET CRIRE


Hi - Tech ( Les produits hi - tech ...) T l HD : 299 Console de jeux : 150 Tablette PC : 350 Smartphone : 320 V tements () Pull : 40 T - shirt : 20 Pyjama : 15

La deuxime solution est de faire en sorte que la premire requte soit termine avant lexcution des suivantes. Pour cela, il sut de forcer lvaluation de la requte en utilisant par exemple un ToList() :
1 2 3 4 5 6 7 8

foreach ( Rayon rayon in baseDeDonnees . Rayons . ToList () ) { Console . WriteLine ( " { 0 } ({ 1 }) " , rayon . Nom , rayon . Description ); foreach ( Produit produit in rayon . Produits ) { Console . WriteLine ( " \ t { 0 } : { 1 } " , produit . Nom , produit . Prix ) ; } }

Ceci est possible, car Entity Framework bncie de lexcution dire ; le ToList() rsout le problme en forant lexcution de la requte. Enn, la dernire solution est de faire en sorte que la premire requte qui charge les Rayons inclue galement le chargement des produits. Ainsi, il ny a quune seule et unique requte qui charge tout. Cela se passe avec la mthode Include, en prcisant le nom de la proprit de navigation charger :
1 2 3 4 5 6 7 8

foreach ( Rayon rayon in baseDeDonnees . Rayons . Include ( " Produits " )) { Console . WriteLine ( " { 0 } ({ 1 }) " , rayon . Nom , rayon . Description ); foreach ( Produit produit in rayon . Produits ) { Console . WriteLine ( " \ t { 0 } : { 1 } " , produit . Nom , produit . Prix ) ; } }

Il y a plusieurs choses remarquer ici. Nous avons vu quEntity Framework est capable daller chercher les donnes lies entre elles grce aux proprits de navigation. Les deux premiers scnarios couverts tirent parti de ce quon appelle le lazy loading que lon peut traduire en chargement paresseux . Cela veut dire que cest uniquement lorsque lon accde la proprit Produits 459

CHAPITRE 37. ACCDER AUX DONNES AVEC ENTITY FRAMEWORK quEntity Framework va aller lire le contenu associ en base de donnes. Ceci implique qu chaque tentative daccs la proprit Produits dun rayon, Entity Framework va eectuer une requte en base de donnes pour ramener les produits concerns. Cest trs bien si on fait a une ou deux fois, mais dans notre cas au nal on fait autant de requte quil y a de rayons. Ce qui nest pas trs performant. . . Le troisime scnario montre lutilisation de la mthode Include qui permet de tout rapatrier en une seule requte, ce qui est videmment plus performant. Faites bien attention votre utilisation dEntity Framework. Si ce nest pas trs grave pour une petite application, cela peut le devenir pour des applications avec des grosses bases de donnes. Alors, vous ne trouvez pas que la lecture en base de donnes est particulirement aise ? Merci Entity Framework ! Notons que nous pouvons galement accder aux rayons dans lesquels sont positionns les produits grce la proprit Rayons. Nous pourrions ventuellement nous en servir pour acher le nombre de rayons dans lesquels le produit est prsent. Lcriture en base de donnes est tout aussi aise. Le principe est dajouter des valeurs notre objet de base de donnes et de sauvegarder les modications. Pour ajouter un nouveau rayon, il sut dappeler la mthode AddObject disponible sur la proprit Rayons. Il ne faudra pas oublier dappeler la mthode SaveChanges qui soccupe dinsrer physiquement les valeurs en base de donnes. Pour crer un nouveau rayon, il sura dinstancier un objet Rayon, de renseigner des proprits, de crer des produits et de les ajouter au rayon, par exemple :
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21

Rayon rayon = new Rayon () ; rayon . Nom = " Vins " ; rayon . Description = " Venez d couvrir notre s lection des plus grands ch teaux " ; Produit produit1 = new Produit () ; produit1 . Nom = " Ch teau ronto " ; produit1 . Prix = 9 . 99M ; produit1 . Stock = 60 ; produit1 . UrlImage = " compl ter ... " ; Produit produit2 = new Produit () ; produit2 . Nom = " Ch teau toro " ; produit2 . Prix = 15 ; produit2 . Stock = 6 ; produit2 . UrlImage = " compl ter ... " ; rayon . Produits . Add ( produit1 ) ; rayon . Produits . Add ( produit2 ) ; baseDeDonnees . Rayons . AddObject ( rayon ) ; baseDeDonnees . SaveChanges () ;

460

SE CONNECTER LA BASE DE DONNES, LIRE ET CRIRE Ainsi, si nous rachons la liste des rayons, nous pourrons voir un rayon de plus contenant des produits en plus. . .
Salon ( Tout ce qu on trouve dans un salon ) T l HD : 299 Console de jeux : 150 Canap : 400 Tablette PC : 350 Cuisine ( Venez d couvrir l univers de la cuisine ) Cuisini re : 280 Bouilloire : 19 Dormir () Canap : 400 Lit 2 places : 149 Pyjama : 15 Hi - Tech ( Les produits hi - tech ...) T l HD : 299 Console de jeux : 150 Tablette PC : 350 Smartphone : 320 V tements () Pull : 40 T - shirt : 20 Pyjama : 15 Vins ( Venez d couvrir notre s lection des plus grands ch teaux ) Ch teau ronto : 10 Ch teau toro : 15

De mme, si vous allez voir en base de donnes, vous aurez bien les nouveaux lments, ainsi que lillustre la gure 37.38. On observe la cration dun nouveau rayon, de deux nouveaux produits et nous avons bien dans la table de relation les nouveaux produits relis au nouveau rayon. Il est galement possible dajouter un produit un rayon. Nous pouvons le faire de deux manires direntes. La premire est dajouter un Produit directement dans la collection Produits dun rayon, Il sera directement ajout dans le rayon de notre choix. La deuxime est dajouter un produit dans la collection Produits et si nous voulons quil soit prsent dans un rayon, il faudra que sa proprit Rayons contienne les rayons dans lesquels nous souhaitons ajouter le produit. Voyons la premire mthode :
1 2 3 4 5 6 7 8 9 10

Rayon rayon = baseDeDonnees . Rayons . First ( r = > r . Nom == " Vins " ) ; Produit produit = new Produit () ; produit . Nom = " Chateau pini re " ; produit . Prix = 12 . 50M ; produit . Stock = 40 ; produit . UrlImage = " vin . jpg " ; produit . Rayons . Add ( rayon ) ; baseDeDonnees . SaveChanges () ;

461

CHAPITRE 37. ACCDER AUX DONNES AVEC ENTITY FRAMEWORK

Figure 37.38 Le nouveau rayon est les nouveaux produits sont visibles dans les tables Nous commenons par rcuprer un rayon, puis nous instancions un objet de type Produit. Enn, nous faisons le lien entre le produit et le rayon en ajoutant le rayon la collection Rayons de notre objet produit. Comme dhabitude, la mthode SaveChanges() permet de faire persister les informations. La seconde mthode est un peu plus simple apprhender ; il sut dinstancier un objet Produit et de lajouter la collection Produits dun rayon :
1 2 3 4 5 6 7 8 9 10 11

Rayon rayon = baseDeDonnees . Rayons . First ( r = > r . Nom == " Vins " ) ; Produit produit = new Produit () ; produit . Nom = " Chateau r ro " ; produit . Prix = 3 . 20M ; produit . Stock = 10 ; produit . UrlImage = " vin1 . jpg " ; rayon . Produits . Add ( produit ) ; baseDeDonnees . SaveChanges () ;

Dans les deux cas, Entity Framework arrive faire le lien entre un rayon et un produit. Le chteau pinire et le chteau rro ont bien t ajouts. . . ! Vous pouvez galement vrier en base de donnes que la relation entre le rayon et le produit a bien t faite. Il est galement possible de modier des enregistrements en base de donnes. Le principe est de modier llment concern dans lobjet de base de donnes et dappeler la 462

SE CONNECTER LA BASE DE DONNES, LIRE ET CRIRE mthode SaveChanges() :


1 2 3

Rayon rayon = baseDeDonnees . Rayons . First ( r = > r . Nom == " Vins " ) ; rayon . Description = " Les meilleurs vins " ; baseDeDonnees . SaveChanges () ;

Enn, nous pouvons aussi supprimer des donnes en base. Le principe est le mme que pour lajout. Nous appelons une mthode qui soccupe de la suppression et nous appelons la mthode SaveChanges. Par contre, il faut faire attention lintgrit des donnes. On ne peut pas supprimer un rayon qui a des produits dedans. Il faut commencer par retirer la relation entre les produits et le rayon :
1 2 3 4

Rayon rayon = baseDeDonnees . Rayons . First ( r = > r . Nom == " Vins " ) ; rayon . Produits . Clear () ; baseDeDonnees . DeleteObject ( rayon ) ; baseDeDonnees . SaveChanges () ;

Le fait dappeler la mthode Clear() sur les produits du rayon vide le rayon de ses produits. Il ny a donc plus de produits dans ce rayon, mais ils existent toujours en base de donnes car ils nont pas t supprims physiquement. Cest important car ces produits peuvent galement tre prsents dans dautres rayons, nous ne pouvons donc pas les supprimer. Ensuite, on utilise la mthode DeleteObject pour supprimer un lment de la base de donnes. Ici, nous supprimons le rayon et nous validons les modications avec SaveChanges(). Notez que si nous navions pas vid le rayon de ses produits, la suppression du rayon aurait t impossible car comme il existe une relation entre les produits et les rayons et que notre base de donnes possde une contrainte dintgrit (la cl trangre) entre les produits et le rayon, la suppression aurait provoqu une erreur. En eet, la table RayonProduit contiendrait un identiant de rayon qui nexiste plus. Cest impossible ! La contrainte de la cl trangre est l pour nous assurer que nous ne faisons pas nimporte quoi dans la base de donnes et quelle est toujours cohrente. Si nous lavions fait, nous aurions eu lexception suivante :
Exception non g r e : System . Data . UpdateException : Une erreur s est produite lors de la mise jour des entr es . Pour plus d informations , consultez l exception interne . ---> System . Data . SqlClient . SqlException : L instruction DELETE est en conflit avec la contrainte REFERENCE " FK \ _RayonProduit \ _Rayon ". Le conflit s est produit dans la base de donn es " basecommerce " , table " dbo . RayonProduit " , column Rayons \ _ID . L instruction a t arr t e . [...]

Remarquez que nous aurons dsormais des produits orphelins. Tout dpend de ce que lon veut faire maintenant. Souhaitons-nous quils soient supprims galement vu quils ne sont plus dans aucun rayon ? Souhaitons-nous quils restent prsents pour pouvoir les ajouter ultrieurement un autre rayon ? a, cest vous qui dcidez ! Maintenant que vous savez supprimer des objets, vous pouvez faire comme bon vous semble. Notons avant de terminer quil est tout fait possible de faire plusieurs ajouts, modications ou suppressions en mme temps. Il sura de terminer toutes les instructions 463

CHAPITRE 37. ACCDER AUX DONNES AVEC ENTITY FRAMEWORK par la mthode SaveChanges() qui sarrangera pour tout faire persister !

En rsum
Entity Framework est loutil de mapping objet relationnel de Microsoft permettant de travailler sur une base de donnes relationnelle avec une approche oriente objet. Entity Framework est capable de modliser des donnes et de gnrer les tables correspondantes en base de donnes sans quil soit ncessaire de matriser le SQL. Il simplie grandement la lecture et lcriture des donnes en base et tire parti, si besoin, des mcanismes de chargement paresseux.

464

Chapitre

38
Dicult :

Les tests unitaires

ne des grandes proccupations des crateurs de logiciels est dtre certains que leur application informatique fonctionne et surtout quelle fonctionne dans toutes les situations possibles. Nous avons tous dj vu notre systme dexploitation planter, ou bien notre logiciel de traitement de texte nous faire perdre les 50 pages de rapport que nous tions en train de taper. Ou encore, un lment inattendu dans un jeu o lon arrive passer travers un mur alors quon ne devrait pas. . . Bref, pour tre sr que son application fonctionne, il faut faire des tests.

465

CHAPITRE 38. LES TESTS UNITAIRES

Quest-ce quun test unitaire et pourquoi en faire ?


Un test constitue une faon de vrier quun systme informatique fonctionne. Tester son application cest bien. Il faut absolument le faire. Cest en gnral une pratique plutt laisse de ct, car rbarbative. Il y a plusieurs faons de faire des tests. Celle qui semble la plus naturelle est celle qui se fait manuellement. On lance son application, on clique partout, on regarde si elle fonctionne. Celle que je vais prsenter ici constitue une pratique automatise visant sassurer que des bouts de code fonctionnent comme il faut et que tous les scnarios dun dveloppement sont couverts par un test. Lorsque les tests couvrent tous les scnarios dun code, nous pouvons assurer que notre code fonctionne. De plus, cela permet de faire des oprations de maintenance sur le code tout en tant certain quil naura pas subi de rgressions. De la mme faon, les tests sont un let de scurit lorsquon souhaite refactoriser son code ou loptimiser. Cela permet dans certains cas davoir un guide pendant le dveloppement, notamment lorsquon pratique le TDD. Le Test Driven Development (TDD) 1 est une mthode de dveloppement de logiciel qui prconise dcrire les tests unitaires avant dcrire le code source dun logiciel. Nous y reviendrons ultrieurement. Un test est donc un bout de code qui permet de tester un autre code.

En gnral, un test se dcompose en trois parties, suivant le schma AAA , qui correspond aux mots anglais Arrange, Act, Assert, que lon peut traduire en franais par : Arranger, Agir, Auditer. Arranger : il sagit dans un premier temps de dnir les objets, les variables ncessaires au bon fonctionnement de son test (initialiser les variables, initialiser les objets passer en paramtres de la mthode tester, etc.). Agir : ensuite, il sagit dexcuter laction que lon souhaite tester (en gnral, excuter la mthode que lon veut tester, etc.). Auditer : enn, il faut vrier que le rsultat obtenu est conforme nos attentes.

Notre premier test


Imaginons que nous voulions tester une mthode toute simple qui fait laddition entre deux nombres, par exemple la mthode suivante :
1 2 3 4

public static int Addition ( int a , int b ) { return a + b ; }

Faire un test consiste crire des bouts de code permettant de sassurer que le code fonctionne. Cela peut-tre par exemple :
1. En franais, le TDD se dit dveloppement pilot par les tests .

466

NOTRE PREMIER TEST


1 2 3 4 5 6 7 8 9 10 11

static void Main ( string [] args ) { // arranger int a = 1 ; int b = 2 ; // agir int resultat = Addition (a , b ) ; // auditer if ( resultat != 3 ) Console . WriteLine ( " Le test a rat " ) ; }

Ici, le test passe bien, ouf ! Pour tre complet, le test doit couvrir un maximum de situations ; il faut donc tester notre code avec dautres valeurs, et ne pas oublier les valeurs limites :
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18

static void Main ( string [] args ) { int a = 1 ; int b = 2 ; int resultat = Addition (a , b ) ; if ( resultat != 3 ) Console . WriteLine ( " Le test a rat " ) ; a = 0; b = 0; resultat = Addition (a , b ) ; if ( resultat != 0 ) Console . WriteLine ( " Le test a rat " ) ; a = -5 ; b = 5; resultat = Addition (a , b ) ; if ( resultat != 0 ) Console . WriteLine ( " Le test a rat " ) ; }

Voil pour le principe. Ici, nous considrons avoir crit susamment de tests pour nous assurer que cette mthode est bien fonctionnelle. Bien sr, cette mthode tait par dnition fonctionnelle, mais il est important de prendre le rexe de tester des fonctionnalits qui sont dterminantes pour notre application. Voyons maintenant comment nous pourrions tester une mthode avec lapproche TDD. Pour rappel, lors dune approche TDD, le but est de pouvoir faire un dveloppement partir des cas de tests pralablement tablis par la personne qui exprime le besoin ou suivant les spcications fonctionnelles. Imaginons que nous voulions tester une mthode qui calcule la factorielle dun nombre. Nous savons que la factorielle de 0 vaut 1, la factorielle de 1 vaut 1. Commenons par crire les tests :
1 2 3

static void Main ( string [] args ) { int valeur = 0 ;

467

CHAPITRE 38. LES TESTS UNITAIRES


4 5 6 7 8 9 10 11 12

int resultat = Factorielle ( valeur ) ; if ( resultat != 1 ) Console . WriteLine ( " Le test a rat " ) ; valeur = 1 ; resultat = Factorielle ( valeur ) ; if ( resultat != 1 ) Console . WriteLine ( " Le test a rat " ) ;

Le code ne compile pas ! Forcment, nous navons pas encore cr la mthode Factorielle. Cest la premire tape. La suite de la mthode est de faire en sorte que le test compile, mais il chouera puisque la mthode nest pas encore implmente :
1 2 3 4

public static int Factorielle ( int a ) { throw new N o tI mp le m en te d Ex ce pt i on () ; }

Il faudra ensuite crire le code minimal qui servira faire passer nos deux tests. Cela peut-tre :
1 2 3 4

public static int Factorielle ( int a ) { return 1 ; }

Si nous excutons nos tests, nous voyons que cette mthode est fonctionnelle car ils passent tous. La suite de la mthode consiste refactoriser le code, loptimiser. Ici, il ny a rien faire, car cest vraiment simple. On se rend compte par contre quon na pas couvert normment de cas de tests ; faire juste des tests avec 0 et 1 cest un peu lger. . . Nous savons que la factorielle de 2 vaut 2, la factorielle de 3 vaut 6, la factorielle de 4 vaut 24, etc. Continuons crire des tests (il faut bien sr garder les anciens tests an dtre srs quon couvre un maximum de cas) :
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16

static void Main ( string [] args ) { int valeur = 0 ; int resultat = Factorielle ( valeur ) ; if ( resultat != 1 ) Console . WriteLine ( " Le test a rat " ) ; valeur = 1 ; resultat = Factorielle ( valeur ) ; if ( resultat != 1 ) Console . WriteLine ( " Le test a rat " ) ; valeur = 2 ; resultat = Factorielle ( valeur ) ; if ( resultat != 2 ) Console . WriteLine ( " Le test a rat " ) ;

468

NOTRE PREMIER TEST


17 18 19 20 21 22 23 24 25 26 27

valeur = 3 ; resultat = Factorielle ( valeur ) ; if ( resultat != 6 ) Console . WriteLine ( " Le test a rat " ) ; valeur = 4 ; resultat = Factorielle ( valeur ) ; if ( resultat != 24 ) Console . WriteLine ( " Le test a rat " ) ;

Et nous pouvons crire une mthode Factorielle qui fait passer ces tests :
1 2 3 4 5 6 7 8 9 10

public static int Factorielle ( int a ) { if ( a == 2 ) return 2 ; if ( a == 3 ) return 6 ; if ( a == 4 ) return 24 ; return 1 ; }

Lanons les tests : nous voyons que tout est Ok. Cependant, nous nallons pas faire des if en dclinant tous les cas possibles, il faut donc repasser par ltape damlioration et de refactorisation du code, an dviter les redondances de code, damliorer les algorithmes, etc. Cette opration devient sans risque puisque le test est l pour nous assurer que la modication que lon vient de faire est sans rgression, si le test passe toujours bien sr. . . Nous voyons que nous pouvons amliorer le code en utilisant la vraie formule de la factorielle :
1 2 3 4 5 6 7 8 9

public static int Factorielle ( int a ) { int total = 1 ; for ( int i = 1 ; i <= a ; i ++) { total *= i ; } return total ; }

Ce qui permet dillustrer que, par exemple, la factorielle de 5 est gale 1 2 3 4 5. Relanons nos tests, ils passent tous. Parfait. Nous sommes donc certains que notre changement de code na pas altr la fonctionnalit, car les tests continuent de passer. On peut mme rajouter des tests pour le plaisir, comme la factorielle de 10, histoire davoir quelque chose dun peu plus grand :
1 2

valeur = 10 ; resultat = Factorielle ( valeur ) ;

469

CHAPITRE 38. LES TESTS UNITAIRES


3 4

if ( resultat != 3628800 ) Console . WriteLine ( " Le test a rat " ) ;

Est-ce que cette mthode est optimisable ? Srement. Est-ce quil y a un risque optimiser cette mthode ? Aucun ! En eet, nos tests nous garantissent que sils continuent passer, alors une optimisation nentrane pas de rgression dans la fonctionnalit. On sait par exemple quil y a un autre moyen pour calculer une factorielle. Par exemple, pour calculer la factorielle de 5, il sut de multiplier 5 par la factorielle de 4. Pour calculer la factorielle de 4, il faut multiplier 4 par la factorielle de 3, et ainsi de suite jusqu arriver 1. . . Bref, pour obtenir une factorielle on peut se servir du rsultat de la factorielle du nombre prcdent. Ce qui peut scrire :
1 2 3 4 5 6

public static int Factorielle ( int a ) { if ( a <= 1 ) return 1 ; return a * Factorielle ( a - 1 ) ; }

Ici la mthode Factorielle est une mthode rcursive, cest--dire quelle sappelle elle-mme. Cela nous permet dindiquer que la factorielle dun nombre correspond ce nombre multipli par la factorielle du nombre prcdent. Bien sr, il faut sarrter un moment dans la rcursion. On sarrte ici quand on atteint le chire 1. Pour sassurer que cette factorielle fonctionne bien, il sut de relancer les tests. Tout est Ok, cest parfait ! Voil donc un exemple de TDD. Bien sr, la mthode est ici pousse au maximum pour que vous compreniez lintrt de cette pratique. On peut gagner du temps en partant directement sur la bonne implmentation. Vous verrez quil y a toujours des premiers essais qui satisfont les tests mais quil sera possible damliorer rgulirement le code. Ceci devient possible grce aux tests qui nous assurent que tout continue bien fonctionner. La pratique du TDD dpend de la faon dont le dveloppeur apprhende sa philosophie de dveloppement. Elle est prsente ici pour sensibiliser ce dernier cette pratique mais son utilisation nest pas du tout obligatoire. Voil pour les tests basiques. Cependant, utiliser une application console pour faire ses tests, ce nest pas trs pratique, vous en conviendrez. Nous avons besoin doutils !

Le framework de test
Un framework de test est aux tests ce quun IDE est au dveloppement. Il fournit un environnement structur permettant lexcution de tests, ainsi que des mthodes pour aider au dveloppement de ceux-ci. Il existe plusieurs frameworks de test. Microsoft dispose de son framework, mstest, qui est disponible dans les versions payantes de Visual Studio. Son intrt est quil est fortement intgr lIDE. Son dfaut est quil ne fonctionne pas avec les versions gratuites de lenvironnement de dveloppement. Comme nous sommes partis dans ce livre avec la version gratuite, Visual C# Express, nous nallons pas pouvoir utiliser mstest. 470

LE FRAMEWORK DE TEST Par contre, il existe dautres framework de test, gratuits, comme le trs connu NUnit. NUnit est la version .NET du framework XUnit, qui se dcline pour plusieurs environnements, avec par exemple PHPUnit pour le langage PHP, JUnit, pour java, etc. Premire chose faire : installer NUnit. Pour cela, utilisez le code web suivant : Tlcharger NUnit Code web : 481629

La version que jutilise dans ce livre est la version 2.5.10. Dmarrez linstallation, comme indiqu la gure 38.1.

Figure 38.1 Installation de NUnit Linstallation est en anglais, mais assez facile suivre. Cliquez sur Next puis, aprs avoir accept la licence, vous pouvez choisir linstallation classique (voir la gure 38.2). Une fois le framework de test install, nous pouvons crer un nouveau projet qui contiendra une fonctionnalit tester. Je lappelle MaBibliothequeATester. En gnral, nous allons surtout tester des assemblys avec NUnit. Je cre donc un projet de type bibliothque de classes. Ce projet ne sera pas excutable, car il ne sagit pas dune application console. lintrieur, je vais pouvoir crer une classe utilitaire, disons Math, qui contiendra notre mthode de calcul de factorielle :
1 2 3 4 5 6 7 8 9

public static class Math { public static int Factorielle ( int a ) { if ( a <= 1 ) return 1 ; return a * Factorielle ( a - 1 ) ; } }

471

CHAPITRE 38. LES TESTS UNITAIRES

Figure 38.2 Choix de linstallation classique Ensuite, ajoutons un nouveau projet de type bibliothque de classes o nous allons mettre nos tests unitaires, appelons-le MathTests.Unit. Ce nest pas une norme absolue, mais je vous conseille de suxer vos projets de test avec .Unit, an de les identier facilement. Les tests doivent se mettre dans une classe spciale. Ici aussi, pas de rgle de nommage obligatoire, mais il est intressant davoir une norme pour sy retrouver facilement. Je vous propose de nommer les classes de tests en commenant par le nom de la classe que lon doit tester, suivie du mot Tests. Ce qui donne : MathTests. Pour tre reconnue par le framework de test, la classe doit respecter un certain nombre de contraintes. Elle doit dans un premier temps tre dcore de lattribut [TestFixture]. Il sagit dun attribut qui permet NUnit de reconnatre les classes qui contiennent des tests. Cet attribut tant dans une assembly de NUnit, vous devez rajouter une rfrence lassembly nunit.framework, ainsi quillustr la gure 38.3. Vous devez ensuite inclure lespace de noms adquat :
1

using NUnit . Framework ;

Nous allons pouvoir crer des mthodes lintrieur de cette classe. De la mme faon, une mthode pourra tre reconnue comme une mthode de test si elle est dcore de lattribut [Test]. Ici aussi, il est intressant de suivre une rgle de nommage an de pouvoir identier rapidement lintention de la mthode de test. Je vous propose le nommage suivant : MethodeTestee_EtatInitial_EtatAttendu(). Par exemple, une mthode de test permettant de tester la factorielle pourrait sappeler :
1 2 3 4

[ TestFixture ] public class MathTests { [ Test ]

472

LE FRAMEWORK DE TEST

Figure 38.3 Ajout dune rfrence au framework de test


5 6 7 8 9

public void F a c t o r i e l l e _ A v e c V a l e u r 3 _ R e t o u r n e 6 () { // test faire }

Il existe plein dautres attributs que vous dcouvrirez ultrieurement. Il est temps de passer lcriture du test et surtout la vrication du rsultat. Pour cela, on utilise des mthodes de NUnit qui nous permettent de vrier par exemple quune valeur est gale une autre attendue. Cela se fait grce la mthode Assert.AreEqual() :
1 2 3 4 5 6 7

[ Test ] public void F a c t o r i e l l e _ A v e c V a l e u r 3 _ R e t o u r n e 6 () { int valeur = 3 ; int resultat = M aBibl iotheq ueATe ster . Math . Factorielle ( valeur ) ; Assert . AreEqual (6 , resultat ) ; }

Elle permet de vrier que la variable valeur vaut bien 6. Rajoutons tant quon y est une mthode de test qui choue :
1 2 3 4 5

[ Test ] public void F a c t o r i e l l e _ A v e c V a l e u r 1 0 _ R e t o u r n e 1 () { int valeur = 10 ; int resultat = M aBibl iotheq ueATe ster . Math . Factorielle ( valeur ) ;

473

CHAPITRE 38. LES TESTS UNITAIRES


6 7

Assert . AreEqual (1 , resultat , " La valeur doit tre gale 1 ");

Il est important que chaque mthode qui soccupe de tester une fonctionnalit, le fasse laide dun cas unique comme illustr juste au-dessus. La premire mthode teste la fonctionnalit Factorielle pour le cas o la valeur vaut 3 et la seconde soccupe du cas o la valeur vaut 10. Vous pouvez rajouter autant de mthodes de tests que vous le souhaitez tant quelles sont dcores de lattribut [Test]. Jen ai prot pour ajouter un message qui permettra dindiquer des informations complmentaires si le test choue. Compilez maintenant le projet, allez dans le rpertoire dinstallation de NUnit (C:\Program Files\NUnit 2.5.10\bin\net-2.0) et lancez lapplication nunit.exe (voir gure 38.4).

Figure 38.4 Interface de NUnit La premire chose faire est de crer un nouveau projet (voir la gure 38.5). Appelez-le ProjetTest par exemple. Il faut ensuite ajouter une assembly de test ; allez dans Project > Add Assembly comme indiqu la gure 38.6. Pour nir, allez pointer lassembly de tests, savoir MathTests.Unit.dll. NUnit analyse lassembly et fait apparatre la liste des tests qui composent notre assembly, en se basant sur les attributs TestFixture et Test (voir la gure 38.7). Nous pouvons prsent lancer les tests en cliquant sur Run ! On saperoit rapidement, en observant la gure 38.8, quil y a un test qui passe (icne verte) et un test qui choue (icne rouge). Forcment, notre test ntait pas bon, il faut le rcrire. Nous voyons galement quil 474

LE FRAMEWORK DE TEST

Figure 38.5 Cration dun nouveau projet NUnit

Figure 38.6 Ajout de lassembly contenant les tests

475

CHAPITRE 38. LES TESTS UNITAIRES

Figure 38.7 Les tests prsents dans lassembly

Figure 38.8 Premire excution des tests

476

LE FRAMEWORK DE TEST nous indique que le rsultat attendu tait 1 alors que le rsultat obtenu est de 3628800. Nous pouvons galement voir le message que nous avons demand dacher en cas derreur. Le souci avec NUnit, cest qu partir du moment o il a charg la dll pour lancer les tests, il nest plus possible de faire de modications, car toute tentative de compilation provoquera une erreur o il sera mentionn quil ne peut pas faire de modications car le chier est dj utilis ailleurs. Ce qui est vrai. Nous serons donc obligs de fermer puis de rouvrir NUnit. noter que dans les versions payantes de Visual Studio, nous avons la possibilit de congurer NUnit en tant quoutil externe, ce que nous ne pouvons pas faire avec la version gratuite. Il va falloir faire avec. . . Cest un des inconvnients de la gratuit. . . ! Nous pouvons cependant un peu tricher en dnissant un vnement de post-compilation, qui consiste lancer NUnit automatiquement. Pour cela, allez dans les proprits du projet, onglet vnements de build et tapez la commande suivante : "C:\Program Files\NUnit 2.5.10\bin\net-2.0\nunit.exe" $(TargetFileName). Ici, nous indiquons quaprs la compilation, il va lancer le programme nunit.exe en prenant en paramtre le rsultat de la compilation, reprsent par la variable interne de Visual C# Express : $(TargetFileName) (voir la gure 38.9).

Figure 38.9 Modication des lments de post-compilation Par contre, cela veut dire que NUnit va se lancer chaque compilation, ce qui nest peuttre pas le but recherch. . . Il faudra galement fermer NUnit avant de pouvoir faire quoi que ce soit dautre. noter que maintenant que nous savons faire de lintrospection sur les mthodes et les attributs dune classe, nous devrions tre capables de crer une petite application qui excute les tests automatiquement ! Pour en nir avec NUnit, notons quil y a beaucoup de mthodes permettant de vrier si un rsultat est correct. Regardons les assertions suivantes :
1

bool b = true ;

477

CHAPITRE 38. LES TESTS UNITAIRES


2 3 4 5 6

Assert . IsTrue ( b ) ; string s = null ; Assert . IsNull ( s ) ; int i = 10 ; Assert . Greater (i , 6 ) ;

Elles parlent delles-mmes. La premire permet de vrier quune condition est vraie. La deuxime permet de vrier la nullit dune variable. La dernire permet de vrier quune variable est bien suprieure une autre. noter quelles ont chacune leur pendant (IsFalse, IsNotNull, Less). En regardant la compltion automatique, vous dcouvrirez dautres mthodes de vrication, mais celles-ci sont globalement sufsantes. Nous pouvons galement utiliser une syntaxe un peu plus parlante, surtout pour les anglophones :
1

Assert . That (i , Is . EqualTo ( 10 ) ) ;

Il est galement possible dutiliser un attribut pour vrier quune mthode lve bien une exception, par exemple :
1 2 3 4 5 6

[ Test ] [ Expected Exception ( typeof ( FormatException ) ) ] public void T o I n t 3 2 _ A v e c C h a i n e N o n N u m e r i q u e _ L e v e U n e E x c e p t i o n () { Convert . ToInt32 ( " abc " ) ; }

Dans ce cas, le test passe si la mthode lve bien une FormatException. Avant de terminer, prsentons deux attributs supplmentaires : les attributs SetUp et TearDown. Ils permettent de dcorer des mthodes qui seront appeles respectivement avant et aprs chaque test. Cest lendroit idal pour factoriser des initialisations ou des nettoyages dont dpendent tous les tests.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18

[ TestFixture ] public class MathTests { [ SetUp ] public void Ini ti ali sat io nDe sT est s () { // rajouter les initialisations } [ Test ] public void F a c t o r i e l l e _ A v e c V a l e u r 3 _ R e t o u r n e 6 () { // test faire } [ TearDown ] public void NettoyageDesTests () {

478

LE FRAMEWORK DE SIMULACRE
19 20 21

// nettoyer les variables , ...

Il existe plein dautres choses utiles dire sur NUnit, comme la description des autres attributs, mais il nest pas utile de les voir toutes notre niveau. Nhsitez pas aller voir sur internet des informations plus pousses si vous ressentez le besoin dapprofondir votre matrise des tests.

Le framework de simulacre
Un framework de simulacre fournit un moyen de tester une mthode en lisolant du reste du systme. Imaginons par exemple une mthode qui permette de rcuprer la mto du jour, en allant la lire dans une base de donnes. Nous avons ici un problme, car lorsque nous excutons le test le lundi, il pleut. Quand nous excutons le test le mardi, il fait beau, etc. Nous avons ici une information qui varie au cours du temps. Il est donc dicile de tester automatiquement que la mthode arrive bien construire la mto du jour partir de ces informations, vu quelles varient. Le but de ces frameworks est de pouvoir bouchonner le code dont notre dveloppement dpend, an de pouvoir le tester unitairement, sans dpendance et isol du reste du systme. Cela veut dire que dans notre test, nous allons remplacer la lecture en base de donnes par une fausse mthode qui renvoie toujours quil fait beau. Cependant, ceci doit se faire sans modier notre application, sinon cela na pas dintrt. Voil quoi servent ces framework de simulacres. Il en existe plusieurs, plus ou moins complexes. Citons par exemple Moq (prononcez moque-you ) ou encore Moles (il y en a plein dautres). Lintrt de Moq est quil est simple daccs, nous allons le prsenter rapidement. Il permet de faire des choses simples et facilement. Tandis que Moles est un peu plus volu mais plus complexe prendre en main. Vous y reviendrez sans doute ultrieurement. Pour le tlcharger, utilisez le code web suivant : Tlcharger Moq Code web : 225970

Pas de systme dinstallation volu, il y aura juste une dll rfrencer. Ajoutez donc la rfrence la dll moq.dll qui se trouve dans le sous-rpertoire NET40. Ensuite, pour pouvoir bouchonner facilement une classe, elle doit implmenter une interface. Imaginons la classe daccs aux donnes suivante :
1 2 3 4 5 6 7 8 9

public class Dal : IDal { public Meteo ObtenirLaMeteoDuJour () { // ici , c ' est le code pour lire en base de donn es // mais finalement , peu importe ce qu ' on y met vu qu ' on va bouchonner la m thode throw new N o tI mp le m en te dE x ce pt i on () ; } }

479

CHAPITRE 38. LES TESTS UNITAIRES Qui implmente linterface suivante :


1 2 3 4

public interface IDal { Meteo ObtenirLaMeteoDuJour () ; }

Avec lobjet Meteo suivant :


1 2 3 4 5

public class Meteo { public double Temperature { get ; set ; } public Temps Temps { get ; set ; } }

Et lnumration Temps suivante :


1 2 3 4 5

public enum Temps { Soleil , Pluie }

Comme nous allons le voir, nous pouvons galement crire un test qui bouchonne lappel la mthode ObtenirLaMeteoDuJour, qui doit normalement aller lire en base de donnes, pour renvoyer un objet la place. Pour bien montrer ce fonctionnement, jai fait en sorte que la mthode lve une exception, comme a, si on passe dedans a sera tout de suite visible. La mthode de test classique devrait tre :
1 2 3 4 5 6 7 8

[ Test ] public void O b t e n i r L a M e t e o D u J o u r _ A v e c U n B o u c h o n _ R e t o u r n e S o l e i l () { IDal dal = new Dal () ; Meteo meteoDuJour = dal . ObtenirLaMeteoDuJour () ; Assert . AreEqual ( 25 , meteoDuJour . Temperature ) ; Assert . AreEqual ( Temps . Soleil , meteoDuJour . Temps ) ; }

Si nous excutons le test, nous aurons une exception. Utilisons maintenant Moq pour bouchonner cet appel et le remplacer par ce que lon veut :
1 2 3 4 5 6 7 8 9

[ Test ] public void O b t e n i r L a M e t e o D u J o u r _ A v e c U n B o u c h o n _ R e t o u r n e S o l e i l () { Meteo fausseMeteo = new Meteo { Temperature = 25 , Temps = Temps . Soleil }; Mock < IDal > mock = new Mock < IDal >() ; mock . Setup ( dal = > dal . ObtenirLaMeteoDuJour () ) . Returns ( fausseMeteo ) ; IDal fausseDal = mock . Object ; Meteo meteoDuJour = fausseDal . ObtenirLaMeteoDuJour () ;

480

LE FRAMEWORK DE SIMULACRE
10 11 12

Assert . AreEqual ( 25 , meteoDuJour . Temperature ) ; Assert . AreEqual ( Temps . Soleil , meteoDuJour . Temps ) ;

On utilise lobjet gnrique Mock pour crer un faux objet du type de notre interface. On utilise la mthode Setup travers une expression lambda pour indiquer que la mthode ObtenirLaMeteoDuJour retournera en fait un faux objet mto. Cela se fait tout naturellement en utilisant la mthode Returns(). Lavantage de ces constructions est que la syntaxe parle delle-mme partir du moment o lon connat les expressions lambdas. On obtient ensuite une instance de notre objet grce la proprit Object et cest ce faux objet que nous pourrons comparer nos valeurs. Bien sr, ici, ce test na pas grand intrt. Mais il faut le voir un niveau plus gnral. Imaginons que nous ayons besoin de tester la fonctionnalit qui met en forme cet objet mto rcupr de la base de donnes ou bien lalgorithme qui nous permet de faire des statistiques sur ces donnes mtos. . . L, nous sommes srs de pouvoir nous baser sur une valeur connue de la dpendance la base de donnes. Cela permettra galement de dcliner tous les cas possibles en changeant la valeur du bouchon et de faire les tests les plus exhaustifs possibles. Nous pouvons faire la mme chose avec les proprits. Imaginons la classe suivante dont la proprit valeur retourne un nombre alatoire :
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21

public interface IGenerateur { int Valeur { get ; } } public class Generateur : IGenerateur { private Random r ; public Generateur () { r = new Random () ; } public int Valeur { get { return r . Next (0 , 100 ) ; } }

Nous pourrions avoir besoin de bouchonner cette proprit pour quelle renvoie un nombre connu lavance. Cela se fera de la mme faon :
1 2 3 4

Mock < IGenerateur > mock = new Mock < IGenerateur >() ; mock . Setup ( generateur = > generateur . Valeur ) . Returns ( 5 ) ; Assert . AreEqual (5 , mock . Object . Valeur ) ;

481

CHAPITRE 38. LES TESTS UNITAIRES Ici, la proprit Valeur renverra toujours 5 en se moquant bien du gnrateur de nombre alatoire. . . Je marrte l pour laperu de ce framework de simulacre. Nous avons pu voir quil pouvait facilement bouchonner des dpendances nous permettant de faciliter la mise en place de nos tests unitaires. Rappelez-vous que pour quun test soit ecace, il doit pouvoir se concentrer sur un point prcis du code sans tre gn par les dpendances ventuelles qui peuvent agir sur ltat du test un instant t .

En rsum
Les tests unitaires sont un moyen ecace de tester des bouts de code dans une application an de garantir son bon fonctionnement. Ils sont un let de scurit permettant de faire des oprations de maintenance, de refactoring ou doptimisation sur le code. Les frameworks de tests unitaires sont en gnral accompagns doutils permettant de superviser le bon droulement des tests et la couverture de tests.

482

Chapitre

39
Dicult :

Les types dapplications pouvant tre dveloppes en C#

ous savez quoi ? Avec le C# on peut crer autre chose que des applications console ! On peut faire des applications avec des boutons et des menus, ou des sites web et mme des jeux ! Dans ce chapitre, je vais vous indiquer rapidement les dirents types dapplications quon peut dvelopper avec le C#. Chaque paragraphe de ce chapitre ncessiterait un livre entier pour tre correctement trait, aussi, ce ne sera quun trs bref aperu. Jespre que vous me pardonnerez daller si vite mais je souhaite nanmoins piquer votre curiosit pour vous donner envie daller explorer tous ces nouveaux mondes qui souvrent vous ! Il y aura certainement des notions que vous ne comprendrez pas compltement en fonction des thmes abords. Il faudra aller jeter un il complmentaire sur internet ou attendre un prochain livre !

483

CHAPITRE 39. LES TYPES DAPPLICATIONS POUVANT TRE DVELOPPES EN C#

Crer une application Windows avec WPF


Les applications Windows sont ce quon appelle des applications clients lourds. Elles sexcutent directement depuis notre systme dexploitation. Nous pouvons crer toutes sortes dapplications, comme un traitement de texte, une calculatrice, etc. Nous avons dj cr une application Windows travers notre projet console, sauf que nous tions rapidement limits. Avec WPF, nous allons pouvoir crer des applications graphiques avec des boutons, des menus, etc. Bref, tout ce qui compose une application habituelle. WPF signie Windows Presentation Foundation. Il sagit dune bibliothque permettant de raliser des applications graphiques. Ces applications sont dites vnementielles car elles ragissent des vnements (clic sur un bouton, redimensionnement de la fentre, saisie de texte, etc.) Pour construire des applications WPF, nous aurons besoin de deux langages. Un langage de prsentation qui va permettre de dcrire le contenu de notre fentre : le XAML(prononcez xamelle ) et du C# qui va permettre de faire tout le code mtier.

Crer une application web avec ASP.NET


ASP.NET cest la plateforme de Microsoft pour raliser des applications web. Cest un peu comme PHP, sauf que, vous vous en doutez, ASP.NET sappuie massivement sur le framework .NET. Et tout comme WPF, il sagit de bibliothques qui vont permettre de raliser facilement son site web. Il existe deux ASP.NET : lASP.NET WebForms et lASP.NET MVC. ASP.NET WebForms cest tout un mcanisme qui permet de faciliter la cration dune application web en faisant comme si ctait une application Windows. Cest--dire que le framework soccupe de grer toute la persistance dinformations entre les dirents tats des pages. Il permet aussi de travailler avec une approche vnementielle, comme une application Windows. Le premier but dASP.NET WebForms tait de faire en sorte que les personnes qui avaient dj fait du dveloppement Windows (avec des langages comme le Visual Basic ou autre) puissent facilement faire du dveloppement web, dans un contexte qui leur serait familier. ASP.NET MVC est plus rcent et ore une approche o le dveloppeur doit bien connatre tous les mcanismes du web. Il ore galement une plus grande matrise sur le rendu du site web. Enn, il intgre par dfaut tous les mcanismes prouvs du fameux patron de conception (design pattern) MVC. On ne peut pas dire quASP.NET WebForms soit mieux ou moins bien quASP.NET MVC. Il sagit de deux faons direntes de crer des sites web. Chacune a ses avantages et ses inconvnients. Par contre, les deux se basent sur un socle commun qui est le cur dASP.NET. 484

CRER UNE APPLICATION CLIENT RICHE AVEC SILVERLIGHT

Crer une application client riche avec Silverlight


Nous avons vu quil existait des applications type clients lourds, comme les applications console ou les applications WPF. Nous avons galement vu des applications web, avec ASP. NET WebForms ou ASP.NET MVC. Il existe quelque chose entre les deux, ce sont les applications dites client riche . Ce sont des applications qui ressemblent des applications lourdes, mais qui sexcutent lintrieur dun navigateur internet plutt que directement au niveau du systme dexploitation. Vous connaissez srement le clbre ash , trs populaire grce la multitude de jeux disponibles sur internet. Microsoft possde galement des bibliothques permettant de raliser des applications clients riches : Silverlight. Une application client riche sexcute donc directement dans un navigateur internet, comme Internet Explorer, Firefox ou Chrome. Ces applications sexcutent dans un plugin du navigateur. Pour excuter des applications ash ou des applications Silverlight, le navigateur devra possder le plugin adquat. Du fait quelles sexcutent dans un navigateur, ces applications ont quelques restrictions. Elles ne peuvent par dfaut pas accder au contenu du disque dur de lutilisateur, ce qui est nalement plutt pas mal pour une application disponible directement sur internet. Elles sexcutent uniquement dans la zone mmoire du navigateur, une espce de bac sable dont on ne peut pas schapper et do il est impossible daccder aux ressources directes de lordinateur sur lequel sexcute lapplication, au contraire des applications WPF par exemple. Ces applications clients riches ressemblent normment aux applications clients lourds avec quelques restrictions. Silverlight est donc une espce de WPF allg qui ne garde que lessentiel de lessentiel. Nous serons donc capables de raliser des applications sexcutant dans notre navigateur grce au C# et au XAML.

Le graphisme et les jeux avec XNA


Eh oui, il est aussi possible de raliser des jeux avec le C#. Mme si tout ce quon a vu auparavant permet la ralisation dapplications de gestion, Microsoft dispose aussi de tout ce quil faut pour raliser des jeux, grce XNA. XNA est un ensemble de bibliothques permettant de crer des applications graphiques mais cest galement un outil permettant dintgrer des contenus facilement dans des jeux (comme des images ou des musiques). Avec XNA, il est possible de faire des jeux qui fonctionnent sous Windows mais galement sous Xbox ou sur les tlphones Windows.

Crer une application mobile avec Windows Phone 7


Le C# nous permet galement de dvelopper des applications pour tlphones mobiles quips du systme dexploitation Windows Phone. Cest un point trs important car il est possible de mutualiser beaucoup de choses que nous avons apprises pour les transposer dans le monde en plein essor des smartphones. Ce qui est intressant avec les applications Windows Phone cest que nous rutilisons 485

CHAPITRE 39. LES TYPES DAPPLICATIONS POUVANT TRE DVELOPPES EN C# le savoir que nous avons pu acqurir dans les autres types dapplications C#. En eet, pour raliser des applications de gestion, nous allons utiliser Silverlight et pour raliser des jeux, nous utiliserons XNA.

Crer un service web avec WCF


Avec le C# il est galement trs facile de crer des services web. Un service web permet en gnral daccder des fonctionnalits depuis nimporte o, travers internet. Citons par exemple les services web dAmazon qui nous permettent de rcuprer des informations sur des livres, ou encore des services web qui permettent dobtenir la mto du jour. Bref, cest un moyen de communication entre applications htrognes potentiellement situes des emplacements physiques trs loigns. En imaginant que nous ayons galement besoin dexposer des mthodes lextrieur, pour quun fournisseur vienne consulter ltat de nos commandes ou quun client puisse suivre lavance de la sienne. . ., nous allons devoir crer un service web. Le framework .NET dispose de tout un framework pour cela qui sappelle WCF : Windows Communication Foundation. Un service web est une espce dapplication web qui rpond des requtes permettant dappeler une mthode avec des paramtres et de recevoir en rponse le retour de la mthode. Lintrt dun service web est quil est indpendant de la technologie. Mme si on crit un service web avec du C#, il peut tre appel par du java ou du PHP. Retrouvez des explications plus compltes et des exemples pratiques via le code web suivant : Aller plus loin Code web : 777901

486

Pour conclure
a y est, ce livre est termin. Le C# na (presque) plus de secret pour nous. Tout au long de ce livre nous avons dcouvert comment dvelopper des applications avec le C#. Il a fallu dans un premier temps apprendre les bases du C#, ce qui nest pas sans douleurs quand on na jamais fait de programmation ! Pouvoir apprhender correctement ce quest une variable ou comment drouler son premier programme nest pas une mince aaire. Mais petit petit, nous avons relev le d en prsentant des fonctionnalits qui nous serviront tout le temps, pour la moindre petite application. Forts de ces nouveaux apprentissages, nous avons ensuite dcouvert le monde de la programmation oriente objet et comment le C# tait un vrai langage orient objet. travers des notions comme les classes et autres proprits, nous avons pu tirer parti de cette faon de modliser des applications pour les adapter nos besoins. Je le reconnais, ce nest pas une partie facile. Les dirents concepts peuvent donner rapidement mal la tte ! Ce qui est certain cest que petit petit, ils vont devenir de plus en plus clairs. On peut trs bien commencer dvelopper en orient objet sans vraiment en matriser toutes les subtilits. Cest en pratiquant et en restant curieux sur le sujet que vous progresserez. Enn, nous avons pouss un peu plus loin dans les arcanes du C#, nous permettant de raliser des applications de plus en plus compliques. Nous avons galement montr comment utiliser les bases de donnes avec Entity Framework. Savoir lire et crire dans une base de donnes est un lment fondamental pour toute application de gestion qui se respecte. Nous avons galement aperu ce que lon pouvait faire avec le C#. De lapplication Windows, en passant par les jeux ou les applications web, le C# combin au framework .NET permet vraiment de faire beaucoup de choses ! Vous avez dsormais les cls pour dmarrer. Il ne reste plus qu vous plonger dans les domaines qui vous intressent an de raliser les applications dont vous avez envie. Pourquoi pas une application pour les smartphones ? Cest trs la mode. Noubliez pas que cest force de pratiquer, dessayer de crer des petites applications de rien du tout, que vous nirez par tre un dveloppeur conrm, capable son tour daider les autres. Nhsitez pas faire et refaire les TP. Aprs avoir ni le livre, vous pourrez srement amliorer vos premires versions des TP. Crez-en, xez vous des petits ds et venez les changer avec nous sur le Site du Zro ! ;-)

Index

A abstract . . . . . . . . . . . . . . . . . . . . . . . . . . . . 233 Action . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 327 aectation . . . . . . . . . . . . . . . . . . . . . . . . . . . . 38 application mobile. . . . . . . . . . . . . . . . . . . . . . . . . . .485 web . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 484 Windows . . . . . . . . . . . . . . . . . . . . . . . . 484 args . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 144 ASP.NET . . . . . . . . . . . . . . . . . . . . . . . . . . . . 484 assembly . . . . . . . . . . . . . . . . . . . . . . . 8, 78, 361 attribut . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 391 auto-compltion . . . . . . . . . . . . . . . . . . . . . . . 31 B base de donnes . . . . . . . . . . . . . . . . . 416, 432 bibliothque . . . . . . . . . . . . . . . . . . . . . . . . . 361 boolen . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 47 boucle . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 91 for . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 92 foreach . . . . . . . . . . . . . . . . . . . . . . . . . . 95 while . . . . . . . . . . . . . . . . . . . . . . . . . . . . 98 break . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 100

comparaison . . . . . . . . . . . . . . . . . . . . . . 46, 222 compilation . . . . . . . . . . . . . . . . . . . . . . . . . . . 21 compltion automatique . . . . . . . . . . . . . . . 31 concatnation . . . . . . . . . . . . . . . . . . . . . . . . . 38 condition. . . . . . . . . . . . . . . . . . . . . . . . . . . . . .45 conguration . . . . . . . . . . . . . . . . . . . . . . . . . 399 console . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 18 constructeur . . . . . . . . . . . . . . . . . . . . . . . . . 184 continue . . . . . . . . . . . . . . . . . . . . . . . . . . . . 101 conversion . . . . . . . . . . . . . . . . . . . . . . . . . . . 111 culture . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 385 D dbogueur . . . . . . . . . . . . . . . . . . . . . . . . . . . 125 dcrmentation. . . . . . . . . . . . . . . . . . . . . . . .40 dlgu . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 322

E else . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 46 else if . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 49 encapsulation . . . . . . . . . . . . . . . . . . . . . . . . 159 enregistrement . . . . . . . . . . . . . . . . . . . . . . . 432 Entity Framework. . . . . . . . . . . . . . . . . . . .432 numration . . . . . . . . . . . . . . . . . . . . . . . . . 379 C event . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 330 caractre . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 121 exception . . . . . . . . . . . . . . . . . . . . . . . . . . . . 336 caractres spciaux . . . . . . . . . . . . . . . . . . . . 41 catch . . . . . . . . . . . . . . . . . . . . . . . . . . . 337 casting . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 112 throw . . . . . . . . . . . . . . . . . . . . . . . . . . . 342 chane de connexion . . . . . . . . . . . . . 403, 453 extension . . . . . . . . . . . . . . . . . . . . . . . . . . . . 423 classe . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 166 F cl trangre . . . . . . . . . . . . . . . . . . . . . . . . . 448 cl primaire . . . . . . . . . . . . . . . . . . . . . . . . . . 448 finally . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 347 CLR . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 6 for . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 92 commentaire . . . . . . . . . . . . . . . . . . . . . . . . . . 31 foreach . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 70 488

INDEX formatage . . . . . . . . . . . . . . . . . . . . . . . . . . . . 384 protected . . . . . . . . . . . . . . . . . . . . . . . . . . . 196 framework .NET . . . . . . . . . . . . . . . . . . . . . . . 8 public . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 172 Func . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 327 R ramasse-miettes . . . . . . . . . . . . . . . . . . . . . . 375 G garbage collector . . . . . . . . . . . . . . . . . . . . . 375 ref . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 267 gnrique . . . . . . . . . . . . . . . . . . . . . . . . . . . . 282 rfrence . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 193 groupes . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 407 rexion . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 393 requtes . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 416 H S hritage . . . . . . . . . . . . . . . . . . . . 159, 194, 372 sealed . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 372 I Silverlight . . . . . . . . . . . . . . . . . . . . . . . . . . . . 485 IDE . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 12 SQL . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 416 if . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 46 static . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 238 incrmentation . . . . . . . . . . . . . . . . . . . . . . . . 40 structure . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 274 interface . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 225 substitution . . . . . . . . . . . . . . . . . . . . . 161, 207 internal . . . . . . . . . . . . . . . . . . . . . . . . . . . . 368 switch . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 49 syntaxe . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 27 L LINQ . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 416 T liste . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 70 tableau . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 68 test . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 466 M simulacre . . . . . . . . . . . . . . . . . . . . . . . . 479 mmoire . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 373 this . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 189 mthode . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 58 Microsoft SQL Server . . . . . . . . . . . . . . . . 444 U modlisation . . . . . . . . . . . . . . . . . . . . . . . . . 433 using . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 76 modulo . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 101 V Mono . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 12 Moq . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 479 valeur . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 193 var . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 244 N variable . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 35 null . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 187 Visual C# 2010 Express . . . . . . . . . . . . . . 11 NUnit . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 471 W O WCF . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 486 object . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 203 where . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 291 opration . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 38 while . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 98 out . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 269 Windows Phone 7 . . . . . . . . . . . . . . . . . . . . 485 WPF . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 484 P partial . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 236 X pile . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 373 XML . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 398 pile des appels . . . . . . . . . . . . . . . . . . . . . . . 132 XNA. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .485 point darrt . . . . . . . . . . . . . . . . . . . . . . . . . 127 Y polymorphisme . . . . . . . . . . . . . . . . . . 161, 211 private . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 172 yield . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 379 489

Dpt lgal : mars 2012 ISBN : 978-2-9535278-6-5 Code diteur : 978-2-9535278 Imprim en France Achev dimprimer le 20 mars 2012 sur les presses de Corlet Imprimeur (Cond-sur-Noireau) Numro imprimeur : 144485

Mentions lgales : Conception couverture : Fan Jiyong Illustrations chapitres : Fan Jiyong

You might also like