Vous êtes sur la page 1sur 565

Stephen Randy Davis

F-rst
Y,

ii/;'''!i:

r:l l :' ul1 rt''7'l !; y'

;,, ::.

C#

pour les Nuls

Publi par Hungry Minds. Inc.


9t)9

Third Avenue

New york, NY 10022

Copyright O 2001 par Hungry Minds, Inc. Pour les Nuls est une mrqlre dpose de Hungry Mincls, Inc For Dummies est lrne marque clpose de Hungry Minds, Inc Collection dirige par Jean-Pierre Cano

Traduction : Philippe Reboul


dition : Pierre Chauvor Maquette et illustration': Stphane Angot Tous droits rservs. Toute reproduction, mme partielle, du contenu, de la couverture ou des icnes, par quelque proccl que ce soit (lectronique, photocopie, bande magntique ou autre) est interdite sans autorisation par crit de Hungry Minds, Inc.

ildition franaise publie en accord avec Hungry Minds, Inc.


O 2002 par clitions First Interactive
33, avenue de la Rpublique 75011 Paris - France

Tt. 01 40'2r 46 46
Fax

0l

40 2L 46 20

E-mail : firstinfo@efirst.com

Web: www.efirst.com
ISBN: 2-84427-259-2 Dpt lgal : 1"' trimestre 2002 Limites de responsabilit et de garantie. L'auteur et I'diteur de cet ouvrage ont consacr tous leurs efforts prparer ce livre. Hrrngry Minds et l'auteur dclinent toute responsabilit concernant la fiabilit ou I'exhaustivit clu contenu de cet ouvrage. Ils n'assument pas de responsabilits pour ses qualits d'aclaptation quelque objectif que ce soit, et ne pourront tre en aucun cas tenus responsables Jrour quelque perte, profit ou autre dommage commercial que ce soit, notamment mais pas exclusivement particulier, accessoire, consquent, ou autres.
Marques dposes. Toutes les informations connues ont t communiques sur les marques dposes pour les procluits, services et socits mentionns dans cet ouvrage. Hungry Mincls, Inc. et les ditions First Interactive clclinent toute responsabilit quant I'exhaustivit et l'interprtation des informations. Tous les autres noms cle marque et de produits utiliss dans cet ouvrage sont des marques dposes ou cles appellations commerciales de leur propritaire respectif.

Sommaire

Prgmire parte

Crer fus premigrs prngrammes C#.....................

Chapitre I : Crer votre premier programme C# pour Windows................ 3

Chapitre 2 : Crer votre premire application console en C#


Crer un modle d'application console Crer le programme source Tester le rsultat .............. Crer votre premire vritable application console Examinons ce programme Le cadre de travail du programme Les commentaires
La substance du programme .............

2r
22 22 23 24 25 26 26 27

Deurime parte

: Programmaton lmentaire en C#.............. .... 29


valeur
........

Chapitre 3 : Dclarer des variables de type

3l

Dclarer une variable .......... Qu'est-ce qq'un int ? ........ Les rgles de dclaration de variable............. Variations sur un thme : des int de diffrents types

..........32
.............. 33

...34
.....' 35

ut

G#

pour les Nuls

Reprsenter des fractions Utiliser des variables en virgule flottante Dclarer une variable virgule flottante Convertissons encore quelques tempratures ....... Quelques limitations des variables en virgule flottante Utiliser le type decimal, hybride d'entier et de virgule flottante Dclarer une variable de type decimal ......... Comparer les types decimal, int, et float Soyons logique, examinons le type bool Un coup d'il aux types caractre La variable de type char Types char spciaux Le type string Comparer string et char Qu'est-ce qu'un type valeur ?......... Dclarer des constantes numriques
Changer de type : le cast

37 38

38
40 40 42 43

44 44
45 45 46 46

47 49
50

5l
53
53 54 55 56 57 59 ?......... 60
61

Chapitre 4 : Les oprateurs sont sympas


Faire de I'arithmtique Les oprateurs simples Ordre d'excution des oprateurs ............

L'oprateur d'assignation et ses variantes L'oprateur d'incrmentation Faire des comparaisons - est-ce logique ? .............. Comparer des nombres en virgule flottante : qui a le plus gros float Encore plus fort : les oprateurs logiques Trouver les mes surs : accorder les types d'expression Calculer le type d'une opration Assigner un type L'oprateur ternaire, le redoutable

63 63 65 66

Chapitre 5 : Contrler le flux d'excution d'un programme...................... 69


Contrler le flux d'excution........... Et si j'ai besoin d'un exemple ?
Qu'est-ce que je peux faire d'autre ? ....... viter mme le else
70
71

Instructions if imbriques
Les commandes de boucle

74 75 76 79

Commenons par la boucle de base, while Et maintenant, do... while Briser une boucle, c'est facile Faire des boucles jusqu' ce qu'on y arrive Les rgles de porte des variables Comprendre Ia boucle la plus utilise : for ........

80 84
85 86 90
91

sommaire Ul
Un exemple de boucle for ..........

9l
92 93 97
100

Pourquoi auriez-vous besoin d'une autre boucle ?.............. Des boucles imbriques ....... L'instruction de contrle switch Le modeste goto

Troisine parte

: Programmaton et obiets...............................

l0l
104

Chapitre 6 : Rassembler des donnes : classes et tableaux ..................... f 03


Montrez votre classe Dfinir une classe ............. Quel est notre objet ?.............. Accder aux membres d'un objet
r05
106

r07

111 Pouvez-vous me cionner des rfrences ?.............. Les classes qui contiennent des classes sont les plus heureuses du monde.. 113 115 Les membres statiques d'une classe 116 Dfinir des membres de type const 116 Les tableaux : la classe Array

Les arguments du tableau Le tableau longueur fixe Le tableau longueur variable .......... Des tableaux d'objets ..........

116

tt7
r20 r24
r27

Une structure de contrle de flux pour tous les tableaux : foreach Trier un tableau d'objets

r28

Chapitre 7: Mettre en marche quelques fonctions de grande classe ..... 135


Dfinir et utiliser une fonction.......... Un exemple de fonction pour vos fichiers ............ Donner ses arguments une fonction .......... Passer un argument une fonction ......... Passer plusieurs arguments une fonction Accorder la dfinition d'un argument et son utilisation Surcharger une fonction ne signifie pas lui donner trop de travail Implmenter des arguments par dfaut Passer des arguments d'un type valeur Retourner une valeur I'expditeur ........ Utiliser return pour retourner une valeur.............. Retourner une valeur en utilisant un passage par rfrence ......... Quand utiliser return et quand utiliser out ? Dfinir une fonction qui ne retourne pas de valeur La question de MainQ : passer des arguments un programme Passer des arguments I'invite de DOS Passer dcs arguments partir d'une fentre Passer des arguments partir de Visual Studio .NET
135

r37

r43 r44 r45

t46
r47

t49

l5l
157

r57
158 158 160

164
165

r68

t70

Ul

c# pour tes Nuts

Chapitre 8 : Mthodes de classe


Passer un objet une fonction Dfinir des fonctions et des mthodes d'obiet Dfinir une fonction membre statique d'une classe Dfinir une mthode ........ Le nom complet d'une mthode

175
175
177 177

179

r82
183

Accder I'objet courant

184 Qu'est-ce que this ? .............. 185 this est-il explicite ?.............. Quand 188 Et quand je n'ai pas this ?.............. 190 Obtenir de I'aide de Visual Studio - la saisie automatique ........... Obtenir de I'aide sur les fonctions intgres de la bibliothque standard C#. 191 193 Obtenir de I'aide sur vos propres fonctions et mthodes ...........

Encore plus d'aide ............. Gnrer une documentation XML

195

200

Chapitre 9 : Jouer avec des chanes en C# ........


Effectuer des oprations courantes sur une chalne ............. L'union est indivisible, ainsi sont les chalnes galit pour toutes les chalnes : la mthode CompareQ ............ Voulez-vous comparer en majuscules ou en minuscules ? .............. Et si ie veux utiliser switch ? Lire les caractres saisis Analyser une entre numrique ........... Traiter une suite de chiffres.............. Contrler manuellement la sortie Utiliser les mthodes TrimQ et PadQ Recoller ce que le logiciel a spar : utiliser la concatnation Mettre SplitQ dans le programme de concatnation Maltriser String.FormatQ

20r
202 202 204 208
209 210

212 215

2r7 2r7

22r
223
224

Quatrime partie : La programmaton oriente biet................. 229


Chapitre l0 : La programmation oriente objet : qu'est-ce que c'est ? ...231
L'abstraction, concept numro un de la programmation oriente objet............. 231 ...232 Prparer des nachos fonctionnels .233 Prparer des nachos orients objet La classification, concept numro deux de la programmation oriente objet ....... 234 ...... 235 Pourquoi classifier ?.............. Une interface utilisable, concept numro trois de la programmation oriente objet . 236
Le contrle d'accs, concept numro quatre de la programmation oriente objet .... 237

Comment la programmation oriente objet est-elle implmente par C#........... 238

Sommaire

IX

Chapitre

I : Rendre

une classe responsable ......

239
239

Restreindre I'accs des membres de classe Un exemple public de public BankAccount ......... Allons plus loin : les autres niveaux de scurit ............ Pourquoi se proccuper du contrle d'accs ? Des mthodes pour accder des objets Le contrle d'accs vole votre secours , Et alors ? ...,.......... Dfinir des proprits de classe Donner un bon dpart vos objets : les constructeurs Le constructeur fourni par C# Le constructeur par dfaut .............. Construisons quelque chose Excuter le constructeur partir du dbogueur Initialiser un objet directement : le constructeur par dfaut Voyons comment se fait la construction avec des initialisations Surcharger le constructeur viter les duplications entre les constructeurs tre avare de ses obiets

240 243
244

;; ";;;,pi; :

245
:

246

250 250
252

253 255 256

258
261
262

263 265 270

Chapitre l2 : Acceptez-vous I'hritage ? .........


Hriter d'une classe quoi me sert I'hritage ? .............. Un exemple plus concret : hriter d'une classe BankAccount .........
EST_UN par rapport A_UN La relation EST_UN La relation A_UN
272 274

- j'ai du mal m'y retrouver........

Contenir BankAccount pour y accder


Quand utiliser EST_UN et quand utiliser A_UN ? .............

275 278 278 279 280

28r
282 282 283 284 286 286
..

Autres considrations
Changer de classe
Des casts invalides I'excution..........

viter les conversions invalides en utilisant Ie mot-cl is L'hritage et le constructeur Invoquer le constructeur par dfaut de la classe de base Passer des arguments au constructeur de la classe de base : le mot-cl base La classe BankAccount modifie .........

288

29r
293

Chapitre 13 : Quel est donc ce polymorphisme

295
...............296

Surcharger une mthode hrite Ce n'est qu'une question de surcharge de fonction classe cliffrente, mthode cliffrente Redfinir une mthode d'une classe de base

.......296
...........297

.298

G#

pour les Nuls

Revenir la base
Le polyrnorphisme

303
305

Qu'y a-t-il de mal utiliser chaque fois le type dclar? Accder par le polymorphisme une mthode redfinie en utilisant is Dclarer une mthode comme virtuelle La priode abstraite de C# Le factoring entre classes Il ne me reste qu'un concept : la classe abstraite Comment utiliser une classe abstraite ? .......... Crer un obiet d'une classe abstraite : non ! Redrnarrer une hirarchie de classes
Sceller une classe

306
.... 308

309
311 311

3r7
318 320

32r
325

Chapitre l4 : Quand une classe n'est pas une classe : I'interface et la structure........... .............327
327 Qu'est-ce que PEUT_ETRE_UTILISE_COMME ? 329 Qu'est-ce qu'une interface ? ............. Pourriez-vous me donner un exemple simple ?........... 330 Ptris-je voir un programme qui PEUT-TRE- UTILIS-COMME un exemple ? ...... 332 Crer votre interface "faites-le vous-mme" 332 Interfaces prdfinies 334 Assembler le tout 336 Hritage et interface ............ 342 Rencontrer une interface abstraite 342 Une structure n'a pas de classe 345 La structure C# 346 Le constructeur de structure.......... 348 Les mthodes d'une structure sont ruses............ 349 Mettre une structure l'preuve par I'exemple .......... 350 Rconcilier la valeur et la rfrence : unifier le systme de types 353 Les types structure prdfinis .............. 353 Comment le systrne de types est-il unifi par des structures communes ? 354 Un exemple ............

Chapitre 15 : Quelques exceptions d'exception

..............

........... 359
360

Traiter une erreur I'ancienne mode : la retourner ......... Retourner une indication d'erreur Je suis l pour signaler ce qui me parat ncessaire ............. Utiliser un rncanisme d'exceptions pour signaler les erreurs ............. Puis-je avoir un exemple ?.......... Crer votre propre classe d'exceptions .........
Assigner plusieurs blocs catch Laisser quelques envois vous filer entre les doigts Relancer un objet Iiedfinir une classe d'exceptions

362
365 367

368

37r
373 375

378
380

Somma ire

XI

Chapitre 16 : Manipuler des fichiers en

C#.........

.......... 385
... 385
................ 387 ............... 388 ......... 388

Contrler I'accs aux classes avec les espaces de nom........ Rassembler des donnes dans des fichiers ............ Utiliser StreamWriter ........ Amliorez votre comprhension et votre vitesse de lecture avec

Diviser un mme programme en plusieurs fichiers source Runir des fichiers source dans un espace de nom Dclarer un espace de nom Accder des modules du mme espace de nom Utiliser un espace de nom avec le mot-cl using

........... 390
........ 391

.394
. 396

StreamReader

...... 402

Cin4uime partie : Programtner lrour Windouls a(ec Uisual Studio

.... 407
409
410 410
411

Quel est Ie problme ? Exposer le problme ............. Concevoir la prsentation Ma solution .......... Dessiner la solution ............. Crer le cadre de travail de I'application Windows Ignorez ce type qui se cache derrire le rideau diter la fentre d'dition Construire les menus ........... Ajouter les contrles d'ajustement de la police Encore un coup de peinture et nous y sommes ............. Redimensionner le formulaire ........... Qu'avons-nous fabriqu ? .............. Comment apprendre connaltre les composants ? Et maintenant ?

412 412
413

415
417

419
422

424 426
429 431
431

433
433

Un menu garanti pour diter le menu Edition Mettre hardiment en gras et en italique Changer de police et de taille Implmenter les options du menu Format Choisir la taille de police Changer de taille en utilisant la TrackBar Changer de taille en utilisant la TextBox......... Enregistrer le texte de I'utilisateur Lire le nom du fichier

435

439
439

440
442 442

Lire un fichier RTF.........

444 446 446 448

Xl I

G#

pour tes Nuls

.. 449 crire un fichier RTF........ Mettre Lire et crire dans une bolte, avec un menu par-dessus.................. 450 Ne perdez pas mes modifications en quittant ! .......... ............. 452 . 456 Implmenter le bouton de fermeture de la fentre Raliser vos propres applications Windows ............ 457

Sixine parte : Petits suttplments par paque$ de dx...........r. 459


Chapitre 19 : Les dix erreurs de gnration les plus courantes (et comment y remdier)..........
.......... 461

'className' ne contient pas de dfinition pour'memberName' ........... 462 ......... 464 Impossible de convertir implicitement le type 'x'en 'y' 'className.memberName'est inaccessible en raison de son niveau de protection .. 466 .... 467 Utilisation d'une variable locale non assigne 'n' Le fichier'programName.exe' ne peut pas tre copi dans le rpertoire ... 468 d'excution. Le processus ne peut pas.. Le mot-cl new est requis sur'subclassName. methodName', car il masque le membre hrit 'baseclassName.methodName' ............ ....... 469 'subclassName' : ne peut pas hriter de la classe scelle'baseclassName' ......... 470

'className'n'implmentepaslemembred'interface'methodName' .. 470
471

'methodName' : tous les chemins de code ne retournent pas ncessairement une valeur ) attendue

472

Chapitre 20 : Les dix plus importantes diffrences entre C# et C++ ....... 473
Pas de donnes ni de fonctions globales Tous les objets sont allous partir du tas Les variables de type pointeur ne sont pas autorises.............. Vendez-moi quelques-unes de vos proprits ............. Je n'inclurai plus jamais un fichier 474 474 475 475 476 477 478 478 478 479

Ne construisez pas, initialisez ....... Dfinis soigneusement tes types de variable, mon enfant Pas d'hritage multiple Prvoir une bonne interface Le systme des types unifis

Index

481

lntroduction

u fil des annes, les langages de programmation ont beaucoup volu. Dans les premiers temps, les langages taient malcommodes et les outils volumineux. Ecrire un programme qui fasse quoi que ce soit d'utile tait une chose difficile. Au fur et mesure des progrs de la technologie, des langages plus avancs apparaissaient sur le march. Il y eut donc, assez rapidement, le langage C, et par la suite Q++ (prononcer "C plus plus"). Les outils s'amlioraient aussi. Trs vite, il apparut des environnements de dveloppement intgr, avec des diteurs, des concepteurs, des dbogueurs et Dieu sait quoi d'autre, runis dans des ensembles faits pour vous accompagner du berceau la tombe. On pourrait croire que ces nouveaux outils avaient rendu la programmation plus facile, mais il n'en tait rien : les problmes n'en taient que plus compliqus. C'est juste au moment o je pensais que les programmeurs allaient enfin rattraper ce processus qu'est apparu le dveloppement pour le Web.

Avec I'avnement du Web, le monde s'est divis en deux camps : les adeptes des solutions bases sur le systme d'exploitation Windows, et "les autres". Au dbut, ce sont "les autres" qui prirent I'avantage. Leurs outils, bass sur le langage Java, permettaient d'crire des programmes distribus sur le Web.
C'est en juin 2000 que Microsoft a prsent sa rponse, sous la forme d'une famille de langages et d'outils appele .NET (prononcer "point net", ou "dot net" pour faire amricain), avec son emblmatique langage de

programmation C# (prononcer "C sharp", autrement dit "do dise"). Bientt peut-tre, on pourra programmer en si bmol majeur !
Les buveurs de Java en revendiquent la supriorit, mais les NETitiens ont aussi leurs arguments. Sans prendre part leur polmique, on peut dire qu'une bonne partie de la diffrence peut se rsumer en une phrase : Java vous dit qu'il vous suffit de tout rcrire en Java, et vous pourrez excuter

KIU

G#

pour tes Nuts

le rsultat sur n'importe quelle machine ; .NET vous dit de ne rien rcrire, et vous pourrez excuter le rsultat sous Windows. (En principe, .NET n'est pas directement li au systme d'exploitation Windows, mais en pratique il y a bien peu de chances que d'autres systmes d'exploitation importants viennent se placer sous la bannire .NET.)
C# fonctionne au mieux dans I'environnement .NET, permettant de crer

des programmes qui communiquent sur le Web, capables notamment de fournir des services des pages Web existantes. C# peut tre intgr d'autres langages de programmation, comme Visual Basic et Visual C++, permettant aux programmeurs de faire migrer les applications existantes vers le Web sans qu'il soit ncessaire de les rcrire toutes pour cela.

Toutefois, C# n'en est pas moins un langage autonome. Avec I'environnement Microsoft Visual Studio .NET, C# apporte aux programmeurs les instruments dont ils ont besoin pour crer des applications harmonieuses.

Au suiet de ce liure
Ce livre a pour but de vous dcrire C#, mais il y a une difficult. C# a t cr par Microsoft en tant que partie essentielle de son initiative .NET. Pour des raisons sans doute politiques, Microsoft a soumis au comit de normalisation internationale ECMA au cours de l't 2000 les spcifications du langage C#, bien avant que .NET ne devienne une ralit. En thorie, n'importe quelle entreprise peut donc proposer sa propre version de C#, crite pour fonctionner sous n'importe quel systme d'exploitation

et sur n'importe quelle machine plus grosse qu'une calculatrice.

Toutefois, au moment o j'cris ces lignes, il n'existe qu'un seul fournisseur qui propose un compilateur C# : Microsoft. En outre, Visual C# n'est propos que d'une seule manire : en tant qu'lment de la suite d'outils Visual Studio .NET. Aussi, pour vous dcrire C#, je ne pourrai viter de vous parler de Visual Studio, au moins jusqu' un certain point ; j'ai donc essay d'en maintenir l'vocation un minimum raisonnable. Je pourrais me contenter de vous dire : "Ouvrez votre programme de la manire qui vous plaira" ; mais je vous dirai plutt : "Lancez C# partir de Visual Studio en appuyant sur la touche F5." Je veux que vous puissiez-vous concentrer sur le langage C# sans avoir vous casser la tte sur des questions mineures.

Introduction

XU

D'un autre ct, je suis conscient du fait que beaucoup de lecteurs, sinon la

plupart d'entre eux, voudront utiliser C# dans le but d'crire cles applications pour Windows. Bien que ce ne soit pas un livre sur la programmation sous Windows en tant que telle, j'ai consacr une partie montrer comrnent C# et Visual Studio forment, ensemble, un puissant environnement de programmation pour Windows.

utilisateurs se serviront de C# afin de crer des applications distribues pour le Web ; mais comme on ne peut pas tout mettre dans ce livre, il me faut bien dfinir une limite quelque part. C# pour les lYuls ne s'attaque pas aux questions de .NET et de la programmation distribue.
Je sais aussi que certains

Hrlpothses qratutes
Avant de pouvoir commencer programmer en C#, il vous faut avoir install sur votre ordinateur un environnement de dveloppement C# ; autrement dit, au moment o j'cris, Visual Studio de Microsoft. Pour construire les programmes de ce livre, vous devez avoir install Visual Studio .NET. Pour pouvoir seulement excuter un programme gnr avec C#, il faut avoir le Common Language Runtime (CLR). Au cours de sa procdure d'installation, Visual Studio .NET copie le CLR sur votre machine. D'autre part, Microsoft a I'intention d'inclure le CLR dans les versions ultrieures de Windows, mais ne I'a pas encore fait pour le moment.
A

Lomment utiliser ce litlre


J'ai fait en sorte que ce livre soit aussi facile utiliser que possible. Il est dj bien assez difficile de comprendre un nouveau langage. Inutile de rendre les choses encore plus compliques. Ce livre est divis en six parties. Dans la premire, je vous prsente la programmation en C# avec Visual Studio. Vous y serez guid tape par tape travers la cration de deux types diffrents de programme. Je vous encourage fortement commencer par l en lisant ces deux chapitres avant de vous aventurer dans les autres parties du livre. Mme si vous avez dj crit des programmes, c'est le schma de base prsent dans la premire partie qui sera utilis tout au long du livre.

XUI

c# pour tes Nuts

De la deuxirne la quatrirne partie, les chapitres sont autonomes. Je les ai crits de manire que vous puissiez ouvrir le livre au hasard sur n'importe lequel d'entre eux et commencer lire. Toutefois, si vous tes un dbutant en programmation, il vous faudra commencer par lire la deuxime partie avant de pouvoir passer la suite. Mais si vous revenez un sujet particulier pour vous rafralchir la mmoire. vous ne devriez pas avoir de difficults aller directement la section correspondante sans conrmencer par lire les 20 pages prcdentes. La cinquime partie revient quelque peu au style "faites comme ceci". C# pour les Nuls est un livre sur la programmation en C#, mais c'est en crant de vritables applications pour Windows que C# et Visual Studio .NET brillent de tous leurs feux. Cette partie va donc vous guider
travers les tapes de la construction d'un programme pour Windows, au-del des choses lmentaires. Une fois que vous aurez tout lu, vous ne saurez pas encore tout sur la construction d'applications Windows puissantes, rnais vous aurez appris ce qu'il faut pour partir dans cette

direction.
Et bien sr, la sixime partie termine le livre selon la tradition des livres Pour 1e.s 1/u/.s.

Comment ce lure est organs


Voici un bref tour d'horizon de ce que vous allez trouver dans chaque partie :

Premire partie

: Crer tus premiers

proqrammes c#
Dans votre vie future de programmeur C#, vous allez crer beaucoup de programmes. Quelle meilleure manire de commencer que d'crire une petite application Windows amusante ('ai bien dit petite) ? Cette partie va vous montrer, tape par tape, comment crir'e la plus petite application Windows possible en utilisant l'interface Visual Studio .NET. Vous apprendrez aussi crer le cadre de base C# que nous allons utiliser dans le reste du livre,

ntroduction

XUI

Deuxitne parte en C#

: Programmaton lnentaire

Dans sa dfinition la plus lmentaire, une pice de Shakespeare n'est rien d'autre qu'un ensemble de sries de mots, lies les unes aux autres. D'un point de vue tout aussi lmentaire, 90 "/n de l'criture de n'importe quel programme C# consiste en cration de variables, en oprations arithmtiques et en instructions de contrle du chemin d'excution du programme. Cette partie est consacre ces oprations lmentaires.

Troisitne parte

: Programmation et objets

Dclarer des variables ici et l et faire avec elles des additions et des soustractions est une chose, crire de vritables programmes pour de vritables utilisateurs en est une autre. La troisime partie est consacre la manire d'organiser vos donnes pour les rendre plus faciles utiliser dans la cration d'un programme.

Quatrrne parte : La proqrammaton orente

objet
Vous pouvez toujours organiser les diffrentes parties d'un avion comme vous voulez, mais tant que vous ne serez pas arriv lui faire faire quelque chose, ce ne sera rien d'autre qu'une collection de parties. Il pourra aller quelque part seulement lorsque vous I'aurez fait dcoller.
C'est sur la base du mme principe que la quatrime partie va vous expliquer comment transformer une collection de donnes en un vritable objet. Un objet qui contient diffrents lments, bien sr, mais qui peut imiter les proprits d'un objet du monde rel. Cette partie prsente donc I'essence de la programmation oriente objet.

Cnquime partie : Programmer pnur Windows auec Uisual Studio


Il ne suffit pas d'avoir compris le langage C# pour savoir crire une application Windows complte avec toutes sortes de fonctions, de boutons et autres raffinements. Rien que pour le plaisir, la cinquime partie

XUlll

cnpour tes Nuts

vous guide dans I'utilisation de C# avec I'interface Visual Studio afin de crer une application Windows "non lmentaire". Vous serez fier du rsultat, mme si vos enfants n'appellent pas leurs copains pour le voir.

Sxime partie : Petts sulrtrlments par paquets de dix


C# est trs dou pour trouver des erreurs dans vos programmes. Par moment, je le trouve mme un peu trop empress me faire remarquer

F.

.\

mes faiblesses. Mais, croyez-le ou non, il fait a pour vous rendre service. Il vous fait remarquer des problmes que vous auriez dt dcouvrir vousmme s'il n'avait pas t l pour a.

Malheureusement, les messages d'erreur peuvent tre un peu confus. L'un des chapitres de cette partie prsente les messages d'erreur de gnration C# les plus courants, leur signification, et la manire de s'en dbarrasser.
De nombreux lecteurs viendront C# avec I'exprience antrieure d'un autre langage de programmation. Le deuxime chapitre de cette partie expose les dix principales diffrences entre C# et son gniteur, C++.

Au sujet du site Web


Sur notre site Web, vous trouverez tout Ie code source contenu dans ce liwe. Rendez-vous sur le site des ditions First I'adresse w.w.w. ef irst . com. Une fois sur la page d'accueil, cliquez sur First Interactive, puis sur la rubrique Tlchargement. Ensuite, faites dfiler les ouwages jusqu' C# Pour les [Vuls, cliquez sur le lien pour tlcharger le fichier ZIP contenant I'ensemble des fichiers, et dcompressezJe dans un rpertoire de votre choix.

lcnes utilises dans ce liure


Tout au long de ce livre, j'utilise les icnes suivantes pour mettre en vidence des informations importantes.
e9N'@^

S%|
= td

\7 / premire lecture. \/

Cette icne indique des aspects techniques que vous pouvez ignorer en

Introduction

XIX

U
.

L'icne Truc signale une information qui peut vous pargner pas mal de temps et d'efforts.

Souvenez-vous de cela. C'est important.

f\ =(D
*-f*li3
F

Souvenez-vous aussi de ce qui est indiqu par cette icne. C'est le genre de chose qui vous tombe dessus au moment o vous vous y attendez le moins et qui peut produire un bogue vraiment difficile dbusquer. Cette icne identifie le code que vous trouverez sur le site des ditions First. Vous y gagnerez quelques efforts de frappe au clavier, mais n'en abusez pas. Vous comprendrez mieux C# en saisissant les programmes vous-mme.

l
| | \-.Y

l----Fr:fn

Contuntions utilses dans ce liure


Pour faciliter les choses, j'ai utilis diffrentes conventions. Les termes qui ne sont pas des "mots ordinaires" apparaissent dans cette poiice, afin de rduire au minimum les risques de confusion. Les listings de programmes sont mis en retrait dans le texte de la faon suivante :
use System;
namespace MyNameSpace
{

public class
{

MyClass

Chaque listing est suivi par une explication subtile et profonde. Les programmes complets sont en tlchargement sur le site des ditions First, ce qui fera votre bonheur, mais les petits fragments de code n'y sont pas. Enfin, vous verrez des squences d'ouverture de menus comme dans "Slectionnez Fichier/Ouvrir avec/Bloc-notes", ce qui signifie : cliquer sur le menu Fichier, puis, dans le menu qui apparalt, sur Ouvrir avec, et enfin, dans le sous-menu qui apparalt, de slectionner Bloc-notes.

XX

C# pour

les Nuls

0 aller mantenant
Naturellement, la premire tape est de comprendre le langage C#, idalement en lisant C# pour les [t'luls. En ce qui me concerne, je m'accorderais quelques mois pour crire des programmes C# simples avant de passer l'tape suivante qui est d'apprendre crer des applications Windows. La cinquime partie pourrait faire paratre les choses faciles, mais il y a pas mal de piges. Essayez tous les composants disponibles dans la bolte outils de Visual Studio. Son systme d'aide en ligne, trs complet et pratique, les dcrits tous. Accordez-vous un bon nombre de mois d'exprience de cration d'applications Windows avant de vous lancer dans l'criture de programmes destins tre distribus sur Internet. Entre-temps, vous disposez de plusieurs endroits pour vous tenir au courant de I'actualit de C#. Pour commencer, tournez-vous vers la source officielle I nsdn . microsof t . com. Il existe aussi de nombreux sites Web de programmeurs qui contiennent des lments trs complets sur C#, et qui permettent aussi de participer des discussions en ligne sur les sujets les plus divers, de la manire d'enregistrer un fichier source aux mrites combins des ramasse-miettes (garbage collectors) dterministes et non dterministes. Voici quelques grands sites sur C#, sans ordre

particulier

,/ t/ t/

www.

codeguru. earthweb . com/ c sharp


com

csharpindex.
r^/ww.

c- sharpcorner. com

J'ai aussi mon propre site Web, wlvw. stephendavls . com, qui contient une liste de questions frquemment poses (FAQ, Frequently Asked Questions). S'il y a une chose que vous n'arrivez pas comprendre, Ia rponse ce qui vous proccupe s'y trouve peut-tre dj. J'y ai aussi ajout une liste de toutes les erreurs qui ont pu se glisser dans le livre. Enfin, il y a un lien vers mon adresse de messagerie qui vous permettra de m'envoyer un mail si vous ne trouvez pas ce que vous cherchez.

Premire partie

Greruos premiers programmes c#

"Avont d'aborder fes ospects avancs comme la fonction' Eject erLesTouristesQuiNaSuiventPos', nous sllons commence por es principes de bese."
f

ici ce que vous ayez maltris C#, vous avez pas mal de chemin faire. Autant commencer par vous amuser un peu. Cette premire partie va vous montrer les tapes de la cration d'une application Windows aussi simple que possible en utilisant I'interface de Visual Studio .NET. Vous y apprendrez aussi crer le cadre de travail de base en C# pour les exemples de programmes qui apparaissent tout au long de ce livre.

Ilr tt: t I V

Dans cette parte...

Chapitre

Grervotre premier programme c#pour Wi ndows


Dans ce chapitre :
Qu'est-ce qu'un programme ? Qu'est-ce que C# ? O suis-je Crer un programme pour Windows. Bien accorder votre environnement Visual Studio .NET pour C#.
?

ans ce chapitre, je vais donner quelques explications sur les ordinateurs, les langages de programmation, C#, et Visual Studio .NET. Ensuite, je vous guiderai travers les tapes de la cration d'un programme pour Windows trs simple, crit en C#.

Les langaqes de programmation, C#, et .NET


Un ordinateur est un serviteur remarquablement rapide, mais remarquablement stupide. Il fera tout ce que vous lui demanderez (dans la limite de ses capacits) trs vite, et mme de plus en plus vite. I'heure actuelle, un microprocesseur d'usage courant pour PC est capable de traiter prs d'un milliard d'oprations par seconde.

Premire partie:Crer vos premiers programmes

C#

Malheureusement, un ordinateur ne comprend rien de ce qui ressemble un langage humain. Vous pouvez toujours me dire : "Mon tlphone compose le numro de la personne que je veux appeler si je lui dis son nom. Je sais qu'il y a un petit ordinateur qui pilote mon tlphone. Donc, cet ordinateur parle franais." En fait, c'est un programme qui interprte ce que vous dites, pas I'ordinateur lui-mme.
Le langage de I'ordinateur est souvent appel longage machine. Pour un tre humain, il est possible, mais extrmement difficile et fertile en erreurs, d'crire en langage machine.

{cg)

1t$!Qa. Pour des raisons historiques, le langage machine est aussi appel langage d'assemblage. Chaque constructeur fournissait avec ses machines un programme nomm assembleur qui convertissait des mots particuliers en instructions du langage machine. Ainsi, vous pouviez crire des choses vraiment cryptiques du genre l'{OV AX, Ci{ (c'est une vritable instruction pour processeur Intel), et I'assembleur convertissait cette instruction en une suite de bits correspondant une seule instruction machine.
Les tres humains et les ordinateurs ont dcid de se rencontrer quelque part entre les deux. Les programmeurs crivent leurs programmes dans un langage qui est loin d'tre aussi libre que le langage humain, mais beaucoup plus souple et plus facile utiliser que le langage machine. Les langages qui occupent cette zone intermdiaire (par exemple C#) sont appels langages de hout niueau (le terme haut a ici un sens relatif).

Qu'est-ce (u'un programme )


Qu'est-ce qu'un programme ? Avant tout, un programme pour Windows est un fichier excutable que I'on peut lancer en double-cliquant sur son icne dans une fentre. Par exemple, la version du traitement de texte Word que j'utilise pour crire ce livre est un programme. On peut appeler cela un progromme excutable, ou tout simplement un excutoble. Le nom

du fichier d'un programme excutable se termine gnralement par I'extension .EXE.


Mais un programme est aussi autre chose. Un programme excutable comporte un ou plusieurs fichiers source. Un fichier de programme C# est un fichier texte qui contient une squence de commandes C#, se suivant selon les rgles de la syntaxe de C#. On appelle fichier source un tel fichier, probablement parce que c'est une source de frustration et d'angoisse.

Chapitre 1 : Crer votre premier programme C# pour Windows

Qu'est-ce que C#

Le langage de programmation C# est I'un de ces langages intermdiaires qu'utilisent les programmeurs pour crer des programmes excutables. C#

comble le foss qui existait entre le puissant mais compliqu C+* et le facile mais limit Visual Basic. Un fichier de programme C# porte I'extension .CS.
C# est
:

t/ t/ t/ t/

Souple : Un programme C# peut tre excut sur la machine sur laquelle il se trouve ou bien transmis par I'intermdiaire du Web pour tre excut sur un ordinateur distant. Puissant : C# dispose essentiellement du mme jeu d'instructions que C++, mais avec les angles arrondis.
Facile utiliser: Dans C#, les commandes responsables de la plupart des erreurs dans Q+r ort t modifies pour les rendre plus stres.

Visuel : La bibliothque de C# fournit les outils ncessaires pour crer directement des fentres d'affichage labores, avec des menus droulants, des fentres onglets, des barres de dfilement et des images d'arrire-plan, entre autres.
Prt pour Internet : C# est le pivot de la nouvelle stratgie Internet de Microsoft, nomme .NET (prononcer point net).

t/

,/ Sr:

Tout langage destin une utilisation sur Internet doit contenir sous une forme ou sous une autre des outils de scurit pour se protger contre les hackers.

Enfin, C# est une partie intgrante de .NET.

Qu'est-ce 4ue .NET

.NET est la stratgie adopte par Microsoft dans le but d'ouvrir le Web aux simples mortels comme vous et moi. Pour comprendre cela, il vous faut en savoir un peu plus.

Il est trs difficile de programmer pour Internet dans des langages un peu anciens comme C ou C++. Sun Microsystems a rpondu ce problme en crant le langage Java. Celui-ci repose sur la syntaxe de C++, rendue un peu plus accessible, et est centr sur le principe d'un dveloppement distribu.

Premire partie : Crer vos premiers programmes G#

Quand un programmeur dit "distribu", il pense des ordinateurs disperss gographiquement, excutant des programmes qui se parlent les uns aux autres, dans la plupart des cas par Internet.

Microsoft a dcid de se lancer dans la course et a acquis une licence du code source de Java, crant sa propre version nomme Visual J++ (prononcer "J plus plus"). Microsoft obtint ainsi un accs instantan aux progrs accomplis par Sun et de nombreuses autres entreprises en dveloppant des utilitaires en Java. Il y eut toutefois quelques problmes lorsque Microsoft tenta d'ajouter des fonctions Java, car son contrat de licence du code source le lui interdisait. Pire encore, le contrat tait si simple qu'il tait impossible d'y lire autre chose que ce qu'on avait voulu y mettre. Sun avait russi bouter Microsoft hors du march Java. Il tait finalement aussi bien de se retirer de Java, parce qu'il avait un srieux problme : pour en tirer tous les avantages, il y avait intrt crire tout son programme en Java. Comme Microsoft avait trop de dveloppeurs et trop de millions de lignes de code source existantes, il lui fallait inventer un moyen de prendre en compte plusieurs langages. C'est ainsi que .NET vint au monde.
.NET est un cadre de travail, en bien des points semblable celui de Java. La plate-forme de la gnration prcdente tait constitue d'outils aux noms tranges, comme Visual C++ 6.0, COM+, ASP*, Dynamic Linked Libraries et Windows 2000 (et versions antrieures). .NET leur apporte Visual Studio .NET, une amlioration de COM+, ASP.NET, une nouvelle version de Windows, et des serveurs prenant en compte .NET. .NET quant lui prend en compte les nouveaux standards de communication comme XML et SOAP, plutt que les formats propritaires de Microsoft. Enfin, .NET prend en compte le dernier grand mot d'ordre qui fait fureur, comme en son temps I'orientation objet : les services Web.

'qg,

^tK

Microsoft revendique volontiers que .NET est trs suprieur la suite d'outils pour le Web de Sun, base sur Java, mais la question n'est pas l. Contrairement Java, .NET ne vous demande pas de rcrire vos programmes existants. Un programmeur Visual Basic peut se contenter d'ajouter son programme quelques lignes de C# afin de le rendre "bon pour le Web" (ce qui signifie qu'il sait se procurer des donnes sur Internet). .NET prend en compte tous les langages de Microsoft, plus une vingtaine de langages d'autres origines, mais c'est bien C# qui est le navire amiral de la flotte .NET. Contrairement la plupart des autres langages, C# peut accder toutes les fonctions de .NET.

Chapitre 1 : Grer votre premier programme C# pour Windows

Qu'est-ce que Uisual Studo .NET ? et C#

Vous vous posez strement beaucoup de questions. Le premier langage de programmation populaire de Microsoft a t Visual Q++, ainsi nomm parce qu'il avait une interface utilisateur graphique (ou GUI, Graphical User Interface). Celle-ci contenait tout ce dont on pouvait avoir besoin pour dvelopper des programmes C*+ bien ficels. Puis Microsoft a cr d'autres langages de type "Visual", notamment Visual Basic et Visual FoxPro, pour finalement les intgrer tous dans un mme environnement :Visual Studio. Visual Studio 6.0 se faisant de moins en moins jeune, les dveloppeurs en attendaient avec impatience la version 7. C'est peu aprs le lancement de celle-ci que Microsoft a dcid de la renommer Visual Studio .NET, de manire mettre en vidence la relation entre ce nouvel environnement et .NET.

D'abord, j'ai plutt pris a pour un stratagme, jusqu'au moment o j'ai commenc I'examiner srieusement. Visual Studio .NET est assez significativement diffrent de ses prdcesseurs, suffisamment pour iustifier un nouveau nom. Microsoft a nomm Visual C# son implmentation du langage C#. En ralit, ce n'est rien d'autre que le composant C# de Visual Studio. C# est C#, avec ou sans Visual Studio.
Et voil. Plus de questions.

Crer une application pour hAindoos a(lec C#


Pour vous aider vous mettre dans le bain avec C# et Visual Studio, cette section va vous conduire travers les tapes de la cration d'un programme Windows. Un programme Windows est couramment appel une application Windows, plus familirement WinApp. Notre premire WinApp nous servira de schma de base pour les programmes Windows que nous allons crer par la suite. En outre, ce programme va vous servir de test pour votre environnement Visual Studio. Ce n'est qu'un test, mais c'est aussi un vritable programme Windows. Si vous russissez crer, gnrer et excuter ce programme, alors votre environnement Visual Studio est correctement configur, et vous tes prt lever I'ancre.

Premire partie : er vos premiers programmes

C#

Crer le nodle
crire une application Windows partir de zro est un processus clifficile, c'est bien connu. ll y a beaucoup de gestionnaires de sessions, de descripteurs, de contextes, beaucoup de dfis relever, mme pour un programme simple. Visual Studio .NET en gnral et C# en particulier simplifient considrablement la tche de cration d'une application Windows, mme dj trs simple. Pour tre franc, je regrette un peu que vous ne soyez pas oblig de tout faire la main. Si le cur vous en dit, vous pouvez essayer avec Visual C**. . . Mais je n'insiste pas.
Comme le langage C# est conu spcialement pour faire des programmes qui s'excutent sous Windows, il peut vous pargner bien des complications. En plus, Visual Studio .NET comporte un Assistant Applications qui permet de crer des modles de programme. Typiquement, un modle de programme ne fait rien par lui-mme, en tout cas rien d'utile (un peu comme la plupart de mes programmes), mais il vous fait passer sans effort le premier obstacle dr,r dmarrage. Certains modles de programme sont raisonnablement sophistiqus. En fait, vous serez bien tonn de tout ce que l'Assistant Applications est capable de faire.

Pour commencer, lancez Visual Studio .NET.


q/\

*\

'e,

N'oubliez pas qu'il faut d'abord avoir install Visual Studio.

l.

Pour lancer Visual Studio, cliquez sur DmarrerlProgrammes/ Microsoft Visual Studio.NET 7.0/Microsoft Visual Studio.NET 7.0, comme le montre la Figure l. l.
Le CPU s'agite, le disque de mme, et Visual Studio apparalt. C'est ici que les choses deviennent intressantes.

2.

Slectionnez Fichierfl\ouveau/Projet, comme le monEe la Figure 1.2.

Visual Studio ouvre la bote de dialogue Nouveau projet, comme le montre la Figure 1.3.
Un projet est une collection de fichiers que Visual Studio assemble pour en faire un seul programme. Tous vos fichiers seront des fichiers source C#, portant I'extension .CS. Un fichier de projet

porte I'extension

.PRJ.

Chapitre 1 : Crer votre premier programme C# pour Windows

.-.il

I
'-l ,.!
.fu
Arcessoires

&5dnsr*s
t ---?t
t,-dI

ffi
P66 de

omarage xptor*eur Windows


Explorer

trvd

t*il,***
Windws Update Nouveu dscumffit Of f ic

,#

,..& Internt

.p :! ffi ftl j -:i ,$

outlookExpress outils Microsoft office

"|ffi, Mi..rlt1utlod.,
NicroscftExrd

tticrosdt t4ord

xn"t relp workshop


F4'(r')sdt
,NET

Frm$,rk5D{

uvrir un dscrfient Office

mfrosoft np6ftion CBrfer Teit Mi(losaft Offir Tols visud stldi,NET Enterpris Feturs visud Studio.NEl lools

Figure 1.1: La ligne

lP

N5ml for Visual studio,t'ET 7,0

pas le plus
c#.

droite n'est

court chemin du Bureau


;fioemaner

ir 3J

tlglts

rl^6

3.

Dans le volet Types de projets, slectionnez Projets Visual C#, et dans le volet Modles, sIectionnez Application Windows. Si vous ne voyez pas la bonne icne de modle, ne vous inquitez pas. Faites dfiler Ie contenu du volet Modles pour la faire apparatre.
Ne cliquez pas encore sur OK.

4.

Dans le champ Nom, entrez un nom pour votre projet ou laissez le nom par dfaut. L'Assistant Applications va crer un dossier dans lequel il va stocker diffrents fichiers, notamment le fichier source initial C# du projet. L'Assistant Applications utilise comme nom de ce dossier le nom que vous avez entr dans le champ Nom. Le nom initial par dfaut est 'un/indowsAppllcationl. Si vous I'avez dj utilis pour un projet, il devient WindowsApplicat ion} ou rjlndowsAppiication3. et ainsi de suite.

Pour cet exemple, vous pouvez utiliser le nom par dfaut ainsi que I'emplacement par dfaut pour Ie nouveau dossier : Mes documents\Projets Visual Studio\Wi nd owsAp p 1 i c at i on 1 .

t0

Premire partie : Crer vos premiers programmes

C#

Figure 1.2 Crer un n0uveau projet vous met sur la voie d'une
:

a pplication Windows.

f;g iiJ
J Pr,:,lels !'i5uBl Eitsir --i Prrrits ltisul f# J Ffflett liituil !:++ J Fr ljts dt rrrniitturaLiorr et J lLrE5 r,liel:5 J -irilui:frrri !4:rt:l 5frt,Jir

w
de 'lepl,:ienre

@
Fibtiottreowde
Ert,liothque de ,:,lnlrlBs ",LlindDi!5

Figure 1.3

ffi

L Assistant Applications

de Visual
nouvea

I | lll :J1
,

-*
A5P,FIET

Eibliothque de contrles U/eb

AFliration
AsF'.rJE

studio est prt crer p0ur v0us un


u

<t
Prijet de
':reatron

j
,..

d'une pplirtion vec une interfare utrliEateur Windoi+s

Emplacement

,-

l,f,,tuments and 5ettrnqslAdrnrnistr,:teur\Mes ,Jo,:umentsiFr,

;|

Parcourir

pr0gramme

Le proiel v he rr sur C:\,,.\Mes dscumentsiProiets Visuol Studio\WindowsApplictiont.

Windows.

vtlus

T- "l --l

Annuhr

Aid*

5.

Cliquez sur OK.


Le disque dur s'agite quelques instants (parfois longs) avant d'ouvrir un cadre vierge nomm Forml au milieu de la fentre Visual Studio.

Ghapitre 1 : Crer votre premier programme C# pour Windows

tl

Gnrer et excuter tlotre premier ritable 'Uindouls programme u


Une fois que I'Assistant Applications a charg le modle de programme, Visual Studio ouvre le programme en mode Conception. Vous devez convertir en application Windows ce programme source C# vide, rien que pour vous assurer que I'application initiale cre par I'Assistant Applications ne contient pas d'erreurs.

t\r

7 ,31ll )

^"9FK \g/

On appel le gnrer(build) l'acte de convertir un fichier source C# en une vritable application Windows en tat de fonctionner. Si votre fichier source contient des erreurs, Visual C# les trouvera dans le processus de construction.

Slectionnez Gnrer/Gnrer. Une fentre de rsultats s'ouvre, dans laquelle dfile une succession de messages. Le dernier de ces messages doittreGnration : 1 a russi, O a chou, 0 a t ignor.
La Figure 1.4 montre quoi ressemble mon Bureau aprs la gnration de

I'application Windows par dfaut. Vous pouvez dplacer les fentres comme vous voulez. Les plus importantes sont la fentre Forml.csIDesign] et la fentre Sortie.

Dgnns
:

g'-til5
Debuq

Fentre

'

-#

Help

Forml

5ystem,Wrrdorys

Forrn5.Ffr w

fl:l e, f--l z l.-l zr l@l / B , ;. ..,.u.,"" , ArressibleDescfl r AaessrbleNdnre


1

i AccessibleRole Defaqlt ; h?rrn,:rttt dtrloror Ll Lontror ; EacksroundlmogtI iaucun) : aursor Oefault :E) nont l'rrosol't 5ans 5el ForeColor I Conhol1ext
FrmBsrder5tl,l 5izble
No

j
Figure 1.4 : Le modle de pr0gramme initial pour Windows n'a rien de bien

Rightl0teft Text, :8 l. +rr rt*rI rtzzt,tl : AllowDrF antextllenu i Enbled ImeMode E ! .tq rIr.tr r' ,tti::t*r
El (Dyndfri.Propertr

Forml
False

r
Gerrrat rorl
:

{aucunl
True

Nofontrl

4 l:,*.1t4't.,,,. t,,.-,,. ext

-,

:. ..: :::.::.. ... :.:: ::.:::..

Le txte cor*cnu dnr ce csntrle,

exc ita nt.

{
La gnration a rursi

Coll

Chl

l2

Premire partie : Crer vos premiers programmes

G#

Vous pouvez maintenant excuter ce programme en slectionnant Dboguer/ Excuter sans dbogage. Lorsque vous le lancez, ce prograrnme doit ouwir une fentre exactement semblable la fentre Forml.cs[Design], mais sans les points, comme le montre la Figure 1.5.

Figure 1.5 modle

La fentre du

d application
Windows fonctionne, mais elle ne suffit pas convaincre
que Visual

Studio.NET vaut son prix.

- 1t${Qa.

{dg)

Dans la terminologie C#, cette fentre s'appelle un formulaire. Un formulaire est dot d'un cadre, avec en haut une barre de titre contenant les boutons Rduire, Agrandir, et Fermer.

Pour arrter le programme, cliquez sur le bouton Fermer dans le coin suprieur droit de la fentre.
Vous voyez: il n'est pas si difficile de programmer en C#.

Indpendamment de ce qu'il fait, ce programme initial est un test pour votre installation. Si vous tes parvenu jusqu'ici, alors votre environnement Visual Studio est dans l'tat qui convient aux programmes que nous allons voir dans la suite de ce livre.

Pendant que vous y tes, mettez donc jour votre CV pour y faire savoir que vous tes officiellement un programmeur d'application Windows. Pour le moment, vous pouvez vous contenter de mettre "application" au singulier.

Dessner une applcation


Le programme Windows par dfaut n'est pas bien excitant, mais vous pouvez I'amliorer un peu. Revenez dans Visual Studio, et slectionnez I'onglet Forml.csIDesign]. C'est la fentre du Concepteur de formulaires.

Chapitre 1 : Crer votre premier programme G# pour Windows

t3

Le Concepteur de formulaires est un outil trs puissant. Il vous permet de "dessiner" vos programmes dans le formulaire. Une fois que vous avez termin, cliquez sur Gnrer, et le Concepteur de formulaires cre le code C# ncessaire pour raliser une application avec le joli cadre que vous venez de dessiner. Dans les sections suivantes, vous allez gnrer une application avec deux champs de texte et un bouton. L'utilisateur peut saisir ce qu'il veut dans I'un des champs de texte (la source), mais pas dans I'autre (la cible). Lorsque I'utilisateur clique sur un bouton intitul Copier, le programme copie le texte du champ source dans le champ cible.

tl,lettre en place Quel(ues contrles L'interface utilisateur de Visual Studio est constitue de diffrentes fentres. Tous les lments comme les boutons et les zones de texte sont des contr1es. Afin de crer un programme Windows, vous allez utiliser ces outils pour en raliser l'interface utilisateur graphique (GUI), qui est gnralement la partie Ia plus difficile raliser d'un programme Windows. Dans le Concepteur de formulaires, ces outils se trouvent dans une fentre nomme Bolte outils.
Si la B1te outils n'est pas ouverte, slectionnez Affichage/Bolte outils. La Figure 1.6 montre cette Bote outils.

DDnns

,:ompsnts
'r/y'rndows Forn-rs

\ A A
gll
iii,r-

Fc,inteur

tatel
LinLLabel

E.rttc'n
Te:<tBo:r Marnf"lenu

$ lv (. Figure 1.6: La Bote


-dl
,

LneaxDox
HlOtrUtf0Tl

Grupuox Picture8x
h'Anel

outils de Visual Studio contient une quantit de contrles


utiles.

-.j

! .9 ll :g ;:-

natacrid
ListBctx

checkedlist8ox
L0mBoErlx Llslvre({

;:

Presi-F,piers cirr:ulire Gnerni

t4

Premire partie : Grer vos premiers programmes

C#

.$$G ^/ t(7, V

Si vos fentres ne sont pas aux mmes

endroits que les miennes, ne vous

Hinquitezpas.VotreBo1teoutilspeuttrsbiensetrouvergauche,

droite ou au milieu de l'cran. Vous pouvez dplacer chaque fentre o vous voulez dans la fentre Visual Studio.
La Bote outils comporte plusieurs sections, dont Donnes, Composants,

et Windows Forms (qui sera peut-tre devenue "Formulaires Windows" dans la version que vous aurez entre les mains). Ces sections permettent simplement d'organiser les contrles afin que vous puissiez les trouver plus facilement. La Bolte outils contient de trs nombreux contrles, et vous pouvez aussi crer les vtres. Dans la Bolte outils, cliquez sur Windows Forms. Ces contrles vont vous permettre d'amliorer vos formulaires. Vous pouvez utiliser les petites flches que vous voyez droite pour faire dfiler la liste.

Pour ajouter un contrle dans un formulaire, il suffit de le faire glisser et de le dposer I'endroit voulu. Essayez :

l.

Faites glisser le contrle Textbox sur le formulaire Forml, et

relchez le bouton de la souris.


Une zone de texte apparalt dans le formulaire, contenant le texte t extEox 1 . C'est le nom assign ce contrle par le Concepteur de formulaires. Vous pouvez redimensionner la zone de texte en cliquant dessus et en faisant glisser ses poignes.

Vous ne pouvez augmenter que la longueur d'une zone de texte, pas sa hauteur, car par dfaut une zone de texte ne comporte qu'une
seule ligne.

2. 3. 4.

Faites glisser une autre zone de texte et dposez-la au-dessous de

la premire.
Faites maintenant glisser un bouton et dposez-le au-dessous des

deux zones de texte.


Redimensionnez le formulaire et dplacez les contrles que vous venez d'y mettre jusqu' ce que le rsultat vous convienne.
La Figure 1.7 montre mon formulaire.

Chapitre 1 : Crer votre premier programme C# pour Windows

t5

Figure 1.7 Mon

Wi ''

+1lx
... ....

Iter:tE'rr:1

formulaire 0. est srement plus beau

ltertEr'irj

que le vtre.

rllatri ser

le

s propri ts

Le problme le plus flagrant de cette application est maintenant que l'tiquette du bouton qu'elle contient, buttonl, n'est pas trs descriptive. Nous allons commencer par y remdier. Chaque contrle possde un ensemble de proprits qui en dterminent I'apparence et le fonctionnement. Vous pouvez y accder par la fentre Proprits :

l. 2.

SIectionnez le bouton en cliquant dessus.


Faites apparatre la fentre Proprits en slectionnant Affrchage/ Fentre Proprits.
Le contrle Button possde plusieurs ensembles de proprits,

dont les proprits d'apparence, qui apparaissent dans la partie suprieure de la fentre Proprits, et les proprits de comportement, qui apparaissent au-dessous. C'est la proprit Text que vous
devez changer.

3.

Dans la colonne de gauche de la fentre Proprits, slectionnez la proprit Text. Dans la colonne de droite, tapez Copier, et

appuyez sur Entre.


La Figure 1.8 montre ces paramtres dans la fentre Proprits, et le formulaire qui en rsulte.

t6

Premire partie : Crer vos premiers programmes

G#

button

51'sl:ern, \,l/ind,:','rs, Forms,

Butt'ln

f-'=l s; | .- I dr
n P n'
Copier

frl,a1 lt9 | /

-t

Ere,:h,tri,undlmaqe l--_l (aucun)


Default FlaL5lyle

5tandard
l'licrrrsoFt 5ns 5erifj tl,Z5pt

l-aJTtl:

Ftrefol,:r
Inr,rqe

! [--l l-l
No

fontrolText (nurun)

Inr,r'lelgn
Intaqelrr,le;r

Ivliddlefenter lnucun)

RighlT,:Lel't

Figure 1.8 La fentre Proprits v0us 0ermet de matriser vos contrles.


:

f"liddle,lerrter

E | ..,,,i.,,,;r..
llr,r+Dr,:p IdIS
I UCUn

aonLe:rtlt:rrU
ttralogResult

l,l:rne

'

. l':l:l:l

':

Text
Le texte cntenu dans ce rontlle,

Vous pouvez utiliser la proprit Text d'un contrle zone de texte pour en changer le contenu initial. Pour les deux zones de texte de notre exemple, j'ai dfini cette proprit comme "Tapez quelque chose ici" et "Le programme copie ici ce que vous avez tap", afin que I'utilisateur sache quoi faire aprs avoir lanc le programme.
De mme, la proprit Text du formulaire lui-mme correspond au texte qui apparalt dans sa barre de titre, et vous pouvez la changer. Cliquez dans un endroit quelconque dans le formulaire, tapez ce qui vous convient dans la proprit Text, et appuyez sur Entre. J'ai fait apparaltre dans la barre de titre "Programme qui copie du texte." 4.

Slectionnez la zone de texte du bas, et faites dfrler ses prcprites de comportement pour faire apparare la proprit ReadOnly (lecture seule). Dfinissez cette proprit comme vraie en cliquant dessus et en sIectionnant True dans le menu droulant qui apparat, comme le montre la Figure 1.9. Dans la barre d'outils de Visual Studio, cliquez sur Ie bouton

D.

Enregistrer pour enregistrer votre travail.


Pendant que vous travaillez, pensez cliquer rgulirement sur le bouton Enregistrer pour tre sr de perdre le moins de choses possible en cas d'incident.

Chapitre 1 : Crer votre premier programme C# pour Windows

t7

Figure 1.9:

Dfinir une
zone de texte en lecture seule (Read0nly) empche
lTape:

quelque cf'cse ici

,.i,t'ar:i .- :

progal]w

-r:.l,tf:r.,r

L. .:.l

copie ici ce que rous yez tp

l'utilisateur
de modifier le champ

c0rrespondant.

Gnrer l'application Pour gnrer I'application, slectionnez Gnrer/Gnrer. Cette action gnre une nouvelle application Windows avec le formulaire que vous venez de crer. Si elle n'tait pas dj ouverte, la fentre Sortie apparat, dans laquelle vous voyez dfiler le flot de messages dont le dernier doit

treGnration : 1 a russi, O a chou, 0 a t ignor.

Vous pouvez maintenant excuter le programme en slectionnant Dboguer/Excuter sans dbogage. Le programme ouvre un formulaire conforme celui que vous venez de crer, comme le montre la Figure 1.10. Vous pouvez taper ce que vous voulez dans la zone de texte du haut, mais vous ne pouvez rien entrer dans celle du bas ( moins d'avoir oubli de dfinir comme vraie la proprit ReadOnly).

Figure 1.10:
La fentre du

-ltrl

est le

pr0gramme

lrEEtrEE@
lle
programme copie ici ce qu vous avez tap

formulaire
que v0us venez de

crer.

t8

Premire partie : Crer vos premiers programmes C#

Faisons-lui faire quelque chose


Ce programme se prsente bien, mais il ne fait rien. Si vous cliquez sur le bouton Copier, rien ne se passe. Jusqu'ici, vous n'avez fait que dfinir les proprits d'apparence - celles qui dfinissent I'apparence des contrles. Il vous faut maintenant mettre dans Ie bouton Copier I'astuce qui va lui faire effectivement copier le texte de la source la cible :

l. 2.

Dans le Concepteur de formulaires, slectionnez le bouton Copier. Dans la fentre Proprits, cliquez sur le bouton contenant un clair, au-dessus de la liste des proprits, pour ouvrir un nouvel ensemble des proprits.
Ce sont les unements.lls dfinissent ce que fait un contrle au cours de I'excution du programme.

Vous devez dfinir l'vnement Click. Comme son nom I'indique, il dfinit ce que fait le bouton lorsque I'utilisateur clique dessus.

3.

Doublecliquez sur l'vnement Click et voyez l'cran se hnsfonner.


La fentre de conception est I'une des deux manires de voir votre application. L'autre est la fentre de code qui montre le code source C# que le Concepteur de formulaires a construit pour vous automatiquement. Visual Studio sait qu'il vous faut entrer un peu de code C# afin que votre programme fasse ce que vous attendez de lui.

Lorsque vous double-cliquez sur la proprit Click, Visual Studio affiche la fentre Code et cre une nouvelle mthode, laquelle il donne le nom descriptif buttonl*Click ( ) . Lorsque I'utilisateur clique sur le bouton Copier, cette mthode effectue le transfert du texte de textBoxl, la source, textBox2,la cible. Pour le moment, ne vous inquitez pas de ce qu'est une mthode. J'en donnerai la description au Chapitre 8.
Cette mthode copie simplement la proprit Text de dans la proprit Text de textBox2.

textBoxl

4.

Dans la mthode buttonl_C1ick O, ajoutez simplement la ligne de code suivante :

textBox2.Text = textBoxl.Text;

Ghapitre 1 : Crer votre premier programme G# pour Windows

tg

1r${Qa^

ftg)

Remarquez que C# essaie de vous faciliter la tche de saisie du code. La Figure 1.1 1 montre I'affichage au moment o je tape le nom de la dernire proprit Text de la ligne ci-dessus. La liste droulante des proprits de la zone de texte correspondante apparalt, vous offrant un aide-mmoire des proprits disponibles, avec une info-bulle qui vous dit de quoi il s'agit. Cette fonction vous permet de complter automatiquement ce que vous tapez, et c'est une aide prcieuse au cours de la programmation (pour que a marche, ne faites pas de fautes de frappe dans ce qui prcde - attention aux majuscules et

minuscules).

F.,:hiet Edition Affrchage

PJojet

Ferr
:':)',

ll 'i
I'tt

!bog,:er

.3:
t'.

' L - .,! rai'I#


,' Forml,cs*
|

utils

-.

l?f

nd: 1,,:AFFI c:lr,rn i.F:{ni I

:l
i

[::l rl, fa L.,_l:r IEI


l.:'T-.Tl1r 1ll

:J
i

:-lf,l1r

!r:,lal llil]til

..FIrr:at 1rn. Frtrl ilrr Frlrr1 (l j ;

Figure 1.11
La fonction

tion automatique affiche


les noms des

de suggeslel | 5l:rln0 | E'[Un' bn5. I Lrl

1r
5rne

>l

+r

proprits au fur et
mesure que vous tapez.

:t*
Z
Pret

; :,.;,, .. E

ssrtie
Col 40

5.

Slectionnez Gnrer/Gnrer pour ajouter au programme la nouvelle mthode de clic.

Essayer le produit final


Pour excuter encore une fois le programme, slectionnez Dboguer/ Excuter sans dbogage. Tapez un peu de texte dans Ia zone de texte source, et cliquez sur le bouton Copier. Le texte est aussitt copi dans la

20

Premire partie : Crer vos premiers programmes

G#

zone de texte cible, comme le montre la Figure 1.12. Vous pouvez joyeusement rpter le processus autant que vous voulez avec tout ce qui vous passe par la tte, jusqu' ce que l'puisement vous submerge.

-ltrlxJ
lJ'ai tap r,ea darrr la zone de lerte t.rurrte lJ'ai tap ceci dan le asne de texte srrurce

Figure 1.12
a marche

En considrant le processus de cration, vous serez peut-tre frapp par son orientation graphique. Faites glisser des contrles, dposez-les o vous voulez dans le formulaire, dfinissez leurs proprits, et vous y tes. Il vous a suffi d'crire une seule ligne de code C#, et mme a n'tait pas

bien difficile.

'qE,

^tK

on pourrait objecter que ce programme ne fait pas grand-chose, mais je ne suis pas d'accord. Consultez simplement un manuel de programmation des dbuts de Windows, l'poque o les assistants d'application n'existaient pas encore, et vous verrez combien d'heures de programmation il fallait pour raliser une application aussi simple que celle-ci.

Programmeurs Uisual Basic, attention

Ceux d'entre vous qui sont des programmeurs Visual Basic ont peut-tre une impression de dj vu. En fait, le Concepteur de formulaires fonctionne assez largement comme les dernires versions de I'environnement Visual Basic, mais il est beaucoup plus puissant (en tout cas, par rapport aux versions antrieures Visual Basic .NET). Le langage C# en lui-mme est d'ailleurs plus puissant que le prcdent Visual Basic. La bibliothque de routines .NET est plus puissante que I'ancienne bibliothque de Visual Basic. Enfin, .NET prend en compte le dveloppement distribu et en diffrents langages, ce que ne faisait pas Visual Basic. En clehors de tout cela, je dirais qu'ils sont peu prs identiques.

Chapitre 2

Grervotre premire application console en C#


Dans ce chapitre : Crer une application console plus maniable'

Examiner le modle d'application console' Explorer les diffrentes parties du modle'

ne me dcourageant pour le programmeur c# dbutant. si vous type du programme croyezpas, alleisimplement voir Ie Chapitre 1. Un de moins qu" t'on appelle application console gnre significativement code c# et est beaucoup plus facile comprendre.

me le programme windows le plus lmentaire peut tre

crer un modle Dans ce chapitre, vous allez utiliser Visual Studio afin de peu manuelleun d,application console, que vous allez ensuite simplifier pour bon nombre ment. Vous pourrez utifiser le rsultat comme modle des programmes que je prsente dans ce livre'

tout cas des premires parties) est de SP*E Le principal but de ce livre (en pour y faire en c# un ieu qui aura un succs \ ;J; aider comprendre c#. \ -.i !g:I langage c#'

\ 5

l que vous connaissiez le I mondial, il faut d'abord

22

Premire partie : Crer vos premiers programmes

C#

Crer un rnodle d'applicaton cnnsnle


^"1$K +Y ,^, \ (O ) V-,/
Les instructions suivantes concernent Visual Studio. Si vous utilisez un autre environnement, c'est la documentation de celui-ci que vous devez vous rfrer. Mais quel que soit votre environnement, vous pouvez y taper directement le code C#.

Crer le programme source


Pour crer votre modle d'application console en C#, suivez ces tapes
:

l.

Slectionnez Fichier/Nouveau/Projet pour crer un nouveau projet.

Visual Studio affiche une fentre contenant des icnes qui reprsentent les diffrents types d'application que vous pouvez crer.

2.

Dans cette fentre Nouveau projet, cliquez sur I'icne

Application

console.

f\ /
.--).

e,
\

Faites attention bien slectionner le dossier des projets Visual C# dans la fentre Nouveau projet. Si vous en slectionnez un autre par erreur, Visual Studio peut crer une horreur comme une application Visual Basic ou Visual C*n. Avec Visual Studio, il est ncessaire de crer un projet avant de pouvoir commencer entrer votre programme C#. Un projet est un peu comme un tiroir dans lequel vous allez entasser tous les fichiers qui constituent votre programme. Lorsque vous demandez au compilateur de gnrer le programme, il extrait du projet les fichiers dont il a besoin afin de crer le programme partir de ceux-ci. Le nom par dfaut de votre premire application est

ConsoleApplicationl. L'emplacement par dfaut du fichier correspondant est un peu trop en profondeur mon gott dans le dossier Mes documents. Puisque je suis un peu difficile (ou peut-tre parce que j'cris un livre), je prfre classer mes programmes o je veux, et pas ncessairement l o Visual Studio veut les mettre.

3.

Pour changer le dossier par dfaut de vos programmes, cliquez sur le bouton Parcourir et naviguezjusqu' I'endroit voulu.
Pour le dossier dans lequel je place tous mes programmes, j'ai choisi le nom Programmes C#.

Chapitre 2:Crer votre premire application console en G#

23

4. 5.

Dans le champ Nom, entrez le nom que vous voulez donner au projet que vous crez. Pour ce premier programme, nous allons nous en tenir la tradition avec Hel1o.

Cliquez sur OK.


Aprs quelques instants de travail, Visual Studio gnre un fichier nomm Clas s 1 . c s. (Si vous regardez dans la fentre nomme Explorateur de soiutions, vous y verrez plusieurs autres fichiers. Vous pouvez les ignorer pour le moment.)

Le contenu de votre premire application console apparalt ainsi (les

commentaires en anglais seront peut-tre en franais dans la version que vous utiliserez) :
using
System;

nanespace He11o
tt

lll
||

(rrmlnary)

| III
{

Suwary description
4tsw*nary)
Class1

for

Classl.

class

static void Main(string[1


{

args)

ll /l il

rooo: Add code to start application here

]
'l

f\ =(t

Par rapport au programme excutable Windows que vous avez cr au Chapitre 1, Visual Studio a invers les lignes using Systern et namespace He11o. C# accepte ces commandes dans un ordre comme dans I'autre (il y a une diffrence. mais elle est subtile et bien en dehors du cadre de ce chapitre).

Tester le rsultat
Slectionnez Gnrer/Gnrer pour faire de votre programme C# un programme excutable.

24

Premire partie : Grer vos premiers programmes C#

Visual Studio rpond par le message suivant

Dbut de

la gnration: Projet: Hel1o, Configuration:

Debug ,NEf -

Prparation des ressources. . . Mise jour des rfrences... Compilation principale en cours. . . Gnration termine -- 0 erreur, 0 avertissenent Gnration d'assenblys satellites en cours...

---Gnration

:I

Ternin --a russi, 0 a chou, 0 a t ignor

Dans lequel Ie point important est


,hv-

I a russi.

^rtC

f fxll

tL]r, -

Unerglegnraledelaprogrammationestque''aruSSi''Veutdire,,a
va", et "a chou" veut dire "a ne va pas". Pour excuter le programme, slectionnez Dboguer/Dmarrer. Le programme se termine immdiatement. Apparemment, il n'a rien fait du tout, et en fait c'est bien le cas. Le modle n'est rien d'autre qu'une coquille vide.

Crer tutre premre ttrtable application console


Modifiez maintenant le fichier du modle C1ass1. cs, conformment ce qui suit.
-rr r\V-

.,-.71r{ LL?r, C

^1

Ne vous proccupez pas d'entrer un ou deux espaces ou une ou deux lignes blanches. En revanche, respectez les majuscules et les minuscules.

using Systen;
namespace IIe1lo
t
^!!hr.^ PUUTfL LrAD ^t^^a I,^.SS1 UrA

I | 'est ici que coureRce le progranne static void Mai.n(string I args)


{

Chapitre

2: Crer votre premire application console en G#

25

// Dernande son non 1'util-isateur Console.Writeline("Entrez votre nom: t') ;


string

ll Lit le nom entr par 1'utilisateur sNane - Console.Readline0;


Accueille 1'utilisateur par son non

/i

Console.hlriteline("He1Lo,

"*

sNane)

i / Rttend confirnation de ltuti.lisateur Console.Writeline("Appuyez sur Entre pour terniner...")

Console.ReadO;

Slectionnez Gnrer/Gnrer pour faire de cette nouvelle version de C1ass1. cs le programme C1ass1. exe.
Dans la fentre Visual Studio .NET, cliquez sur Dboguer/Dmarrer. Le programme vous demande immdiatement votre nom. Tapez votre nom et appuyez sur la touche Entre. Le programme rpond :
Entrez votre non:
Hildegarde He11o, Hildegarde
Appuyez

sur Entre pour terniner...

Le programme rpond par le mot "Hello", suivi par le nom que vous avez entr. Puis il attend que vous appuyiez sur Ia touche Entre pour rendre

son dernier soupir.

At'^!\ commande DOS. Ouvrez une fentre DOS. Tapez cd \Programmes :HW ) c*rH"llo\bin\Debug. Puis, pour excuter le progra..", tapez Hello \/ et appuyez sur Entre. Le rsultat doit tre le mme. Vous pouvez aussi
naviguer jusqu'au dossier \Programmes C1\ue11o\bin\Debug dans I'Explorateur Windows et double-cliquer sur le fichier He11o. exe.

4${ea^

Vous pouvez aussi excuter votre programme partir de la ligne de

Eramnons ce programme
Dans les sections qui suivent, nous allons prendre part I'une aprs I'autre chaque partie de cette application console en C# afin d'en comprendre le

fonctionnement.

26

Premire partie : Grer vos premiers programmes

G#

Le eadre de trat/al du programme


Pour toute application console, le cadre de travail de base commene par
using
System;

nanespace Hel1o

publie class Classl


t
I

C'est

ici

que coruaenc

le

prograrnme

public static void Main(string[] args) i

//

Le code sera plac

ici

l l

L'excution proprement dite du programme commence iuste aprs I'instruction qui contient Main et se termine la parenthse fermante qui suit Main. Je vous expliquerai en temps utile le sens de ces instructions. Je ne peux pas en dire plus pour le moment.

L'instruction using Sysrem peut venir juste avant ou juste aprs I'instruction namespace Hel1o l. L'ordre dans lequel elles se prsentent n'a pas
d'importance.

Les commentares
Ce modle contient dj un certain nombre de lignes, et j'en ai ajout

plusieurs autres, comme ici

// C'est ici que conmence 1e programme public static void Maj"n(stringIJ args)
Dans cet exemple, C# ignore la premire ligne. C'est ce que I'on appelle un

commentaire.

^}kt ftXl lLvrf \SrZ7 -

Toute ligne commenant par lloupar lll es:une ligne de texte libre qui sera ignore par C#. Pour le moment, vous pouvez consiclrer ll eI lll comme quivalents.

Ghapitre 2 : Crer votre premire application console en G#

27

Pourquoi mettre dans un programme des lignes destines tre ignores


Un programme, mme un programme C#, n'est pas facile comprendre. Souvenez-vous qu'un langage de programmation est un compromis entre ce que comprennent les ordinateurs et ce que comprennent les tres humains. Introduire des commentaires permet d'expliquer les instructions C#. Ceux-ci peuvent vous aider crire le code, et ils seront particulirement utiles au malchanceux qui devra reprendre votre programme un an plus tard et reconstituer votre logique. Ajouter des explications facilite beaucoup les choses.

.qa/ ft}Il \\f/

N'hsitez pas faire des commentaires, et faites-en le plus tt possible. Ils vous aideront, ainsi que les autres programmeurs concerns, vous rappeler ce que vous avez voulu faire en crivant ces instructions.

La substance du lrrogramme
Le cur de ce programme se trouve dans le bloc de code dlimit par

I'instructionMain i ):

/l

Denande son non

1'utilisateur
nogl:

console.l,lriteline("Entrez votre

")

ll tit
string

1e non entr par 1'utilisateur sNane = Console.Readline0;


nom
;

/l

Accueille 1'utilisateur par son


.l,Jriteline ("He11o

Console

,"*

sName)

L'excution du programme commence par la premire instruction C#: Console . WriteLine. Celle-ci crit dans la console la chane de caractres

Entrez votre

nom:.

L'instruction suivante lit la rponse entre par I'utilisateur et la stocke dans une "bolte de travail" nomme sName fi'en dirai plus sur ces emplacements de stockage au Chapitre 3). La troisime instruction combine la chne He11o et le nom entr par I'utilisateur, et envoie I'ensemble sur la console.

trois dernires lignes attendent que I'utilisateur appuie sur la touche Entre avant de poursuivre. Elles assurent ainsi que I'utilisateur a le temps de lire ce que le programme vient d'afficher :
Les

28

Premire partie : Crer vos premiers programmes

G#

/l Attend confirmation de 1'utilisateur Console.EriteLine ("Appuyez sur Entre pour terniner. . . ")
Console.Read0;

Cette tape peut tre importante selon votre environnement et selon la manire dont vous excutez le programme. Dans Visual Studio, vous avez deux manires d'excuter un programme. Si vous utilisez la commande DboguerlDmarrer, Visual Studio ferme la fentre de rsultats ds que le programme se termine. C'est la mme chose qui se produit lorsque vous excutez le programme en double-cliquant sur I'icne du fichier excutable dans I'Explorateur Windows.

"tq-$, -i il j
x

T .t

Quelle que soit la manire dont vous excutez le programme, attendre que l'utilisateur appuie sur la touche Entre avant de quitter rsout tous les problmes.

Deuxime partie

Programmation lmentaire en C,#

"Excusez-mai. Y a-t-il quelgu'un ici qui ne soit PAS en train de pcrlar de C# ?"

Dans cette partie...


es programmes les plus rcents de commerce lectronique, de business to business et de dot.com en tout genre utilisent

les mmes lments de base que le plus simple programme de conversion de temprature. Cette partie prsente les bases de la cration de variables, de I'excution d'oprations arithmtiques, et de la maltrise du cheminement de I'excution d'un programme.

Chapitre 3

Dclarer des variables de type valeur


Dans ce chapitre :
Crer un emplacement de stockage : la variable en C#.

Utiliser des entiers. Traiter des valeurs fractionnelles.


Dclarer des variables d'autres types.

Traiter des constantes numriques.


Changer de type.

a plus fondamentale de toutes les notions de la programmation est celle de variable. Une variable C# est comme une petite bolte dans

laquelle vous pouvez stocker des choses, en particulier des nombres. pour vous en servir ensuite.
Le terme uariable est emprunt au monde des mathmatiques. Par exemple
:

signifie qu' partir du moment o on a crit cela, on peut utiliser le terme n quand on veut dire 1, aussi longtemps que I'on n'aura pas attribu un autre sens n (un nombre, une quation, un concept ou autre).

32

Deuxime partie : Programmation lmentaire en C#

Dans le monde de la programmation, la signification du mot variable n'est gure diffrente. Lorsqu'un programmeur C# crit :

int

n' I'

Ces instructions clfinissent un "lment" n, et lui assignent la valeur 1. partir de ce point dans le programme, la variable n a la valeur I jusqu' ce

que le programmeur change cette valeur pour un autre nombre.

Malheureusement pour les programmeurs, C# impose plusieurs limitations aux variables - limitations dont les mathmaticiens n'ont pas se soucier (sauf ceux qui s'aventurent lire ce livre).

Dclarer une t/ariable


Quand un mathmaticien dit : "n gale 1", cela signifie que le terme n est quivalent 1, raisonnement que vous pouvez trouver trange. Le mathtnaticien est libre d'introduire des variables au gr de sa fantaisie. Par

exemple
x=yl

+zy+y
,, + 1 ^1^-^ I ' ! qrur

si k:
x=k2

Ici, le mathmaticien a crit une quation quadratique. Peut-tre les variables x et y ont-elles dj t dfinies quelque part. Toutefois, lorsqu'il voit apparaltre une nouvelle variable, k, le programme tombe des nues. Dans cet exemple, ft ne signifie pas essentiellement qu'il a la valeur de y plus 1, mais qu'il reprsente le concept de y plus 1. C'est une sorte de raccourci. Jetez un coup d'il n'importe quel manuel de mathmatiques, et vous veyyez ce que je veux dire. Je dis bien : jetez un coup d'cril. Vous tes ici pour lire mon livre et pas un autre. Un programmeur doit tre prcis dans Ia terminologie qu'il utilise. Par exemple, il peut crire le code suivant :
i nt

Ghapitre 3 : 0clarer des variables de type valeur

33

La premire ligne signifie : "Creuser un petit espace de stockage dans la

mmoire de I'ordinateur, et lui assigner le nom n." Cette tape revient ajouter un dossier dans une armoire dossiers suspendus et crire n sur son tiquette. La deuxime ligne signifie : "Stocker la valeur I dans la variable n, en remplaant par cette valeur tout ce que la variable pouvait contenir auparavant." Dans une armoire dossiers suspendus, l'quivalent serait : "Ouvrir le dossier n, enlever tout ce qu'il contient, et mettre I la place."

Le symbole = est appel oprateur d'assignation. Je dis bien le "symbole

et non le "signe" ou autre terme plus ou moins vague.

^tK =({g,

dit : "n gale 1." Le programmeur C# le dit d'une manire plus prcise : "Stocker la valeur I dans la variable n." (Pensez I'armoire dossiers suspendus, et vous veyrez que c'est prfrable.) Les oprateurs C# disent I'ordinateur ce que vous voulez faire. Autrement dit, les oprateurs sont des verbes et non des descripteurs. L'oprateur d'assignation prend la valeur qui est sa droite et la stocke dans la
Le mathmaticien

variable qui est sa gauche.

Qu'est-ce qu'un i

rrt .)

Les mathmaticiens manipulent des concepts. Ils peuvent crer des variables quand a leur chante, et une mme variable peut revtir diffrentes significations dans la mme quation. Au mieux, un mathmaticien considre une variable comme une valeur sans forme fixe, au pire, comme un vague concept. Ne riez pas, c'est probablement de la mme manire que vous voyez les choses.
Si le mathmaticien
n 1l l. t - r

crit

n l. t .. = .| . -

n n

: Maison * ttleg llartiens

sont panni

nousn

Chacune de ces lignes associe la variable n une chose diffrente, et le mathmaticien n'y pense mme pas. Je n'y pense pas beaucoup moi-mme, sauf pour la dernire ligne.

d'offrir une telle souplesse. En C#, chaque variable possde un type fixe. Lorsque vous choisissez un nouveau dossier suspendu pour
C# est loin

34

Deuxime partie : Programmation lmentaire en

C#

votre armoire, vous devez en prendre un de la taille qui convient. Si vous avez choisi un dossier suspendu "de type entier", vous ne pouvez pas esprer y mettre la carte de France.
Pour I'exemple de la section prcdente, vous allez choisir un dossier suspendu conu dans le but de contenir un nombre entier : ce que C# appelle une variable de type int (integer). Les entiers sont les nombres comme 0, 1, 2,3, et ainsi de suite, plus les nombres ngatifs, -1, -2, -3, et ainsi de suite.

Avant de pouvoir utiliser une variable, vous devez la dclarer. Une fois que vous avez dclar une variable de type int, elle peut contenir et rgurgiter des valeurs entires, comme le montre I'exemple suivant :
IT
'i

Dclare une variable entire n


Dc1are une

nf

variable entire
2

et f initialise

avec 1a valeur

int n=

m = ?'

Assigne

la variable n la valeur stoeke dans m

La premire ligne aprs le commentaire est une dclaration qui cre une zone de stockage, n, faite pour contenir une valeur entire. Aussi longtemps qu'il ne lui est pas assign une valeur, la valeur initiale de n n'est pas spcifie. La deuxime dclaration cre une variable entire m, avec 2

pour valeur initiale.


Le terme initialisersignifie assigner une valeur initiale. Initialiser une ^$tt$ variable, c'est lui assigner une valeur pour la premire fois. Tant qu'une -f il )

\g/

variable n'a pas t initialise, on n'en connalt pas la valeur.

La dernire instruction de cet exemple assigne la variable n la valeur stocke dans m, qui est 2. La variable n continue contenir la valeur 2 jusqu' ce qu'une nouvelle valeur lui soit assigne.

Les rgles de dclaration de uarable


Vous pouvez initialiser une variable dans la dclaration elle-mme
.

I DcIare une nouvelle variable int | et lui donne I eomne valeur initiale int o = 1l
/ |

Ghapitre 3 : Dclarer des variables de type

valeur 3 5

Ce qui revient mettre la valeur 1 dans votre nouveau dossier suspendu au moment o vous le mettez dans I'armoire, plutt que de le mettre d'abord pour le rouvrir ensuite et y mettre la valeur.

Vous pouvez dclarer une variable n'importe o (en fait, presque n'importe o) clans un programme, mais vous ne pouvez pas utiliser une variable avant de I'avoir dclare et de lui avoir donn une valeur. Les deux instruc-

tions suivantes sont donc illicites

ll Ce qui suit est illicite car n n'a pas reu ll une valeur avant d'tre utilise int m; n=n; ll Ce qui suit est illicite car p n'a pas t ll dclare avant d'tre utilise
p = 2i

int

pi

Enfin, vous ne pouvez pas dclarer deux fois la mme variable.

Uariations sur un thme : des


tapes
La plupart des variables simples sont de type

int

de dffrents

int. C# en offre un certain nombre de variantes pour quelques occasions particulires.


Toutes les variables de type int sont limites aux nombres entiers. Le type int souffre aussi d'autres limitations. Par exemple, une variable de type int ne peut stocker de valeur qu' I'intrieur de l'tendue -2 milliards

1t$!Qa^

;L t 'rilliards.
't

^.v7atr L'tendue exacte est de -2 147 483 648 2 147 483 647. :(dqfl ) \/

;l -\

\J/

^s}K\ ^..?r vra \-/


\E/"
)

Deux milliards de centimtres reprsentent peu prs la rnoiti de la circonfrence de la Terre.

36

Deuxime partie: Programmation lmentaire en C#

Au cas o 2 milliards ne vous suffirait pas, C# offre un type d'entier nomm iong (abrviation de long int) qui peut contenir un nombre entier beaucoup plus long. Le seul inconvnient du type long est qu'il occupe plus de place dans votre armoire : un entier de type long consomme huit octets (64 bits), soit deux fois plus qu'un int ordinaire.

.r$c ./ t(7, Y

Autrement dit, un entier iong occupe deux dossiers d'une capacit d'un d'armoire dossiers suspendus commence tre un peu use, je parlerai partir de maintenant en octets.

HintclansvotrearmoiredossierSSuSpendus'Commecettemtaphore

Un entier l-ong reprsente un nombre entier qui peut aller approximative-

ment de -1Ots 101e.

'qg,
Type sbyte byte

^.rr7p7\ L'tendue

1e${Qa.

exacte d'un entier long est de -9 223 372 036 854 775 808

9 223 372 036 854 775 807.

C# offre plusieurs autres types de variable entire montrs par le Tableau 3.1.

Tableau 3.1 : Taille et tendue des types entiers en C#.


Taille
1

(octets)

tendue
-128 127

Exemple sbyte sb = 12; byte b = 12;

0255
-32,768 32,767 0 65,535 -2 milliards 2 milliards 0 4 milliards
-101s

short
ush ort

2 2

short Sr = 123456', ushort usil = 62345678;

int
uint
long ulong

4 4
B B

1nt

n = 1234567890;

uint un = 3234567890U
long | = 1234567890121 long ul = 123456789012U1

101e

(beaucoup)

1020

Comme je I'expliquerai dans la section "Dclarer des constantes numriques", plus loin dans ce chapitre, une valeur fixe telle que 1 a aussi un type. Par dfaut, une constante simple comme 1 est considre de type int. Une constante de type autre que int doit tre marque par son type. Par exemple, l23U est un entier non sign (unsigned), de type ui irt.

Chapitre 3 : Dclarer des variables de type valeur

37

La plupart des variables de type entier sont signes, ce qui signifie qu'elles ont un signe (+ ou -), et qu'elles peuvent donc reprsenter des valeurs

ngatives. Un entier non sign ne peut reprsenter que des valeurs positives, avec I'avantage de pouvoir contenir une valeur deux fois plus leve. Comme vous pouvez le voir dans le Tableau 3.1, les noms de la plupart des types d'entier non sign commencent par u (pour unsigned), alors que les types signs n'ont gnralement pas de prfixe.

Reprsenter des fractions


Les entiers conviennent trs bien pour la plupart des calculs. mais beaucoup font intervenir des fractions, qui ne peuvent tre reprsentes par des nombres entiers. L'quation toute simple qui convertit en degrs Celsius une temprature exprime en degrs Fahrenheit met le problme en vidence :

// Conversion de 1a temprature 41 degrs int nFahr = 41; int nCelsius = (nFahr - 32) - (5 / 9)

Fahrenheit

Cette quation fonctionnera trs bien en nombres entiers pour certaines valeurs. Par exemple, 4l degrs Fahrenheit quivaut 5 degrs Celsius. Essayons une autre valeur : 100 degrs Fahrenheit. Selon notre quation, 100-32 = 68;68 fois 519 = 37. C'est faux:la bonne rponse est 37,78. Mais cette rponse est encore fausse car le rsultat exact est 37 ,77 7. . . avec des
7

jusqu' l'infini.

Une variable int ne peut reprsenter que des nombres entiers. L'quivalent -9t4K entier i( de 37,78 est 37. Cette manire d'escamoter la partie dcimale d'un ilfi ) nombre pour le faire tenir clans une variable entire s'appelle tronquer. \g)

=l

tg9!Ra" Tronquer n'est pas la mme chose qu'arrondir. Tronquer consiste S7^l \ supprimer la partie dcimale, alors qu'arrondir consiste prendre la

Sf f i({",'

valeur entire la plus proche. Ainsi, tronquer 1,9 donne donne la valeur 2.

1.

Arrondir

1,9

Pour une temprature, 37 peut tre une approximation satisfaisante. On ne va pas mettre une chemise manches longues 37 degrs et une chemise manches courtes 37,7 degrs. Mais pour bien des applications, sinon presque toutes, il est inacceptable de tronquer un nombre.

38

Deuxime partie : Programmation lmentaire en

G#

:HW

plus compliqu que a. Une variable de type int n peut pas 1r$!Qa" En fait, c'est \ stoker la fraction 519 qui correspond toujours pour elle la valeur 0. Il en ^v71t7 ) rsulte que l'quation de notre exemple ci-dessus calcule la valeur 0 pour \/ nCelsius pour toutes les valeurs de nFahr. On est bien oblig d'admettre que c'est inacceptable.
Sur le site Web, le dossier ConvertTernperatureWithRound0ff, contient un programme de conversion de temprature avec des variables de type int. ce stade, vous n'en comprendrez peut-tre pas tous les dtails, mais vous pouvez jeter un coup d'il aux quations et excuter le programme Ciassl . exe pour voir les rsultats qu'il produit.

Utliser des tlariables en tlirgule flottante


Les limitations d'une variable de type int sont inacceptables pour la plupart des applications. L'tendue n'est gnralement pas le problme : un entier long de 64 bits des chances d'tre suffisant pour tout le monde. Ce qui est difficile avaler, c'est de n'avoir droit qu'aux nombres

entiers.
Dans bien des cas, vous aurez besoin de nombres dont la partie dcimale n'est pas nulle. Les mathmaticiens les appellent les nombres rels. J'ai

toujours trouv a ridicule. Y a-t-il des nombres entiers qui soient irrels

=( UA )

./

-sq4<o
i:

N7/

Remarquez que j'ai dit qu'un nombre rel peut avoir une partie dcimale non nulle, mais ce n'est pas obligatoire. Autrement dit, 1,5 est un nombre rel, mais 1,0 galement. Par exemple, 1,0 + 0,1 gale 1,1. Un nombre rel plus un nombre rel donne un nombre rel. Gardez cela en tte pour la suite de ce chapitre.

Heureusement, C# connalt les nombres rels. Il y en a de deux sortes : dcimaux et virgule flottante. Le type le plus courant est virgule flottante. Je dcrirai le type decimal un peu plus loin dans ce chapitre.

Dclarer une tlarable r/rgule flottante


Une variable en virgule flottante est de type f 1oat, et vous pouvez la

dclarer de la faon suivante

float f =

1.0:

Ghapitre 3 : Dcf arer des variables de type valeur

39

Une fois dclare comme f 1oat, la variable f est de type f loat pour toutes les instructions qui vont s'y appliquer. que la virgule dcimale \eENrgr. Un nombre virgule flottante doit son nom au fait "flotter" y gauche est autorise lieu de droite au de se trouver un S/^T \ permet fixe. Il reprsenter emplacement donc de aussi bien 10,0 que 1,00 =ldl\y / \/ ou 0,100, ou tout ce que I'on peut imaginer.
.

Le Tableau 3.2 donne I'ensemble des types de variable en virgule flottante. Tous ces types sclnt signs, ce qui veut dire qu'une variable en virgule flottante peut recevoir une valeur ngative aussi bien que positive.

Tableau 3.2: Taille et tendue des types de variable en virgule flottante.


Type

Taille [octets]
B

Etendue
1.5

Prcision
3.4

Exemple

float double

* 10'45

* 1038
10308

6-

16

* 5.0 10324

x 1.7

chiffres 15 - 16 chiffres
7

float f

= 1.2F;

double d = 1.2:

-$tf,

Le type par dfaut pour une variable en virgule flottante est double et

nonr :.--'
Dans le Tableau 3.2, la colonne Prcision contient le nombre de chiffres exacts pour chaque type de variable virgule flottante. Par exemple, la fraction 5/9 vaut exactement 0,555... avec une infinit de 5. Mais une variable de type f loat contient un certain nombre de chiffres exacts, ce qui veut dire que les chiffres qui se trouvent au-del du sixime ne le sont pas ncessairement. Aussi, exprim dans le type f loat, 5/9 pourrait trs

bien apparatre comme ceci


0,5555551457382

Vous savez donc que tous les chiffres apparaissant aprs le sixime 5 ne peuvent pas tre considrs comme exacts.

-rK

'qg,

Une variable de type f loat possde en fait 6,5 chiffres exacts. Cette valeur trange vient du fait que la prcision en virgule flottante est donne par un calcul qui fait intervenir 10 puissance un logarithme en base 2. Voulez-vous vraiment en savoir plus ?

40

Deuxime partie : Programmation lmentaire en

G#

Avec une variable de type double, la mme fraction 5/9 pourra apparaltre de cette facon :

0.t 23 t555555555555555 iB23


Le type double possde quant lui entre

l5 et

16 chiffres exacts.

.st 1 Itgrf s---'r


-

Comme en C# une variable virgule flottante est par dfaut en double une raison particulire de ne pas le faire. Toutefois, qu'il utilise le type ' ' 1e ou le type f 1oat, on dira toujours d'un programme qu'il travaille douD

Hprcision(letypedoub1e),vouspouveZutilisercetype,moinsd'avoir
en virgule flottante.

Conturtissons encnre quelques tempratures


Voici la formule qui permet de convertir en degrs Celsius une temprature en degrs Fahrenheit en utilisant des variables en virgule flottante :
double dCelsius

= (dFahr - 32.0) * (5.0

g,O)

*rt't${ Le site Web contient une version en virgule flottante, nomme ;d F1$? convertTernperaturewithFloat, du programme de conversion de temorature' !=,
L'exemple suivant montre le rsultat de I'excution du programme Con,zertTemner-af rrre'u,/lthF1oat, utilisant des variables de type double:
Entrez 1a tenprature en degrs Fahrenheit: 100 Temprature en degrs Celsius - 37.777771777711779 Appuyez sur Entre pour quitter le progranme..,

C'est mieux que les problmes de robinet que I'on apprenait rsoudre

cole primaire.

Quel4ues linitations des tlarables en rgule


f lottante
Vous pouvez tre tent d'utiliser tout le temps des variables virgule flottante parce qu'elles rsolvent le problme des nombres tronqus. Il

Chapitre 3 : Dclarer des variables de type valeur

4l

est vrai qu'elles utilisent plus de mmoire, mais de nos jours la nrmoire ne cotte pas cher. Alors, pourquoi pas ? Mais les variables virgule flottante ont aussi des limitations.

Utiliser une t/ariable comme compteur


Vous ne pouvez pas utiliser une variable virgule flottante comme compteur. En C#, certaines structures ont besoin de compter (comme dans 1, 2,3, et ainsi cle suite). Nous savons tous que 1,0,2,0, et 3,0 font aussi bien que 1,2,3 pour compter, mais C# ne le sait pas. Comment ferait C# pour savoir si vous voulez dire l0 000 001 ou 10 000 000 ?
Que vous trouviez ou non cet argument convaincant, vous ire pouvez pas

utiliser une variable virgule flottante comme compteur.

Comparer des nombres Il faut tre trs prudent quand on compare des nombres en virgule flottante. Par exemple, 1.2,5 peut tre reprsent comme 12,500001. La plupart des gens ne se proccupent pas de la prcision de ce petit 1 supplmentaire, mais un ordinateur prend les choses de faon extrmement littrale. Pour C#, 12,500000 et 12,500001 ne sont pas du tout la
mme chose.

Aussi, si vous ajoutez l,l ce que vous voyez comme 1,1, vous ne pouvez pas savoir a priori si le rsultat est2,2 ou 2,200001. Et si vous demandez "dDoubleVariabie est-elle gale 2,2 ?", vous n'aurez pas forcment le rsultat que vous attendez. En gnral, vous allez devoir vous en remettre une comparaison un peu factice comme celle-ci : "La valeur absolue de la diffrence entre dDoubleVariable et 2,2 est-elle

infrieure 0,000001 ?"


Le processeur Pentium a une astuce pour rendre ce problme moins gnant : ^cj!t_Qa^ 4/1!;l\ il effectue les calculs en virgule flottante dans un format particulirement :HW ) tong, c'est--dire qu'il utilise 80 bits au lieu de 64. Quand on arrondit un \/ nombre en virgule flottante de 80 bits pour en faire une variable de type f loat de 64 bits, on obtient (presque) toujours le rsultat attendu, mme s'il y avait un ou deux bits errons dans le nombre de 80 bits.

42

Deuxime partie: Programmation lmentaire en

C#

La ttitesse de calcul
Les processeurs de la famille x86 utiliss par les PC un peu anciens fonctionnant sous Windows faisaient les calculs arithmtiques en nombres entiers beaucoup plus vite que les mmes calculs avec des nombres en virgule flottante. De nos jours, ce serait sortir de ses habitudes pour un programmeur que de limiter son programme des calculs arithmtiques en

nombres entiers. Avec le processeur Pentium III de mon PC, pour un simple test (peut-tre trop simple) d' peu prs 300 millions d'additions et de soustractions, le rapport de vitesse a t d'environ 3 1. Autrement dit, pour toute addition en type double, j'aurais pu faire trois additions en type int (les calculs comportant des multiplications et des divisions donneraient peuttre des rsultats diffrents).

Ae/!!\ viter les effets de cache. Le programme et les donnes taient mis en 9(dW ) .a.he, mais le compilateur ne pouvait mettre en cache dans les registres \/ du CPU aucun rsultat intermdiaire.
Une tendue pas si limite
Dans le pass, une variable en virgule flottante pouvait possder une tendue beaucoup plus large qu'une variable d'un type entier. C'est toujours le cas, mais l'tendue du type long est assez grande pour rendre la question pratiquement dpourvue d'intrt.

1t${Qa^ J'ai d crire mes oprations d'addition et de soustraction de manire

f\ =Q)

Mme si une variable de type f loat peut reprsenter un assez grand nombre, le nombre de chiffres exacts est limit. Par exemple, il n'y aura pas de diffrence entre 123456789F et 123456000F.

Utliser le tupe d e c im aI, hr4brde d'entier et de rgule f lottante


Comme je I'explique dans les sections prcdentes de ce chapitre, les types entiers comme les types en virgule flottante ont chacun leurs inconvnients. Une variable en virgule flottante a des problmes d'arrondi, ainsi que des limites de prcision, alors qu'une variable 1nt fait

Chapitre 3 : Dcf aret des variables de type valeur

43

tout simplement sauter la partie dcimale du nombre. Dans certains cas, il vous faudra une variable qui combine le meilleur de ces deux types :

t/ t/

Comme une variable en virgule flottante, pouvoir reprsenter une

fraction.
Comme une variable entire, offrir une valeur exacte utilisable dans des calculs. Par exemple, 12,5 est effectivement 12,5, et non
12,500001.

Heureusement, C# offre un tel type de variable, nomm decimai. Une variable de ce type peut reprsenter tout nombre compris entre 10t8 et 1028 (a fait beaucoup de zros). Et elle le fait sans problmes d'arrondi.

Dclarer une ttariable de ttlpe

d e c 1ma 1
:

Une variable de type decimal se dclare comme n'importe quelle autre decinal decinal decinal

rn1:
m2

= 100; rn3 = 100M:

/l ll ll

Bien
Mieux Eneore nieux

La dclaration de m1 dfinit une variable mi sans lui donner une valeur

initiale. Tant que vous ne lui aurez pas assign une valeur, son contenu est indtermin. C'est sans importance, car C# ne vous laissera pas utiliser cette variable tant que vous ne lui aurez pas donn une valeur.
La seconde dclaration cre une variable rn2 et lui donne 100 comme valeur initiale. Ce qui n'est pas vident, c'est que 100 est en fait de type int. C# doit donc convertir cette valeur de type int n type decimal avant de I'initialiser. Heureusement, C# comprend ce que vous voulez dire et effectue la conversion pour vous. La dclaration de m3 est Ia meilleure des trois. Elle initialise m3 avec la constante de type decimal 100M. La lettre M la fin du nombre signifie que la constante est de type decimal. $/oyez la section "Dclarer des

constantes numriques", plus loin dans ce chapitre.)

44

euxime partie : Programmation lmentaire en C#

Conparer les trlpes

dec

ima 1,

int, et f1 o at

Une variable de type dec imal semble avoir tous les avantages et aucun des inconvnients des types int et double. Elle a une trs grande tendue, elle n'a pas de problmes d'arrondi, et 25,0 y est bien 25,0 et non 25,00001. Le type decimai a toutefois deux limitations significatives. Tout d'abord,

une variable de type decimal ne peut pas tre utilise comme compteur, car elle peut contenir une partie dcimale. Vous ne pouvez donc pas vous en servir dans une boucle de contrle de flux, comme je I'explique au Chapitre 5. Le second inconvnient du type decirnal- est tout aussi srieux, ou mme plus. Les calculs effectus avec des variables de ce type sont significativement plus lents que ceux effectus avec des variables de type int ou f1cat. Je dis bien "significativement". Mon test simple de 300 millions d'additions et de soustractions a t peu prs cinquante fois plus long qu'avec le type int, et je souponne que ce rapport serait encore plus dfavorable avec des oprations plus complexes. En outre, la plupart des fonctions de calcul, comme les exponentielies ou les fonctions trigonomtriques n'admettent pas le type decimai.

Il est clair que le type decinal convient trs bien auxapplications comme la comptabilit, pour lesquelles la prcision est trs importante mais le nombre de calculs relativement rduit.

Soqons loqque, examinons le tqtte b o o 1


Enfin, un type de variable logique. Une variable du type boolen bool

peut prendre deux valeurs : true ou f alse (vrai ou faux). Je parle srieusement : un type de variable rien que pour deux valeurs.

:(dw

tgq\Qa. Les programmeurs C et C** 6n1 I'habitude d'utiliser une variable i-nt avec ^t7q \ la valeur 0 (zro) pour signif ier f a 1 s :,r, et une valeur autre que zro pour ) signifier tru.e. a ne marche pas en C#. \/
Une variable boo 1 se dclare de la faon suivante bool variableBool = true:
:

Chapitre 3 : Dclarer des variables de type valeur

45

Il n'existe aucun chemin de conversion entre une variable bool et tous les autres types. Autrement dit, vous ne pouvez pas convertir directement une variable bool en quelque chose d'autre (et mme si vous pouviez, vous ne devriez pas, parce que a n'a aucun sens). En particulier, vous ne pouvez pas transformer une variable bool en int (par exemple sur la base du principe que false devient zro), ni en string (par exemple sur la base du principe que false devient "fa1se"). Toutefois, une variable de type bool joue un rle important pour forcer I'excution d'un programme C# suivre tel ou tel cheminement, comme je I'explique au Chapitre 5.

Un coup d'l aur types caractre


Un programme qui ne fait rien d'autre que cracher des nombres peut convenir trs bien des mathmaticiens, des comptables, des assureurs qui font des statistiques, et des gens qui font des calculs balistiques (ne riez pas, les premiers ordinateurs ont t construits pour gnrer cles tables de trajectoires d'obus I'usage des artilleurs). Mais pour la plupart des applications, les programmes doivent pouvoir traiter des lettres aussi bien que des nombres. C# dispose de deux manires diffrentes de traiter les caractres : titre individuel (par le type char), et sous forme de chalnes (par le type string).

La turiable de tupe char


Une variable de type char est une bolte qui peut contenir un seul caractre. Une constante caractre apparalt tel un caractre entour d'apostrophes,

comme dans cet exemple


char c - tat;

Vous pouvez y mettre n'importe quel caractre de I'alphabet latin, hbreu, arabe, cyrillique, et bien d'autres. Vous pouvez aussi y mettre des caractres japonais Katakana ou bien des caractres Kanji, chinois ou japonais.
En outre, le type char peut tre utilis comme compteur, ce qui veut dire que vous pouvez avoir recours une variable char pour contrler les structures de boucle que je dcrirai au Chapitre 5. Une variable caractre

ne peut avoir aucun problme d'arrondi.

46

Deuxime partie : Programmation lmentaire en C#

^nu1$Q( trs bien y stocker un caractre Kanji que vous trouvez trs beau, mais +/ .--., \
t^-.

Une variable de type char n'est pas associe une police. Vous pouvez
si

tO / V,,/

vous I'affichez, il ne ressemblera rien si vous ne le faites pas avec la bonne police.

Tqpes

char s1rciaur

Certains caractres, selon la police utilise, ne sont pas imprimables, au sens o vous ne voyez rien si vous les affichez l'cran ou si vous les imprimez avec votre imprimante. L'exemple le plus banal en est I'espace, reprsent par ' '. Il y a aussi des caractres qui n'ont pas d'quivalent sous forme de lettre (par exemple, le caractre de tabulation). Pour reprsenter ces caractres, C# utilise la barre oblique inverse, comme le montre le Tableau 3.3.

Tableau 3.3 : Caractres spciaux.


Gonstante caractre

Valeur nouvelle ligne

'v'
'\0'

tabulation caractre null retour chariot barre oblique inverse

'\r' '\\'

Le trlpe
Le type

string
:

srring est galement d'usage courant. Les exemples suivants montrent comment dclarer et initialiser une variable de type st ring
I

I dclaration puis initialisation I initialisation avec 1a dclaration


une chaine";

string soneStringl;
soneStringl = "eeci est une chane";
I

string soneString2 = "ceci est

Chapitre 3 : Dclarer des variables de ttpe valeur

47

Une constante de type chne est une chalne de caractres entoure de guillemets. Une chalne peut contenir tous les caractres spciaux du Tableau 3.3. Une chalne ne peut pas s'tendre sur plus d'une ligne dans Ie fichier source C#, mais elle peut contenir le caractre de retour la ligne, comme le montre I'exemple suivant :
I I eeci est illicite string soneString * "Ceci est une ligne et en voil une autre"; I I nais ceci est autoris stri.ng sonestring = "Ceci est une ligne\net en voil une autre";

A I'excution, la dernire ligne de I'exemple ci-dessus crit les deux membres de phrase sur deux lignes successives :
Ceci est une ligne et en voil une autre

Une variable de type string ne peut pas tre utilise comme compteur, et ce n'est pas un type contenant une valeur. Il n'existe aucune "chalne" qui ait une signification intrinsque pour le processeur. Seul un des oprateurs

habituels est utilisable avec un objet de type string:l'opratur la concatnation de deux chalnes en une seule. Par exemple : string s

+ effectue

- "Ceei est un nenbre de phrase" * " et en voil un autrerr;

Ce code place dans la variable

string

s la chalne suivante

'rteci est

un nembre de uhrase

t en voiL un autre'l

Encore un mot : une chane ne contenant aucun caractre (que I'on crira "") est une chalne valide.

^ Conparer

string t char
ring est trs

Bien qu'une chane soit constitue de caractres, le type st

diffrent du type char. Naturellement, il existe quelques diffrences triviales. Un caractre est plac entre apostrophes, comme ceci :
la
|

48

Deuxime partie : Programmation lmentaire en G#

alors qu'une chalne est place entre guillemets


"ceci. est une chalne"

Les rgles qui s'appliquent aux chalnes ne sont pas les mmes que celles qui s'appliquent aux caractres. Pour commencer, une variable char ne contient par dfinition qu'un seul caractre. Le code suivant, par exemple, n'a aucun sens :
char cl l1. char c2 = rht. char c3 =c1*c2 En fait, ce code peut presque se compiler, mais avec une signification compltement diffrente de I'intention initiale. Ces instructions provo-

'qE,

^"tK

quent la conversion de c1 en une variable int qui reoit la valeur numrique du caractre initialement contenu dans c 1, puis la mme chose pour c2, eT finalement I'addition de ces deux entiers. L'erreur se produit lorsque I'on essaie de stocker le rsultat dans c3. Une valeur numrique de type int peut pas tre stocke dans une variable de type char, par dfinition plus petite. En tout cas, cette opration n'a aucun sens.
Une chane, en revanche, peut avoir une longueur quelconque, et la concatnation de deux chalnes a un sens :
_+_.i-^ Lr{rr a+r'.ina L!rrr ^1 r 1 4 : = il^ilr a llhll u
, . t

strjnq

s3

= s1 i s2; l lTe rsultat est

"ab"

Il y a dans la bibliothque de C# une collection entire d'oprations sur les chalnes. Je les dcrirai au Chapitre 9.

Conventions sur les noms


La programmation est assez di{-ficile sans que les programmeurs la rendent plus difficile encore. Pour rendre votre code sourceC# plus lisible, adoptez une c0nvention sur les noms, et tenez-vous-y. ldalement, cette convention doit tre aussi proche que possible de celle adopte par la plupart des programmeurs C#.

Chapitre 3 : Dclarer des variables de type valeur

49

La rgf e gnrale estque le nom d'un objet autre qu'une variable doit commencer par une lettre

majuscule. Choisissez des noms aussi descriptifs que possible, ce qui signifie bien souvent des noms composs de plusieurs mots. Chacun de ces mots doit commencer par une majuscule {sauf le premier dans le cas d'une variable}, et ils doivent tre colls les uns aux autres, sans tre relis par des tirets de soulignement, comme ceci : ceciEsrUnlcngNomDeVariable

J'ai aussi adopt la rgle supplmentaire selon laquelle la premire lettre du nom d'une variable en indique le type. La plupart de ces lettres sont assez explicites : f pour f 1oat, d pour double, s pour string, et ainsi de suite. La seule qui ncessite un petit effort de
mmoire est n pour int. ll y a toutefois une exception cette rgle : pour des raisons qui remontent au langage FOfiTRAN des annes soixante, les lettres i, j, et k sont couramment utiliss seules comme nom pour les variables de type int. Remarquezque j'aiditavoir"adopt" cette convention. Ce n'estpas moiquil'aiinvente, mais quelgu'un qui travaillait chez Microsoft l'poque du langage C. Comme il tait d'origine hongroise, rette convention a reu le nom de notation hongroise.
La notation hongroise semble ne plus tre en faveur. Je continue toutefois la prfrer, car elle me permet de connatre du premier coup d'il le type de chaque variable dans un programme sans avoir me rfrer la dclaration.

Qu'est-ce qu'un tqpe aleur

Toutes les instructions C# doivent tre traduites en instructions machine du processeur utilis. Dans le cas du PC, un processeur Intel. Ces processeurs utilisent aussi la notion de variable. Par exemple, le processeur Intel comporte huit emplacements internes, appels registres, dont chacun peut stocker un int. Sans trop entrer dans les dtails, je dirai simplement que les types dcrits dans ce chapitre, I'exception de decimal et strlng, sont intrinsques au processeur. Aussi, il existe une instruction machine qui signifie : "Ajouter un int un autre inr", et une instruction semblable pour ajouter un double un double. Comme ces types de variable sont intgrs au fonctionnement du processeur, on les appelle des types intrinsques.
En outre, les types de variable que je dcris dans ce chapitre sont de longueur fixe (encore une fois, I'exception de string). Une variable de longueur fixe occupe toujours la mme quantit de mmoire. Ainsi, si j'cris I'instruction a : b, C# peut transfrer la valeur de b dans a sans prendre

50

Deuxime partie : Programmation lmentaire en G#

des mesures spciales pour traiter un type de donne de longueur variable.


Un type de variable qui a cette caractristique est appel typu ualeur.

.6

Les types int, double, L-.oii, et leurs drivs immdiats, comme int non sign, sont des types de variable intrinsques. Les types de variable intrinsques, ainsi que Ie type decinal, sont aussi appels types valeur. Le type string rr'est ni I'un ni I'autre. Les types dfinis par le programmeur, que je dcris au Chapitre 6, ne sont ni des types valeur, ni des types intrinsques.

Dclarer des constantes nutnriques


-of4q ^-/ -t- \ ':-/ -i ) I oute expressron II
Bien qu'il y ait trs peu d'absolus dans la vie, je vais vous dire quelque chose d'absolu sur C# :
a Lrne valeur et un type.

Dans une dclaration comme int n, vous pouvez facilement voir que la variable n est de type int, et vous pouvez raisonnablement supposer que le calcul n * 1 est de type int. Mais quel est le type de la constante 1 ?
Le type d'une constante dpend de deux choses : sa valeur, et la prsence optionnelle d'une lettre descriptive la fin de celle-ci. Tout nombre entier infrieur 2 milliards est suppos tre de type int. Un nombre entier suprieur 2 milliards est suppos tre de type 1ong. Tout nombre en virgule flottante est sutrtpos tre de type double. Le Tableau 3.4 prsente des constantes dclares pour tre d'un type particulier. La lettre qui sert de descripteur peut tre aussi bien en

majuscule qu'en minuscule. tu et 1u sont quivalents.

Tableau 3.4 : Constantes dclares avec leur type.


Constante
1

Type
Lnt rinqi onod .),'.- * int

iU
1L

long int
d o ui:,

1.0

1e

Ghapitre 3: Dclarer des variables de type

valeur 5 |

Constante
1.0F
1M

Type
f!IUAL I ^-r_

decimal bool bool


^t^LllAt

t rue
^1^^ I.1 LbC
r-l a

'\n'

char (caractre de nouvelle ligne) char (caractre dont la valeur numrique est hex
123)

'\x123' "a string"

string
s

t r ins (chane vide)

Lnanger ae rype

' '

.'

le cast

Un tre humain ne traite pas de manire diffrente les diffrents types de nombre. Par exemple, un individu normal (contrairement un programmeur C# distingu tel que vous) ne se demande pas si le nombre 1 est sign, non sign, court ou long. Bien que C# considre ces divers types comme diffrents, il n'ignore pas qu'il existe une relation entre eux. Par exemple, le code suivant convertit une variable de type int r long :

int

nValue = 10;

long lValue; 1Va1ue = nValue;

//

ceci fonctionne

int peut tre convertie en long car toute valeur peut tre stocke dans une variable de type long et que I'un comme I'autre type peut tre utilis comme compteur.
Une variable de type

lnt

Toutefois, une conversion dans la direction oppose peut poser des problmes. Par exemple, ce qui suit est illicite :
long lValue =
10;
I

int

nValue;

nValue =

lValue;

ceei est

illicite

52

Deuxime partie : Programmation lmentaire en C#

Certaines valeurs que l'on peut stocker dans une variable de type long sont trop grandes pour une variable int (par exemple, 4 milliards). Dans ce cas, C# gnre une erreur, car des informations pourraient tre perdues dans la conversion. Ce type de bogue est trs difficile identifier. Mais si vous savez que la conversion est possible ? Par exemple, bien que lVa1ue soit de type 1cng, peut-tre savez-vous que sa valeur ne peut pas dpasser 100 dans ce programme. Dans ce cas, la conversion de la variable de type long iValue en variable nValue de type int ne poserait aucun

problme.
Vous pouvez dire C# que vous savez ce que vous faites en utilisant un
CqST:

long

1Va1ue

10;

int

nValue;

nValue

= (int)lValue; ll naintenant a marche

Dans un cast, vous placez entre parenthses le nom du type que vous voulez obtenir, juste avant le nom de la variable convertir. Le cast cidessus dit : "Convertir en int la valeur de 1Va1ue. Je sais ce que je fais."
Un nombre qui peut tre utilis comme compteur peut tre converti automatiquement en type f 1oar, mais la conversion d'un nombre en virgule flottante en nombre pouvant servir de compteur ncessite un cast:
double dValue = 10.0;

long lVa1ue = (1ong)dValue;

Toute conversion de et vers le type decimal ncessite un cast. En fait, tout type numrique peut tre converti en n'importe quel autre type numrique par I'application d'un cast. Ni le type bool ni le type strlng ne peuvent tre convertis directement en un autre type, quel qu'il soit.

Ae/!!\ un caractre ou un type boolen en son quivalent de type string. Par :(dq9 ) vous po,rrr, convertir la valeur de type bool true pour en faire "r"-ple, \/ la valeur string "true", mais on ne peut pas considrer cela comme une
conversion directe. La valeur boolenne choses absolument diffrentes.

1t$!ea.

C# comporte des fonctions intgres qui peuvent convertir un nombre,

true et la chane "true" sont

des

Chapitre 4

Les op rateurs sont sympas


Dans ce chapitre :
Faisons un peu d'arithmtique. Faisons des comparaisons.

Aller plus loin avec des op:rateurs logiques.

es mathmaticiens crent des variables et les manipulent de diffrentes manires. Ils les additionnent, les multiplient, parfois mme les intgrent. Le Chapitre 2 explique comment dclarer et dfinir des variables, mais il ne dit rien sur la manire de les utiliser afin d'en faire quelque chose. Ce chapitre examine les oprations que I'on peut excuter avec des variables pour raliser effectivement quelque chose.

^qa/ ff"ll \f/

crire un programme qui fait vraiment quelque chose, c'est bien. Si vous n'y arrivez pas, vous ne devien drezjamais un vritable programmeur C#, moins, bien str, que vous ne soyez un consultant, comme moi.

Fare de l'arithnt4ue
L'ensemble des oprateurs arithmtiques est divis en plusieurs groupes : Ies oprateurs arithmtiques simples, Ies oprateurs d'assignation, et un groupe d'oprateurs spciaux, propres la programmation. Une fois que vous les aurez digrs, il vous faudra faire de mme pour un autre ensemble d'oprateurs : les oprateurs logiques.

5tt

Deuxime partie : Programmation lmentaire en G#

Les oprateurs smples


Les oprateurs simples sont pour la plupart ceux que vous avez appris l'cole primaire. Le Tableau 4.1 en donne la liste :

Tableau 4.1 : Les oprateurs simples.


0prateur
- (moins unaire)

Signification
prendre le ngatif de la valeur multiplier

I
+

diviser additionner
so ustra i re

- (moins binaire)
%

modulo

La plupart de ces oprateurs sont appels oprateurs binaires, parce qu'ils oprent sur deux valeurs : celle qui se trouve du ct gauche de I'oprateur et celle qui se trouve du ct droit. La seule exception est le moins unaire, mais il est aussi simple que les autres :

int nl * 5; int n2 = -nl ;

n2 a naintenant 1a valeur -5

La valeur de - n est le ngatif de la valeur de n.

L'oprateur modulo vous est peut-tre moins familier que les autres. C'est tout simplement le reste d'une division. Ainsi, 5 % 3 vaut 2, et 25 % 3 vaut 1e$!Qa. rqr,/-t \

1(25-3.8).
La dfinition stricte de I'oprateur
7u

'qE,

est : "x = (x I y) * x

Yo

y"

Les oprateurs arithmtiques autres que modulo sont dfinis pour tous les types numriques. L'oprateur modulo n'est pas dfini pour les types en virgule flottante, car une division effectue en virgule flottante n'a pas de reste.

Ghapitre 4 : Les oprateurs sont sympas

55

0rdre d'excution des oprateurs


Il arrive que le sens d'une expression arithmtique ne soit pas parfaitement clair. Par exemple :

intn:5
Est-ce que le programmeur veut dire "multiplier 5 par 3 et ajouter 2", ce qui fait 17, ou bien "multiplier 5 par la somme de 3 et 2", ce qui fait 25 ?

S/ =(

-efr{c j5:

\ $g ,

C# effectue I'excution d'une suite d'oprateurs de gauche droite. Le rsultat de I'exemple ci<lessus est donc I'assignation de la valeur 17 la variable n.

Dans I'exemple suivant, C# dtermine la valeur de n en commenant par diviser 24 par 6, puis en divisant le rsultat de cette opration par 2 (et non en divisant 24 par le rsultat de la division de 6 par 2) :

intn*241612
D'autre part, les oprateurs ont une hirarchie, ou ordre de priorit. C# commence par examiner I'expression, et excute les oprations en commenant par celle qui a le niveau de priorit le plus lev. Dans mes prcdents livres, je me suis donn le plus grand mal pour expliquer la priorit des oprateurs, mais je me suis depuis rendu compte que ce n'tait qu'une perte de temps (et de neurones). Il vaut toujours mieux se dbarrasser de la question de la priorit des oprateurs en utilisant les parenthses.
De cette faon, la valeur de I'expression suivante est claire, quel que soit

I'ordre de priorit des oprateurs

int

n = (7

3) * (4 + (6 / 3));

C# commence par valuer I'expression qui se trouve dans le bloc de

parenthses le plus profondment enfoui

int

n = (7

3)

(4 + 2);

Cela fait, il remonte vers le bloc de parenthses le plus englobant, en valuant chaque bloc I'un aprs I'autre :

int n = 1 *

6;

56

Deuxime partie : Programmation lmentaire en

C#

Pour arriver au rsultat final

intn=7
Cette rgle connat peut-tre une exception. Je trouve ce comportement intolrable, mais de nombreux programmeurs omettent les parenthses dans des exemples comme le suivant, car tout le monde sait que la priorit de la multiplication est plus leve que celle de I'addition :

intn*7*2n3;
Le rsultat de cette expression

et 13 (et non 27).

L'oprateur d'assgnation et ses (ariantes


C# a hrit de C et C++ une ide intressante : I'assignation y est un oprateur binaire. L'oprateur d'assignation a la valeur de I'argument qui est sa droite. L'assignation a le mme type que les deux arguments qui

doivent donc eux-mmes tre de mme type.


Cette nouvelle conception de I'oprateur d'assignation n'a aucun effet sur les expressions que vous avez vues jusqu'ici :
n=5

3;

Dans cet exemple, 5 * 3 vaut 15 et est de type int. L'oprateur d'assignation stocke la valeur de type int qui se trouve sa droite dans la variable de type int qui se trouve sa gauche, et retourne la valeur 15. Mais ce n'est pas tout, cette nouvelle conception de I'oprateur d'assignation autorise la forme suivante :
m

=n=5

3;

Les oprateurs d'assignation sont valus I'un aprs I'autre, de droite gauche. Dans cet exemple, le premier partir de la droite stocke la valeur 15 dans la variable n, et retourne 15. Le deuxime et dernier partir de la droite stocke la valeur l5 dans n et retourne 15, qui n'est utilis par aucun

autre oprateur.

Ghapitre 4 : Les oprateurs sont

sympas 5 7

Du fait de cette dfinition trange de I'oprateur d'assignation, les expres-

sions suivantes, bien qu'tranges, sont licites

int int
n -

n;
m; n = 1.

C# offre une extension de I'ensemble des oprateurs simples avec un ensemble d'oprateurs construits partir d'autres oprateurs binaires.

Par exemple
n

**

1;

Cette expression est quivalente :


n * n * 1.

Il existe un tel oprateur d'assignation pour pratiquement tous les oprateurs binaires. Je ne sais pas exactement comment ils sont venus au monde, mais pourtant, ils existent.

L' o p

rate ur d' i n cr tn e n tat o n


:

Parmi toutes les additions que I'on peut avoir faire dans un programme, la plus courante consiste ajouter I une variable
n

- n * 1;
:

Nous avons vu que C# offre le raccourci suivant


n *=
1;

Mais c'est encore trop compliqu. C# fait encore mieux

++n;
Les

/l

incrruente n de I n

de la valeur

trois instructions ci-dessus sont quivalentes. Chacune incrmente l.

58

Deuxime partie: Programmation lmentaire en G#

L'oprateur d'incrmentation est plutt bizarre, mais, croyez-le ou non, C# en a en fait deux:ttn, et n*t. Le premir, **n, estl'oproteur de princrmentution, et le secod, n**, estl'oprateur de postincrmentation. La diffrence est subtile, mais importante.
Souvenez-vous que toute expression a un type et une valeur. Dans I'exemple suivart, *+n et rr** sont tous deux de type int i

int
n=

n;
1;

int int

o
m

++n;

n rt = Ij. t

= n**;
m

Mais quelles sont les valeurs qui en rsultent pour un indice : c'est 1 ou 2.)

et o ? (Je vous donne

La valeur de o est 2, eTla valeur de m est 1. Autrement dit, la valeur de I'expression **n est la valeur de n aprs avoir t incrmente. alors que la valeur de I'expression n** est la valeur de n avant d'avoir t incrmente. Dans les deux cas, Ie rsultat est 2.

Sur le mmeprincipe, il y a des oprateurs de dcrmentation (n- - et - n) pour remplacer n : n 1. Ils fonctionnent exactement de la mme manire que les oprateurs d'incrmentation.

Pourquoi un oprateur d'incrmentation, et pourquoi avoir deux ?

Gn

L'obscure raison d'tre de l'oprateur d'incrmentation vientdu faitque le calculateur PDP-8

des annes soixante-dix possdait une instruction d'incrmentation. La chose serait


aujourd'hui de peu d'intrt si le langage C, I'origine de la ligne qui conduit aujourd'hui C#, n'avait pas t crit justement pour le PDP-8. Comme cette machine possdait une

instruction d'incrmentation, n++ gnrait moins d'instructions machine que n : n*1. Puisque les machines de l'poque taienttrs lentes, on ne mnageait pas ses efforts pour
faire l'conomie de quelques instructions machine.

Les compilateurs d'aujourd'hui sont plus astucieux, il n'y a davantage de diffrence entre le n*1, donc plus de besoin pour un oprateur temps d'excution de n** et celui iJe n

Ghapitre 4 : les oprateurs sont

sympas 59

d'incrmentation. Mais les programmeur$ ssnt des cratures qui ont leurs habitudes, st cet oprateur est toujours l. Vous ne verrez presque jamais un programmeur C++ incrmenter ull:rri3.ble en utilis.ant la,forme plus longue mais plus intuitive n n*1. Vous le verrez plutt utiiiser l'oprate ur d' inc rm e nttin.

D'autre part,lorsqu'on le rencontre isol {c'est--dire pas I'intrieur d'une expression plus

grandel, c'est presque toujours I'oprateur de postincrmentation qui apparat et non


I'oprateur de princrmentation. ll n'y a aucun raison cela, en dehors de l'habitude et du

faii que a l'ir plus cool.

Faire des comparasons

- est-ce logi4ue .)

C# comporte galement un ensemble d'oprateurs de comparaison

logique, montrs par le Tableau 4.2.

Tableau 4.2: Les oprateurs de comparaison logique.

b a>b a >= b a<b (= b a l= b


==

a a la mme valeur que a est plus grand que b

a est suprieur ou gal b

a est plus petit que b


a est infrieur ou gal b a n'est pas gal b

Ces oprateurs sont appels oprateurs de comparoison logique, car ils

retournent une valeur de type bool true ou false (vrai ou faux). Voici un exemple qui fait intervenir une comparaison logique
:

int n = 5; int n * 6t bool b * n )

n;

60

Deuxime partie : Programmation lmentaire en C#

Cet exemple assigne la valeur f alse la variable b, car 5 n'est pas plus grand que 6. Les oprateurs de comparaison logique sont dfinis pour tous les types numriques, notamment f 1oat, double, decimal, et char. Tout ce qui suit est licite
:

_ i i b=10M)12M;

bool b; b = 3 ) 2; b = 3.0 ) 2.0;

;;; ;;;;

Un oprateur de comparaison logique produit toujours un rsultat de type booL. Il n'est pas valide pour une variable de type string (C# offre d'autres moyens de comparer des chanes).

Conparer des nombres en Uirgule flottante : ,^ r--1 I ^r,. -.,-l I oar ! qu plus qros r a Ie
Comparer deux nombres en virgule flottante tient parfois un peu du jeu de hasard, et il faut tre trs prudent. Considrez les comparaisons suivantes
:

float f1; float f2;


fl = 1fl'

f.2 =

bool b1 = (3
1/ = tt I 4.

f.I |

3i

* f2)

==

fl;

bool b2 = (3 * f2) == fl;


b I et le calcul de b 2 est la valeur originale de f 1. Quelles sont donc les valeurs de b1 et b2 ? Lavaleur de b2 est videmment true:9/3vaut 3;3 * 3vaut 9;et 9 est gal 9.

La seule diff rence entre le calcul de

Lavaleurde bi n'est pas aussi vidente: 10/3vaut 3.333...3.333... * 3 vaut 9.999... 9.999... est-il gal 10 ? a dpend du niveau intellectuel de votre processeur et de votre compilateur. Avec un Pentium ou un processeur plus rcent, C# n'est pas assez malin pour se rendre compte que b I

Chapitre 4 : Les oprateurs sont

sympas 6 I

devrait tre true si le rsultat du calcul est un peu dcal par rapport la comparaison.

At^tJ\ Pour faire un peu mieux, vous pouvez utiliser de la faon suivante la fonction de valeur absolue pour comparer f 1 et f 2 =HlW \/ )
:

lrpU{a"

Math.Abs(d1

- 3.0 -

dZ)

.00001; //choisissez

le niveau de prcision

rrue dans les deux cas. Vous pouvez utiliser la Epsilon la place de .00001 pour obtenir le niveau de constante Double. possible. Epsilon est la plus petite diffrence plus prcision le lev type double qui ne sont pas rigoureusede variables possible entre deux
Cette fonction retourne

ment gales.

Encore plus fort


Les variables de type

: les oprateurs logques

bool disposent d'un autre ensemble d'oprateurs logiques, dfinis rien que pour elles, montrs par Ie Tableau 4'3.

Tableau 4.3: Les oprateurs logiques.

Oprateur
!a

Betourne t rue si...


a est

false
t rue

a&b

a et b sont

alb a^b
a&&b all
b

a ou b ou les deux sont a est

true

(aussi appel a eVou b)

true

ou b est

true

rTtis pas les deux (aussi appel a

xor

b)

a et b sont a ou b sont

true true

avec une valuation en court-circuit


avec une valuation en court-circuit

L'oprateur !est l'quivalent logique du signe moins. Par exemple, !a est true si a est fa1se, et false si a St true.
Les deux oprateurs suivants sont assez clairs. Tout d'abord, a & b n'est true quesi a et b sont true. Et a I b est true si a oub Sont true. Le signe n (aussi appel le ou exclusif) est un peu une bte curieuse. anb est true si a ou b sont true mais pas si a etb sont true.

62

Deuxime partie : Programmation lmentaire en

G#

Ces trois oprateurs produisent comme rsultat une valeur logique de type boo1. Les oprateurs &, | , et n existent aussi dans une version que I'on appelle oprateur de bits. Appliqus une variable de type j-nt, ils oprent bit par bit. Ainsi, 6 &3vaut 2 (01102 &00112 donne 0010t, 6 I 3vaut 7 (01102 i 00112 donne 01112), et 6 ^ 3 vaut 5 (01102 ^ 00112 donne 01012). L'arithmtique binaire est une chose extrmement sympathique, mais sort du cadre de cet

'qg,

^dK

ouvrage. Les deux derniers oprateurs logiques sont semblables aux trois premiers, mais prsentent une diffrence subtile avec eux. Considrez

I'exemple suivant

bool b = (boolExpressionl) & (boolExpression2); Dans ce cas, C# value boolExpressiorrl et boolExpression2, en cherchant dterminer si I'une et I'autre sont true pour en dduire la valeur de b. Toutefois, cet effort pourrait tre inutile. Si I'une de ces deux expressions est fa1se, il n'y a aucune raison d'valuer I'autre, car quelle qu'en soit la valeur, le rsultat de I'ensemble sera f alse.

L'oprateur sions :

&&

permet d'viter d'valuer inutilement les deux expres-

bool b = (boolExpressionl) && (boolExpressi.on2);

Dans ce cas, C# value boolExpressionl. Si elle est fa1se, b reoit la valeur f alse, et le programme poursuit son chemin. Si elle est true, C# value boolExpression2 et stocke le rsultat dans b.

\',lix lfej| Y
llu

L'oprateur && utilise ce que I'on appelle une eualuation en court-circuit, car il court-circuite si ncessaire la seconde opration boolenne. L'oprateur I i fonctionne sur le mme principe
bool b = (boolExpressionl)
Si
:

ll

(boolExpression2);

boolExpressionl est true, il n'y a aucune raison d'valuer booiExpression2, car le rsultat sera t rue de toute faon.

Chapitre 4 : Les oprateurs sont sympas

63

Troutler les mes surs : accorder les tqpes d'erpression


Dans un calcul, le type d'une expression est tout aussi important que sa valeur. Examinez I'expression suivante :
i n+ n ll lllL
. ,

n=5

*5t

7:

Ma calculatrice me dit que n vaut 32, mais cette expression a aussi un type.

Traduite en termes de types, cette expression devient

int [*] int * int * int;


Afin d'valuer le type d'une expression, suivez le mme cheminement que pour en dterminer la valeur. La multiplication a une priorit plus leve que I'addition. Un int multipli par un int donne un int. L'addition vient ensuite. Un int plus un int donne un int. On peut donc rduire I'expression ci-dessus de la faon suivante :

int*int*int int * int


int

Calculer le tqpe d'une opraton


Accorder des types suppose de creuser dans les sous-expressions. Chaque expression a un type, et les types du ct gauche et du ct droit d'un oprateur doivent correspondre ce qui est attendu par celui- ci :
typel (op) type2 @---)
tYPe3

(La flche signifie "produit".) typ" avec I'oprateur op.

I et type2 doivent tre compatibles

64

Deuxime partie : Programmation lmentaire en

G#

La plupart des oprateurs admettent diffrents types. Par exemple, I'oprateur de multiplication :

int uint long float

* decimal @- - -) * double double @---)


decimal

* long * float

* int * uint

{9---2 1nt
@- -

-) uint
decimal double

@---) long @---) float

Ainsi,2*
type

3 utilise la version

int * int

de I'oprateur

* pour produire 6, de

int.
inltlcite

Connersion de tqpe

Tout cela est trs bien pour multiplier deux int ou deux f loat. Mais qu'arrive-t-il lorsque les deux arguments ne sont pas de mme type ? Par exemple, dans ce cas : int nl = 10:
double d2 = 5.0; double dResult =

nl *

d2;

Tout d'abord, C# ne comporte pas d'opration 1nt * double. Il pourrait se contenter de produire un message d'erreur, mais il essaie plutt de comprendre ce qu'a voulu faire Ie programmeur. C# dispose des versions int * :inr et dou'l1e * ,jouble de la multiplication. Il pourrait convertir d2 en son quivalent int, mais il en rsulterait la perte de la partie dcimale du nombre (ce qui se trouve droite du point dcimal). C# convertit donc en dor-rbie lavariable int n1 t utilise I'opration ciouble * ciouble. C'est ce que I'on appelle une promotion implicite.
Une promotion implicite est implicite parce que C# I'effectue automatiquement, et c'est une ptomotion parce qu'elle transforme une valeur d'un certain type en un type de capacit suprieure. La liste des oprateurs de multiplication donne Ia section prcdente apparat dans I'ordre de promotion croissante de irit dor,rble ou de int decimal.ll n'existe aucune conversion implicite entre les types en virgule flottante et le type iecinal. La conversion d'un type de capacit suprieure tel que double en un type de moindre capacit tel que int s'appelle une rtrogrodotion.
Une promotion est aussi appele cont.tersion uerc le hout, et une rtrogradation conuersion uers le bas.

Chapitre 4 : Les oprateurs sont


;1t0lv.r

sympas 6 5

Connersion de tqpe explicite

le cast

Et si C# se trompait ? Et si le programmeur voulait vraiment effectuer une multiplication en nombres entiers ?

Vous pouvez toujours changer le type d'une variable d'un type valeur en utilisant I'oprateur cast. Un casf consiste mettre entre parenthses le type dsir et le placer immdiatement avant Ia variable ou I'expression
concerne.
De cette faon, I'expression suivante utilise I'oprateur

int * int

int

n1

1;

double 2 = 5.0; int nResult = nl

- (int)d2;

Le cast de

2 en l

nt est une rtrogrodation explicite, parce que le program-

meur a explicitement dclar son intention. Vous pouvez faire une conversion explicite entre deux types valeur quels qu'ils soient, que ce soit une promotion ou une rtrogradation.
vitez les conversions de type implicites. Utilisez plutt un cast avec les types valeur pour faire des conversions explicites.

Laissez la logi4ue

tran(uille

C# n'offre aucune conversion de type de ou vers le type boo1.

Assigner un tqtte
Le mme principe de compatibilit de types s'applique I'oprateur

d'assignation.

66

Deuxime partie : Programmation lmentaire en C#

=f 1| ^f\

\!!_/

En gnral, une incompatibilit de type produisant un message d'erreur a" compilation se produit dans I'oprateur d'assignation, mais pas

I'endroit qui est la source de I'incompatibilit.


Considrez I'exemple de multiplication suivant int nl =
.i n+ tlt L -t llA

10;

( * n1. E J.V ttLt

La deuxime ligne de cet exemple gnre un message d'erreur d une incompatibilit de type, mais I'erreur se produit lors de I'assignation, et non lors de la multiplication. En voici la terrible histoire : afin d'effectuer la multiplication, C# convertit implicitement n1 en double. C# peut alors effectuer une multiplication en type double, dont le rsultat est dans le

tout-puissant type

oub 1e.

Toutefois, les types de ce qui est droite et gauche de I'oprateur d'assignation doivent tre compatibles, mais le type de ce qui est gauche ne peut pas changer, car C# n'accepte pas de rtrograder implicitement une expression. Le compilateur gnre donc le message d'erreur suivant :

Impossible de convertir lmplicitenent 1e type double en int.


C# autorise cette expression avec un cast explicite
:

int nl -

int n2 = (int) (5.0 - nl);


(Les parenthses sont ncessaires parce que I'oprateur de cast a un niveau de priorit trs lev.) Cela fonctionne. La variable n 1 est promue en double, la multiplication est effectue, et le rsultat en double est rtrograd en int. Toutefois, on peut alors se demander si le programmeur est sain d'esprit, car il aurait t beaucoup plus facile pour lui comme pour le compilateur d'crire 5 * n1.

1o;

L'oprateur ternare, le redoutable


La plupart des oprateurs admettent deux arguments, certains n'en admettent qu'un, et un seul en admet trois : I'oprateur ternaire. Celui-ci est redoutable et pour une bonne raison. Il a le format suivant :
bool expression ? expressionl

expression2

Chapitre 4 : Les oprateurs sont

sympas 67

Et je rendrai les choses encore plus confuses avec un exemple

int a: 1: int b = 2; intnMax=(a)b)?a:b;


Dans cet exemple, si a est plus grand que b, la valeur de I'expression est a. Si a n'est pas plus grand que b, la valeur de I'expression est b.

L'oprateur ternaire est impopulaire pour plusieurs raisons. Tout d'abord, il n'est pas ncessaire. Utiliser le type d'une instruction if (que nous dcrirons au Chapitre 5) a le mme effet et est plus facile comprendre. D'autre part, I'oprateur ternaire donne une vritable expression, quel que soit son degr de ressemblance avec un type ou un autre d'instruction if . Par exemple, les expressions I et 2 doivent tre de mme type. Il en rsulte ceci :

int a:

1;

double b = 0.0:

intnMax=(a)b)?a:b;

Cette instruction ne se compile pas, alors que nMax aurait d recevoir la valeur de a. Comme a et b doivent tre de mme type, a St promu en doubl e pour tre compatible avec b. Le type qui rsulte de ?: est maintenant double, qui doit tre explicitement rtrograd en 1nt pour que I'assignation soit possible :

int a = 1;
double b = 0.0;

int
/I

nMax;

ceei fonctionne
mme

nl{ax

* (int)((a ) b) ? a :
que ceci

b)

nMax=(a)b)?a:(int)b;
Vous aurez rarement I'occasion de voir une utilisation de l'oprateur

//de

ternaire.

Chapitre 5

Gontrler lefl ux d'excuti on d'un programme


Dans ce chapitre : Prendre une dcision si vous le pouvez. Dcider quoi faire d'autre.
Faire des boucles sans tourner en rond.

Utiliser la boucle whi1e. Utiliser la boucle for.

onsidrez le trs simple programme suivant


using Systen; e He11o1,lor1
{

narnespac

public class Classl

, t,

/Je

prograrnme coru[ence

lcl

static
(

voi.d Main(string[l args)

denande son non


.

1'utilisateur

Console

tonsole.l,lriteli.ne("1{e11o, " * sNane) ; //attend confirnation de 1'utilisateur Console.I.lriteLine("Appuyez sur Entre pour terminer...")
Console.Read0
;

string sNane - Console. Readline ( ) ; //accueille 1'utilisateur par son nom

//tit

Writeline ( "Entrez votre non: " ) ; te nom entr par 1'utilisateur

70

Deuxime partie : Programmation lmentaire en C#

l l
]

En dehors du fait qu'il prsente quelques aspects fondamentaux de la programmation en C#, ce programme n'a pratiquement aucun intrt. Il

ne fait qu'afficher ce que vous avez entr. On peut imaginer un exemple un peu plus compliqu qui prendrait les donnes saisies par I'utilisateur, ferait quelques calculs avec elles et afficherait un rsultat (sinon pourquoi faire des calculs ?), puis se terminerait. Toutefois, mme un tel programme ne peut avoir qu'une utilit limite. L'une des caractristiques les plus importantes de n'importe quel processeur est sa capacit de prendre des dcisions. Par "prendre des dcisions", je veux dire que le processeur oriente I'excution du programme vers un chemin d'instructions si une certaine condition est vraie, et vers un autre chemin dans le cas contraire. Tout langage de programmation doit comporter cette capacit fondamentale pour contrler le flux d'excution des programmes. Il y a trois types de base d'instructions de contrle de flux : I'instruction if . la boucle. et le saut. L'une des instructions de boucle, foreach, est dcrite au Chapitre 6.

fr \(Ll

Controler le flux d'excution


La base de la capacit de prise de dcision de C# est I'instruction

1f

if
{

(bool expression)

/l
]

1'excution est oriente

ici si
nrrp

1'expression est vraie

// 1'excution se nottrsttit iei.

1'expression soit vraie ou non

Les parenthses qui suivent immdiatement I'instruction if contiennent une instruction de type boo i (pour en savoir plus sur les expressions de type boo1, reportez-vous au Chapitre 4). Juste aprs cette expression, il y a un bloc de code, dlimit par une paire de parenthses. Si I'expression est vraie, le programme excute ce bloc de code. Si I'expression n'est pas vraie, il ignore ce bloc de code et passe directement ce qui suit.

Chapitre

5: Contrler le flux d'excution d'un programme

7t

L'instruction if est plus facile comprendre avec un exemple concret

I garantir ll si st ir(a()
I

que a

n'est pas infrieur 0 infrieur 0.

tl
a

. alors,
0;

assigner

l
Ce fragment de code permet de garantir que la variable a est toujours suprieure ou gale zro. L'instruction if dit : "Si a est infrieur 0,

alorsassigner0a."

.rK
=qE,)

Les parenthses sont facultatives. C# traite

exaCtement de la mme manire que "if (expres sion boolenne) { instruction ) ". Le consensus gnral (auquel je souscris) est de toujours utiliser les parenthses. Autrement dit, faitesle.

"if (expression

boolerne)

instruction"

Et si

i'a

beson d'un exemple

Imaginez un petit programme qui calcule des intrts. L'utilisateur entre le principal et le taux d'intrt, et le programme donne la valeur qui en rsulte la fin de I'anne (ce n'est pas un programme trs sophistiqu). En C#, ce calcul tout simple apparat comme suit :

//calcul de l"a valeur


I lplus

du prineipaL

intrt
;

decinal nlnterestPaid ; mlnterestPaj.d = nPrincipal t (nlnterest / tOO) le total /1calcu1e naintenant -;;i".ip.i-i nnterestPaid ; u;.tni--t;;;i La premire quation multiplie le principal,

mPrincipal, par le taux d'intrt est gnralement pour payer, mf nterestPaid. I'intrt pourcentage), obtenir exprim en qui donne le nouveau principal, ce payer au ajout est alors L'intrt principal, stock dans la variable rnTotal.
d'intrt, mlnteresr (divis par
100, car le taux

Le programme doit tre capable de rpondre presque tout ce qu'un tre humain est capable d'entrer. Par exemple, on ne peut pas accepter un principal ou un intrt ngatif. Le programme Calculatelnterest ci-dessus contient des vrifications pour viter ce genre de choses :

72

Deuxime partie : Programmation lmentaire en G#

_-"-ffi
,

// II ll II II
L

Calculatelnterest calcule 1e montant de f intrt


payer pour un principal donn. Si le principal ou le taux d'intrt est ngatif,

oroduit un nessage d 'erreur


or v--ve-.

using

System;

nanespace Calculate

Interest

public class
t

Class1

public static
t

int Main(string[J args)


le principal initial
;

//demande 1'utilisateur d'entrer Console, Write ("Entrez le principal

: "); string sPrincipal = Console.ReadLine0 ;

decirual nrPrincipal = Convert,ToDecinal(sPrincipal) | 1,,rifia nrrs 1e principal n'est r-pas ---ongatif --r / \ 1I tnrr]-nclpal ^\ u/

Console.l,lriteline{"Le principal ne peut pas tre ngatif");


mPrincipal = 0;
!l

l
//demande 1'utilisateur d'entrer le taux d'intrt Console,llrite("Entrez 1e taux d'intrt :") ;

strins sTnterest = Console.Readline0 ; decimal nlnterest = Convert.ToDecinal (slnterest) ; I lvrifie oue 1e taux d'intrt ntest pas ngatif.

if
I
I

(mTntprest
\Ir+.. Lvvv e

0)
;

Console.l,lriteline("Le taux d'intrt doit tre positif")

nlnterest
1

0;

i/""1.ut* la valeur du principal IIplvs f intrt


decinal mlnterestPaid ; nlnterestPaid = mPrincipal
//ca1cu1e maintenant

(nlnterest

tOO)

Console.l^lritelineO ; /i skip a line Console. i,iriteLine ("Princi.pa1 = "

le total decirnal motal = nPrincipal * //affiche rsultat

mlnterestPaid;

Console.l,lriteline("Taux d'intrt
.

Console Console

trlriteline ( "Intert pay = rr * mlnterestPaid); = tr * nlotal) ; .1{riteline ( "Total I / attend confirmation de 1'utilisateur Console.Writeline("Appuyez sur Entre pour terniner. . .t')
Console.

llriteline

* nPrincipal) * " * nlnterest *

"%");

Console.Read0;

Chapitre 5: Contrler le flux d'excution d'un programme

73

return

0i

Int e rest commence par demander son nom I'utilisateur en utilisant I'instruction WriteLine O pour crire une chane sur la console.
Le programme Ca1 culate

^9\./ ft}il \r/

Dites l'utilisateur exactement ce que vous voulez. Si possible, incliquez aussi le format que vous voulez. Les utilisateurs donnent rarement de bonnes rponses une invite aussi peu claire que ).

Notre exemple utilise la commande Readline ( ) pour lire sous forme d'une chalne de caractres ce que tape I'utilisateur au clavier jusqu' la touche Entre. Comme ce programme attend le principal dans le type decimal, la chalne entre doit tre convertie avec la commande Con.",ert. ToDecimal ( ). Le rsultat est alors stock dans mPrincipal.

o$!9{r SZ- ^\ (t / V--/

Les commandes Readline O, Writeline O, et ToDecimal O, sont toutes des exemples d'cppels de fonction. Je dcrirai en dtail les appels cle fonctions au Chapitre 6, mais ceuxl sont assez immdiatement comprhensibles. Vous devriez avoir au moins une ide de ce dont il s'agit. Si mes lumineuses explications ne sont pas assez lumineuses pour vous, vous pouvez les ignorer et aller voir le Chapitre 6.

mPrincipal. Si sa valeur est ngative, le programme annonce sans mnagement I'utilisateur qu'il a fait une nerie. Il fait ensuite la mme chose pour le taux d'intrt. Cela fait, il effectue le calcul de I'intrt, trs simple, que nous avons dj vu plus haut, et affiche le rsultat en utilisant une srie de commandes ',^,riLeLineO.
La ligne suivante effectue la vrification de

Le programme affiche les rsultats suivants, sur la base d'un principal lgitime et d'un taux d'intrt usuraire, curieusement lgal en bien des

contres

Entrez 1e princip at :1234 Entrez le taux drintrt :21

Taux

= Principal d'intrt =
rntert pay =

1234
Zlol
1t, 259.14

74

Deuxime partie : Programmation lmentaire en

G#

Total
Appuyez

1493.14

sur Entre pour terniner...


:

Avec une entre invalide, le programme produit la rponse suivante


Entrez 1e principal :1234 Entrez le taux d'intrt :-12.5 Le taux d'intrt doit tre positif

Principal
Taux

1234

d'intrt = 0%

Intert pay =

Total
Appuyez

1234

sur Entre pour terminer..,

^$$G t

7X t(9, Y

Pour que le source soit plus lisible, mettez en retrait les lignes d'une instruction if. C# ne tient pas compte de la mise en retrait. Beaucoup d'diteurs de code comporte une fonction de mise en retrait automatique : chaque fois que vous tapez la commande 1f , le texte correspondant est mis en retrait automatiquement. Pour activer cette fonction dans Visual Studio, slectionnez Outils/Options, et cliquez sur le dossier diteur de texte. Parmi les sous-dossiers de celui-ci, slectionnez C#, et, dans ce dernier, Tabulations. Dans cette page, slectionnez Mise en retrait Intelligente, et dans la zone Tabulations, spcifiez la taille du retrait que vous voulez (en nombre d'espaces). Pour ce livre, j'ai utilis une mise en retrait de deux espaces.

Qu'est-ce que je peux faire d'autre

Certaines fonctions ont besoin de tester des conditions mutuellement exclusives. Par exemple, le segment de code suivant stocke le plus lev de deux nombres, a et b dans la variable max :

l/ stocke dans riax le plus lev de a et b int nax; I I si a est plus grand que b. if(a)b)
t
I

/
= ai

conserr/

conne maxinun

mar
J

I si a est infrieur ot gal b,

Ghapitre

5: Gontrler le flux d'excution d'un proglamme 7 5

if (a (= b)
I

I
= b;

conserve

conne maxinum

max

if est ici inutile, car les deux conditions sont mutuellement exclusives. Si a est plus grand que b, alors il ne peut pas tre infrieur ou gal b. C'est pour les situations de ce type qu'il y a dans
La seconde instruction
C# une instruction e1se.

Le mot-cl else dfinit un bloc de code qui sera excut si I'expression logique contenue dans I'instruction if n'est pas vraie.

Notre calcul de maximum devient maintenant

// stocke dans max le plus lev de a et b int max; I I si a est plus grand que b. if(a)b)
t II max
I

.conserve a conne naxinum; sinon


a;

e1s e
I

I
= b;

conserve b conne naxinum

max
J

Si a est plus grand que b, c'est le premier bloc de code qui est excut.

Dans le cas contraire, c'est le second. Au bout du compte, nax contient la valeur du plus grand de a ou b.

^ EUlrcf meme rc e-Lse


Les squences de plusieurs clauses else peuvent donner une certaine confusion. Certains programmeurs, dont moi-mme, prfrent les viter lorsque a permet de faire un code plus clair. On pourrait crire le calcul du maximum de la facon suivante :

*...

// stocke int max;

dans max 1e plus 1ev de a

et b

76

Deuxime partie : Programmation lmentaire en G#

//

^ --^-J l-^-! L -1,.rttp h suppose que d PJ-u l.duu nec lo


n.c

nax = 8;
I I si ce n'est if(b)a)

ll
max

,..a1ors, on peut changer d'avis = b;

Il y a des programmeurs qui vitent ce style comme la peste, et je peux les comprendre, mais a ne veut pas dire que je vais faire comme eux. Je me contente de les comprendre. Les deux styles, avec ou sans "e1se", sont couramment utiliss, et vous les rencontrerez souvent.

lnstructions
Le programme

if

inbrques

Caiculatelnterest prvient I'utilisateur en cas d'entre invalide, mais il ne semble pas trs pertinent de poursuivre le calcul de I'intrt si I'une des valeurs est invalide. a ne peut gure tirer consquence ici, parce que le calcul de I'intrt est pratiquement immdiat et parce que I'utilisateur peut en ignorer le rsultat, mais il y a bien des calculs qui sont loin d'tre aussi rapides. De plus, pourquoi demander I'utilisateur un taux d'intrt s'il a dj entr une valeur invalide pour le principal ? ll sait bien que le rsultat du calcul sera invalide, quelle que soit la valeur qu'il saisit maintenant.
Le programme ne devrait donc demander le taux d'intrt I'utilisateur que si la valeur du principal est valide, et n'effectuer Ie calcul de I'intrt que si les deux valeurs sont valides. Pour raliser cela, il vous faut deux instructions 1f . I'une dans I'autre. Une instruction if place dans le corps d'une autre instruction appele une instruction imbrique.
Le programme suivant,

if

est

instructions

if

C a 1 c u 1 at e I nt e r e s t\^li thEmb eci d e dTe s t, utilise des imbriques pour viter les questions inutiles si un problme

est dtect avec les valeurs entres.

I I || II II
I I

CateulatelnterestWithEmbeddedest calcule 1e montant de f

intrt

payer pour un principal donn. Si le principal ou le taux d'intrt est

ngatif, alors

gnre ttn nessage d'erreur

Ghapitre 5 : Gontrler le flux d'excution d'un

programme 7

I
System;

et n'effectue pas le

ca1cu1.

using
t

natuespace CalculatelnterestWithBmbeddedTest

public class
{

Class1

public static void Main(string[] args)


t

//detin:.t un taux d'intrt

naxinun

int nMaxinunlnterest = 50; //demande 1'utilisateur d'entrer 1e principal initial Console.l^irite("Entrez 1e principal :") ; string sPrincipal = Console.ReadlineO decimal mPrincipal : Convert,ToDecimal(sPrincipal) I lsi Ie principal est ngatif. { \/ u./ ^\ 1t lmrrlncr_pal
; ;

IL
l

gnre un message d'erreur.

Console,I,{riteLine("Le principal ne peut pas tre ngatif"); else


t
I

Console.Write("Entrez 1e taux d'intrt :"); string sTnterest = Console. ReadLine 0 ; decimal nlnterest = Convert.ToDeciraal(sTnterest)

sinon,

demande 1e

taux d'intrt

d'intrt est ngatif ou trop 1ev. if (nlnterest ( 0 ll rnlnterest ) nMaximumlnterest)


7e taux
{

I lsi

.gnre un autre nessage d'erreur II Console.l,lriteline("Le taux d'intrt doi.t tre positif 'r + "et pas suprieur u + nMaximunlnterest) mlnterest = 0;
l erse
1

I tprincipal et f intrt sont tous //1e //calcule donc La valeur du principal

deux 'ralides

//p1us lrintrt
decimal mlnterestPaid ; mlnterestPaid * mPrincipal * (nlnterest / 100); /lca1eule naintenant 1e tstal decimal motal * nPrincipaL * mnterestPaid;
/
f I t ar, 1 r. rsultat /affiche

Consol"e.l,lriteline0

Console,1{riteline("Principal = " r mPrincipal); Console.Writeline("Taux d'intrt = tr f nlnterest *


ConsoLe.I,lriteLine()
;

; //

skip a line

"%"):

Deuxime partie : Programmation lmentaire en

C#

onsole.Writeline("Intrt
Console.l,lriteline
)

('tTotal
de

pay = tr + mlnterestPaid);
=
rt + nlotal)
;

llattend confirmation
Console.Read0;

1'utilisateur
. . ")
;

Console.l,lriteline("Appuyez sur Entre pour terniner.

Le programme commence par lire Ia valeur du principal entre par I'utilisateur. Si elle est ngative, il affiche un message d'erreur et se termine. Dans le cas contraire, Ie contrle passe la clause e1se, et le programme poursuit

son excution. Dans cet exemple, la vrification du taux d'intrt a t amliore. Le programme demande ici un taux d'intrt qui ne soit pas ngatif (rgle mathmatique) et qui soit infrieur un maximum (rgle juridique). Cette

instruction i

f utilise un test compos


( 0 ll
nlnterest

if

(mlnterest

nl'laxinumlnterest)

Cette expression est vraie si mI nt e r e s t est infrieur zro ou si nlnteresr- est plus grand que nllaximumnterest. Remarquez que j'ai
-r\0.

dclar nl'laxirnurillnteres'1- en haut du programme au lieu de le coder localemenl sous forme de constante.
-a

t\\vl

I/....fil l\rt
L

Dfinissez toujours au dbut de votre programme les constantes importantes.

Placer les constantes dans des variables au dbut du programme est utile plusieurs titres. Tout d'abord, chaque constante a ainsi un nom : nMaximunlnterest est beaucoup plus descriptif que 50. D'autre part, elles sont beaucoup plus faciles retrouver dans les expressions. Enfin, il est beaucoup plus ais d'en changer la valeur en cas de ncessit. Remarquez que c'est n|laxinunlnterst qui apparat dans le message d'erreur. Si vous remplacez nl4axi:,nurnlnterest par 60, par exemple, cette modification n'affecte pas seulement le test, mais aussi le message d'erreur.
Si I'utilisateur entre une valeur correcte pour le principal mais un taux

d'intrt ngatif, le programme affiche


Entrez 1e principal :I234

Entrez le taux d'intrt :-12.5

Chapitre 5 : Contrler le flux d'excution d'un programme

79

Le taux
Appuyez

d'intrt doit tre positif et pas suprieur 50.


sur Entre pour terniner.
.
.

Ce n'est que si I'utilisateur entre des valeurs correctes pour le principal et

pour le taux d'intrt que le programme effectue le calcul demand :


Entrez 1e principal :1234 Entrez le tux d'intrt :12.5

Principal
Taux

= 1234 d'intrt = 12.5

r,

Total

Intert pay = 154.25 = 1388.25


sur Entre pour terminer.
.
.

Appuyez

Les comtnandes de boucle


L'instruction i f permet un programme de s'orienter sur un chemin ou sur un autre dans le code en cours d'excution, selon la valeur d'une expression booienne. Elle permet de faire des programmes incomparablement plus intressants que ceux qui sont dpourvus de capacit de dcision. Ajoutez maintenant la capacit d'excuter un ensemble d'instructions de faon itrative, et vous avrez fait un autre saut qualitatif dans la capacit de vos programmes. Considrez le programme Calculatelnterest que nous avons vu plus haut dans ce chapitre. On pourrait faire la mme chose avec une calculatrice ou mme la main avec un crayon et un papier, en se donnant moins de mal que pour crire et excuter un programme.
Et si vous pouviez calculer le montant du principal pour chaque priode d'un an successive ? Ce serait beaucoup plus utile. Une simple macro de feuille de calcul Excel serait toujours plus facile raliser, mais au moins,

ilyaunprogrs.
Ce qu'il vous faut, c'est un moyen pour I'ordinateur d'excuter plusieurs fois la mme squence d'instructions. C'est ce qu'on appelle une boucle.

80

Deuxime partie : Programmation lmentaire en C#

Commenons qtar la boucle de base, wh


Le mot-cl de C# simple :

1
--1-

\-

;l:rie

permet d'excuter une boucle de la forme la plus

vhile (boo1 expression)


{

//1'excution est rpte tant que 1'expression reste vraie


]

La premire fois que I'instruction de boucle while est rencontre, I'expression boolenne est value. Si elle est vraie, le code contenu dans Ie bloc qui suit est excut. Lorsque I'accolade qui en indique la fin est rencontre, I'excution reprencl I'instruction wl'ri 1e. Ds que I'expression boolenne est fausse, le bloc de code qui suit est ignor, et l'excution du programme passe clirectentent ce qui suit. Si la condition n'est pas vraie la premire fois que I'instruction

wliile

st

rencontre. le bloc de code qui suit n'est jamais excut.

=,:c|,,)

cfq-'.'

Les programrneurs s'expriment souvent de faon un peu bizarre (il sont d'ailleurs bizarres la plupart du temps). Un programmeur pourrait dire qu'une bor-rcle est excute jusqu' ce qu'une certaine condition soit fausse. Pour moi, cela voudrait dire que le contrle passe en dehors de la boucle ds que la condition devient fausse, quel que soit le point o il en est de son excution ce moment-l. Ce n'est videmment pas comrne a que a se passe. l-e programme ne vrifie si la condition est vraie ou non que lorsque le contrle cle I'excution arrive effectivement en haut de la

boucle. Vous pouvez utiliser I'instruction ',;fri i e pour raliser le programme Cari:lll..tie-r.i.:,r ,jl'iab1e., Qui est une version en boucle du programme Ca i c,,r 1 a t e I r i e r e s *.. Ca i c u1 at e 1 i t e r e s t TaL i e calcule une table des valeurs du principal pour chaque anne, en mettant en vidence I'accumulation des ir-rtrts annuels :
/

/ CalculatelnterestTable - ca1cul de f intrt cumul pay sur la base d'un principe dtermin I sur une oriode de olusieurs annes II
I
Ce

urrr JyLcur,
nmpsn2c l.qirrLUPsu!
{

r^rrlatelntereStTable

"^ u!lr "-^

Q"^+^-. /Lcl4,

Chapitre 5: Contrler le flux d'excution d'un

programme

8l

public class
{

C1ass1

public static void Main(stringIJ args)


{

//demande 1'utilisateur d'entrer 1e principal Console.hlrite("Entrez 1e principal : ") ;

initial

string sPrincipal = Console,ReadLine0


tl l / Dr r /llal
. / n

;
;

decimal mPrincipal = Convert.ToDecimal (sPrincipal)


nrln.1h.1 yrarrLfyof
a / ^\

an+ rrtrBdLl!. c>L n-.+i1


uJ

1r {mHr1nc1nl \
t

ll
]

.gnre un message d'erreur.

Console.WriteLine("Le principal ne peut pas tre ngatif");


else
{

sinon,

denande 1e

taux d'intrt

Console.I,irite("Entrez 1e taux d'intrt :"); string sTnterest = Console,Readli-ne0 ; decimal nilnterest = Convert,ToDecimal(slnterest)

//si 1e taux d'intrt est ngatif.. if (mlnterest ( 0)


{

gnre un autre message d'erreur IL Console.i,iriteline("Le taux d'intrt doit tre positif")

nnterest = l
e1s e {

0;

//1e principal et le taux d'intrt sont


//demande donc 1e nombre d'annes Console.l.Jrite("Entrez 1e nombre d'annes strins sDuration = Console. Readline ( ) ;

vaLides

:")
;

int

nDuration = Convert.ToInt32 (sDuration)

llv.rifie la valeur entre Console.l,JriteLine0; I I crit


Console.1rlriteLine

= " -l- nPrincipal) ; Console,I,,Iriteline("Taux d'intrt = 'r + mlnterest + rtt/rt)'

("Principal
)
;

une

ligne blanche
nDuration

Console.Writeline("Dure ='tf
(

"ans");

Console.l,,lriteLi.ne

lleffectue
inf
{

une boucle selon 1e nombre d'annes spcifi

nerr

= l'

while(nYear (= nDuration)

l/ca1cule 1a valeur du principal


ll . I'intrt l/plus

deci.mal mlnterestPaid

mlnterestPaid = mPrincipal

* (mlnterest I

100);

82

Deuxime partie:Programmation lmentaire en C#

/lealcu1e naintenant 1e nouveau principal en ajoutant mPrincipal = mPrincipal * nlnterestPaid; //arrondit 1e principal" au centirue le plus proche

//f intrt

au principal prcdent

nPrincipal : decimal.Round (nrPrinci-pa|, I I atfiche le rsultat


Console . l.Jriteline (nYear
I I passe 1'anne suivante nYear = nYear * li

2)

+ rr - rr + nTPrincipal

l
II

attend confirmation de 1'utilisateur Console,Vlriteline("Appuyez sur Entre pour terminer. . .")

Console.Read0;

l L'essai d'excution de ce programme donne ce qui suit


Entrez le principal :1234 Entrez 1e taux d'intrt : i2 .5 Entrez 1e nonbre d'annes :10
:

Principal
Taux

= 1234 d'intrt = L2.5'l' = 10 ans Dure


1

- 1388.25

2-1561.78
3-1757

4-r97 6 .62 5-2223.7 6-2501.66 7 -2814,37

B-3166.1i
9-3561,94 10-4007.18 Appuyez sur Entre pour

terniner,

Chaque valeur reprsente le principal total I'issue du nombre d'annes coules, Sur la base d'un cumul annuel d'intrt simple. Par exemple, un principal initial de | 234 12,5'1, donne 3 561 ,94 au bout de neuf ans.

Ghapitre 5;Contrfer le flux d'excution d'un programme

83

1ep!Qa"

La plupart des valeurs comportent deux dcimales pour les centimes.

{dg)

Comme les zros de la partie dcimale ne sont pas affichs, certaines valeurs n'ont qu'un chiffre ou mme aucun aprs la virgule. Ainsi, 72,70 est affich comme 12,7. Vous pouvez y remdier en utilisant les caractres de rnise en forme dcrits au Chapitre 9. Le prograrnme Calcuiar-elnterestTabie commence par lire la valeur du

principal et celle du taux d'intrt entres par I'utilisateur, et par vrifier quelles sont valides. Il lit ensuite le nombre d'annes sur lequel effectuer I'itration. et stocke cette valeur dans la variable nDuration.
Avant d'entrer dans la boucle while, le programme dclare une variable nYear, qu'il initialise la valeur i. Ce sera "l'anne en cours", c'est--dire que cette valeur va changer "chaque anne" chaque boucle successive excute par le programme. Si le numro de I'anne contenu dans n r e a r est infrieur la dure totale contenue dans nDurati on, le principal pour "l'anne en cours" est recalcul en utilisant I'intrt calcul sur la base de "l'anne prcdente". Le principal calcul est affich avec le numro de I'anne correspondante.

=\l\y j \/

1r$!Qa" 7^T \ L'instruction decirnal


plus proche.

Round ( )

arrondit la valeur calcule au centime le

La cl du fonctionnement de ce programme se trouve dans la dernire

ligne du bloc. L'instruction nYear

nYear

t 1; incrmente

nYear de

1.

Si nYear a la valeur 3 avant cette instruction, elle aura la valeur 4 aprs. Cette incrmentation fait passer le calcul d'une anne la suivante.

Une fois que I'anne a t incrmente, le contrle revient en haut de la boucle, o la valeur de n lear est compare la dure demande. Dans I'exemple excut ci-dessus, si le numro de I'anne en cours est infrieur ou gal 10, le calcul continue. Aprs avoir t incrmente dix fois, la valeurde nYear devient 11, qui est plus grand que 10, et lecontrledu programme passe I'instruction qui suit immdiatement la boucle whi1e. Autrement dit. il sort de la boucle.

^9\.1 (Gil \Zl

La plupart des commandes de boucle suivent ce mme principe de base incrmenter une variable servant de compteur jusqu' ce qu'elle dpasse une valeur fixe.

qui.ontiste

La variable nYear servant de compteur dans

Calc.rlatelnterestTable

doit tre dclare et initialise avant la boucle while dans laquelle elle est utilise. En outre, I'incrmentation de la variable nYear doit gnralement tre la dernire instruction de la boucle. Comme le montre cet exemple,

84

Deuxime partie : Programmation lmentaire en

G#

vous devez prvoir de quelles variables vous aurez besoin. Ce procd vous sera plus facile manier une fois que vous aurez crit quelques milliers de boucls'v,'hi1e, comme moi.

>/
(

\t/

tl ^,,\I
\-----./

Lorsque vous crivez une boucle r'hi 1e, n'oubliez pas d'incrmenter la variable servant de compteur, comme je l'ai fait dans cet exemple :

int
{

nyer = 1;
/ a ^\

vn1le lnlear \ lu,l


l

instructions,

J'ai omis I'instruction nYear : nYear * 1 ;. Sans I'incrmentation, la valeur de nlear est toujours l, et le programme continue excuter la boucle sans jamais s'arrter. C'est ce qu'on appelle une boucle infinie. La seule manire d'en sortir est d'arrter le programme (ou de redmarrer

I'ordinateur).

.l

Faites attention ce que la condition de sortie de la boucle puisse rellement tre satisfaite. En gnral, il suffit pour cela que la variable compteur soit correctement incrmente. Sans cette prcaution, vous tes bon pour

la boucle infinie et I'utilisateur rancunier. Comme une boucle infinie est une faute assez courante, ne soyez pas trop vex si vous vous y laissez prendre.

Et maintenan\

o...

wh

i 1e

Il existe une variante de while :c'est la boucle dc... whiie. Avec elle, la condition n'est value qu' la fin de la boucle :

int
do

nYear

= J;
instructions.

t
I

nYear = nYear * 1; while (nYear ( nDuration);

ta Oitterence de la boucle whl1e, la boucle d:r... whl1e est excute au moins une fois, quelle que soit la valeur de rLDur ation. Toutefois, ce type de boucle est assez peu utilis en pratique.

Chapitre 5 : Contrler le flux d'excution d'un programme

85

Briser une boucle, c'est facle


Il existe deux instructions de contrle spciales que vous pouvez utiliser dans une boucle:break et continue. La commande break fait passer le contrle la premire expression qui suit la boucle dans laquelle elle se trouve. La commancle conl:i nue fait passer le contrle directement I'expression conditionnelle en haut de la boucle afin de recommencer de la manire apProprie.

Hdoutequ'ilyaitbeaucoupdeprogrammeursquisesouviennentseulement

-q\!C ^,

J'ai

rarement utilis continue dans ma carrire de programmeur, et je

I[qIl \Zl

de son existence. Ne I'oubliez tout de mme pas compltement. Elle vous servira peut-tre un jour pour jouer au Scrabble. Par exemple, imaginez que vous vouliez rcuprer votre argent la banque ds que le principal dpasse un certain nombre de fois le montant initial, indpendamment du nombre d'annes coules. Vous pouvez facilement rsoudre ce problme en ajoutant ce qui suit dans la boucle
:

1t
t !

(mPrincipal
break;

(maxPorrer

n0riginalPrincipal)

j La commande b reak ne sera excute que lorsque la condition de I'instruction if sera vraie. Dans ce cas, lorsque la valeur calcule du principal sera suprieure maxPower multipli par la valeur initiale du principal. L'excution de la commancle break fait passer le contrle en dehors de la

boucle whrle (nYear: excution jusqu' sa fin. './ F

',.t$f$

f-fiT.X.)

I li'/

ha"

Vous trouverez sur le site Web une version du calcul de table d'intrt qui comporte cette adjonction (il serait un peu long d'en donner le source ici).

Voici un exemple de rsultats affichs par ce programme


Entrez 1e principal :100 Entrez 1e taux d'intrt :25 Entrez le nombre d'annes :100

Principal
Taux

100

d'intrt = 25i,
= 100 ans

Dure

86

Deuxime partie : Programmation lmentaire en C#

Arrter
1-r25
2-156.25 3-195.31 4-244.14 5-305,1B 6-381.4B 7-476.85 B-596.06 9-745.08

si la valeur initiale

est nultiDlie nar

10

10-93 I .35 i1-1164.19


nnrrrroz crlr Fntr nnrrr tarminor

Le programme se termine ds que le principal calcul dpasse I 000 o. Comme vous voyez, il est plus performant que la llelle au bois dormant.

Faire des boucles iusqu' ce qu'on ,/ arrie


Le programme Cairru,ratelnrel"esi-Tab1e est assez malin pour se terminer si I'utilisateur entre une valeur invalide, mais c'est tout de mme un peu dur pour I'utilisateur de le planter l sans autre forme de procs. Mme mon peu sympathique programrne de comptabilit me donne droit trois essais pour entrer mon rnot de passe avant de me laisser tomber.
Une combinaison de iihlie et tt:e;,r permet de donner au programme un peu plus de souplesse. Le progralnme ila,tcr-riaielni r cstTableluloreF'cr qir.ing en montre le principe :

// CalculatelnterestTableMoreForgiving - calcule I'intrt pay sur un nombre d'annes dtermin. Cette II || version donne L'utilisateur 3 possibilits II un principal et un taux d'intrt valides.
rtqino Svqtpm'
nmF sna c

tln I

r"rr

late Inte restTab

leMo

reFors.ivins

,,^i-urrr6 O,,^r^-. y LEul,

public class Classi


i

public static void Main(string[] args)


{

// dfinit un taux d'intrt maxi.mal int nMaxinumfnterest = 50:

Chapitre 5 : Contrler le flux d'excution d'un programme

87

:,,^^,, t: ^^ Yu ^,,',,ne u a Lc rI rI JuDgu

//

denande

1'utilisateur le nrincin:l initial; ValeUf valide soit entre

continue

decinal mPrincipal;
while (true)
{

Console.l*lrite("Entrez 1e principal :") a*r.inn oDrinnj^a1 = COnSOle,ReadLine0 r rrrlryc Lr rrr !

; ;
;

nPrincipal = Convert.?oDecimal (sPrincipal) I I sort de 1a boucle si valeur entre est valide


lt lmfrlnclpaL )=
i \

u)
^\

break ]
I

onro

11n

mpsso

rl'errerrr si

val

ertr entre est invalide

Console.WriteLine("Le principal ne peut pas tre ngatif");


Console.l,JriteLine ( "Veui11ez recomnencer" ) ;

Console.l'lriteline
1

//
{

demande

maintenant

1'utilisateur le taux d'intrt

decinal nlnterest:
whi.1e

(true)
;

mlnterest : Convert.ToDecirnal (slnterest) ; ll n'accepte pas un taux d'intrt ngatif ou trop grand'.' j.f (mlnterest >= 0 && mlnterest (= nMaxinunlnterest)
{

Console,l^lrite("Entrez 1e taux d'intrt :") string sTnterest : Console.ReadlineO ;

break; ]
I

Console.l^lriteLine("Le taux d'intrt doit tre positif " * "et pas suprieur " + nMaximumlnterest)
Console,l^lriteLine ("Veui.11ez reconnencer" ) ;

.et

gnre aussi un message d'erreur


;

Console,illriteline0;
]

ll I'int|rt corune 1e principal sont valides, // demande donc 1e nombre d'annes


Console.

lirite ( "Entrez 1e nombre d'annes string sDuration = Console. Readline 0 ;

:"

int nDuration = Convert. ToInt32 (sDuration) l/ vrifie la valeur entre Console.Writeline0; l/ crit une I i -.o h l rnnha . '\ Console.l,lriteline("Principal =
; ,' + ' mPrrnnlh srr!frj!rHufl'

I t.

Console.}lriteline ("Taux d' intrt


. lrlriteLine ( "Dure /\ uonsote. l/crlteLlne U ;

Console

" * mlnterest * "%") l " * nDuration f " ans");

// effectue

une boucle sur 1e nombre d'annes spcifi

88

Deuxime partie : Programmation lmentaire en C#

while (nTear (= nDuration)


{

int nYear = 1; /l i/
ll

calcule 1a valeur du principal


Dlus

l'lnteret
:

I I I'intrt au prcdent principal nPrincipal : mPrincipal * mlnterestPaid; // arrondit 1e principal au centine 1e plus mPrincipal = decinal . Round (mPrincipa! , 2) : I I af.lche 1e rsultat

decinal mlnterestPaid mlnterestPaid = mPrincipal n (mlnterest I t0O); l/ calcule naintenant le nouveau principal en ajoutant

proche

Console.I^Iritetine(nYear + rf -fr // oasse 1'anne suivante


nYear = near

nPrincipal)

1;

ll attend confirmation de 1'utilisateur Console.l,lriteline("Appuyez sur Entre pour terminer. . . ");


Console.Read ( ) ;

Ce programme fonctionne largement de la mme manire que les exemples prcdents, sauf pour ce qui est entr par I'utilisateur. Dans ce cas, c'est une boucle while eui remplace les instructions 1f utilises prcdemment pour dtecter les entres invalides. Par exemple : decinal mPrincipal: while (true)
{

Console.I{rite("Entrez 1e principal :") string sPrincipal = Console.Readline0

;
:

mPrincipal = Convert.ToDecimal(sPrincipal) ; I I sort de 1a boucle si valeur entre est valide if (mPrincipal )= 0)


{
L

break;

// gnre un message d'erreur si valeur entre est invalide Console.\lJriteLine("Le principal ne peut pas tre ngatif"); Console,Writeline ("Veui11ez recomnencer") ; Console . llriteLi.ne ( ) ;
]

Chapitre 5:Contrler le flux d'excution d'un programme

8g

Cette portion de code reoit une valeur de I'utilisateur I'intrieur d'une boucle. Si la valeur entre est satisfaisante, Ie programme sort de la boucle et poursuit son excution. Si la valeur est incorrecte, un message d'erreur est envoy I'utilisateur, et le contrle repasse au dbut de la boucle de saisie.

|X IICff I \z

Vous pouvez le voir de cette faon : "Le programme ne sort pas de la boucle tant que I'utilisateur n'a pas rpondu correctement."
Remarquez que la condition a t inverse, car il ne s'agit plus qu'une rponse incorrecte produise un message d'erreur, mais qu'une rponse correcte fasse sortir de la boucle. Dans la partie concernant Ia saisie du taux d'intrt, par exemple, le test Principai < 0 | mF rincipal ) nl'laximumlnterest devient mlnterest >: 0 && mlnterest (: nMaximumlnterest. Il est clair que mlnterest ): 0 est le contraire de mlnterest ( 0. Ce qui n'est peuttre pas aussi vident est que le OR | | est remplac par un AND &&. Autrement dit : "Sortir de la boucle si le taux d'intrt est suprieur zro et infrieur au montant maximum."

Dernier point noter: la variable mPrincipai doit tre dclare en dehors de la boucle, pour des questions de rgles sur la porte des variables, que j'expliquerai dans Ia section suivante de ce chapitre.

q/

{f\

Vous allez peut-tre trouver cela vident, mais I'expression true est value
comme true. Par con-cquent, while (true) est I'archtype de la boucle infinie. C'est la commande break qu'elle contient qui fait sortir de la boucle. Aussi, si vous utilisez une boucle while (true), faites particulirement attention ce que la condition de break puisse tre satisfaite.

e,
z<r- \

Voici un exemple de rsultat d'excution de ce programme


Entrez 1e principal :-1000 Le principal ne peut pas tre ngatif

Veuillez

recommencer

Entrez 1e principal :1000 Entrez 1e taux d'intrt : - l0 Le taux d'intrt doit tre positif et ps suprieur

50

Veuillez

recomnencer

Entrez le taux d'intrt :10 Entrez 1e nombre d'annes :5

Princinel

000

90

Deuxime partie : Programmation lmentaire en

G#

Taux

d'intrt = 10%
=5
ans

Dure
t - 1100

z'!trv
1^i^ ^

3 - 1331

4-1464.r
5-1610.51 Appuyez sur Entre pour

terniner...

Le programme n'accepte ni principal ngatif ni taux d'intrt ngatif, et

m'explique patiemment mon erreur chaque fois. Expliquez toujours exactement son erreur I'utilisateur avant de lui demander nouveau d'entrer une valeur.

Les rgles de porte des

hriables
:

Une variable dclare dans le corps d'une boucle n'est dfinie que dans

cette boucle. Examinez ce fragment de code

int
{

nDays

while(nDays

(
.

1;

nDuration)
n\/c1rro = llvoauc / nllrrr<. LLUqJ D t I

.in+ n,,nra^a -LllL rlvIBtr

instructions

nDays

= nDays

1;

Lavariable nA."'eiage n'est pas dfinie en dehors de la boucle vrhile. Il ya diffrentes raisons cela, mais considrez celle-ci : lors de la premire excution de la boucle, le programme rencontre la dclaration lnt nA,,'ere,ge, et la variable est dfinie. Lors de la seconde excution de la boucle, le programme rencontre nouveau la dclaration de nAve r age. S'il n'y avait pas les rgles de porte des variables, ce serait une erreur, car la variable est dj dfinie.

^2K :(dqf

\/

Il y a d'autres raisons, plus convaincantes que celle-ci, mais je m'en tiendrai l pour le moment.

Il me suffit de clire que la variable nAverag disparalt, aux yeux de C#, ds que le programme atteint I'accolade fermante qui indique la fin de la boucle.

Chapitre 5: Contrler le flux d'excution d'un

programme

9l

0mpf9ttr ilre la boucle la plus utilse.' f


La boucle

or

wl,ile est la plus simple des structures de boucle cle C#. et la plus utilise aprs f or.
Une boucle f or a la structnre suivante
f.or
{
:

(initflxpressioni condition
I

increnent[xpression)

instructions.

Lorsqu'une boucle

for est rencontre,

le programme commence par excu-

ter inrtEx,f ression. puis il value la condition. Si la condition est vraie, le prograrnrne excute les instructions qui constituent Ie corps de la boucle, lequel est dlimit par les accolades qui suivent immdiatement I'instruction ,or. Lorsque I'accolade fermante est atteinte, le contrle passe I'excution de increinentExpressi on, puis nouveau l'valuation de la condition, et la boucle recommence aussi lorlgtemps que la condition de f or reste vraie.
En fait, la dfinition d'une boucle f

or peut tre convertie dans la boucle

whiie suivante

initExpression; hile (condition)


t

instructions.

incrementExDression; l

Un evemlrle de boucle f
d'une boucle f or
:

Un exemple vous permettra de mieux comprendre le fonctionnement

1/ instructions
a=
1;

C#

92

Deuxime partie : Programmation lmentaire en

G#

I I et naintenant une boucle for(int nYear = 1; nYear ( nDuratj,on; nYear = nYear *


{

1)

IL
] I
a L. ^ = 1,

corps de

la

boucle

I Ie programme continue ici

1 ;. Il Supposez que le programme vienne d'excuter I'instruction a dclare ensuite la variable nf ear t I'initialise 1. Cela fait, il compare near nr)urar,rcr,. Si rrYear est plus petit que rrDulaiion, le corps de la boucle (les instructions contenues dans les accolades) est excut. Lorsqu'il rencontre I'accolacle fermante, le programme revient en haut de la boucle, et excute I'expression rrYear : nYeai: * i avant d'effectuer la

comparaison nTear

nDurar-ion.

Pour(uoi auriez-(/ous besoin d'une autre boucle

A quoi peut bien servir une boucle f o r si C# perrnet de faire la mme chose avec une boucle,,;hrie ? La rponse la plus simple est qu'elle ne sert rien. Une boucle f rr n'ajoute rien ce qu'une boucle whiie permet dj de faire. Toutefois. les cliffrentes parties de la boucle ror existent par commodit, et pour diffrencier clairement les trois parties que toute boucle doit comporter : I'initialisation, le critre de sortie, et I'incrmentation. Non seulement c'est plus facile lire, mais c'est aussi plus difficile rater (souvenez-vous que les erreurs les plus courantes dans une boucle v;hiLe sont d'oublier d'incrmenter la variable compteur et de ne pas dfinir correctement le critre de sortie). Indpendamrnent de tout alibi justificateur, la raison la plus importante de comprendre la boucle f or est que c'est celle que tout le monde utilise, donc celle que vous allez voir neuf fois sur dix quand vous lirez du code

crit par quelqu'un d'autre.


,-,: 1t$!log^ La boucle f st conue de telle sorte que la premire expression initialise \ une variable compteur, et la dernire I'incrmente. Toutefois, le langage C# ^r,7 \dW n'impose pas cette rgle. Vous pouvez faire ce que vous voulez dans ces ) \/ deux parties de I'instruction, mais sachez que vous seriez mal inspir d'y faire autre crose"

Chapitre

5: Contrler le flux d'excution d'un programme

93

L'oprateur d'incrmer-rtation est particulirement populaire dans Ies boucles f or fie dcris I'oprateur cl'incrmentation, ainsi que d'autres, au Chapitre 4). Une boucle f or pour notre exemple de calcul des intrts cumuls pourra s'crire ainsi :

for(int
i
I

nYear

= l;

nYear

( nDuration; nYear*i)

.corps de 1a boucle

.q$q ,., t(9, Y

C'est presque toujours I'oprateur cle postincrmentation qlle volls verrez

Hdansunebouclefor,pluttquel'oprateurcleprincrmentation,bienque
I'effet en soit le mme dans ce cas. Il n'y a pas d'autres rai.sons cela que I'habitude et le fait que a a I'air plus cool. (On m'a dit que a marchait trs bien pour briser la glace. Rien n'est moins str, mais vous pouvez toujours essayer d'exiber votre code, tout hasard.)

La boucle f or a aussi une variante clont je ne peux pas faire semblant de comprendre la raison d'tre. Si la condition logique est omise, elle est considre comme vraie. Par consquent, f ol I : ;) produit une boucle

infinie.

.$a/ ft?ll NV|

Vous verrezeffectiven-rent f o r ( i ',',utilise pour raliser une boucle infinie beaucoup plus souvent Que ,,1,i 1e (r i ,re ) . Pourquoi ? Je tr'en ai pas la moindre ide.

Des boucles imbriques


Une boucle peut tre place I'intrieur cl'une autre boucle
:

for(
t

,condition
\

.)
.)

fnr l'
!vr

,r1tr
.

conditi.on

t
I

corps de 1a boucle

l
l

t(

-.sg4le 1g )

Une boucle incluse dans une autre est entirement excute chaque passage de la boucle qui ia contient'

U"" boucle incluse

dans une autre boucle est appele une boucle imbrique.

e4

Deuxime partie : Programmation lmentaire en G#

Des boucles imbriques ne peuvent pas tre "entrelaces". Par exemple,

ce qui suit n'est pas possible


do
{

I ldbut d'une boucle

do

for(
{

.)

I ldbut d'une boucle for

] while (
]

.)

I I

ltin lfin

de 1a boucle do.. while de 1a boucle for

Je ne suis mme pas trs sr de ce que a voudrait dire, mais c'est sans

importance, puisque de toute faon c'est illicite.


Une instruction break dans une boucle imbrique ne fait sortir que de la

boucle dans laquelle elle se trouve.


Dans I'exemple suivant, I'instruction revenir la boucle A :
L,

i'r.?,'r'.

fait sortir de la boucle B, et

// boucle for A .condition for(


{

.)

// boucle for B for( .autre condi.tion


{

,)

II if (. t

. .

corps du code de La boucle .) condition

break:
]
] ]

llfait sortir de 1a boucle

B nais pas de

C# n'a pas de commande

:, r'g.11.,

eui fasse sortir simultanment des deux

boucles.

^v7-y eHq9 \/

1t${a. \
)

Ce n'est pas une linritation aussi importante qu'il y parat. En pratique, la

logique souvent cornplexe de telles boucles imbriques est mieux encapsule dans une fonction. L'excution d'un rerurn I'intrieur de n'importe quelle boucle fait alors sortir de la fonction, donc de toutes les boucles imbriques, quelle que soit la profondeur laquelle on peut se trouver. Je dcrirai les fonctions au Chapitre 7.

Ghapitre 5 : Gontrler le flux d'excution d'un

programme

I5

..rtlga p" I 'l* r ffit-'l

Hl

Le saugrenu programme Display'XWithNestedLoo;:s utilise deux boucles imbriques pour afficher un grand X sur la console de I'application :

// I
{

DisplayXWithNestedloops
System;

- utilise

deux boucles iurbriques pour dessiner un X

using

nanespace DisplayXWithNestedloops

publi.c class Class1


t

public static void Main(string[] args)


t

int nConsolel,lidth = 40; // itre sur 1es lignes du "cadre" for(int nRowNum = 0; nRowNun ( nConsoleWidth: nRovNum {= 2)
t

// itre naintenant sur les colonnes for (int nColumnNun = 0; n0olunnNun ( nConsoleWidth;
nColumnNum#') t I I

char c

= ' t; I si Ie numro de la ligne est ga1 celui de 1a colonne... if (nColumnNun == nRovNum)


i

I Ie

caractre par dfaut est un espace

c
l

IL ='\\':

renplace 1'espace par un backslash

ll si 7a colonne est du ct oppos de la ligne... int nMirrorColunn = nConsoleWidth - nRovNum; if (nColunnNun == nMirrorColumn)
{

renplace 1'espace par un slash

',
'l

c o'/';

affiche 1e caractre correspondant I'intersection de la ligne et de la colonne Console.l4rrite(c);


I

I I

Console.l.lriteline 0 l

ll attend, confirmation de 1'utilisateur Consol.e.l.lriteline("Appuyez sur Entre pour ternriner...")


Console,Read0;

96

Deuxime partie : Programmation lmentaire en

C#

l l

Ce programme commence par dfinir un nombre arbitraire de lignes et de colonnes, reprsentant la taille du X dessiner. Si vous augmentez ce nombre, le X sort de la fentre de I'application.

or pour raliser I'itration sur les lignes du X. I'intrieur de celle-ci, il entre dans une seconde boucle for qui ralise I'itration sur les colonnes de I'affichage. Ce procd dfinit une matrice d'affichage. Il ne reste plus qu' dcider quelles cellules de la matrice recevront un espace, ce qui les rendra invisibles, et lesquelles recevront un caractre. Remplissez les bonnes cellules, et vous aurez un X.
Le programme commence par dfinir une variable c de type char, qu'il initialise avec un espace qui sera sa valeur par clfaut. Il compare ensuite le numro de la ligne et celui de la colonne. S'ils sont gaux, il remplace I'espace par une barre oblique inverse (backslash).

Ce programme utilise une boucle f

=Qt "t\

:::l(rtllJ"il?'i;Ji'n""

Souvenez-vous que le backslash est utilis pour indiquer les caractres re caractre de nouverre rigne Le caractre

Par lui-mme, le remplacement de I'espace lorsque le numro de la ligne est gal au numro de la colonne trace une ligne du coin suprieur gauche de Ia matrice au coin infrieur droit. Pour obtenir un effet miroir, le prograrnme place une barre oblique ('/') lorsque le numro de la colonne symtrique est gal au numro de la ligne.
Ce qui donne le rsultat suivant
:

Ghapitre 5 : Contrler le flux d'excution d'un programme

97

\ \ \ \
Appuyez

sur Entre pour terminer...

Il y a des choses plus utiles, mais c'est amusant.

*-o"uli3
f--t-i..(.)

I t\t

voulez tre srieux, allez voir I'exemple DisplaySin, qui utilise le mme genre de logique pour afficher verticalement une ligne sinusoidale dans la fentre de I'application. Je suis peut-tre un excit (et mme certainement), mais j'aime beaucoup ce programme. C'est sur des programmes de ce genre qu'il m'est arriv de me casser les dents.
Si vous

L'instruction de contrle s\^/i t

chl

Il vous arrivera souvent de vouloir tester la valeur d'une variable. Par exemple, nl"laritaisiatus pourrait valoir 0 pour signifier "clibataire", 1 pour "mari", 2 pour "divorc", 3 pour "veuf", et 4 pour "c'est pas vos oignons". Afin de reconnatre ces diffrents cas, vous pouvez utiliser une srie d'instructions if :

if (nMaritalStatus =:
t

0)

I ]

I ldoit tre clibataire I . instructions.

e1s e t

if (nMaritalStatus == 1)
I

//doit tre nari


I

.utres instructions

Et ainsi de suite.

Vous pouvez vous rendre compte que la rptition de ces instructions if est un peu fastidieuse. Il est si courant d'avoir tester des cas multiples que C# offre une structure spciale pour faire un choix dans un ensemble

98

Deuxime partie : Programmation lmentaire en C#

de conditions mutuellement exclusives. Cette instruction s'appelle switch et fonctionne de la faon suivante :
switch (nMari.talStatus
{
)

case 0:
I
^^^^ 1.

I I I I I

instructions si clibataire. instructions si mari. instructions si divorc. instructions si veuf.


al1ez vous rhabiller.

break;
LA l.

break;

case 2:
I

break;

case 3:
I

break;

case 4:
I

break;

default:

//c'est
break; break;
]

I lpasse ici quand aucun cas ne correspond probablement une condition d'erreur
;

L'expression qui se trouve en haut de l'instruction swit,-h est value. Dans ce cas, c'est simplement la variable nflaritaiS ratus" La valeur de cette expression est alors compare celle qui suit chaque mot-cl case. Si elle ne correspond aucun de ces cas, le contrle passe directement la condition,lefault. L'argument de I'instruction switcl'r peut aussi tre de type st string s = "Davis";
switch
{
(

ring

s)

case ttDavistt:
I

I I
"
:

le contrle

passera par ici..

break; case "Srnith":


I

instructions si mari. instructions si divorc.

1^-^^1,.

cse "Jones
I

Chapitre 5 : Contrler le flux d'excution d'un programme

99

break;

case "Hvidsten":

tl

instructions

si veuf.

break;

default:

/l
j

o1t^r1n n.c passe i^.i.rro..l r.u anrrecnnnd rLr 9UOllU AULUII LAJ n

break;

L'utilisation de I'instruction swirci, comporte quelques contraintes svres:

,/

L'argument de

s-,,;i

rch O doit tre

cl'ur-r

type admis comme compteur

ou de type str:iii:.,.

,/
t/ t/

Les valeurs en virgule flottante sont exclues.

Lesvaleurs de case doivent tre de mme type que I'expression de


sr,,ui

t ch.

Les valeurs de r-S doivent tre des constantes au sens o leur valeur doit tre connue lors de la compilation (une instruction telle que case x st illicite, moins que x ne soit une constante).

t/

Chaque clause cese doit se terminer par une instruction breat", (ou autre commande de sortie dont nous n'avons pas encore parl, comme return). Cette commande de sortie fait sortir le

contrle de I'instruction switcl'.


Cette rgle a toutefois une exception : une mme clause cas- peut comporter plusieurs fois le mot-cl i:,?,s-, comme dans I'exemple suivant :

string s = "Davis";
switch (s)
{

case ttDavigtt: case "Hvidsten':

lltait
case "Smith":
I

1a mme chose pour Davis ou Hvidsten


mme

I lcar leur si.tuation est la


break;

I I
passe
;

instructions si. nari.

break;

default:
I
b

ici

quand aucun cas ne corresponi

reak

100

Deuxime partie:Programmation lmentaire en

C#

Ce procd permet au programme cl'excuter les mmes oprations, que

le contenu de la chalne soit "Davis" ou "Hvidsten".

Le modeste goto
Vous pouvez aussi transfrer le contrle d'une manire non structure en utilisant I'instruction goto. Elle est suivie par I'un des lments suivants:

t/ t/ t/

Une tiquette.

Un case d'une instruction sw. rch.


Le mot-cl def au1t, reprsentant la clause par dfaut d'une instruc-

tion sw,tch.
Le fragment de code suivant montre comment est utilise I'instruction

gcto

I lsl 7a condition est vraie. if(a)b) i

IL
]
I

le contr1e
.

passe de goto

1'tiquette spcifie

goto exitlabel;

quel que soit


:

1e

r"odc

nrri s trnrve ici,

exitlabel

//le eontrle nasse ici

L'instruction goto est impopulaire, pour les mmes raisons qui en font une commande de contrle si puissante : elle est presque entirement dpourvue de structure. Si vous I'utilisez, il peut tre extrmement difficile de maltriser le flux de I'excution au-del d'un petit morceau de code particulirement trivial.

.$sc ./ t\?, Y

L'utilisation de goto a dclench quasiment des guerres de religion. En tion. En ralit, goto n'est ni si horrible ni ncessaire. Comme vous pourrez presque toujours viter de vous en servir, je vous recommande de vous en tenir bonne distance.

Hfait,lelangageC#lui-mmeatcritiqupouravoiradoptcetteinstruc-

Tioisime partie

Programmation et obiets
'yoil {Trzan en avoir rnrre I Encore mauvais message I Quoi a veut dire zt TarZan tout essayer lTrzan furieux
comrne Cheetah
t

Dans cette parte...


ne chose est de dclarer une variable ici et l pour faire des additions et des soustractions ; tout autre chose est d'crire de vritables programmes que les gens peuvent utiliser (des gens ordinaires, mais des gens). Dans cette partie, vous allez dcouvrir comment regrouper des donnes et faire des oprations sur ces donnes. Ce sont les connaissances de base ncessaires tout travail de programmation, que vous veryez souvent dans les offres d'emploi.

Ghapitre 6

Rassembler des donnes classes ettableaux


Dans ce chapitre :
Les classes en C#.

Stocker des donnes dans un objet. Assigner et utiliser une rfrence un objet. Crer et gnrer un tableau d'objets.

ous pouvez librement dclarer et utiliser tous les types intrinsques, tels que in i, ':1.,.iib1e et bocl, afin de stocker les informations ncessaires vos programmes. Pour certains programmes, de si simples variables ne suffisent pas. Toutefois, nombre de programmes ont besoin de rassembler sous forme d'ensembles pratiques les donnes qui sont en relation les unes avec les autres. Certains programmes ont besoin de rassembler les donnes qui appartiennent logiquernent un mme groupe mais ne sont pas pour autant de mme type. Par exemple, une application utilise par une universit traite des tudiants, chacun ayant son nom, la moyenne de ses notes, et son numro d'identification. Logiquement, le nom peut tre de type s*r-ring, la moyenne des notes de type double, et le numro d'identification de type 1ong. Un programme de ce type a besoin de runir toutes ces variables de types diffrents dans une mme structure nomme Stucient. Heureusement, C# offre une structure appele c/asse qui permet de regrouper des variables de types diffrents.

|04

Troisime partie : Programmation et obiets

Dans d'autres cas, un programme aura besoin de rassembler une srie d'objets de mme type. Prenez par exemple un programme qui calcule la moyenne gnrale des notes d'un tudiant sur I'ensemble d'un cycle d'tudes. Comme on veut que la prcision du rsultat final ne soit pas affecte par I'arrondi des moyennes intermdiaires, le type double est ce qui convient le mieux pour la moyenne de chaque matire pour chaque anne. Il faudra donc une forme ou une autre de collection de variables de type double afin de contenir toutes les moyennes annuelles pour chaque matire. C'est dans ce but que C# permet de raliser un tableau. Enfin, un vritable programme de traitement des donnes sur les tudiants aura besoin de dfinir des groupes d'tudiants par diplme. Un tel programme devra alors faire fusionner la notion de classe et la notion de tableau pour raliser un tableau d'tudiants. Par la magie de la programmation en C#, c'est ce que vous pouvez faire aussi.

lVlontrez uotre classe


Une classe est une runion de donnes et de fonctions dissemblables, dans un mme petit ensemble bien ordonn. C# vous donne la libert de faire des classes aussi mal fichues que vous voulez, mais une classe a pour but de reprsenter un concept. Les analystes disent : "Une classe introduit dans le programme une carte du problme rsoudre." Par exemple, imaginez que vous vouliez raliser un simulateur de trafic. Celui-ci va reprsenter le trafic, dans le but de raliser de nouvelles rues, avec des intersections ou mme des autoroutes. J'aimerais bien que vous fassiez un simulateur de trafic qui rsoudrait le problme de I'intersection devant chez moi.

Toute description d'un problme concernant le trafic comporterait le terme uhicule. Un vhicule a une vitesse maximale, qui doit avoir sa place dans les quations. Il a aussi un poids, et certains sont purement et simplemer-rt des paves. D'autre part, ur-r vhicule peut dmarrer et s'arrter. La notion de uhicule f.ait donc partie du problme rsoudre.
Un bon programme de simulation de trafic en C# comprendrait ncessairement la classe Vehicle, dans laquelle seraient dcrites les proprits significatives d'un vhicule. La class r,'L-hiir,t e aurait des proprits telles eue d f opSpeeo, n;ielght, et bClunker. Je parlerai des proprits stop et g.i au Chapitre 7.

Ghapitre 6: Rassembler des donnes: classes et

tableaux

| 05

Dfnir une classe


La classe Vehi c 1e pourrait par exemple se prsenter ainsi
:

public class Vehicle


{

public string sModel; public string sManufacturer; public int nNun0fDoors; public int nNum0fWheels;
j

I I // //
I I

non du nod1e non du construcreur nomhrp d nortcs du vhicule nombre de roues du vhicule

La dfinition d'une classe commence par les mots du nom de la classe. dans ce cas. \rehic1e.

pubiic c 1ass, suivis

Yi

.gtt\

o/
.'-<) \

C# fait la diffrence entre les majuscules et les minuscules dans les noms de classe, comme pour tous les autres noms utiliss en C#. C# n'impose

aucune rgle sur les noms de classe, mais il existe une rgle non officielle selon laquelle le nom d'une classe doit commencer par une majuscule.
Le nom d'une classe est suivi par une accolade ouwante et une accolade fermante. Entre ces deux accolades apparaissent les membre.s que comporte ventuellement cette classe. Les membres d'une classe sont des variables qui en constituent les lments. Dans cet exemple, la classe Vehicie commence par le membre string sModel, qui contient Ie modle du vhicule. Si c'est une voiture particulire, le nom du modle pourrait tre "Eldorado". Vous en voyez certainement tous les jours. Le second membre de notre exemple de classe Vehicle est str:ing sllanufacturer, qui contient naturellement le nom du constructeur. Enfin, les deux dernires proprits sont le nombre de portes et le nombre de roues du vhicule.

Comme pour toute variable, donnez aux membres d'une classe des noms aussi descriptifs que possible. Dans I'exemple ci-dessus, j'ai ajout des commentaires la dclaration de chaque membre, mais ce n'tait pas ncessaire. Le nom de chaque variable dit clairement de quoi il s'agit.

L'attribut publec eui prcde le nom de la classe rend celle-ci universellement accessible en tout endroit du programme. De mme, I'attribut pubiic plac devant le nom d'un membre de la classe le rend tout aussi accessible en tout endroit du programme. On peut galement utiliser d'autres attributs. Le Chapitre 11 traite en dtail la question de I'accessibilit.

106

Troisime partie:Programmation et obiets

Une dfinition de classe doit dcrire les proprits d'un objet qui joue un rle incontournable dans le problme rsoudre. C'est un peu difficile faire dans I'immdiat, parce que vous ne savez pas encore quel est le problme, mais je suppose que vous voyez o je veux en venir.

Quel est notre objet

Dfinir une classe Vehic 1e n'est pas la mme chose que de construire une voiture. Vous n'aurez pas ici emboutir de la tle ni visser des crous. Un objet classc se dclare de faon semblable, mais pas tout fait identique, un objet intrinsque.
D'une faon gnrale, le terme objet signifie "quelque chose". a ne nous aide pas beaucoup. Une variable int est un objet int. Un vhicule est un objet vehrcle. Vous-rnme, vous tes un objet lecteur. Quant moi, je

suis un auteur...
Le fragment de code suivant cre une voiture de la classe Vehic 1e
Vehicle myCar =
myCar;
ner^r
:

Vehicle0

La premire ligne dclare une variable myCar de type Vehicle, tout comme vous auriez pu dclarer un objet nQuelqueChose de la classe int. La commande nern' vehicle ( ) cre un objet de type Vehicle et le stocke dans la variable rlyCar. Le new n'a rien voir avec l'ge de myCar. Cette commande cre une nouvelle zone de mmoire dans laquelle votre programme pourra stocker les proprits de myCar. Dans la terminologie C#, on dira que myCar est un objet de la classe Vehicie, mais aussi que mvCar est une instance de Vehicle. Dans ce contexte, instonce signifie "un exemple de", ou "un exemplaire de". On peut aussi utiliser ce terme sous forme de verbe, en parlant d'insfancier un Vehicie.

Comparez la dclaration de myCar avec celle de la variable entire

num

int
nun

h11m. 1,

Chapitre

6: Rassembler des donnes: classes et tableaux

107

[.a prernire ligne dclare la variable num, et la deuxime ligne stocke dans I'emplacement dfini par la variable nr-iin une constante dj existante de

type int.
mmoire I'objet 1t${Qa^ Il y a en fait une diffrence dans la rnanire de stocker en \ intrinsque nun et I'objet mvCar. La constante 1 n'occupe pas de mmoire, ^.v7q car le CPU et le compilateur C# savent dj I'un et I'autre ce qu'est un "1". N{ais votre CPU ne sait pas ce qu'est un ehicle. L'expression ne,,r -,i ehic Le alloue l'espace mmoire ncessaire la description d'un objet ,,'-.hrcJ e pour le CPU, pour C#, et pour le reste du monde.

'(cg,

Accder aur membres d'un objet


Tout objet de la classe Vehicle a ses propres membres. L'expression suivante stocke le nombre I dans le membre nlJumberOf Dcors de I'objet rfrenc par rnvCar :
mvCar.nNumberOfDoors

1;

e(dw
\.,

Aa,/

1r!!{a"

tarl \ valeur. L'objet


)

Toute opration en C# doit tre value par type aussi bien que par myCar est un objet du type ehic ie. La variable ','ei''ic1e. nliuinbe,rOf Doors est cle type int (voyezla dfinition de la classe Vehicle). Comme la constante 5 est aussi de type int, le type de ce qui est droite de l'oprateur d'assignation est le mme que celui de ce qui est gauche.
De mme, le code suivant stocke une rfrence aux chalnes dcrivant le modle et le nom du constructeur de myCar :
nyCar.sManufacturer =
myCar,sModel

= "Izeta";

I'BMI,I"; // ne perdez pas espoir I / c'est une poque disparue

(L'lzeta tait une petite voiture construite pendant les annes cinquante, dont I'unique porte constituait toute la face avant.)

I 08

Troisime partie : Programmation et objets

Soyons ringard$ : pourquoi s' embter avec des classes ?


Avec le temps,l'difice des classes a pris de l'importance dans les langages de programmation. Si vous examinez la chane que forment les principaux langages, et leurs priodes de popularit maximale, vous p0uvez y distinguer le schma suivant ;

,t
tl

Fortran (de la fin des annes cinquante au dbut des annes quatre-vingtl : pas de notion de classe.

C{delafindesannessoixante-dixaudbutdesannesquatre-vingtdix} :lesclasses
ne sont utilises qu' des fins d'organisation. ll est possible d'crire des progrrnmes

qui n'en font aucun usage.

t/

C++ {du milieu des annes quatre-vingt aujourd'hui) : la notion de classe y est beaucoup plus volue, ll esttoujours possible d'crire des programmes qui ne s'en servent pas, mais seulement en se limitant un sous-ensemble du langage.

,/ ,/

Java (du milieu des annes quatre-vingt-dix auiourd'hui) : la notion de classe y est fondamentale. lmpossible d'crire du code sans y avoir recours.
C#

(aujourd'hui) : comme Java.

La notion de classe a pris une importance croissante parce que les programmeurs se sont rendu compte que les classes taient trs efficaces pour reprsenter des objet du monde rel. lmaginez par exemple que je sois en train d'crire un programme de gestion de comptes bancaires. Un compte bancaire prsente des caractristiques telles que le nom de son titulaire, le numro du compte, le solde, et le nom de la banque. 0r, je sais bien que ces proprits font partie d'un mme ensemble, car elles dcrivent toutes le mme objet: un compte de ma banque. Connatre le solde sans connatre le numro du compte correspondant, par exemple, n'a aucun sens.

string contenant contenant le numro du compte, une variable double ou dec ima1, contenantle solde, une variable st ring contenantle nom de la banque, et ainsi de suite. Un seul objet de la classe BankAccount contient donc toutes les proprits. pertinentes pour mon problme, d'un compte bancaire donn.
En C#,ie

peux crerune classe BankAccount, associe une variable

le nom du titulaire, une variable

int

Ghapitre

6: Rassembler des donnes: classes et tableaux

109

Un exemple de programmes base d'objets


Le trs simple programme suivant, VehicleDataOnlr'
:

t/ t/ t/ t/

Dfinit la classe ehic1e.


Cre un objet nyCar. Assigne les proprits de myCar. Rcupre ces valeurs dans I'objet pour les afficher.
cre un objet de type Vehicle,
donne une

|/ lr"hinlalaraonlrr

valeur ses nembres partir des saisies de 1'utilisateur, et affiche 1e tout

"^:-,uDrrr

C.,^+^*. J D LErl,

namespace VehicleDataOnly
t
r

public class Vehicle


{

nublic -strins sModel; I / norn du modle -- -"D public string sManufacturer; | / non, du constructeur public int nNum0fDoors; / I nombre de portes du vhicule public int nNum0fWheels; I I nonbre de roues du vhicule
I
)

_,,L1.t^ PUUAIL {

^1^^_ \_fd

.,1 -SS1 Urd

I t t

C'est

static

ici que commence 1e programme voj-d Main(stringIJ args)


1'utilisateur
;

//
I

denande son non

Console.Writeline("Entrez 1es proprits de votre vhicule")

cre une instance de Vehicle


myCar

Vehicle

= new Vehicle0;

une variable pour donner une valeur un membre Console.Write("Notn du rnodle = ") ;
myCar. sModel

// utilise

string s = Console.Readline{); = s;

I I on peut ussi donner une valeur un menbre directenent Console,l,Jrite("Nom du constructeur = tr); myCar.sManufacturer = Console.neadtine0 ;

I leetwe du reste des donnes


= ");
;

Console.Write("Nombre de portes s = Console.Readline{) ;


myCar.nNumOfDoors

= Convert.Tolnt32(s) tonsole.l.Jrite("Nonbre de roues = ");

s = Console.ReadlineO;

I |0

Troisime partie : Programmation et objets

myCar.nNum0fl{hee1s

Console.Writeline(myCar.sManufacturer *' rr + myCar.sModel) ; Console.I,iriteLine("avec " * myCar.nNun0fDoors t " portes, "

= Convert.ToInt32 (s) ; affiche maintenant 1es rsultats Console .1,{riteLine (" \nVotre vhicule est une r') ;

/l

attend confirmation de 1'utilisateur Console.lJriteline("Appuyez sur Entre pour terminer. . .")


Console.Read0;

/l

* "sur " { my0ar.nNum0fWheels * " rouesrt)


;

Le source de ce programme commence par une dfinition de la classe

Vehi

c 1 e.

La dfinition d'une classe peut tre place avant ou aprs Ciassl. C'est sans importance. Adoptez simplement un style, et gardez-le. Le programme cre un objet myCar de la classe Vehicle, puis remplit chacune de ces proprits en lisant ce qui est saisi au clavier par I'utilisateur. Il n'y a pas de vrification de la validit des donnes. Le programme restitue alors l'cran, dans un format lgrement diffrent, les donnes saisies par I'utilisateur.

L'excution de ce programme affiche les rsultats de la faon suivante


Entrez 1es proprits de votre vhicule du mod1e = Metropolitan Nom du constructeur = Nash Nombre de portes = 2
Nom

Nombre de roues

Votre vhicule est


\T^^L r"lelropolt I\asn M^+-^-^1.i+

une

lan

Appuyez

avec 2 portes, sur 4 roueg sur Entre pour terniner..,

ta aifference de Readline O, les appels Read ( ) laissent le curseur la fin de la chalne affiche. La saisie de I'utilisateur apparalt donc sur la mme ligne que I'invite. D'autre part, I'ajout du caractre de nouvelle ligne, '\n', produit une ligne blanche I'affichage, sans avoir utiliser pour cela driteline O.

Ghapitre 6:Rassembler des donnes: classes et

tableaux

I I I

Distinguer

les objets les uns des autres

Un constructeur automobile sait identifier sans erreur chaque voiture qu'il produit. De mme, un programme peut crer de nombreux objets de la mme classe :
Vehiele carl = nerr VehicleO;
ear1. sManufacturer
I

carl. sModel = "Avanti"

= "Studebaker" ;
I

ee qui

suit est

sans

effet sur carl

Vehicle earl = new Vehicle0;


car2. sManufacturer = "Hudson"; car2. nVehicleNanePart = "Hornet"
:

objet carT et que vous lui assignezle nom de constructeur "Hudson", a n'aura aucun effet sur la Studebakr carI
Si vous crez un

La capacit de distinguer les objets les uns des autres constitue une partie de la puissance de la notion de classe. L'objet associ la 2 CV

Citron peut tre cr, manipul ou mme ignor, comme une entit part entire, distincte des autres objets, y compris I'Avanti (bien que ce soient toutes deux des classiques).

Poutlez-(lntts me donner des rfrences

L'oprateur point et I'oprateur d'assignation sont les deux seuls oprateurs dfinis sur les types de rfrence :
I

cte une rfrence nul1e

Vehicle yourCar; l/ assigne une valeur la rfrence yourCar = new Vehicle0 I


yourCar. sManufacturer = "Rambler" ; ll cre une nouvelle rfrence et 1a Vehicle yourSpousalCar = yourCar;

fait

pointer vers 1e mrne objet

La premire ligne cre un objet yourCar sans lui assigner une valeur. On dit qu'une rfrence qui n'a pas t initialise pointe vers I'o7et nu7l. Toute tentative d'utiliser une rfrence non initialise produit une erreur

immdiate qui met fin I'excution du programme.

| |2

Troisime partie:Programmation et objets

:(d

ler

6${ea^ -!3\ Le compilateur

W ) \/ rfrence non initialise met fin immdiatement

C# est capable d'identifier la plupart des tentatives d'utiliser une rfrence non initialise, et d'afficher un avertissement lors de la gnration. Si une telle erreur n'est pas dtecte par le compilateur, tout accs une
I'excution du programme.

La deuxime instruction cre un nouvel objet Vehic 1e, et I'assigne yourCar. La dernire instruction de ce fragment de code assigne la rfrence yor-lrSpousalCar la rfrence yourCar. Comme le montre la Figure 6.1,Ie rsultat de cette instruction est que yourSpousalCar se rfre au mme objet que yourCar.

Figure 6.1

La relation

..--yourcut\

rf ren ces
au mrre objet.

entre deux

iii?i1:J, />tr"h'*l l-....--l "Rambler" yourSpo usalCar-/


Y
I

Les deux appels suivants ont le mme cffet

/l construisez votre voiture Vehicle yourCar = new Vehicle0;


yourCar,sModel = "Ford T";
I I eIIe appartient aussi votre femne Vehicle yourSpousalCar = yourCar; I I si 1'une change, 1'autre change aussi yourSpousalCar,sModel = "Daytona" ;

Console.l,Jriteline("votre voiture est une t' + yourCar,sModel);

L'excution de ce programme afficherait "Da5rtona", et non "Ford T". Remarquez que yourSp<rusalCar ne pointe pas vers yourCar. Au contraire, ce sont les deux qui se rfrent au mme vhicule.
En outre, la rfrence yourSpousalCar serait encore valide, mme si la

variable yourCar tait d'une manire ou d'une autre "perdue" (se trouvait hors de porte, par exemple) :

// construisez votre voiture Vehicle yourCar = new Vehicle0;


yourCar,sModel = "Ford Tr';
II I

I ^11 '+rsJ11 Ir dPydrLrrIL

votre vvLl fenme !uus aussi q duDf

Ghapitre 6 : Rassembler des donnes : classes et tableaux

n3

Vehicle yourSpousalCar = your0ar; ll ryand e11e s'en va avec votre voiture. yourCar = nu1l; I I yourCar rfrence maintenant "1'objet

NULL"

le mme vhicule Console.l^lriteLine("Votre voiture tait une " * yourSpousalCar.sModel)


yourSpousalCar rfrence toujours

IL

L'excution de ce programme affiche le rsultat "Votre voiture tait une Ford T", bien que la rfrence vou rCar ne soit plus valide.

frg)

qt$!Qa" L'objet n'est plus cccessible partir de la rfrence yourCar. Il ne devient pas compltement inaccessible tant que )'olrrCar t yourSpousallla: ne sont pas "perdus" ou annuls.

Les classes qui contiennent des classes sont les plus heureuses du monde
Les membres d'une classe peuvent eux-mmes tre des rfrences d'ar-rtres classes. Par exemple, un vhicule a un moteur, qui a une puissance et cliff rents paramtres qui dfinissent son efficacit (mais un vlo n'a pas de cylindre). On peut introduire ces diffrents facteurs dans la classe, comme ceci :
-.,L1.: ^ Lr4 ^1^^- \r^L.: VErlfL ^1e PUUTTL
{

public string sModel; public string sManufacturer;


^'.11 yuvrll

;^ i-+ -1,.-nfDOOfS; rllL llllqltlv


-T'.^^f\nJheelS; rrrrul[v

;-+ rlrL yu!ffL ^"L1.i^

public
nrrhl yuvlrL i^

int

nPower;
licnln.'pmpnt. Urryrq!Lu!rrL,

lnrrhla uvuua

I I // // // //
I I

non du modle nan du constructeur noinhrp d nortes du vhicule nombre de roues du vhicule puissance du moteur (Chevaux-Vapeur) cylindre du moteur (litres)

Toutefois, la puissance et la cylindre du moteur ne rsument pas toutes les caractristiques cle la voiture. Par exemple, la Jeep de mon fils est propose ivec deux moteurs diffrents qui lui donnent une puissance compltement riiffrente. La Jeep de 2,4litres de cylindre est un veau, alors que Ia mme voiture quipe du moteur de 4 litres est plutt nerveuse.

Autrement clit, le moteur est une entit lui seul et mrite sa propre classe
class Motor
f I
n"hlin yuVI rL int lll L nDnr.rar. lMWq!
,

/i

puissance du moteur (Chevaux-Vapeur)

I I tl

Troisime partie : Programmation et obiets

public double displacernent;


1

/l

cylindre du moteur (litres)

Et vous pouvez utiliser cette classe dans la classe

Vehicle

public class Vehicle


t

public string sModel; public string sl{anufacturer; public int nNurn0fDoors ; public int nNun0fWheels; public Motor notor;

/ I l/ II
I

non du mod1e
non du constructeur nombre de portes du vhicule

nonbre de roues du vhicule

La cration de mvCar se prsente maintenant ainsi:

lcr.ons d'abord un

t'lotor LargerMotor

new Motor0; largerMotor.nPower = 230:

objet de 1a classe

Motor

largerMotor.displacenent = 4.0i I I crons maintenant 1a voiture

Vehicle sonsCar = nel'I Vehicle0;


sonsCar.sModel

"Cherokee Sport";

sonsCar. sManfacturer "JeeP"; sonsCar.nNumberofDoors = 2 ; sons0ar.number0fWheels = 4;

llmettons un noteur dans 1a voiture


sonsCar.notor = largerMotor
;

L'objet de la classe Vehicle vous offre deux moyens d'accder la cylindre de son moteur. Vous pouvez procder une tape la fois :
Motor m = sons0ar.notor; Console,Writeline("La cylindre du noteur est

"*

m.displacenent);

Ou alors. v accder directement

Console.Idriteline("La cylindre du noteur est

"*

sonsCar.motor.displacernent);

D'une manire ou d'une autre, vous ne pouvez accder la cylindre (displacemenl-) que par I'objet de la classe Motor.
,c$ARG'f

;..

ffi =,

Cet exemple fait partie du programme VehlcleAndMotor QUi se trouve sur le site \\:eb.

Ghapitre 6: Rassembler des donnes: classes et

tableaux

| |5

Les metnbres staques d'une classe


La plupart des membres d'une classe servent dcrire chaque objet de cette classe. Voyez la classe Car :
nrrhlin
{

nlnc< (ler

public string slicensePlate;


]

//1e

numro d'

innatriculation

Le numro d'immatriculation est une proprit d'objet, ce qui signifie qu'il clfinit individuellement chaque objet de la classe Car. Par exemple, vous avez de la chance que ma voiture n'ait pas le numro ci immatriculation que la vtre. a pourrait vous attirer des ennuis.
Car nyCar =
myCar.
ner^r Car

slicensePlate =

"XYZ123'r

Car yourCar = net$I Car0; yourCrr. slicensePlate = "48C789"

Mais il y a aussi des proprits partages par toutes les voitures. Par exemple, le nombre total de voitures construites est une proprit de la classe Car, et non de quelconque objet. Un tel membre d'une classe est appel proprit de closse, et est identifi en C# par le mot static :
nrrhlin
{

nl.c< Crr

public static int nNumber0fCars; /lnombre de voitures construites public string slicensePlate; l11e nuurro d' innatriculation
]

Ce n'est pas par un objet de la classe qu'on accde un membre statique,

mais par la classe elle-rnme, comme le montre cet exemple


I I cr6e un nouvel objet de 1a classe Car Car newCar = netri Car 0 ; new0ar.
I

Car

slicensePLate = "48C123" ; incrnente le nombre de voitures pour . nNurnbe r0fCars** ;

tenir conpte de 1a nouvelle

On accde au membre d'objet newCar. sLicensePiare par I'objet newCar, alors qu'on accde au membre de classe (statique) Car. nl'iumloerOfCars par la classe Car.

| |6

Troisime partie : Programmation et objets

Dfinir des membres de trlpe const


Le type const est un type spcial de membre statique. La valeur d'une variable const doit tre tablie dans la dclaration, et vous ne pouvez la changer nulle part dans le programme :

class C1ass1
{

// nonbre de jours dans 1'anne public const int nDayslnYear = 366; public static void Main(stringil args)
{

int
{

for(int
/

[]

nMaxTemperatures

index =

0; index (

new

int

[nDaysnYear]

nDayslnYear; index**)
chaque

I .additionne la temprature naxinale pour /l jour de 1'anne.

Vous pouvez utiliser en n'importe quel endroit de votre programme la constante nDavs InYear la place de la valeur 366. L'utilit d'une variable de type const est de remplacer une constante dpourvue de signification telle que 366 par un nom descriptif comme nf avs InYear, ce qui rend le programme plus lisible.

Les

tableauv : la classe

An-,-r-, lf r r d_y

Les variables qui ne contiennent qu'une seule valeur sont bien pratiques, et

les classes qui permettent de dcrire les objets composites sont cruciales. Mais il vous faut aussi une structure capable de contenir un ensemble d'objets de mme type. La classe intgre 'lr ra.,/ est une structure qui peut contenir une srie d'lments de mme type (valeurs de type 1nt, double, et ainsi de suite, ou alors obiets de la classe \iehi,---e, i{or-or:, et ainsi de suite).

Les arguments du tableau


Considrez le problme du calcul de Ia moyenne d'un ensemble de dix nombres en virgule flottante. Chacun de ces dix nombres ncessite son

Chapitre 6 : Rassembler des donnes : classes et tableaux

n7

propre stockage au format double (calculer une moyenne avec des variables int pourrait produire des erreurs d'arrondi, comme nous I'avons dit au Chapitre 3)
:

double d0 = 5. double dl = ), double d2 = J. double d3 double d4 = 5. d0uble d) * R, double d6 * I' double d7 = Qr double d8 = 1. double d9 = f .

Vous devez maintenant faire la somme de toutes ces valeurs, puis Ia diviser par 10 (le nombre de valeurs) :
double dsun = d0 + dl + d2 + d3 + d4 + d5 + d6 + double dAverage = dSum / 10;

di + d8 + d9;

Il est un peu fastidieux d'crire le nom de chacun de ces lments pour en faire la somme. Passe encore si vous n'avez que dix nombres, mais imaginez que vous en ayez cent ou meme mille.

Le tableau longueur fixe


Heureusement, vous n'avez pas besoin de nommer chaque lment sparment. C# offre une structure, le tableau, qui permet de stocker une squence de valeurs. En utilisant un tableau, vous pouvez rcrire de la faon suivante le premier fragment de code de la section prcdente :
double[] dArray =

(.5,2,7,3,5, 6.5, 8, 1, 9, 1, 3l;

-$ttK La classe Array prsente une syntaxe spciale qui la rend plus facile -f il ) utiliser. Les doubles crochets ll reprsentent Ia manire qui permet Wt/
d'accder aux diffrents lments du tableau
dArray[0] correspond dArray[1] correspond
d0 d1
:

I |8

Troisime partie : Programmation et objets

L'lment numro 0 du tableau correspond d0, l'lment numro et ainsi de suite.

1 d1,

Les nurnros cles lments du tableau (0, 1 ,2, eI ainsi de suite) constituent l'index.

#i e,

L'index d'un tableau commence 0, et non l. Par consquent, l'lment du tableau correspondant I'index I n'est pas le premier lment, mais l'lment numro 1, ou "l'lment I de I'index". Le premier lment est l'lment numro 0. Si vous voulez vraiment parler normalement, souvenez-vous que le premier lment est I'index 0, et le deuxime I'index 1.
dAr ray ne constituerait pas une grande amlioration sans la possibilit

une boucle

d'utiliser une variable comme index du tableau. Il est plus facile d'utiliser f o r que de se rfrer manuellement chaque lment, comme le montre le programme suivant :
i

FixedArrayAverage

calcule 1a moyenne d'un


de valeurs en

nombre dternin

utilisant

une boucle

nanespace FixedArrayAvera ge
{

-^ '.^.1 urrr5

C,,^+^-. uJ LIr,

public class Classl


{

public static int Main(stringll args)


{

double[] dArray
f5
I

=
JrJ

7
r,

? 5

R 1

1
.,

Jt

?].

I tait la sonme des valeurs I I dans 1a variable dSum


double
{

du tableau

dSum

0;

for(inti=0;i(11;i++)
dsum ]

dSun

-f dnrray[iJ

// calcule maintenant 1a moyenne double dAverage = dSum / i0;


Console . llr j.telj.ne (dAverage) I
;

I attend confirmation de 1'utilisateur


rr).

Console.hlriteline("Appuyez sur nntre pour terminer.


Console. Read

return
l
]
]

0;

Chapitre 6 : Rassembler des donnes : classes et tableaux

ng

Le programme commence par initialiser 0 la variable dSum. Il effectue ensuite une boucle sur les valeurs stockes dans dArray, en ajoutant ctracune d'elles clSum. la fin de la boucle, dSun contient la somme de toutes les valeurs du tableau. Celle-ci est alors divise par le nombre d'lments pour obtenir la moyenne. Le rsultat affich par I'excution de ce programme est 4,6, comme on pouvait I'attendre fi'ai vrifi avec ma

calculatrice).

Et si vous dpassez la
Le programme

taille du tableau

FixedArrayAverage effectue une boucle sur un tableau de dix lments. Heureusement, cette boucle passe effectivement sur tous ces dix lments. Mais si j'avais fait une erreur dans l'itration ? ll y a deux cas envisager.
Sije n'avais itr que sur neuf lments, C# ne l'aurait pas considr comme une erreur : s vous voulez lire neuf lments d'un tableau qui en contient dix, de quel droit C# viendrait-il le contester ? Bien sr, la moyenne serait incorrecte, mais le programme n'aurait aucun moyen de le savoir. Etsij'avais itr suronze lments {ou plus} ? Maintenant, a regarde beaucoup C#.C#ne vous permet pas d'indexer au-del de la taille d'un tableau, de crainte d'craser une valeur importante dans la mmoire. Pour le vrifier, j'ai remplac le test de comparaison de la boucle

forpf cequisuit:for(int

i:0; i (

11; i++),enremplaant10par11.L'excution
:

du programme a produit I'erreur suivante {en franais et en anglais dans le texte) Exception non gre : Systen.Index0ut0fRangeException : une exeeption qui de type Systen.Index0ut0fRangeException a t 1eve. at FixedArrayAverage.Classl.Main(StringIJ args) in c : \c#programs\ fixedarrayaverage\class1 . cs: line l7

Au premier abord, ce message d'erreur parat imposant, mais on peut facilement en saisir l'essentiel : il s'est produit une erreur IndexOut0fRangeException. ll est clair que C# indique que le programme a essay d'accder un tableau au-del de ses limites (le onzime lmentd'un tableau qui n'en cornporte que dix). La suite du message indique la ligne exacte laquelle s'est produite cette tentative d'accs, mais vous n'avez pas encore assez avanc dans ce livre pour comprendre tout ce qu'il vous dit.

Chapitre

6:

Rassembler des donnes: classes et

tableaux

l2 |

for (int i = 0; i (
{

I I dclare un tableau de 1a tail1e correspondante double[] dArray : new doublelnumElenrents]; I I rennf it ie tahleau avec les valeurs

numElements; i++)

// demande 1'utilisateur une nouvelle valeur Console.Write("Entrez la valeur nerr + (1 + 1) + string sVal = Console.ReadLine0; double dValue = Convert.ToDouble(sVa1) ; // stocke 1a nouvelle valeur dans 1e tableau
dArrayIi] =
i
I I

": ");

dValue;

I lait 1'addition de 'nunElenents' I du tableau dans 1a variable dsum


dSum

valeurs

double
{

for (int i = 0; i (
]

0;

nunELements; i++)

dsum=dSum*dArrayliJ;

// calcule maintenant 1a moyenne = dSum I nunrElements; double dAverape *"''-*b" // affiche 1es rsultats dans un fornat agrable
Console.i,.iriteline0;
Console
,

l,/rite (dAverage

* " est la for (int i = 1; i (


{

moyenne
;

de ("

+ dArray [o] )

nuniElements; i++)

Console.Write(rr ''j- rt + dArrayIi]);

Console.l.IriteLine(") I u + numElements); // attend confirmation de 1'utilisateur Console,I,rlriteLine("Appuyez sur Entre pour terminer. ..")
Console.Read0;

return

0;

Voici un exemple de rsultats affichs, pour lequel j'ai entr cinq valeurs, de I 5, dont la moyenne calcule par le programme est de 3 :
Nonbre de valeurs pour 1a moyenne

calculer

Entrez la valeur ne1: I Entrez 1a valeur ne\: 2 Entrez 1a valeur ne3: 3 Entrez la valeur neA: 4

Chapitre 6:Rassembler des donnes:classes et

tableaux

123

// derrande 1'utilisateur une nouvelle valeur Console.hlrite("Entrez 1a valeur nett + (i. + t) * string sVal = Console.ReadlineO; double dValue = Double.FromString(sVa1) ; l/ stocke la nouvelle valeur dans 1e tableau
dArrayIi] =
]
dValue;

": ");

Le tableau dAr r a-; St dclar comme ayant une longueur de numElements. L'astucieux programmeur (moi-mme) a donc utilis une

boucle f or pour itrer numElements fois sur les lments du tableau.


Il serait lamentable d'avoir trimbaler partout avec dArray la variable numElements, rien que pour conntre la longueur du tableau. Heureusement, ce n'est pas ncessaire. Un tableau possde une proprit nomme Length qui contient sa longueur. dArray. Lerrgth a donc la mme valeur que nunElements.

La boucle f or suivante aurait t prfrable

ll rpnnfit lp tableau avec 1es valeurs for (int i = 0; i ( dArray.Length; i++)


{

Pour4uoi les dclarations des tableaux de longueur fire et de longueur hriable sont-elles si diffrentes ?
Superficiellement, la syntaxe de la dclaration d'un tableau de longueur fixe ou de longueur variable est assez diffrente :
doubl.e[] dFixedlengthArray * {5,2,7,3.5, 6.5, double[] dVariabLelengthArry - new doublelll ;

8, 1, 9,

l,

3l;

La diffrence vient du fait que C# essaie de vous viter un peu de travail. C# alloue votre place la mmoire ncessaire dans le cas d'un tableau

de longueur fixe comme dFixedLengthArray. J'aurais pu aussi le faire moi-mme :


double[] dFixedlengthArray = nerr double[10] {5, 2, 7,

Ici, j'ai utilis new pour allouer explicitement la mmoire, et j'ai fait suivre cette dclaration par les valeurs initiales des membres du tableau.

Chapitre 6 : Rassembler des donnes : classes et

tableaux | 2 5
:

Le programme peut maintenant dfinir les proprits de chaque tudiant

studentslil = new Student0; students Ii] . sName = "Mon nom";


students

lil

dCPn

dMyGPA;

C'est cette merveille que vous pouvez voir dans le programme .Ar,erageStr-rc1er,t,,lFA ci-dessous, qui recueille des informations sur un certain nombre d'tudiants et affiche la moyenne globale des points de leurs units de valeur
:

I I

I I

AverageStudentGPA
("^+^-. uJLrlrl

- calcule
d'UV

1a moyenne des points

(GPA)

d'un certain nombre d'l r:liants.

',^.i-^ urrr5

nanespace AverageStudentGPA
{

public class
{

Student
sName;

public string
nrrhlin

inrrh'l ^ dp[.

//

moyenne des

points

d'UV

]
n115S1 ^..L1.: ^ Ufd ^1^^^ Urd PUUATU
{

public static void Main(string[] args)


t

l/ dernande 1e nombre d'tudiants Console.tlriteline("Entrez le nombre d'tudiants") string s = Console.Readline0;


int nNumber0fStudents = Convert.Tont32 // dfinit un tableau d'objets Student
(s)
;

I rennlit maintenant 1e tableau for (int i: 0; i ( students.Length;


I
{

Studentl] students = nel^r StudentlnNunber0fStudents]


i++)

I denande 1e nom 1'utilisateur, et aioute i ll a t'index, parce que 1es objets des tableaux en C# // sont numrots partir de 0
I

Console.Write("Intrez 1e non de I'tudiant "

+(i+1)t":");

string

sName

= ConsoLe.Readline0;

Console.Write("Entrez sa moyenne de points d'UV string sAvg = Console.Readline0; double dGPA = Convert.ToDouble(sAvg) ; I I cre un obipt Strrdpnf nartir de ces donnes Student thisStudent = neld Student 0 ;

: ");

thisstudent.

sName

sName;

Chapitre 6 : Rassembler des donnes : classes et tableaux

t27

,l^ F,ntrez. s DttLLL luvjllrl q mvn1 uE -^'i-+^ yvfrrL

I rTTlt . j,5 uv

Entrez 1e
lllL!4

n-+-^-

de 1'tudiant 3: Carrie de yvrrrLo ooints d'UV : 4.0 nlUJClI-lC uL u uv


nom

Appuyez

La noyenne gnrale des 3 tudiants est 3.5 sur Entre pour terminer...

^$st

1^{t t\7, Y

singulier, cornme s:-dent. D'autre part, il doit inclure le nom de la classe, Comme clans baiSt':iclent ou goocistudeni, ou encore sex,vCoedSt,urlent. Le nom d'un tableau (ou de tout autre collection, vrai dire) doit de prfrence tre au pluriel, comme stuients ou phonelJunbers, ou encore p ho n eli,.irib e i s I nli;". P a 1mP I 1 o i. Comme d'habitude, ces suggestions ne refltent que I'opinion de I'auteur de ce livre et non de l'diteur, encore moins de ses actionnaires. C# ne se proccupe absolument pas de la manire dont vous dfinissez les noms de vos variables.

Une structure de contrle de flur pnur tous les tableaur : foreachr


A partir d'un tableau d'objets de la classe Studeni-, la boucle suivante calcule la moyenne des points de leurs UV :
nrrhl'i
{ nrrhl i n <trino
nrthli^ rlnrrhla

c el nss Strrdent
cT\^*^ ' uriOlltE, rlflP'

//

movenne

des points d'UV

l
n'!hlr^ PUUTTL LrA ^t^dd r'r^dr1 VrdD

public static void Main(string[] args)


{

I I

.ere le tableau, I I et fait 1a moyenne des tudiants


dSum

du tableau
1fT
I

double
{

for (int i
dSun

= 0.0; = 0; i ( students,Length;
.dgPn;

*= students Ii]

l
double dAvg = dsum/students.Length; II .utilise le tableau.

I 28

Troisime partie : Programmation et objets

La boucle

- o r-

effectue une itration sur tous les membres du tableau.

-/ \ \ students. i,enpth contient le nombre d'lments du tableau. ;/ ^..L -(' a-;,,.":"" r,;r:,rr" o" contrle de flux, nomme roreach, spcialement conue pour I'itration dans un conteneur tel qu'un tableau. Elle fonctionne de la faon suivante :
Ill t ^.:+ rcllL l^ rd *^,,^-^^ lruyetllte ^uej

tudiants du tableau

double
{

dSum

= 0.0;

foreach (Student stud in students)


dSun ]

*= stud.

dGPA;

double dAvg

dSur/students.Length;

Lors du premier passage de la boucle, I'instruction r o re;ach va chercher le premier objet Student dans le tableau, et le stocke dans la variable .i A chaque passage successif, I'instruction f oreach va chercher stud. l'lment suivant. Le contrle sort de f oreach lorsque tous les lments du tableau ont t traits de cette faon.
Remarquez qu'aucun index n'apparalt dans I'instruction f creach, ce qui rduit considrablement les risques d'erreur.

U
oc$IQa"

Les programmeurs C, C++ et Java ne se sentiront sans doute pas trs confortables au premier abord avec f oreach, car cette instruction est propre C#. Mais elle a la qualit de grandir en quelque sorte pour vous. Pour accder aux tableaux, c'est la plus facile utiliser de toutes les commandes de boucle.
La structure f oreach est en fait plus puissante que cet exemple ne le laisse paraltre. En dehors des tableaux, elle fonctionne aussi avec d'autres types de collection (les collections sont expliques au Chapitre 16). D'autre part, notre exemple de foreach ignorerait les lments du tableau qui ne seraient pas

frg)

du type Student.

Trer un tableau d'objets


La ncessit de trier les lments d'un tableau est une difficult bien connue de la programmation. Que la taille d'un tableau ne puisse tre ni augmente ni rduite ne signifie pas que ces lments ne puissent pas

Ghapitre

6:

Rassembler des donnes:classes et

tableaux I

29

tre dplacs, ni que I'on ne puisse en ajouter ou en supprimer. Par exemple, le fragment de code suivant permute deux lments de Student dans le tableau students :
Student tenp = students[i];

studentsIi]

/l

net de ct 1'tudiant noi

studentsIj]

studentsljl = tenp; Ici, la rfrence d'objet de I'emplacement i du tableau students est sauvegard, afin qu'elle ne soit pas perdue lorsque la deuxime instruction change la valeur de students Ii] . Enfin, le contenu de la variable temp est stock I'emplacement j. Dans une reprsentation visuelle, cette opration ressemble la Figure 6.2.
Avant:

studentslil

studentsljl

Aprs:
Figure 6.2 "Permuter
:

signifie en
ra lit

deux objets'

"permuter
deux

rfrences
au mme objet".

Le programme suivant donne une dmonstration de la manire d'utiliser la possibilit de manipuler les lments d'un tableau pour effectuer un tri. Cet algorithme de tri s'appelle "en bulles" (bubble sort). Ce n'est pas le

130

Troisime partie: Programmation et objets

meilleur sur de grands tableaux contenar-rt des milliers cl'lments, mais il est simple et efficace pour de petits tableaux: ll SortStudents - ce programne montre connent trier un tableau d'objets II
,.^-:-O,,^+^-. u rrLB D) L Crrl
,

nnsnctr SortStudents
t

class Class i
{

public static 'roid Main(string[1 args)


t
I

cre un tableau d'tudiants


;

Studentll students = new Student[5]; students IO] = Stuaent,Nevstudent ( "ilomer", 0)

students It] : Student,NenSt,rCent("Lisa", 4.0) ; students IZ] = Student . NewStrrdent ( "Bart" , 2. 0) ; students [3] : Student . Newstudent ("l,{arge" , 3 .0)

;
:

rrl = Stuoeirt.NewStuIsn;("llagg-e", 3.5) studentsl4i // output the iist as is: Console . ldriteline (r'A,rant de tr j-er : " ) ;

OutputStudentArray ( students ) ;
I I trie maintenant 1a liste des I I du meilleur au pius nauvais

tudiants
;

Console.WriteLine("\nTri en ccurs\n")
Student. Sort (students)
;

// affiche la liste trie


Console.Writeline("tudiants par rsultats dcroissants :")
OutputStudentArray ( student
s
;

/l attend confirmation de 1'utilisateur


Console,!,IriteLi.ne("Appuyez sur Entre pour terminer, . .")
Console. Read 0
]
;
;

l/
{

OutputStudentArray affiche tous 1es tudiants du tableau

public static void 0utputStudentArray(Studentl] students)


foreach(Student s

in

students)
(

i
Console.llriteline (s.GetString
l l
] i / Student class Student
{ -,.L1.i^ puD-r-LC ^+-.i-^ Lrrng sflame; s ^N )

description d'un tudiant

(nom

et rsultats)

nrrhlic riorrhlp dGrade : 0.0;

Chapitre 6 : Rassembler des donnes : cf asses et

tableaux |

3 |

ll Netrstudent - retourne un nouvel objet Student initialis public static Student Nerustudent(string sName, double dGrade)
{

Student student = nerd Student 0


student. sName = sName; student. dGrade = dGrade;

return student;
l
I
II I UsLLrarr

I r'^+e*-)

LUllvc!

en cours public string GetString0


{
a++-ia^ ^: Lrrrr [il.
,

^rvertit Lf L en crr Lrrdrlrc chane 1'obiet

Student

s *=

dGrade; g f= tr - tr'

s f= sName; return s;
]

/l II
{

Sort

- trie

un tableau d'tudiants par ordre de rsultats deroissants, avec 1'algorithme en bulles

public static void Sort(Studentll students)


hnnl hRonontT.nnn. // rnte 1a bouc'i c ittsntt' ne ntr4 1a liste soit trie
UU

^
{

cet indicateur sera dfini comme vrai si un objet est trouv ma1 class bRepeatloop = false; i/ itre sur 1a liste des tudiants for(int index = 0; index ( (students.Length 1); index++)
| I
{

| I

I si deux tudiants sont dans le mauvais sens. if (studentsIindex] ,dGrade (


I

studentsIindex + 1] ,dGrade)
t

i1s sont

permuts.

Student to = students Iindex] ; Student from = studentslindex + 1];


students

// soient dans le bon ordre) bRepeatloop = true;


l
]

studentslindex + 1] = to; II . et f indj.cateur bRepeatloop dit si il faudra I I f.aire encore un passage sur 1a liste des tudiants I I (continue itrer jusqu' ce que tous les objets

lindex]

from;

32

Troisime partie : Programmation et objets

I while (bRepeatloop);
j

Commenons par examiner ce qu'affiche le programrne, rien que pour nous faire une ide :
Avant de

0-

trier

Homer

2 - Bart 3 - Marge 3.5 - Maggie

Tri

en cours
:

tudiants par rsultats dcroissants

4
2

Li.sa Maggie Marge

30-

3.5 -

Bart
Honer
o'rr ur Fn+r6a lrrL!s nnrrr Pvu! tarminar LErluJllc!,,.

nnttt'ao nyyuJL

Afin de gagner du temps, le vtre comme le mien, j'ai cod localement la cration de cinq tudiants. La mthode lrlewS*r-udenr O alloue un nouvel objet Student, initialise son nom et son "grade", et retourne le rsultat. Le programme utilise la fonction OutpL,rtS tudentAr ra',1(. ) pour afficher le tableau des tudiants avant qu'il soit tri.
Le programme invoque ensuite la fonction Sort= (.i
.

Aprs le tri, Ie programme rpte le processus d'affichage dans le seul but de vous impressionner avec le rsultat maintenant tri.

str, la principale nouveaut du programme Scrr:Students est la mthode Sort O. Cet algorithme fonctionne en effectuant une boucle continue sur la liste des tudiants jusqu' ce qu'elle soit trie. chaque passage, le programme compare chaque tudiant son voisin. S'ils ne sont pas dans Ie bon ordre, la fonction les permute et met jour un indicateur pour signaler que la liste n'tait pas compltement trie. Les Figures 6.3 6.6 montrent la liste des tudiants aprs chaque passage.
Bien

Chapitre

6: Rassembler des donnes: classes et tableaux

133

Homer
Figure 6.3
:

a
4
2
3

Lisa

le tri en
bul I es.

Avant de c0mmencer

Bart

Marge Maggie

Figure 6.4
:

Lisa Bart
Marge
Maggie

4
2
3

passage

Aprs le premier
du

3.5

tri en bulles.

Homer

O <-

Homer finit par se retrouver tout en bas.

Figure 6.5
:

Lisa 4 <Marge
Maggie
3

Lisa reste

tout en haut.

Aprs le
deuxime passage du tri en bulles.

3.5

Bart 2 <Homer A

Bart est descendu, mais reste au-dessus d'Homer.

Figure 6.6
:

Aprs

dernier

I'ava nt-

Lisa
Maooie

4
3.5

passage, la liste est

trie. Le
passage final met fin au tri en constatant que rien ne change.

E _ ) Maggie 4 Marge 3 Bart 2


Homer A

et Marge sont permutes.

|3

Troisime partie : Programmation et obiets

Au bout du compte, les rneilleurs tudiants, comme Lisa et Maegie, remontent comme des bulles jusqu'en haut, alors que les plus mauvais, comme Homer, tombent au fond. Voil pourquoi a s'appelle le tri en bulles.

-gf{c 137 -^$: \, =f ItU ) {t-l

Ce qui permet de raliser une fonction de tri, celle-ci ou n'importe quelle autre, c'est que les lments du tableau peuvent tre rordonns en assignant un autre lment clu tableau la valeur qui en rfrence un autre. Remarquez que I'assignation d'une rfrence ne fait pas une copie de I'objet, raison pour laquelle c'est une opration trs rapicle.

Chapitre 7

Mettre en marche quelques fonctions de grande classe


Dans ce chapitre : Dfinir une fonction.
Passer des arguments une fonction.

Obtenir des rsultats (c'est agrable).


tudier I'exemple 1,{riteLine O. Passer des arguments au programme.

es programmeurs ont besoin d'avoir la possibilit de diviser de

grands programmes en morceaux plus petits, donc plus faciles manier. Par exemple, les programmes prsents dans les chapitres prcdents sont proches des limites de ce qu'une personne normalement constitue peut digrer en une seule fois.
C# permet de diviser le code d'un programme en un certain nombre de morceaux que I'on appelle des fonctions. Des fonctions bien conues et bien implmentes peuvent simplifier considrablement le travail d'criture

d'un programme complexe.

Dfinr et utiliser une fonction


Considrez I'exemple suivant
class Exanple
t
:

36

Troisime partie : Programmation et obiets

h'rhl1^ yuvrrL

rh+

hln+,

nrrhl i,. ct:t i c i n- nStatic Int j nrrhf r*'---in vn rl Mpn\prFunction ()


t

Console.1,rlriteLine("ceci est une fonction membre")


]

public static vo:d ClassFur:c:jcn


{

()

Console.I^lriteline("ceci est une fonction de classe");


J

L'lment ilr,- est r:r nTetnbre drntn(r. une clonne menrbre d une classe (la classe E.',a.-r ;,;), comme nous en avons vu beaLlcoup au Chapitre 6, mais l'lment I'j-.:r., r r F'-r .'- '. est (l'Lrn qenre nouveau : c'est une fonction membre (une fonction. menrbre cl'une classe). Une fonction est un bloc de code C# clue volls pouvez excLlter en rfrenant son nom. Ce sera plus clair avec Lln exemple (n'oubliez pas nos conventions, le nom d'une classe commence par Llne majllscule : .,i::r.p ' er est un objet de la
,

classe Exan,nl e ).

Le fragment de code suivant assigne une valeur la donne nlrrt (membre de I'objet ei:an; I *. de la classe i,:,:,r: r - , ), et la varialtle statique nSrar: ic lnt (rfrence par la classe r;.:ar',.- et non l)ar un objet de cette classe, puisqu'elle est statique)
:

Exanple example = new Exanple0; I lrra


ovrmnlo nTnt = l.

rrn nhiot
membre donne

/lutilise 1'objet pour initialiser


//un

Exanrple.nStaticlnt = 2i

//utilise
//.,//un

1a classe pour

initialiser

*^-L-^ ^++-.i mernDre srarlque

Le fragment de code suivant dfinit et invoque llemberFunction | ) et ClassFunction (1, presque de la mme manire :
Example exanple

= new nxample0; I lcrp rrn nhipt


;

example . MemberFunct j.on ( )

I ltttil

icp I'nhict

nnrrr inrrnnrtpr

Exanple. ClassFunction

/ lune fonction membre //utilise 1a classe pour invoquer / lune fonction de classe

L'expression exanl le .l'Ien:^,r:i !,.r:,,:tl lr i' . passe le contrle au code contenu dans la fonction. Le processus suivi par C# pour Exan,ple. ClassFurrcr:ion ( ) est presque identique. L'excution de ce sirnple fragment de code produit

Chapitre 7: Mettre en marche quelques fonctions de grande

classe |

37

I'affichage suivant I'aide de l'instruction',r''riteLine () contenue dans chaque fonction : ceci est une fonction nembre ceci est une fonction de classe Une fois que la fonction a achev son excution, elle restitue le contrle au point o elle a t invoque.
f >,1

llglf

Lorsquejeclcrisdesfonctions,jemetslesparenthsespourqu'elles

soient plus faciles reconnaltre, sinon j'ai un peu de nral rn'y retrouver.

Ce petit morceau de code C# avec ses deux exemples cle fonctions ne fait rien d'autre qu'afficher deux petites chalnes cle caractres sur la console, rnais urte fonction effectue gnralement des oprations utiles et parfois cornplexes ; par exemple calculer un sinus ou concatner cleux char-res cle caractres, otr encore, envoyer subrepticement par rnail votre {.JI- Microsoft. Vous pouvez faire des fonctictns aussi longues et compliques que vous votrlez.

Un enemple de foncton qtour uos fichiers


Dans cette section, je vais reprendre les exemples monolithiques du programffie i,ai c,i,.,te,l1,er es-i'ial,ie du Chapitre 5, et les cliviser en

plusieurs fonctions cle taille raisonnable, pour rnontrer quel point I'utilisation de fonctions peut contribuer rendre le progranrrne plus facile crire et comprendre.
Je dcrirai en dtail les rnanires de clfinir et d'appeler une fonction dans

des sections ultrieures de ce chapitre. Cet exemple n'est l que pour donner une vue d'ensemble.
En lisant simplement les commentaires sans le code C#, vous devez pouvoir vous faire une ide assez claire de ce que fait un programme. Si

ce n'est pas le cas, c'est que les commentaires sont mal faits. Dans les grandes lignes, le programme Ca. cur atelnre res'-Tab i e apparalt

comme suit

public static void Main(string[] args)


{

//demande

1'utilisateur d'entrer 1e principal

initial

38

Troisime partie : Programmation et objets

//si le principal est ngatif


I gnre un message d'erreur l/den,ande 1'utilisateur d'entrer 1e taux d'intrt I lsi 7e taux d'intrt est ngatif, gnre un message d'erreur //denande 1'utilisateur d'entrer le nombre d'annes I laff.iche 1es donnes saisies par 1'utilisteur //effectue une boucle avec 1e nombre d'annes socifi while(nYear (= nDuration)
I
r
L

//ca1cule 1a valeur du principal

//olus I'intrt / /rf ti".i.r. 1es rsultats


lI .

i l Si vous I'examinez avec un peu de recul, vous verrez que ce programme

se dcompose en trois sections distinctes

t/

Une section initiale de saisie dans laquelle I'utilisateur entre les donnes, savoir le principal, le taux d'intrt, et la clure. Une section d'affichage des donrres entres, afin que I'utilisateur puisse les vrifier. Une section finale qui calcule et affiche les rsultats.

t/ t/

Ce sont de bons endroits o regarder pour trouver la bonne manire de diviser un programme. En fait, si vous examinez de plus prs la section de saisie de ce programme, vous pouvez voir que c'est essentiellement le

mme code qui est utilis pour saisir

,/
t/ tz

Le principal. Le taux d'intrt.

La dure.

Cette observation nous donne un autre bon endroit o chercher. C'est partir de l que j'ai cr le programme Caiculatelnr:erestTabie i,,r'i t- hFunc l i on s l

I I II II II
I I

CalculatelnterestTablel,lithFunctions

- gnre une table d'intrts semblable d'autres programmes de


une

table d'intrts. nais utilise certaine division du travail


entre plusieurs fonctions.

Chapitre 7 : Mettre en marche quelques fonctions de grande

classe | 39

using Systen;
namespace
{

CalculatelnterestTablel,lithFunct ions
C1ass1

public class
{

public static voj.d Main(stri.ng[] args)


{

decimal mPrincipal : 0; decimal mlnterest = 0;


deci.mal mDuration

//Section L - saisie des =


(

donnes ncessaires pour

crer la table

0;
,

ref mPrincipal ref mlnterest, ref mDuration); l/Section 2 - affiche les donnes pour vrification Console.Writeline0; II skip aline Console.IdriteLine("Principal = " *mPrincipal); Console.llri.teline("Taux d'intrt = rr + nlnterest + "%"); = rr +mDuration+ " ans"); Console.Writeline("Dure
InputlnterestData
Console.I,Iriteli.ne
/
(

lSeclton 3 - affiche La table des intrte calcuLs

OutputlnterestTable (nPrincipal, mlnterest, mDuration) ; I I attend confirnation de 1'utilisateur Console.i.trriteline("Appuyez sur Entre pour terminer. . . ")
Console.ReadO; l

r.i+ -"-tir - If nr.i neina.l / Inoutlntere.+n.+. Lv! v L!q Lq L q !a! L1! IE! 1e y! f,tr arrulyal du LIAv Uu Clavier II les informati.ons sur le taux d'intrt ll et 1a dure, ncessaires pour calculer II la table des valeurs futures / / (CeUte fonction inrplmente la Section 1 en 1a divisant
i
. ,

/ len Erots

conposanLs)

nrrhl'in stetin

rroid TnnrrtTntora<tlleiafrof
ryqsq\!

eoinel mPrinninal

ref decimal nlnterest, ref decimal mDuration)


{

I ta -

lecture du principal
;

mPrincipal = InputPositiveDecimal("1e principal") I I 7b - lecture du taux d'intrt mlnterest = InputPositiveDecimal("le taux d'intrt") I I 7e - lecture de la dure mDuration : InputPositiveDecimal ("1a dure") ;
]

lecture d'un nombre dcima1 nositif yvf ufr partir du clavier //(sai'sie du principal, du taux d'intrt ou de La dure
InputPositiveDecimal ---r-,-

// II

l/i\ s'agit de saisir un nonbre //vtttLer gu,il est positif)

dcimaL

et

de

| 40

Troisime partie : Programmation et objets

public static decimal InputPositiveDecinal(string


I (

sPrompt)

I
I t

eont

inrre irscu' ce cue

1'utilisateur entre

une valeur valide

while (true)

ll denande une valeur Console. Write ( "Entrez 'r


string slnput :
I

1'utilisateur + sPrompt * " : t') ;

I Lit

une valeur dcimale saisie au clavier Console.Readline0 ;


;

decimal mValue = Convert.ToDecimal (slnput) I I sort de la boucle si la valeur entre est correcte

i,f

(mValue

)=

0)

{-.
// retourne 1a valeur
return
'I

dcirnale valide entre par 1'uti.lisateur

mValue;

l ^1 | ^--^.. -.i ^-^ ^.i -^genere ^--^ pour slgnaler Slnon, un message ^^,,l'erreur Console.\tlriteline(sPrompt * " doit avoir une valeur positive"); Console,l,{riteline ("Veui11ez reconnencer") ;

/ I/ I

Console.lJriteline0;
l
J

.I

// OutputtnterestTable - partir du principal et du taux d'intrt, gnre 1a table des valeurs futures pour II le nombre de priodes indiques par II mDuration. II / / (cecl inplnent.e la section 3 du progranure) public static void OutputlnterestTable(decinal rnPrincipal,
decimal mTnterest, decimal mDuration)
{

for (int
t
I

nYear

= 1;

nYear (= mDuration; nYear**)

// II

calcule 1a valeur du principal plus f intert


;

* (mlnterest / 100); // calcule naintenant 1e nouveau principal en ajoutant / I 7'intrt au prcdent principal mPrincipal = nPrincipal t mlnterestPj.d; I I arrondit 1e principal au centime 1e plus proche mPrincipal : decinal Round (rnPrincipal , 2) ll affiche 1e rsultat Console.l,rlriteline(nYear + rt-ti * nPrincipal)
mlnterestPaid: mPrincipal
, ; ;

decimal nlnterestPaid

]
I

Ghapitre 7 : Mettre en marche quelques fonctions de grande

classe | 4 |

J'ai divis I'ensemble Main O en trois parties clairement distinctes, dont chacune est indique par un commentaire en gras, ensuite j'ai divis

nouveau Ia premire section en 1a, 1b, et lc.

^$tlK i( il ) \g)

Normalement, les commentaires en gras n'auraient rien faire l. Ils ne feraient eu'"n.ombrer inutilement le source. En pratique, de tels commentaires sont inutiles si vos fonctions sont bien conues. La section 1 appelle la fonction inputlnterestData O afin de saisir les trois variables dont le programme a besoin pour calculer les rsultats : mPrinclpal, mlnterest, et mDuration. La section 2 affiche ces trois valeurs de la mme manire que les versions antrieures du programme. La partie finale utilise la fonction OutputlnterestTable O pour afficher les rsultats.

Commenant par le bas et progressant vers le haut, la fonction OutputlnterestTable O effectue une boucle pour calculer les intrts successifs. C'est la mme boucle que celle qui est utilise dans le programme CalculateInterestTable, sans avoir recours une fonction, au Chapitre 5. Mais I'avantage de cette version est que lorsque vous crivez cette section du code, vous n'avez pas besoin de vous proccuper des dtails de la saisie et de la vrification des donnes. En crivant cette fonction, vous avez simplement penser : "tant donnes trois valeurs, le principal, le taux d'intrt et la dure, calculer et afficher la table des intrts." C'est tout. Une fois que vous avez termin, vous pouvez revenir la ligne qui a invoqu la fonction 0rrtnutTnteresf Table O, et continuer

partir de l.

C'est la mme logique de diviser pour rgner qui est l'uvre dans la fonction InputlnterestData O. Vous pouvez vous y concentrer exclusivement sur la saisie des trois valeurs dcimales. Toutefois. dans ce cas. on s'aperoit que la saisie de chaque valeur fait appel aux mmes oprations. La fonction InputPositiveDecimal O rassemble ces oprations dans un bloc de code que vous pouvez appliquer tout aussi bien au principal, au taux d'intrt, et la dure. Cette fonction TnputPositiveDecimal O affiche I'invite qu'elle a reue lorsqu'elle a t invoque, et attend la saisie de I'utilisateur. Puis, si cette valeur n'est pas ngative, elle la retourne au point o elle a t appele. Si la valeur est ngative, la fonction affiche un message d'erreur et demande nouveau la valeur I'utilisateur.

t42

Troisime partie : Programmation et objets

Du point de vue de I'utilisateur. ce programme foncticlnne exactement cle la mme manire que la version nronolitl-riclue clu Chapitre 5, et c'est bien ce que nous vouli<lns :
Entrez 1e principal:100 Entrez le taux d'intrt: - 10 le taux d'intrt doit avoir une valeur nositive

Veuillez

recommencer
10

Entrez 1e taux d'intrt: Entrez 1a dure:10

= 100 Principal d'intrt = 10i, Dure = 10 ans


Taux

1-i10 2-tzL 3-i33.1


4-146,41

5-16i.05
6-171.16
7

- 194.88 214 .37

8-

9-235.8r
1A - 259

.39

Appuyez

sur Entre pour terminer,..

J'ai donc pris un prograntme un peu long et un peu compliqu, et je I'ai divis en lments plus petits et pius con'r1trhensibles, tout en faisant

disparaltre certaines cluplications qu'il comportait.

Pourquoi des fonctions

modeste pourunefonction aussi petite que f rspia,-t-tat jo O, mais unefonction peuttre beaucoup plus grande. En outre, une fonction d'usage courant comme r g peut tre invoque en des centaines d'endroits diffrents d'un mme prggramme.
,,ni

DisplayRatto O chaque fois que ncessaire, pratiquement dans le seul but de mettre plusieurs fois dans le programme le mme bloc de code. L'conomie peut paratre
une fonction

Lorsque le langage F0RTRAN a introduit la notion de fonction dans les annes cinquante, le seuf but en tait d'viter la duplication de code en rassemblant les portions identiques dans un seul f ment commun. f maginez que vous ayez eu crire un programme devant calculer et afficher des ratios en de nombreux endroits diffrents. Votre pr0gramme aurait pu appeler

itelile

Ghapitre 7:Mettre en marche quelques fonctions de grande

classe | 43

lly a un autre avantage qui devient rapidement vident: il est plus facile d'crire correctementlecoded'unefonction.LafonctionDispl-ayRatloO vrifiequelednominateurn'est
pas nul. Sivous rptez le code du calcul en plusieurs endroits dans votre pr0gramme, il est

facile d'oublier ce test ici ou l.


Encore un avantage un peu moins vident: une fonction bien conue rduit la complexit d'un programme. Une fonction bien dfinie doit correspondre un concept bien dfini. ll doit tre possible d'en dcrire la finalit sans utiliser les mots ef et ou.
Une fonction comm calculareSin ( ) constitue un exemple idal. Le programmeur qui a besoin de raliser de tels calculs peut alors implmenter cefie opration complexe sans inquitude sur la manire de I'utiliser, et sans se proccuper de son fonctionnement interne. Le nornbre de choses dont le programmeur doit se proccuper en est considrablement rduit. D'autre part, en rduisant le nombre de "variables", une tche imporante se trouve rduite deux tches nettement plus petites et plus faciles. Un programme de grande taille {par exemple un traitement de texte} se compose de nombreuses couches successives de fonctions, corresp0ndant des niveaux croissants d'abstraction. Par exemple, une fonction RedisplayDocument ( ) appellerait sans aucun doute une fonc-

tion Reparagraph ( ) pour rafficher les paragraphes dans le document. La fonction ReparagraphO devrait alors son tour invoquer une fonction CalculateWordWrap o

pour dterminer o placer les retours la ligne qui dterminent I'affichage du paragraphe. talculatel,Iordwrap O elle-mme appellerait une fonction LookupwordBreak O pour
dciderdes ventuelles coupures de mots la fin des lignes. Comme vous le voyez, nous venons de dcrire chacune de ces fonctions en une seule phrase simple.
Sans la possibilit de reprsenter des concepts complexes, il deviendrait presque impossible d'crire des programmes de complexit simplement moyenne, a fortiori un systme d'exploi-

tation romme Windows XP, un utilitaire comme WinZip, un traitement de terte c0mrne
WordPerfect, ou encore un jeu comme StarFighter, pour ne citer que quelques exemples.

Donner ses argutnents une fonction


Une mthode comme celle de I'exemple suivant est peu prs aussi utile que ma brosse cheveux car aucune donne n'est pass la fonction, et aucune n'en sort :
nrrhl ic .qtetic

vnid 0rrtnrt o

Console.ldriteline("ceci est une fonction")


I

|44

Troisime partie : Programmation et

ob

jets

Comparez cet exemple aux vritables fonctions qui font waiment quelque chose. Par exemple, I'opration de calcul d'un sinus ncessite une donne (il faut bien que ce soit Ie sinus de quelque chose). De mme, pour concatner deux chanes en une seule, il faut commencer par en avoir deux. Il faut passer deux chalnes comme donnes la fonction Concatenate r ) . Il vous faut donc un moyen de passer des donnes une fonction et de rcuprer ce qui en sort.

Passer un arqument une fonction


Les valeurs qui constituent des donnes d'une fonction sont appeles orguments de la fonction.La plupart des fonctions ont besoin d'argurnents pour accomplir ce qu'elles ont faire. Pour passer des arguments une fonction, on en place la liste entre les parenthses qui suivent son nom. Yoyez maintenant la petite modification apporte la classe Exarrpie :

public class
{ nrrhl puurlLin
{

Example
rrnir{ vuru f\rrf ntr* llctrin^ vuLyuL \DL!rtl "n^(+ri-^'l tutrLJL!rir/

<f af in LeLfL

Console.Writeline("Output0 a reu lrargument + funcString);


]
1

"

J'aurais pu invoquer cette fonction depuis la mme classe de Ia faon suivante :


rlTJal i rrf nrrf f rrlfrv \ ^rr

)
/

.
t

Et j'aurais reu le mme mmorable rsultat


"+-"+t'l uurpuL|./ 1|^-^.'*^-+ a reu I ; argumenr . U^l1 nel.Lo

Le programme passe la fonction Output O une rfrence la chalne "Hello". Lafonction reoit cet rfrence et lui assigne le nom fuircString. La fonctiot Outp,.rt O peut alors utiliser furrcString dans le code qu'elle contient, comme n'importe quel autre variable de type str irrg.
Je vais maintenant

apporter une modification mineure cet exemple


"He11o";

string upperString =
Output (upperString)
;

Ghapitre 7:Mettre en marche quelques fonctions de grande

classe I

45

L'assignation de la variable upperString lui fait rfrencer la chalne "Hello". L'invocation Output (upperString) passe la fonction I'objet rfrence par upFerStrrng, eui est notre vieille connaissance "Hello". La Figure 7.1 reprsente ce processus. partir de l, I'effet est le mme.

Figure 7.1 Out


fr

L'invo cation

- rnnorStri nc
copie la valeur de
da ns

put

upperString :r Jrr il r9 =-\


\

rrnnor(t.i nr -rr. _" -_ _.,t frnntr:


nc

Output

(funcString)/

\,/ Y/

------>

"Hello"

Passer ltluseurs argutnents une fonction


Quand je demande ma fille de laver la voiture, elle me donne en gnral plusieurs arguments. Comme elle passe beaucoup de temps sur le canap pour y rflchir. elle peut effectivement en avoir plusieurs sa disposition.

Vous pouvez dfinir une fonction comportant plusieurs arguments de divers types. Considrez I'exemple suivant, AverageAr:olrispla,u* O :

*rui3
,.r

AverageAndDisplay
System;

C'
,./

+-l:,;\ I tti I |

using
t

namespace Exanple

nrrhlic elnss Classl


t

public static void Main(string[] args)


t
(

// accde 1a fonction menbre AverageAndDisplay("UV 1", 3.5, ''lJV 2rr, 4,0) //


Console.Read0;

attend confirmation de 1'utilisateur Console.l,lriteLine("Appuyez sur Entre pour terminer..,")

l
I I

nom et affiche 1e rsultat public static void AverageAndDisplay(string s1, double d1, string s2, double d2)

I /

AverageAndDisplay

- fait

1a moyenne de deux nombres associs

leur

4aa

4O

Troisime partie : Programmation et'obiets

double dAverage = (dl + d2) | 2: Console,Writeline("La noyenne de " * s1 f " dont 1a valeur est "

* dl ts2 *"etde" * " dont 1a valeur est " t d2 * rr est gale " + dAverage);

L'excution de ce petit programme produit I'affichage suivant (le saut de ligne ne vient pas du programme) :
La moyenne de UV 1 dont
nnlrroz crrr Fntr6o nnrrr

la valeur est 3,5 et de dont 1a valeur est 4 est ga1e 3.75


1'orm'inor

UV

La fonction ,..erageAndDispi at. ( I est dclare avec plusieurs arguments, dans I'ordre dans lequel ils doivent lui tre passs. Comme d'habitucle, I'excution de notre exemple de programme commence avec la premire instruction qui suit 1'{ain (). La premire ligne qui ne soit pas un commentaire dans i"iain 0 invoque la fonction AverageArrdDisplal'() en lui passant les deux chlnes "UV 1" et "uV 2", et les deux valeurs de type

d,I

e3.5et4.0.

La fonction i..'erageAndltisplai. O calcule la moyenne des deux valeurs double c1i et d2, qui lui ont t passes avec leurs noms respectifs contenus dans sl et s2. et cette movenne est stocke dans dA-,,eraqe.

Accorder la dfintion d'un argument et son utlsaton


Dans un appel de fonction, I'ordre et le type des arguments doivent correspondre la dfinition de la fonction. Ce qui suit est illicite et produit une erreur lors de la gnration :

/l

AverageWithCompilerError -cette version ne se spmnil

n'c

',--l*^ uf lrB C,,^+^*. )/ Lxl ,

namespace AverageWithComp
{

ilerError

Ghapitre 7: Mettre en marche quelques fonctions de grande

ctasse | 47

public class C1ass1


{

public static void t'lain(string


I

ll

args)

l/ accde la fonction membre AverageAndDisplay("UV 1", rrUV 2", /l


Console.Read0; l
I
I

3.5, 4.0)

attend confirmation de 1'utilisateur Console.firiteLine ("Appuyez sur Entre pour terniner.

..")

nom et affiche 1e rsultat public static void AverageAndDisplay(string s1, double d1, string s2, double d2)

I |

AverageAndDisplay

- fait

1a moyenne de deux nonbres associs

leur

double dAverage

Console.l,lriteline("La noyenne de " * sl t t' dont la valeur est " *

= (d1 + d2) I

2;
d1

*s2 *"etde" * " dont 1a valeur est " * d2 * " est ga1e " + dAverage);

l
J I

C# ne peut pas faire correspondre les arguments qui sont passs dans I'appel AverageAndDisplay 0 avec la dfinition de la fonction. La chane "LJV 1"

correspond bien au premier argument qui est de type string dans la dfinition de la fonction, mais pour le deuxime, la dfinition de la fonction demande un type double alors que c'est une chne qui est passe dans I'appel.

Il est facile de voir que j'ai simplement interverti le deuxime et le troisime argument dans I'appel de la fonction. Voil ce que je n'aime pas avec ces ordinateurs : ils prennent littralement tout ce que je leur dis. Je sais bien que je I'ai dit, mais ils pourraient comprendre ce que je voulais dire !

Surcharqer une fonction ne signfie pas lu donner trop de traMl


-f

5>}qS, il \ \gt/

Vous pouvez donner le mme nom deux fonctions d'une mme classe, condition que leurs arguments soient diffrents. On appelle a surcharger le nom de la fonction.

t48

Troisime partie : Programmation et objets

Voici un exemple cle surcharge


/ /

I nvE!acnllu!f,yrqjvvrrvqu9u ore lltvlr--montr LLLE version vsrDrvrr a-^"^-^ ^^ ^-rn"r snl rvOverl o,adpd - cette
ro

1r fnnntinn rurr Lfv!r

Arrpr:snn,1T1.i.^l rrvu!u;CnllUUfyroJ

o-'

peut tre surcharge


rrqino Srr<tpm' *- -"b
nanespac
{

Ave

rageAndDisplayOver loaded
{ le.gg]

njthltn
{

crec<

public static void Main(stringil args)


{

// accde la premire fonction membre AverageAndDisplay("mes points d'UV", 3.5, "vos points d'UV", 4.0);
Console.WritelineO;

// accde la deuxi.ne fonc*.ion nembre AverageAndDisplay (3.5, 1. )


;

ll attend confirmation de 1'util-isateur


Console.i{riteLine("Appu1's2 sur Entre pour terminer.,.")
Console.Read0; l
I
I
;

I I

ArerageAndDisplay

- fait ia moyenne de deux nombres associs leur non et affiche 1e rsultat


d1, d2)

public static void AverageAnriDisplayfstring s1, double srring s2, double


{

doub:e dAverage

= (dl + d2) I

?.;

Console,WriteLine("La moyenne de

" T s1 * 'r dont 1a yaleur est " *


"

d1);

Console,lrlritel,ine("et de

+^1

t ",lorri:1a vaieur est " * d2 * " est gaie " + dAverage);


l
i ^"L1 ^ PUVTTL a+^+.i n LALaL

void AverageAndDispJ-ay(double di, doubie

d2)

double dAverase

: (Ci + d2) I

2:

Console.hiriteLine ("La rnoyenrre de

" * d1 +d2 *"et" * " est gale " + dAverage);

u'gelir,dDrsplay (). Il invoque I'une puis I'autre en leur passant respectivement les arguments qu'il leur faut. C# peut iclentifier la fonctit-rn (lemancle par le programrne en
Ce programme clfinit deux versi()n.s de la fonction r-,

Ghapitre 7:Mettre en marche quelques fonctions de grande

classe I 49

comparant I'appel la dfinition. Le programme se compile correctement, et son excution donne I'affichaee suivant :
La moyenne de nes points d'UV dont 1a valeur est 3,5

^+ UC VU' PUrrlLb L
r !o^ -^,,^--^ ruvJsrrlr Annlt\to" cltr

r^ "^^ -^i-+^

J'UV dont

la valeur est 4 est

ga1e

3,75

,1 I ( EL t, cL a^^1 ^ \ .3,75 ^ JrJ ^+ T ^^t cdr uc Fntra n^rrr tormlnr

En rgle gnrale, C# ne permet pas deux fonctions du mme programrne d'avoir le mme nom. Aprs tout, comment pourrait-il deviner quelle fonction vous vouliez appeler ? Mais le nombre et le type des arguments de la fonction font partie de son nom. On pourrait appeler une fonction tout simplement r.,.'eraceAr.il.-rispi a.i'\, mais C# fait la diffrence entre les fonctions .ieras.i\nilispla; is',r ing,,i:,:t,1e, stl'ing, douL'-.e et h.rerageAn:lli s1;1a,,' iclouble . i1o,lb 'e I . En voyant les choses de cette faon, il est clair que les deux fonctions sont diffrentes.

lnplmenter des arguments par dfaut


Bien souvent, vous voudrez pouvoir disposer de deux versions (ou plus) d'une mme fonction. L'une pourra tre la version complique qui offre une grande souplesse mais ncessite de nombreux arguments pour tre appele, dont plusieurs que I'utilisateur peut trs bien ne mme pas

comprendre.

"Qt ;:iffili,'il:i:ilJr'"iit

.P\

En pratique, quand on parle de "l'utilisateur" d'une fonction, il s'agit

usage ce n'est pas rorcment

re

Une autre version de la mme fonction, bien qu'un peu fade, offrirait des performances acceptables, en remplaant certains des arguments par des valeurs par dfaut. La surcharge d'un nom de fonction permet d'implmenter facilement des valeurs par dfaut. Examinez ces deux versions de la fonction L)i spl31-p.eundeCDecina i O
:

// I II
/

FunctionsWithDefaultArguments

- offre des variantes de la mme fonction, certaines avec des arguments par dfaut, en surchargeant 1e nom de 1a fonction

| 50

Troisime partie : Programmation et objets

using
i

System; nanespac e FunctionsWithDefaultArormpnts

public class C1ass1


t

public static void Main(string[] args)


{

/l ll

accde 1a fonction nenbre


3)

. Writeline ( " { 0 } ", DisplayRoundedDec inal ( 1 2 . 34567 8i'{, attend confirrnation de 1'utilisateur Console. t^lriteLine ("Appuyez sur. Entre pour terminer , . . " ) ;

Console

Console.Read0; l
I
I

// I
tl

DisplayRoundedDecimal

- convertit

en chane une valeur dcimale, avec le nombre spcifi de chiffres significatifs


mValue,
)

public static string DisplayRoundedDecimal(decimal


t

int nNunberOfSignifieantDigits //
commence pas

//
tt

arrondir 1e nombre sur 1a base du nonbre spcifi de chiffres significatifs


mRoundedValue =
dec

decinal

imal . Round (nValue

nNunb erOfS

string s *
reurn s;
]

//

ignif ic antDigits
;

Convertit en chalne

obtenu Convert.?oString(rnRoundedValue)

le rsultat

public static string DisplayRoundedDecinal(decirnal


t

mValue)

// I

invoque DisplayRoundedDecimal(decinal,

int),
;

en spcifiant 1e nonbre de chiffres par dfaut


DisplayRoundedDecinal(mValue, 2)

string s =
return s;
l l
]

La fonction DlspiayRoundedDecimal (decirnal , int ) convertit en chne la valeur dcimale qui lui est passe, avec le nombre spcifi de chiffres aprs la virgule. Comme les valeurs de type decimal sont trs souvent utilises pour reprsenter des valeurs montaires, on utilise le plus souvent deux chiffres aprs la virgule. Aussi, la fonction DisplayRoundedDecimal (decimal ) fait le mme travail de conversion, mais en utilisant le paramtre par dfaut de detx chiffres aprs la virgule, ce qui vite I'utilisateur d'avoir seulement se demander ce que veut dire le deuxime argument.

Chapitre 7 : Mettre en marche quelques fonctions de grande classe

t5t

de cette fonction fait son -gU!{c Rernarquez que la version gnrique (declrnai) (Cecinal travail en appelant la version complte . j nt). Laversion \ .-d/ :$: =( lt[ ,) gnrique trouve toute seule les arguments que le programmeur n a pas \St-i envie de chercher clans la documentation.

=oi

*fR.''

Fournir des arguments par dfaut prsente plus d'intrt que d'pargner simplement quelques efforts un programmeur paresseux. Les recherches inutiles dans la documentation pour y trouver Ia signification d'arguments qui sont normalement dfinis par dfaut distraient le programmeur de sa tche principale et la lui rendent plus difficile, lui font perdre clu temps, et augmentent le risque de produire des erreurs. Le programmeur qui est I'auteur d'une fonction comprend les relations entre ses arguments. C'est lui que revient la charge d'en fournir une version simplifie, plus facile utiliser.

Passer des arguments d'un trlpe

hleur

Les types de variables de base, int, doubie, et iecinal, sont appels Upes ualeur.ll y a deux manires de passer une fonction des variables d'un type valeur. La forme par dfaut consiste passer par uoleur. L'autre

#i [o,

forme consiste

pctsser

par rfrence.

Les programmeurs ont leur manire de dire les choses. En parlant d'un type valeur. quand un programmeur dit "passer une variable une for-rction", cela signifie gnralement "passer une fonction la valeur d'une variable".

Passer

par ualeur des arguments d'un tqpe Ualeur

Contrairement une rfrence un objet, une variable d'un type valeur comme int ou iouble est normalement passe parvaleur, ce qui signifie que c'est la valeur contenue dans la variable qui est passe la fonction, et non la variable elle-mme.
Passer par valeur a pour effet que la modification de la valeur d'une variable d'un type valeur dans une fonction ne change pas la valeur de cette variable dans le programme qui appelle la fonction.

//
{

PassByValue
System;

montre 1a smantique du passage par valeur

using

namespace PassByValue

I5

Troisime partie : Programmation et

ob

jets

public class Classi


{

I I Update II
I I ..

- essaie

de modifier 1a valeur

des arguments qui

lui sort

passs
C)

public static void Update(int i,


L

doubLe

1 =

/t)'

d = 20.0:

l public static void Main(string[] args)


t

I I dc|are int i = 1;

deux variables

et 1es ini.ti-alise

double d = 2.0; Console.WriteLine("Avant 1'appel 'Jpdate(int, double) :") Console.Writeline(!'i - 'r + i + ", d = " * d); /i invoque 1a fonction

Update(i, d);

ll renarquez que les valeurs 1 et 2.0 n'ont pas chang Console.lrlriteLine ("Aprs 1'appe1 Update (int, double) : ")
Console.Writeline("i = " + j- + 'r, d = " + d); // attend confirmation de 1'utilisateur Console.!'IriteLine("Appuyez sur Entre pour terminer,,.")
Console,
1

Read

L'excution de ce programme prodtrit l'affichage suivant


Avant 1'appe1 Updare(int, double):

i = i, d = 2
Aprs 1'appe1 Update(int, double):
nnrrrroz crrr F'nfra ve nnrrl|orminor
rvu!

L'appel -Lipria"- () passe les valeurs I et 2.0, et non une rfrence aux variables i et C. Aussi, Ia modification de ces valeurs I'intrieur de Ia fonction n'a aucun effet sur Ia valeur des variables dans la rclutine appelant la fonction.

Passer par rfrence des arguments d'un t,lpe

hleur

Il est avantageux de passer par rfrence une fonction un argumeltt d'un type valeur, en particulier lorsque le programme appelant besoin de donner

Ghap itre

7: Mettre en marche quelques fonctions de grande classe I

53

cette fonction la possibilit de changer la valeur de la variable. Le programme qui suit, PassByReference, met en vidence cette possibilit.
C# donne au programmeur la possibilit cle passer des arguments par rfrence en utilisant les mots-cls ref et cut. C'est ce que montre une petite modification de I'exemple PassByValue de Ia section prcdente.

//

PassByReference
C.,^+ ^* JLc$,
,

demonstrate pass by reference semantics

_-"-ffi

,, ^.: - ^ urrr

@t

namespace PassByReference
(
L

public elass Classl


{ t

de nrodifier 1a valeur des argunents qui 1ui sont passs public static void Update(ref int i, out double
I I

I I

Update

- essaie

d)

i = l0; d = 20.0;
]

public static void Hain(string[] args)


t I I dclare int i = 1;

deux vari.ables

et 1es initialise

double d I Console.I,IriteLine("Avant 1'appe1 Update(ref int, out double):"); Console.ltlriteline(rri = r? + i + ", d n'est pas initialis"); // invoque 1a fonetion Update(ref i, out d); l-l renarquez que les valeurs 1 et 2.0 n'ont pas chang Console.llriteline("Aprs 1'appel Update(ref int, out double) :") ; Console.Writeline('ri - u * i + ", d = + d) ;

" /i attend confirmation de 1'utilisateur

tonsole.l^lriteLine("Appuyez sur ntre pour terminer.


uons0le.KeadlJ;

. . ")

Le mot-cl ref indique que c'est une rfrence que C# doit passer i, et non la valeur contenue dans cette variable. Il en rsulte que les modifications apportes cette valeur dans la fonction sont exportes dans le programme qui I'appelle.
De faon similaire, le mot<l out dit "restituer par rfrence, mais peu m'importe quelle tait la valeur initiale, puisque de toute faon je vais la remplacer" (a fait

I5

Troisime partie : Programmation et objets

beaucoup de choses en trois mots). Le mot<l out est applicable lorsque la fonction ne fait que retourner une valeur au programme appelant.

L'excution de ce programme produit I'affichage suivant


Avant 1'appel Update(ref int, out double): i = 1, d n'est pas initialis
nyrs

i = 10, d = 20
Appuyez

^--:^

1 dPvL L

^--^r

iinriate(ref d uyuoLs\rr rrrLr

int. out uuL uuuuItr,/, double)


.
,

sur Entre pour terminer.

Un argument out est toujours ref

Notez que les valeurs initiales de i et de d sont crases dans la fonction Update O. De retour dans Main O, ces variables ont reu leur valeur modifie. Comparez cela la fonction Pas sByValue ( ) , dans laquelle les variables ne reoivent pas leur valeur modifie.

Ne passez pas une uariable par rfrence une fonction deux fois en mme temps
Jamais, sous aucun prtexte, vous ne devez passer deux fois par rfrence la mme variable dans un mme appel une fonction. La raison en est plus difficile expliquer qu' montrer. Examinez la fonction Update O suivante :
tt // ^ PassByReferenceError

montre une
quand on

situation drerrur potentielle appelle une fonction avec des arguments passs par rfrence

using
{

System;

namespce PassByReferenceBrror

pub-lic class ClassI


t

Update

- essaie

de modifier la valeur des arguments qui 1ui sont passs

public static void DisplayAndUpdate(ref int nVarl, ref


t
I

int

nVar2)

Console.ltlriteline("La valeur initiale de nVarl est nVarl = 10;


Console,l^Iriteline("La valeur

" I rrYqrr,/,
tr

initiale de nVar2 est

I nvrlJ:

nfar} = 20:
'I

public static void Main(string[] args)


{

dclare deux variables

et

1es

initialise

Chap itre

7: Mettre en marche quelques fonctions de grande classe | 5


int n = 1; Console.l,/riteline("Avanr 1'appe1 Update(ref n, ref n) :"); Console.l,lriteline('r1 = t' * n) ;
Console. i,lri"teline O
;

// invoque 1a fonction DisplayAndUpdate(ref n, ref n); // remarquez que 1es valeurs 1 et 2.0 n'ont
Console.I,lritelineO;

pas chang

Console.I,iriteLine("Aprs 1'appe1 Update(ref n, ref n):"); Console.Writeline('t1 = " * n) ; // attend confirmation de 1'uti-lisareur Console.WriteLine("Appuyez sur Entre pour terminer. ..") ;
Console.ReadO;

Update i r"i i:ri . ref in: ) est maintenant dclare pour accepter par rfrence deux arguments de type in*-, ce qui, en soi, n'est pas un problme. Le problme se produit lorsque la fonction l"lain O invoque Update O en lui passant la mme variable pour les deux arguments. Dans la fonction, --ipiate ' t modifie rrruiar,, ce qui, par rfrence n, remplace sa valeur initiale de I par la valeur 10. En mme temps, Upclate O mociifie nVar2, alors que la valeur de n, laquelle elle se rfre, a dj t modifie en recevant la valeur 10.
Ce qui est mis en vidence dans I'exemple suivant
Avant 1'appe1 Update(ref
:

n, ref n):
1

n=1
La valeur La valeur

initiale de nVarl est initiale de nVar2 est

10

Aprs 1'appe1 Update(ref

n, ref n):
t!
!

n=20
nnrrrran nyyuJ4 arrr u! Fnfpf,6 lltL! nnttr yvur torm"inar L!llrrrl!

df\ =(D

Ce qui se passe exactement au cours de ce petit jeu de scne entre n, nVarl, et nVar2, est peu prs aussi vident que la danse nuptiale d'un oiseau

exotique. Ni I'auteur de la fonction Lpdate O, ni le programmeur qui I'utilise, n'ont prvu ce curieux rsultat. Autrement dit, ne le faites pas.
Il n'y a aucun inconvnient passer une mme valeur plusieurs arguments diffrents dans un meme appel de fonction sitoutes les variables sont passes par valeur.

56

Troisime partie : Programmation et objets

Pourquoi y a-t-il certains arguments qui sortent mais n'entrent pas ?


C#veille sans relche empcher le programmeur de faire une btise. L'une des btises que peutfaire un programmeur est d'oublier d'initialiser une variable avant de commencer s'en servir (c'est particulirement vrai pour les variables utilises comme compteur|. Lorsque vous essayez d'utiliser une variable que vous avez dclare mais pas initialise, C# gnre une erreur.

Console.l{riteline("ceci est une erreur " * nVariable) ; nVariable = 1; Console.l{riteline('rnais ceci n'en est pas Itn " * nVariable)

int

nVariable;

Mais C# ne peut pas assurer la surveillance des variables I'intrieur d'une fonction

void SomeFunction(ref int nVariable


{

Console ]

l.lriteline ( "ceci est-i1 une erreur ? 't + nVariable);

Comment SomeFunction ( ) pourrait-elle

savoirsinVarlable

initialise avantd'tre
:

passedansl'appeldelafonction ?Ellenelepeutpas.Aufieude cela,C#examinelavariable


dans l'appel. Par exemple, I'appel suivant gnre une erreur de compilation

int

nUninitializedVariable
(

SorneFunet ion

ref

nUninit ia1 izedVariabl. e ) ;

Si C# avait acce pt cet appel, SomeFunction ( ) se serait vu passer une rfrence une variable non initialise {donc n'ayant aucun sens}. Le mot-cl out permet aux deux parties de se mettre d'accord sur le fait qu'aucune valeur n'a encore t assigne la variable. L'exemple suivant se compile sans problme :

int nljninitializedVariable
SomeFunction

;
;

(out nUninitializedVariable)

Au passage, il est licite de passer en tant qu'argument

out une variable initialise

int nlnitializedVariable = 1;
SomeFunction

(out nTnitializedVariable)

LavafeurdenlnitiallzedlariableseracrasedansSomeFunctionO,maisiln'ya
aucun risque que soit passe une valeur dpourvue de sens.

Ghapitre 7: Mettre en marche quelques fonctions de grande

classe I 57

Retourner une

hleur l'erltdteur

Dans bien des oprations clu monde rel, des valeurs sont cres pour tre retournes ;\ celui clui les a envoyes. Par exemple, sir' i' I accepte un argument pour lequel elle calcule la fonction trigonomtrique sinus, et retourne la valeur corresponclante. Une fonction dispose de deux moyens pour retourner une valeur celui qui I'a appele. La plus courante est I'utilisation clu champ r err.l::i, mais ily a une autre mthode qui utilise l'appel par rfrence.

Utilser

return ttour retourner


Example d2)

une

hleur

L'exemple suivant montre une petite fonction qui retourne la moyenne des arguments qui lui sont passs :
public class
t

public static double Average(double d1, double


{

double dAverage

(d1 + d2)

z:

return
l

dAverage: Test

public static void


I

double v1 = 1.0; double v2 = 3.0; double dAverageValue = Average(v1, v2); Console.I,lriteline("La moyenne de 't { vl

1"etde"+v2*t'est" * dAverageValue);

I I eeci foncti.onne galement Console.Writeline{"La moyenne de 't

I, vcrdctur, ^..::-i: ,'.',.,r v2));


] ]

u' " * est "

v1

Remarquez tout d'abord que je dclare cette fonction comme pub-iic Cc,ribLe rr,.,ei'ag: i,. Le dciLbie qui prcde le nom signifie que la fonction A.u'er:aBe () retourne une valeur en double prcision celui qui I'a appele. La fonction ur.rage i) attribue les noms d1 et d2 auxvaleurs en double prcision qui lui sont passes. Elle cre une variable oAi e rage laquelle elle assigne la moyenne de di et d2, puis elle retourne au programme appelant la valeur contenue dans dAvei'age.

I5

Troisime partie : Programmation et obiets

"(o)

A9L\

Dans ce cas, certains diraient que "la fonction retotrrne ,-,..,,'i :1!,:,". C'est un abus de langage, mais un raccourc'i rl'usagc courant. Dire clur: c,.'r,r-,r-r: ou t<lut autre variable est passe ou retourne r-r 11trr,: ce soit n'a aucun sens. Dans ce cas, c'est la valc'ttr cotttetttte cians i ",r ,,.'.,flui est retourne au programme appelant. L'appel

quel autre appel de fonction. mais la valeur de type . r1-.'.:i':.i;t: i , est stocke clans la variablet I : :., , i,

, 'i i,::r-l:r i r clans la fonction

-",

senrble iclenticlue n'importe

,., , r'tr-rlrnr-'e par

Une fonction qui retourne une valeur. c()nrme ', "' ,. . ne peut pas la retourner en rencontrant la dernire lrarenthse fernrante de la fonction. Si c'tait le cas, comrnent ferait C# pour savoir cprelle valeur retourner ?

Retourner une fuleur en utlsant un passage par rfrence


Une fonction peut aussi retourner une ou plusieurs valeurs la routine qui I'appelle en utilisant les mots-cls : :: et (-,Lrr. Reqardez I'exemple Llpdate (; dcrit dans la section "Passer par rfrence cles argurnents d'un type valeur", plus haut dans ce chapitre :
de nrodifier 1a valeur des arguments qui 1ui sont passs public static void Update(ref int i, out double
I
It r

t tt^)^+^ ^^^^t^ uPuoLc - trDditr

C)

f
! d = rv /t)
t

tt.

vcld, comme si elle ne retournait pas de valeur programme au appelant, mais puisque la variable I est dclare r-ef et la variable d est dclare out, toute modification apporte ces deux variables dans -;tpoat- e ( ) est conserve dans le prograrnme appelant.
La fonction est dclare

Quand utlser

return et (uand utliser out

Vous pensez peut-tre : "Une fonction peut retourner une valeur au programme appelant, ou bien utiliser pour cela c,r ou rr: i. Quancl faut-il utiliser rer-i1rn, t quand faut-il utiliser oui ?" Aprs tout, j'aurais trs bien pu crire de la faon suivante la fonction A.;erage i, :

Ghapitre 7: Mettre en marche quelques fonctions de grande

classe I

59

public class
{

Example d2)

public static void Average(out double dResults, double d1, double


{

dResults = (dt
] nrrhf in ctatin
{

d2)

I
()

z;

rrnid 'test

double v1 = 1.0; double v2 = 3.0; double dAverageValue;


Average(dAverageValue,

v1,

v2)

Console.l.lriteline("La noyenne de " *

v1

*ttetdett*v2*"est" f dAverageValue;

C'est le plus souvent par I'instruction retur:n que vous allez retourner une valeur au programme appelant, plutt que par la directive c,,1i:, bien qu'il soit difficile de contester que a revient au mme.

frg)

rloubie ncessite ogq{qa. Utiliser our- avec une variable d'un type valeur comme un procd supplmentaire que I'on appelle boxing, dont la description sort du cadre de ce livre. Toutefois, I'efficacit ne doit pas tre un facteur cl dans votre choix.
C'est typiquement quand une fonction retourne plusieurs valeurs au programme appelant que vous allez utiliser o'L1tr. Par exemple : public class i
Example

-+-+.:^,,.rid ^ LdLfU -..L1.: VU PUUr,l-U

AverageAndProduct

(out double dAverage, out double dProduct,


double

dl,

double d2)

dAverage=(dl+62;
dProduct =
]

dl *

l2;

d2;

.r$9- 1 7X tsfl, Y

Une fonction qui retourne elle seule plusieurs valeurs est une crature que I'on ne rencontre pas aussi souve;t qu'on pourrait le croire. Une telle fonction est souvent encapsule dans un obiet de classe ou dans un tableau de valeurs.

I 60

Troisime partie : Programmation et obiets

Dfinir une foncton qui ne retourne lras de

laleur

La dclaration public double A.",erage (double, double) dclare une fonction A',,'erage O, qui retourne la moyenne de ses arguments sous forme double.

Il y a des fonctions qui ne retournent aucune valeur au programme appelant. Une fonction que nous avons utilise plus haut comme exemple, AverageA:dDispia-r ( ), affiche la moyenne des arguments qui lui sont passs, mais ne retourne pas cette moyenne au programme appelant. Ce n'est peut-tre pas une bonne ide, mais telle n'est pas ici la question. Au lieu de laisser en blanc le type retourn, une fonction comffle A.rerap.eAndDi spla\.() est dclare de la faon suivante:
publi.c void AverageAndDisplay(double, double)

Le mot-cl .,'oio, plac I'endroit o apparaltrait normalement le type retourn, signifie pos de Upe. Autrement dit, la dclaration void indique que la fonction A'..erageAncDispiay O ne retourne aucune valeur au

programme appelant.

{$a/ ft>ll l/i7 -z

Une fonction qui ne retourne aucune valeur est appele une fonction sans gpe (uoid function). Par opposition, une fonction qui retourne une valeur est appele une fonction Upe (non-uoid function).
Une fonction type doit restituer le contrle au programme appelant par une instruction rerurn suivie par la valeur retourner. Une fonction sans type n'a aucune valeur retourner. Elle restitue le contrle lorsqu'elle rencontre un retui n eui n'est suivi d'aucune valeur. Par dfaut, une fonction sans type se termine automatiquement (restitue le contrle au programme appelant) lorsque le contrle atteint I'accolade fermante qui en indique la fin.

Examinez la fonction public class


{

li splayRatlo ( )

Exanple

^+^+.:^ .,^id ^"L1.:^ DL4LfL yuurrL DisplayRatio (double dNumerator, vu

double dDenoninator)
t

si le
if
t

dnominateur est gal zro, (dDenoninator == 0.0)

Ghapitre 7 : Mettre en marche quelques fonctions de grande

classe | 6l

I I

affiche un message d'erreur et...


(

Console , WriteLine
I

"Le dnoninateur d'un quotient ne peut pas tre 0"); retourne l"a fonction appelante

return:
l
I I ceci n'est excut que si dDenominator n'est pas double dRatio = dNumerator / dDenominator;
nu1

Console.lllriteline("Le quotient " * dNumerator

*"sur"*dDenominator * " est ea1 " * dRatio);

La fonctiot l-'r spla-L'Ratic I I regarde si la valeur de dLenoninator st gale zro. Si c'est le cas, elle affiche un rnessage d'erreur et restitue le contrle au programme appelant, sans essayer de calculer le ratio. Sans cette prcaution, la valeur du numrateur serait divise par zro, et produirait une erreur du processeur, que I'on appelle aussi du nom plus imag de

processor upchuck (Autrement dit, "le processeur dgueule". Dsol.)

zro,la fonction affiche le ratio. La qui parenthse fermante suit immdiatement I'instruction I,,,rriteLlne o qui indique la fin de la fonction Dispiar,P.atio ( ), donc joue le est celle rle d'une instruction ret l,ir ll.
Si dnenominator rl'st pas gal

Rfrence nul1 et rfrence zro


Lorsqu'elfe est cre, une variable de rfrence se voit assigner la valeur par dfaut nu11. Mais une rfrence nu11 n'est pas la mme chose qu'une rfrence zro. Par exemple, les deux rfrences ci-dessous sont compltement diffrentes :

class
{

Example

int
I

nValue;

I I cre une rfrence Exanple refl;

null refl

ll

Example

cre naintenant une rfrence un objet de valeur nu11e ref2 = new 8xample0;

ref2.nValue = 0:

t62

Troisime partie : Programmation et objets

variable ref 1 est peu prs aussivide que mon portefeuille. Elle pointe vers l'objetnull-. c'est--dire vers aucun objet. En revanche, ref 2 pointe vers un obiet dont la valeur est 0.
La

Cette diffrence est beaucoup moins claire dans l'exemple suivant:

string
-qtrino

s1; ? = trlr'

C'est essentiellement la mme chose: s1 pointe vers I'objet nu11, et s2 pointe vers une chane vide, La diffrence est significative, comme fe montre la fonction suivante :
I

I Test -

modules dp

test

norrr

rrti.ljser la bibliothque TestLibrary

nanespace
{

lest

using Systeu;

public class Classl


{

puurr.c srarr.c ^+^+.i^


hrrL1.i^

int
..i-+

rt^il----:, rl Main(string IJ strings)\

Console.lr'riteline("Ce prograurne uti.lise tr * "1a fonction TestString()',) Console.tlriteLine0; Exanple exanpleObject = new Sxarnple0;

Console.llriteline("Passage d'un objet nu11 :") string s - nu11;


exanpleObj ect. TestString { s)
;

Console.l.IriteLine0;

Console.Writeline0;

// passe naintenant la fonction une chaine vide Console.Hriteline("Passage d'une chane vide :") ; exanpleObject . TestString ( " ") ;

i I enfin, passage d,une vritable chane Console.l,lriteline("passage d'une vritable chane :") exanple0bject.TestString(',chane de test',) ; Console.

Irlriteline

ll t
I

attend confirmation de
()

l,utilisateur
sur Entre,pour terniner. . .,,)
;

ConsoLe.i,lriteline(,,Appuyez
Console. nead return 0;

Chapitre 7: Mettre en marche quetques fonctions de grande

cfasse | 63

nl acc

Fvamnl o

public vojd TestString(string sTest)


{

// commence par vrifier si l"a chane est vide if (sTest =: nu11)


i Console.i,{riteLine ("sTest est vide")
;

reurn:
J

I I vrif.ie si sTest pointe vers une chane vj-de if (String.Conpare(sTest, "") =: 0)


{

Console.l,'lriteLine("sTest rfrence une chane vj.de") return ;


l

// puisque tout va bien, affiche 1a chane Console.iljriteLine("sTest se rfre : "' * sTest + rrIir)
1

'

TestStrlng O utilise la comparaison sTest :: nul-l poursavoirsiune chane apourvaleurnuil.MaistestStringO doitutiliserlafonctionCompareO pourtestersi une chane vide {Compare O retourne un 0 si les deux chanes qui lui sont passes sont
Lafonction
gales).
Ce programme affiche les rsultats suivants
:

n^ *-^^*^-*^ "*r1ise uLrlrE la rvllL 1a fonction Lfulr TestStrinso v yru6!nutrs - -- -- -----o v


Passage

d'un objet nu11

sest est vide


d'une chane vide : sTest rfrence une chane vide
Passape Passage d'une
.^ IEltrrtr E rfr^ . d..

vritable chaine
t+an1 LcL qfrjnsl u,!rlrb

Appuyez

sur Entre pour terminer.,.

t64

Troisime partie : Programmation et objets

La question de Ma i ii a un pr0qramme
s'agit
:

) : passer des arguments

Examinez toutes les applications console de ce livre. L'excution commence toujours par |1a,r, i r. Sa dclaration vc)Lls dit clairement de quoi il

public static void Main(stringIJ


t I

args)
programme.

emplacement de

votre

l'lair-' i t est une fonction statique ou une fclnctior-r de classe cle la classe Class -. dfinie par I'Assistant Applications cle \risual Stuclio. ;.1-r ne

retourne aucune valeur et accepte comme arguments un tableau d'objets de type sr r-n,1. Que sont ces chalnes ?
Pour excuter une application console, I'utilisateur entre le nont du programme. Aprs ce nom, il a la possibilit cl'ajouter des argumeltts. C'est ce que vous voyez tout Ie temps, par exenlple avec une conlmtrncle contme oi..)' ri"c nf irlrrei C: \inc,nCL,ts;.rier, Qui copie le fichier ilL.itf ,.:i -er dans le dossier noi,dCrSSier du rpertoire racine du lecteur C. Comme vous pouvez le voir dans I'exemple ,,rl spia.,'Ar gu;f,rrl t.s suivant, le tableau de valeurs de type st ring pass llal n i I constitue les arguments du programme :

/l

DisplayArguments
(rra+^m ' pJ Lu,

affiche
au

1es arguments

qui sont

passs

programme

srrr5 ".{-^

namespace DisplayArgument
{ PsuIrL
{

Lr

rcL

public static int Main(string[] args)


{

//

compte 1e nombre dtargunents

Console.l,{riteLine("Ce programme
args . Length)
I I les arguments sont int nCount = 0; foreach(string arg in args)
: ;

a {0) arguments"

Ghapitre 7 : Mettre en marche quelques fonctions de grande classe

t65

Console.IrJritelj.ne("L'argunent {0} est [1]",

ncount#, arg)
]

I I attend confirmation de 1'utilisateur Console.lrlriteLine("Appuyez sur Entre pour terminer'

"")

Console.ReadO; return 0:

Ce programme commence par afficher la longueur du tableau args. Cette valeur correspond au nombre cles arguments passs la fonction. Le programme effectue alors une boucle sur tous le.s lrnents cle ar's, affichant successivement chacun d'entre eux Sur la console.

L'excution de ce Programme Peut produire par exemple les rsultats suivants :


DisplayArgunents
Ce programne

lc argl argZ a 3 argunents

L'argunent 0 est /c L'argunent 1 est argl Ltargument 2 est arg2 Appuyez sur Entre pour terminer...

Vous pouvez voir que le nom du programme lui-mme n'apparalt pas dans la liste des arguments (il existe aussi une fonction qui permet au programme de trouver dynamiquement son propre nom). D'autre part' I'option / c n'est pas traite diffremment des autres arguments' C'est le programme lui-mme qui se charge de I'analyse des arguments qui lui sont Passs.

wLaplupartdesapplicationSconSoleautorisentl'utilisationd'optionsqui

lLrrlrf Y

permettent de contrler certains dtails du fonctionnement du programme.

Passer des arguments

l'nte de D0S

Pour excuter partir de I'invite de Dos le programme -,isp ial'^:g ln( rrs, suiVez ces tapes :

1.

Cliquez sur Dmarrer/Programmes/Accessoires/Invite de commandes.

I 66

Troisime partie : Programmation et obiets

Vous devez voir apparaltre une fentre fond noir contenant la

respectable antiquit C:
2.

\)

suivie d'un curseur clignotant.

Naviguez jusqu'au dossier contenant le projet DispiayArguments en tapant au clavier \Programmes C#\DisplayArguments. (Le dossier par dfaut pour les exemples de ce programme est

Prograinnes

C/.

Utilisez le vtre si vous en avez choisi un autre)

L'invite devient
Si vous ne le

c : \ C/Programs

\nisplayArguments).

trouvez pas, utilisez Windows pour rechercher le programme. Dans I'Explorateur Windows, cliquez du bouton droit sur le dossier racine C:\, et slectionnez Rechercher comme le montre la Figure 7.2.

Figure7.2: L'utilitaire de

de Windows
est une aide

recherc he

prcieuse
p0ur

retrouver ses
f

ic h iers.

Dans la bolte de dialogue qui apparat, Entre DisplayArguments.exe, et cliquez sur Rechercher. Le nom du fichier apparalt en haut du volet de droite de la fentre Rsultats de la recherche, comme le montre la Figure 7.3. Ignorezle fichier DisplayArguments. exe qui se trouve dans le dossier obj. Vous aurez peut-tre besoin de faire dfiler horizontalement Ie contenu de la fentre pour voir le chemin d'accs complet au fichier s'il est enfoui profondment dans I'arborescence des dossiers. C'est souvent le cas sivous stockez vos fichiers dans le dossier Mes documents.

Ghapitre 7:Mettre en marche quelques fonctions de grande

ctasse |

67

Visual Studio .NET place normalement les excutables qu'il gnre dans un sous-dossier bir'\'l-.hug,. Mais si vous modifiez la configuration, ce dossier peut tout aussi bien tre bin\r:elease ou un autre.

,;-;;,
Fi,:hier Edrl:rm tfirhaqe Fs'/ils utrl5 |

-li:l

''
Adresse IlJ Re(her.her

;
1..

)
fi:f

$recf en:ter -jDcssierr J


r

t't

@
{". ,']l

Flultill,l

:! - \tf: ] rfrtra! -# [ if i,. ri]jf arls'ban ['t!] r1f:rl:, ',-l:1-. 'r<:{ t.lr, - F\ :,1 ifrrr ' ,t [ ::t l.r Ar ]!ririi :,t I [ b:]

t{ Noveru

,"V

"1 Flechercher des lrchrers ou dossrers


F:fifafr l.r: |!:lrer: :r ;: Jl::,er: r r ' trr'b1,r+rqrr rtr f: rie

lI

i-rlr tr I ii l.f

U
Figure 7.3
Le voil ! Le nom du
:

lJ.

:r,t r,:r

dossier apparat droite du


nom du
f

ichier.

3.

Dans la fentre d'invite de commandes, tapez cd debug\bin pour

passer dans le rpertoire qui contient les excutables.

L'invite devient

: \ ClPr c gr

ar,s'rtispla-,'Arguments \bin\,De:ug.

Windows accepte sans problme les noms de fichier ou de rpertoire contenant des espaces, mais DOS peut avoir du mal s'y retrouver. Si vous avez un nom de fichier ou de rpertoire contenant des espaces, vous devez I'entourer par des guillemets. Par exemple, pour naviguer jusqu' un fichier qui se trouve dans le dossier Mes fichiers, i'utiliserai une commande comme celle-ci :
cd \"Mes fichi-ers"
4.

I'invite de commandes, tapez DisplayArguments lc argl arg2 pour excuter le programme DisplayArguments.exe.
Le programme

doit rpondre en affichant les rsultats montrs par

la Figure 7.4.

| 68

Troisime partie : Programmation et objets

Figure 7.4 de

L'excution

Argunents
partir de
I invite du

Dlsplay

D0S affiche
les arguments que vous avez passs au programme.

Passer des arguments a

partir d'une fentre

Vous pouvez excuter un programme comme DisplayArgrLments en tapant son nom dans la ligne de commande d'une fentre de commandes. Vous pouvez aussi I'excuter partir de I'interface Windows en doublecliquant sur le nom du fichier du programme, dans une fentre ou dans I'Explorateur Windows.
Comme le montre la Figure 7.5, un double clic sur le fichier Displ-ayArgurints excute le programme comme si vous aviez entr son nom sans arguments sur la ligne de commande :
Ce programme a 0 argunent Appuyez sur Entre pour terminer. . .

Pour terminer le programme et fermer la fentre, appuyez sur Entre. Faire glisser et dposer un ou plusieurs fichiers sur le fichier DisplayArguments. exe excute le programme comme si vous aviez entr DisplayArguments noms des ftchiers sur la Iigne de commande. (Pour faire glisser et dposer plusieurs fichiers la fois, commencez par les slectionner en cliquant successivement sur chacun d'eux tout en maintenant enfonce Ia touche Ctrl, comme le montre la Figure 7.6 ; puis faites glisser I'ensemble pour le dposer sur DisplayArgunrents. exe.) Faire glisser et dposer simultanment les fichiers argl . rxt et ar92. txt sur DisplayArguments. exe provoque I'excution du programme avec plusieurs arguments comme le montre la Figure 7.7.

Chapitre 7: Mettre en marche quelques fonctions de grande

classe

169

lr;---;
I
i-r*n,i;;,*ntt _)
Jirhrrre
K trj,triL -'r I ilr] t:t i'

t-t
P,Fte de tra\.3i1

drer:t iJl-[r,1
[ro55rr

i Ft!fJfr:.ll

r-l-

"&)

F\t.rris iJEu

!,: -

+ J | - *l . l. lttl + I [r:,:]rl:rrlj lrl iii * _J ir,ar:t,rt + I Fr: tr.lr I tE! _,.] Frt'tr :r'li;: ,--#

iJ
rt:

Figure 7.5
Dans

internt E:{plrr

Windows, vous p0uvez excuter un


pr0gramme

!j'."Dnarrr ,.]ullr,l

Elpfert

console en doublecliquant sur


le nom du

fichier
c0rresp0ndant.

|.7;\

;i
Fsvari: r:.eau

l:.:.|:'

Internet Er,k'rer

Figure 7.6: Dans

:rt:'el

lirrL,r';r,-fir'r

-l

ri'lr

4,'.
f,m3frtsr utlok

Windows,

Elpress

VOUS OOUVEZ

faire olisser
et dposer un
c_!l

:rnl'l. rFr ::
[,t::Ft:i'ir

fichier sur un
programme consote pour dclencher
s0n

''

]i

iorbeille

At,6t13 qr'trfr1ill! ilr

<trsFl: ,{r,:rnr,t:: ,:5[r ]r


[,r::t]3i qr lUfi{!:!: :.rj:

]u1f

|:5.i5r:t
r::E

execuuon.
i

-l

I 70

Troisime partie : Programmation et objets

La Figr,rre 7.7 montre le rsultat de I'excution de f)i :. -.)

'--,

-.';

dposant des.sus les fichiers ar:gi . *.xi t


!

' l- J.-. 111 :

:,

' : Il

.,ri'r..- . :r,,,..

JJ
'l- FTf.drl

l4e5 dc,:umPrrls

Figure 7.7 Faire glisser des fichiers sur le nom


:

)
l-r ,i

JFe,:f,er,:her -j!ur::,rer: .,!

,-l
s:1
Pste d trd?il

Adr5ri I

,l

d'un
programme

produit le
mme
l\.-.

t+,

rsultat que
si vous l'aviez

lntrrr;t ExFlor?i

excut pa rtir de la ligne de


commande en lui

tk,

-'

Dmrrer 'Sutloal ErFrBJ5

- l

tsl

rlr :r,nl

passant les
noms des

oa'' . '1ii
Corbeille

tj],-l::,.1 :: Etr:.t,I: r!,,n,t,,i: :::t:t n)l'1i= rln,e-ii r::[f]ll::r .tr:Fl:,'rt,rr enr', r.

+: :

li '

f -r:r,::i: -s i :r:i ,rl! -1.: ,.r:ri.l1 rr Nr:i!" r: 1r i l':rl a-,rl' ir,ri,:

fichiers
correspon dants.
-

ot,iEtl

jl

5lilnnr,iJl

ste\ =(t

Remarquez que Windows passe les fichiers , , s;,ia. .:i-!r1ii?rr:,, dans un ordre quelconque.

Passer des arguments a Studo .NET

partir de Uisual

Pour excuter un programme partir de Visual Studio .NET, commencez par vous assurer que le programme est gnr sans erreurs. Slectionnez Gnrer/Gnrer, et regardez si la fentre Sortie affiche cles erreurs. La rponse satisfaisante est Gnratlor. : i -' r'rsri, 0 a^ rh ,', I a t igrror". Si ce n'est pas a, votre programme ne dmarrera pas.
partir cle l, I'excution de votre prograrnme sans lui passer cl'arguments est un jeu d'enfant. Si la gnration a russi, slectionnez Dboguer/Dmarrer (ou appuyez sur F5), et Ie programme dmarre.

Ghapitre 7: Mettre en marche quelques fonctions de grande

classe l7l

Par dfaut, c'est sans arguments que Visual Studio excute un programme.
Si ce n'est pas ce que vous voulez, vous devez indiquer Visual Studio les

arguments utiliser

l.

Ouvrez I'Explorateur de solutions en slectionnant Affichage/ Explorateur de solutions.


La fentre de I'Explorateur de solutions affiche une description de votre solution. Une solution se compose d'un ou plusieurs projets. Chaque projet est la description d'un programme. Par exemple, le projet Disp-Lav,rrguments dit que Ciassi.cs est I'un des fichiers de ce programme et que celui-ci est une application console. Un projet contient aussi d'autres proprits, dont les arguments utiliser pour excuter le programme avec Visual Studio. C'est au Chapitre 17 que je dcris le fichier de solution.

2.

Cliquez du bouton droit sur Displar',.r.Linrnr-s, et slectionnez Proprites dans le menu qui apparat, comme le montre la Figure 7.8. Une fentre comme celle de la Figure 7.9 apparalt, montrant beaucoup de proprits du projet avec lesquelles vous pouvez jouer. S'il vous plalt, ne le faites pas.
n!.11!j.,,j,

:
Help

Eichir Edition {fhchaqe

Pritet nref Qbaguer qutjls FeBtfe

J'

__1

. .-:
l

,*:.i

r,E.ur

- i$

@re
rr!u:'!-,f !a -. i

Elasst.cs

r.i:1rl lllrstErr,
! r. i'r r j -aiLr.U1rrr-::

'1
:J

i/d r'r u ,{r t,5Eli. :r'trfrirr'1 i I

,,,1 I r,-

:.=-

T:=r lr.: IIalr:i:', 1Lr._il] ::{:

; t) , r - :'i'ri

ttr rr: : hltTffi iPtSujtgllit*.ilJJli]llr + _J Flrerr:i 'Li. lenettt tj] A:serrl'lr Ir,f :.: l'leferef {l - :ss1.'::: Ajlrllei

,lji,uter rrre reIerentt l,rrler un reiererrce'{tt,


[:eftrrr,:rJnrnrt t,rc]el

pro prits

Figure 7,8 : Pour accder aux

r-: r:.r. j , :l: 1: .:!.aa:" .:a rf p l r,tq rur(': '. ,t.i - l . III i: L I rr- I ar :{-r . LEJI h; ; ri": : : :. .a. "l .1 ..1' .'- a 1t1i rril!rlnt = tl: .1r:f ill EJJ
j

de,lerr;rt;le
)

let,lluf Erteqrst:rer f,'5F 3rAf lJf.nL5

i:,:i1:lr'siIllr:1

d'un projet, cliquez du bouton droit


sur son nom.

Y. 1t'g

net

l:?l

lW.

3.

Dans Ie volet de gauche de la fentre Pages de proprits, sous Ie dossier Proprits de configuration, slectionnez Dbogage.

| 72

Troisime partie : Programmation et obiets

Dans le volet de droite, dans la rubrique Options de dmarrage, il a un champ nomm Arquments de la ligne de commande.
x
F_rcfief Edihon Al'l'ichge lr

jet

Ge*rer

Qbcguer

gutil! Feqh*

Help

.+,l-

!
I

-. -a q

aa .'

L1trlrl

' ,:a"* :
Explorateur de soh:tions - Disp{,,.

Classt,cs

4 .X

?f
I

t,isFl

:'Ar',: rn'er't': Te:t:

:)
F.h--rj-,tr-d'

-.,r -a) fr :olrt'on

'Lr5Flni,Arqrrrnfrt5' l1 Frr,jel

!-0nfi,rurttiDn

---: 8....
tI-,i:,iF"

Flate-forme

, Fr;tr*

'***!

GestionnderonfiE:ratim:.,.

.,
,r:i;

.,..
rl:,r.r

Arguments
de la ligne de
comma n0e de la fentre Pages de

Figure 7,9 Dans le champ

[]nr,3r r r l',lF ir:

[]enr,rrt r l-lFL

F.rr lP d.,,r,,!,1P
rlr.'r l 'lEt'i,,t3tt

':l:,rer e Jehr,tq
,:tr/Ef l dEt,r!l,l,l
..,

A:F A:,F.llET
n:n
3ra,-tE
_ir

Fil5E
Flse

Fi

:l:rr'f l d--b,tl.:,1 :l(,lL a,

rer

F,l15

.l'

EtrG@EGIEE!E@/t'irlrlrrqil
FFerl:Ie J lfd'r]rl T,:rllr:!rj rtlt]tSet InlfTl Erfr rlrE Ttita

proprits,
entrez les a rguments du programme.

1..,
1

Arguments de la ligne de commnde

,,

Indique ler f'lunrents de l ligne de corrmande appeler lcrs de l'excution du prograrrrnre, oisF,nibl-s lorsque le mlde Dboqage est d#ini Four le d ,,,

t
Fl

f tr- -l

Annul?r

,,

, I tit

4.

Tapez les arguments que vous voulez passer votre programme quand il est lanc par Visual Studio.
Dans la Figure 7.9, ce sont les arguments passs au programme.

/c argl

ar92 qui seront en

5. Cliquez sur OK, puis excutez le programme normalement


slectionnant Dboguer/Dmarrer.

Comme le montre la Figure 7.10, Visual Studio ouvre une fentre DOS avec les rsultats attendus :
Ce prograrnme

a3

arguments

L'argument 0 est lc L'argument 1 est argl L'argument 2 est arg} Appuyez sur Entre pour terminer...

Ghapitre 7:Mettre en marche quelques fonctions de grande

classe | 73

ttrtils

Feqte

Help

etu,1

Figure 7.10

des argua

Visual Studio peut passer ments une pplication


console.

La seule diffrence entre la sortie de I'excution d'un programme partir de Visual Studio .NET et la sortie de I'excution du mme programme

partir de la ligne de commancle est I'absence du nom du programme dans


I'affichage.

La fonction

Writ eline

()

Vous avez peut-tre remarqu que l'instructiofi

i,,/rireLine O que vous avez utilise jusqu' programmes n'est rien d'autre qu'un appel une fonction, invoque maintenant dans divers que avec ce l'on appelle une classe Console l
Console.Writeline("ce;:i est un appel de fonction");

I,'Iriteline ( ) est I'une des nombreuses


C#.

Console est une classe prdfinie qui se rfre

fonctions prdfinies offertes par l'environnement fa console de l'application.

| 74

Troisime partie : Programmation et objets

O que vous avez utilise jusqu'ici dans divers exemples est une simple chane. L'oprateur "+" permet aux programmeurs de combiner des chanes, ou de combiner une chane et une variable intrinsque avant que le rsultat de cette opration soit pass Writeline ( ) :
L'argument de la fonction \r,lriteLine

string s =

"Sarh"

COnSOle,Writelin.i/'rTo -rennol1o

r'*

* " et j'ai t'+ 3 + "ang.")

Tout ce que voit

Writeline ( ) dans cet exemple Writeline

est "Je m'appelle Sarah et j'ai 3 ans."


:

Dans une autre forme,

O offre un ensemble d'arguments plus souple

Console.Writeline("Je n'appe11e {01 et "sarah',- 3);

j'ai {1} ans.",

lci, la chane "Sarah" est insre l'endroit o apparat le symbole {0}. Le zro se rfre au premier argument qui suit la chane elle-mme. Le nombre entier 3 est insr l'endroit marqu par{1}. Cette forme est plus efficace que I'exemple prcdent, car la concatnation de chanes n'estpas une chose aussifacile qu'ilyparat. C'estunetche quiprend dutemps. mais ilfaut bien que quelqu'un e fasse.
f

ll n'y aurait pas grand-chose d'autre en dire si c'tait l la seule diffrence. Mais cette deuxime forme de t,vriteLine O offre galement diffrentes possibilits de contrle du format de sortie. Je les dcrirai au Chapitre I.

Chapitre

Mthodes de classe
Dans ce chaptre :
Passer un objet une fonction.

Convertir en mthode une fonction membre. Qu'est-ce que this ?


Crer une trs belle documentation.

es fonctions dcrites au Chapitre 7 sont un excellent moyen de diviser

un problme de programmation en lments plus petits et plus maltrisables. La possibilit de passer une fonction et de rcuprer des valeurs entires ou en virgule flottante permet au code de I'application de communiquer avec elle. Quelques variables ne peuvent communiquer que les informations qu'elles contiennent. Un programme orient objet repose sur le regroupement d'informations dans des objets. C'est pour cette raison que C# offre un moyen pratique et lgant de communiquer des fonctions des objets de
classe.

Passer un objet une foncton


Vous pouvez passer une rfrence un objet comme argument une fonction de la mme manire qu'une variable d'un type valeur, une diffrence prs : un objet est toujours pass par rfrence.

.a-2

IIO

rroisime partie : Programmation et objets

Le petit programme suivant montre comment passer un objet une

fonction

// ll
i

Pass0bjecr

montre comment passer un objet une fonction

rrcino Sv<tom'
nmsDc PassOhr'ect

nrrl^rlic r. lnss Strrdent


{

pubiic string
l
nl.hlr^ yuurrL ^ ^^n Lad' t'-SS1 uf,d.

sName;

public static void Main(string[] args)


{

Student student = new Student 0

// dfinit
OutputName I

1e nom en

accdant directement

Console.ldriteLine("La premire
student, sName

fois :")

= "Madeleine";
;

(student)

change 1e nom en
,

Console
SetName

utilisant une fonction l,IriteLine ( "Aprs avoir t modifi


;

:"

(student, "l{i1la")

OutputName(student);

// attend confirmation de 1'utilisateur


Console.WriteLine("Appuyez sur Entre pour terminer...") Console.Read0;
;

// vuLPuLrrd.xrtr ^"+-"+T^-^ affiche le nom de 1'tudi"ant public static void OutputName(Student student)
rI
{

//affiche le nom de 1'tudiant couranr Console.l,trriteLine ( "Le non de 1'tudiant


l

est

{0} "

student. sNane)

/l SetName nrodifie le
t

non de

1'objet tudiant
sName)

public static void SetNane(Student student, string


student.sName

sNane;

l l

Le programme cre un objet de la classe Studenr, ui ne comporte rien d'autre qu'un nom. Ici, nous aimons la simplicit chez les tudiants. Le programme commence par dfinir directement le nom de l'tudiant, et le

Chapitre

I : Mthodes de classe

t77

passe la fonction d'affichage Out putlJane ( ) , qui affiche alors le nom de tout objet de la classe Student qu'elle reoit. Le programme change alors le nom de l'tudiant en appelant la fonction SetNanie O. Comme en C#, tous les objets sont passs par rfrence, le changement fait str.rclent est rpercut dans la fonction appelante. Lorsque Main O affiche nouveau le norn de l'tudiant, celui-ci a charrg
T ^ *-^-.: -^ !d u!Euf,!g

-^i lurD.^

Le non de 1'tudiant est Madeleine Aprs avoir t nodifi. : Le non de 1'tudi-ant est Wil1a Appuyez sur Entre pour terminer..

La fonction SetName ( ) change le nom de l'objet studerrt de la classe Srudenr, t cette modification est reprise par le programme appelant.

Dfnr des fonctons et des nthodes d'objet


Une classe est faite pour rassembler des lments qui reprsentent des objets ou des concepts du monde rel. Par exemple, une classe Vehir:1e peut contenir des lments qui sont des donnes telles que la vitesse maximale, le poids, la capacit de charge, et ainsi de suite. Mais un objet de la classe Vehicle possde galement des proprits actives : la capacit de dmarrer, de s'arrter, et ainsi de suite. Celles-ci sont dcrites par des fonctions qui utilisent les donnes des objets de cette classe. Ses fonctions font partie de la classe Vehicle tout autant que les proprits de ses objets.

Dfnir une foncton membre stati4ue d'une


classe
Vous pourriez par exemple rcrire le programme de la section prcdente en I'amliorant un peu :
/

il

Pass0bj ectToMetnberFunction

utilise
dans

des fonctions nenbres statiques


champs

pour manipuler des

I'obiet

using

System;

nanespace Pass0b j ectToMemberFunction

| 78

Troisime partie : Programmation et objets

public class
{

Student
sliame;

public string

/l
t

OutputNane

- affiche 1e non de 1'tudiant

public static void 0utputName(Student student)


I I affiche le nom de 1'tudiant courant Console.Writeti.ne("Le nom de 1'tudiant est

t0J",

student.sNane);

// SetNanre modifie le
public static void
t

non de

1'objet

student
sName)

SetName(Student

student, string

student,sNane = sNane:

l l
-..11.: ^1^-PUUITL^ LrA t

Classl

public static void Main(stringll args)


t

Student student netr Student 0 ; I I f.init le nom en y accdant directement Console.l^IriteLine("La prenire foi.s :") ;
student. sName

trMdeleine";

fonction Console.l,lriteline("Aprs avoir t nodifi :")


Student.SetName(student, "l.Jil1a")
Student . OutputName ( student )
I
; ;

Student, OutputNane ( student) ; I I ehange 1e non en utilisant une

attend, confirmation de 1'utilisateur Console.]rfriteLine("Appuyez sur Entre pour terniner. . . ") Console.Read0;
]

]
l

Ce programme ne prsente qu'une diffrence significative avec le programme

Pass0bject de la section prcdente:j'ai mis les fonctions OutputName O et SetNanre O dans la classe Student.
Du fait de cette modification, Main t ) doit rfrencer la classe Student dans les appels Setl'Jame O et OutputName O. Ces deux fonctions sont maintenant des membres de la classe Student, t non de C1ass1, la fonction dans laquelle rside l"lain O.
C'est une tape modeste mais significative. Placer OutputName O dans la

classe elle-mme la rend plus rutilisable : une fonction extrieure qui aura

Ghapitre E : Mthodes de classe

t79

besoin d'afficher l'objet trouvera OutprrtlJane i) avec d'autres fonctions d'affichage cet endroit, car faisant partie de la classe. C'est galement une meilleure solution cl'un point de vrre philosophique. Clas s 1 ne doit pas avoir se proccuper de la rnanire d'initialiser le nom d'un objet Stu,-leni, ni de la manire d'afficher des lrnents importants. C'est la classe Srudent qui cloit contenir ces infornrations.
En fait, ce n'est pas fla 1n "Madeleine". EIIe devrait
(

) clui devrait comrnencer par initialiser le nom plutt appeler pour cela SetNarni: i).

Depuis la classe Stucient, une fonction membre peut en invoquer une autre sans avoir voquer explicitement le nom de la classe. Se ti{ame () peut invoquer t)utputl'larne ( ) sans avoir besoin pour cela de rfrencer le norn de la classe. Si vous omettez celui-ci, C# suppose que la fonction laquelle vous voulez accder appartient la mme classe.

Dfinir une nt(tode


C'est par I'objet, et non par la classe, que l'on accde un membre donne d'un objet. On peut donc crire :
Student student = new Student 0
student.sName

t'Madeleine"

C# vous permet d'invoquer de la mme manire une fonction membre non statique
:

student. SetNane ("Madeleine" ) ;

C'est la technique que montre I'exemple suivant

*-f,*i3 l----Hi';'f
I
trY"Y/

//
{

InvokeMethod

invoque une fonction nembre oarti.r de 1'obr'et

using Systen;
namespace InvokeMethod

class Student
t | | te nom de 1'tudiant dcrit 1'objet student pubLic string sFirstName;

publie string slastNane; // SetName - net de ct le non de l'tudiant

I80

Troisime partie : Programmation et

ob

jets

public void SetName(string


I

sFNane,

string

slName)

sFirstNane slastName
l

: :

sFNane;

slNane:

// II
{

ToNameString

- converti en chane pour affichage


1'obiet
student

public string toNameString0

string s = sFirstNane
return s;
)

tr rr + slastName;

public class Classl


{

public static void Main0


{

Student student = new Student 0 ; student . SetName ("Stephen" , "Davis") ; Console.I,.Iriteline("Le non de 1'tudiant est'r

* studenr.ToNaneStringO // attend confirniation de 1'utili"sateur


Console,Read0; l
)
1

Console.\'lriteline("Appuyez sur Entre pour terminer., .")

La sortie de ce programme est cette simple liqne


Le non de 1'tudiant est Steohen Davis

En dehors d'avoir un nom beaucoup plus court, ce programme est trs semblable au programile FassOb j ectTol,lenberFunction que nous avons

vu plus haut. Cette fonction utilise des fonctions non statiques pour manipuler un prnom et un nom.
Le programme commence par crer un nouvel objet, sti-rdent, de la classe Student. Il invoque ensuite la fonction SetName O, qui stocke les deux chalnes "Stephen" et "Davis" dans les membres donne sFirstlJane et sLastl'Iarne. Enfin, le programme appelle la fonction membre TolJameSt rlng ( ) , qui retourne le nom complet de student concatnant les deux chanes.

ChapitreS: Mthodes de classe

| 8l

Pour des raisons historiques qui n'ont rien voir avec C#, une fonction membre non statique est communment appele une ntthode. J'utilise le terme ntthode pour une fonction membre non statique, et le terme fonction pour toutes les autres fonctions.
Regardez nouveau la fonction Setf,lame O qui met jour le nom et le prnom dans un objet de la classe Student. Quel objet modifie
Setl{arne

Pour voir le problme, considrez I'exemple suivant

Student christa = nei," Student 0 ; Student sarah = new Student ( ) ; christa. SetName ( uChrista" , "Snith" ) ;
sarah. SetName ("Sarah"

"Jones")

Le premier appel Setl'Jame O met jour le nom et le prnom de I'objet

christa.

Le deuxime appel met jour I'objet sarah.

:712 lfdll lL-r^t \Zl

Voil pourquoi un programmeur C# dit que cette mthode opre sur I'objet couront. Dans le premier appel, I'objet courant est cl,rista. dans le deuxime, c'est sarah.

Pourquoi des mthodes

Pourquoi des mthodes ? Pourquoi de simples fonctions ne suffiraient-elles pas ? Les mthodes jouent deux rles diffrents mais importants.
La mthode SetName ( ) masque les dtails de la manire dont les noms sont stocks dans la classe Student. Ce sont des informations dont des fonctions extrieures Student ne

sont pas censes avoir besoin. C'est un peu comme la manire dont nous utilisons les boutons d'un four micro-ondes : ces boutons masguent le fonctionnement interne de
l'appareil, que nous n'avons pas besoin de connatre.
Le second rle d'une mthode est de reprsenter les proprits vritables de la classe. Un avion peut acclrer, virer, dcof ler et atterrir (entre autres choses). Une classe Al rplane

complte devrait donc comporter les mthodes Accelerate ( ), Bank ( ). Take0f f ( ), et Land ( ), reproduisant fidlement ces proprits. Mettre la reprsentation d'une classe en ccord avec son quivalent du monde rel permet de penser un programme dans des termes quisont ceux du vritable problme, plutt qu'en un vocabulaire ar-tificiel dict par le langage de programmation utilis.

| 82

Troisime partie:Programmation et obiets

Le nom complet d'une mthode


La description que j'ai faite du nom d'une mthocle comporte un problme subtil mais important. Pour le voir, exarninez I'exemple de code suivant
:

nrrhl i
{

n cl ess

Pprson

public void Addresso


t

Console.

l^lriteline ( "Hi" ;

l
nrrhl
t

i,.

r.

l:ss T,etter
sAddress;
sNewAddress)

string

//rnet de ct 1'adresse publi.c void Address(string


{

sAddress l l

= sNevAddress;

Toute considration ultrieure sur la mthode.oclrsLr i I est maintenant ambigu. La rnthode Adoress O de Person lt'a rien voir avec la mthode ACdress (l cle l.etter. Si un ami programmeur me dit d'utiliser la mthode .,,idress (,, de quelle Address ( ) parle-t-il ?
Le problme ne vient pas des mthodes elles-mmes mais de ma description. En fait, il n'y a pas de mthode Address O, mais seulement une mthode Person. Ad.lress ( ) et une mthode Letter. Address O. Ajouter le nom de la classe au dbut du nom de la mthode indique clairement de quelle mthode il s'agit.

Cette description est trs semblable la question des noms propres. Dans ma famille, on m'appelle Stephen. Il n'y a pas d'autre Stephen dans ma famille, mais il y en a deux autres l o je travaille.
Si je djeune avec quelques collgues et que les deux autres Stephen ne sont

pas l, il est vident que le nom Sfep hen se rfre moi. Mais de retour dans les bureaux, si vous appelez le nom "Stephen", c'est ambigu car il peut se rfrer n'importe lequel de nous trois. Il vous faudra donc appeler "Stephen Davis" pour viter Ia confusion avec "Stephen Williams" ou "Stephen Leija".

Autrement dit, vous pouvez considrer Address O comme le prnom, ou le surnom. d'une mthode.

Chapitre 8 : Mthodes de classe

I 83

=l ,,1 -t'

AP\ \tt

\ I

Le nom de la classe est un autre moyen de diffrencier cles nom.s cle mthode surchargs, les autres tant les noms et le nombre de ses arguments de tonctron.

Accder l'o biet courant


Examinez la mthode SruCerni . Setl'lan class
i
nom de jn ctrinc nrrhl L!rrr yuurrL
.in nrrh'l yuUlaL clrinc L!ilr

Student

llle

1'tudiant dcrit 1'obiet student


!t!DLL\dUlY, ^T^a+trI^-^. IrL.lIdllltj'

.F'i-.+\1-*^.

//
t
f

SetName

met de ct

le

non de 1'tudiant
sFName,

public void SetName(string


sFirstName = sFNane;

string

slName)

slastName = slNane;

l
l

public class C1ass1


{

public static void Hain0


{

Student studentl = nevr Student 0 ; studentl . SetNarne ("Joseph" , "Snith") Student student2 - new Student 0 ; student2. SetNarne("John", "Davis") ;

l
]

La fonction Main O utilise la mthode Setllame O pour mettre jour d'abord student 1, puis student2. Mais vous ne voyez de rfrence aucun objet de la classe Student dans Ia mthode SetlJame ( ) elle-mme. En fait, elle ne contient aucune rfrence un objet de la classe Student. Une mthode opre sur "l'objet courant". Comment fait-elle pour savoir quel est I'objet courant ? L'objet courant est pri de se lever.
La rponse est simple. L'objet courant est pass comme argument implicite dans I'appel la mthode. Par exemple
:

studentl

SetNane

("Joseph"

, "Snith") ;

|84

Troisime partie : Programmation et objets

Cet appel est quivalent :


Student.setName(student1,

"Joseph", "Smith") ; I I appel quivalent


I I

I (mais ceci ne sera pas I g6n& eorrecternent)

Je ne suis pas en train de dire que vous pouvez invoquer SetNane O de deux

manires diffrentes, mais simplement que les deux appels sont quivalents d'un point de vue smantique. L'objet qui se trouve juste gauche de "." (le premier argument cach) est pass la fonction tout comme les autres arguments. Passer un objet implicitement est facile avaler, mais que diriez-vous d'une rfrence d'une mthode une autre ? public class Student
t
ntrhli^ c*riro cE'i r! r"ctNrmo' D Llrell PUVTTL L! f rr r -,.1^1.i ^T ^ ^+-.:-^ ^^+[1^-^. LtArr !ALrrdrl, PUUaJL
t

public void SetName(string sFirstName, strlng


t

slastNarne)

SetFirstName ( sFirstName) SetlastName ( slastName) ; ]

public void SetFirstNane(string


t

sName)

sFirstNane = l

sNane i
sName)

public void SetlastName(string


{

slastNane = l
]

sName;

Aucun objet n'apparat dans I'appel SetFirstName O. L'objet courant continue tre pass en silence d'un appel de mthode au suivant. Un accs n'importe quel membre depuis une mthode d'objet est cens concerner I'objet courant.

Qu'est-ce que

this

.)

Contrairement la plupart des arguments, toutefois, I'objet courant n'apparat pas dans Ia liste des arguments de la fonction et ne se voit donc pas assigner un nom par le programmeur. Au lieu de cela, C# assigne cet objet le nom this.

Ghapitre

8: Mthodes de classe

| 85

=(d

#\
)

this est un mot-cl, et ne doit

clonc tre utilis pour rien d'autre.


:

On pourrait donc crire I'exemple prcdent de la faon suivante public class


t
yswLLw ^,,L1i^ ntrhl in yss4+s a+r.i-^ aD"i *-+1^*^. o u. rrr$ sFirstNarne nT aa+1\T^n^. ei-rinc' r L. rrri !d. Lllclllle ,
;

Student

public void SetName(string sFirstName, string


t

slastName)

ll rfrence expliciterent "1'objet courant" rfrencs par this


this. SetFirstNane (sFirstName) ; this . SetLastNane (slastName) ;
]

public void SetFirstName(string


t

sName)

this.sFirstName =

sName;

l public void
{

SetlastNane

(string

sNane)

this.
]

slastName

sName;

Remarquez I'introduction explicite du mot-cl this. Ajouter this aux rfrences au membre n'ajoute en fait rien, car this est implicitement suppos. Toutefois, quand Main ( ) effectue I'appel suivant, this rfrence stud ent I partout dans S erName ( ) et dans toute autre mthode que

pourrait appeler Main O.


student1,SetName("John", "Smith")
;

Quand

rhl s est-il explcite

.)

Normalement, il n'est pas ncessaire de se rfrer explicitement this, car il est implicitement compris par Ie compilateur l o il est ncessaire. Toutefois, il y a deux cas assez courants dans lesquels this est ncessaire. Tout d'abord, pour initialiser un membre donne:

1l
t

Address

- dfinit

un "cadre de base" pour une adresse aux

USA

class

Person
sName;

public string

| 86

Troisime partie : Programmation et objets

yuuf nrrhli^ yu!arL

rL

rll

rltu

r'n'izl vvlu

Tni+(o1r'i IllaL \L!arr6 1g

s1\ame,

r-nr ntu/
. Yh\

t
L

this.
J

sName

sName;
;

this . nID =
1

nID

Les arguments de la mthode Init O sont nomms sllanc t nIi', ce qui est identique aux noms des membres donne correspondants. Cette disposition rend la fonction facile lire, car elle permet de savoir exactement o est stock quel argument. Le seul problme est que le nom si'i.,Lme dans la liste des arguments rend obscur le nom du membre donne.

L'introduction de this permet de savoir de quel -ql'Jane il s'agit : dans Init O, Ie nom sllame se rfre I'argument de la fonction, alors que thi s . sNane se rfre au membre donne.
Vous aurez galement besoin de this pour mettre de ct l'objet courant afin de I'utiliser ultrieurement ou de le faire utiliser par une autre fonction. Regardez I'exemple suivant, R,e f e r enc i n gTh i sExp 1 i c it i'i:
/ / Rpf ercnni noThi cEynl /
n'i t lrr - n nr montre -- riogramme utiliser explicitement 1a rfrence this
'i

conment

rrs'ino Svctpm'
namespace Ref erenc ingTh j.sExplic t

itly

public class
{
(

C1ass1

public static int Main(string[] strings)


t

I lcre un objet student Student student = new Student ( ) ;


student. Init("Stephen Davis", L234) //inscrit 1'tudiant un cours
Console. ItlriteLine
lttt

510tog1e tu1"/; student.Enro11 ("Biologie 101") /laffichage des cours auxquels est inscrit 1'tudiant Console.Iljriteline("Nouve11es caractristiques de 1'tudiant :")

f"rnscrlptlon e )tepnen Davls a


;

!^'rr\

student. DisplayCourse

//

Console.

attend confirmation de 1'utilisateur Idriteline ( "Appuyez sur Entre pour terminer.


0;

..")

Console.ReadO;

return

Chapitre

I : Mthodes de classe | 87

// Student - notre tudiant d'universit orrblic elass Strrdent


{

lltout tudiant a un nom et un nunro d'identification (id)


public string public int
sName;

nID;
;

l/1e cours auquel est inscrit 1'tudiant ll Init - initialise 1'objet student public void Init(string sName, int nID)
{

Courselnstance courselnstance

this.sNane =

this.nID : nID;
]

sName;

courselnstance = nul1;

/l Enro1l - inscrit 1'tudiant courant un public void Enro1l(string sCourseID)


{

cours

courselnstance = new Courselnstance 0


courselnstance. l

Init (this ,

sCourseID)

//affiche 1e nom de 1'tudiant I I et Ie cours


yni r.l Di.^l ""f^'r.^^/\ nrrhl r----- in -Prdluuurs\/
{

Console. l,irj.teLine (sName)

courselnstance.Display ( ) ;
l

n^,.rselnstance -..L1.r^ ^1 ^^- VVU PUUTTL LID


{

// II

Courselnstance

associe 1'tudiant au cours auquel i1 est inscrit

oublic Student student; public string sCourselD;


I

I tnit - tablit le lien

entre l'tudiant et 1e cours


sCourseID)

public void Init(Student student, string


i

this. student = student; this. sCourseID = sCourseD;


Display - affiche f public void Display0
]

//
{

intitul

du cours

Console.l,lriteline (sCourseID)
l l
tt

| 88

Troisime partie : Programmation et objets

Ce programme est trs quelconque. L'objet de la classe Student peut contenir un nom, un identificateur, et un seul type de cours universitaire (ce n'est pas

un tudiant trs occup). l,lain ( ) cre l'tudiant, puis invoque lnit I ) pour initiali.ser I'objet de la classe Str-iCent. ce point, la rfrence corjf sLrf ristance reoit la valeur nui 1, car l'tudiant ne s'est pas encore inscrit au cours.

Enroll O inscrit l'tudiant en initialisant courselnst,ance avec un nouvel objet. Toutefois, la mthode Cour-qelnstance.Inrr O prend un tudiant comme premier argument avec I'identificateur du cours comme deuxime argument. Quel tudiant faut-il passer ? Il est vident qu'il faut passer l'tudiant courant, celui auquel se rfre this (on peut donc dire que Enrcli t ) inscrit cet (tris) tudiant au cours Courseinstanr:e). Les mthodes Disi-.1a-, O affichent I'tucliant et les noms des cours.
La mthode

Et quand je n'ai pas

this I

Mlanger des fonctions de classe et des rnthodes d'objet, c'est un peu cornme de mlanger des cow-boys et les propritaires de ranch. Heureusement, C# vous donne quelque moyen de contourner les problmes relationnels de ces cratures. a me rappelle un peu la chanson d'Oklahornct!: "Oh, la fonction et la mthode peuvent tre amies..."

Pour voir le problme, regardez I'exemple de programme Mixin gFunc t i onsAndMethod s :

// II
t
I

l'tixingfunctionsAndl'lethods
C,,^+^*. y Lctrl,

- mlanger des fonctions de classe et des mthodes d'objet peut causer des problnes

',^i-^ ufrr6

nane space MixingFunct ionsAndMethods


nrrb'1

ic

cl ass Strrdent

public string sFirstName; public string slastNane;

// lnitstudent - initialise 1'objet


l

student
slastName)

public void InitStudent(string sFirstName, string this. sFirstName = sFirstNane: this. slastNane = slastName I
.I

I/
{

OutputBanner affiche

introduction

public static void OutputBanner0


Console.I,rIritel,ine("Regardez comme

je suis nalin :");

Ghapitre 8:Mthodes de

classe

189

I ]

I Console.l,/riteLine(? quel objet student utilisons-nous ?);


OutputBannerAndNarre
()

public void
{

// c'est 1a classe Student qui est suppose mais pas a i / 1'objet est pass 1a mthode statique
OutputBanner ( )
;

ll ce n'est pas 1'objet this qui est pass mais 1'objet i/ student courant qui est pass explicitenent
OutputNane ]

(this

//
{

Outputnane

- affiche 1e norr de 1'tudiant

public static void Outputliane(Student student)

Console.l.]riteLine
1

ll iei,1'objet student est rfrenc explicitement ( "Le non de 1'tudiant est {01 "
,

student . ToNameStrins
t nom

))

// ToNaneString - va chercher 1e n,rhl i n ctri-1g ToNameString 0


{

de 1'tudiant

ll ici, le non de 1'objet courant est implicite // ce qui. aurait pu tre crit // return this.sFirstName + rt tr * thi-s.slastNane; return sFirstName + tr n * slastNane:
:

] ]

public class
t

C1ass1

public static void Main(string[] args)


i Student student = new Student 0 ; student. InitStudent("Made1eine", "Cather") // affiche 1a bannire et te nom
Student. OutputBanner 0 ; Student . OutputNane ( student)
;

Console.TiritelineO; // affi.che nouveau 1a bannire et 1e


student . OutputBannerAndName i ) ;

nom

I I attend confirnation de 1'utilisateur Console.llriteLine("Appuyez sur Entre pour terminer... ")

Console.

Read

l
]
I

Commencez par le bas, avec |lain () , pour mieux voir le problme. Ce programme commence par crer un objet StLident t initialiser son nom.

| 90

Troisime partie : Programmation et objets

Maintenant, ce nigaud (le programme, pas l'tudiant) veut simplement afficher le nom, prcd par un bref message et une bannire. l,lain O commence par afficher la bannire et le message en utilisant des fonctions de classe. Le programme invoque la fonction OutputBanner o pour la bannire, et la fonctio ( 'rf pr-rtllame ) pour afficher le message et le nom de l'tudiant. La fonction OutputBanner () affiche simplement un message sur la console. Main () passe I'objet student comme argument OutputNarne O afin que celle-ci puisse afficher le nom de l'tudiant.
Ensuite, Main ( ) utilise I'approche de Ia fonction ou de la mthode d'objet pour afficher la bannire et le message en appelant student . 0utputBannerndl,larre 1 .r . 0utp,-rtBanner AnillJarie ( ) commence par invoquer la fonction statique 0rrtputBanner'( ). La classe Sttident est suppose. Aucun objet n'est pass, car la fonction statique n'en a pas besoin. Ensuite, OutputBannerAndl'Iame o appelle la fonction 0utputl.Jame () . Celle-ci est galement une fonction statique mais un objet de la classe St,udent lui est pass comme argument

par

Lrr r

pu: Ban:.- r And I Ja:-

Un cas plus intressant est I'appel de Tol'.larneStrlng () depuis CutoiltNane O. Cette dernire fonction est dclare sratlc, et par consquent n'a pas de this. Elle a un objet explicite de la classe Student qu'elle utilise pour raliser

cet appel. La fonctioo 0utpul-Banner (I voudrait peut-tre pouvoir appeler aussi ToNameString O, mais elle n'a pas d'objet de la classe Str-rdent utiliser. Elle n'a pas de pointeur tfiis parce que c'est une fonction statique et qu'aucun objet ne lui a t pass explicitement. Une fonction statique ne peut pas appeler une mthode non statique sans lui passer explicitement un objet. Pas d'objet, pas d'appel.

0btenir de l'aide de Usual Studo autotnat(ue

- la saise

Visual Studio .NET comporte une fonction de saisie automatique extrmement utile au programmeur. Lorsque vous tapez le nom d'une classe ou d'un objet dans votre code source, Visual Studio utilise les premiers caractres que vous tapez pour anticiper la suite et vous proposer un choix de noms parmi lesquels se trouve celui que vous voulez saisir.

Ghapitre

I : Mthodes de classe

t9t

Cette fonction de saisie automatique est plus facile dcrire par un exemple. J'utiliserai pour cela le fragment suivant du code source du

programme Mixi

ngFrrn c t, i on s Andi'lethod s

// affiche 1a bannire et 1e non


Student .OutputBanner ( ) ; Student . OutputName ( student )
;

Console.I,Triteline0; I I af.fiche nouveau

la bannire et 1e nom

student . OutputBannerAndName ( ) ;

0btenir de l'aide sur les fonctons ntgres de la bibliothque standard C#


Dans le fragment de code ci-dessus, lorsque je tape Console., Visual Studio affiche la liste des mthodes de Consol-e. Lorsque que je tape le 1,/, Visual Studio slectionne dans cette liste la premire mthode dont le nom commence par W, qui est i,Jrite O.Le dplacement de la slection d'un cran vers le bas, en utilisant la touche de curseur correspondante, slectionne r,n/rireLirre O. droite de la liste, en regard de vJriteLine ( ), apparalt une info-bulle qui en contient la description, comme le montre la Figure 8.1. Cette info-bulle indique galement qu'il existe dix-huit autres versions surcharges de la fonction r,,rrriteLine O (chacune avec un

ensemble d'arguments diffrent, bien str).

Figure 8.1
La

fonction
r
:it llrlE 851 ,-rrf
r:

automatique
de Visual Studio est une aide
,l

de saisie

prcieuse pour choisir la bonne


mthode.

"' n"rd :,ti:r,t i Ftldlrrr tti-=,:' Q F f rrr-eE,l!Jl5


C :letErr':t
O:,eLIn

Entr.:

t rlr 1llf

. ''

I
r I
T

e ili 'rl
Q'.\rit:.:

-&

Il me reste complter le nom de la fonction, \ririteli ne. Ds que je tape la parenthse ouvrante, VisualStudio affiche une infobulle indiquant les arguments que comporte la fonction, comme le montre la Figure 8.2. Remarquez

| 92

Troisime partie : Programmation et objets

que cette info-bulle commence par indiquer le numro de la version de la fonction parmi toutes celles qui existent, avec deux flches qui permettent de faire dfiler ces versions pour identifier celle que vous voulez.

Schier

E-ditron

Efqjt

lenrer Qeboguer q,!til: FanLre

$e{p

14,tt,.
-'lr -+ "|'

a"
!

&*&q4

1&.-

tr _
;l ]

r,ebu,,l )rplorteur dE scdulfrns - t4ixin..,

Elassl.cs+

Ji

lt!llr:,rn,tF:nttr'rr:nlt4elh:,l5., lii:1

tlr,trn,i:trn,'t[] ar't:t

:l

{ :J

$ -

5,rlrrl:rr'r'll irr'tFrrr:i.,rsAr'llrElh:,15

VixinqfunctronAndMethods

Figure 8.2 de saisie


a

La fonction

-utomatiq ue
affiche aussi la liste des arguments pour la version de votre choix
de la
. I j I I - .'rrl ir'rs:le
'JlrtrteLirr

istring format,

t,.3f.jnt5

,tt,je':f[] :rl1l

fonction

\,,rriteline

(1.

-rtC .($-:-

lsz//
-

Vous n'avez donc pas besoin de taper le nom de la fonction. Imaginez que vous ayez tap Writel pour identifier exactement la mthode voulue. En voyant le nom iir:iteLine slectionn dans la liste, il vous suffit de taper une parenthse ouvrante pour que Visual Studio complte automatiquement ce nom pour vous, aprs quoi, il vous restera taper les paramtres que vous voulez passer, et la parenthse fermante. Pour faire apparaltre la description des arguments de la version de ',,iriteline 0 que vous cherchez, cliquez sur I'une des flches dans I'infobulle qui apparalt lorsque vous tapez la parenthse ouvrante. Dans cette info-bulle, la description du premier argument que vous avez saisir apparalt en gras, comme le montre la Figure 8.2. Aussitt que j'ai entr la chalne "chane", et une virgule. Visual Studio met en gras la description du prochain argument saisir, comme le montre la
Figure 8.3.

Ghapitre I : Mthodes de classe

| 93

rtcnter
'

l--, ,,, . ..' + 4, 'l'-,


rl-lw

toriln t'

{fFichage

lrr,1e!

Qenrer Qboguer Qutils Fe8tre

Help

'
ii4:{ ,ii.t
1*:

&

"&'&

l.& -

'P ;l I

oetu!
Erploraleur ,je ,:lutions -

flasst.cs*

l"liin.,. +

.v.

Cr,t:in strrnll;r,1:i

:J L :J

:-clrIc'r l lr rr,lFrir'rlr:f;5r,Jl lBlf di

.F HixrnqFun{tionf AndMethods + :j e|eferari 1l] ::enblrlrri:':: .j! al551 rs

Figure 8.3

A chaque

Studio

tape, Visual
l^ I lE 1'l - ,/,r I ' ,rfsirl rriillLr.e lilrrrrl ir,fiill:, prdnrs obiect[] argl :- rl,-lrl.t .._]. Irl-L:ltrr.pr.i_rr,-l].J:illt I ;

affiche en gras la desc ription du prochain argument


saisir.

Ds que vous tapez la virgule qui suit un argument aprs I'avoir saisi, Visual Studio affiche en gras dans I'info-bulle la description du prochain argument
saisir. Bien

str, cette aide est disponible pour toutes les rnthodes intgres de la bibliothque standard C# utilises par votre programme.

0btenir de l'ade sur rlos prolrres fonctions et nthodes


Vous pouvez aussi obtenir de I'aide sur vos propres fonctions.
En continuant avec I'exemple de la section prcdente, j'efface la chalne

"chalne" pour la remplacer intentionnellement par une chalne vide : Console.'ririteLir:e i). Sur la ligne suivante, je tape "student.". Ds que j'ai tap le point, Visual Studio affiche la liste des membres de I'objet strLden.i-, comme Ie montre la Figure 8.4.
Remarquez les icnes qui prcdent les noms des mthodes dans la fentre d'aide : un petit rectangle qui penche vers la droite indiclue un membre donne ; un petit rectangle qui penche vers la gauche indique une mthode.

t94

Troisime partie : Programmation et obiets

" .::'
Pfrlel
$nrer

Qeboguer

Qutils

Fe1he

Help

*7*

,"6 "& -

,tr

v, tel:tt_t
EaplorterJr desdulirn:: -

;i

ll:rnsrrrr':[]

:,;:'

:J

)*"
|

lL'li{rr.,.

4 rl

*j ,:l

:',: :| rr'l,lr inqF!fr :ir : n:r|Jl lEi:frt,,J:' - -@HixinqFunctionsAndMethodt


i j rE lrrilLE:

1ll :;errl,l,'irri: ::
r_!l d;s:1,cr

arlt:

-l:'

:Li::-

1,r,1rl

Figure 8.4 La saisle automatique est galement disponible


:

4 9 4

g
t\\vl

DOUT VOS

eEl,lfir, rr;
/::Lt:ll,llriE S li,fjtrn:,1:rrr,l e T,t5lrrr,l

E,tr:ij ,lHi::l-',ri ,.eL fr ir t;t uleri

pr0pres

\z

^r\|' ,-f,

,a

Ces icnes sont faciles reconnaltre. Celle d'un membre donne est en bleu clair. celle d'une mthode est en violet et prcde de trois traits

horizontaux.
Dans la fentre, il y a des mthodes que je ne reconnais pas. Ce sont des mthodes de base que reoivent d'office tous les objets. Dans ce groupe de

mthocles standard. vous voyez notre propre ilutpr-itBannerAn,ilrlane (). Ds que je tape le O, elle est mise en surbrillance, et I'info-bulle apparalt pour en dcrire les arguments, afin que je sache comment I'utiliser.

^qa/ I/}il tSZt Y

Encore une fois, il vous suffit alors de taper une parenthse ouvrante pour que le nom de la mthode, pralablement mis en surbrillance dans la liste. soit automatiquement complt. Cette aide marche aussi pour les fonctions. Lorsque j'entre le nom de classe S:,ident suivi par un point, Visual Studio affiche la liste des membres de SruCent. Si je tape ensuite OutputN, Visual Studio affiche I'info-bulle contenant la liste des arguments de Outputllan.e | ), comme le montre la Figure 8.5.

Ghapitre 8: Mthodes de

classe |

95

n hiet

Editin

,t,+J

4a:,,

':.:,..

;t,

',J':"1 :
|

flassl.cs*
I

E) ,liil.3lpur '-l ls:r

'le sDlrjhns -

Mr{n,,, 4

ti lii, ir,QFur rlEr,srdlfethi'd:

-{ -t

]'-:r'lutrrr, l'' rr,lFun,:lDn:Andtiel:h'rd::' - -@HixrngrunctronsAndMethods

Figure 8.5

!;il}:
:

I1- !:'Ii::r:: : In:rS

La fonction

I'rrif i :.i:t ii e

r..,tr,:l lliitlt i-:lr1tr,.r[]

aL!{;rl

- utom atiq
a

de saisie

5tu-lnt :.t-u,tlent = ]iEi" Slr.lclEllr il j slritlnt, Ifi:1trf,,lrt ("I'lf,leIi1nE". "i.ir:Lr"l,:


ri:::'t..::41 .-ara.:.:, ., qt r\ilf r !rr hrlr Er,r,1- i ' ::it )-lrrf , f,rt prrt-14
.

de Visual Studio donne bea ucoup

ir,rr-rr 1 E,uu1.
-

d'information, pour les


mthodes
d'o bjet

( ur:ie

:i i

i,,utt,ult8ar,r,er

ll'ttulea

i,'

trt

rtl I ix i il,J,l trrl :f

Jerlf

comme pour les fonctions de classe.

Encore plus d'ade


La fonction de saisie automatique cle Visual Studio apporte une aide importante en anticipant sur les membres qlre vous voulez saisir ds que vous entrez le nom de la classe ou de l'obiet.

Visual Studio ne peut fournir qu'une aicle limite pour les fonctions et les classes cres par I'utilisateur. Par exemple, il ne sait pas ce que fait la mthode Or,itpurlJame O. Heureusement, Visual Studio vous offre un moyen dtourn de dire la fonction de saisie automatique ce que fait la fonction, et mme un peu plus.
Pour indiquer une ligne de commentaire normal, vous utilisez deux barres obliques : / /. Mais Visual Studio comprend aussi comme un commentaire spcial ce qui est indiqu par trois barres obliques : I I I . Un tel commentaire de documentotion permet de donner Visual Studio des informations supplmentaires, utilisables par la fonction de saisie automatique.

t96

Troisime partie: Programmation et objets

S%H\ dispose d'un programme supplmentaire capable d'extraire les commen=\J\y J taires marqus par ces trois barres obliques pour les rassembler dans un Y'/ fichier de documentation spar. C# a apport une amlioration cette innovation : I'aide en cours cl'dition.
Un commentaire de clocumentation petrt contenir n'importe quelle combinaison des contmandes montres par le Tableau 8.1.

1t$!Qa^ Pour tre honnte, c'est le langage Java qui a introduit cette ide.

Java

Tableau 8.1 : Balises communes des commentaires de documentation.


Balise
<pa ra m></pa ra m>

Signification
Description d'un argument de la fonction, affiche par I aide aprs la saisie du nom de la fonction et de la parenthse ouvrante, expliquant
ce que vous avez saisir.
a

<su mma ry></summ

ry>

Description de la fonction elle-mme, affiche en cours d'dition lors de la saisie du nom de la fonction. Description de la valeur retourne par la fonction.

<returns></returns>

Un commentaire de documentation doit se conformer la rgle XML/HTML

une commande commence par ( c onmand ) et se termine par ( / c cmrnand ). En fait, on les appelle ordinairement bolises XML, du fait de leur relation
avec XML.

4i7q \

'Qg,

or$lQa^ Vous disposez de bien d'autres balises XML. Pour en savoir plus leur sujet, consultez I'aide en ligne de Visual Studio (plus officiellement connue sous le nom de MSDN pour Visual Studio) en slectionnant ?/lndex, et tapez "XML" dans le champ Rechercher.
L'exemple suivant est une version commente du programme Mixj-ngFunctionsAndl"{ethod s :

// II
{

I'li"xi.ngFunctionsAndMethods
System;

mthodes

mlanger des fonctions de cl"asse et des d'objet peut causer des problnes

using

namespace MixingFunctionsAndMethods

lll
II

ktnnary) sinple description d'un tudiant

I 26

Troisime partie : Programmation et objets

thisStudent.dGPA = dGPA; ll ajoute 1'objet Student au tableau studentsli] = thisStudent;


]
I

ealcule la noyenne des tudiants du tableau


students.Length; i++)
.

double dSun = 0.0;


t

for (int i = 0; i (
dSum

*=

students

Ii]

dGPA;

]
rlnrrhlo uvuure Tarnfh. dAtro unv6 = /(116/o+rr,.lanfa u!ud/ LUUstlLD,!slrLrl fho error age -;
,

/ 1 nrrtnrrt

Console.l{riteline0

Console.Writeline("La moyenne gnrale des "

* students.Length * " tudiants est " + dAvg); l/ attend confirmation de lrutilisateur


;

Console.l,lriteline("Appuyez sur Entre pour terminer, . .t')


Console.Read0;

Le programme demande I'utilisateur le nombre d'tucliants prendre en compte. Il cre ensuite le tableau de rfrences des objets Student,

correctement dimensionn.
Le programme entre maintenant dans une boucle for initiale qui va lui permettre de remplir le tableau. L'utilisateur se voit demander le nombre et la moyenne des UV de chaque tudiant, I'un aprs I'autre. Ces donnes

sont utilises pour crer un objet de Student, qui devient aussitt le nouvel lment du tableau.
Une fois que toutes les rfrences des objets de Student sont leur place, le programme entre dans une deuxime boucle. Dans celleci, la moyenne des LJV de chaque tudiant est lue au moyen de I'instruction students [1] . GPA. Toutes ces moyennes sont arrondies et additionnes, la moyenne gnrale en est calcule, puis finalement affiche pour I'utilisateur.

Voici un exemple de rsultats affichs par ce programme


Entrez
J

le

nombre

d'tudiants

le non de 1'tudiant l: Randy Entrez s moyenne de points d'UV : 3.0 Entrez le nom de 1'tudiant 2: Jeff
Entrez

t24

Troisime partie : Programmation et objets

Oes tableaw( d'objets


Les programmeurs ont souvent besoin de travailler avec des ensembles d'objets dfinis par I'utilisateur. Par exemple, une universit aura besoin de dfinir une structure pour dcrire la population des tudiants qui suivent ses cours. Une classe Str.i,lent simplifie peut se dfinir ainsi public class Student
{
:

^,.L1i^ puDi_r_c ^+-..i-^ ^I\l srrlng sr\ane;

public double
i

dGPA;

//

moyenne

des points d'UV

Cette classe ne contient rien d'autre que le nom de l'tudiant et la moyenne des points de ses "units de valeur" (ou U\). Je mets "units de valeur" entre guillemets parce que cet exemple (et tous ceux qui suivent jusqu' la fin du chapitre) repose sur le systme universitaire amricain, dans lequel la notion de "grade" correspond trs approximativement nos UV (GPA signifie Grade Point Average, autrement dit, dans notre exemple, moyenne des points d'UV). La ligne suivante dclare un tableau de classe S' u; rir
i

num

rfrences des objets de la

Studenl[] students =

nerr/ St.udentfnu:rl;

S/

gs\
/-.)

e,
\

new Student inunl ne dclare pos un tableau d'objets de la classe Student. Cette ligne dclare un tableau de rfrences des objets de la classe Student. Jusqu'ici, chaque lment srudents l1l rfrence I'objet nul1. On pourrait aussi dire qu'aucun des lments du tableau ne pointe vers un objet de Student. Il faut commencer par remplir le tableau, comme ceci :

for (int i = 0; i (
t

students,Length; i++)

students[i] =

new Students0;

t22

Troisime partie : Programmation et objets

Entrez 1a valeur

ne5

3 est 1a noyenne de (1 + 2 + 3 + 4 + 5)
Appuyez

sur Entre pour terniner...

Le programme VariableArravAverap.e commence par demander I'utilisateur le nombre de valeurs dont il veut calculer la moyenne. Le rsultat est stock dans la variable int numElenents. Dans I'exemple cidessus, j'ai entr la valeur 5.
Le programme continue en dfinissant le tableau dArra.v avec le nombre d'lments spcifi. Dans ce cas, il dfinit un tableau cinq lments. Puis le programme effectue une boucle avec le nombre d'itrations spcifi par numElemnts, lisant chaque fois une nouvelle valeur entre par I'utilisateur.

Une fois que I'utilisateur a entr les valeurs, le programme applique le mme algorithme utilis par le programme FixedArrai'A"eL age pour calculer la moyenne des valeurs.

Enfin, la section finale affiche le rsultat du calcul, avec les valeurs qui ont t entres, dans une prsentation agrable lire.

U
^dK :(dw
\/
)

ll n'est pas toujours facile d'obtenir un affichage satisfaisant sur la console. Examinez soigneusement chaque instruction du programme FixedArralrArrerage, les accolades ouvrantes, les signes gale, les signes plus, et toutes les valeurs de la squence, et comparez le tout avec I'affichage.
Le programme Varj-ableArrayAverage ne satisfait pas entirement ma soif de souplesse. Je ne veux pas avoir besoin de lui dire de combien de valeurs je veux faire la moyenne. Je prfre entrer autant de nombres que je veux, et demander au programme au moment que je choisis de calculer la moyenne de ce que j'ai entr. C# offre d'autres types de conteneurs, dont certains que je peux agrandir ou rduire volont. IIs sont dcrits au Chapitre 16.

La proprit

fength

La boucl f or que nous avons utilise pour remplir le tableau dans le programme'/ariableArrayAverage commence de la faon suivante :
/ / dclare un tableau de la tai1le correspondante double[1 darray = new doublelnunElernents]; // renplit le tableau avec les valeurs

for (int i = 0; i (

numElennts; iff")

| 20

Troisime partie : Programmation et objets

Le tableau

longueur uarable

Le tableau utilis dans I'exemple de programme FlxedArrayAverage souffre de deux problmes srieux. Tout d'abord, la taille du tableau est fixe dix lments. Pire encore, la valeur de ces dix lments est directement spcifie dans le programme.

Un programme qui pourrait lire un nombre variable de valeurs, ventuellement dtermines par I'utilisateur au cours de I'excution, serait beaucoup plus souple. Il fonctionnerait non seulement pour les dix valeurs spcifies dans FixedArrayAverage, mais aussi pour n'importe quel autre

ensemble de valeurs.
La dclaration d'un tableau de longueur variable diffre lgrement de celle d'un tableau de longueur fixe et valeurs fixes : double[] dArray :
N new double[N];

reprsente le nombre d'lments allouer.

La nouvelle version de ce programme, VariableArrayAverage, permet I'utilisateur de spcifier le nombre de valeurs entrer. Comme ce programme conserye les valeurs entres, non seulement il calcule la moyenne, mais il

affiche aussi les rsultats dans un format agrable

I I II II II II II
I

',lariableArrayAverage fait 1a noyenne des valeurs d'un tableau dont la taille est dternine par l'utilisateur lors de l'excution. Remplir un tableau avec des valeurs pernet de les rfrencer aussi souvent qu. t'on veut. Dans ce cas, 1e tableau produit un affichage agrable,

namespace VariableArrayAverage
l
t

using Systen; public class Classl


t -.,L1.i^ puorr.c ^+-+
{

sraric int Main(string[]


par

args)

// II

commence

que

1'utilisateur a f intention d'entrer


calculer

lire

1e nonbre de types double

Console.l,lrite("Nombre de valeurs pour 1a moyenne : Console.Readline0 a+riaa .IlmElements Lr frr rlr Ic .I\auuarr \ / , ;

: ");

int nunElements = tonvert.Tolnt32 Console.Writeline0;

(sNumElements)

Chapitre

I : Mthodes de classe I 97

ll/
{

(lsunnary)
Student

uublic class

| | / (orr-^orrr).

I t' tudiant reoit un nom I (lswnary) oublic strins sFirstName; /// (sumnary) I I I non de famille de 1'tudiant
II II \/UrUrtrdrj/ IJ IllI 1l^.,**^-.,\

oublic strins

slastName;

/l
II

lnitStudent

- initialise 1'objet student

I I | (<,rmmrrrr\

I initialise 1'objet student avant qu'i1 puisse tre utilis lll (lsunnary) I I I (paran name:"sFirstName")l'tudiant reoit un nom(/param) I I I ftaran name="sLastName")nom de fami11e de 1'tudiant(/param) public void InitStudent(strir:g sFirstName, string sLastName)
{

this. sFirstNane : sFirstNane; this. slastName = slastNane;

l
/

I M

/ n,.+-,,+n LPU Llclllls!


(orr^ rr.r)

affi.che f introduction

lll

lll
II
{

a.tiche une bannire avant d'afficher les nons des tudiants

(lswnary)
n 1ri 11 a)lltnlrtRennor s usrrr.!
() \ /

nrrhf i n ctati

Console,l,lriteLine("Regardez comme je suis malin :") ; // Console.IdriteLine(? quel objet student utilisons-nous ?);
] /

lll lll
i

0utnrrtBannerAndNane

Gunnary)

lll <l*r*m"rrr)
i

affiehe une bannire suivie par e voi


11

le

non de

1'objet student courant

nrrh l

0rrtnutBannerAndNane

//
ll //

c'est la classe Student qui est


'l

p<t nnss rrtnrrtRannpr f l '


/ / I 'nhict

suppose mais pas a e mthode st:ti nttp

ce nrest pas 1'objet this qui est pass nais 1'objet student courant qui est pass explicitement OutputName(this, 5);

t98

Troisime partie : Programmation et obiets

// Outputname - affiche le nom de 1'tudiant /// (summary) I I I atf,iche sur la console le nom de 1'tudiant I I | (ls,innary) l l l haran name="student")Le non de 1'tudiant que

ilt

vous voulez afficher(/Param)

Ghapitre I : Mthodes de classe

199

/l affiche la bannire et le
Student. OutputBanner

nom

string s =

Student.0utputNanre(student,

5);

Console.I^IriteLine0;

// affiche nouveau
I

1a bannire student . OutputBannerAndName ( ) ;

et

1e

nom

I attend eonfirmation de 1'utilisateur


,

Console

Console,Read

Writeline ( "Appuyez sur Entre pour terminer 0;

il\,

Les commentaires expliquent la finalit de la fonction, quoi sert chaque argument, le type de donne retourne, et la relation avec une autre fonction. En pratique, les tapes suivantes dcrivent ce qui est affich lorsque je saisis dans Ilain O la fonction -S:udent .0utputNanLe O :

1. Visual Studio

me propose une liste de fonctions. Une fois que j'ai mis en surbrillance celle que je veux, Outputlr'are ( ), Visual Studio en affiche une courte description, extraite de 'surnnai,: comme le montre la Figure 8.6.
?

'

/s,rmnrar_y'),

nnref

EE80n

ffiirha,1e
;,:!?

Piet

i:'; ;.,::,.. t
tlassl,c;*
/lt"t
|

1.

"..

enrer

&*&

Qbrguer n&. /6

Sutili Fettre

Eeh

it.l
|

:tt't:t

'tr -.:srl

Lebu'l

nlfrr :lr.nrr llnpffri'i

| tt:lnstrrnl[]arqsi

:l

I I

| l:

-J :J

-'.tri,- ' rl r,tts x'-r ', .a il'--f .Hixinqrun(tionrAndMethods + jl ifEr,rt (-| tinrL,t'lni ,:r rjf Clr'l.cs

Figure 8.6

Avec un

document
EN

pr0gramme XML,

Visual Studio est capable


de bien mieux

C
E

E,rurls uLF|JLEnrnr

FeiErEn,:Eluls

dcrire la fonction
et ses a rguments.

IJ

,t

200

Troisime partie : Programmation et objets

2. 3.

Une fois que j'ai saisi ou slectionn Ie nom cle la fonction, Vi.sual Studio affiche une description clu prernier paramtre, extraite dtr champ (par an)( lpa,,;rm), ainsi clue sorr type.

Visual Studio rpte ce processus pour le deuxinre argunrent, n lriden t .

Bien c1r-r'ils sclient un peu fastidieux saisir, les comrnentaires cle clocumentation rendent les mth<ldes beaucoup plus faciles utiliser.

Gnrer une documentation XILL


Vous pouvez facilement clemancler Visual Stuclio cl'extraire sous forme de fichier XML tous les comnrentaires de clocumentation que vr)rrs avez
entrs. Cette section est trs technique. Si vous ne savez pas ce qu'est un fichier XML, tout cela ne vous dira pas grancl-chose. Si vous savez comment est fait un fichier XML, vous allez trouver cette fonction trs utile. Slectionnez Affichage/Explorateur de solutions pour afficher I'Explorateur de solutions. Dans I'Explorateur de solutions, cliquez clu bouton droit sur le norl du prograrnme, et slectionnez Proprits. Dans le volet cle gauche de la fentre Pages de proprits, cliquez sur le dossier Prcprits de configuration 1:our I'ouvrir, et slectionnez Gnrer clans les pages clui apparaissent au-dessous de ce dossier. Dans la section Sortie clu volet de droite de la fentre Pages de proprits, slectionnez la proprit nontme Ficli-ier de clocumt:ntatiorr XML. Dans la cellule qui se trouve clroite de ce norn, entrez un nom de fichier. Comme je n'avais pas de meilleure ide. j'ai mis xn-Loul-pu1 . xml. Clicluez sur OK pour applicluer cette modification et fermer la fentre Pages de proprits.
:$uv \'1

=Qg,

^"tFJk

tZX It9r, L-,{t


,-t -.

I
-

Vous pouvez aussi accder aux proprits clu projet en slectionnant Projet/Proprits.
Slectionnez maintenant Gnrer/Rgnrer tout pour tre bien t gnr correctement.

str

que tout a

Regarclez dans le mme dossier que le fichier source ul asg I . cs (le fichier du projet est dans le mme clossier). Le nouveau fichier xrnl,rrrtf,r-Lt. rrni dcrit toutes les fonctions clocurlentes par les balises XML.

Chapitre

Joueravec des chanes en C#


Dans ce chapitre :

Tordre une chalne et tirer dessus - mais vous ne pouvez pas la pousser. Analyser une chalne lue par le programme. Mettre en forme manuellement une chalne de sortie.
Mettre en forme une chalne de sortie en utilisant la mthode

;r ring

. Fo

ra:

,,

I{ou, de nombreuses applications, vous pouvez traiter un lment de I type string comme n'importe quel type cle variabte intgr, tel que int ou char. Certaines des oprations ordinairement rserves pour ces
types intrinsques sont utilisables pour les chalnes
:

int i - i;
string s = "abc"

/i II

dclare et ini-tialise un int dclare et initialise un string

Pour d'autres aspects, un lment dfinie par I'utilisateur : string s1 * nev String0; string g2 = "abcd";

string

est trait comme une classe

int nlengthOfString = s2.Length;


Alors, qu'est-ce que c'est : un type de variable ou une classe ? En fait, St ring est une classe pour laquelle C# offre un traitement spcial. Par exemple, le mot-cl string est synonyme du nom de classe Str-irq
.

String s1 = "abcd"; // assigne une chane littrale un objet String string s2 = sl : I I assigne un objet String une variable string

202

Troisime partie : Programmation et objets

Dans cet exemple, s-rl est dclar en tant qu'objet de la classe )^rring (avec un 5 tnajr-tscule), alors que s2 est clclar en tant que variable cle type s trr rrg (avec un.s minuscule). Mais ces deux assignatiorls montrent que str j rg et :i tr ing sont de mme type (autrernent dit, compatibles).

7^H\ =(,l\y / \/

.t9!I{0?r

En fait, cette proprit est galernent vraie pour les autres types de variable, mais clans Lrne mesure plus limite. Mme le type int- possde sa classe corresponclante, Ir,t,l2, douf-,ie correspond la classe t-)oub1e, et ainsi cle suite. La cliffrence est que ritrirrg et String, sont rellement une seule et mme chose.

Effectuer des oprations courantes sur une chane


Les programmeurs C# effectuent plus d'oprations sur les chanes que la chirurgie esthtique sur les hollywoodiens qui ne demandent que a. Il n'y a gr-rre cle progrummes qui n'utilisent pas I'opratenr d'acldition sur des ctrnlne.s
-+*.:*LL D :

t'Randy" ^IT^-^ rrr r\d.r*c

Console.1,lriteline(*'Son nom est 'r

sNarne);

rinu qui fournit cet oprateurspcial, mais elle offre galement cl'trutre.s rnthodes, plus clirectes, pour manipuler les chalnes.
C'est laclasse 5jt

L'unon est ndiusible, ainsi sont les chanes


De ce que vous n'avez pas forcment appris l'cole, il y a au moins une chose c1u'il vous faut apprenclre : une fois qu'il a t cr, vous ne pouvez pas moclifier un objet st riiig. Mme si je parle de modifier une chane, C#

ne dispose d'aucune opration qui modifie I'objet string lui-mme. Il existe toutes sortes cl'oprations pour modifier la chalne avec Iaquelle vous travaillez, mais c'est toujours avec un nouvel objet que la chane modifie est retourne. Par exemple, I'opration "ll s'appelle" + "Hectc)r" ne modifie aucune de ces deux chalnes, mais en produit une troisime : "ll s'appelle Hector". L'une des consquences de ce principe est que vous n'avez pas vous inquiter que quelqu'un moclifie une chane "derrire votre dos".

Chapitre 9 : Jouer avec des chanes en C#

203

Voyez cet exernple simple


,. ^ ^ II I| -. MotifrrS+rinn

- les m+hodes fottrnies

nar la claSSe

I II II ll
I
'.^.i-^ uarr C.'^+^!jLslu,
'

String ne modifient pas l'objet


lui-mme (s.ToUpper| ne nodifie pas s, mais retourne une nouvelle chaine

qui a t convertj.e)

namespace Example

i
LfdS I t urdnl

public static void Main(stringIJ args)


{

objet student si = new Student0; s1. sName = "Jenny"; I I cre maintenant un nouvel objet
ct.e un

ll

Student

avec

le

nme nom

Student s2 = new Student 0


92. sName

= sl.

sName;

l "changer" le nom de 1'objet sl ne change pas I I I'obiet 1ui-mme, parce que ToUpper0 retourne l/ une nouvelle chane sans modifier 1'original
l
s2.sNarne

s1. sName.ToUpperO s1. sNane, s2. sName)


;

Console.l^Iriteline("sl - [0], s2 - {11",

I attend confirmation de 1'utilisateur


;

Console.l^IriteLine("Appuyez sur Entre pour terminer., .")


Console.ReadO;
'I

ll Student class
I (

nous avons besoi-n d'une classe eontenant une chane

Student
sName;

public String
]

l
]

Les objets Str-Lcli-rt :r, et s2 sont dfinis de telle manire que leur membre donne sfJarae pointe vers la mme chalne. L'appel la mthode ToUpper ( ) convertit la chalne s l. sr'lane pour la mettre entirernent en majuscules. Normalement, cela devrait poser un problme, car s I et s 2 pointent tous deux vers le meme objet, mais TcLIi per ( ) ne modifie pas sName : elle cre une nouvelle chalne er] maiuscules.

204

Troisime partie:Programmation et objers

La sortie de ce l)rogramme est fort simple

sl'Jenny
Appuyez

s2 -

JENNY

sur Entre pour terminer...

=(f

7^[ \

19!llQp" L'invariabilit des chalnes est galentent importante pour les constantes de
type
s Lr

V / \-l

i ng. Une cirane conlrne "c]eci est une chalne" est une forme cle constante cle type ::t r iirg. tout cornme I est une constante cle type j nt. De la tnlne matrire clrre je rre jette pas mes chemises aprs usage pour rcluire le volume cle ma garde-robe, un cornpilateur peut choisir de combiner tou.s les accs la mrne constatrte "ceci est une c:ha1ne". Le principe cle rutilisation d'une constartte cle type chalne permet cle rcluire la taille cl'un prograrnrle, mais il serait irnpossible si un objet de type:;rring pouvait tre moclifi.

Egalit pour toutes les chanes : la nthode


Conipar e o
De nombreuses clprations traitent une chalne comme un ol-rjet unique. C i,,np a r e ( ) compare deux chalnes comme si elles taient cles nombres :

Par exerntr-lle, la rnthode

tl tl

Si la chalrte de gauche est suprieure la chalne de clroite, iJriilpar e ()

retourne
.Si

1.

la chane de gauche est infrieure la ctra-re de droite, L)oni;:rr c ()


-1.

retourne

t/

Si les cleux chanes sclnt gales, Compare ( )

retourne

0.

Rduit aux conlmentaires clui le dcrivent, I'algorithme fonctionne cle la faon suivante :
conpare(string
{

si, string

s2)

// effectue une boucle sur chaque caractre des chanes, jusqu, ll ce qu'un caractre d'une chane soit pLus grand que I I le caractre correspondanr de 1'autre chane
foreach caractre de 1a chane 1a plus courte if (1e caractre de s1 ) au caractre de s2, vus
comme

des nombres)

return I

if (le caractre de s2 ( au caractre de s1)


return - l

ll lous

1es caractres correspondent, mais

si

1a chane

s1

Chapitre 9 : Jouer avec des chanes en G#

205

I I est plus longue, alors e11e est plus grande si s1 contient encore des caractres

I si s2 est plus longue, alors e11e est plus grande si s2 contient encore des caractres
I

return

return
I

-1

I si tous les caractres correspondnt et si les deux chaines l l ont 1a mnre longueur, alors e11es sont "gales"
return
]
0

Ainsi, "abcd" est plus grand que "abbd", et "abcde" est plus grand que "abcd". Vous n'aurez pas besoin tous les jours de savoir si une chane est plus grande qu'une autre, mais il vous arrivera d'avoir besoin de savoir si deux chalnes sont gales.

Conpare O retourne 0 lorsque les deux chanes sont gales. Le programme de test suivant utilise cette caractristique de Compare O pour effectuer une certaine opration quand il rencontre une ou des chalne(s) particulire(s).

Eui ldAsentence demande I'utilisateur d'entrer des li.enes de texte. Chaque ligne est concatne avec la prcdente pour former une phrase, jusqu' ce que I'utilisateur entre les mots EXIT, exit, QILII ou quit : La- L-|ffi r I r,in

Srd$STa itl/ nuitalsentence - 1e progranrne


ffil| t ll ll II II
,
I
L

suivant construit

des phrases en concatnant 1es saisies de l'utilisateur, jusqu' ce que celui-ci entre 1'un des caractres de fin ce progranne donne un exenple de la ncessit de vrifier si deux ehanes sont ga1es

using

System;

namespac

e BuildASentence
C1ass1

publi.c class
{

public static void Main(stringiJ args)


{

Console.l{riteline("Chaque ligne que vous entrez sera" * "ajoute une phrase, jusqu' ce que vous" * "entriez EXIT ou QUIT"); // deniande une saisie I'utilisateur et continue concatner // jusqu' ce que 1'utilisateur entre exit ou quit I | (comnence avec une phrase vide)

strins sSentence = t"t;

206

Troisime partie : Programmation et objets

for(;;)
t
I I Lit 1a saisie suivante Console.Writeline("Entrez une chane") ; string sLine = Console.ReadLine0; ll sort de 1a boucle si c'est une chane de if (IsTerminateString (sLine) )
{

fin

break;
]

sSentence

// sinon, ajoute 1a phrase 1a chane saisie : String.Concat(sSentence, sline) ; I I dit 1'utilisateur o i-l en esr
;

Console.WriteLine("\nVous avez entr : {01", ssentence)


l

Console.l,lriteLine("\nPhrase complte :\n{01", sSentence)


I I attend confirnation de lrutilisateur Console.l,lriteLine("Appuyez sur Entre pour terminer.

, . ")

Console.Read0;

// IsTerminatestring - retourne true si 1a chane source ll est gale l'une des chanes de fin public static bool IsTerninateString(string source)
{

string IJ sTerms = {"EXII",


ll^,.i+ll f
L

"QIJTT"
r

qUit,, l

It

conpare 1a chane entre chacune des chanes de fin licites

foreach(string sTerm
{

in

sTerms)

if
{ )

/1 retourn true si
return true;

1es deux chanes sont ga1es (String.Compare(source, sTerm) == 0)

return false;

Aprs avoir demand I'utilisateur de saisir la prernire ligne, le programme cr une chalne initiale vide nomme sSentence, puis il entre dans une boucle "infinie".

Chapitre

I : Jouer avec des chanes en G#

207

Les structures while (true) et f or (; ;) produisent une boucle sans fin, c'est--dire aussi longtemps qu'un break ou retur:n interne n'en fait pas sortir. Les deux boucles sont quivalentes, et dans la pratique vous rencontrerez les deux.

BuildAsentence demande I'utilisateur d'entrer une ligne de texte. qu'il lit avec la mthode Readline (). Puis il vrifie si la chalne entre est otr non le signal convenu pour la fin, en utilisant la chalne cre localement, TsTerminateString O. Cette fonction retourne true si sline est I'une des chalnes convenues pour la fin, et f alse dans le cas contraire.

b'
4f\ =)

Par convention, le nom d'une fonction qui teste une proprit et retourne true ou f aise doit commencer par Is. Dans notre exemple, le nom de la fonction I sTerminateString ( ) signifie la question : "sLine est-elle une chalne de fin ?" Bien str, ce n'est l qu'une convention humaine. Elle ne

signifie rien pour C#.

sline n'est pas I'une des chalnes de fin, elle est concatne avec la partie de la phrase dj saisie, au moyen de la fonction St ring . Cciic a: Le programme affiche immdiatement le rsultat, afin que I'utilisateur sache o il en est.
Si La mthode

()

IsTernrnateString O clfinit un tableau de chalnes sTe:ns, dont chaque membre est I'une des chalnes de fin. Si la chalne teste est gale I'une des chalnes de ce tableau, cette mthode retourne true, ce qui conduit le programme s'arrter plus vite qu'un programmeur oblig crire en COBOL.
Le programme doit prendre en compte "EXIT" et "exit", car Conpare o considre par dfaut ces deux chalnes comme diffrentes. ( la manire dont le programme est crit, il ne connalt que deux manires d'crire exrr. Une chalne telle que "Exit" ou "eXit" ne serait pas reconnue comme chalne de fin.) La fonction IsTerminateString O effectue une boucle pour chacune des chalnes du tableau des chalnes de fin. Si Compare O retourne que la chalne teste est gale I'une des chalnes de fin du tableau, la fonction IsTerminateString O retourn true. Si aucune galit n'a t trouve la fin de la boucle, la fonction IsTerminateStrine () retourne false.
:t\vl

lforl
l-t

ff-^l

jL'itrationSuruntableauestuntrsbonmoyendetestersiunevariable
correspond une valeur parmi plusieurs.

.a

208

Troisime partie : Programmation et objets

Voici un exemple de rsultat du programme Build.ASentence


taL^^..^ 1-j^-^ .:, ..-^ ^..^ ^.i^..+i^ rrrc vudguc voUS entfez Sera ajoUte Une Ad ohrase iusou' np attp vntts pntriez EXIT ou QUIT Entrez une chane Progranner avec C#
vu qw4r

Vous avez

entr :

Progranrner avec

C/l

Entrez une chane

, crest

amusant

Vous avez

entr :
uroins)

Programmer avec

C1,

c'est

amusant

Entrez une chane

(plus ou
Vous avez
EXIT

entr ;Programner avec Cll, crest amusant (p1us ou

moins)

Entrez une chane

Phrrso
[yHeJ

nnmnT

tp

Programner avec
Annrrrroz v! crrr

Cil, c'est amusant (p1us ou noins)


nnnr forminar

F'nfro

J'ai mis en gras ce qui a t saisi par I'utilisateur.

Uoulez-uous comparer en maiuscules 0u en tll mrnuscules !


La mthode Ccnpare ( I utilise par isTerrnina-r-eString O considre "EXIT"

et "exit" comme cles chalnes diffrentes. Mais il existe une autre version surcharge de cette fonction qui comporte un troisime argument. Celui-ci indique si la comparaison doit ou non faire la diffrence entre les majuscules et les minuscules. L'argument Lr ue indique d'ignorer Ia diffrence.

Laversion suivante de IsTermirrateStringO retourne trlle si lachane qui lui est passe correspond une chalne de fin, qu'elle soit en majuscules, en minuscules ou dans n'importe quelle combinaison des deux.

//
t

ll est

Iserminatestring - retourne true si 1a chane source string ga1e l'une des chaines de fin

is

equal

nrrhl'in

cfaiin hool IsTerninateString(string


..6\ULr

sOurCe)

//donne true si on lui passe exit ou /l des najuscules et des minuscules

quit,

sans

tenir

conpte

Chapitre

I : Jouer avec des chanes en C#


l
l

209

return (String.Comnare("exit", source, true) == 0) (String.Compare("quit", source, true) == 0);


]

Cette versicln d - ::1r'i,,'ir l i.,,,, e.1r l'- .,;, i est plus sirnple que la prcclente qui utilisait une boucle. Elle n'a pas be.soin de se proccuper cles majuscules et des minuscules. et elle peut utiliser une seule instruction conditionnelle,

car elle n'a nraintenant que deux possibiliteis prenclre en compte. Cette version de I ,:'i'r: i.,.ir;: i 1-', r. :- i:

!r

n'a rnme pas besoin cl'une instruc-

tion i f. L'expression boolenne retourne clirecternent la valeur calcule.

Et s je tleux utlser

a- '-i bW-L L

-l-

ch

.)

Pour tester si une chalne est gale une valeur particulire. vous pouvez aussi utiliser la structure :;-",,;ri-,ri , r.

51}qS. ( il ) \U /

En gnral, on se sert de la structure s-,n 1'- r;11 r r pour comparer une variable utilise comme compteur un ensemble de valeurs possibles, mais cette structure fonctionne atrssi sur cles chalnes. La version suivante cle ,

sT-ollilaie:,-. r irr-

r..'

utilise la structure s..,it.:h i )

// II
{

IsTerminateString - retourne true si 1a chane source est ga1e l'une des chanes de fin
source)

public static bool TsTerrninateString(string


switch ( source
{
)

case I'EXfT":
c

as

e ttexit

"

^a^ Lqc

il^ITT11||. qurr

case t'quit":

return true;
]

return false:
]

l Cette approche fonctionne parce que vous ne comparezici qu'un nombre limit de chalnes. Une boucle f or O offre un moyen beaucoup plus souple de rechercher des valeurs de type chalne. La version de Compare () qui ignore la distinction entre majuscules et minuscules donne au programme une plus grande souplesse.

2l 0

Troisime partie : Programmation et objets

Lre les caractres sarsis


Un programme peut lire ce qui est saisi au clavier caractre par caractre, mais cette approche peut devenir problmatique, car il faut se soucier des fins de ligne et autres. Une approche plus pratique consiste lire la chalne pour examiner ensuite les caractres qu'elle contient.

L'analyse des caractres que contient une chalne est aussi un sujet que je n'aime pas voquer, cle crainte que les programmeurs n'abusent de cette technique. Il arrive que les programmeurs aillent un peu trop vite sauter sur une chalne avant qu'elle soit entirement saisie pour en extraire ce qu'ils y trouvent. C'est particulirement vrai des programmeurs f ++, cr jusqu' I'introduction d'une classe de chalnes, c'tait la seule manire dont ils pouvaient manipuler les chanes.

structttre f r.-,r'each ou I'oprateur inder [], un programme peut lire une chalne conlme si c'tait un tableau de caractres.
En utilisant la

5lttrS, Bien str, une chalne n'est pas simplement un tableau cle caractres. Si on i( il ) n" peut lire une chalne qu'un caractre la fois, on ne peut pas l'crire de \ ln ./ la mme manire.
L'exemple sirnple clu programme St r in de cette technique
,

gToCh

rAc

ces

s montre l'utilisation

lt

^ StrlngloUnarAccess

accde aux caractres d'une chalne coffne si 1a chane tait un tableau

using Systen;
namespace StringToCharAccess
{
I r . I ^l public class Classl t
L

1 .l ^..L ^ srar:-c ^+^+-i ^ voj.d Main(string[] args) puDl:.c

t
I

I lit

une chane saisie au clavier

Console.l,lriteline("Entrez au hasard une chane de caraetres" * "(attention I au hasard)");

strins sRandom = Console.Readline0 ; // conmence par afficher sous forne de chane


-"--"b

Console.WriteLine("Votre saisie conme chane


Console.I,rIriteline
ConsoLe.
T

:" f

sRandon);

// affiche maintenant sous forme de suite de caractres l,Irite ( "Votre saisie affiche en utilisant foreach foreach(char c in sRandom)

:"

Ghapitre 9 : Jouer avec des chanes en C#

2n

Console.Write(c);
]

Console.I.lritelineO; ll ternine la ligne I I put a blank line divider


Console.l,trriteline 0 ; I I af.f.iche naintenant sous forne de

suite de caractres

for(int i = 0; i (
r t

Console.l'irite(rrVotre saisie affiche en


sRandom.Length; i++)
;

utilisant for :t');

Console. l^Irite (sRandomIi] )


]

Console.I,Iriteline0; i / ternine 1a ligne // attend confirnation de 1'utilisateur


Console.!trriteLine("Appuyez
uonsoIe. Kead U
;

sur Entre pour terminer. ..")

trois manires diffrentes une chalne saisie au hasard par I'utilisateur. Il commence par I'afficher en utilisant la mthode habituelle l,irir-el.ine (string), puis il I'affiche en utilisant la structure f oreach pour en extraire chaque caractre I'un aprs I'autre, et enfin. il se sert de I'index d'un tableau avec [] pour faire la mme chose.
Ce programme affiche de Ce qui donne le rsultat suivant
:

Entrez au hasard une chane de caractres (attention Stephen Davis est un beau garon

: au hasard)

Votre saisie

conme

chaine : Stephen Davis est un beau garon

Votre saisie affiche en utilisant foreach : Stephen Davj.s est un beau garon Votre saisie affiche en utilisant for : Stephen Davis est un beau garon sur Enrre pour terminer...

Appuyez

On ne se lasse pas d'une vrit. Dans certains cas, vous ne voudrez pas avoir un caractre non imorimable une extrmit ou I'autre de la chalne. Un coractre non imprimoble est un caractre qui n'est pas normalement affich l'cran : un espace, une nouvelle ligne, une tabulation, et quelques autres.

2I2

Troisime partie : Programmation et objets

Pour purer de ces caractres les extrmits de Ia chalne, vous pouvez utiliser la mthode Trim O :

ll

se dbarrasse des espaces chaque extrmit d'une chane

sRandom

= sRandon.Trin0

Bien que ce soit une fonction membre, Strrng. Tri n O retourne une nouvelle chalne. La version prcdente de la chalne avec les caractres imprimables en surnombre est perdue et ne peut plus tre utilise.

Analqser une entre numr(ue


La fonction R eadllne | ) utilise pour lire sur la console retourne un type string. Un programme qui attend une entre numrique doit convertir cette chalne. C# offre dans la classe Con-... r t I'outil de conversion dont vous avez besoin pour cela. Cette classe comporte une mthode de conversion du type sr-ring tous les autres types cle variable. Ainsi, le fragment de code suivant lit un nombre saisi au clavier, et le stocke dans une variable de type int :

string s = Console.Readline0;

int n = Convert. Int32

(s)

Les autres mthodes de conversion portent des noms plus vidents ToDouble ( ). ToFloat ( ). et ToBoolean ( ).
To I

nt 3 2 ( ) se rfre un entier sign de 32 bits (32 bits est la longueur d'un int normal). ToInt64O correspond un long (qui fait 64 bits). Lorsque Corvert O rencontre un type de caractre inattendu, il peut produire un rsultat inattendu. Vous devez donc tre str du type de donne que vous tes en train de manier.
La fonction suivante retourne true si la chane qui lui est passe n'est constitue que de chiffres. Vous pouvez appeler cette fonction avant de convertir la chalne en un type entier. Si une chalne de caractre n'est constitue que de chiffres, il y a des chances que ce soit un nombre licite.

=( ^' \r'r

Atry( \?_/

\/

Pour une variable en virgule flottante, il serait ncessaire de prvoir la virgule, ainsi que le signe moins pour les nombres ngatifs. Ne vous , laissez Pas surPrendre.

Ghapitre 9:Jouer avec des chanes en C#

2|3

// IsAllDigits - retourne true si tous les caractres sont des chiffres II


public static bool IsAllDigits(string
t
sRar^r)

de

la

chane

// r.ommpne nar se dbarragser des caractres inutiles /l chaque extrnit ; s'iL ne reste rien, // c'est que la chane nrest pas un nonbre string s = sRaw.Trim0; i / supprime 1es espaces aux extrmits if (s.Length == 0)
t

return false;
l

//
i

for(int
|

effectue une boucle sur 1a chaine index = 0; index ( s.Length; index**)


chane

ll si ce nrest pas un chiffre, c'est que la | n'est sans doute pas un nonbre if (Char. IsDigit (s Ij.ndex] ) == false)
{

return false;
l

l
I

tous 1es caractres sont des chiffres, 1a chane doit tre un

nombre

return true:
l

non que rien, c'est imprimable aux deux extrmits de la chalne. S'il ne reste la chalne est vide et ne peut pas etre un entier. Puis, la fonction passe en boucle sur chaque caractre de la chane. Si I'un de ces caractres n'est pas un chiffre, la fonction retourne f a1se, indiquant que la chalne n'est sans doute pas un nombre. Si cette fonction retourne true, il y a les plus grandes chances que la chane puisse tre convertie en un type entier.
La fonction

IsAllDigirs O commence par supprimer tout caractre

L'chantillon de code suivant lit un nombre saisi au clavier, et I'affiche sur la console (pour simplifier I'exemple, j'ai omis I'utilisation de la fonction

lsAllIJrsrrs(
.

t]

).

o$'ARGFf, l, t- T__^1 $'


La.

!!t rsAllDigits t-t1 1ni-ir Systemt

1!^-- dmonstration de 1a mthode IsAllDigits

using
t

namespace Exanple

class Classl
{

public static
{

int Main(string[]

args)

2I4

Troisime partie : Programmation et objets

I I lit une chane saisie au clavier Console.Writeline("Entrez un nonbre entier") ; string s - Console.Readline0; // conmence par vrifier si 1a chane entre peut tre un nombre

if
t

(!

IsAllnigits

(s)

Console,l^iriteline("Ce n'est pas un nombre


]
e1s e
{

!");

I I convertit la chaine en un nonbre entier int n = Int32.Parse(s); // affiche maintenant le double du nonbre Console.Writeline("2 * {0} = {1}", n, 2 * n);
]

// attend confirnation de 1'util"isateur


Console.Il]riteline("Appuyez sur Entre pour terniner...")
uonsol.e. Kead U
; ;

return

0;

Le programme lit sur la console une ligne saisie au clavier. Si I sAl1Di gi r s ( ) retourne fa1se, le programme fait des remontrances I'utilisateur. Dans le cas contraire, le programme convertit la chalne en nombre par I'appel Con'rert .'fc r-nt3 2 ( ) . Enfin, Ie programme affiche le nombre ainsi que le double de celui-ci (pour bien montrer qu'il a effectivement converti la chne, comme il le dit).

Voici un exemple de fonctionnement de ce programme


Entrez un nombre entier
iA3

Ce n'est pas un nombre ! Appuyez sur Entre pour terminer. .

meilleure approche pourrait tre de laisser Convert essayer de 1t9!t\JQa. Une convertir n'importe quoi et de traiter les exceptions qui pourraient en 7^l \ =[,\7 / sortir. Toutefois, il y a les plus grandes chances qu'elle ne produise

\/

aucune exception, mais retourne simplement des rsultats incorrects (par exemple, I quand on lui propose "lA3").

Ghapitre 9 : Jouer avec des chanes en C#

2l 5

Traiter une sute de chffres


Bien souvent, un programme reoit une suite de chiffres taps au clavier sur une seule ligne. En utilisant la mthode S,: ri r,g. Srl it ( ) , vous pouvez facile. ment diviser cette chalne en un certain nombre de soulschalnes, une pour chacun des nornbres clont I'ensemble est constitu, et les traiter sparment. La fonction S rr I j t i .r clivise une chalne en un tableau cle chalnes plus petites en utilisant pour cela un dlirniteur. Par exenrple, sivous demanclez Solit O de diviser une chalne en utilisant la virqule comrne dlimiteur. " 1,2,3" produit trois chalnes : " l" , "2" et "3".

Le programnre suivant utilise Sp,r+ ,..) pour saisir une suite de nombres

additionner

#"Ti3 ,\ ffi..1
,

lI tt II II

Pr(sn1rnnpWithSnlir - 'l it ripg Srie de nOmbres spars par des virgules, les transforme en
nombres

entiers. et en affiche 1a somne

nanespac t

e ParseSequencelllithSplit
System;
C1ass1

using class
{

public static
{

int

Main(string[1 args)

//

derrande
.

Console

1'utilisateur de saisir une srie de nornbres illriteLine ( 'rEntrez une srie de nombres spars par des virgules" ):
une

I lit

ligne de texte

string input = Console.Readline0;


Gonsole.Writeline0;

// convertit la ligne en segnents ll en utilisant 1a virgule ou 1'espace conme sparateur charll cDividers = {',', ' 'J; string [] segnrents = input, Split (cDividers) ; /l convertit chaque segment en nonbre int nSonme = 0:
foreach(string s in
{

segments)

I if
I t

(saute tout segment vide) (r.T,eneth ) 0)

ll if
{

sawe les chanes qui ne sont pas des nombres

(IsAllDieits

s)

2I6

Troisime partie : Programmation et objets

l/ converti.t 1a chane en un entier 32 bits int num : Int32.Parse(s); Console.I,lr j-teline ("Nouveau nonbre = {0J 'r , nun) ; /l ajoute ce nombre 1a sonme
nSum

*=

num;

// affiche 1a somne
Console.WriteLine("Sonne
I

= {0}", nSun) ; I attend confirmation de 1'utilisateur ..")


;

Console.WriteLine("Appuyez sur Entre pour terminer.


Console.
Read

return
l
i

0;

/ fsnllni.gits - retourne true si tous 1es I sont des chiffres public static bool IsAllDigits(string sRaw)
I

caractres

// comnence Dar se dbarrasser des caractres inutiles // chaque extrmit ; s'i1 ne reste rien // c'est que 1a chane n'est pas un nombre string s = sRaw.Trim0; if (r.Length == 0)
{

return false;
l

// effectue une boucle sur 1a chane for(int index = 0; index ( s.Length; index**)
{

I si ce nrest pas un chiffre, c'est I n'est sans doute pas un nombre if (Char. IsDigit (s Iindex] ) == false)
I
I
{

que 1a chane

return false;
l
] I l

I tous 1es caractres sont des chiffres, c'est


l-rt1.

sans doute un nombre

rt1trr)

Le programme Par seSequence'rn/ithSplit commence par lire une chalne saisie au clavier. Il passe la mthode Split O le tableau cDirriders afin d'indiquer que la virgule et I'espace sont les caractres utiliss pour sparer deux lrornbres dans la chalne.

Chapitre 9 : Jouer avec des chanes en C#

2t7

Le programme effectue une itration sur chacun des "sous-tableaux" crs par Spli: O en utilisant la structure foreach. Il ignore tous les sous-tableaux de longueur nulle (qui rsulteraient de la prsence de deux dlimiteurs conscutifs). Le programme vrifie ensuite que la chalne contient effectivement un nombre en utilisant la mthode IsAllDlgits o Chaque nombre valide est converti en entier puis ajout la variable nSun. Les nombres qui ne sont pas valides sont ignors fi'ai choisi de ne pas mettre de message d'erreur).

Voici un exemple d'excution de ce programme

Entrez une srie de nombres spars par des virgules


11 !'LJ
Q'

7J,A

Nouveau Nouveau Nouveau Nouveau


Sonme

nombre

1 2

nonbre = nonbre = nonbre =


10

Appuyez

sur Entre pour terniner.

Le programme parcourt cette liste, acceptant comme sparateurs Ia virgule, I'espace ou les deux. Il ignore le a et affiche le rsultat 10.
^r\C. :\\v-a

l[Orf Y

HDansunprogrammedestinunusagevritable,vousnevoudreZSanS

doute pas ignorer une donne incorrecte sans rien signaler I'utilisateur.

Contrler manuellement la sortie


La maltrise de la sortie d'un programme est un aspect trs important de la manipulation des chalnes. Soyons clair : la sortie d'un programme est ce qu'en voit I'utilisateur. Quelle que soit l'lgance de sa logique interne, I'utilisateur ne sera pas bien impressionn si la sortie est plutt piteuse.

String offre des rnoyens de mettre en forme directement pour la sortie des donnes de type chane. Les sections suivantes dcrivent les mtho des Trim ( ), Pad ( ), padRisht ( ), Padleft ( ), Subst:ing ( ), et concar I ) .
La classe

Utiliser les mthodes Trim ( ) et

PaC ( )

Vous pouvez utiliser la mthode Trim O pour supprimer les caractres indsirables aux deux extrmits d'une chalne. Vous allez typiquement

2 |8

Troisime partie : Programmation et obiets

vous en servir pour supprimer des espaces afin d'aligner correctement les chalnes envoves la sortie.
Les fonctions Pad sont un autre moyen d'usage courant pour mettre en forme la sortie. Celles-ci ajoutent des caractres I'une ou I'autre extrmit d'une chalne pour lui donner une longueur dtermine. Par exemple, vous pourrez vouloir ajouter des espaces I'extrmit clroite ou gauche d'une chane pour I'aligner droite ou gauche, ou alors ajouter des "*" ou autres caractres pour signifier quelque chose de particulier. Le programme A1ignoutput suivant utilise ces deux fonctions pour extraire et aligner une srie de noms :

,,1{J\,-

'odsg9
| l* [-T''?

//
{

Ali8n0urput

justifie

gauche et aligne un ensenble

de chanes pour enbellir nnespace Align0utput


,'-.i-^ urrr5 Q.'a+a* 9JLclu, '

la sortie

du programme

class C1ass1
{

public static int Main(string[] args)


t
v

strinsil
Y_ +..b !J

nanes

= ["Christa ", t' Sarah" ,


,rsant,,

"Jonathan"

" Hildegarde "J; // cornnence par affieher les noms coame ils se prsentent I I (tout en se souvenant de la chane 1a plus longue)
Console.Writeline("Les nons suivants ont des "
{I ttl^-^,,^,,.t rUrr{JUt
nanes)
vr!

Liff ranfocrr). Lvu /

'

foreach(string s in
{

Console.lllriteline("Ceci est Le non


I

'{0J' initia1", s);

Console

I^lriteline ( ) ;
de rnanire qu'e1Ies soient nme longueur
;

// nodifie maintenant 1es chanes, /i justifies gauche et qu'elles


ll irstifips pt sl'ion6oq

aient toute la

strlngIJ sAlignedNames = TrinndPad(names) // affiche enfin 1es chaines modifies,


Console..|,trriteline("Voici 1es

mmes noms"

foreach(string s

in

"affichS SUr 1a baSe de 1a nme lono'rorrrrr)'


sAlignedNames)

Chapitre 9: Jouer avec des chanes en C#

2|I

Console.
]

Writeline ( "Ceci est

le non'{01'aprs alignement", s);


..")

// attend confirnration de 1'utilisateur Console.I,lriteline("Appuyez sur Entre pour terminer.


Console.Read0;

return
]

0;

insre 1es espaces ncessaires pour les aligner toutes sur la plus longue public sttj.c string[] trirnlndPad(string[] strings)

// I |/ II
I

lrim.qndPad

- partir d'un tableau de chanes, supprine


les
espaces chaque

extrnit,

puis

i
copie l-e tableau source dans un tableau rye vous pourrez manipuler stringll stringsloAlign = new StringIstrings.Length]
I

// |

for(int i = 0; i ( stringsToAlign.Length;
t

l/ //

conrrence

par suppriner 1es espaces inutiles


i++)

chaque

extrmj.t de chaque non

stringsoAlignliJ = strings[i] .Trin0


]

l/ trouve naintenant 1a longueur de la chane 1a plus longue, I I de faon que toutes 1es autres s'alJ.gnent sur el1e int nMaxlength = 0;
foreach(string s in stringsToAlign)
t

if (s.Length )
t

nMaxlength)

nMaxlength = s. Length; l

// enfin. iustifie toutes 1es chanes sur la base I I de 1a longueur de 1a chane 1a plus longue for(int i = 0; i ( stringsToAlign.Length; i++)
{

stringsToAlign
l

stringsToAiign[i] .PadRight(nMaxlength + l)

Ii]

=
;

//
]

retourne 1e rsultat 1a fonction appelante


;

return stringsToAlign

220

Troisime partie : Programmation et objets

Al ignOutp rt dfinit un tableau de noms de longueur et d'alignement ingaux (on pourrait tout aussi facilement crire un programme pour lire ces noms sur la console ou dans un fichier). La fonction Main O commence par afficher les noms tels qu'ils sont, puis les aligne en utilisant la mthode TrinAndPao O avant d'afficher nouveau le rsultat sous forme de chalnes de longueur gale avec les noms aligns gauche :
Les nons suivnts ont des longueurs diffrentes

'Christa ' initial ' Sarah' initial teci- est 1e nom 'Jonathan' initial Ceci est le nom 'Sam' initial Ceci est le nom ' Hildesarde ' i.nitial
Ceci est 1e non Ceci est 1e nom

Voici les
Ceci Ceci Ceci Ceci Ceci

nnes noms affichs sur

la

est le

est
est

est
est

nom le nom 1e nom 1e nom le nom

' ' ' ' 'Hildegarde '

'Christa 'Sarah 'Jonathan 'Sam

aprs aprs aprs aprs aprs

base de 1a nme longueur alignement alignement alignement alignement alignement

La mthode TrirnAndPad O commence par faire une copie du tableau de chalnes reu. En gnral, une fonction qui opre sur un tableau doit retourner un nouveau tableau modifi plutt que de modifier le tableau qui lui est pass. C'est un peu comme quand j'emprunte le pickup de mon beau-frre : il s'attend le voir revenir dans l'tat o il me I'a prt.
TrlmAndPad O commence par effectuer une itration sur les lments du tableau, appelant Trin ( ) sur chaque lment pour en supprimer les caractres inutiles chaque extrmit. Puis la fonction effectue nouveau une itration sur les lments du tableau pour en trouver le membre le plus long. Elle effectue enfin une dernire itration, appelant PadRight ( ) pour ajouter les espaces ncessaires chaque lment, afin qu'ils aient tous la longueur du plus long. PadRi ght ( 10 ) ajoute des espaces I'extrmit droite d'une chane jusqu' lui donner une longueur de 10 caractres. Par exemple, elle ajoute quatre espaces I'extrmit droite d'une chane de six caractres.

TrimAndPad O retourne le tableau des chalnes allges de leurs caractres non imprimables droite et gauche par Trim O , et mis la bonne longueur, du bot"t ct, par PadRight O. Main 0 effectue une itration sur cette liste pour afficher I'une aprs I'autre toutes les chanes.

Chapitre 9 : Jouer avec des chanes en G#

22 |

Recoller ce que le logiciel a spar : utiliser la concatnation


Vous serez souvent confront la ncessit de diviser une chalne en plusieurs morceaux, ou d'insrer une chalne au milieu d'une autre. Le remplacement d'un caractre par un autre est trs facile faire avec la mthode Replare O :
cf rino L!rrl o = ttI'\anor vqrlbe Rpnttin<fl'

a.Replace(s, ",'l')

Dans cet exemple, la chalne est convertie en "DangerlRequins

Remplacer toutes les apparitions d'un caractre par un autre (dans ce cas, I'espace par un point d'exclamation) est particulirement utile pour gnrer une chane contenant la virgule comme sparateur afin de la diviser ultrieurement. Toutefois, le cas le plus courant et le plus difficile est I'opration qui consiste diviser une chalne en plusieurs sous-ensembles, les manipuler sparment, puis les recombiner pour former nouveau une seule chalne modifie.
Par exemple, la fonction Rerno.reSpecialChars O supprime toutes les apparitions d'un certain nombre de caractres spciaux dans une chalne donne. Le programme Remove\^u'hiteSpace ci-dessous utilise cette fonction pour supprimer les caractres non imprimables (espace, tabulations et caractres de nouvelle ligne) dans une chane :

,,"ctla L*- I].

// RenoveWhiteSpace - dfinit une fonction RenoveSpecial0hars0 qui peut supprimer un caractre quelconque d'un I eertain ensenble drune chane donne. Utilisez II cette fonction pour supprimer 1es caractres II blancs dans une chane utilise comne exenpLe. ll
I

nanespace RemoveWhiteSpace

using
i

System;
C1ass1

public class

nrrhlin cfafi6 i.nt int Main(stringIJ Main(strins['l strings] strinss) yuurrL o,o,ic
t

ll aenntt 1es caractres blancs char[] c!hiteSpace = {' ', '\n', '\t'}; // cornmence par une chane contenant des caractres string s = " ceci est une\nchane'*; ionroi..lfriteline("chane initiale :" * s);

blancs

222

Troisime partie : Programmation et objets

I affiche 1a chane sans les caractres blancs Console.WriteLine("aprs :" I


I RemoveSpeci"al0hars /

(s,

attend confirmation de 1'utilisateur Console,WriteLine("Appuyez sur Entre pour terminer. ..")


Console.Read0;

cWhiteSpace) )

return
l
I

0;

// I

RemovpSnpcialChnrs

nrhl
{

jn

srrnnlimp dp 1a chane toute occurrence du caractre spcifi cfefi n ctri.lg RemoveSpecialChars (string slnput,
char

[]

cTargets)

string s0utput = slnput;

for(;;)
{

// trouve f index du caractre, sort de la I I s'iI n'en reste plus int n0ffset : sOutput . IndexOf (cTargets) ; if (nOffset =: -1)
t

boucle

break;
]

// divise la chane en la partie qui prcde I I Ie caractre et la partie qui 1e suit


bLrlil

: vuLyuL.UuUDLrIrri\v, qtr.i no cRpforo f ca+') . c0rrtnrrt Srrhctrjnn /0 nf lMrDcL/t -+-i^- snl ^^r+^c011tn11t JuuLlr116\r.v!rrLL Srrhctrino (nflf f <pt *' !) I) = vuLyuL, LI

runit maintenant 1es deux sous-chanes et le caractre manquant entre 1es deux s0utput = String.Concat(sBefore, sAfter) ;
I

I //

'

l
rt11rn crrinttt,

l
]

fonctior RemoveSpecialChars O qui constitue le cur de ce programme. Elle retourne une chalne qui est la chalne entre, s Input, mais dont tous les caractres contenus dans le tableau cTargets ont t supprims. Pour mieux comprendre cette fonction, imaginez que la chalne tait "ab,cd,e", et que le tableau de caractres spciaux supprimer contenait simplement le caractre','.
C'est la

La fonctio Remor,,eSpecialChars O entre dans une boucle dont elle ne sort qu'une fois que toutes les virgules ont t supprimes. La fonction Ind ex0f Any ( ) retourne I'index du tableau pour la premire virgule qu'elle peut trouver. Si elle retourne -1, c'est qu'aucune virgule n'a t trouve.

Chapitre

: Jouer avec des chanes en

G#

223

sa premire invocation, inclexLlfArr.,, () retourne un 2 ('a'est {}, 'ir' est 1, et ',' est 2). Les deux fonctions suivantes dcomposent la chalne err morceaux I'endroit donn par I'index. Subs-ti i;:g i.,-t , 2 t cre une souschane compose de deux caractres, et commenant I'index (i : 'ab". Le deuxime appel Subst r ing i3 ) cre une chalne commenant I'irrder 3 et allant jusqu' la fin de la chalne initiale: "cd,e" (c'est le "* 1" qui fait passer aprs la premire virgule). C'est la fonction ',r-,r,r--,-.,-, ' iti recolle les deux sous-chanes pour crer "abccl,e".
Le contrle repasse en haut de la boucle. L'itration suivante trouve la virgule I'index 4.La chalne concatne est "abcde'. Comme il ne reste

plus de virgule, I'index retourn par la dernire itration est


Le programme Femo-,,ei,ihir,eSpar

-1.

affiche une cltalne cclntett;tttt plusieurs types de caractres non imprimables. Il r-rtilise ensuite la fonc:ticlrr F.emo.,Sp-.cia iChars () pour enlever ces caractres non itnprinialtles. La sortie de ce programme se prsente de la faon suivante
:

chane
chane

initiale : ceci est

une

aprs : ceciestunechane
Annrtrroz crrr Fntro nnrrr tormi ner

ll,lettre

Splrt O dans le lrrogramme de

concatnation
Le programme F.er,c-".e1,;'hitespacr donne un exemple d'utilisation cles mthodes Concat O et IndexOf (), mais il n'emprunte pas lavoie laplus efficace. Comme d'habitude, un bref examen rvle une solution lrlus

efficace qui utilise notre vieil ami *qp1ir, i I


/
tl

de 1a ehane toute occurrence du caractre spcifi *..L1.:^ ^+^+)^ ^+-) puDlr_c srrlng RenoveSpecialChars (string slnput, srarj-c
^

KenoveSpeclalunars

- supprine

cherfl r.Taropts)
{

// diviser 1a chane entre en uti.lisant les caractres l/ cible come dlimiteurs string li sSubStrings = slnput. Split (clargets) l/ s0utput contiendra 1es inforinations finales de sortie
;

<l-rino

<0rf ntrt = lllr'

// effectue

une boucle sur 1es sous-chanes rsultant de

la division

22 t,

Troisime partie : Programmation et objets

foreach(string subString in sSubStrings)


{

sOutput = String.Concat(s0utput, subString)


]

return sOutput;
l

Cette version utilise la fonction Split ( ) pour diviser la chalne entre en un ensemble de sous-chalnes sur la base des caractres de sparation. Ceux-ci sont supprims au passage. Ils ne font pas partie des sous-chalnes cres. La boucle foreacli de la deuxime partie du programme recolle les diffrentes sous-chalnes. La sortie du programme est la mme.

llvlatriser String. Format


La classe

o
:

String offre aussi la mthode Format O pour mettre en forrne la sortie, en particulier la sortie des nombres. Dans sa forme la plus simple, Forrnat O permet d'insrer une chalne, une variable numrique ou b<>cllenne dans une chalne de contrle. Par exemple, examinez I'appel suivant
String.Format("{0}

fois {l}

ga1e 12}"

, 2, 3, 2*3);

On appelle chane de contr1e le premier argument de !'ormat O. Les i :i r que vous voyez dans cette chane indiquent que le nirne argument suivant la chalne de contrle doit tre insr ce point. Zro correspond au premier argument (dans ce cas, 2), un se rfre au suivant (3), et ainsi de suite.

Il en rsulte la chalne
"2 fois 3 ga1e 6"

() utilise un format de sortie par dfaut pour chaque type d'argument. Format O permet de modifier le format de sortie en mettant des modificateurs aux emplacements voulus. Le Tableau 9.1 donne une liste de certains de ces contrles. Par exemple, | 0 : E6 I dit : "Afficher les nombres en notation scientifique, en utilisant six caractres pour la mantisse."
Sauf indication contraire, Format

Ghapitre 9:Jouer avec des chanes en G#

225

Tableau 9.1 :Gontrles de mise en forme utilisant

String.Format O.

Contrle
C

Exemple
{0:C} avec

Rsultat 123,456
123,45

Notes
Le symbole montaire dpend du paramtrage de localisation, de mme que I'usage de la virgule ou du point comme sparateur de la paftie

- monnaie

dcimale.
{0:C} avec
D
E

-123,456

(123,45

F)

00123 - dcimal {0:D5} avec 123 - exponentiel {0:E) avec 123,45 1,2345E+02 {0:F2} avec 123,4567 123,45 - fixe - nombre
{0:N}

Entiers seulement.
Oue l'on appelle aussi "notation

scientifique".
Le nombre qui suit le F indique le nombre de chiffres aprs la virgule.

123456,789
123456.789 123456.789

123

456,19

Ajoute le sparateur de milliers (dpend du paramtre de localisation) et arrondit au centime le plus proche.
Contrle le nombre de chiffres aprs la virgule. ldem.
0xFF est gal 255.

{0:N1}

123

456,8

457 X OxFF - hexadcimal {0:X} 012,30 {0:000.00} 12,3 {0:0...} {0:###.##}12,3 12,3 {0:#...}
{0:N0}
123

Met un 0 s'il n'y a pas de chiffre. lmpose un espace blanc si le nombre n'occupe pas I'espace spcifi. Aucun autre champ ne peut enquter sur l'espace dfini par les trois chiffres avant la virgule, et les deux aprs (permet de maintenir l'alignement autour
de la virgule).

{0:##n.0#}

0 ,1234

0,0 12,3%
02,3o/o

Un signe # impose un espace, et un 0 impose l'affichage d'un chiffre, mme si

celui-ci est
{0:# or

0.

0%}

{0:#00.#%l

Le % affiche le nombre sous forme de pourcentage (multiplie par 100 et ajoute

le signe %).

{0:#00.#%]10234

226

Troisime partie : Programmation et objets

Ces contrles de formats peuvent paraltre un peu dconcertants (et je n'ai mme pas parl des contrles dtaills de date et de format montaire). Pour vous aider apprivoiser ces options, le programme suivant, OutputFormatCcirtrcls, vous permet d'entrer un nombre en virgule flottante suivi par une squence de codes de contrle. Le programme affiche alors le nombre en utilisant I'instruction Fornar O avec la squence de contrle de format spcifie :
/t t I
1I

a {Jlrtn11th'rm,atn^-+-^1 vuLyuLrv!llraLvvrlL!vr

I II
t

^ - ^^er^+ yrnrL 1'UtiliSateUr de redfinir 1e fornat des nonbres saisis en utilisant l'excution divers codes de contrle de format

nanespace OutputFornatControls

using
{

System;
C1ass1

public class

public static int Main(stringIJ args)


{

I lit les nombres saisis jusqu' ce que /l 1'utilisateur entre une liene blanche au lieu // d'un nombre
I
1^r rv! | . ' I \ r , /

t I

// I II

commence

par lire un nobre,

et se termine lorsque 1'utilisateur n'entre rien qu'une ligne blanche


sNumber

Console.lilriteline("Entrez un nombre de type double");

string

if
{
I
J

Console.Readline0

(sNumber.Length

0)

break;

double dNurnber : Double.Parse(sNunber) ; I I lit maintenant 1es codes de contr1e, spars I I Les uns des autres par des espaces Console.l{riteline("Entrez 1es codes de contrle" * " spars par un espace"); char l] separator = {' 'J ; string sFormatString = Console.Readline0 ;

stringl]

sFormats

"
(

/l
t

sFornatString. Split separator) ; effectue une boucle sur les codes de contrle 1'un aprs 1'autre

foreach(string s in sormats)

if
{
I

(s.Length !=

0)

cre une co$mande de format complte

Chapitre 9 : Jouer avec des chanes en G#

227

// partir des codes de contrle

entrs

string sFormatConnand = 'riO' rr * g * nltt ' // affiche 1e nonbre entr en utilisant I I Ia connande de fornat reconstitue
Console . 1lIrite
(

"La connande de format {0} donne ",


sFormatConrnand )
;

try
{

Console.

I'lriteline

(sFormatConnand, dNumber)

l
catch (Exception)
{

Console.l.rlriteline ("(commande illgale)")


] Console

.l,lriteline 0

/i attend confirmation de 1'utilisateur Console.Hriteline("Appuyez sur Entre pour terniner. . .")


Console.Read0;

return

0;

Le programme continue lire dans la variable dNumber les nombres entrs par I'utilisateur, jusqu' ce que celui-ci entre une ligne vide. Remarquez que le programme ne comporte aucun test pour dterminer si la valeur entre est un nombre en virgule flottante licite. Nous supposons ici que I'utilisateur sait ce qu'est un nombre. Le programme lit ensuite une srie de codes de contrle, spars par des espaces. Chaque contrle est combin avec une chalne "{0}" dans la variable sFormatComrnand. Par exemple, si vous avez entr N4, le programme stocke la chane de contrle "{0:N4}". L'instruction suivante crit sur la console le nombre dNumber en utilisant la commande sFormatCommand ainsi construite :
Console

liriteline

sFo rmatComnand

dNumbe r )

Dans le cas de notre N4. la commande serait


Console.Writeline("{0:N4}",
dNumber)
;

228

Troisime parrie : programmarion er objers

Voici un exemple de sortie obtenue avec ce programme ('ai mis en gras ce que j'ai entr) :
Bntrez un nombre de type double
1,2345 ,67 89

c E F1 N0
La

Entrez 1es codes de contr1e spars par un


0000000.00000
commande

espace
F

de format {0:Cl donne 12 345,69

La comnande de format {0:E} donne 1,234568E+004 La connande de fornat {0:F1l donne IZ34S,7 La La
commande commande

de fornat {0:N0l donne

IZ

346

de format {0:0000000.000001 donne 0012345,67g90

Entrez un nombre de type double


,L2345

Entrez 1es codes de contr1e spars par un


00.0%

espace

La commande de format {0:00.0:(} Entrez un nombre de type double


Appuyez

donne

L2,3',i,

sur Entre pour ternirer...

Appliqu au nombre 12345,6789, la commancle N0 ajoute des sparateurs de milliers aux bons endroits (c'est la partie "N") et fait disparaltre tout ce qui suit la virgule dcimale (c'est la partie "0"), pour afficher 12 346 (le dernier chiffre a t arrondi, et non tronqu).
De mme, appliqu 0,12345, le cocle de contrle 00.0i% affiche r2,3,/o. Le code % multiplie le nombre par 100 et ajoute le signe %,.Le00.0 indique que la sortie doit comporter au moins cleux chiffres gauche cle la virgule, et seulement un droite. Avec le rnme 00.0'/n,le nombre 0,01 est affich comme 0I.0'/,,. Le mystrieux t t'tr' . . catch attrape au passage toutes les erreurs qui ,194(ac peuvent se produire si jamais vous entrez une commande cle format illicite, 6f^ \ pu, "D",
=

exemple un [J\f (- ) Chapitre 15.

qui signifie dcimal. Je parlerai des exceptions au

0uatrime partie

La programmation

oriente obiet

Dans cette parte...


a programmation oriente objet est le terme dont I'usage

est accompagn de la plus grande quantit de mousse dans le monde de la programmation (il a t clips pendant un an ou deux par ".com" et "e-commerce", mais vous pouvez oublier tout a depuis le crash .com de 2001). C++ rVndique d'tre un langage orient objet. C'est ce qui le diffrencie de C. Java est sans aucun doute un langage orient objet, de mme qu'une centaine ou peu prs d'autres langages invents au cours des dix dernires annes. Mais que signifie orient objet ? Est-ce que je I'ai ? Est-ce que je peux

I'avoir

La quatrime partie prsente les caractristiques de C# qui en font un langage fondamentalement orient objet.

Chapitre 10

La programmation oriente

obiet:qu'est-ce que c'est ?


Dans ce chapitre : La programmation oriente objet et le four micro-ondes. Les bases de la programmation oriente objet.

Abstraction et classification.
Comprendre I'importance de la programmation oriente objet.

e chapitre apporte tout simplement la rponse la question : "Quels

sont les concepts sur lesquels repose la programmation oriente objet, et en quoi sont-ils diffrents de ceux que nous avons vus dans la deuxime partie de ce livre ?"

L'abstractinfr, cnncept numro un de la programmation oriente objet


Quand je regarde un match de football (amricain) la tlvision avec mon fils, il me prend souvent une envie irrsistible de nachos (chose invente jadis par les Mexicains pour les gens qui regardent la tlvision aujourd'hui). Je mets des chips dans une assiette, je les recouvre de haricots, de fromage et de beaucoup de jalapeflos, et je mets le tout quelques minutes dans le four micro-ondes.

Pour utiliser le four micro-ondes, j'ouvre la porte, je mets I'assiette I'intrieur, je referme la porte, et j'appuie sur quelques boutons qui se

232

0uatrime partie:La programmation oriente obiet

trouvent sur la face avant. Quelques minutes plus tard, les nachos sont prts.
Maintenant, pensez tout ce que je ne fais pas pour utiliser le four

micro-ondes:

t/

Je ne change pas le cblage ni quoi que ce soit I'intrieur du four micro-ondes pour le faire fonctionner. Il a une interface (la face

avant avec tous ses boutons et I'affichage de I'heure) qui me permet de faire tout ce dont i'ai besoin.

t/ t/ /

Je n'ai pas modifier le logiciel utilis par son microprocesseur

pour piloter le fonctionnement du four, mme si c'est un autre plat que j'ai fait chauffer la dernire fois que je m'en suis servi.
Je ne regarde pas sous le capot.

Mme si c'tait mon mtier de tout savoir sur le fonctionnement interne d'un four micro-ondes, y compris sur son logiciel, je ne me proccuperais pas de tout cela pour I'utiliser dans le seul but de faire chauffer mes nachos.

Ce ne sont pas l des observations profondes. On ne peut vivre avec le stress que jusqu' une certaine limite. Pour rduire le nombre de choses dont on a se proccuper, on ne travaille que jusqu' un certain niveau de dtail. Dans la langue de la programmation oriente objet (OO), le niveau de dtail auquel on travaille est appel niueau d'abstraction. Autrement dit, pour faire chauffer mes nachos, j'ai foit obstroction des dtails du fonctionnement interne du four micro-ondes.

Lorsque je fais chauffer des nachos, je vois le four micro-ondes comme une bolte. Tant que je n'utilise que I'interface du four (les boutons de la face avant), rien de ce que je fais n'est susceptible de le faire entrer dans un tat instable et de le mettre hors d'usage, ou pire, de transformer mes nachos en une masse noire informe et d'y mettre le feu.

Prparer des nachos fonctionnels


Imaginez que je demande mon fils d'crire un algorithme dcrivant la manire dont son pre prpare les nachos. Quand il aura compris ce que je veux, il crira quelque chose comme : "Ouvrir une bolte de haricots, rper du fromage, couper les jalapeos" et ainsi de suite. Une fois arriv

Ghapitre 10: La programmation oriente objet:qu'est-ce que c'est

? 233

au four micro-ondes, il crira sans doute : "Faire chauffer cinq minutes dans le four micro-ondes." Cette description est simple et complte, mais ce n'est pas de cette faon

qu'un prograrnmeur crirait un programme fonctionnel pour prparer des nachos. Un programmeur vit dans un monde dpourvu d'objets tels que des fours micro-ondes et autres appareils mnagers. II se proccupe gnralement de diagrammes de flux, avec des milliers de chemins fonctionnels. Dans une solution fonctionnelle au problme des nachos, le contrle passerait de mes doigts aux boutons de la face avant du four, puis son fonctionnement interne. Trs vite, le flux suivrait les chemins d'une logique complexe sur la dure pendant laquelle faire fonctionner le gnrateur de micro-ondes, et le moment de faire retentir la petite musique qui vous dit que c'est prt.
Dans ce monde de programmation fonctionnelle, il n'est pas facile de penser en termes de niveaux d'abstraction. Il n'y a ici ni objets ni abstractions derrire lesquels on pourrait rnasquer la complexit.

Prparer des nachos orents objet


Dans une approche oriente objet de la prparation des nachos, je commencerais par identifier les diffrents types d'objets intervenant dans le problme : chips, haricots, fromage et four micro-ondes. Ensuite, j'entreprendrais la tche de reprsenter ces objets dans le logiciel, sans me proccuper des dtails de leur utilisation dans le programme final.

Lorsque je fais cela, on dit que je travaille (et que je pense) au niveau des objets de base. ll me faut penser faire un four utile, mais ce stade je n'ai pas encore rflchir au processus logique de la prparation des nachos. Aprs tout, les concepteurs du four micro-ondes n'ont pas pens spcifiquement ma manire de me prparer des nachos. Ils se sont plutt consacrs rsoudre le problme de la conception et de la fabrication d'un four micro-ondes utile.
Une fois que j'ai cod et test les objets dont j'ai besoin, je peux monter au

niveau d'abstraction suivant. .fe peux maintenant quitter le niveau du four pour penser au niveau de la prparation des nachos. ce stade, je peux traduire directement en code C# les instructions rdiges par mon fils.

23 4

0uatrime partie : La programmation oriente objet

La classfcation, coneept numro deur de la programmation ori ente objet


La notion de classification est insparable cle Ia notion d'abstraction. Si je demandais mon fils : "Qu'est-ce qu'un four micro-ondes ?", il rpondrait sans doute:"C'est un four qui..." Et si je lui clemandais:"Qu'est-ce qu'un four ?", il pourrait rpondre: "C'est un appareil mnager qui..." Et si je lui demandais : "Qu'est-ce qu'un appareil rnnager ?", il rponclrait peuttre : "Pourquoi poses-tu des questions stupides ?"

Les rponses donnes par mon fils dans mon exemple viennent de ce qu'il sait de notre four micro-ondes, cas particulier du type d'objet appel four micro-oncles. D'autre part, mon fils considre un four micro-ondes comme un four d'un type particulier, et un four en gnral comrne un appareil mnager d'un type particulier.
Dans la langue de la programmation oriente objet, mon four micro<rndes est une instonce de la classe Four micro-oncles. l-a classe Four microondes est une sousclasse de la classe Four. et la classe Four est une sous<lasse de la classe Appareil mnager.

L'tre humain aime classifier. Tout ce qui peuple notre rnonde est ordonn en taxonomies. Ce procd nous pernret de rduire le nombre de choses que nous avons retenir. Pensez par exemple la premire fois que vous avez vu une "spacecar". La publicit la dcrivait probablernent comme rvolutionnaire ("vous ne verrez plus jamais I'automobile de la mme manire"). Et il est vrai que c'tait une nouveaut, mais aprs tout une spacecar n'est rien d'autre qu'une voiture. En tant que telle, elle partage toutes ses proprits (ou au moir-rs la plupart) avec les autres voitures. Elle a un volant, des siges, un moteur, des freins, et ainsi de suite. Je peux en conduire une sans commencer par lire le mode d'emploi.
Je n'ai pas besoin de m'encombrer la mmoire avec la liste de

tout ce

qu'une spacecar partage avec les autres voitures. Tout ce que j'ai retenir est "une spacecar est une voiture qui...", t les quelques proprits qui sont propres aux spacecars (par exemple, le prix). Mais je peux aller plus loin. La classe Voiture est une sous-classe de la classe Vhicules roues, laquelle contient d'autres rnembres, coffrme les camions et les dcapotables. Et la classe Vhicules roues peut tre une sous-classe de la classe Vhicule, qui contient les bateaux et les avions. Et ainsi de suite, aussi loin que vous voulez.

Chapitre 10: La programmation oriente objet: qu'est-ce que c'est

Pour(uoi classif ier


.)

? 235

Pourquoi devrait-on classifier ? a a I'air de demancler du travzril. D'ailleurs, a fait si longtemps qu'on utilise I'approche font:tionrrelle. alors pourquoi changer maintenant ?
La conception et la fabrication d'un four micro-ondes spcialelnerrt porlr ce problme particulier peut sembler une tche plus facile que la ralisation cl'un objet four, plus gnrique. Supposez par exemple que je veuille fabrirluer trn four micro-ondes pour faire chauffer des nachos et rien d'autre. Il ne rne faudrait rien d'autre dans le panneau de commande.s qu'un bouton Drnarrer, car j'utilise toujours le mme temps cle chauffage pour mes nachos. .le pourrais me dispenser de tous les autres boutons comrne Dconglation et autres. D'autre part, il n'aurait besoin de contenir rien de plus qu'une assiette. Iin volume permettant de faire cuire une dinde serait ici du gaspillage. Je peux donc me dispenser du concept de "four micro-ondes". Je n'ai

besoin que de ce qu'il fait. Puis, j'introcluis dans le processus les instrtrctions qui permettent de le faire fonctionner : "Mettre les nachos clans la Llolte connecter le fil rouge au fil noir ; mettre le tube raclar sous tension cle 3 000 volts ; entendre le petit bruit qui indique le drnarrage ; ne pas s'approcher trop prs si on a I'intention d'avclir des enfant.s." Ce genre de choses.
;

Mais I'approche fonctionnelle a quelques inconvnients

t/

Trop complique : Je ne veux pas mlanger les cltaiis cle la fabrication d'un four micro-ondes avec ceux cle la prparation cles nachos. Si je ne peux pas dfinir les objets et les extraire de cette montagne de dtails pour les utiliser de faon indpendante, je suis oblig de prendre en compte tous les dtails de tous les aspects du problme en mme temps.
Dpourvue de souplesse : Un jour ou I'autre, je peux avoir besoin de remplacer le four micro-ondes par un four d'un autre type. Il clevrait tre possible de le faire tant qu'ils ont la mme interface. S'ils ne sont pas clairement dlimits et dvelopps de faon inclpendante, Lur objet d'un certain type ne peut pas tre simplement remplac par un autre.

t/

tz

Non rutilisable : Un four permet de faire de nombreux prlats diffrents. Je ne veux pas avoir crer un nouveau four polrr chaque nouvelle recette. Aprs avoir rsolu le problrne une fois, je veux pouvoir rutiliser la mrne .solution en d'autre.s endroits de mon programme. Et si j'ai vraiment de la chance, je pourrai nrrne la rutiliser plus tard dans d'autres programmes.

236

0uatrime partie : [a programmation oriente objet

Une interface utilsable, concept numro trois de la ltrogrammaton nriente objet


Un objet doit tre capable de prsenter une interface extrieure suffisante, mais aussi simple que possible. C'est un peu I'inverse clu concept numro quatre. Si I'interface de I'objet est insuffisante, les utilisateurs peuvent tre amens en ouvrir le capot, en violation directe des lclis cle Dieu et de la Socit (ou tout au moins en violation des lois du Texas sur la responsabilit juridique - je vous le dconseille fortement). D'un autre ct, si son interface est trop complique, personne n'achtera I'objet, ou en tout cas personne n'utilisera toutes ses fonctionnalits. Les gens se plaignent rgulirement de la complexit de leurs magntoscopes. Ils ont trop de boutons avec trop de fonctions diffrentes. Bien souvent, un mme bouton a plusieurs fonctions diffrentes selon l'tat de I'appareil. En plus, il n'y a pas deux moclles de magntoscopes clui aient la mme interface. Quelles qu'en soient les raisons, les magntoscopes ont des interfaces trop compliques et trop peu standardises pour tre utilisables par la plupart des gens.

Comparez cela avec une voiture. Il serait difficile de prtendre qtr'une voiture est moins complique qu'un magntoscope, mais les gens ne semblent pas avoir de difficults les conduire. Je vois au moins trois diffrences significatives entre une voiture et un magntoscope. Toutes les voitures prsentent plus ou moins les mmes commandes peu prs au mme endroit. Par exemple (histoire vraie), ma sceur a eu une voiture (oserais-je le dire, une voiture franaise) dont la commande des phares tait gauche du volant, combine avec la commande du clignotant. Il fallait pousser la manette vers le bas pour teindre les phares, et vers le haut pour les allumer. On peut trouver que c'est une petite diffrence, mais je ne suis jamais arriv tourner gauche de nuit avec cette voiture sans teindre les phares.
Une voiture bien conue n'utilise pas la mme commande pour plusieurs oprations diffrentes selon l'tat dans lequel elle se trouve. Je ne connais que trs peu d'exceptions cette rgle.

Chapitre

l0:

La pr0grammation oriente

objet: qu'est-ce que c'est

237

Le contrle d'accs, concept numro (uatre de la programmation oriente objet


Un four micro-ondes doit tre construit de telle sorte qu'aucune combinaison de pressions sur les boutons de la face avant ne puisse me blesser en aucune manire. Il y a certainement des combinaisons qui ne font rien, mais aucune ne doit :

tz

Endommager I'appareil. Vous devez pouvoir placer I'appareil dans une sorte d'tat trange dans lequel il ne fera rien tant que vous ne I'aurez pas ranim, mais il doit tre impossible de causer un dommage quelconque I'appareil en utilisant les commandes de la face
avant.

,/

Mettre le feu I'appareil et par consquent la maison. Que I'appareil tombe en panne, c'est ennuyeux, mais qu'il prenne feu, c'est beaucoup plus grave. Nous vivons dans une socit trs procdurire. Il peut rsulter de ce genre de choses des procs trs curieux.

Toutefois, pour que ces deux rgles soient respectes, vous avez aussi votre part de responsabilit : vous ne pouvez f,aire aucune modification I'intrieur de I'appareil. Presque tous les appareils mnagers, de n'importe quel niveau de complexit, notamment les fours micro-ondes, comportent un petit sceau qui empche le consommateur d'accder leurs composants internes. Si le sceau est bris, la responsabilit du fabricant n'est plus engage. Si je modifie les composants internes d'un four, c'est moi qui suis responsable s'il met le feu la maison.
De mme, une classe doit permettre de contrler I'accs ses membres.

Aucune squence d'appels aux membres d'une classe ne doit provoquer le plantage de mon programme. La classe ne peut pas le garantir si des lments externes ont accs ses composants et son tat interne. La classe doit pouvoir maintenir ses membres critiques inaccessibles au monde extrieur.

238

0uatrime partie : [a programmation orienre objer

Comment la programtnation nrente objet est-elle imltlmente par C#


Dans un certain sens, ce n'est pas la bonne question. C# est un langage orient objet : il n'implmente pas la programmation oriente objet, c'est le programmeur qui le fait. Vous pouvez crire un programme qui ne soit pas orient objet en C# comme dans n'importe quel autre langage, mais C# permet dcrire facilement un programme orient objet.
C# offre les fonctionnalits ncessaires l'criture de programmes orients

objet:
t/
Le contrle d'accs : C# permet de contrler la manire dont on accde un membre. Les mots-cls C# vous permettent de dclarer certains membres ouverts au publlc alors que les membres internes (lnterna1) sont protgs (prorected) des regards extrieurs et que leurs secrets sont maintenus privs (pri,"'ate). Le Chapitre ll vous livre les secrets du contrle d'accs. La spcialisation : C# supporte la spcialisation travers un mcanisme appel hritage de closse. une classe hrite des membres d'une autre classe. Par exemple, vous pouvez crer une classe car comme type particulier de la classe vehlcle. Le Chapitre 12 est le spcialiste de la spcialisation.

t/

Polymorphisme : Cette caractristique permet un objet d'excuter une opration la manire qui lui convient. Le type Fuse de la classe vhic1e peut implmenter I'opration Dmarrage trs diffremment de ce que fait le type voiture de la mme classe (en tout cas, j'espre que c'est toujours le cas pour ma voiture). Ces chapitres l3 et 14 ont chacun leur propre manire de dcrire le polymorphisme.

Rendre une classe

responsable
Dans ce chapitre : Permettre une classe de se protger par le contrle d'accs. Permettre un objet de s'initialiser lui-mme par le constructeur.

Dfinir plusieurs constructeurs pour la mme classe.


Construire des membres statiques ou des membres de classe.

ne classe doit tre tenue pour responsable de ses actions. Tout comme un four micro-ondes ne doit pas prendre feu si j'appuie sur le mauvais bouton, une classe ne doit pas mourir d'pouvante si je lui prsente des donnes incorrectes.

Pour tre tenue responsable de ses actions, une classe doit avoir la garantie que son tat initial est correct, et pouvoir contrler ses tats suivants afin qu'ils le restent. C'est ce que permet C#.

Restreindre l'accs A des membres de classe


Une classe simple dfinit tous ses membres comme public. Considrez un programme BankAccounr qui tient jour un membre donne ba lance contenant le solde de chaque compte. Dfinir ce membre comme pLrbl-ic permet tout le monde d'y accder.
Je ne sais pas comment est votre banque, mais la mienne est loin d'tre assez confiante pour mettre ma disposition une pile d'argent, et un

240

Ouatrime partie : La programmation oriente objet

registre sur lequel il me suffirait d'inscrire ce que j'ai pris dans Ia pile ou ce que j'y ai ajout. Aprs tout, je pourrais trs bien oublier d'inscrire mes retraits dans le registre. Je ne suis plus si jeune. Ma mmoire baisse.
Le contrle d'accs permet d'viter les petites erreurs comme d'oublier d'inscrire un retrait ici ou l. Il pernret au.ssi cl'viter de vritables grosses erreurs avec les retraits.
Je sais exactement ce que pensent ceux qui ont I'esprit fonctionr-rel : "ll suffit de clfinir une rgle selon laquelle les autres classes ne peuvent pas

accder directernent au membre r.,a:-:c.." Cette approche pourrait fonctionner en thorie, mais en pratique a ne marche pas. Les gens sont toujours plein de bonnes intentions au dpart. mais ces bonnes intentions sont crases sous Ie poids de I'exigence de terminer le produit pour le livrer au client.

Un exemple yrublic de

public

BairkAc c ount

L'exemple suivant de classe BarkAc.:-rr..:rr dclare toutes se.s mthcldes pub11c, mais dclare comme pri.'are les deux membres donne nliextAcccunt et rlEalance :

i/ II II II II

BankAccount

- cre un conpte bancaire


(conserve
monde

en

de type double pour stocker

utilisant une variable le solde du compte


au

le solde dans une variable prive

pour masquer son inplmentation

extrieur)

u9rrr6 "^i-^

C"^+^*. 9J D L!r,

nanespce DoubleBankAccount
{
nrrhra^ yuurru Lra ^rdd t'^SS1 vl

public static void Main(stringIJ args)


t I I cre un nouveau compte bancaire Console.l.IriteLine("Cration d'un objet compte bancaire") BankAccount ba = ner,.r BankAccount0;

ba. InitBankAccount

//on peut
I

I //

accder au solde par 1a mthode Deposit() car e1le a accs tous 1es

membres donne

ba.Deposit(10); // 1'accs direct un menbre donne provoque une erreur

ll Ia

conpilation

Chapitre 1l : Rendre une classe responsable

24

Console.liriteli-ne("Au cas o vous arriveriez jusqu'ici"

* "\nCe qui suit est cens produire" * I'une erreur 1a compilation");


;

ba. dBalanee

t= 10 // attend confirmation de 1'utilisateur


;

Console.l'IriteLine("Appuyez sur Entre pour terminer,..")


Console,Read

ll
{

Banknccount

- dfinit

une classe

qui reprsente un compte

siurple

nrrhl

ie class

BankAccount

nrivatp statie int nNextAccountNunber : 1000; private int nAccountNumber; // conserve 1e sol-de dans une seule variable de type double nrivatp dorrhlp dBalance; // ln:-t - initialise 1e compte avec le prochain numro cie compte

et un solde de 0 II puDllc vo10 rnr.tbanxAccoun , \/\


{

nAccountNumber = **nNextAccountNumber

dBalance = 0.0;

//
i

GetBalance

- retourne 1e solde

courant

public double GetBalance0 return


l
dBalance;

//
{

AccountNumber

public

int

GetAccountNumber0
;

return
l

nAccountNunber

public void SetAccountNumber(int


{

nAccountNumber)

this.nAccountNumber = nAccountNumber
I/ I/ n^-^^'r+ r,/trPUlL

LUUL dnt uyuL nositif - +^!it PvorLr!

est

autoris

public void Deposit (double


t

dAmount)

if
{

(dAnount dBalance

0.0)
dAmount;

*=

// I!

Withdrar,{

- tout retrait est autoris jusqu' la valeur du solde ; retourne 1e nontant retir

public double l,lithdrar,r(double dWithdrawal)

242

Ouatrime partie : [a programmation oriente objet

if
{

(dBalance (= dWithdrawal) dWithdrawal = dBalance;

l
dBalance -= dWithdrawal return dWithdrawal;
;

/i
t

GetStrins -- *.. -_,-

public string GetString0

retourne dans une chane les informations srrr lpr.onnt s Lrvrr uL 4c LvxlP L

string s = String.Fornat("/{Ol = {t:C}",


GetAccountt{umber
(

GetBalance 0 )
ratll rn c
.

]
]

Souvenez-vous que dans ce code, dBalance dhrithdrawat est la rnme chose eue dBalance : dBalance dwithdrawal. Les programmeurs C# ont tendance utiliser la notation la plus concise possible.

Dclarer un membre comme public le rend disponible pour n'importe quel autre code dans votre programme.
La classe BankAccount offre la mthode InitBankAccounr O pour initialiser les membres de la classe, la mthode Deposit O pour traiter cles dpts, et la mthode Withdraw O pour traiter les retraits. Les mthocles Deposit O et Withdr aw O fournissent mme des rgles rudimentaires, comme : "on ne peut pas dposer une valeur ngative", et "On ne peut pas retirer plus que ce que contient le compte." Vous convienclrez certainement que ce sont de bonnes rgles pour une banque. Toutefois, n'importe qui peut accder tout cela aussi Iongtemps que dBalance st accessible aux mthodes externes (dans ce contexte, externe signifie "externe la classe, mais dans le mme programme").

Avant de trop vous enthousiasmer, remarquez que ce programme ne se gnre pas. Une tentative de le gnrer gnre en fait le message cl'erreur

suivant

'DoubleBankAccount.BankAccount.dBalancer

niveau de protection.

est inaccessible en raison de son

Ghapitre 11 : Rendre une classe responsable

43

Je ne sais pas pourquoi

il ne se contente pas de dire : "N'entrez pas, c'est priv", mais c'est essentiellement ce que a veut dire. L'instruction ba.dBalance +: 10; est illicite parce que dBalance n'est pas accessible Main O. Le remplacement de cette ligne par ba. Deposit (10) rsout le
problme.
Le type d'accs par dfaut est private. Oublier de dclarer un membre en tant que tel revient le dclarer comme private, mais il vaut mieux

{q./ n)il lS/ Y

indiquer le mot private pour viter toute ambiguit.

Allons plus lon .' les autres nitleaux de scurt

=(t
fry\

Cette section suppose quelques notions sur l'hritage (Chapitre 12) et les espaces de noms (Chapitre 16). Vous pouvez I'ignorer pour le moment, mais vous Saurez qu'elle est l lorsque vous en aurez besoin.
C# offre d'autres niveaux de scurit, au-del de

public et private

t/ t/ ,/ ,/

Un membre

public est accessible par toutes

les classes du programme.

Un membre private n'est accessible que par la classe dans laquelle il est dclar. Un membre protected n'est accessible que par la classe dans laquelle il est dclar et par toutes ses sous-classes. Un membre internal est accessible par toutes les classes du mme espace de noms (essentiellement, par tout groupe de modules C# que vous aurez spcifi pour cela, c'est--dire tous les modules que vous aurez crits pour le programme, mais pas ceux crits par votre voisin de palier). Un membre

t/

laquelle il est dclar et toutes ses sous-classes, ainsi que par les
classes du mme module.

internal protected

est accessible par la classe dans

C'est le masquage d'un membre en le dclarant private qui offre le maximum de scurit. Toutefois, dans de nombreux cas, vous n'aurez pas besoin de ce niveau de scurit. Aprs tout, comme les membres d'une sous-classe dpendent dj des membres de la classe de base, protected offre un niveau de scurit confortable.

244

0uatrime partie : La programmation oriente objet

Si vous dclarez chaque module comme un espace de nom diffrent, la

dclaratiort d'un rnembre comme i.nternal le rend disponible uniquement dans ce module. Mais si vous utilisez un seul espace de nom pour tous vos modules, il n'y aura gure de diffrence entre une dclaration irrternai ou irrl,ernal proreirteci et une dclaration public.

Pourquoi se t rocculrer du contrle d'accs


/

Dclarer les mernbres internes d'une classe comme public est une mauvaise ide. au moins pour les raisons suivantes :

public, vous ne pouvez pas savoir facilement quand et comment ils sont modifis. Pourquoi utiliser les mthodes Derosit () et i^/ithdraw ( ) pour traiter les chques ? En fait, pourquoi avoir besoin de ces mthodes ? N'importe quelle mthode de n'importe quelle classe peut modifier ces lments n'importe quancl. Si d'autres fonctions peuvent accder ces membres donne, elles le feront certainement.
Si tous les membres donne sont

Mon programme BankAccounr peut trs bien tourner pendant une heure ou deux avant que je ralise que I'un des comptes a un solde ngatif. La mthode \,,iithdraw O aurait d garantir que cela ne puisse pas arriver. De toute vidence, une autre fonction a dt accder au solde sans passer par Wi thd raw ( ) . Dcouvrir quelle fonction en est responsable et de quelle manire est un problme

trs difficile. t/
Exposer tous les membres donne d'une classe rend I'interface trop complique. En tant que programmeur utilisant Ia classe BankAccount, je ne veux rien savoir de ce qu'elle contient. Il me suffit de savoir qu'elle me permet de dposer et de retirer des
fonds. Exposer les lments internes conduit exporter les rgles de classe. Par exemple, la classe BankAccount ne permet en aucune circonstance que le solde devienne ngatif. C'est une rgle commerciale de la banque, qui doit tre isole dans la mthode I,,/irhd raw ( ) , faute de quoi il faudra ajouter la vrification de cette rgle en tout endroit du programme o le solde est modifi.

t/

Qu'arrive-t-il lorsque la banque dcide de modifier les rgles pour que les "clients privilgis" soient autoriss avoir un solde lgrement ngatif

Ghapitre 11 : Rendre une classe responsable

245

sur une priode limite ? Il me faut maintenant rechercher dans tout le programme toutes les portions de code qui accdent au solde afin d'y adapter en consquence les vrifications correspondantes.

Oes mthodes pour accder des objets


attentivement la classe tsankAccount, vous y verrez quelques autres mthodes. L'une d'elles, GetStrlng (), retourne une version de type chane du compte, adapte l'affichage par une instruction Console. writeLine O. Toutefois, I'affichage du contenu d'un objet BankAccount peut tre difficile si ce contenu est inaccessible. D'autre part, selon la politique "Rendez Csar ce qui est Csar", c'est la classe que revient le droit de dcider comment elle doit tre affiche.
Vous remarquerez aussi une mthode, GetBalance O, et un ensemble de mthodes : Get:\cccuntNunber ( ) et SetAccountNumber O. Vous vous demandez peut-tre pourquoi j'ai dclar comme priirate un membre donne comme CBalance, tout en fournissant une mthode GetBalance o pour en retourner la valeur. J'ai deux raisons pour cela. Tout d'abord, GetBalance O n'offre pas de moyen de modifier dBalance. Elle ne fait qu'en retourner la valeur, ce qui fait que le solde est en lecture seule. Par analogie avec une vritable banque, je peux consulter le solde de mon compte librement, mais je ne peux pas en retirer de I'argent sans passer par la procdure de retrait de la banque.
En second lieu, GetBaiance O masque aux mthodes externes le format interne de la classe. Il est tout fait possible que GetBalance O effectue
Si vous examinez plus

de nombreux calculs concernant la lecture des reus, des frais de gestion de compte, et tout ce que ma banque veut soustraire du solde de mon compte. Les fonctions externes n'en savent rien et n'ont rien en faire. Naturellement, je veux savoir quels frais on m'a fait payer, mais je ne peux rien y faire, moins de changer de banque.

Enfin, GetBalanre () offre un mcanisme qui permet d'apporter des modifications internes la classe sans qu'il soit ncessaire de changer les utilisateurs de BankAccount. Si le ministre des Finances demande ma banque de grer autrement ses dpts, cela ne doit rien changer la manire dont je peux accder mon compte.

246

Ouatrime partie : La programmation oriente objet

Le controle d'accs fule tutre secours : un


egemple
Le programnle I)r, ,i. l-.-B:lr:ik..ir r':rrlfL' suivant ntet etr vidence un dfaut potentiel dans le programrne Ban.t.ri:.i.rr.irii'. Le progranrme complet est sur le site Web, rnais le listing c:i-dessous ne montre que l'iain O, clui est la seule portion du programnre comportant une diffrenc-e avec Barkr,,:cor-rirt, :

// || II II II
t

DoubleBankAccount

- cre un compte bancaire

en

utilisant

une variable

de type double pour stocker le solde du compte (conserve 1e solde dans une variable prive pour masquer son implmentation au
monde
Toct

extrieur)

nqnocn:no

using Systen; nrrhlie class Classl


{

public static int Main(string[] strings)


{

I I cr6e un nouveau cotnpte bancaire Consol-e.Writeline ("Crati.on d'un objet compte bancaire")

BankAccount ba = new BankAccount ba. InitBankAccount ( ) ; // effectue un dpt

rlorrble rjDenosit : I23.454: Console.ltlriteLine("Dpt de i0:C)", dDeposit)


ba. Deposit (dDeposit)
;

I I solde du conrpte Console.l,lriteLine ("Compte : {0i", ba.GetString0 );

\
I

I et voil le problrre
;
\s:'su4

double dAddition = 0.002; Console,!,IriteLine("Ajout de {0:C}", dAddition) .i hn - Dpnosi t { rlAddition) ; // solde rsultant Console,Writeline{"Compte rsultant = {0}",

ba.GetString0);
attend confirmation de L'utilisateur Console,Writeline("Appuyez sur Entre pour terminer.
Console.Read0;

ll

rr

I'

return

0:

Ghapitre 11 : Rendre une classe responsable

247

La fonction llain i. ) cre un compte bancaire puis y dpose 123,454 F, montant qui contient un nombre dcimal de centimes. l'lain O ajoute alors une petite fraction de centinles au solde, et affiche le solde rsultant.

La sortie de ce programme se prsente de la faon suivante Cration d'un objet compte bancaire
Dpt de 123,45 F Compte = lftOOt - 123,45
E

Ajout de 0,00 F Compte rsultant


nnrrrraz nPyuJ arrr u!

#tOOt
nnrrr PUur

123,45
.

F
,

1'n+ro lltL!cc

torm.inor L!urllcr.

C'est l que les utilisateurs commencent se plaindre. Pour moi, je n'arrive pas mettre mes chquiers en accord avec les relevs de compte de ma banque. En fait, je suis trs content si je tombe juste 100 dollars prs, mais il y a des gens qui tiennent absolument ce que leur relev de compte soit bon au centime prs. Apparemment, il y a un bogue dans ce programme. Le problme, bien str, c'est que 123,454 F apparalt comme 123,45 F. Pour viter cela, la banque dcide d'arrondir les clpts et les retraits au centime le plus proche. Si vous dposez 723,454 F, la banque en retire les 0,4 centimes en excs. Comme elle fait la mme chose lorsque la diffrence est en votre faveur, a ne change rien dans la dure.

La manire la plus facile de raliser cela consiste convertir les comptes en deciniai et utiliser la mthode Round0f f O, comme le montre le

programme

Dec

lrnalBankAccount suivant

// II
r

DecimalBankAccount

- cre un compte bancaire

en

utilisant

une

variable decinal pour stocker 1e solde du conpte


Dec

ttsino Svstom' nanespace


t

irralBankAccount
C1ass1

public class
{

-,,r"1.i^ void VUaU LdLrL PUUafL ^+^+)^


t

Main(stringil
rlclrlr\LrrrrlJ

dL>l args)

I I cre un nouveau compte bancaire Console.i,IriteLine("Cration d'un objet conpte bancaire") BankAccount ba = new BankAccount O ;

ba. InitBankAccount ( ) effectue un dpt

//

double dDeposit = 123.454:


Console. l,lriteLine ( "Dpt

de

{0 : Ci "

dDeposit)

2 48

Ouatrime partie : [a programmation oriente objet

ba. Deposit (dDeposit )

ll

.."1dp drr ..\mnt

= {01 ", ba.GetStringO); // et maj.ntenant, ajout d'un trs petit nontant double dAddition : 0.002; Console.l,lriteLine("Ajout de i0:CJ", dAddition)
Console.WriteLine("Compte
;

ba. Deposit
I

(dAddition)

I solde rsultant
ba.GetString0):

Console,I^lriteLine("Compte rsultant

: [0]",

// attend confirmation de 1'utilisateur


Console.WriteLine("Appuyez sur Entre pour terminer.,.")
Console. Read 0
j
;
;

//
I
I

BankRccount

- dfinit

une classe

qui reprsente un compte

simple

public class

BankAccount 1000;

private static i.nt nNextAccountNumber = private int nAccountNumber;

//
I

conserve 1e solde dans une seule vari.able de tvoe decirnal


nBalance; numro de compte

private decimal

I tnit - initialise le compte avec le prochain et un solde de 0


1 .:

-.,1" yuuf
{

rL

fllrLUo r^ i*!rnkACcoUnt 0 ^ ,.^: vvru


;

nAccountNunber = **nNextAccountNumber mBalance = 0;


l

i
{

GetBalance

- retourne le solde courant

public double GetBalance0

return
l

(double)nBalance

//
{

AccountNumber

public

int

GetAccountNumber

return nAccountNumber l public void SetAccountNumber(int


;

nAccountNumber)

this . nAccountNumber =
]

nAccountNumber

// Denosit - 1'orr dnt nositif

esr autoris

public void Deposit(double


{

dAmount)

if

(dAnount

0.0)

Chapitre 11 : Rendre une classe resp0nsable

2 49

// //

arrondit la variable double au centime le plus proche d'effectuer 1e dpt


mTemp

avnt

decimal
mTemp

(decimal)dAmount;

= Decimal,Round(mTerrp, 2);

mBalance += nTenp; ]

// II
{

Wi"thdrar,v

- tout retrait est autoris jusqu' la valeur du solde ; retourne le montant retir
dWithdrar,val)

public decimal Withdraw(decimal

if
{

(mBalance

(= dWithdrawal)

dWithdrawal
]

mBalance;

mBalance -= dllithdrawal; return dl^lithdrawal ;

// GetString - retourne dans une chane les infornations srrr le public string GetString0
{

c.)mnte

string s = String.Format("lf{Ol =
rtlrrn a.

{1:C1",
,

GetAccountNunber ( ) GetBalance 0 ) ; ]
J

J'ai converti toutes les reprsentations internes en valeur dec rnat, qui est dans tous les cas un type mieux adapt que dcubl au traitement de solde de compte bancaire. La mthode Deposit (,r utilise maintenant la fonction Decimal. Found O pour arrondir Ie montant des dpts au centime le plus proche avant d'effectuer le dpt correspondant. La sortie de ce programme est maintenant ce que nous sommes en droit

d'attendre

Cration d'un objet compte bancaire


conpte - lltoot = r23,45 Ajout de 0,00 F
Appuyez

Dpt de 123,45

Conpte rsultant = #toot

= r23,45 E
.
.

sur Entre pour terniner.

50

Ouatrime partie : La programmation oriente

ob

jet

Et alors )
On pourrait toujours dire que j'aurais d crire ds le dpart le programme BankAccount en utilisant le type decimal pour les donnes saisies, et je serais probablement cl'accord. Mais ce n'est pas si vident. Bien des applications ont t crites en utilisant le type double comme moyen de stockage. Un problme s'est produit, mais la classe EankAccor-rnt tait capable de le rsoudre de faon interne sans ncessiter de modifications I'application elle.mme.

Dans ce cas, la seule fonction affecte a t ltlain O , mais les effets auraient pu s'tendre des centaines d'autres fonctions accdant aux comptes bancaires, et ces fonctions auraient pu se trouver dans des dizaines de modules. Si la correction avait t faite I'intrieur de la classe Ba nhAc c c, Lri-i1-, aucune de ces fonctions n'aurait dt tre modifie. Mais cela n'aurait pas t possible si les membres internes de la classe avaient t exposs des fonctions externes.

=O,

#\

n,;iilL::.'"""u

Les moclifications internes une classe ncessitent quand mme toujours diverses portions de code, mme si celles-ci n'ont pas

Dfnir des proprits de classe


Les mthodes GetX O et SetX O des diffrentes versions du programme Bank-Acco-rnt sont appeles fonctions d'occs, ou, plus simplement, eccesseurs. Bien qu'en thorie elles soient synonymes de bonnes habitudes de programmation, les fonctions d'accs peuvent en pratique devenir un peu maladroites. Par exemple, Ie code suivant est ncessaire pour incrmenter nAc c ounrl'Junber de 1.
SetAccountNun,ber(GetAccountNumber11

1)

C# dfinit une structure nomme une proprit qui permet d'utiliser beaucoup plus facilement les fonctions d'accs. Le fragment de code

suivant dfinit une proprit de lecture-criture, AccounrNumber:


public
t

int

AccountNumber

set Ireturn nAccountNumber; --_..------,,J set {nAccountNunber = value: }

Chapitre 11 : Rendre une classe responsable

5I

La section get est implmente chaque fois que la proprit est lue, alors que la section set est invoque lors de l'criture. La proprit Balance cidessous est en lecture seule, car seule la section set est dfinie : public double
{

Balance

o1
t

return
l
]

(double)mBalance

I'utilisation, ces proprits apparaissent de la faon suivante


BankAccount ba

new BankAccount

//
I

stocke 1a proprit numro de compte

ba.AccountNumber

1001

Console.writeLine("lf[0] = {l :c}",
ba.AccountNumber, ba.Balance)
;

I tit les deux proprits

Les proprits Accountliumber et Balance ressemblent beaucoup des

membres donne publics, par leur prsentation comme par leur utilisation. Toutefois, les proprits permettent la classe de protger ses membres internes (tsa1ance est une proprit en lecture seule) et de masquer leur implmentation. Remarquez que Balance effectue une conversion. Elle aurait pu aussi excuter les calculs les plus abondants.
<$-_--

Ilof l L-.t
v

xParconvention(cen'estpaSuneobligationdeC#),lenomd'uneproprit
commence par une lettre majuscule. Une proprit n'est pas ncessairement dpourvue d'effet. Le compilateur C# peut optimiser un simple accesseur pour qu'il ne gnre pas plus de code machine que I'accs direct un membre donne. C'est important, pas seulement pour I'application, mais aussi pour C# lui-mme. Toute la bibliothque C# fait un usage abondant des proprits.

Proprits staques
Un membre donne statique (de classe) peut tre expos par I'intermdiaire d'une proprit statique, comme le montre I'exemple simple suivant public class
I

BankAccount

252

Ouatrime partie:La programmation oriente objet

private static int nNextAccountNumber = public static int NextAccountNunber


{

1000;

get

{return

nNextAecountNunber

il
]

La proprit l'lext,\ccr-,untlir:rnber est accessible par la classe, car ce n'est pas la proprit d'un objet particulier :
I I Lit la proprit numro de conpte int nValue = BankAccount.NextAccountNumber;

Proprits a(lec effets de bord


Une opration get peut excuter plus cle travail que la simple extraction de la proprit associe : public static

I
I I
1

int

AccountNumber

I la proprit et prpare I "*rr^it I'extraction de la suivante


{

get

return

**nNextAccountNumber

Cette proprit incrmente le membre statique numro de compte avant de retourner le rsultat. Toutefois, ce n'est sans doute pas une bonne ide, car I'utilisateur de la proprit n'a aucune ide de ce qui se passe en dehors de la lecture de la proprit.

Tout comme les fonctions accesseurs qu'elles imitent, les proprits ne doivent pas changer l'tat d'une classe.

u0nner un bon dpart os objets : les c0nstructeurs


Contrler I'accs une classe n'est que la nroiti du problme. Un objet a besoin d'un bon dpart dans lavie s'il veut qrandir. IJr-re classe peut

fournir une mthode d'initialisation, appele par I'application pour faire

Chapitre 11 : Rendre une classe responsable

53

dmarrer les choses, mais que se passe-t-il si I'application oublie d'appeler la fonction ? La classe commence avec de mauvaises initialisations, et la situation ne peut pas s'amliorer ensuite. Si vous voulez tenir la classe pour responsable de ce qu'elle fait, vous devez commencer par lui assurer un bon dmarrage.
C# rsout le problme en appelant la fonction d'initialisation pour vous.

Par exemple
MyObject mo

new My0bject0;

En d'autres termes, non seulement cette instruction va chercher un objet

dans une zone particulire de la mmoire, mais elle I'initialise en appelant la fonction d'initiali.sation.
Ne confondez pas les termes classe eI objet. Chien est une classe. Mon chien S c o c. r e r est un obiet de la classe Chi en.

Le constructeur fourn par C#


C# se dbrouille trs bien pour savoir si une variable a t initialise. Il ne vous permettra pas d'utiliser une variable non initialise. Par exemple, le code suivant gnre une erreur la compilation :

public static void Main(string[] args)


{

int
]

n;

double d;

double dCalculatedValue = n

d:

C# sait que ni n ni d n'ont reu une valeur, et ne leur permet pas d'tre utilises dans I'expression. La compilation de ce petit programme gnre les erreurs de compilation suivantes :

Utilisation d'une variable Utilisation d'une variable

1oca1e non assigne 1oca1e non assigne

t-l ll

tdt

Par comparaison, C# offre un constructeur par dfaut qui initialise le contenu d'un objet 0 pour une variable intrinsque, f alse pour une

25

0uatrime partie : La programmation oriente objer

variable boolenne, et nLr-11 pour une rfrence d'objet. Voyez I'exemple de programme suivant :
,1d; urrr ^^ Q*a+^* J) Llt, '

namespace DecimalBankAccount
{

public class Ciassl


i

public stat j.c void


t

Maj_n(string

[]

args)

/l courmence par crer un objet My0bject local0bject = new My0bject0; Console.i^IriteLine("1oca1Object.n est {0J", localObject.n) if (localObiect.next0biect
(
L

== null)
;

Console.i^lriteline ("local0bjecr.nextObject est null")


l I

I attend confirnation de 1'utilisateur Console.!'IriteLine("Appuyez sur Entre pour terminer. . .")


Console.
Read

l l

public class
t

MyObject

internal int n; internal MyObject next0bject


l l

e c r_, eui contient une variable L cle type int, et une rfrence un objet, nertOb i e c t. La fonction i"iair i r cre un objet l1',,Crb,ject, et affiche Ie contenu initial de rr et de rextrjb',,, r.

Ce programme dfinit une classe MyOb i

Ce programme

produit la sortie suivante


0

1oca10bject,n est

1^^^1^L.'^^+ f vLorvuJ EL L . -^--t0hipct ilE - _ _ J _ _ Annttrroz qttr Entro nnrrr

p<t

nrr'1

forminor

Lorsque I'objet est cr, C# excute ur-r petit morceau de code pour I'initialiser, ainsi que ses membres. Livrs eux-mlnes, les membres donne,.\:i.,'l:r,ject.i-, et le-xtr-;L e.i ne contienclraient que de.s valellrs alatoires. sans signification.
Le code qui initialise les objets lorsqu'ils sont crs s'appelle le constructeur.

Ghapitre 11 : Bendre une classe responsable

25

Le constructeur par dfaut


C# garantit qu'un objet commence sa vie dans un tat connu : rien que des zros. Toutefois, pour de nombreuses classes (sans doute la plupart des classes), ce n'est pas un tat valide. Considrezla classe Ba:rr,.-r,:.r.r iirr: que nous avons dj vue dans ce chapitre :

public class
{

BankAccount

int

nAecountNumber;

double dBalance;

IL

autres

membres

Bien qu'un solde initial de 0 soit acceptable, un numro de compte gal 0 n'est certainement pas un numro de compte valide.
La classe BankAccount contient la mthode IniiEa:r.r.,ci-ri-rr-.r-,1- ir POuf initialiser I'objet. Toutefois, cette solution fait peser une responsabilit trop lourde sur I'application elle-mme. Si I'application n'invoque pas la fonction InitBankAccount O, les mthodes de compte bancaire ne fonctionneront sans doute pas, sans que ce soit de leur faute. Il est prfrable qu'une classe ne dpende pas de fonctions externes pour mettre ses objet dans un tat valide.

En rponse ce problme, la classe peut fournir une fonction spciale qui sera automatiquement appele par C# lors de la cration de I'objet : le constructeur de classe. Celui-ci aurait pu tre nomm rnit ( ) , Starr ( ) , ou Create O, pourvu que ce nom vous plaise, mais le constructeur porte le nom de la classe. Aussi, le constructeur de la classe tsankAcccunt- se

prsente de la faon suivante


public
{

int Main(stringil args)


= new BankAccount O ;

BankAccount ba

l public class
{

BankAccount

II /i

Ies numros de compte coruencent 1000 squentiellement partir de 1

et

augmentent

static int

| | nat inrrr

nNextAccountNunber = 1000: 'l p nrrmrn rlo nnmnf o at 'l o cn1rl o nnrrr nhrnrro nhipt

int

nAccountNumber:

double dBalance:

256

Ouatrime partie : La programmation oriente objet

/ constructeur BankAccount
BankAccount
()

public
L

nAccountNunber

dBalance

: 0.0;

= *tnNextAccountNumber

l
duL!c -^-L-^^ llElllu!cD.

Le contenu du constructeur Bankcccunt est le mme que celui de la mthode oriqinale Ir r*: . . ' ). Toutefois, une mthode n'est ni dclare nl utilise de la rnme manire

t/ ,/ t/

Le constructeur porte le mme nom que la classe. Le constrtrcteur n'a pas de type retourn, mme pas void.
l'{ai
rr r i n'a pas besoin cl'invoquer une fonction supplmentaire pour initialiser I'objet lorsqu'il est cr.

Construisons quelque chose


Essayez donc un de ces constructeurs.Yoyez le programme D emo r-r s t r a r'- e D e f a l i t C o n s t r u c t o Tr Suivant :

//

DenonstrateDefaultConstructor

des constructeurs par dfaut ; cre une classe avec un constructeur, puis excute quelques scnarios
ttcino *- -" Svctpm.
namespce DemonstrateDefaultConstructor
I t

nontre

le

fonctionnenent

il
{

//

Hy0bject

cre une classe avec un constructeur bruyant

et rrn

oh i

et i nterne

publie class My0bject

// ce membre est
I

une proprit de
a+a+i nflhi LLlUVUJ
.

ctnt.i Mrrfltharh.inn+ LqLrL n rrJvLrrcrvuJtrLL

ce membre

}ly0ther0bject0; est une proprit de 1'objet


new
drrnnmi nh i

la

classe

Mrr0thorhiant

public
t

My0bject

0
I

Console.lJriteline ("Dmarrage du constructeur ilyObject") dynanicObj : new My0ther0bject0; Console.llriteline (r'Fin du construeteur MyObject") ;

Chapitre 11 : Rendre une classe resp0nsable

57

Mrrfltherflh jont ^1... r'rjvLrrrvuJLL ^"h1i ^ UId PUUTIL


L

/i II

My0ther0bject

- cette

classe a aussi un constructeuf bruyant

nais pas de nembres j.nternes


nrrhi i n lv{rr0rLarhi ont
{

()

Console.Writeline("Construction de My0therObject en cours")


l
l

public class Classi


t

public static void Main(string[] args)


I
t

Console. trlriteLine ("Dmarrage de Main ( ) " ) I cr.e un ob jet new My0bject0; My0bject localObject

attend confirmation de 1'utilisateur Console.l{riteLine("Appuyez sur Entre pour terminer.. .r') ;


Console.Read0;
J

/l

L'excution de ce programme gnre la sortie suivante


Dmarrage de Main0

tonstruetion de My0ther0bject en cours


Dmarrage du constructeur MyObject Construction de Hy0ther0bject en cours

Fin du constructeur My0bject


Appuyez

sur Entre pour terminer.

Reconstruisons ce qui vient de se produire

1. 2. 3.

Le programme dmarre, et Main ( ) affiche le message initial. Main ( ) cre un localObject, de type My0b j ect.

ject contient un membre statique, static0b j, de la classe MyOther0bject. Tous les membres donne statiques sont crs avant que le premier MyOb ject soit construit. Dans ce cas, C# remplit static0b j avec un Ml,'Other0b j ect nouvellement cr, avant de passer le contrle au constructeur MyOb j ect. Cette tape correspond au second message.
|1y0b

258

Ouatrime partie: La programmation oriente objet

4.

Le constructeur d l"i-r'ObJ ec t reoit le contrle. Il affiche son premier message : Dna rrage dLi colrstrr-icteur MyCb i ect. Le constructeur l"lyOir r'ect cre un objet cle la classe l'1'y0tirer0b j ect en utilisant I'oprateur new. et affiche le deuxime message du

5.

con-structeur
6. 7.

I"h'Ot he rOb

J1

ec

Le contrle revient au constructeur i'll-,'lli', 1eci, eui retourne i"lain O .

Mission accorlplie

Ercuter le constructeur partir du dbogueur


Pour avoir encore un peu plus cle mrite, excutez maintenant le mme programme partir du dbogueur :

l. 2.

Gnrez nouveau le prograrnme

slectionnez Gnrer/Gnrer.

Avant de commencer excuter le programme partir du dbogueur, dfinissez un point d'arrt I'appel Conso ie . i'iri-i eLine () dans le constmcteur i4y0therOb j ect.
Pour dfinir un point cl'arrt, clicluez <'lans la barre grise verticale qui constitue le bord gauche cle l;r ferrtre de code, en regard de la ligne pour laquelle vous voulez clfinir un point d'arrt.
La Figure
1

l.l

montre I'affichage avec le point d'arrt.

3.

Au lieu de slectionner Dtloguer/Dmarrer, selectionnez Dboguer/ Pas pas dtaill (ou, mieux encore, appuyez sur la touche Fl l). Vos fentres doivent s'agiter un peu pendant quelques secondes, puis I'appel Console.l',r j telin-o O doit apparaltre sur fond jaune.

4.

Appuyez nouveau sur la touche Fl l. Votre affichage doit maintenant ressembler ce que montre la Figure 11.2.

D.

Slectionnez Dboguer/Dmarrer ou appuyez sur F5, et le programme s'excute jusqu'au point d'arrt dans MyOtherCb_jecr, comme le montre la ligne en surbrillance dans la Figure 11.3. Appuyez encore deux fois sur la touche Fl l, et vous tes de nouveau au dbut du constrrcteur l{.,,tlb i ec+,, comme le montre la Figure I1.4.

6.

Ghapitre 11 : Rendre une classe responsable

5g

Figure 11.1:

sur fond
I Fr rtl orll

La ligne en surbrillance

rouge dans le constructeur


rt

indique la prsence d'un point


d arrt.

F
Frqrrrrffle

w *. :. .';
Ii'r-] t,tr

,,": "-..: rle,': fr) v


ThfEad :1, ).:,,

- r,"i.i.., a"

t,4

ag ., '..,

Au.g.&&

4
- -

,:rrrL',rLet,i

[];trl .::,in! nrnr:r ?

Ffam de Frle t,rrrnslr,ilet)Birull,,,:n5lrutl::r

._).
flassl.rs
j

.::,i

tfi

(i.1
J
|

,tr.
1'1,:ln

r,ehu,] -

rS

strir,l[] rrq:l

,l :J
*1
i.,

-:'
,$ + u' .r L.11]f5:! StauE.jrlt: _,:n!t.tE:] DenronstrateDefaultf onstructor 4 FfrETr r,ts: 1!] A::enrbl'lnf,:,,:r 1! Clasrl.cs
|

Figure 11.2: L'affichage

dbogueur
de Visual

du

Exlortur d. AutEmtiqLe
l'+om I'rr :1,-rt'tttl: !,11uf

x J

Pile de5

Fpls ttrr

I Lar

studio, juste avant de passer au

ntl

(:r

I er']n5l:rEtEDetrlll:i:r,n5lrU:l:r{,e:,el[,trf]rrslr ll:e[]Ef,3U l:r-LrrrslfUlof

:#

constructeur.

nAuromitiquel
L gnertirn a

tr

ffipiluout
Ln 38

rBssi

Col

tr;, ir

f l ::,:t.t

a;

260

Ouatrime partie:La programmation oriente obiet

ficfrier Edilir-,n Atfi{h,3qe qrjet rarrer Qhoqrer gutils ' : He. . - -."".';)- '.:. ? w i: ,' ;-'
"r]1
F

r,t)t amr

ll!:l
.,...,,
I

t,frr: n5l:r

i/:et, - lln ed Iir!],: i:n: rrn,


,t:t.

:.

tflassl.cs

:;l;:;I 't

,:::r,

, '

,tr

r'rt,r,l

,!a
Er

t,L,rt!r de s,:|:lron: - trenl:r;tr


-

,.. f

,,

:i
---1

:j

i' .r' -'_: . . :. its , ' ,_ .. ,::. .p DenronstrateDefaultf onstructor

:i

_,lr_-:

I.TIrr--Llr_:

"'-

I_=f

r.l r t:r|l

l1'rr

le constructeur
l"h;t'}|horlhioe
l

Figure 1 1,3 : Le contrle passe 0ans

!riJ:1.r,: r:lti:

'.::r i ll.ir[

.-!::]r':j[]
FluaLr.:1:{=

''- I:: :' LF.'Ir r: -L -r.= | "l

-il
Autrntiqu

avant de se diriger vers

l!fi l+i lfi:

trleut itnarjlrllaIal]rll :,rjlrr,:l:r l1

T|pe

S;r *}

:,r:frr [,r,:rr:|:.

le constructeur

l,Ii'Object.

Fr,3m d

Frie

Iaf r]rr!lrnl[ii]ll, t,

,rTr!ff!:Lrr

'

['etu,1

:J
--J

:l

Figure 11.4 : Le construc-

teur
M-,'Obi ec

t
utontigue
l,J,ln
TYpe
-

reoit le contrle une fois que


l'o
b

jet

+il
['f-,]rrilr ltEIrf,:!li]:itrr!ff rJ:l:r,r ll,/:-t,jE,:ll trnr:rrslr

statique
Itf.rlthonlh-i.e

F l d:: FF,l!

tr'rn
( ttn,rr:Lr:l"t'rl.irfi,:rrlff,l:l,t,t 'elIif:f5]:tt|[,i:,t]:,_,:ir:l;!:]:r,l# 'rriir r.i-f i i l::f [nr:riifitE[i:r]ii ':,r!lftl[i[]lll_:fst:rtr:r':1,:#

tu-ol
] ]

1f,,.

t
L4l ,rlhtrqui |
L gnerarln

n t6
c0nstru it,
$rsl l# reusii

)
EJ
Fre des ar,,ets

r.tl
Ln
t

h
{-,rl

,t

i-l',

Chapitre 11 : Rendre une classe responsable

261

t. Continuez d'appuyer sur la touche Fl I pour avancer dans le programme.


N'oubliez pas de continuer aprs la commande [],.-,i-, s o L e . r. .: :l Vous devrez appuyer sur Entre dans la fentre du programrle avant de pouvoir continuer avancer pas pas dans la fentre du dbogueur Visual Studio.
.

lnitialser un objet drectement : le constructeur par dfaut


Vous pourriez croire que presque n'importe quelle classe peut avoir un constructeur par dfaut d'un certain type, et dans une certaine mesure. vous avez raison. Toutefois, C# vous permet d'initialiser directenrent un membre donne en utilisant une instruction d'initialisation. Ainsi, j'aurais pu crire la classe Bar,kAccount de Ia faon suivante
publie class
t
BankAccount
:

//
| |

l-es nunros de compte commencent I I g9uElrLftrarclucllL ^t ^,,^^.i ^1|pmpnf uc o nert.i yq! Lrr r dp 1

1000

et

augmentent

static i.nt nNextAccountNunber = 1000; ll net jour 1e numro de compte et 1e solde pour

chaque objet

int

nAccountNumber

= **nNextAccountNunber

double dBalance = 0.0t

ll

autres

memDres.

nAccountNumber t dBalance se voient assigner une valeur dans leur dclaration, ce qui a le mme effet qu'un constructeur. Soyons trs clair sur ce qui va se passer exactement. Vous pensez peuttre que cette instruction assigne directement 0.0 cga Lance. Mais dBalance n'existe qu'en tant que partie d'un objet. Aussi, I'assignation n'est-elle pas excute avant qu'un objet BankAccount soit cr. En fait, cette assignation est excute chaque fois qu'un tel objet est cr.
C# rcolte toutes les instructions d'initialisation qui apparaissent dans les

dclarations de la classe, et les runit dans un constructeur initial.

262

Ouatrime partie : La programmation oriente objet

Les instructions d'initialisation sont excutes dans I'ordre o elles se prsentent dans les dclarations de la classe. Si C# rencontre des initialisations et un constructeur. les initialisations sont excutes avant le corps du constructeur.

Uoqons comment se des initialisations

fait la construction

auec

Dplacez maintenant I'appel rrew l"f.,'OtherObiect O du constructeur l1_1,f;5 ject la dclarettion elle-mme, comme ci-dessous, puis excutez nouveau le progranlnle :
nrrhf
{

ic class Mv0hiect
une proprit de
ctetirnhi

// ce membre est // ce membre


MvthprChiont

la

classe

<tnt'i n Mvthorhiont

= new My0ther0bject0; est une proprit de 1'objet


rlrrn-aminOhi = now Mrrthorh'iont vvJ
L \ /

f)

.
t

public
{

My0bj

ect

()

Console.tr.lriteline ("Dmarrage du constructeur My0bject") Console.Writeline{"Fi.n du constructeur My0bject") ;


)

Le programme modifi donne la sortie suivante


Dmarrage de Main0

Construction de My0therObject en cours Construction de My0therObject en cours Dmarrage du constructeur My0bject Fin du constructeur My0bject Appuyez sur Entre pour terminer...

Vous trouverez le programme complet sur le site Web, sous le nom remarquable de lenon s t r at e Con s t ruc t o rlv'i th I nlt i aliz e r .

Chapitre 11 : Rendre une classe responsable

2 63

Surcharger le constructeur
On peut surcharger un constructeur, tout comme n'importe quelle autre

mthode.
Surcharger une fonction signifie dfinir deux fonctions portant le mme nom, mais ayant des arguments diffrents. Pour en savoir plus, voyez le

Chapitre

7.

Imaginez que vous vouliez offrir deux manires de crer un Bank-Account une avec un solde zro, comme le mien la plupart du temps, et une autre avec une valeur initiale :

f\

#"*$3 ffir] I t"'7


,

// tl

BankAccountWithMultipleConstructors

ll
t

fournit notre compte


System;

bancaire

un certain nonbre de construeteurs, un pour chaque occasion

using

nmespac

e BankAccountWithMult

ip1 eConstructors

using Systen; public class


t

C1ass1

public static
{

int Main(stringil args)


initiales
valides

cre un conpte bancaire avec des valeurs

BanhAccount ba1 ankAccount ba? BankAccount ba3

nerrr Bankccount

0
;

Console.l,rlriteline (bal . GetString(

))

= new BankAccount(100);

Console.lririteline (ba2 . GetString 0 ) ; = new BankAccount(Iz3|, 200); Console,Writeline (ba3, GetString 0 ) ; // attend confirmation de 1'utilisateur Console.i,ilriteLine("Appuyez sur Entre pour terminer...")

Console.Read0;

return
l
]

0;

//
t

public class

BankAccount simule un BankAccount

sinple conpte bancaire

I I les numros de compte coffrencent 1000 et ^^-+i - ,l^ // DLyusrrLlcrrulclIL souentiel I ^-^-+ a uE 1 PaL Lal static i.nt nNextAccountNumber = 1000;
t t

augmentent

264

ouatrime partie: La programmarion oriente objer

Il +4^^+: .'^..- re numro // r:.enr a Jour de compte et 1e solde


1

i.nt

nAccountNunber;

double dBalance;

// fournit
public
{

une srie de constructeurs selon 1es besoins


()

BankAccount

nAccountNurnber

#nNextAccountNurnber

dBalance = 0.0;

public
{

BankAccount

(double dlnitialBalance)
dfaut

// reprend une partie du code du constructeur par


nAccountNumber

if (dlnitialBalance (
{

= #nNextAccountNumber ; /l et nai"ntenant, le code propre ce constructeur l/ commence avec 1e solde initial, condition qu'i1 soit positif
0)

dlnitialBalance =
l
dBalance l

0;

: dlnitialBalance; (int nlnitialAccountNumber,


double dlnitialBalance)

public
{

BankAccount

II if t

ignore 1es numros de compte ngatifs (nlnitialAccountNumber (= 0)


;

nlnitialAccountNumber = *tnNextAccountNumber
]

nAccountNumber

= nlnitialAccountNumber

// comnence avec le solde initial, condition qu'i1 soit positif if (dInitialBalance ( o)


{

dTnitialBalance
]

0;

dBalance = dlnitialBalance;
I

- 11! public string GetString0

return String.Format("#{O} = {l:N}",


nAccountNunber, dBalance)
]
;

Ghapitre 11 : Rendre une classe responsable

26 5

Cette version du programme BankAccountWithMuitipleCcnstruciors

comporte trois constructeurs

t/ t/ t/

Le premier constructeur assigne un numro de compte, et dfinit un solde gal 0. Le deuxime constructeur assigne un numro de compte, mais initialise le solde du compte avec une valeur positive. Les valeurs de solde ngatives sont ignores.
Le troisime constructeur permet I'utilisateur de spcifier un

numro de compte positif et un solde positif.

trois constructeurs, Main ( ) cre un compte bancaire diffrent, et affiche les objets crs. La sortie de I'excution de ce programme se prsente de la faon suivante :
En utilisant chacun de ces

/ltoot =

o.oo

llnaz: ioo.oo lltzlt = 2oo.oo


nnrrrroz crrr T'ntr6o nnrrr torminpr

Dans le monde rel, une classe effectuerait beaucoup plus de tests sur les paramtres d'entre donns au constructeur, pour vrifier leur validit.
Ce sont les mmes rgles qui s'appliquent aux fonctions vous permettant de diffrencier les constructeurs. Le premier objet tre construit par Main O, bai, est cr sans argument et est donc orient vers le constructeur par dfaut pour y recevoir le numro de compte par dfaut et un solde gal zro. Le deuxime compte, ba2, est envoy au constructeur BankAccount (double) pour y recevoir le numro de compte suivant, mais il est cr avec un solde initial de 100. Le troisime, ba3, reoit un traitement complet, BankAc c ount ( int , ri oub 1e ) , avec son propre numro de compte et un solde initial.

Ettter les duplicatnns entre les cnnstructeurs


Tout comme un scnario de srie tlvise, les trois constructeurs de BankAcc ounr comportent une proportion significative de duplications. Comme on peut I'imaginer, la situation serait bien pire dans des classes du monde rel qui pourraient avoir de nombreux constructeurs, et surtout bien plus de donnes initialiser. De plus, les tests effectuer sur les donnes saisies peuvent avoir une plus grande importance dans une classe

)t

266

Ouatrime partie : La programmation oriente obiet

du monde rel que sur une page Web. La duplication cle rgles commerciales est la fois fastidieuse et source d'erreurs. Les vrifications peuvent facilemer-rt se trouver en dsaccorcl. Par exemple, ch-r fait cl'une simple erreur de codage, deux constructeurs peuvent appliquer atr solde cles rgles diffrentes. De telles erreurs sont trs difficiles retrouver.
Vous prfreriez peut-tre qu'un constructeur en appelle urt autre. rnais les constructeurs ne sont pas des fonctions : on ne peut pas les appeler. Toutefois, vous pouvez crer une alternative sous la forrne cl'une fonction qui effectue la vritable construction, et lui passer le contrle, comrne le mOntre le programme Salrkr.cccJntCorrst r',-.i i ir: s:r,Fr,/':rr. , ';lt ci-dessous :

// II II II

BankAccountContructorsAndFunction

fournit notre compte

bancaire

un certain nonbre de constructeurs, un pour chaque occasion


o

name spac {
"^.; -^ uDrrr6

using Systen; e BankAc c ount Cont ruct


Q"n+an. ujLru,

r sAndFunct

ion

publj-c class
{

C1ass1

public static int Main(stringIJ args)


{

I I cre un compte bancaire avec des valeurs BankAccount ba1 = new BankAccount 0 ;

initiales valides

Console.ItIriteline (bal.GetString 0 ) ;
BankAccount ba2

= new BankAccount
(ba2 . GetString

100)

Console.Writeline

0);

BankAccount ba3 = new BankAccount(i234, 200); Console. l./riteLine (ba3 , GetString 0 ) ;

// attend confirmation de 1'utilisateur


Console.WriteLine("Appuyez sur Entre pour terniner. Console.Read0;

..")

return
j l

0;

//
{

BankAccount

- simule un simple

compte bancaire

public class

BankAccount

I I |es numros de compte commencent 1000 et augnentent l/ squentiellement partir de 1 static int nNextAccountNumber = 1000; // tient jour le numro de compte et le solde int nAccountNumber;

double dBalance; // place tout 1e vritable code

d'initiaiisation

Chapitre

tl : Rendre une classe responsable 267

I
{

public

dans une fonctj.on conventionnelle spare BankAccount0


;

.
]
{

Init(**nAceountNumber, 0'0)

public

BankAccount

(double dlnitialBalance)
;

Tnit (**nAccountNumber, dlnitialBalance)


]

ll c'est 1e constructeur le nlrrs sneifinrre orii fait I I 7e vritable travail"


public
{

tout

SankAccount

(int

nlnitialAecountNunber, double dIni.tialBalance )


;

Init (nlni.tialAccountNumber, dlnitialBalance)


nrirrato,,nid lnil (int nlnitialAccountNUrnber,
double dnitialSalance)
t nAccountNunber

= nlnitialAccountNunber : 1e solde initial, condition qu'i1 soit positif 1r (0ln1t141alance \ u/

/l
{

comnence avec
I rt

I F

^\

dlnitialBalance =
]

0:

dBalance = dlnitialBalance;
]

public string GetString0


t

return String.Format("{{0i = {l :NJ",


nAccountNunber, dBalance) ]
;

i
]

Dans cet exemple, c'est la mthode

Init O qui fait le vritable travail de

construction. Toutefois, cette approche n'est pas absolument irrprochable pour plusieurs raisons, dont I'appel d'une mthode d'un objet avant que celui-ci ait t entirement construit n'est pas la moindre. C'est une chose trs dangereuse.
Heureusement, ce n'est pas ncessaire. Un constructeur peut se rfrer un autre avec une variante de I'utilisation du mot-cl tlris :

// II II

SankAccountContructorsAndThis

fourni.t notre compte bancaire un certain nombre de constructeurs.

268

Ouatrime partie : La programmation oriente objet

un Dour chaoue occasion


ti^.i-^

ublrrB

Q"a+an'

oy b Ltrnl,

nare t

space BankAce ountC0ntructo rsAndhis


C,"^+^-, Jy Lcnr,

,,^;-^ urll

public class Classl


{

public static int Main(stringI args)


{

cre un conpte bancaire avec des valeurs


;

initiales

valides

= new BankAccount 0 ; Console.l,iriteline (bal.GetString0 ) BankAccount ba2 = new BankAccount(100);


BankAccount ba1

Console.Writeline (ba2, GetString


BankAccount ba3

= new BankAccountQ234, 20);

Console.l,Iriteline (ba3 .GetString 0 ) ; // attend confirnation de 1'utilisateur Console. l^lriteline ( "Appuyez sur Entre pour terminer.
Console. Read 0 return 0:
;

..")i

l
1

I I nankeccount - simule un simple compte bancaire publj-c class BankAccount

I les numros de cornpte commencent 1000 et augmentent // squentiellement partir de 1 static int nNextAccountNumber = 1000; ll tient iour ie numro de comnte et le solde int nAccountNumber;
I

double dBalance; /l invoque 1e constructeur spcifique en fournissant I I des valeurs par dfaut pour les arguments manqunts

nrrhlin

Ranlrnn9111
lerrrrr.uvur.!\/

0 : thiS(0, 0) tl
. srr+v\v, w/

public BankAccount(double dlnitialBalance) : this (0, dlnitialBalance) {} // c'est le constructeur le plus spcifJ.que quL fait tout
I

I te vritable travail
BankAccount

public
t
II // if
t
I

(int

nlnitialAccountNumber,
1e dlnitialBalance)

doub

indique que nous devons

ignore 1es numros de compte ngatifs ; un numro de cornpte nu1 utiliser 1e prochain nunro disponible (nlnitialAccountNurnber (= 0)
;

nlnitialAccountNumber = **nNextAccountNunber
l

nAccountNunber

= nlnitialAccountNumber

Chapitre 11 : Rendre une classe responsable

269

// if
{

commence

avec

(dTnitialBalance \v-.r+!rs,!**-nce dTnitialBalance

le solde initial, condition gu'il soit positif

( \

0) uJ

0;

j dBalance = dlnitialBalance;

public stri.ng GetString0


t

return String.Fornar("{ltOt = [].:N]",


nAccountNumber, dBalance) ]
J

Cette version de BankAccount contient les trois constructeurs que nous avons vus dans la version prcdente, mais au lieu de rpter les mmes tests dans chaque constructeur, les deux constructeurs les plus simples invoquent le troisime (le plus souple), qui fournit des valeurs par dfaut pour les arguments manquants. La cration d'un objet en utilisant le constructeur par dfaut invoque le constructeur BankAccount ( ) :
BankAccount

bai =

new BankAccount

Le constructeur BankAccount O donne immdiatement le contrle au

constructeur BankAccount
dfaut 0 et 0.0
:

(int,

Couble), en lui passant les valeurs par

public BankAccount0

: this(0, 0) tl

Le tout-puissant troisime constructeur a t modifi pour rechercher un numro de compte nul et le remplacer par un numro valide.
Le contrle est restitu au constructeur par dfaut une fois que le constructeur invoqu a termin son travail. Dans ce cas, le corps du constructeur

par dfaut est vide.


La cration d'un compte bancaire avec un solde mais un numro de compte par dfaut passe par le chemin suivant : public BankAccount(double d)

: this(0, d) {l

270

Ouatrime partie : La programmation oriente objet

ue a(lare de
Si vous

ses

objets

On ne peut pas construire un objet sans un constructeur correspondant. dfinissezvolre propre constructeur, C# retire le sien. En combinant ces deux aspects, vous pouvez crer une classe qui ne peut tre instancie que localement. Par exemple, seule une mthode dfinie dans le mme espace de nom que BankAccount peut crer un objet BankAccount avec Ie constructeur dclar comme internal (pour en savoir plus sur les espaces de nom, reportez-vous au Chapitre l6) :

ll
{

Ba*.kAccount

simule un simple compte bancaire

public class
I

BankAccount

I Les numros de compte connencent 1000 et augnentent // sarrentieilement oartir de i static int nNextAccountNumber = 1000: ll tient jour 1e nunro ds ssmnte ot le snldp int nAccountNunber;
double dBalance: // invoque 1e constructeur spcifique en fournissant I I des valeurs par dfaut pour 1es arguments manquants

internal
{

BankAccount

()

nAccountNumber dBalance = 0:

= *tnNextAccountNumber

public string GetString0


{

return String.Format
] ]

("11{Ol

{1 ;N1",
;

nAccountNunber, dBalance)

Ghapitre 12

Acceptez-vous I'h ritage ?


Dans ce chapitre :

Dfinir un nouveau type de classe. lrlus fondamental. Faire la diffrence entre "EST UN" et "A UN".
Changer la classe d'un objet.

Construire des membres statiques, clu de classe. Inclure des constructeurs dan.s une hirarchie cl'hritage. Invoquer spcifiquement le constructeur cle la classe de base.

trois principes : la possibilit (l'encapsulation), la possibilit de contrler I'accs aux objets d'hriter d'autres classes, et la possibilit de rpondre de faon approprie (le polymorphisme).
a programmation oriente objet repose sur

L'hritage est une notion ordinaire. Je suis un tre humain, sauf I'instant o je sors du sontmeil. J'hrite de certaimes proprits de la classe Humain, comme ma capacit de dialoguer (plus ou ntoins), et ma dpendance l'gard de I'air, de la nourriture et de boissclns contenant beaucoup de cafine. La classe Hunain hrite sa clpenclance l'garcl de I'air, de I'eau et de Ia nourriture de la classe llammif re, qui, elle-mnte, hrite de la classe Animal.
La capacit cle transmettre cles proprits un "hritier" est un aspect trs puissant de la programmation oriente objet. E,lle permet de dcrire les

choses d'une manire conomique. Par exemple, si mon fils me demande : "Qu'est-ce que c'est un canard ?" Je peux rpondre : "C'est un oiseau qui fait coin coin." En dpit cle ce que vous pouvez penser, cette rponse contient une quantit considrable cl'informations. Mon fils sait ce qu'est un oiseau, et il sait maintenant qu'utr canard possde toutes les proprits qu'il connalt des oiseaux, plus la proprit supplmentaire "faire coin coin".

272

Quatrime partie : La programmation oriente obiet

Les langages orients objet expriment cette relation d'hritage en permettant une classe d'hriter d'une autre. C'est cette caractristique qui permet aux langages orients objet de produire des modles Plus Proches du monde rel que les langages qui ne disposent pas du PrinciPe cle

I'hritage.

Hriter d'une classe


Dans I'exemple IttherltanceExarnple suivant, la classe SubCl-ass hrite de la classe IlaseCrass l

//

InheritanceExample
System;

- offre 1a dmonstration la plus simple de 1'hritage

using
{

nanespace InheritanceExanPle

public class
t

BaseClass

public int nDataMenber; public voi-d Somel'lethod0


t

Console.!'lriteline ("SoneMethod l
l

")

public class SubClass:


t

BaseClass

public void
{

Some0therMethod0
:

Console.l,lriteLine ("Sone0therMethod 0 ")

l
l

public class Test


{

public static
{

int Main(stringIJ args)


:");

I I cre un objet de 1a classe de base Console.ltlriteline ("Utilisons un objet de 1a classe de base

BaseClass bc = new BaseClassO; bc.nDataMember = 1; bc.SomeMethodO;

Console.lJriteline ("Utilisons un objet d'une sous-classe SubClass sc = ne.,/ Sub0lassO;


sc.nDataMernber sc. SoneMethod(J

//

crons naintenant un lment d'une sous - classe

:");

=
;

2;

Ghapitre 12: Acceptez-vous l'hritage

? 273

sc. Sonre0therMethod ( )

// attend confirmation de 1'utilisateur


Console.tdriteline ("Appuyez sur Entre pour terminer, . .")
Console.Read0;
;

return

0:

La classe BaseCiass est dfinie av'c un rnembre donne, et Ltne simple fonction membre, Soneile'r1ro,1 r t. L'objet L,i de la classe BaseC rass st cr et utilis dans liai:i i ).
La classe SubClass hrite de la classe EaseCia:rs en plaant le nom de celle<i aprs le signe deux-points (:) dans sa dclaration. SubClass rcupre donc tous les membres de BaseCiass, et peut y ajouter les siens. ilain ( I montre

que SubClass maintenant un membre clonne, rrDatalien'.ler, et une fonction membre, SoneMethoc O, qui viennent rejoindre le nouveau membre de la famille, la petite mthode SoneCthe rt-l r a,;s . r .

produit Ia sortie attendue fie suis toujours surpris quand un de mes programmes donne les rsultats attendus) :
Le programme

Utilisons un objet de 1a classe de base


SoneMethod ( )

Utilisons un objet d'une sous-classe


SoneMethod
()

Sone0therMethod ( ) Appuyez sur Entre pour terminer. . .

Ceci est stupfiant


Pour comprendre leur environnement, les tres humains construisent de vastes taxonomies. Par exemple, Milou est un cas pafticulier de chien, qui est un cas particulier de canid, qui est un cas particulier de mammifre, et ainsi de suite. Notre reprsentation du monde qui nous entoure estfaonne par cette manire de classifier les choses.

Dans un langage orient objetcomme t#, nous disons que la classe Student hrite de la classe Person. Nous disons aussi Que Person est une classe de base de Student, et que Student est une sous-classe de Person. Enfin, nous disons qu'un Student EST-UNE

rerson.

274

ouatrime partie : La programmation oriente objet

Bemarquez que la proprit IST-UN n'est pas rflexive:un student EST_UNE person, mais I'inverse n'est pas vrai. Une Person N'EST*PAS_UN Srudent. Un nonc comme celui-ci se rfre toujours au cas gnral. ll pourrait se trouver qu'une person particulire

soit effectivement un Student, mais beaucoup de gens qui sont membres de la classe Person ne sont pas membres de la classe Student. En outre,la classe Student possde
des proprits qu'elle ne partage pas avec la classe pe rs on. Par exemple, un studen r a une moyenne de points d'UV, mais une person ordinaire n'en a pas.

aussi une Person. Et il doit en tre ainsi : siun GraduateStudent EST_UN student et un Student EST-UNE Person, alors un GraduateStudent EST_UNE person. C0FD.

L'hritage est une proprit transitive. Par exemple, si je dfinis une nouvelle classe GraduateStudnt cOmme une sOus-classe de Student, alors un Graduatestudent St

quoi me sert l'hritage

L'hritage a plusieurs fonctions irnportantes. Vous pourriez penser qu'il sert rduire le volume de ce que vous avez taper au clavier. Dans une certaine mesure, c'est vrai : lorsque je dcris un objet de la classe student, je n'ai pas besoin de rpter les proprits d'une person. Un aspect plus important, mais li celui-ci, est le grand mot d'or dre rutiti.ser. Les thoriciens des langages de programmation savent depuis longtemps qu'il est absurde de recommencer de zro pour chaque nouveau projet en reconstruisant chaque fois les mmes composants.
Comparez la situation du clveloppement cle logiciel celle cl'autres industries. Y a-t-il beaucoup de constructeurs automobile qui commencent par concevoir et fabriquer leurs propres pinces et tournevis pour construire une voiture ? Et mme s'ils le faisaient, combien recommenceraient de zro en ralisant des outils entirement nouveaux pour chaque nouveau modle ? Dans les autres industries, on s'est rendu compte qu'il est plus pertinent d'utiliser des vis et des crous standards, et mme des composants plus importants comme des moteurs, que de repartir de zro chaque fois.

L'hritage permet de tirer le meilleur parti des composants logiciels existants. Vous pouvez adapter des classes existantes de nouvelles applications sans leur apporter de modifications internes. C'est une nouvelle sous-classe, contenant les ajouts et les modifications ncessaires, qui hrite des proprits d'une classe existante.

Ghapitre 12 :Acceptez-vous l'hritage

? 27 5

Cette capacit va de pair avec un troisime avantage de I'hritage. Imaginez que vous hritiez d'une classe existante. Un peu plus tard, vous vous apercevez que celle-ci a un bogue qu'il vous faut corriger. Si vous avez modifi la classe pour la rutiliser, vous devez rechercher manuellement le bogue, le corriger et tester le rsultat, sparment, pour chaque application qui I'utilise. Si vous avez hrit de la classe sans lui faire de modifications, vous pourrez dans la plupart des cas introduire sans surprises la classe corrige dans toutes les applications qui I'utilisent.

Mais le plus grand avantage de principe de I'hritage est de coller la ralit de la vie. Les choses hritent des proprits d'autres choses. Comme disait ma grand-mre, c'est la nature des choses.

Un evernlrle plus concret : (triter d'une classe BankAc c ount


Ma banque connalt plusieurs types de comptes bancaires. L'un d'eux, le compte rmunr, possde des proprits ordinaires d'un compte bancaire, plus la capacit d'accumuler des intrts. L'exemple de programme suivant, SimpleSavingsAccount, ralise en C# un modle de ces relations
q/\

*\

e,
ry

Que les plus impressionnables d'entre vous ne s'affolent pas : ce listing est un peu long, mais il est divis en parties clairement distinctes.

--"-ffi

l/ ll
i

SinplesavingsAccount

inplnente un SavingsAccount

coiture

forne

d'un
System;

BankAccoTJnt

; n'rrtiLise pas de mthode virtuelle

using

nanespace SimpleSavingsAccount

I I II
I
I

BankAccount

sinule un conpte bancaire possdant un numro de compte (assign La cration du conpte) et un solde

public elass
t
II //

BankAccouni

les numros de conpte connencent 1000 squentiellement partir de 1

et

augnentent

nNextAccountNunber I net jour 1e numro de cornpte et pubLic int nAccountNunber;

public static

int

1000;

Ie solde pour

chaque objet

public deciural -l

mBalance

I tnit - initialise 1e conpte avec le proehain nunro et 1e solde initial" spcifi I/

de compte

2 76

0uatrime partie : La programmation oriente

ob

jet

(qui est ga1 zro par dfaut) I| public void InitBankAccount0


{

InitBankAccount (0) l

public void InitBankAccount (decimal mlnitialBalance)


{

nAccountNunber

**nNextAccountNumber

nBalance = mlni"tialBalancei

//
t

Balance (so1de)
Balance nBalance;
]

public decimal

get I return
]

//
{

Deposit

- tout )
0)

dpt

positif est

autoris

public void

Deposit (decimal mnount)

if
{

(mAmount

nBalance *= mAnount;

l
]

1/ I'lithdraw
I

I
if
{

- tout retrait est autoris jusqu' la valeur du solde ; retourne 1e nontant retir
mWithdrawal)

public decimal Withdraw(decinal


{

(mBalance

(=

mWithdrawal)

nWithdrawal = nBalance; ] nBalance -= rnl,lithdrawal ;

return
]

nrWithdrawal;

//
{

ToString

public string

le compte sous forme de chane ToBankAccountString( )


met
;

return String.Fornat("{0} - {1:C}",


nAccountNunber, mBalance)
] ]

//
{

SavingsAccount

compte bancaire

qui rapporte des intrts

public class SavingsAccount

BankAccount

public decimal nlnterestRate;

l/ tnitsavingsAccount - 1it le taux d'intrt, exprim en pourcentage (valeur comprise entre 0 et II


public void InitSavingsAccount (decirnal mlnterestRate)

100)

Chapitre 12:Acceptez-vous l'hritage

? 277

InitSavingsAccount

(0,

mlnterestRate)

nrrhl.in .,ni.{ fnjtSavincsccount(decimal mlnitiai, decimal mlnterestRate)


y su r:v

this.mlnterestRate : mlnterestRate /
l

Ini-tBankAccount (mInitia1 )

tO0:

// Accurnulatelnterest t

invoque une

fois par

priode

public void Accumulatelnterest()


mBalance

= mBalance * (decimal) (mBalance * mlnterestRate)

//
t

ToStri-ng

- met le

compte sous

forne de chane

public string ToSavingsAccountString0 return String.Format(" [0]


l
(

{11,')

",

ToBankAccountString0, mlnterestRate

100)

l public class C1ass1


IL

public static int Main(string[] args)


{

I cre un conpte bancaire et 1'affiche

BankAccount ba = new BankAccount{); ba. InitBankAccount ( 100) ;

ba.Deposit(100); Console.1,{riteline("Conpte {0J", ba.ToBankAccountString0 ) ; I I et naintenant un compte rmunr SavingsAccount sa = new SavingsAccount();
sa. InitSavi.ngsAccount

(100,
(

12. 5M)

sa.Accumulatelnterest

; ;

Console.i^lriteline("Compte {01", sa.ToSavingsAccountString()) // attend confirmation de 1'utilisateur Console.WriteLine("Appuyez sur Entre pour terminer. ..") ;
Console.Read0;

return
'|

0;

J 1

)
1

rrr i n'est gure cliffrente de celles qui apparaissent dans d'autres chapitres de ce livre. Elle commence par la fonction d'initialisation sLlrcharge lri'. 3:rri..,rc.roiir'- | I : une pour les comptes qui Sont Crs avec un Solde initial, une autre pour ceux qui devront se contenter de comnlencer cle zrtl.
La classe Bank,!.,.

278

Ouatrime partie : La programmation orienre

ob

jer

La proprit Balance permet de lire le solde, mais sans donner la possibilit de le modifier. La mthode Deposit O accepte tout dpt positif. La mthode Withd raw ( ) vous permet de retirer tout ce que vous voulez dans la limite de ce que vous avez sur votre compte. ToBankAccountStrine o cre une chalne qui donne la description du compte.

La classe SavingsAccount hrite de toutes ces bonnes choses de

BankAccount. A cela, elle ajoute un taux d'intrt, et la possibilit d'accumuler des intrts intervalle rgulier.
Maln O en fait le moins possible. Elle cre un BankAccount, affiche le compte, cre un SavingsAccount, ajoute une priode d'intrts, et affiche le rsultat :
Compte 1001 200,00 n (12,5%) Compte 1002 112,50 Appuyez sur Entre pour terminer...

ld!75 f\!/ Y

Remarquez que la mthode InitSavingsAccount o invoque InitBankAccount O. Cela initialise les membres donne propres au compte. La mthode InitSa-.'ingsAccount O aurait pu les initialiser directement, mais il est de meilleure pratique de permettre BankAccount d'initialiser ses propres membres.

EST_UN par ralrport A_UN t. m'ul retroutler

- i'ai du mal

La relation entre SavingsAccount et BankAccount n'est rien d'autre que la relation fondamentale EST-UN. Pour commencer, je vais vous montrer pourquoi, puis je vous montrerai quoi ressemblerait une relation A_UN.

La relaton EST UN
La relation EST-UN entre -s3r.,i11gsr\ccount et BarrkAccount est mise en vidence par la modification suivante c 1a s s 1 dans le programme SirnpleSa-,'iiigsAccouni- de la section prcdente :
pub-Lrc
I
t

class Classl
ef f

/ fti rpetttennsi f -

public static void DirectDeposit(BankAccount

ectue automaticuement su Lvruo 1e rint 11 'rrn


ba,

chnrrp LrtcYu

Chapitre

2:

Acceptez-vous l'hrit age

? 27I

rlpnim:i
ba. Deposit (mPay)
;

mPnrr)

public static
t I

int Main(stringIJ args)


et 1'affiche
0
;

cre un conpte bancaire

BankAccount ba = new BankAccount ba. InitBankAccount ( 100) ;

DirectDeposit(ba, 100) ; Console.liriteline("Compte


/

{0J

I et naintenanr un compre

",

ba.ToBankAccountString0 ) ;

rnunr

SavingsAccount sa = ne!/ SavingsAccount0; sa. InitSavingsAccount ( i2 . 5M) ;

DirectDeposit(sa,
Console,

100) ; sa, Accumulatelnterest ( )

{0J " , sa, TosavingsAccountString 0 ) ; // attend confirmation de 1'utilisateur tonsole.T,Iriteline("Appuyez sur Entre pour terniner...',) ;

i,lriteline ( "Conpte

Console.ReadO;

return
] ]

0;

Les effets de ce programme n'ont pas chang. La seule vritable diffrence est que tous les dpts sont maintenant effectus par la fonction locale DirectDeposit | ), Les arguments de cette fonction sont le compte bancaire et le montant dposer. Remarquez (c'est le bon ct de la chose) que l'lain ( ) peut passer DirecrDeposit O soit un compte ordinaire, soit un compte rmunr, car ur.t SavingsAccc'unt EST_UN Bank,:corrni et en reoit par consquent tous les droits et privilges.

Contenir B ankAc c ou nt pour ,/ accder


La classe Sa,,'ingsAccorrnt aurait pu accder d'une autre manire aux

membres de BankAccount

//
{

SavingsAccount

public class

- compte bancaire SavingsAccount_

qui rapporte des intrts

public SankAccount bankAccount ; public decimal nlnterestRate;

//

InitsavingsAccount

- 1it 1e taux d'intrt,

exprin

en

28

0uatrime partie : La programmation oriente objet

pourcentage (valeur conprise entre 0


bankAccount,
dec

et

100)

public void InitSavingsAccount(BankAccount


t

imal mlnterestRate)

this.bankAccount = bankAccount ; this.mlnterestRate = mlnterestRate


]

100:

//
{

nrrbl

Accumulatelnterest - invoque une ie void Accumulatelnterest0


uv!ve \/

fois par

priode

bankAccount.mBalance

= bankAccount.mBalance

+
j

(bankAccount.nBalance

* mlnterestRate) ;

// Deposit - tout dpt positif est autoris


public void Deposit (decimal
{

mAmount)

bankAccount . Deposit (rnAnount )


l

i
I
I t

/ I

Withdraw

- tout retrait est autoris jusqu' la valeur du solde : retourne 1e montant retir
rnWithdrawal)
;

public double Withdraw(decimal return


'l
.J

bankAccount, Withdraw(mWi.thdra,,+a1 )

Ici, la classe Sa'ringsAccount contient un membre donne bankAccount (au lieu cl'en hriter de'laril.,."ie rri)-n:). L'objet bankAccount contient le solde et le numro clu compte, irtformations ncessaires pour la gestion du compte rmunr. Les donnes propres un compte rmunr sont contenues dans la classe Sa.,,'ing.:ticit.ult t
Dans ce cas, nous disons eue...,:^'.':',.E.----,-r'

rii:'t

A_UN BanitAccounl--.

La relation

A UN
:

La relation A_UN est fondamentalement diffrente de la relation EST_UN. Cette diffrence ne selnble pas rnauvaise dans I'exemple de code suivant
I cr.e un nouveu compte rmunr BankAccount ba = new BankAccount 0

SavingsAccount_ sa = nev SavingsAccount sa. InitSavingsAccount (ba, 5 ) ;


I

0;

I et y

dpose

cent euros
;

sa.Deposit(100)

Ghapitre 12: Acceptez-vous l'hritage

? 28 |

/
.

sa

puis accumule des intrts Accunulatelnterest ( ) ;

Le problme est qu'un SavingsAccount_ ne peut pas tre utilis comme

un BankAccount. Par exemple, le code suivant ne marche pas

ll DirectDeposit - effectue autonatiquenent 1e dpt d'un void DirectDeposit(BankAccount ba, int nPay)
{

chque

]
t

ba.Deposit (nPay)

void SoneFunction0

// 1'exemple qui suit ne narche pas SavingsAccount* sa = new SavingsAccount_0;


DirectDeposit (sa, 100)
I
)
;

suite.

DirectDeposit ( ) ne peut pas accepter un SavingsAccount_ en lieu et place d'un BankAccount. C# ne peut voir aucune relation vidente entre les deux.

Quand utilser EST_UN et quand utiliser A_UN

La distinction entre les relations EST_UN et A_UN est plus qu'une question de commodit logicielle, Cette relation a un corollaire dans le monde rel.

Par exemple, une Ford Explorer EST_UNE voiture (quand elle n'est pas sur Ie toit). Une Explorer A_UN moteur. Si un ami me dit : "Viens avec ta voiture", et que j'arrive dans une Explorer, il n'a aucun reproche me faire (ou alors, s'il en a, ce n'est pas parce que I'Explorer n'est pas une voiture). Il pourrait me faire des reproches si j'arrivais en portant dans mes bras le moteur de mon Explorer. La classe Explorer doit apporter une extension la classe Car, non seulement pour donner Explorer accs aux mthodes de Car, mais aussi pour exprimer la relation fondamentale entre les deux. Malheureusement, un programmeur dbutant peut faire hriter Car de Motor, donnant la classe Car accs aux membres de Motor, dont Car a besoin pour fonctionner. Par exemple, Car peut hriter de la mthode Motor. Go O, mais cet exemple met en lumire I'un des problmes qui rsultent de cette approche. Mme si les tres humains s'expriment parfois

28

Ouatrime partie : La programmation oriente

ob

jet

de faon ambigu, faire dmarrer une voiture n'est pas la mme chose que faire dmarrer un moteur. L'opration dmarrage de la voiture dpend videmment du dmarrage du moteur, mais ce sont deux choses distinctes : il faut aussi passer la premire, lcher les freins, et ainsi de suite. PIus encore, sans doute, faire hriter Car de i{ctor est une reprsentation errone des choses. Une voiture n'est tout simplement pas un type particulier de moteur.

i( "9K il ) \g_/

L'lgance du logiciel est un but qui se passe de justification. Non seulement elle le rend plus comprhensible, plus fiable et ais maintenir, mais elle rjouit le gott et facilite la digestion, entre autres.

Autres considrations
C# implmente un ensemble de caractristiques conues pour supporter

I'hritage.

Changer de classe
Un programme peut changer Ia classe d'un objet. En fait, c'est une chose que vous avez dj vue dans cet exemple. SomeFunctlon O peut passer un

objet SavlngsAccouirt une mthode qui attend un objet BankAccounr.


Vous pouvez rendre cette conversion plus explicite
BankAccount ba;
:

SavingsAccount sa = ba ba sa

netll

: sa; = (BankAccount)sa; : :
ba;

ll

SavingsAccout

II 1o n^ct avnl ini+^ II tI r! !qoL yrfLrLc

ox: une conversion vers

1e

bas

imo l 'i c'i

te est

admise

cDL ^^* rrf r

// Nont I I Ia conversion vers I I ceci est correct

1e haut

impiicite est i.nterdite

sa

(SavingsAccount)ba;

La premire ligne stocke un objet SavrngsAccoinr- dans une variable BankAccoLlnt. C# effectue pour vous cette conversion. La deuxime ligne

utilise I'oprateur cast pour convertir explicitement I'objet.


Les deux dernires lignes reconvertissent I'objet BankAccount en

Savingsccounr.

Chapitre 12 : Acceptez-vous l'hritage

? 2 83

La proprit EST_UN n'est pa.s rflexive. Autrement dit, mme si Explorer -.of4Q jE: est une voiture, une voiture n'e.st pa.s ncessairement une Explorer. De eY \ =( gA J mme, un EaiLi'...,r:-.r,r,*- n'est pas ncessairement un Sa.,ringsAccount, et la conversion implicite n'est clonc pas autorise. La dernire ligne est admise \-,/

parce que le prograrnmeur a indiclu sa volont de "tenter le coup".

Des casts inalides

l'excution

En gnral, le castir-rg cl'un objet de lai,i,...CCo.1fr1- Sa,"'ingsAccount st une opration dangereuse. Consiclrez l'exentple suivant :

public static void


t

ProcessAmount(BankAccount bankAccount)

I I dpose une grosse somme sur bankAccount.Deposit ( toooo. oo) ;

le

compte

I I si 1'objet est un SavingsAccount, // recueille f intrt ds maintenant SavingsAccount SavingsAccount = (savingsAccount)bankAccount;


^^^..-+ ^^,,.:--ov rrrnLLuurt, ^^.,-..1 ^+^T-+^-^^+ Accumulatelnterest ^ / l ( )
;

]
yuurrL ^',L1.i^ a+a+.ia LoLlL "^.i,l vulu T^^+/r^^+/\ IcLUoL\l

SavingsAccount sa = nehr SavingsAccount O ProcessAnount (sa) ; BankAecount ba = new BankAccount 0 ; ProcessAmount (ba) ;
l

ProcessAnaount () excute un certain nombre d'oprations, dont I'invocation de la mthode Accr:nulateliri-ef esr i'r. Le cast de ba un SavingsAccount est ncessaire, parce eue ba est dclar comm urr BankAccount. Le programme se compile correctement, parce que toutes les conversions de type sont faites par un cast explicite.

Tout se passe bien avec le premier appel f'rocessAmount O, dans Test O. L'objet sa de la classe Sa..ingsi\ccoririt est pass la mthode ProcessAmount l). Le cast de Fai'k.\cco'.rnr- Sa-ringsAccount ne pose pas de problme parce qlle I'objet ha tait de toute faon I'origine un objet Sar.ings . .r'. .
Le deuxime appel Pr o..rss.i.11,-r,lrr i n'est toutefois pas aussi chanceux. ' Le cast Sa.,'irrg,sAr:r:crrnT- ne peut pas etre autoris. L'obiet ba rI' pas de mthode Ac cunuiat r-: i nt e r e st
1

28

Ouatrime partie : La programmation oriente

ob

jet

s/\

Af\

e,

Une conversion incorrecte gnre une erreur I'excution du programme (ce qu'on appelle une erreur run-time). Une erreur I'excution est beau-

coup plus difficile identifier et corriger qu'une erreur la compilation.

uiter les conuersions innalides en utilisant le mot-cl i s


La fonction ProcessAmount O se porterait trs bien si elle pouvait tre

stre que I'objet qui lui est pass est bien un Sa,"'lngsAccounr avant
d'effectuer la conversion. C'est dans ce but que C# offre le mot-cl 1s.

L'oprateur i s admet un objet sa gauche et un type sa droite. Il retourne 'r f ue si le type I'excution de I'objet qui est sa gauche est compatible avec le type qui est sa droite.
Vous pouvez modifier I'exemple prcdent pour viter I'erreur I'excu-

tion en utilisant I'oprateur is


public static void
t

ProcessAmount(BankAccount bankAccount)

l/ dpose une grosse somme sur le compte bankAccount. Deposit ( 10000 . 00) ; I I si 1'objet est un SavingsAccount if (bankAccount is SavingsAccount)
{

ll .,.recuei11e f intrt ds maintenant SavingsAccount SavingsAccount = (SavingsAccount)bankAccount; savingsAccount . Accumulatelnterest ( ) ;


j
]

public static void


{

TestCast

SavingsAccount sa = nev/ SavingsAccount0;


ProcessAmount (sa)
;

BankAccount ba = ner,i BankAceount ProcessAmount (ba) ;

L'instruction rf supplmentaire teste I'objet banx.rccoLl-ir- pour vrifier qu'il est bien de la classe SavlngsAccounr. L'oprateur is retourne true lorsque ProcessAnount O est appele pour la premire fois. Toutefois, lorsqu'un objet bankAccount lui est pass dans le deuxime appel, I'oprateur i^s retourne faise, vitant ainsi le cast invalide. Cette version de ce programme ne gnre pas d'erreur I'excution.

Ghapitre 12 : Acceptez-vous l'hritage

? 28 5

^qa/ ft?ll \1l/

D'un ct, je vous recommande fortement cle protger tous vos casts vers le haut avec I'oprateur i s pour viter le risque d'une erreur I'excution, d'un autre ct, je vous conseille d'viter tout cast vers le haut, si possible.

La classe ob

j ect

Les classes suivantes sont en relation les unes avec les autres:

public class

MyBaseClass {}

pubLic class I'lySubClass

I'lyBase0lass {}

La relation entre ces deux classes permet au programmeur d'effectuer le test suivant

l'excution: public class Test


{

public static void


t

GenericFunction(MyBase01ass

mc)

I I si 1'objet est vrainent if (nc is !'lySubClass)

une sous-classe..,

ll ...alors
I

1e

MySubClass msc

traite

corme une sous-classe

(MySubClass)mc;

suite

l
]
]

Dans ce cas, la fonction Gene r icFunc r i on O diffren cie les sous-classes de MyB a s eC l

as

en utilisant le mot-cl

is.

Commentfaire la diffrence entre ces classes, apparemmentsans lien entre elles, en utilisant 0prateur is ? C# tend toutes ces classes partir de leur classe de base commune, object. Autrement dit, toute classe qui n'hrite pas spcifiquement d'une utre classe hrite de Ia classe object. Ainsi, les deux dclarations suivantes sontidentiques:
le mme

class MyClassl : object {} class My0lass2 {)

MyClassl et MyClass2 ont en commun la classe de base object, ce qui autorise


fonction gnrique suivante
:

la

oublic class '(

Test

286

Ouatrime partie: La programmation oriente obiet

public static void GenericFunction(object


i

o)

if
I
r

(o

is l'IyClass1)
=
(MyClass1)o;

MyClassl mcl
tl tt
)

l
'I

GenerlcFunction ( ) peut tre invoque avec n'imporle queltype d'objet. extraira des hutres ob j ect toutes les perles de MyClass 1.

Le mot-cl :-s

L'hritage et le constructeur
Le programme Inherir-anceExampl e que nous avons vu plus haut dans ce chapitre repose sur ces horribles fonctions Inlt. . . pour initialiser les objets BatrkAccoufit et SavingsAccoLiitt en leur donnant un tat valide. quiper ces classes de constructeurs est certainement la meilleure manire de procder, mais elle introduit une petite complication.

lntuquer le constructeur par dfaut de la


classe de base
Le constructeur par dfaut de la classe de base est invoqu chaque fois qu'une sous-class'e est construite. Le constructeur de la sous-classe invoque automatiquement le cclnstructeur de la classe de base, comme le

montre cet exemple simple

| I

ll Inhpri+inoAConstructor - montrp orre lp consTrl{"teur de 1a classe de base est invoqu {

I
e.,-+^JJ D LEIT,'

autonatiquenent

,,^.:-^ urlr

namespace
t

InheritingAConstructor
C1ass1

public class
t

Chapitre 12 : Acceptez-vous I'hritage

? 287

public static
I

int Main(string[]

args)
I

Console.I.Jriteline("Cration d'un objet Base0lass")

Base0lass bc = new Base0lassO; Console.Writeline("\n'laintenant, cration d'un objet Subclass") SubClass sc * nefir SubClass 0 ; l/ attend confirnation de 1'utilisateur ; Console.Writeline("Appuyez sur Entre pour terniner"'") Console.nead0;

return
l

public class
{

BaseClass

public
{

BaseClass

0
;

Console.liriteline ( "Construction de BaseClass")


1

publie class SubClass


I
I

Base0lass

public Sub0lass0
.,
]

Console'I,lriteline ("Construc

l
font rien de plus qu'afficher Les constructeurs de Baseclass et subclass ne de I'obiet Baseclass un message sur la ligne de commande. La cration La cration d'un invoque le construceur par dfaut de la classe BaseClass' avant d'invoquer Son objet SubClass invoque le constructeur de BaseClass

propre constructeur.
Ce programme donne la sortie suivante
Cration d'un objet Baee0lass Construction de BaseClass Maintenant, cration d'un objet Sub0lass
:

tonstruction de

BaseCl'ass
'

Construction de SubClass Appuyez sur Entre pour terniner"

beaucoup aux diffrents une hirarchie de classes hrites ressemble trouve au-dessus des classes tages d'un immeuble. Chaque classe Se

288

0uatrime partie : La programmarion orienre objer

dont elle ralise une extension. Il y a une raison cela : chaque classe est responsable de ce qu'elle fait. Une sous-classe ne doit pas plus tre tenue pour responsable de I'initialisation des membres de la classe de base qu'une fonction extrieure quelconque. La classe BaseClass doit se voir donner la possibilit de construire ses membres avant que les membres de SubClass aient la possibilit d'y accder.

Passer des arquments au constructeur de la

classe de base .. le mot-cl

base

La sous-classe invoque le constructeur par dfaut de sa classe de base, sauf indication contraire, mme partir cl'un ct-rnstructeur cl'une sousclasse autre que le constructeur par dfaut. C'est ce que montre I'exemple lgrement modifi ci-dessous : using
{

System

nanespace Exanple

public class Classl


{

public static int Main(stringIJ args)


t

Console.l,ilriteLine("Invocation de SubClass 0',)


SubClass

scl = new SubClassO;

Console.hlriteLine("\nlnvocation de SubClass(int) "1 ; Sub0lass sc2 = new SubClass(0); // attend confirmation de 1'utilisateur Console,i.IriteLine("Appuyez sur Entre pour terminer.
Console.Read O
;

. . ")

return
]

0;

public class
{

BaseClass

public
{

BaseClass

0
r

console.Llriteline("construction de Baseclass (default)")


]

publie BaseClass(int i)
t
COnSOle .l,/s

itoT.i

/'rC^ncrrtnri6l de BaseClaSS (int)

")

l public class SubClass:

Base0lass

Chapitre 12: Acceptez-vous l'hritage

? 289

n11n | 1a

\11n1

Iteq

/\

Console.

l{riteLine ( "Construction de SubClass (default)")

l oublic SubClass(int i)
{

Console.1,IriteLine('rConstruction de SubClass (int) ")

L'excution de ce progralnme donne les rsultats suivants


Invocation de SubClassi) Construction de BaseClass (default) Construction de SubClass (default) Invocation de SubClass(int) Construction de BaseClass (default) Construction de SubClass (int)
nnrrrraa nyPUJ ! orrr U: Frl ro !rtLrc nnrrr yvu! torminor Lsrrufrrs! '..

Le programme commence par crer un objet par dfaut. Comme prvu, C# invoque le constructeur par dfaut cle SiibClar;s, qui commence par passer le contrle au constructeur par dfaut de BaseClass. Le programme cre alors un objet, en passant un argument entier. nouveau comme prvu, C#

invoque SubClass (int). Ce constructeur invoque le constructeur par dfaut de BaseCjass, colTtme dans I'exemple prcdent, car il n'a pas de
donnes passer.

Un constructeur d'une sous-classe peut invoquer un constructeur particulier de la classe de base en utilisant le mot-cl base. est trs similaire ^$tf$ Ce ;( la mme classe en utilisant le mot-cl this. Pour tout savoir sur autre de un il ) les constructeurs avec this, voyez le Chapitre 11' \ry

procd

la manire dont un constructeur en invoque

Par exemple, examinez le petit programme I1.",3l.,sl3seConstructor

// II II

InvokeBaseConstruetor

nontre

coTnment une sous-classe peut invoquer le constructeur de 1a clagse de base de son choix en utilisant 1e mot-cl base

using

System;

nanespace TnvokeBaseConstructo r

290

0uatrime partie:La programmation oriente objet

public class BaseClass


t

public
t

BaseClass

()

Console.l,iiriteLine("Construction de BaseClass (default) ")

l public BaseClass(int i)
{

Console.I^IriteLine("Construction de BaseClass(
l l

[0])", i) ;

public class SubClass:


t

BaseClass

public
{

SubClass

()

Console.l'/riteline("Construction de SubClass (default)")


l

public SubClass(int
{

i1, int i2) : base(i1) il,


i2);

Console.lrlriteLine("Construction de SubClass(i01, {1J)",


]
]

public class
t

C1ass1

public static
t

int Main(stringIJ args)


SubClass 0

Console,lllriteLine("Invocation de SubClass sc1 = new SubClass0;

")

Console.WriteLine("\nlnvocation de SubClass(t, 2) ") ; SubClass sc2 = ner,r SubClass(1, 2); // attend confirmation de 1'utilisateur Console . l'/riteLine ( "Appuyez sur Entre pour terminer.
Console,Read0;

..")

return l
]

0;

l
Ce programme donne la sortie suivante Invocation de SubClass 0
:

tonstruction de Base0lass (default)


Construction de SubClass (default)

Invocation de SubClass(1,

2)

Ghapitre 12 : Acceptez-vous l'hritage

? 2g

Construction de BaseClass ( 1 ) tonstruction de SubClass (1, 2) Appuyez sur Entre pour terminer...

Cette version commence de la mme manire que les exemples prcdents,


en crant un objet SubCiass par dfaut, utilisant les constructeurs par

dfaut de BaseClass et de SubCiass.


Le deuxime objet est cr avec I'expression SubClass (1, 2).C# invoque le constructeur SubClass (1nt, int), qui utilise le mot-cl base pour passer I'une des valeurs au constructeur tsaseCias s (int ) . On peut se douter que SubClas s passe Ie premier argument la classe de base pour traitement, et continue en utilisant la deuxime valeur pour elle-mme.

La classe BankAc

ount nodife

Le programme ConstructorSavingsAccounr est une version modifie du programme SinpleBankAccount. Dans cette version, le constructeur de SavingsAccount peut repasser des informations aux constructeurs de BankAccount. Seuls Main O et les constructeurs eux-mmes apparaissent ici

*u"uli3 ,\tr

// ll ll II
t

ConstructorsavingsAccount

colnme

implmente un SavingsAccount forme d'un SankAccount : n'utilise

aucune mthode
System;

virtuelle,

mai.s implmente

correctenent 1es constructeurs

using

namespace ConstructorSavingsAccount

/i | II
|
t L

BankAccount

- simule un compte bancaire

possdant

un numro de compte (assign 1a cration du compte) et un solde


BankAccount

public cLass
II /l

tes numros de conpte conmencent 1000 et augnentent squentiellenent partir de 1


chaoue obiet

public static int nNextAccountNumber = 1000; ll net jour le nunro de comntp pt le solrle nour public int nAceountNumber; public decimal nBalance;

//
t

tonstrueteurs
BankAccount

public
]

-(

/\ /^\ ) this
:

(0

public

Bankccount

(decinal nlnitialBalance)

292

0uatrime parrie:La programmarion orienre objer

nAccountNumber = finNextAccountNunber mBalance = mlnitialBalance ;


l
I

mme

chose

ici
intrts

//
{

savingsAccount

public class

compte bancaire qui rapporte des Savi_ngsAccount : BankAccount

public decimal nlnterestRate;


I

exprim en pourcenrage (valeur comprise entre 0 et 100) n'rl, 1.i^ avr_ngsAccount(decirnal c-,,.;-^^^. puur_r-c nlnterestRate) : this(0, mlnterestRate)
{

// I

tnitsavingsAccount

* 1it 1e taux d'intrt,

public SavingsAccount(decimal mlnitial,


decimal mlnterestRate)
{

: base(nInitial)

this.mlnterestRate = nlnterestRate
l
I ]

tOO;

mme

chose

ici

public class
{

C1ass1

// u].recrueposlr n;-^^*n^-^^..ir // - effectue automatiquement le dpt d'un chque public static void DirectDeposit(BankAccount ba,

decimai npay)
{

ba. Deposit (mpay)

public stati.c int Main(string[] args)


{

I I cre un compte bancaire et 1'affiche BankAccount ba = new BankAccount(i00);

DirectDeposit(ba,

100)

Di.rectDeposit (sa, t00) ; sa.Accumulatelnterest ( ) ;

Console,l,Iriteline("Conpte {0}", ba.ToBankAccountString0 ) ; // et naintenant un compte rmunr SavingsAccount sa = neu, SavingsAccount(12,5M) ;

Console,l^lriteline("Compte [0] ", sa.ToSavingsAccountString0 ) ; / I attend confirmation de 1'utilisateur Console.l,lriteline("Appuyez sur Entre pour terminer..,") ;
Console.Read0;

return
l

0;

Ghapitre 12

: Acceptez-vous

'hr itage

? 293

BankAccourlt dfinit cleux constructeurs : un qui admet un solde initial, et le

constructeur par dfaut qui ne I'aclmet pas. Afin cl'viter toute publication de code dans le constructeur, le constructeur par dfaut invoque le constructeur de tsanL<A.rcc'it.'ri:,, . ,:,i- 1 .ti:t:ri- - I elt utilisant le mot-cl thls.
La classe Sa-"'ir,gsrLrLrr,rrl: fournit galenrent deux constructeurs. Le constructeur S a.lt:,gs,!c..,,:nt i'i:rt-er:sr- f a+,e) invoque le constructeur SavingsAccoi.Lil i i tresi t ir-:. iit.lttai baiance; en lui passant un solde initial de 0. Ce constructeur, le plus gnral, passe le solde initial au constructeur Banli;(rij.iLLnt rir,tial baiance) en utilisant le mot-cl base, comme le montre de faon graphique la Figure 72.1.

Figure 12.1 . Le cheminement de la constru ction


(.errino<Arnn mt

BankAccount(0)

nurr" le solde la classe de base

d'un objet
en utilisant le c0nstructe u r par dfaut.

Savings Account (125%,

gl
la valeur par

donne au solde

dfaut 0

Savin gs Ac cou nt (125%l

J'ai modifi Main O pour me dbarrasser de ces infernales fonctions Init. . . O, en les remplaant par des constructeurs. La sortie de ce programme est la mme :
compte 1001 compte 1002
Appuyez

- 200,00 - 112,50

(12,5ti)
.
.

sur Entre pour terminer.

Le destructeur
C# offre aussi une mthode qui fait I'inverse du constructeur, appele le destructeur. Le destructeur porte le nom de la classe, prcd par un tilde (-). Par exemple, la rnthode -Faseijlass est le destructellr de BaseCiass. C# invoque le destructeur lorsqu'il n'utilise plus I'objet. Le destructeur

par dfaut est le seul qui peut tre cr, car le destructeur ne peut tre invoqu directement. En outre, le destructeur est toujours virtuel.

294

0uatrime partie: La programmation oriente objet

Dans le cas d'une succession d'hritages de classes, les destructeurs sont invoqus dans I'ordre inverse des constructeurs. Autrement dit, le destructeur de la sous-classe est invoqu avant le destructeur de la classe de base.

Le ramasse-miettes et le destructeur G#
mthode du destructeur estbeaucoup moins utile en C#que dans d'autres langagesorients objet, comme C++, car C# possde de ce I'on appelle une destruction non dterministe.
La

La mmoire alloue un objet est supprime du tas lorsque le programme excute la commande new. Ce bloc de mmoire reste rserv aussi longtemps que les rfrences valides celui-ci restent actives.
Une zone de mmoire est dite "inaccessible" lorsque la dernire rfrence celle-ci passe hors de porte. Autrement dit, personne ne peut plus accder cette zone de mmoire quand

plus rien n'y fait rfrence.


C# ne fait rien de particulier lorsqu'une zone de mmoire devient inaccessible. Une tche de faible priorit est excute I'arrire-plan, recherchant les zones de mmoire inaccessibles. Ce qu'on appelle le ramasse-miettes s'excute un faible niveau de priorit afin d'viter de diminuer les performances du programme. Le ramasse-miettes restitue au tas les zones de mmoire inaccessibles qu'il trouve. En

temps normal, le ramasse-miettes opre en silence l'arrire-plan. ll ne prend le contrle du programme qu' de brefs moments, lorsque le tas est sur le point d'tre court de mmoire,

Le destructeur de C# est non dterministe parce qu'il ne peut pas tre invoqu avant que I'objet it t rcupr par le ramasse-miettes, ce qui peut se produire longtemps aprs qu'il
a cess d'tre utilis. En fait, si le programme se termine avant que l'objet soittrouv par le ramasse-miettes et retourn au tas, le destructeur n'est pas invoqu du tout.

Au bout du compte, l'effet qui en rsulte est qu'un programm eur C# ne peut pas se reposer sur le destructeur pour oprer automatiquement comme dans un langage comme C++.

Chapitre 13

Ouel est donc ce

polymorphisme
Dans ce chapitre :

L'embarras du choix : masquer ou craser une mthode de la classe de base. Construire des classes abstraites : parlez-vous srieusement ? Dclarer comme abstraites une mthode et la classe qui la contient.
Faire commencer une nouvelle hirarchie au-dessus d'une hirarchie existante. Empcher qu'une classe puisse tre transforme en sous-classe.

hritug" permet une claSse "d'adopter" les membreS d'une autre. Ainsi, je peux crer une classs 5st,ingsAccount qui hrite de membres donne comme inBalance et de mthodes comme Deposit O de la classe de base BankAccount. C'est trs joli, mais cette dfinition de I'hritage ne suffit pas reprsenter convenablement ce qui se passe dans le
monde rel.
llu

jlff tl$/, Y

Si

vous avez besoin de vous rafralchir la mmoire sur I'hritage des classes, relisez le Chapitre 14.
Un four micro-oncles est un type de four, non pas parce qu'il a I'air d'un four, mais parce qu'il remplit les mmes fonctions qu'un four. Un four micro-ondes remplit aussi des fonctions supplmentaires, mais au moins, il remplit les fonctions de base d'un four - et le plus important, c'est qu'il fait chauffer mes nachos quand je dis "StartCooking". Le processus interne que doit mettre en uvre le four pour remplir sa mission ne m'intresse pas, pas plus que le type de four dont il s'agit, ni son fabricant.

296

Ouatrime partie: La programmation oriente objet

De notre point de vue d'tre humain, la diffrence entre un four microondes et un four conventionnel ne semble pas de la plus haute importance, mais envisagez un instant la question du point de vue du four. Les tapes

du processus interne mis en uvre par un four conventionnel sont compltement diffrentes de celles d'un four micro-ondes (sans parler d'un four convection).
Le pouvoir du principe de I'hritage repose sur le fait qu'une sous-classe n'est pas oblige d'hriter I'identique de toutes les mthodes de la classe de

base. Une sous-classe peut hriter de I'essence des mthodes de la classe de base tout en ralisant une implmentation diffrente de leurs dtails.

Surcharger une mthode hrte


Plusieurs fonctions peuvent porter le mme nom, condition qu'elles soient diffrencies par le nombre et/ou le type de leurs arguments.

Ce n'est
PY

(u'une question de surcharge de foncton

-u#t$ Donner le mme nom deux fonctions (ou plus) s'appelle surchorger un \ =i -^- ) no- de fonction.

Les arguments d'une fonction font partie de son nom complet, comme le montre I'exemple suivant : oublic class
I

l4vClass

nrrhlin
t

ct:f in rrnid I'rrnnti^n1)

/
)

I f.aire

quelque chose
rrnirl rrnntinn/int\
srr urv.r \Jrrr/

nrrhl in cfrtin
t

I faire cirelnue chose d'autre


d)

public static void AFunction(double


{

I faire encore quelque

chose d'autre

l public static void Main(stringll args)


t

Ghapitre 13 : Ouel est donc ce polymorphisme

? 29 7

AFunetion0;

Alunction(l);
AFunction(2.0);

j
C# peut diffrencier les mthodes par leurs arguments. Chacun cles appels dans Main O accde une fonction diffrente. Le type retourn ne fait pas partie du nom complet. Vous pouvez avoir deux fonctions qui ne diffrent que par le type retourn.

#5

e,

A classe diffrente, nthode diffrente


Comme on peut s'y attendre, la classe laquelle appartient une fonction ou une mthode fait aussi partie de son nom complet. Voyez le segment de code suivant : public class
{

MyClass

public static void AFunction0; public static void AMethodO;


]

public
{

UrClass

public static void AFunction0; public static void AMethod0; public class C1ass1
t

publi.c static
{

vo

j-d Main (string [1 args)

/\ urutass.AtunctlonU ; // invoque la fonction nenbre MyClass. My0lass ncObject : new MyClass0; ncObject.Al'lethod0;

AMethod ( )

Le nom de la classe fait partie du nom tendu de la fonction. Il y a le mme tlpe de relation entre la fonction MyClass.AFunction O et la fonction UrC-Lass . AFunction 0 qu'entre la fonction MaVoiture. Dmarrerl'latinHiver () et la

fonction VotreVoiture . DmarrerMatinHiver

) (avec

la vtre, a marche).

29 8

0uatrime partie : La programmation orienre

ob

jer

Redfinir une nthode d'une classe de base


Ainsi, une mthode cl'une classe peut surcharger une autre mthode de la mme classe en ayant des arguments diffrents. De mme, une mthode peut aussi .surcharger une rnthode de sa classe de base. Surcharger une mthode d'une classe de base s'appelle redfinir, ou cocher la mthode.
Imaginez que ma banque adopte une politique qui tablisse une diffrence entre les retraits sur les comptes rmunrs et les autres types de retrait. Pour les besoins de notre exemple, imaginez aussi qu'un retrait effectu sur un compte rmunr cote une commission de 1,50 F.

Avec I'approche fonctionnelle, vous pourriez implmenter cette politique en dfinissant dans la classe un indicateur qui dise si I'objet est un Sa-ringSnCC,r,.lnr ou un simple Eani:A,:c,Juni. La mthode de retrait devrait alors tester l'indicateur pour savoir si elle doit ou non imputer la commission cle 1.50 F :
public
{

BankAccount

(int

nAccountType)

private decimal mBalance: private bool isSavingsAccount;

// indique 1e solde initial et dit si le compte // que vous tes en train de crer est ou non // un compte rmunr
public
{

BankAccount

(decimal mInitialBalance,

bool isSavingsAccount)
mBalance

= mlnitialBalance

; :

this, isSavinssAccount = isSavinssAccount


i

public decimal Withdrav(decimal


t

nAmount)

I I si Ie compte est un conpte rnunr if (isSavingsAccount)

i
I
ll

.,

.alors soustrait 1.50


-:1,50M:

mBalance

// poursuit avec le mme code pour le retrait if (mAnountToWithdraw ) mBalance)


{

mAmountToWithdrar,v ]

mBalance

mBalance

-=

nAmountToWithdraw;
rar^r
:

return

mAnountTo}.fithd

Ghapitre 13 : 0uel est donc ce polymorphisme

2gg

'I

class Mytlass
{

public void
t
I
1

SomeFunction0

I le veux ne crer

un conpte rrnunr

BankAccount ba
r

= new BankAccount(0, true);

Ma fonction doit clire au constructeur si le compte bancaire est un Savingsccoirrrt ert lui passant un indicateur. Le constructeur conserve cet indicateur et I'utilise dans la rnthode 'riithoraw O pour dcider s'il faut imputer la commission de 1,50 F. L'approche oriente objet consiste reclfinir la mthode Withci raw ( ) dans la classe de base BanL<Account, derrire une mthode de meme nom, de mme taille et de mme couleur de cheveux, dans la classe SavingsAccount

.e"*li3 h ffirii I tY,x

HidinsWithdrawal - redfinit la mthode de retrait de la I .... II classe de base avec une mthode de La II sous-classe du mne non
i

t,

using Systen;
nnespace Hidi.ngl,lithdrawal
t

// BankAccount - un compte bancaire trs ordinaire pub1lc class BankAccount


t
f

protected decirnal nBalance;

public
t

BankAccount

(decimal mlnitialBalance)
;

mBalance = mnitialBalance
1

public decimal
{

Balance
}

get { return nBalance;


J

nrrhl t
t

r'c decimal Withdraw(decimal \sv!+rx

mAmount)

decinal

if

mAmountToWithdraw = nAmount; (nAurountoWj.thdrai,s mBalance)

mAmountoWithdrarar

= mBalance

l
mBalance

-= nAnountTol,lithdraw;
;

return

mAnountTol,Ii.thdraw

300

0uatrime partie:La programmation oriente objet

//
I

SavingsAccount

compte bancaire

qui rapporte des intrts

public class SavingsAccount

BankAccount

public decimal mlnterestRate;

//

SavingsAccount

- 1it

1e taux d'intrt, exprim en pourcentage (valeur comprise entre 0

et 10)

public

SavingsAccount (decimal
dec

mInitialBalance,

irnal mlnterestRate)

:
r
I
1

base

(mInitialBalance)
100;

this.mlnterestRate = mlnterestRate /
)

// Accumulatelnterest r I

invoque une
!L!v u

fois par

priode

nrrhl i c voi rl Anr^trmrri atoTntpro<t

i
\

)
/

mBalance

mBalance

(mBalance

mTnterestRate);

//

Withdraw

- tout retrait est autoris iusou' 1a valeur du solde ; retourne le montant retir
F

public decimal Withdraw(decinal mWithdraval)


{

// soustrait 1.50
base. 'vlj-thdraw( | | VVU UVUVgL I I "^"^

l . 5t't) ;
maintenant lnaLLLLgllIlL EtlELLUtrl effectuer un Ull
;

retrait
!CL!1L

nrri restp avec ce re !sLC yur


dVLu

return
l
]
nlrhri^ PUUTfL ^t^^a Urdb

base . Wi.thdraw (mi^iithdrawal )

t r^^^1 ufdD

public static void


{

MakeAWithdrar+al(BankAccount ba,

decimal nAmount)
ba. Withdraw (mAmount)
;

public static int Main(string[] args)


{

BankAccount ba; avrngsAcCounL sa; ll cre un compte bancaire, en


C^"-i-^^aanln+

retire

100

F, et

// affiche les rsultats


ba = nevr BankAccount(200M);
ba. I.Iithdrar,,i{
1

00M)

// essaie de faj-re la
sa.llithdraw(100M)
;

mme

chose avec un cornpte rmunr

sa = new SavingsAccount(200M, 12);

// affiche 1e solde rsultant

Chapitre 13 : 0uel est donc ce polymorphisme

? 30 I

Console.l,iriteLine("Quand il est invoqu directenent ;") Console.l,rlrj-teLi.ne("Le solde de BankAccount est {0:C1",
ba.Balance); Console.l,iriteLine
(

"Le solde de SavingsAccount est {0: CJ " ,


sa. Balance)
;

// attend confirmation de 1'utilisateur


Console.i,Iriteline("Appuyez sur Entre pour terniner...")
Console.Read{);
;

return

0;

Dans ce cas, la fonction l'1ain () cre un objet BankAcco'rnt avec un solde initial cle 200 F, et effectue un retrait de 100 F. l"laln 0 rpte cette opration avec un objet Sa.,'ingsAc.ourr:. Lorsque l'lain 0 effectue le retrait depuis la classe de base, llaril,Ac c cunt . irii thd r ar" ( ) effectue cette tche avec un aplomb remarquable. Lorsque l"iain O retire ensuite 100 F du compte rmunr, la mthode
Savings,-.,1,' ;rLrrt
.

i.ii:iidrar,,O fait payer les 1,50 F.


.

..$$C ,t Remarquer que la classe Sa.;ingsAccount .l,riithdrait utilis tsankgcco,int H,,l|ithdr:aw()pluttquedemanipulerdirectementlesolde.Danstoutela

t(9, y

mesure du possible. faites en sorte que ce soit la classe de base qui manipule

elle-mme ses membres.

En 4uoi uaut-il mieux redfinir une mthode (u'aiouter un simple test ? Vu de I'extrieur. I'ajout d'un indicateur la mthode BankAcount . Wlthdraw 0 peut sembler plus simple que le procd qui consiste redfinir une mthode. Aprs tout, a ne fait qu'ajouter quatre petites lignes de code, dont deux ne sont que des accolades.
Le problme, c'est en quelque sorte la tuyauterie. Le premier inconvnient est que la classe llankAccounr n'a aucune raison de se mler des dtails de Sa,,'ings,\ccor'Lnt. Cela violerait notre rgle "Rendons Csar ce qui est Csar", et nous conduit au vritable problme : imaginez que ma banque dcide d'ajouter des comptes CheckingAccount ou CDAccount ou encore Tb I i iAc c ounr: ? C'est une chose possible, et tous ces types de compte diffrents seraient associs des politiques de retrait diffrentes, chacune ncessitant son propre indicateur. Aprs I'ajout de trois ou quatre nouveaux types de compte, notre vieille mthode 'dlthd rav; ( ) commencerait devenir bien complique. Chacune de ces classes devrait plutt s'occuper elle-mme de sa politique de retrait et laisser tranquille notre pauvre vieille

BankAccour,t .,/ilthd raw

302

0uatrime partie:La programmation oriente objet

Et si je redfinis accidentellement une mthode de

la

classe de base ?

Il peut arriver tout le monde de redfinir accidentellement une mthode de la classe de base. Par exemple, je peux avoir une mthode Vhlcule. Virage ( ) qui fait tourner le vhicule. Plus tard, quelqu'un tend ma classe Vhicule avec une classe Avion, dont la mthode Virage O est entirement diffrente. Il est clair que nous avons l un cas de confusion d'identit. Ces deux mthodes n'ont rien voir I'une avec I'autre, sinon qu'elles portent le mme nom.

Heureusement pour nous, C# sait dtecter ce problme.


En compilant I'exemple prcdent, Hidlng\,,/ithcl raw ( ) , C# gnre un avertissement patibulaire. Le texte de ce message est un peu long, mais en voici la partie importante :
Le not-cl new est requis sur ' Hidingi,lithdrawal. SavingsAccount.
masque
'

trtlj.thdrarv
(

(decimal)', car i1
imal

le

membre

hrit
dec

HidingWithdrarral . BankAc count . t.,lithd raw

)'

C# essaie de vous dire que vous avez crit dans une sous-classe une mthode portant le mme nom qu'une mthode de la classe de base. Estce vraiment ce que vous vouliez faire ?

.3!!- .a- Ce message t\7, Y

n'est qu'un avertissement. Vous ne le remarquerez mme pas,

iNmoinsdepasser|afentreSortiepourvoircequiyestaffich'Dans

presque tous les cas, vous y verrez un avertissement qui vous prvient que quelque chose pourrait bien vous mordre si vous n'y mettez pas bon ordre.

Le descripteur ne" indique C# qu'une mthode est redfinie intentionnellement et que ce n'est pas le rsultat d'une ngligence :

//
{

new
I

plus de problmes avec withdraw0 public decimal Withdraw(decinal dWithdraval)

pas de modifications internes.

U
:(dw

Cette utilisation du mot-cl new n'a rien voir avec I'utilisation du mme mot-cl pour crer un objet.

permettrai de faire remarquer ici que c'est I'une des choses que je 1r${Qa" Je me \ trouve agaantes chez C# (et C++ vnt lui) : faites ce que vous voulez ^r,7rt7 avec mes mthodes, mais ne surchargez pas mes mots-cls. Quand je dis \/ ) new, c'est que je veux crer un objet. Ils auraient pu utiliser un autre motcl pour indiquer une surcharge intentionnelle.

Ghapitre 13:0uel est donc ce polymorphisme

? 303

ReUenr

la ba s e

Revenons la mthode savingsAccounr .1,{ithdraw O de I'exemple que nous avons vu plus haut dans ce chapitre. L'appel BankAccount . w"-thd r aw ( ) depuis cette nouvelle mthode contient le motcl supplmentaire ba s e.
La version suivante de la fonction avec ce mot-cl supplmentaire ne marche pas :
new
{

public double Withdraw(double dWithdrawal)


I

double dAnountllithdrawn = Withdraro(dltithdrawal)

if
t ]

(++nNunber0fl^lithdrawalsThisPeriod
dArnountl,lithdravn

1)

t=

l,lithdraw (1 . 5 ) ;
;

return
l

dAnountl{ithdrar+n

Cet appel a le mme problme que celui-ci


vo]-d tn
{ f

tn]; I I je m'appelle

moi-nne

l L'appel fn O depuis fn 0 aboutit s'appeler soi-mme, sans fin. De mme, un appel de Withdraw 0 elle,mme fait qu'elle s'appelle elle-mme en boucle, comme un chat qui court aprs sa queue, jusqu' ce que Ie programme finisse par se planter. D'une manire ou d'une autre, il vous faut indiquer C# que I'appel depuis SavingsAccount . Withdraw ( ) est l pour invoquer la mthode de la classe de base, BankAccount . \,/ithdraw 0 . Une solution consiste faire un cast du pointeur this dans un objet de la classe BankAccounr avant d'effectuer I'appel.

ll
{

//

tlithdraw

- cette version accde 1a rnthode redfinie dans 1a classe de


objet de la classe

new
I

base en dfinissant explicitement 1e cast de 1'objet this public double Withdrar,r{double dWithdrawal) dans un
BankAccount

I cast du pointeur this

30 4

Ouatrime partie : La programmation oriente obiet

= (BankAccount)this; invoque Withdrar.+0 en utilisant cet objet BankAccount appelle 1a fonction BankAceount.Withdraw0 double dAmountl^lithdrawn = ba.Withdraru(dWithdrawal) I if (**nSurrber0fWithdrawalsThisPeriod ) 1)
BankAccount ba

// i/
{

dAnountWi.thdralrn
]

f=

ba , I^Iithdrar,r (1

.5

return

dAtnountWithd

rar,un :

Cette solution fonctionne : I'appel ba . l,,Jithd raw ( ) invoque maintenant la mthode BankAccor.lnt, comme on le voulait. L'inconvnient de cette approche est la rfrence explicite Bani.;Account. Une modification ultrieure du programme pourrait modifier la hirarchie d'hritage de telle manire que -sa.,'ings,.iccoLlnr n'hrite plus directement de BankAccount,. Une telle rorganisation brise cette fonction d'une faon qu'un nouveau programrneur pourra avoir du mal trouver. Pour moi, je n'arriverais jamais trouver un bogue comme celui-l. Il vous faut un moyen de dire C# d'appeler la fonction ''^Iithdrawo depuis "la classe qui est juste au-dessus" dans la hirarchie, sans la nommer explicitement. Ce serait la classe qui est tendue par savirrgsAcc'.-'illrt. C'est dans ce but que C# comporte le mot-cl base.

=t
.-!/

C'est le mme rnot-cl base qu'utilise un constructeur pour passer des arguments au constructeur de la classe de base. Le mot-cl base de C# est la mme chose que this, mais redfinit le cast la classe de base, quelle que soit cette classe :

ll II
t
/
II

With&aw

- tout retrait est autoris jusqu' la valeur du soide ; retourne l-e montant retir

new

public decimal Withdraw(decimal nWithdrawal)

/ sousrrait 1 .50 F
1

base. l,lithdraw(

.5M)

// vous pouvez maintenant effectuer un retrait


return
I

avec ce

qui reste

base.Withdrai,r(mWithdrawal)

invoque maintenant la mthode vitant par-l I'cueil qui consiste s'invoquer elle-mme. En outre, cette solution ne sera pas brise si la hirarchie d'hritage est modifie.
13ankccorLn: . iriit-hrir ar' O ,

L'appel base.,,iit-hdra-,

(;

Chapitre 13 : 0uel est donc ce polymorphisme

? 30 5

Le polqnorphisme
Vous pouvez surcharger une mthocle d'une classe de base avec une mthode d'une sous-classe. Aussi simple qu'elle paraisse, cette solution apporte des possibilits considrables, et de ces possibilits viennent des dangers.

Voil une question difficile : la clcision d'appeler l,ankAccount.l^iithdraw ou Sar.irigsh.iri,r,ir:--. r;- .t. i:a,,ii cloit-elle tre prise la compilation ou I'excution ? Pour faire comprenclre la diffrence, je vais modifier le programme Hiding,r'itlidr.:r,.' i . que nous avons vu plus haut d'une manire apparemment inoffensive. Je vais appeler cette nouvelle version Hidlns.l\iithlr;i,,,,:.1 F,.I.,':i:,rpi,-',-,,-l-" (i'ai allglelistingenn'ymettant pas ce qui n'a pas chang).
public class C1ass1
{

nrrhl i r. ctnti

void

MakeAWithdrar*a1 {BankAccount ba,

decinal
{

rnAmount)

ba. Withdraw (mAnount )


)

public static int Main(stringIJ args)


{

BankAccount ba;

SavingsAccount sa;

ba = new BankAccount(200M);
MakeAWithdrawal MakeAWithdrawal

(ba,

100M)

sa = new SavingsAccount(200M, L2);

(sa, 100M) ; tl // affiche 1e solde rsultant

Console.WriteLine("\nvoqu par un intermdiaire,") ; Console.l,iriteLine("Le solde de BankAccount est [0:C]", ba.Balance) ; Console.1{riteLine("Le solde de SavingsAccount est {0:CJ", sa.Balance) /l attend confiriiiation de 1'utilisateur Console . l,trriteLine ( "Appuyez sur Entre pour terniner. . . " ) ;
Console.
Read

return
]

0;

306

Ouatrime partie:La programmation oriente obiet

La sortie de ce progralntne peut tre ou ne pas tre dconcertante, selon ce que vous attendiez
:

voqu pr un interndiaire, Le solde de BankAccount est i00,00 F Le solde de SavingsAccount est 100,00 Appuyez sur Entre pour terminer...

Cette fois, plutt qtre cl'effectuer un retrait datts lilain O, le programme passe I'objet cclmpte ttancaire la fonctittn l"lal.reAl,"r'ithdrar,,ual O. La premire question est clpourvue de mystre : Pourquoi la fonction l"1ake,-riritrcli il-niar,. i r ac<:epte-t.-elle un ttbjet Sa.,'ingsr\ccount alors qu'elle dit clairement qu'elle attend un objet Iiar-LkHcc(,u1rt ? La rponse est vidente : "Patrce qu'un S :- -., i r' g sAc c i)'r1n i. ES'|'-UN B a nkAc r: oLint." La deuxime question est plus subtile. Quand il lui est pass un objet Bankrrccourlt, l"iaiier,i.ii tt'c-l1a',,,,ai (t invoque Bankccount . \,^rithciraw (J. C'est assez clair. Mais lorsqu'il lui est pass un objet SavingsAccount, IlakeAii ithtrrawal i ) appelle la nrnre mthode. Ne devrait-elle pas invoquer la mthode n'i t-t,c r aw ( ) dans la sous-classe ?
I-e procureur veut montrer que I'appel ba
.'r^i

ithtlr:aw O devrait invoquer

la mthode Barii..c,-t.rn:.i^iir-irrlrai;O.ll est clair que I'objet ba est un BankAccouirt. Faire autre chose ne pourrait que faire naltre la confusion. Mais la dfense a rles trnclius dans i"lain 1) pour prouver que bien que I'objet ira soit dclar comme ban.l.'.^c.roi-1ir-, c'est en fait un Savin! sc. r,rirrr. Le jury ne s'y retrouve plus. Les deux arguments sont tout aussi valides I'un que I'autre.
Dans ce cas, C# se range du ct du procureur. Le choix le plus str est de s'en tenir au type dclar, parce qu'il vite toute erreur de communication.

L'objet est donc clclar tre un Bankc.rc,un1,, et la cause est enteudue.

9u'tl A-t-il de mal utiliser chaque fos le


tqpe dclar

Dans certains cas, vous ne vouclrez pas utiliser le type dclar. Ce que vous voudrez vrailnent, c'est effectr-rer I'appel sur la base du type rel, c'est--dire le type i'exctrtion, par oppositiort au type dclar. Cette possibilit de dcider I'excution s'appelle polyrnorphisme, ou late binding (liaison

tardive). Utiliser le type dclar s'appelle early binding (liaison prcoce).

Chapitre 13 : 0uel est donc ce polymorphisme


1t$!Qa.

? 307

{:/rr

=qE)

Le terme polymorphisme vient du grec : poly signifie plusieurs , morph

signifie forme, et isme signifie peut-tre quelque chose en grec. Polymorphisme et late binding ne sont pas exactement la mme chose,
La de se

K mais la diffrence est subtile. notion polymorphisme rfre la possibilit de dcider I'excution qu'elle mthode invoquer. La notion '(dg, de late binding rfre la manire dont un langage implmente
se le

polymorphisme.
Le polymorphisme est un aspect crucial de la puissance de la programmation oriente objet. Il est si important qu'un langage qui ne le comporte pas ne peut pas tre prsent comme un langage orient objet.

'(dE,)

1t$!Qa. \ Un langage qui comporte des classes mais pas le polymorphisme est ^.v7q appel langage base objets. Le langage Ada en est un exemple.
Sans le polymorphisme, I'hritage n'aurait gure de signification. Je vais

donner encore un exemple pour vous le montrer. Imaginez que j'aie crit ce programme succs mondial qui utilisait une classe nomm Student. Aprs quelques mois de conception, de codage et de test, je publie cette application pour rcolter les avis de mes collgues et des critiques en tout genre (on a mme parl de crer une nouvelle catgorie de prix Nobel pour le logiciel, mais par modestie jai ignor ces suggestions).
Le temps passe, et mon patron me demande d'ajouter ce programme la prise en compte des tudiants diplms, qui ne sont pas tout fait identi-

ques aux tudiants ordinaires (ils revendiquent sans doute de ne pas tre identiques du tout). Supposez que la formule de calcul des frais de scolarit soit compltement diffrente de celle utilise pour un tudiant ordinaire. Mais mon patron ne sait pas et ne veut pas savoir qu' ce niveau de profondeur du programme il y a de nombreux appels la fonction calcTuition O .

void SomeFunction(Student
t

s)

quoi qu'el1e puisse faire.


continue

s.CalcTuition0;
l

il me faudrait passer en revue le code de sorleF,;ncticn O pour vrifier si I'objet student qui lui est pass est un Gradua*r-eS*.udent ou un Student. Le programme appellerait Student. CaicTuition ( ) lorsque s est un Student, et GraduateStudent. CalcTuition O lorsque c'est un tudiant diplm.
Si C# n'admettait pas le late binding,

308

0uatrime partie:La programmation oriente obiet

Tout cela ne se prsente pas mal, sauf pour trois choses. Pour commencer, ce n'est I qu'une fonction. Supposez que calcTuition () soit appele depuis de nombreux endroits. Supposez aussi que calcTuition i) ne soit pas la seule diffrence entre les deux classes. Mes chances de trouver tous les endroits qui doivent tre modifis ne sont pas des plus leves.
Avec le polymorphisme, je peux laisser C# dcider de la mthode appeler.

Accder par le polqmorlrhsme une nthode redf inie en utlisant is


Comment rendre mon programme polymorphe ? C# offre une approche pour rsoudre le problme manuellement avec un tout nouveau mot-cl : is. L'expression ba is SavingsAccount retourne true ou false selon la classe de I'objet I'excution. Le type dclar pourrait tre BankAccount, mais quel est-il en ralit ?
public class Classl
{

public static void


{

MakeAWithdrawal(BankAccount ba,

decinal

mArnount)

if ba is
{

Savi-nesAccount

SavingsAccount sa
el se

(SavingsAccount)ba;
;

sa . Withdraw (urAmount )

ba . Withdraw(mAmount )

Maintenant, quand l'{aln O passe la fonction un objet SavingsAccounr, MakeA\ii thd rawaf ( ) vrifie I'excution le type de I'objet ba et invoque Sa';ingsAccount . i,r'ithdr a"' ( )
.

=[J\y \/

og$!ea^ Au passage, je vous signale que le programmeur aurait pu raliser le cast et dans une mme ligne : ( (SavingsAcr.lount )ba) .',,^/ithdraw (mAmount.) . ae/!!t\ \ I'appel 9/nl?Y - "ne mentionne la chose que parce que vous la verrez beaucoup dans des j Je
programmes crits par des gens qui aiment faire de I'esbroufe.

Ghapitre 13:0uel est donc ce polymorphisme

? 309

mauvaise ide. Elle ncessite que SomeFunction O connaisse tous les diffrents types d'tudiants et quels sont ceux qui sont reprsents par les diffrentes classes. Cela fait peser une trop lourde responsabilit sur les paules de la pauvre vieille SomeFunction O . Pour le moment, mon application ne connat que deux types de comptes bancaires, mais supposez que mon patron me demande d'en implmenter un nouveau, CheckingAccount, t que celui-ci soit associ une autre politique de Withd raw ( ) . Mon programme ne fonctionnera pas correctement si je ne trouve pas toutes les fonctions qui testent I'excution le type de cet argument.

En fait, I'approche

"is" fonctionne, mais c'est vraiment une

Dclarer une mthode comme rtuelle


En tant qu'auteur de SomeFunction O , je ne veux pas connaltre tous les

diffrents types de compte. Je veux que ce soit aux programmeurs qui utilisent SomeFunction O de connaltre leurs types de compte, et qu'ils me laissent tranquille avec a. Je veux que ce soit C# qui prenne les dcisions sur les mthodes invoquer en fonction du type de I'objet I'excution. Pour dire C# de faire le choix I'excution de la version de Withdrawai o utiliser, je marque la fonction de la classe de base avec le mot-cl virtual, et la fonction de chaque sous-classe avec le mot-cl overr,de.
J'ai rcrit I'exemple de programme prcdent en utilisant le polymorphisme. J'ai ajout des instructions de sortie aux mthodes L/ithdraw ( ) pour montrer que ce sont effectivement les bonnes mthodes qui sont invoques ('ai supprim ce qui faisait double emploi pour ne pas vous ennuyer avec des choses inutiles). Voici donc le programme Polymorphiclnheritance:

,,"$$Ig
,,.&v- l- lrJ,r ,\ l-..ffvn

ry

I I"\7

// II
t

folynorphiclnheritance
System;

* utilise le
e

redfinir
e Polymorphic Inheritanc

polynorphi.sne pour une mthode dans la classe de base

using

nmespc

/i
I
{

BankAccount

public class i

- un compte bancaire trs ordinaire


mne chose

BankAccount

public virtual decimal Withdraw{decinal


decinal
nAmountToWithdrarrr

L ,
if
I

la

ici
mAnount)

rnAnount

(nAmountToWithdraw

mBalance)

3 |0

Ouatrime partie : La programmation orienre objer

mAmountTol^lithdraw

mBalance

l
nBalance -= mnountToWithdraw;

return
l

mAmountToWithdraw

//
I
t

SavingsAccount

compte bancaire

qui rapporte des intrts

public class SavingsAccount


l

BankAccount

l , la mme chose ici- aussi / tllthdraw * tout retrait est autoris jusqu' la valeur II du solde ; retourne le nontant retir
i

override public decimal Wj.thdraw(decirnal nrWithdrawal)


t

// soustrait 1.50 F base.l{ithdrav(1,5M);


I

tl

vous pouvez naintenant effectuer un


base. Withdrarq(rirWithdraval)
;

retrait

avec ce qui reste

return
)

public class
t

C1ass1

public static void


I t

MakeAWithdrawal(BankAccount ba,

decimal
ba. Withdraw(mAmount) ]
;

mArnount)

public static void Majn(stringll args)


t ll ,

..

nae de rla changement nlrcnnnmnn+ pas

i^-i ici

-^-1,,^ plus non

l
l l

L'excution de ce programme donne la sortie suivante


voqu par un interrndiaire, Le solde de BankAccount est 100,00 F Le solde de SavingsAccount est 98,50 Annrrvoz srrr Fntre norr r terminpr LLLrUrtr! | t
.

thd ra',\i i. ) est marque comme .,,i rrual dans la classe de base BarrkAc.culrt. alors que la mthode i,,ithdraw O de la sous{lasse est marque avec le mot<l L).u,errlCe. Bien que la mthode MakeAl,/ithdrawal 0 soit inchange, le programme donne une sortie cliffrente parce que I'appel ba.l^rithri ra-w ( ; est rsolu sur la base du type de ba I'excution.
La fonction

-r,r'i

Ghapitre 13 : Ouel est donc ce p0lymorphisme

? 3l I

^$C .z 7X t(?, Y

Pour vous faire une ide prcise de la manire dont tout cela fonctionne, il est ncessaire que vous excutiez le programme clans le dbogueur de Visual Studio. Gnrez le programme comme d'habitude, et appuyez sur la touche Fl1 autant de fois que ncessaire pour faire avancer le programme pas pas. Il est impressionnant de voir le mme appel aboutir une mthode ou une autre selon le moment o il se produit.

La ytriode abstraite de C#
ce que je sais, un canarcl est un type cl'oiseau, de mme qu'un colibri et une hirondelle. En fait, tout oiseau est un sous-type d'oiseau. La rciproque de ce principe est qu'il n'existe pas d'oiseau qui ne soit pas un soustype d'oiseau. Cette remarque ne paralt pas trs profonde, mais en fait, elle I'est. L'quivalent logiciel de cet nonc est que tout objet oiseau est une instance d'une certaine sous-classe de Ci seau. Il n'y a pas d'instance de la classe Oisea'i.

Il y a divers types d'oiseaux qui partagent de nombreuses proprits (sinon, ils ne seraient pas des oiseaux), mais il n'y a pas deux types dont toutes les proprits sont les mmes (sinon, ce ne serait pas deux types diffrents). Pour prendre un exemple particulirement simple, tous les oiseaux n'ont pas la mme manire de Voier O. Le canard a son style, I'hirondelle aussi. Ils ont beaucoup de points communs, mais ce n'est pas exactement la mme chose. Le style du colibri est compltement diffrent. Ne me posez pas de questions sur les meus et les autruches.
Mais si tous les oiseaux n'ont pas la mme manire de voler, alors, qu'est-ce que Oiseau. Voler () ? La rponse est simple : c'est le sujet de cette section.

Le factorinq entre classes


Les gens produisent des taxonomies d'objets en les regroupant sur la base des proprits qu'ils partagent. Pour comprendre comment fonctionne la classification, considrez les classes HighSchool et University, comme le montre la Figure 13.1. Cette figure utilise UML (Unified Modeling Language), qui est un langage graphique, pour dcrire une classe en mme temps que les relations de celle-ci avec d'autres.

3 |2

0uatrime partie : La programmation oriente objet

Figure 13.1 Une description sous la forme UML des classes Hi ghS choo i
:

University
- numStudents

Student

+ Enroll 0

- numStudents + nAvgSAT

et Unive r

+ Enroll 0
+ GetGrant 0

sily.

Le langage UML
Le langage

UML{Unified Modeling Language)estun langagede modlisation,permettantde

dfinir clairement une grande partie des relations entre des objets dans un pr0gramme. L'un des avantages d'UML est que I'on peut en ignorer les aspects les plus spcialiss sans en perdre entirement la signification.
Les caractristiques de base d'UML sont les suivantes
:

tl tl tl

Une classe est reprsente par un rectangf e, divis verticalement en trois sections. Le nom de la classe apparat dans la premire section en partant du haut.

Les membres donne de la classe apparaissent dans la section du milieu, et les mthodes dans la section du bas. Si la classe n'a pas de membres donne ou de mthodes, vous pouvez omettre la section du milieu ou celle du bas.
Les membres prcds par un signe + sont publics, et ceux qui sont prcds par un signe - sont privs. UML ne dispose pas de symboles pour reprsenter la visibilit et la protection.

Un membre priv n'est accessible qu' d'autres membres de la mme classe. Un membre public est accessible toutes les classes.

tl

Le symbole "{abstract}" ct d'un nom indique que [a classe ou la mthode est a bstraite. UML utilise en fait un symbole diffrent pour une mthode abstraite, mais je simplifie.

Ghapitre 13 : Ouel est donc ce polymorphisme

? 313

Une flche entre deux classes reprsente une relation entre ces deux classes. Un nombre au-dessus du trait exprime la cardinalit. Le symbole "*" signifie un nombre quelconque. Si aucun nombre n'est prsent, la cardinalit estsuppose gale 1. Ainsi, dans la Figure 13.1, vous pouvez voir qu'une universit peut avoir un nombre

quelconque d'tudiants.

tl

Un trait se terminant par une grande flche largement ouverte exprime la relation EST-UN {l'hritage). D'autres types de relations sont galement reprsents, dont la

relation A-UN.

Un objet Voiture EST_UN Vhicule, mais un objet Voiture A_UN Moteur. Vous pouvez voir dans la Figure 13.1 que les high schools et les universits ont en commun plusieurs proprits similaires (en fait, bien plus qu'on ne pourrait le penser). Ces deux types d'tablissement offrent une mthode Enrol1 O, publiquement disponible, pour ajouter de nouveaux objets Si-udent-. En outre, I'un et I'autre comportent un objet priv nurnStuden*;s eui contient le nombre d'tudiants de l'tablissement. Enfin, I'une des caractristiques communes est la relation entre les tudiants : un tablissement peut avoir un nombre quelconque d'tudiants, mais un tudiant ne peut faire partie que d'un seul tablissement la fois. La plupart des universits, et mme certaines high schools, offrent plus que ce que je viens de dcrire, mais il me suffit d'un type de chaque.
En plus des caractristiques d'une high school, I'universit contient une

mthode GetGrant O et un membre donng n[lrgSAT. L'entre dans une high school ne ncessite pas I'examen SAT (Scholastic Aptitude Test), et on ne peut y obtenir de prt fdral ( moins que je ne sois all dans les mauvaises high schools).
La Figure l3.l est tout fait correcte, mais beaucoup d'informations s'y trouvent dupliques. On pourrait rduire cette duplication en permettant la classe la plus complique, University, d'hriter de la classe plus simple HighSchool, comme le montre la Figure 13.2. La classe HrgfrSchocl est inchange, mais la classe Uni.,rersitv est plus facile dcrire. Nous disons que "une University est une Highschool qui possde aussi un objet nAvgSAT et une mthode GetGrant O ", mais cette solution comporte un problme fondamental : une universit n'est pas une high school qui... quoi que ce soit.

3 I tl

0uatrime partie : La programmation oriente objet

High School
Figure 13.2
:

L'hritage de

- numStudents

HighSchcol

simplifie la
c

+ Enroll 0

lasse
-

Univer

sity,

mais

il

University

introduit
quelq ues

+ nAvgSAT
+ GetGrant 0

problmes.

Vous me direz : "Et alors ? I'hritage fonctionne, et a fait du travail en moins." C'est vrai, mais les rserves que je fais ne sont pas que de triviales questions de style. De fausses reprsentations de ce genre sont sources de confusion pour le programmeur, dans I'immdiat comme par la suite. Un jour, un programmeur qui ne connaltra pas mes astuces de programmation devra lire mon code et comprendre ce qu'il fait. Une reprsentation fausse est clifficile comprendre et utiliser.
En outre, de telles reprsentations fausses peuvent conduire ultrieurement des problmes. lmaginez que la high school dcide de nommer un tudiant "favori" pour son banquet annuel (usage local). Le programmeur, astucieux, ajoute alors la classe HighSchool la mthode \larneFavorit.e O, que I'appli-

cation invoque pour nommer favori I'objet Student correspondant. Mais maintenant, j'ai un problme. La plupart des universits n'ont pas pour pratique de nommer quoi que ce soit favori, mais aussi longtemps que Universi-ty hrite de uighschool, elle hrite de la mthode NameFavorite (). Une mthode supplmentaire peut sembler sans importance. Vous pourriez dire : "ll suffit de I'ignorer."
Une mthode de plus n'a pas grande importance, mais c'est une pierre de plus dans le mur de la confusion. Avec le temps, les mthodes et les proprits supplmentaires s'accumulent, jusqu' ce que la classe iJni versity se trouve bien encombre de tous ces bagages. Ayez piti du pauvre dveloppeur de logiciel qui doit comprenclre quelles mthodes sont "vritables" et lesquelles ne le sont pas.
Un tel "hritage de complaisance" conduit un autre problme. ta manire dont elle est crite, la Figure 13.2 implique qu'une Uni-rersity et une HighSchool ont la mme procdure de recrutement. Si peu waisemblable que cela paraisse, supposez que ce soit wai. Le programme est dvelopp, emball

Ghapitre 13 : 0uel est donc ce polymorphisme

? 3l 5

et expdi au public qui n'attend que lui (bien str, je n'ai pas oubli d'y mettre le nombre de bogues ncessaires pour que tout le monde veuille s'offrir, moyennant un prix tout fait raisonnable, la mise jour vers la version 2).

Quelques mois passent, et l'tablissement dcide de modifier sa procdure de recrutement. Il ne sera pas vident pour tout le monde qu'en modifiant la procdure de recrutement de la high school c'est aussi la procdure d'inscription au collge voisin qui a t modifie.
Comment faire pour viter un tel problme ? Une solution est de ne pas aller l'cole, mais une autre consiste corriger la source du problme : une universit n'est pas un type particulier de high school. Il existe bien une relation entre les deux, mais cette relation n'est pas EST_UN. Au contraire, universits et high schools sont deux types diffrents d'tablissement scolaire. La Figure 13.3 dcrit cette relation. La classe School nouvellement dfinie contient les proprits communes des deux types d'tablissement, y compris leurs relations avec les objets Student. School contient mme la

mthode commune Enro11O, bien qu'elle soit abstraite car Highschool et University ne I'implmentent pas de la mme manire.

School {abstract}

numStudents

E
Figure 13.3
:

+ Enroll 0
- {abstract}
o1 -

Hl ghS cho
9t
s

ity

Univer

doivent

I'une et I'autre tre bases sur


une classe c0mmune

+ nAvgSAT

School.

+ Enroll 0 + NameFavorite

+ Enroll 0
+ GetGrant 0

Les classes HighSchool et University hritent maintenant toutes deux d'une classe de base commune. Chacune contient des membres qui lui sont propres:l'TameFavorite O dans le cas de HighSchool, et GetGrant () pour University. De plus, ces deux classes substituent la mthode Enro1l ( ) une redfinition de celle-ci dcrivant le mode de recrutement de chaque type d'tablissement.

3 |6

0uatrime partie : La programmation oriente objet

L'introduction de la classe School prsente au moins deux gros avantages. Le premier est de correspondre la ralit. Une University est une School, mais ce n'est pas une fligirSchool. Correspondre la ralit, c'est bien, mais ce n'est pas suffisant. Le deuxime avantage est d'isoler chaque classe des modifications apportes I'autre. Quand mon patron viendra me voir un peu plus tard, ce qui se produira sans aucun doute, pour me demander d'introduire le discours de bienvenue I'universit, je pourrai ajouter la mthode Commencenent-Speech i) la classe'tln1,",.ersity, sans affecter la clasSe HighSchcoi.
Ce processus qui consiste externaliser les proprits communes de

classes similaires s'appelle factoring. C'est une caractristique importante des langages orients objet, pour les raisons que nous avons dcrites plus haut, plus une nouvelle : la rduction de la redondance. Je vais me rpter la redondance ne peut faire que du mal. Ne la laissez jamais entrer.

#\ (l.,/

Le factoring n'est lgitime que si la relation d'hritage correspond la ralit. Son application une classe S,--.ur rs et une classe Joystick parce que I'un comme I'autre est un clispositif matriel de pointage est lgitime. Son application une classe S,, r i I et une classe Af f ichag3 parce que I'un comme I'autre fait des appels cle bas niveau au systme d'exploitation ne I'est pas. Le factoring peut produire plusieurs niveaux d'abstraction, et en gnral, c'est le cas. Par exemple, un programme crit pour une hirarchie plus complte d'tablissements scolaires pourrait avoir une structure de classes plus proche cle celle montre par la Figure 13.4.

Etablissement scolaire Figure 13.4:

Le factoring prod u it g n ra lement des c0uc nes

r--

L{"+,q

Suprieur

supplmen-

taires dans la hirarchie d'hritage.

;r Lqi:e

Ecole suprieure
l

Universit

Classes prparatoires

Vous
Sch,:o

voir clue j'ai insr une nouvelle classe entre Universitv et -.11,,,,1i ir 1re. J'ai subdivis cette nouvelle classe en Collese et

Ghapitre 13 : 0uel est donc ce polymorphisme

? 3l 7

Urrl-"'er s-:i.i-r'. Ce type de hirarchie de classes plusieurs niveaux est courant et sotrhaitable lorsque I'on met des relations en facteurs cornmuns. Il correslroncJ a\ la ralit et il pourra parfois vous suggrer des solutions subtiles un problme. Remarr.luez toutefois qu'il n'y a pas de Thorie unifie du factoring applicable n'importe tluel ensernble cle classes. Les relations montres par la Figure 13.4 semblent naturelles, mais supposez qu'une application diffrencie plutt les types d'tatrlissenrents scolaires selon qu'ils sont aclministrs ou non par des ltrs locaux. Les relations correspondantes, montres par la Figure 13.5, conviennent nrieux ce type de problme.

Figure 13.5:

ll nyapas
de factoring "universel".

La manire ppropri e de dfinir les

flasses
dpend en partie du problme
rsoudre.

i
l
I

I I

Collge

Classes prparatoires

ll ne me reste Qu'un

concept

:la

classe abstraite

Si intellectuellement satisfaisant que puisse tre le factoring, il introduit un problme qui lui est propre. Revenez encore une fois BankAccount. Pensez un instant la manire dont vous pourriez dfinir les diffrentes

fonctions membre qui y sont dfinies.


La plupart des fonctions membre de BankAccount ne sont pas un problme, car elles sont implmentes de la mme manire par les deux types de compte. C'est avec BankAccount que vous devez implmenter ces fonctions communes. '!\ i *, hd r aw O , quant elle, est diffrente. Les rgles de retrait

d'un compte rniunr sont diffrentes de celles d'un compte chque. Vous aurez donc implmenter SavingsAccoulrt . i'jithd rawal ( ) diffremment de ClireckingAccount .ldirhdraw O. Mais comment implmenter
Patk..cc:)'-:.-.
.'.

i ,hr t ,^,a I

3l 8

0uatrime partie : La programmation oriente objet

Si vous demandez son aide au

directeur de la banque, j'imagine que la


:

conversation pourrait ressembler ceci

"Quelles sont les rgles pour effectuer un retrait sur un compte ?" demandez-vous, plein d'espoir. "Quel type de compte ? Un cornpte chque ou un compte rmunr ?" "Un compte, rpondez-vous, simplement un compte." Regard vague et dsespr.
Le problme est que cette question n'a pas de sens. Il n'y a pas de compte qui

soit "simplement un compte". Tous les comptes (dans cet exemple) sont soit des comptes chque, soit des comptes rmunrs. La notion de compte est une notion abstraite qui rassemble les proprits communes aux deux classes
correspondant une ralit concrte. Elle est incomplte, car il lui manque la

proprit critique',,\rithciraw O (en allant plus loin dans les dtails, vous trouverez peuttre d'autres proprits qui font dfaut un simple compte).
Le concept de EarLkAccount est un concept abstrait.

Comment utliser une classe abstraite .l


Une classe abstraite sert dcrire des concepts abstraits. Une c/czsse abstruile est une classe comportant une ou plusieurs mthodes abstraites. Une mthode abstraite est une mthode dclare abstract. Allons plus loin : une mthode abstraite n'a pas d'implmentation. Vous tes maintenant dans le brouillard.

Considrez ce programme de dmonstration, allg pour la circonstance

// Abstractlnheritance - 1a classe BankAccount est vraiment abstraite parce qu'il n'existe pas II II d'implmentation unique pour Withdraw
nanesDace Abstractlnheritance
L

..^.: -^ uJrr

C,,a+^*. yLtrilI

ll II
I
t

'

AbstractBase0lass

cre une elasse abstraite de contenant rien d'autre qu'une mthode OutputQ

abstract public class AbstractBaseClass

3 I8

Ouatrime partie : La programmation oriente obiet

Si vous demandez son aide au

directeur de la banque, j'imagine que la conversation pourrait ressembler ceci :


"Quelles sont les rgles pour effectuer un retrait sur un compte ?" demandez-vous, plein d'espoir.
"Quel type de compte ? Un compte chque ou un compte rmunr "Un compte, rpondez-vous, simplement un compte." Regard vague et dsespr.
Le problme est que cette question n'a pas de sens. Il n'y a pas de compte qui soit "simplement un compte". Tous les comptes (dans cet exemple) sont soit des comptes chque, soit des comptes rmunrs. La notion de compte est
?"

une notion abstraite qui rassemble les proprits communes aux deux classes corresponclant une ralit concrte. Elle est incomplte, car il lui manque la proprit critique l^Jithdraw 0 (en allant plus loin dans les dtails, vous trouverez peuttre d'autres proprits qui font dfaut un sirnple compte). Le concept de BankAccount est un concept abstrait.

Comment utiliser une classe abstraite


Une classe abstraite sert clcrire des concepts abstraits.

.)

Une c/ass e abstraile est une classe comportant une ou plusieurs mthodes abstraites. Une mthode abstraite est une mthode dclare abstract. Allons plus loin : une mthode abstraite n'a pas d'implmentation. Vous tes maintenant dans le brouillard.

Considrez ce programme de dmonstration, allg pour la circonstance

// Abstractlnheritanee - la classe BankAccount est vrainent abstraite parce qu'i1 n'existe pas II d'irnplmentation unique pour Withdraw II
nanespace Abstractlnheritance
t

usi-ng Systen; ll AbstractBaseClass


I
t

cre une classe abstraite de contenant rien d'autre qu'une mthode 0utPut0

abstract public class AbstractBaseClass

3l 6

ouatrime partie : La programmation oriente obiet

L'introduction de la classe School- prsente au moins deux gros avantages. Le premier est de correspondre la ralit. Une University est une School,
mais ce n'est pas une lii gh-scho':i' Correspondre la ralit, c'est bien, mais ce n'est pas suffisant. Le deuxime avantage est d'isoler chaque classe des modifications apportes I'autre. Quand mon patron viendra me voir un peu plus tard, ce qui se prodtrira sans aucun doute, pour me demander d'introduire le discours de bienvenue I'universit, je pourrai ajouter la mthode conirnencenelll-s;e e'rl il la classe Llrrllersit-1', sans affecter la classe HighSchco-.
Ce processus qui consiste externaliser les proprits commtlnes de

classes similaires s'appelle foctoring. C'est une caractristique importante des langages orients objet, pour les raisons que nous avons dcrites plus haut, plus une nouvelle : la rduction de la redondance. Je vais me rpter la redondance ne peut faire que du mal. Ne la laissez jamais entrer.

=(0,

*\

Le factoring n'est lgitime que si la relation d'hritage correspond la ralit. Son application une classe Soi,Lrl s et une classe Joystick parce que I'un comme I'autre est un dispositif matriel de pointage est lgitime. Son application une classe ;lil.li:rs et une cla.Sse,{ff ichage parce que I'un Comme I'autre fait cles appels de bas niveau au systme d'exploitation ne I'est pas. Le factoring peut procluire plusieurs niveaux d'abstraction, et en gnral,

c'est le cas. Par exemple, un programme crit pour une hirarchie plus complte d'tablissements scolaires pourrait avoir une structure de classes plus proche de celle montre par la Figure 13.4.

Etablissement scolaire Figure 13.4:

gnra lement des


couc hes

factoring produit
Le

Suprieur

'---3__,
1ry.":_l

supplmen-

taires dans la hirarchie


d hritage.

E."b;pd;l
Classes prparatoires

Universit

Vous pouvez voir clue j' ai insr une nouvelle classe entre University et ' l r's. J'ai subdivis cette nouvelle classe en Coileee et Sch"..r:!i;.--r.

3 |4

Ouatrime partie : La programmation oriente objet

High School
Figure 13.2
:

L'hritage de

- numStudents

simplifie la
classe

HlghSchool

+ Enroll 0

Univer

sity,
quelq
u

mais
es

il

introd uit problmes. + GetGrant 0

Vous me direz : "Et alors ? I'hritage fonctionne, et a fait du travail en moins." C'est vrai. mais les rserves que je fais ne sont pas que de triviales questions de style. De fausses reprsentations de ce genre sont sources de confusion pour le programmeur, dans I'immdiat comme par la suite. Un jour, un programmeur qui ne connatra pas mes astuces de programmation devra lire mon code et comprendre ce qu'il fait. Une reprsentation fausse est difficile comprendre et utiliser.
En outre, de telles reprsentations fausses peuvent conduire ultrieurement des problmes. Imaginez que la high school dcide de nommer un tudiant

"favori" pour son banquet annuel (usage local). Le prograrnmeur, astucieux, ajoute alors Ia classe Highschool la mthode NarneFavorite O, que I'application invoque pour nommer favori l'objet Student correspondant. Mais maintenant, j'ai un problme. La plupart des universits n'ont pas pour pratique de nommer quoi que ce soit favori, mais aussi longtemps eue University hrite de HighSchool, elle hrite de la mthode NameFavorite O. Une mthode supplmentaire peut sembler sans importance. Vous pourriez dire : "ll suffit de I'ignorer,"
Une mthode de plus n'a pas grande importance, mais c'est une pierre de plus dans le mur de la confusion. Avec le temps, les mthodes et les proprits supplmentaires s'accumulent, jusqu' ce que la classe Uni versit's se trouve bien encombre de tous ces bagages. Ayez piti du pauvre dveloppeur de logiciel qui doit comprendre quelles mthodes sont "vritables" et lesquelles ne le sont pas.
Un tel "hritage de complaisance" conduit un autre problme. ta manire dont elle est crite, la Figure 13.2 implique qu'une University et une

HighSchool ont la mme procdure de recrutement. Si peu waisemblable que cela paraisse, supposez que ce soit wai. Le programme est dvelopp, emball

3 |2

Ouatrime partie : La programmation oriente objet

Figure 13.1 : Une description sous la forme UML des classes Hi ghS chc o i

University
- numStudents

Student

+ nAvgSAT

etUrrlver
ci trr

+ Enroll 0 + GetGrant

re9!Qa.
o ^w7-7 z

Le langage UML
Le langage UML {Unified Modeling Language} est un langage de modlisation, permettant de dfinir clairement une grande partie des relations entre des objets dans un programme. L'un des avantages d'UML est que I'on peut en ignorer les aspects les plus spcialiss sans en perdre entirement la signific ation.

Les caractristiques de base d'UML sont les suivantes

,/ / /

Une classe est reprsente par un rectangle, divis vefticalement en trois sections. Le nom de la classe apparat dans f a premire section en partant du haut. Les membres donne de la classe apparaissent dans la section du milieu, et les mthodes dans la section du bas. Si la classe n'a pas de membres donne ou de mthodes, vous pouvez omefire la section du milieu ou celle du bas.
Les membres prcds par un signe + sont publics, et ceux qui sont prcds par un signe - sont privs. UML ne dispose pas de symboles pour reprsenter la visibilit et

la protection.

Un membre priv n'est accessible qu' d'autres membres de fa mme classe. Un membre public est accessible toutes les classes,

Le symbole "{abstract}" ct d'un nom indique que la classe ou la mthode est


abstraite. UML utilise en fait un symbole diffrent pour une mthode abstraite, mais je simplifie.

3l 0

Ouatrime partie : La programmation oriente obiet

mAmountToWithdraw

= nBalance ;

l nBalance -= mAnountToWithdraur;

return
]

mAmountToWithdraw

//
t
I

SavingsAccount

compte bancaire

qui rapporte des intrts

public class SavingsAccount :

BankAccount

1a mrne chose ici aussiWithdra$ - tout retrait est autoris jusqu' 1a valeur du solde ; retourne 1e nontant retir override public decimal Withdraw(decimal mWithdrarual)

I // II
t
t

/1 soustrait 1.50 F
tl

base.Withdrari( 1 . 5M) ; // vous pouvez maintenant effectuer un

retrait

avec ce

qui reste

return
1

base.Withdrarr(mWithdra,,ral)

public class Classl


{
1 ; yuuJfL ^.,k

void vvlu LLIl ^ .+"+is

(BankAccount ba, MakeAllithdrawal lls^cnnfLlrurqwcf \!qrrnrr


dec

imal

rrrAmount

ba . Withdraw (nAmount )

nrrhiir. stetic void Main(string[] args)


\v9+4..oLJ_-o-'

I . . . pas de changement ici

non Plus

]
l

l
1

L'excution de ce programme donne la sortie suivante


par un interndiaire, Le solde de BankAecount est 100,00 F Le solde de SavingsAecount est 98,50 Appuyez sur Entre pour terminer...
voqu

La fonction \^rithdraw

( ) est marque comme virtual dans la classe de base que la mthode tii thd raw ( ) de la soustlasse est marque BankAc c oun r. alors avec le mot-cl override. Bien que la mthode MakeA\,nlithdrawal 0 soit inchange, le programme donne une sortie diffrente parce que I'appel ba. Wlthdraw O est rsolu sur la base du type de ba I'excution.

308

Ouatrime partie : La programmation oriente obiet

Tout cela ne se prsente pas mal, sauf pour trois choses. Pour commencer, ce n'est I qu'une fonction. Supposez que calcTuition () soit appele depuis de nombreux endroits. Supposez aussi que calcTrrition O ne soit pas la seule diffrence entre les deux classes. Mes chances de trouver tous les endroits qui doivent tre modifis ne sont pas des plus leves.
Avec le polymorphisme, je peux laisser C# dcider de la mthode appeler.

Accder rytar le polqmorp(risme une nthode redf inie en utilisant is


Comment rendre mon programme polymorphe ? C# offre une approche pour rsoudre le problme manuellement avec un tout nouveau mot-cl : is. L'expression l:'a is Sa..'ingsAccoLlnr- retourne trlie ou faise selon la classe de I'objet I'excution. Le type dclar pourrait tre BanhAccourir, mais quel est-il en ralit ?
publi-c class
t nrrhl
C1ass1

jn cratis void
ba

MakeAWithdrawal(BankAccount ba,

deci.mal nAmount
{

if
t

is

SavinssAccount

SavingsAccount sa
e1 se

(SavingsAccount)ba;
;

sa . Withdraw (mAmount )

ba

Withdrav

(mAmount

MakeA'uJi

Maintenant, quand l.lain () passe la fonction un objet SavjngsAccorjnt, thd rawal ( I vrifie I'excution le type de I'objet ba et invoque Savirrgsc counr .'yiithdraw ( ) .

= \

7^H\ V / \/

1t$!Qa.

Au passage, je vous signale que le programmeur aurait pu raliser le cast et I'appel dans une nrme ligne I ((SavingsA.-iount)ba) .'y/ithdraw(mAmor-int). Je ne mentionne la chose que parce que vous la verrez beaucoup dans des programmes crits par des gens qui aiment faire de I'esbroufe.

Chapitre 14 : Ouand une classe n'est pas une classe : l'interface et la structure

57

EST-UN

Int32

en utilisant le mot-cl 1s. La sortie de cette


:

portion clu

programme se prsente ainsi


Bxtrai.t d'une liste les
L'lment numro 1 est L'lment numro 3 est
2

nombres

entiers

I-e programme termine son numro de cirque en utilisant une fois de plu.s la descendance d'cb j ect. Toutes les sous-classes d''',r: j i,r i ({:'e.st-e\-clire toutes les classes) implmentent toS:r-rrgi,. Par c<insc1uent, si nous

voulons simplement afficher les membres du tableau d'objets. nolls n'avons absolument pas besoin de nous proccuper cle leur type. La section finale de Main () effectue encore une boucle sur les objets clu tableeru. demanclant cette fois chaque objet de se mettre en forme lui-mrne en r-rtilisant sa mthode ToString O. Les rsultats apparaissent ainsi
:

Affichase de tous 1es obiets de la Liste - --"*bpst (th'i c is a <trino) 0hiets|.0'l L-r --J--"-

0hiatc f1 I act (?) pc1 0biets[2] \ur4t L (fl1p."1 Lal 0hipts f?l pct (4)
vvJL rlhr^+ll,r wuJLDLTJ ^^+ CL 1( \J.J \)

rl,r nr^^..-*o p!u!dill[E


uu

(trrrr^trrroFvemnle)
v

Appuyez

sur Entre pour terminer.

Comme les animaux sortant de I'Arche de No. chaque objet se pr.sente comme le seul de sa catgorie. J'ai implment une nrthocle 'i':-. S : : i :, : triviale pour Classi, rien que pour montrer qu'elle sait jouer avec toutes les autres classes.
,

'qE,

dK d'excuter son tour de

En fait, c'est indubitablement ToString O qui permet C, r'rSr, :.,,,: i : e,, magie. Je ne suis pas all voir dans le cocle source, mais je parierais volontiers que',,,rr-te O accepte ses arguments en tant qu'objets. Elle peut alors invoquer simplemett Tr S *, i r i,g . r sur I'objet pour le convertir en un format affichable (en dehors du premier argument, qui

peut contenir des indications {n} de contrle de format).

306

0uatrime partie: La programmation oriente objet

La sortie de ce progl'arnrne peut tre ce que vous attendiez


:

or.r

ne pas tre dconcertante, selon

par un interr:idiaire, Le solde de BankAccount est 100,00 F Le solde de SavingsAccount est 100,00
voqu
Annrrrroz crrr Fntro nnrrr tarminpr

Cette fois. plutt que d'effectuer un retrait clans i'ia-in O, le programme passe I'objet cclntpte tlancaire la fonction :'1ai:erliiitir,lrawai , ). La prernire question est cltrlourvue de rnystre : Pourquoi la fonction l"Iake.tr,,;rhri'.':,1',,:,r , t, ac:cepte-t-elle un objet Sa'.': rLg:;c.ur,t- 31915 qu'elle dit clairement qu'elle attenrl r,rn objet i-:it:-L,errcc.,;i,i,"ri. ? [,a rponse est vidente : "Parce qu'un Sr-" - il!Sri-ii( (iLii,t. ES"|-UN B:rni.:Acccr.iirt. La deuxirne question est plus subtile. Quand il lui est pass un objet Bank,rccount, l'lair.e.iiv: thcira-*a,l O invoque Bai-rkAccoLlnt .l,r j thdraw O. C'est assez clair. Mais lorsqtr'il lui est pass un objet SavingsAccoLrnr, lvlakeAtilithdrav:a, (; appelle la mme mthode. Ne devrait-elle pas invoquer la mthode i,; r -r. hc r a r i I dans la sous-classe ? Le procureur veut montrer que I'appel ba. Witfidr:aw O devrait invoquer la mthode Bar-iirrr r,.; L.Ln t . ,^/i t:hrl raw ( ) . Il est clair que I'objet ba est un BankAccounr-. Faire autre chose ne pourrait que faire natre la confusion. Mais la dfense a cles tmoins dans l,iain ( ) pour prouver que bien que I'objet ba soit dclar comme BankActroi-rrit, c'est en fait un Sa,.,ingsAcc crLrir'1-. I-e jury ne s'y retrouve plus. Les deux arguments sont tout aussi valides I'un que I'autre.
Dans ce cas, C# se range clu ct du procureur. Le choix le plus sr est de s'en tenir au type dclar, parce qu'il vite toute erreur de communication. L'objet est donc dclar tre un BankAccL)unt, et la cause est entendue.

Qu'U

A-t-il de mal utiliser chaque fois le


.)

tqpe dclar

Dans certains cas, vous ne voudrez pas utiliser le type dclar. Ce que vous voudrez vraiment. c'est effectuer I'appel sur la base du type re[, c'esl--dire le type I'excr,rtion, par opposition au type dclar. Cette possibilit de dcider I'excution s'appelle polymorphisme, ou late binding (liaison tardive). Utiliser le type dclar s'appelle early binding (liaison prcoce).

Chapitre 14 : Ouand une classe n'est pas une classe : l'interface et la structure

35 5

nrlh, i rtoD PUUTIL


{

'^^-1 uJdb

public static int Main(string[1 args)


{

I I cr.e un int et f initiali.se 1a valeur 0 int i = new int0; I I Iut assigne une valeur et la restitue par I I I' interface IFormattable i = 1; OutputFunct ion ( i) I I 7a constante 2 inplmente la mme interface
ll .r'

OutputFunction(2); I I en fait, i'ous pouvez utiliser directenent 1e mme objet Console,T,,/riteLine("Extrait directement = {0}", 3.TcStringO) I I cect peut tre trs utile ; vous pouvez extraire un int
I

I d'tne liste

: ;

Console.klriteLine("\nExtrait d'une liste les nombres entiers") object[1 objects = new object[5];

objects[0] = "this is a string";


objects

ltl = z;

objects[2] = new Classi0; objects13l = a: objects [4] = :. S; for(int index = 0; index ( objects.Length: index+r)
{

if (objectsIindex] is int)
{

int n = (int)objectsIindex];
Console.|lriteLine("L'1ment numro {01 est [1]",

index, n);
)

/l altre utilisation de 1'unification int


t

des types

Console.I/riteLine("\nAffichage de tous les objets de 1a 1iste");


nCount = 0;
ir VVJULD/ ahiantol V ^ IIT (nhiont fnroanh !VIgOLI!\VUJLL

Console.l^lriteLine("0b3'ets
] I

[{0}] est <{1})",

nCount**, o.ToString0 ) ;

I attend confirmation de 1'utilisateur Console.ltlriteLinei"Appuyez sur Entre pour terminer. . . ")


Console. Read O return 0;
;

// 0rrtoutFuncrion - affiChe tOUte prhnio nrri imnlAmgllg II ToString0 public static void OutputFunction(IFormattable id)
{

30 4

Ouatrime partie : La programmation oriente obiet

BankAccount ba

= (BankAccount)this; // invoque inlithdraw0 en utilisant cet objet I I appelle 1a fonction BankAccount.Withdrav0


(+*nNuniberOfi,lithdrawalsThisPeriod
dAmountWithdrarrn
]

BankAccount
;

double dAmountl^lithdrawn = ba.Withdraw(dlJithdrawal)

if
{

)
5)
;

1)

*= ba.Withdraw(1.
l

return

dAmountl{ithdrawn

Cette solution fonctionne : I'appel ba . i,Jittrd raw ( ) invoque maintenant la mthode Bank.r,rrrcur-1r,, comme on le voulait. L'inconvnient de cette approche est la rfrence explicite BankAc c ount. Une modification ultrieure du programme pourrait modifier la hirarchie d'hritage de telle manire que lJa.,,:-rrg,.:.{c. )un-, n'hrite plus directement de BankAc count. Une telle rorganisation brise cette fonction d'une faon qu'un nouveau programmeur pourra avoir clu mal trouver. Pour moi, je n'arriverais jamais trouver un bogue comme celui-l. Il vous faut un moyen de dire C# d'appeler la fonction ,r'ithdra'"ro depuis "la classe qui est juste au-dessus" dans la hirarchie, sans la nommer explicitement. Ce serait la classe qui est tendue par Sa,,,lngsAcccuril-. C'est dans ce but que C# comporte le mot-cl base. C'est le mme mot-cl base qu'utilise un constructeur pour passer des arguments au constructeur de la classe de base. Le mot-cl L,ase de C# est la ntme chose que this, mais redfinit le cast la classe de base, quelle que soit cette classe :

/l II
{

Withdraw

- tout retrait est autoris jusqu' 1a valeur du solde ; retourne 1e montant retir
mhrithdrawal)

new

public decimal Withdraw(decimal

II // soustrart 1.50 F

base.itlithdraw(1.5M)

// vous pouvez maintenant effectuer un retrait


return
] base. Withdrai,r(ml,Iithdrawal )
;

avec ce

qui reste

L'appel base . r^,titilila-w


BankAc
c

( .r

invoque maintenant la mthode

oul: . i,,ri-,.hd r aw O , vitant par-l l'cueil qui consiste s'invoquer

elle-mme. En outre, cette solution ne sera pas brise si la hirarchie d'hritage est modifie.

Chapitre 14 : 0uand une ctasse n'est pas une classe:l'interface et la structure

353

L'appel suivant est I'appel la fonction ChangeRef erenceFunction | ). Cette fonction apparalt identique ChangeValueFunction O, I'exception de I'ajout du mot-cl ref la liste des arguments. Comm test est maintenant pass par rfrence, l'argument t se rfre I'objet original test et non une copie nouvellement cre.
Le dernier appel de l.1ain O est un appel la mthode ChangeMethod O. Comme un appel une mthode passe toujours I'objet courant par rfrence, les modifications effectues par cette mthode sont conserves une fois cle retour dans l"1ain ( )
.

La sortie de ce programme se prsente de la faon suivante Valeur initiale de test id = (10.00, 20.00) Valeur de test aprs 1'appel ChangeValueFunction ( 100, 200. 0) id = (10.00, 200.00) Valeur de test aprs 1'appe1 ChangeReferenceFunction (100, 200. 0)

id = (100.0,

200.00)

Valeur de test aprs I'appe1 ChangeMethod(1000, 2000,0) id = (1,000.00, 2,000.00) Appuyez sur Entre pour terminer.

tlconcler la hleur et la rfrence.' unfier le stlstme de ttqpes


Les structures et les classes prsentent une similitude frappante : toutes deux drivent d'0bject. En fait, toutes les classes et toutes les structures, qu'elles le disent ou non, drivent d'Ob j ect. C'est ce qui unifie les diffrents types de variables.

!,/t-t

1t$!Qa" Cette unification des types de variables est trangre aux autres langages

'qE,

drivs de C, comme C++ et Java. En fait, Ia sparation entre les objets de type rfrence et de type valeur en Java peut tre un vritable casse-tte. Comme tout est un casse-tte en C*+, un de plus ou de moins ne se remarque mme pas.

Les ttlpes structure prdfnis


La similitude entre les types structure et les types valeur simples n'est pas que superficielle. En fait, un type valeur simple est une structure. Par

302

Ouatrime partie : La programmation oriente obiet

Et si je redefinis accidentellement une mthode de la classe de base

.)

Il peut arriver tout le monde de redfinir accidentellement une mthode de Ia classe de base. Par exemple, je peux avoir une mthode t/hlcu1e. Virage o qui fait tourner le vhicule. Plus tard, quelqu'un tend ma classe Vhicule avec une classe Avion, dont la mthode Virage 0 est entirement diffrente. Il est clair que nous avons l un cas de confusion d'identit. Ces deux mthodes n'ont rien voir I'une avec I'autre, sinon qu'elles portent le mme nom.

Heureusement pour nous, C# sait dtecter ce problme.


En compilant I'exemple prcdent, Hiding\,Jithdrar^; () , C# gnre un avertissement patibulaire. Le texte de ce message est un peu long, mais en voici la partie importante :
Le not-cl nerl est requis sur ( ' Hidinglrlithdrawal . SavingsAc eount . liithdrav dec imal .l fi snlrp e memhre hrit ' HidingWithdrawal. BankAccount.I,iithdrau(decimal)'

)' , car il

C# essaie de vous dire que vous avez crit dans une sous-classe une mthode portant le mme nom qu'une mthode de la classe de base. Estce vraiment ce que vous vouliez faire ? message n'est qu'un avertissement. Vous ne le remarquerez mme pas, ^$st -a Ce moins de passer la fentre Sortie pour voir ce qui y est affich. Dans 7X presque tous les cas, vous y verrez un avertissement qui vous prvient que t\!|, quelque Y pourrait pas

chose

bien vous mordre si vous n'y mettez

bon ordre.

Le descripteur new indique C# qu'une mthode est redfinie intentionnellement et que ce n'est pas le rsultat d'une ngligence :

/i
t
I
(

new

plus de problrnes avec withdraw0 public decimal Withdrar,r(decinal dl^lithdrar,ral)

pas de modifications internes,

U
1e$\a.

Cette utilisation du mot-cl new n'a rien voir avec I'utilisation du mme

mot-cl pour crer un objet.


Je me perrnettrai de faire remarquer ici que c'est I'une des choses que je agaantes chez C# (et C+* nvsnt lui) : faites ce que vous voulez

'qE,

^v7|

\ trouve

avec mes mthodes, mais ne surchargez pas mes mots-cls. Quand je dis new, c'st que je veux crer un objet. Ils auraient pu utiliser un autre motcl pour indiquer une surcharge intentionnelle.

Chapitre 14 : Ouand une classe n'est pas une classe: l'interface et la structure

35 |

I I une strrlct neut avoir une mthode public void ChangeMethod(int nNewValue, double
{

dNewValue)

n : nNevValue; d : dNewValue;
6 ^. ToString

//
tt

- redfinit 1a r:rthode ToString dans 1'objet

override public string ToString0


{

return string.Fornat("([0:N], [1:N])", n, d);


i l
hrrhrr^ Puurfr ^r^^ ufdbU / r^-^1 \rId5

public static int Main(string[] args)


i
I

cre un objet Test

Test test = nev Test(i0);


Console.l.jriteLine("Va1eur
OutputFunction (test ) ; // essaie de modifier tH I coume ^^^^^ argument

i-nitiale de test")

1'objet de test en 1e passant


;

ChangeValueFunction(test, 100, 200.0)

Console.illriteline("Valeur de test aprs 1'appe1" t

"

ChangeValueFunction (lOO, 200.0) " ) ;

OutputFunction (test ) ; // essaie de modifier ^^^^^ argument Iilt conme


ChangeReferenceFunction

1'objet de test en 1e passanr

Console.}lriteLine("Va1eur de

(ref test , 100, 200.0) test aprs 1'appe1" +


; ;

" ChangeReferenceFunction(100, 200.0) ") OutputFunction (test ) ;


I I une mthode peut modifier 1'objet test.ChangeMethod(tOOO, 2000.0) ;

Console.WriteLine("Va1eur de tesr aprs 1'appe1"

r
;

'
OutputFunction (test ) ;

ChangeMethod(1000, 2000.0) ")

// attend confirnation de 1'uti.lisateur


Console.WriteLine("Appuyez sur Entre pour terminer...")
Console.ReadO; return 0;
;

I ChangeValueFunction - passe la struct par rfrence public static void ChangeValueFunction(Test t,


I

int
{

newValue, double dNer.vValue)

t.N =
l

newValue;

Test.D = dNewValue;

300

0uatrime partie : La programmation oriente obiet

//
{

SavingsAccount

compte bancaire

qui rapporte des intrts

public class SavingsAccount

BankAccount

public decimal mlnterestRte;


i
I

/ I

SavingsAccount

- iit

1e taux d'intrt, exprim en pourcentage (valeur comprise entre 0

et

100)

public

SavingsAccount (decimal

mnitialBalance,

decimal mlnterestRate)

:
{

base

(nInitialBalance)

this.mlnterestRate = mlnterestRate
l

100;

//
i

Accumultelnterest

invoque une

fois par

priode

nrrblie void Accrrmulatelnterest0


nBalance = mBalance
]

(niBalance

mTnterestRate);

/i II
i

Withdraw

- tout retrait est autoris jusqu' 1a valeur du solde ; retourne 1e montant retir
mWithdrar+al)

public decimal Withdraw(decinal

/l soustrait 1.50 F
base.l,lithdrav ( i . st't) ; I I vous pouvez maintenant effectuer un return base, Withdraw(ml,Iithdrawal) ;

retrait

avec ce qui reste

l
r I | 1 , - , - , -1 ^1 public Classl class

nrrhf
a

ic gtar-ie voiC

MakeAWithdrawal(BankAccount ba,

decimal nAmount)
L

ba . Withd raw (rnAmount )

public static
{

int Main(stringIl

args)

BankAccount ba;

SavingsAccount sa; I I ere un conpte bancaire, en

retire

100

F, et

// //

affiche 1es rsultats


;

ba = new BankAccount(200M);
ba . Withdraw { I 00M)

essaie de
nel,,r

faire

1a mme chose avec un conpte rniunr


;

SavingsAecount (200M, t2) sa.Withdraw(100M); i affiche 1e solde rsultant

sa =

Chapitre 14 : Ouand une classe n'est pas une classe: l'interface et ta structure

349

Vous pouvez raliser vous-mme un constructeur (qui n'est clorrc pas un constructeur par clfaut) qui fait effectivement quelque chose
:

public struct
{

Test

private int n; public Test(int


t

n)

this.n = n:
l
]

public class C1ass1


t
-"1^1.: ^ suaurd ^+^+). puuarL void Main(string[] args)
(

Test test = new Test(10);


l
]

En dpit des apparences, la dclaration test r,,.,i l;r3r. iri r ' n'alloue pas de mmoire. Elle ne fait qu'initialiser la rnmoire du type valeur clui est dj l.

Les mthodes d'une structure sont ruses


Une structure peut avoir des membres qui en sont des instances, nr;tamment des mthodes et des proprits. Une structure peut avoir des menrbres statiques. Ces derniers peuvent avoir des initialiseurs, mais les membres non statiques (1es instances) ne le peuvent pas. Normalement, un objet de type structure est pass une fonction par valeur, mais il peut tre pass par rfrence, condition que ce soit spcifiquement indiqu dans I'appel la fonction. Une structure ne peut pas hriter d'une classe (autre que ' . r '. L, comme je I'explique dans la section "Rconcilier la valeur et Ia rfrence : unifier le systme de types", plus loin dans ce chapitre), et une classe ne peut pas en hriter. Une structure peut implmenter une interface.

ffi

Si vous ne vous souvenez pas de la diffrence entre un membre statique

et une instance, reportez-vous au Chapitre 8. Pour vous rafrachir la mmoire sur le passage par valeur et le passage par rfrence, voyez le Chapitre 7.Le Chapitre 12 traite de l'hritage. Et si vous ne savez pas ce qu'est une interface, c'est dans ce chapitre que vous trouverez la rponse.

298

0uatrime partie:La programmation oriente objet

Redfinir une mthode d'une classe de base


Ainsi. une mthocle d'une classe peut surcharger une autre mthode de la mme classe en ayant des arguments cliffrents. De mme, une mthode peut aussi surcharger une mthode de sa classe de base. Surcharger une mthode d'une clas.se de base s'appelle redfinir, ou cocher la mthode.
Imaginez que ma banque adopte une politique qui tablisse une diffrence entre les retraits sur les comptes rmunrs et les autres types de retrait. Pour les besoins cle notre exemple, imaginez aussi qu'un retrait effectu sur un compte rmunr cotte une conlrnission de 1,50 F.

Avec I'approche fonctionnelle. volls pourriez implmenter cette politique en dfinissant dans la classe un indicateur qui dise si I'objet est un Sa,,'ingsr\c co''iirt ou un simple Bankccount. La mthode de retrait devrait alors tester I'indicateur pour savoir si elle cloit ou non imputer la commission de 1.50 F :
public
t BankAccount

(int

nAccountType)

private decimal mBalance; nri rrrte hool isSavingsAccount;

i/ indique 1e solde initial et dit si 1e compte /l que vous tes en train de crer est ou non l/ rrn eomnte rmunr
oublic
{

BankAccount

(decimal nlnitialBalance,

bool
mBalance

isSavingsAccount

mlnitialBalance;

this.isSavingsAccount l

isSavingsAccount;

public decimal l,iithdraw (decimal mAmount)


i
l
'i

l si
f
(

1e compte est un compte rmunr

i c(erri nnn nnnrrn+ \ \rravrrrnLLvurrL/

;t t

L .,a1ors soustrait 1.50 F

nBalance -= 1.50M;

// poursuit avec 1e mme code pour le retrait if (mAmountoWithdraw ) m3alance)


mAmountToWithdrai^r
)

mBalance

mBalance

-:

nAmountToWithdrar'r;
;

return

nrAnountToWithdraw

Chapitre 14 : Ouand une classe n'est pas une classe: l'interface et la structure

347

n=

1;

ll \a dclaration
MyStruct
ms;

d'une struct ressenble

la

dclaration d'un sinple int

ns.n =

3;

/i

accde aux membres conme un objet de classe

ms,d = 3,0;

I //
I

un objet de classe

doit tre

a11ou

partir

d'une zone spare de 1a mnoire

MyClass mc
mn n = ?.

new MyClass;

mc.d = 2.0;

Un objet strL,icr- est stock en mmoire de la mme manire qu'une variable intrinsque. La variable ns D'est pas une rfrence un bloc de mmoire externe allou partir d'une zone de mmoire spare.
/ Lvat .(dl\y ^tK \/
^ \

La "zone de mmoire spciale" clont viennent les objets de classe s'appelle le /as (the heap).Ne me demandez pas pourquoi.

L'objet ms se trouve dans la mme zone de mmoire locale que la variable n, comme le montre la Figure 14.1.

Figure 14.1 La variable

-rside" dans
la mme zone de mmoire que
la variable de

SITUCtUI mS

1
I

l-n l.+ms
I

3.0
*

mc

type valeur rr, alors que la zone dans laquelle rside I'objet mc vient d'un espace mmoire

Vt't-L
2

2.0

particulier
du tas.

La distinction entre un type rfrence et un type valeur est encore plus vidente dans I'exemple qui suit. L'allocation d'un tableau de 100 objets

296

Ouatrime partie : La programmation oriente obiet

De notre point de vue d'tre humain, la diffrence entre un four microondes et un four conventionnel ne semble pas de la plus haute importance, mais envisagez un instant la question du point de vue du four. Les tapes

du processus interne mis en (Euvre par un four conventionnel sont compltement diffrentes de celles d'un four micro-ondes (sans parler d'un four convection).
Le pouvoir du principe de I'hritage repose sur le fait qu'une sous-classe n'est pas oblige d'hriter l'identique de toutes les mthodes de la classe de base. Une sous-classe peut hriter de I'essence des mthodes de la classe de base tout en ralisant une implmentation diffrente de leurs dtails.

Surcharger une mt(rode hrite


Plusieurs fonctions peuvent porter le mme nom, condition qu'elles soient diffrencies par le nombre et/ou le type de leurs arguments.

Ce

n'est (u'une (uestion de surcharge de foncton

S/

-rf4k\ -6: tl

Donner le mme nom deux fonctions (ou plus) s'appelle surchorger un nom de fonction.
Les arguments d'une fonction font partie de son nom complet, comme le

montre I'exemple suivant


public class
t

My0lass

public static void AFunction0


{

I faire cuelcue a---a

chose

i public static void AFunction(int)


{

I t

t r^: -^ qusrq ^..^1-Ue rda!e

ChOSe

d'aUtre

public static void AFunction(double d)


{

ll
l
{

f.abe encore quelque chose d'autre

public static void Main(string[] args)

Ghapitre 14 : 0uand une classe n'est pas une classe: l'interface et la structure

345

l'exigence d'implmenter CompareTo O. Conr,are Tir I ) y ajoute GetVaiue (.) , qui retourne la valeur des objets dans un r r.',.

"(

^59trK ,ffi \ \T /

Bien qu'elle puisse retourner la valeur cle I'objet .sous forme i nr, il'.' , . l'1rne dit rien sur ce que contient la classe. La gnration cl'une valeur r :,i peut trs bien mettre en jeu un calcul complexe. La classe BaseClass implmente l'interface ICcnpare (la n-rthocle concrte GetValue O retourne le membre donne nYalue). Toutefois, la mthocle CornpareTo (1, qui est galement exige par I'interface l'l r:r.i,.-, i,," est dclare
d.U>

t-

- *,. L ]

, dL

L.

Dclarer une class abstract signifie qu'il s'agit d'un concept incomplet, auquel manque I'implmentation d'une ou de plusieurs proprits (clans ce cas, la mthode CorrrpareTo ( )).

Subtllass fournit la mthode Connareo O ncessaire.


Remarquez que SubClass implmente automatiquement I'interface , : i -. bien qu'elle ne le dise pas explicitement. Base(-i.,-rs vait promis cl'implmenter les mthodes de Iconpare, et SubCias-; EST-UNE lasell, .,::::. En hritant de ses mthodes. SubCl-ass implmente automatiquement Iiti-nrr., ri-:.

Main() cre deux objets de la classe Sui,:Cl-a,,s:; avec cles valeurs cliffrentes. Elle passe ensuite des objets l'11'F unc ( ) . La mthode l'l'.,F.r:r s'attend recevoir deux objets de I'interface I r.li,r.Dari:. i'1-,,l ir:r.- utilise la mthode CompareTo O pour dcider quel objet est le plus grar-rd. pui.s GetValue ( ) pour afficher la "valeur" des deux objets.
,

Ce programme donne une sortie courte et agrable


La valeur de ie1 est 10 et ce11e de ic2 est 20 Les objets eux-mmes considrent que icl est plus Appuyez sur Entre pour terminer. , .

petir

que ic2

Une structure n'a pas de classe


C# semble tre dot d'une double personnalit pour la rnanire cle dcla-

rerlesvariables.Lesvariablesd'untypevaleurCommeli,letl:l.:'
sont dclares et initialises d'une certaine rnanire
:

int n=

'I

29 4

Ouatrime partie : La programmation oriente objet

Dans le cas d'une succession d'hritages de classes, les destructeurs sont invoqus dans I'ordre inverse des constructeurs. Autrement dit, le destructeur de la sous-classe est invoqu avant le destructeur de la classe de base.

1e$!Qa" \ a ^w71-7

Le ramasse-miettes et le destructeur G#
La mthode du destructeur est beaucoup moins utif e en C#que dans d'autres f angages orients objet, cCImme C++, car C# possde de ce I'on appelle une destruction non dterministe.

La mmoire alloue un objet est supprime du tas lorsque le programme excute la comrnande new, Ce bloc de mmoire reste rserv aussi longtemps que les rfrences valides celui-ci restent actives.
Une zone de mmoire est dite "inaccessible" lorsque la dernire rfrence celle-ci passe hors de porte. Autrement dit, personne ne peut plus accder cette zone de mmoire quand

plus rien n'y fait rfrence.


# nefait rien de particulier lorsqu'une zone de mmoire devient inaccessible. Une tche de faible priorit est excute I'arrire-plan, recherchantles zones de mmoire inaccessibles. Ce qu'on appelle le ramasse-miettes s'excute un faible niveau de priorit afin d'viter de diminuer les performances du programme, Le ramasse-miettes restitue au tas les zones de mmoire inaccessibles qu' il trouv.
n temps normal, le ramasse-miettes opre en silence I'arrire-plan. ll ne prend le contrle du programme qu' de brefs moments, lorsque le tas est sur le point d'tre court de mmoire.

t# est non dterministe parce qu'il ne peut pas tre invoqu avant que l'objetaitt rcupr parle ramasse-miettes, ce quipeutse produire longtemps aprs qu'if
Le destructeur de a cess d'tre utilis. En fait, si le programme se termine avant que l'objet soit trouv par le ramasse-miettes et retourn au tas, le destructeur n'est pas invoqu du tout,

Au bout du compte, l'effet qui en rsulte est qu'un programmeur C# ne peut pas se reposer sur le destructeur pour oprer automatiquement comme dans un langage comme C++.

Ghapitre 14 : Ouand une classe n'est pas une clas:;e:l'interface et la structure

343

I Abstractlnterface -

montre comment une interface peut tre implmente avec


,,-^ utrv ^l -^^^ Lroc ^l^+-^) ouo u, ort

C,,^+^-. "--i^^ u1116 uj Lfl,

namespace
t
I

Abstractlnterface

//

ICornpare

- interface capable de se comparer e11e-mme


a- U Jlaff.i^hor EL aJ- LIIC! .o '1rnr a P!vt,! rtelprrr

public interface
{

ICompare

ICornparable
f,tl

// GetValue - retourne s propre valeur sous forme d'un int GetValue O ;


l
BaseClass

; -+L

implmente

f interface

ICompare en

fournissant une mthode concrt,: GetValueO et


rhcf rrnt
nrrh l 'i n

une mthode abstraite Compareo 0 class BaseClass : ICompare

int
i

nValue;
BaseClass

public

(int nInitialValue)

nValue = nInitialValue;
]
lt // II

implmente d'abord f interface Compare avec une nthode concrte


GetValue

public int
{

return l l
/ / (rrhfll

nValue;

//

complte

interface
jr+ rrrL

ICompare avec une mthode


rinlr+hi..nt\ rlrILvuJerLL/,
.

abstraite

eh<trrnf quoLq!L

nrrhl.in yuuraL

l.nmaa-^T^/^h.innruLJtrrPdrtrru\uuJuL

ee -

1^ ^1 ^ ho.o ..l1f ;- j -, 6.66n1+^ ctasse oe udse eli ^ ^.^ ^. reuertlr,ssant --...r1eLe ia

1a mthode abstraite CornpareTo 0

public class SubClass:


I
L

BaseClass

1/ nasse au constructeur de l-a classe de base I I Ia valeur oasse au constructeur nredent public SubClass(int nInitialValue) : base(nInitialValue)
{

]
/
tI

^ uomparelo

imnl mpntp I rinf arfnno


na+ a l n',
1

Tflnmnnrrhl e ' ri-^rrrna

une indication disant si un objet d'une sous'classe esl plus granq qu un autre
',n ^-^^

orrprridp vv!!ru
{

nrrhl yuvarL

ic int irrL

riahthiant) CnmnnrpTn(nhiont vvrxyutrv\vuJsuL trSrrLVUJrL/

BaseClass bc

= (Base0lass)right0bject;
)
;

return GetValue0 .ConpareTo{bc.GetValue0

292

0uatrime partie : La programmation oriente objet

nAccountNumber

= **nNextAccountNumber
;

nBalance = rnlniti-alBalance l
I

mme

chose

ici

//
{

nrrhlin

r*-**,

SavingsAccount - compte bancaire qui rapporte des intrts (errinncnnnrrn# . Ra.Ll nlacq ,*rrgsAccounr : DanKAccount

orrblic dee'inal mlnterestRate;

// tnitSauinssAccount '^"o-"-'--'." - 1it le tarx rJ'intrt exnripf sp I pourcentage (valeur comprise entre 0 et 100) public SavingsAccount(decimal mlnterestRate) : this(0, mlnterestRate)
I
I

l
^,.L1 yuurrLi
{

c-..-i -^^ ^:count rrr6n\ uqv

(decimal mlnitial , decimal mlnterestRate)

: base(minitial)

thi.s.mlnterestRate = mlnterestRate
)

tOO;

I
^l^FLJd

mme
l,^^,j1 Urdi

chose

ici

l
hllhlr^ PUUTfL

//
{

nrrhl i

Directltenosit - effectue automatiouement Ie dot d'rrn n <r.ati n 'oid DirectDeposit (BankAccount ba,
decimal npay)

chcrrp urrrYu

ba. Deposi.t (nPay) ]

public static int Main(string[1 args)


{

I cre un compte bancaire et 1'affiche BankAccount ba = new BankAccount(100);

DirectDenosit (ba, 100) ; Console.l.Iriteline("Compte {0i ", ba,ToBankAccountString0 ) ; // et maintenant un compte rmunr SavingsAccount sa = new SavingsAccount(12.5M) ;

DirectDeposit(sa,
sa.

100)

Accunulatelnterest ( ) ; Console.Writeline("Compte [0J", sa.ToSavingsAccountString() ) ; l/ attend confirmation de 1'utilisateur Console.w*riteLine("Appuyez sur Entre pour terniner...") ;
Console.Read0;

return l
]

0;

Chapitre 14 : Ouand une classe n'est pas une classe: l'interface et la structure

34 |

Le tableau tri d'objets Student st alors pass la mthode localement dfinie DisplavArray O. Celle-ci effectue une itration sur un tableau d'objets qui implmentent Ge*.S*,rinq O. Elle utilise la proprit Array. Length pour savoir combien d'objets contient le tableau, puis elle appelle Get,strirq O pour chaque objet, et affiche le rsultat sur la console en utilisant ririreLine ( I . Le programme, de retour dans Main O , continue en triant et en affichant les oiseaux. Je suppose que vous conviendrez que les oiseaux n'ont rien voir avec des tudiants, mais la classe Bird implmente I'interface IComparable en comparant les noms des oiseaux, et I'interface IDi splayable n retournant le nom de chaque oiseau. Remarquez que lufain ( ) ne rcupre pas cette fois le tableau des oiseaux.
Ce n'est pas ncessaire. C'est la mme chose que ce fragment de code class BaseClass {} class Subtlass : BaseClass {l
:

class C1ass1
{

public static void SomeFunction(Base0lass bc) {} public static void AnotherFunction 0


t

Subtlass sc = new SubClass0;


SomeFunction(sc);

l
]

Ici, un objet de la classe SubClas s put tre pass la place d'un objet d'une classe de base, parce qu'une sous-classe EST_UNE classe de base.

Bird peut tre pass une mthode attendant un tableau d'objets IConparable, parce que Bird implmente cette interface. L'appel suivant lisplayArray O passe le tableau birds, encore une fois sans cast, parce que Bird implmente I'interface IDisplayabie.
De mme, un tableau d'objets

La sortie du programme se prsente de Ia faon suivante

Tri

de

la liste
: IUU
:

des tudiants

L1SA

Marge Bart
Maggie

85

:50 :30 :0

Homer

290

0uatrime partie : ta programmation oriente obiet

public class
{

BaseClass
()

public
{

BaseClass

Console.}lriteline("Construction de BaseClass (default)")


l

public BaseClass(int i)
t

Console.ItiriteLine("Construction de BaseClass([0])", l
l

i)

public class SubClass


t

BaseClass

public
t

SubClass

()

Console.l^Iriteline("Construction
l

de SubClass (default) ") base(i1)

public SubClass(int i

it, int i2) :

Console.I{riteLine("Construction de SubClass({01, [1])",

il,

i2);

]
]

public class Classl


{

public sratic
t

int Main(stringIJ args)


;

Console.Writeline("Invocation de SubClass0 ") SubClass sc1 = new Sub0lass0; Console.lrlriteLine("\nlnvocation de SubClass(1, 2;"; SubClass sc2 = neu SubClass(1, 2); I I attend confirmation de 1'utilisateur Console.l.lriteline ("Appuyez sur Entre pour terniner.
Console.Read0;

..")

return
l
]

0;

l
Ce programme donne la sortie suivante Invocation de Sub0lass 0 Construction de Base0lass (default) Construction de SubClass (default) Invocation de SubClass(1, 2)
:

Ghapitre 14 : Ouand une classe n'est pas une classe: l'interface et la structure

339

public string GetString0


{

string sPadName : Name.fadRight(9) ; string s = String.Format("[0J : {1:N0i",


sPadName, Grade);

return s;
]
]

--Birds - tri des oiseaux par non -l{ --I I Bird * tableau de noms d'oiseau ^-1^..^b1e cl ass uaru Bi rd .: rvvxrPdldUaE. TComr:--^L1^ rn.: Lf,qD LUL>yLajdl
{

private string

sName;

// Constructor - initialise un nouvel objet public Bird(string sName)


t

student

this.
]

sName

sNane;

ll CreateBirdlist - retourne une liste d'oiseaux 1a fonction r+^+..i ^ DLrfrr[J -+-.i-^ i1 SBirdNames = { "Tourtere11e", "Vautour", "A1ouette", "tourneau", "Hirondel1e"] ; "0rivet', "Corbeau", { rf / \ puDl1c statlc blrdL.j ureateblro],lstl/
DLALTL

appelante

Bird[] birds = new BirdIsBirdNarnes,Length]; for(int i = 0; i ( birds.Length; i++)


{

birds
]

Ii]

= new Bird

(sBirdNames

Ii]

return birds;
]

//
{

accde aux mthodes en lecture seule


^+-.i -Srrlng
[T^ Name

-..L1.ipuDl1C

get
{

return
]
]
tt /i /l II

sName;

implmente
Cornpareo

lrinterface IComparable compare les noms des oiseaux, utilise


:

la

mthode de comparaison

intgre de la classe Srring

public int Compareto(object right0bject)


{

// nous aLlons conparer 1'oiseau 'rcourant'r l/ 1'objet oiseau "de Croite"


Bird leftBird = this; Bird rightBird = {Bird)right0bject; return String.Compare(ieftgird.Name, rightBird.Name)
;

288

Ouatrime partie : La programmation oriente obiet

dont elle ralise une extension. Il y a une raison cela : chaque classe est responsable de ce qu'elle fait. Une sous-classe ne doit pas plus tre tenue pour responsable de I'initialisation des membres de la classe de base qu'une fonction extrieure quelconque. La classe BaseClass doit se voir donner la possibilit de construire ses membres avant que les membres de SubCias s aient la possibilit d'y accder.

Passer des arguments au constructeur de la classe de base .' le mot-cl b a s e


La sous-classe invoque le constructeur par dfaut de sa classe de base, sauf indication contraire, mme partir d'un constructeur d'une sousclasse autre que le constructeur par dfaut. C'est ce que montre I'exemple lgrement modifi ci-dessous :
....i-^ urrr6 Qrra+nm ujLrlr

urt

namespaee Exanple t

public class
{

C1ass1

public static int Main(string[] args)


{

Console.lJriteline ("Tnvocatlon de SubClass 0 ")


SubClass

scl =

ner,r SubClass0;

Console.l^Iriteline("\nlnvocation de SubClass (int) ") ; SubClass sc2 = ner,r SubClass (0) ; // attend confirrnation de 1'utilisateur Console.l^IriteLine("Appuyez sur Entre pour terminer. .,")
Console.
Read

return
l
]

0I

public class
{

BaseClass

public
t

BaseClass

0
;

Console.Writeline("Construction de BaseClass (default) ")


]

public BaseClass(int i)
{

Console.}Jriteline ("Construction de Base0lass (int) ")

l l public class SubClass:


SaseClass

Ghapitre 14 : Ouand une classe n'est pas une classe:l'interface et la structure

337

// explicite des objets...


Arrav. Sort l.hi rds)
vv! \vf r

DisplayArray(birds); ll attend confirmation de 1'utilisateur Console.WriteLine("Appuyez sur Entre pour terminer..,")


Console.Read0; l
/
I
| |

| \: ^' 1 -"4 --^" - dLIILiIE d'ohipts nrri !f,>Pr/ll) affiche un Ull tablearr LdUfc@u u vuJL yur inplmentent f interface lDisplayable I
ct:tir vnirl DicnlrrrArrev

nrrhlin
{

(iDisplayable

I displayables)

.int lpnoth: ___ _ _ ___o ___ dr^-1-,.^L1^rrsplaya0res . T^-^*l Lengrn; for(int index = 0; index ( length; index**)
i

Console.l{riteLine(" i0J
l l
]

IDisplayable displayable = displayables Iindex] ", displayable.GetString0


:

ll ---- Students - trie 1es tudiants par moyenne de points d'UV ---// Student - description d'un tudiant avec son nom et ses points d'UV class Student : IComparable, IDisplayable
{

r^..L1^ U rGrade = 0.0; --"i--^+^ UUUUlC PLAVOLg // Constructor - initialise un nouvel objet student public Student(string sName, double dGrade)
I

orivate strins r"'*

sName;

this.sName

ll net de ct les = sName;


dGrade

donnes de

1'objet

this.
l

dGrade;

// CreateStudentList - pour gagner de 1a p1ace, cre I une liste fixe d'tudiants static stringIJ sNames = {"Horner", "Marge", "Bart", "Lisa", "Maggie"} ; static double ll dGrades = l0, 85, 50, 100, 30] ; public static Student[] CreateStudentlist0
I
{

for (int i = 0; i (
{

Student[] sArray = ner/ StudentIsNames.Length]


sNames.Length; i++)

sArray l
F^+1rFh tgLuLll

Ii]

ner,r Student (sNames

lil ,

dGrades

Ii]

^A--^,,, drL4j/,

/l

accde aux mthodes en lecture seule

286

0uatrime partie : La programmation oriente obiet

public static void GenericFunction(object


1

o)

if (o is
{

MyClassl)

MyClassl mc1
1t
'T

(MyClass1)o;

l
'I
J

GenericFunction O peut tre invoque avec n'importe queltype d'objet. Le mot-cl extraira des hutres ob j ect toutes les perles de MyClass 1.

is

L'hritage et le constructeur
Le programme InheritanceEx:rmp1e que nous avons vu plus haut dans ce chapitre repose sur ces horribles fonctions Init. . . pour initialiser les objets BankAc(lounr et SavirrgsAccount en leur donnant un tat valide. quiper ces classes de constructeurs est certainement la meilleure manire de procder, mais elle introduit une petite complication.

lnttoquer le constructeur par dfaut de la classe de base


Le constructeur par dfaut de la classe de base est invoqu chaque fois qu'une sous-classe est construite. Le constructeur de la sous-classe invoque automatiquement le constructeur de la classe de base, comme le montre cet exemple simple :

// II
I

lnheritingA0onstructor

slntre que 1e constructeur de la classe de base est invoqu


autonatiquenent

I
System;

using
t

namespace

InheritingAConstructor
Class1

public class
t

Chapitre 14 : 0uand une classe n'est pas une classe:l'interface et la structure

335

Cette rnttrocle trie trn tableau d'otrjets clui implnrentent I'interface I r-lr:rll ar :,.i:: ... [, classe de ces obf ets n'a mme pas cl'irnportance. Ils pourraient trris bien, par exemple. tre cles objets Strrtent. I-a classe r.rr a-,' pclurrait ntme trier la version suivante de S': rrrleir', l

/l Studenr - description d'un tudiant avec son class Student : IComparable


t

nom

er ses points

d'UV

private double dGrade;

//
{

accde aux mthodes en lecture seule


Grade

oublic dorrble
get
{

return
l

cjGrade;

// II ll
{

CompareTo

compare un tudiant un autre ; un tudiant est "neilleuril gu'un autre si ses points d'UV sont meilleurs

public int CompareTo(object right0bject)


Student leftStudent = this; Student rightStudent = (Student)right0bject; I I o'"-an*e - naintenant 1 , 0, ou 1 sur 1a base du // critre de tri (la moyenne des points d'UV de 1'tudiant) /1 ^r\ 1t {rrphtstrrrlnt.braoe r rett>tudent.Grade)
{

return
i

-1

if
{
Llg!!l

(rightStudent. Grade ) leftStudent. Grade)


rol-rrrn
,

I.

return
]

0;

Le

tri d'un tableau d'objets St,.rdent est rduit


void MyFunction(student
{

un simple appel

[]
;

students)
l0omparable

I trie le tableau d'objets

Array . Sort ( students)

Vous fournissez le cornparateur, et Array fait tout le travail.

284

Ouatrime partie:La programmation oriente objet

=f

Une conversion incorrecte gnre une erreur l'excution du programme 4;#\ (." qu'on une erreur run-time). Une erreur I'excution est beau^< \ coup plus appelle clifficile iclentifier et corriger qu'une erreur la compilation. \!!-/

ttter les conuersions intuldes en utilisant le mot-cl i s


La fonction PrccessAncurrt O se porterait trs bien si elle pouvait tre

stre que I'objet qui lui est pass est bien un Sa,,'i i'ssAccoun: avant
d'effectr-rer Ia conversion. C'est dans ce but que C# offre le rnot-cl j
-:.

L'oprateur is admet un objet sa gauche et un type sa droite. Il retourne true si le type I'excution de I'objet qui est sa gauche est compatible avec le type qui est sa droite.
Vous pouvez modifier I'exemple prcdent pour viter I'erreur I'excu-

tion en utilisant I'oprateur i s :


public static void
{

ProcessAmount (BankAccount bankAccount)

I dpose une grosse somme sur le compte bankAccount. Deposit ( 10000. 00) ; 1'objet est un SavingsAccount I (bankAccount i.s SavingsAccount)

I si if
{

ll ...recuei11e f intrt ds maintenant SavingsAccount SavingsAccount = (SavingsAccount)bankAccount;


savingsAccount
.

Accunulatelnterest ( ) ;

l
]
I -..L1.: ^ LaL_LU ^+^+.1 ^ ..^..i VUI_U PUUr.r_U
{

TestCast

()

SavingsAccount s = new SavingsAccount ProcessAnount (sa) BankAccount ba = new BankAccount 0 ; ProcessAnount (ba) l

L'instruction if supplmentaire teste I'objet b,ank.rccount pour vrifier qu'il est bien de la classe Sa,irngsAccoul,t. L'oprateur is retourne rrue lorsque ProcessArnount, ( ) est appele pour la premire fois. Toutefois, lorsqu'un objet banl.:Account lui est pass dans le deuxime appel, I'oprateur is retourne faise, vitant ainsi le cast invalide. Cette version de ce programme ne gnre pas d'erreur I'excution.

Ghapitre 14 : Ouand une cfasse n'est pas une classe:l'interface et la structure

333

g1 I
L

return
]
I I

dGrade:

] I

I r^-o+-] -^ - rtrrrn unp ro0rsentation r LVU!rr Ull r de 1'tudiant Ug LO Ll Irr -

() -',L1.1 GetStrino ^ D ^+-1^4 L frrb ve ev s rrtb | / PsurlL

string sPadName = Name.PadRight(9) ; string s = String.Format("t0J : tlrN0]",


sPadName, Grade);

return
]

L'appel i'adRrght ( ) garantit que le norn dans lequel sera insr le champ aura au moins neuf caractres de long. Si le nombre fait moins de neuf caractres, la diffrence est comble par des espaces. Ajuster une chalne une longueur standard permet d'aligner des objets en colonne. L'indication | 1 :N0 I dit, "afficher le grade avec des virgules ou des points (selon le paramtre de localisation) comme sparateur de milliers." L'indication {0} qui prcde arrondit la partie dcimale. Avec cette dclaration. je peux maintenant crire le fragment de programme suivant (le programme complet est donn dans la section "Assembler le tout", plus loin dans ce chapitre) :

ii DisplayArray - affiche un tableau d'objets qui II implrnentent f interface IDisplayable public static void DisplayArray (IDisplayable [] displayables)
t

int
t I

length = displayables.Length;
index =

for(int

0;

index

length; index*+)

IDisplayable displayable = displayables Iindex] ; Console.lrlriteline (" i01 ", displayable.GetString0 ) ;


]

l Cette mthode D.spia;'rAr ray i, 1 peut afficher n'importe quel type de tableau, condition que les rnembres du tableau dfinissent une mthode GetStringt). Voici un exemple de sortie de Displai'Arra'y(; :

Homer

:0
:85

Marge

282

0uatrime partie : La programmation oriente obiet

de faon ambigu, faire dmarrer une voiture n'est pas la mme chose que faire dmarrer un moteur. L'opration dmarrage de la voiture clpend videmment du dmarrage du moteur, mais ce sont cleux chose.s distinctes : il faut aussi passer la premire, lcher les freins, et ainsi de suite. Plus encore, sans doute, faire hriter Ca: de i,|r,r-rl st une reprsentation errone des choses. Une voiture n'est tout simplement pas un type particulier de moteur.
L'lgance du logiciel est un but qui se passe de justification. Non seulement elle le rend plus comprhensible, plus fiable et ais maintenir, mais elle rjouit le gott et facilite la digestion, entre autres.

Autres considratons
C# implmente un ensemble de caractristiques conues pour supporter

I'hritage.

Changer de classe
Un programme peut changer la classe d'un objet. En fait, c'est une chose que vous avez dj vue dans cet exemple. SomeFunction O peut passer un

objet SavingsAccount une mthode qui attend un objet BankAcLount.


Vous pouvez rendre cette conversion plus explicite
BankAccount ba I
:

SavingsAccount sa

ne!/ SavingsAccout 0
I

ot<:

I / une conversion vers le bas irnplicite est adnise ba = sa ba = (BankAccount)sa; I I te cast explicite est prfr

sa = sa

ba;

// Nonl l/ 1a conversion vers I I ceci est correcr

1e haut

implicite est interdite

(savingsAccount)ba;

La premire ligne stocke un objet Sa'.'ingsAcccLlnt dans une variable BankAccount. C# effectue pour vous cette conversion. La deuxime ligne

utilise I'oprateur cast pour convertir explicitement I'obiet.


Les deux dernires lignes reconvertissent I'objet BankAccount en

SavinqsAccount.

Chapitre 14 : Ouand une classe n'est pas une classe:l'interface et ta structure

33 |

crire
]

une note sur

le

PDA

l
PUUfAU LIdUU
{

Tan+as !oPLUP

. , /r^-^,'+^vVtuPULtr!,

IRecordable
sNot
e

vvf,u yu9ltL ^,,1-1.i ^ ,,^i,1


{

TakeANote

string

il
l l

raper une note sur 1e clavier

Chacune de ces trois classes hrite d'une classe de base cliffrente. mais implmente la mme interface IRecirrilabie.

r$.; nX tI$, Y

Notez la clistinction dans la terminologie. On hrite d'une case cle base, ou on l'tend, tnais on irnplntente une interface. Ne me regardez pas cornme a. Je ne sais pas pourquoi ce sont ces termes qui ont t choisis, nrais cette terminologie aide effectivement faire les distinctior-rs ncessaires.

L'interface iil.e r.:y'(lab 1e indique que chacune cles trois classes peut tre utilise pour crire une note en utilisant la mthode T,i.i..i ,.i,t . ftour comprenclre I'trtilit de ce procd, voyez la fonction suivantet :
public class Class1
t

static public void RecordShoppinglist(IRecordable


{

recordObject)

I
I

I I

erer une

liste

de commissions
;

string slist = GenerateShoppinglist0


puis 1a noter
(

recordObject. TakeANote
]

slist)

public static void Main(stringll args)


i
PDA pda new PDAO; RecordShoppingList (pda)

Concrtement, ce fragment de code dit que la fonctio Recorcishc,;irr r,gL,st 0 accepte comme argument tout objet qui implmente la mthod fai.+..1t: -.e o (en termes humains, "tout objet qui peut enregistrer une note"). RecordShopplngl-s;t () ne fait aucune hypothse sur Ie type exact d'objet recorciObiect. Que I'objet soit effectivement un PDA ou un certain type de EtectrcnicDe'r'.ic est sans importance, pourvu qu'il puisse prendre une note.

280

0uatrime partie: La programmation oriente obiet

pourcentage (valeur comprise entre 0


(BankAccount bankAccount, dec irnal mlnt erestRate)

et

100)

public void InitSavingsAccount


I
t

this.bankAccount = bankAccount ; this,mln:erestRate : mlnterestRate l

tOO;

// Accumulatelnterest I
t

invoque une

fois par

priode

public void AccumulateTnteresto


bankAccount.mBalance = bankAccount,mBalance t (bankAccount.mBalance * mlnterestRate)
]

l/ Deposit - tout dpt positif est autoris


public void Deposit(decimal
i
bankAccount . Deposi.t (mAmount ) ;
mAmount)

j
i
I t

I I

Withdraw

- tout retrait est autoris jusqu' la valeur du solde ; retourne 1e montant retir

public double Withdraw{decinal ml'iithdrawal) return


l
]

bankAccount . Withdrar^' (mi,lithdrawal )

Ici, la classe Sa.,r j ngsliccount contient un membre donne bankAccount (au lieu d'en hriter de tsankAccor,Lrit). L'objet bankAccount contient le solde et le numro du compte, informations ncessaires pour la gestion du compte rmunr. Les donnes propres un compte rmunr sont contenues dans la classe SavilssAccoLlnr
Dans ce cas, nous disons que :ia-i j ngsccount A_UN BankAccounr:.

La relation

A UN
:

La relation A_UN est fondamentalement diffrente de la relation EST_UN. Cette diffrence ne serlble pas rnauvaise dans I'exemple de code suivant
I cre un nouveau compte rmunr BankAccount ba = nev BankAccount(J SavingsAccount_ sa = new SavingsAccount*0; sa. InitSavingsAccount I

(ba,

dpose cent euros , /. ^^\ sa.ueposltll.uul;

I et y

Ghapitre 14 : Ouand une classe n'est pas une classe:l'interface et la structure

329

Toutefois, cette solution souffre de deux gros problmes. Le premier est fonclanrental : on ne peut pas prtendre que le stylo, le PDA et I'ordinateur portatrle sont lis par une relation cluelconque cle type EST_UN. Savoir cornrllent fonctionne Lln stylo et comment s'en servir pour prendre une rtote ne rne d<lnne auclrne information sur ce qu'est un ordinateur portable et la rnanire dont il enregistre les informations. I-e nom Tl,;:rj,,ri'r :,i,..i,. r,' est pltts rrne clescription qu'une classe de base.
Le second prolllrne est purement technique. Il serait mieux de dcrire L.tl,r,;p COrIlrIle une sous-classe de Cc,trp;',- e r . Bien que I'on puis.se raisonnablemerrt tenclre la class Pirr, partir de la mme classe de base ilc-,il|i.ii f i" on ne lteut pas en clire autant de !'e:r. ll fauclrait pour cela clfinir utt stvlo contme un certain type de i''ier:hariic a ii^jr itirigle-.-rce -l'clutefclis. ou cle l)t,-, . i:e i'1,::l;---r:ir i ii-q!i1r,:tirir':. une classe C# ne peut pas hriter clr-'deux classes cliffrentes en mme temp.s, elle ne peut tre qu'ul] setri et rirrle type tle chose.
En rev:rr;-urt i'r rros trois classes initiale.s, le seul point que les classes Pen, i'r .. et ,', , .;. ottt eI] conrnrun l)our ce que nous voulorts faire est qu'elles perrvenl toirtes tre lrtilises pour stocker cluelque t'lrost-'. [,a relation PLlti'T'-[i'l RIr--L.i']'ll ,lS-CONIME i..,,i,,.- r L.i:r;, t nous permet de communiquer I'usage rllri ;rerrt err etre fait cians un trtrt particulier, sans pour autant irnpli<1uer lute relation irrhrerrte entre ces trois r:lassr:s.

Qu'est-ce qu'une interface

.)

Urte clescrilrtiorr rl'interface ressernl;le beerucoup une classe sans clonnes, dans larluelle torttes les rnthorles seraient abstraites. I-Jne tlescription cl'interface p()Lrr cles "chr-rses qui enregistrent" pourrait ressembler ceci :

interface IRecordable
i

void TakeANote(String
l

sNote)

Rentarquer le rnot-cl inr erf a.ce la place de c1ass. Entre les accolades se trouve urle liste de mthodes abstraites. Une interface ne contient aucune dfinition de nrembre donne.
La rnthode Tal.re.l'loie O est crite sans implmentation. Les mots-cls public et'.'r"-tr-ial ou a.Sstracr ne Sont pas ncessaires. Toutes les mttrocles d'une interface sont publiques, et une interface n'entre pas en jeu clans un hritage normal.

278

0uatrime partie:La programmation oriente objet

cre une chalne qui donne la description du compte.

La proprit Balance permet de lire le solde, mais sans donner la possibilit de le modifier. La mthode Deposit O accepte tout dpt positif. La mthode r^/ithd raw ( ) vous permet de retirer tout ce que vous voulez dans la limite de ce que vous avez sur votre compte. ToBarrkAccor-intS -rir'g o

La classe SavlngsAccount hrite de toutes ces bonnes choses de BankAccount. cela, elle ajoute un taux d'intrt, et la possibilit d'accumuler des intrts intervalle rgulier. in O en fait le moins possible. Elle cre un BankAccount, affiche le compte, cre un Sar.ingsAc c ount, ajoute une priode d'intrts, et affiche le rsultat :
Ma

Compte 1001 200,00 Compte 1002 112,5A D (12,5',,) Appuyez sur Entre pour terminer...

^e.St

7X ttgrf Y

rnitBankAccount O . Cela initialise les membres donne propres au compte. La mthode Initsa..,ingsAccount ( ) aurait pu les initialiser directement, mais il est de meilleure pratique de permettre BankAccount d'initialiser ses propres membres.

par rapport A_UN m'U retroufur


EST_UN

- i'ai du nal

La relation entre Sar-ingsAccount et Banl<Account n'est rien d'autre que la relation fondamentale EST_UN. Pour commencer, je vais vous montrer pourquoi, puis je vous montrerai quoi ressemblerait une relation A_UN.

La relation EST UN
La relation EST_UN entre SavingsAccourlL et Bar,krrircounr est mise en vidence par la modification suivante Classi dans le programme SirnpleSa.,.ingsAccoLlni de la section prcdente
:

public class
t
L

C1ass1

// Direetltenos'i t - effectue

automat-i 0rement

le dnr r1 trrn r.hnnp


ba,

public static void DirectDeposit(BankAccount

Chapitre 14

Ouand une classe n'est pas une classe : I'interface et la

structure
Dans ce chapitre : Explorer la relarion PEUT_TRE_UTIUS_COMME.

Dfinir une interface. Utiliser I'interface pour effectuer des oprations communes. Dfinir une structure. Utiliser la structure pour rassembler des classes, des interfaces et des types valeur intrinsques.

ne classe peut contenir une rfrence une autre classe. C'est alors une simple relation A_UN. Une classe peut tendre une autre classe par le merveilleux procd de I'hritage. C'est une relation EST_UN. L'interface de C# implmente galement une autre association, tout aussi

importante : la relation pEUT_TRE_UTILIS_COtUnzIE.

Qu'e

st-ce que ttEllT _T ?E _l,lT I Ll St!_CLIUME


Si je veux prendre en vitesse une

petite note, je peux la griffonner sur un bout de papier avec un stylo, faire Ia mme chose sur mon assistant numrique personnel (PDA) ou la taper sur mon ordinateur portable.

276

0uatrime partie: La programmation oriente objet

ll
{

(qui. est ga1 zro pat dfaut)


1)

nrrhl in rrnid Tn'i tRanlrAnnnrrnt

InitBankAccount (0)
]

public void InitBankAccount (decimal mlnitialBalance)


{

nAccountNunber = **nNextAccountNumber mBalance * mlni-tialBalance; ]

// Balance (so1de)
tl 4

oublie deeimel
t

Balance

get I return mBalance;]


i

// DeOOsit JL Ji.A+ UCyVL n^..i+'if tr - tor'+ ^st autoris PVfLfr --r---

public void Deposit (decimal


{

mArnount)

if
t ] ]

(mAmount

0)

nBalance *= mAnount;

// ||
t

Wittdrar,I

- tout retrait est autoris jusqu' la valeur du solde ; retourne 1e montant retir ($lJithdrawal)
I

public decimal lti.thdraw(decinal mlJithdrawal)

if
t

(mBalance

nlrlithdrawal = nBalance
mBalance -- ntrli"thdrawal return mWithdrawal;
] I J q^<+-:^ rvuL!rrr tt

uet le

public string
{

compte sous forme de chane ToBankAccountString ( )

return String.Format("{0i
nAccountNunber, mBalance)
]

[1:CJ",

l
I
{

SavingsAccount

compte bancaire

qui rapporte des intrts

public class SavingsAccount

BankAccount

public decimal nnterestRatei


1e taux d'intrt, exprim en pourcentage (valeur comprise entre 0 public void InitSavingsAccount (decimal nlnterestRate) InitsavingsAccount

/l II

- lit

et

100)

Ghapitre 13 : 0uel est donc ce pof ymorphisme

? 32 5

Sceller une classe


Vous pouvez trs bien dcider que vous ne voulez pas que les gnrations futures de prograrnrneurs puissent tendre une de vos clas.se.s. Dans ce cas, vous pouvez la verrouiller en utilisant le mot-cl r;ea-l :'-1. Une classe scelle ne peut tre utilise comme classe de base pour une aritre clas.se, cluelle qu'elle soit" Examinez le bloc de code suivant
,,^i*uf,rr6 O,,^+^*, D Ltrlu. oy
:

public class
i

BankAccount

/l II
{

Withdrawal

* tout retrait est autoris jusqu' la valeur du solde : retourne le montant reti.r
dt/ithdraw)
)")
;

virtual public void Withdraw(double


]

Console.l,{ri-teLine ( "invoque BankAccount.Withdraw(

oublic sealed class SavinssAccorrnt


{

BankAccount

override public void l{ithdrar^r(double dWj.thdra'la1)


r t

COnSole . ldr j tai.i

of"j

nvnnrr" CavingSAcco1nt . Withdrar^r

)")

i
]

public class SpecialSaleAccount :


t

SavingsAccount

override public void l^lithdraw(double dWithdraval)


t Console
J

l,lriteline

("

invoque

Spec

ialSaleAc count . Witlidraw

)"

l Ce fragment de code 'SoecialSaleAccorrnt'

produit I'erreur de compilation suivante


: ne Delrt DAs hriter de la
classe

sce11e'SavingsAccount'

Le mot-cl sealeci vous permet de protger votre classe des assauts d'une ventuelle sous-classe. Par exemple, permettre aux programnteurs d'tendre une classe qui implmente la scurit d'un systme permettrait celui qui le voudrait d'y introduire une porte drobe.

274

0uatrime partie : La programmation oriente obiet

ftemarquez que la proprit EST_UN n'est pas rffexive: un Student EST_UNE person, mais I'inverse n'est pas vrai. Une Person N'EST_PAS_UN Student. Un nonc comme celui-ci se rfre toujours au cas gnral. ll pourrait se trouver qu'une Person particulire soit effectivement un Student, mais beaucoup de gens qui sont membres de la classe Person ne sont pas membres de la classe Student. En outre,la classe Student possde des proprits qu'elle ne partage pas avec la classe Pe rs on. Par exemple, un Student a une moyenne de points d'UV, mais une Person ordinaire n'en a pas.

L'hritage est une proprit transitive. Par exemple, si je dfinis une nouvelle classe
de Student, alors un Graduatestudent est aussi urt Person. Et ildoit en tre ainsi ;si un Graduatestudent EST*UN Student et un Student EST*UN Person, alors un GraduateStudent EST-UNE person, C0FD.

Graduatestudent comme Une sous-classe

quoi me sert l'hritage

L'hritage a plusieurs fonctions importantes. Vous pourriez penser qu'il sert rduire le volume de ce que vous avez taper au clavier. Dans une certaine mesure, c'est vrai : lorsque je dcris un objet de la classe Student, je n'ai pas besoin de rpter les proprits d'une Person. Un aspect plus important, mais li celui-ci, est le grand mot d'ordre rutiliser. Les thoriciens des langages cle programmation savent depuis longtemps qu'il est absurde de recommencer de zro pour chaque nouveau projet en reconstruisant chaque fois les mmes composants.
Comparez la situation du dveloppement de logiciel celle d'autres industries. Y a-t-il beaucoup de constructeurs automobile qui commencent par concevoir et fabriquer leurs propres pinces et tournevis pour construire une voiture ? Et mme s'ils le faisaient, combien recommenceraient de zro en ralisant des outils entirement nouveaux pour chaque nouveau modle ? Dans les autres industries, on s'est rendu compte qu'il est plus pertinent d'utiliser des vis et des crous standards, et mme des composants plus importants comme des moteurs, que de repartir de zro chaque fois.

L'hritage permet de tirer le meilleur parti des composants logiciels existants. Vous pouvez adapter des classes existantes de nouvelles applications sans leur apporter de modifications internes. C'est une nouvelle sous-classe, contenant les ajouts et les modifications ncessaires, qui hrite des proprits cl'une classe existante.

Chapitre 13 : 0uel est donc ce polymorphisme

? 323

Console.!JriteLine(" appelle SpecialSaleAccount.!'lithdravr0")


] l

pendant 1a priode des soldes public class SaleSpecialCustoner : SpecialSaleAccount


I

// I
i

SaleSpecialCustomer

- compte utilis

pour des clients particuliers

cverri.de public void liithdraw(double dWithdrawal)


{

Console. lrlriteLine

("

appelle SaleSpecialCustomer.Withdraw()");

l
]

ll
]

Chacune de ces classes tend la classe qui se trouve au-dessus. Remar-

',,irili,,, brisant en ce point la chane des hritages. Dans la pefspective cle 3.r;,kn... '-.,rriri, leS ClaSSeS Sp,-.cia] caieAr cor,in'. t ili;1eS1re ,:ra !.lr-,s,r.:rr:r(rr ressemblent exactement :ia-,-lngsAccc1.rnt. Ce n'est que {lans la perspective d'un Spe:raLSa ieAc.or-int que la nouvelle version cle iii I i,clr au i' r devient clisponible.
comrne
Cela est drnontr par ce petit programme. La fonction l{ain O invoque

qUeZ tOutefoiS que Si-.e.:lai-Salel'r:.LruLr. i^,:it-horali() a t marqUe

une srie cle mthocles Test (1, dont chacune est conue pour accepter une sous-classe diffrente. Chacune de ces versions de Test () appelle Iwithdra-; ( I clans la perspective d'un objet de classe diffrent.
La sortie cle ce programme se prsente de la faon suivante
:

Passage

nnttr pff
_

d'un BankAccount p,^f rrcr Test (BankACcOUnt) \ appelle BankAccount . Withdraw ( )


r uu L \yg4rr!rr!uvqrr L / ! v urr L /

rfin _*vflrl1uLUultL Pncs:op _-_*D_ d - _-. sn..i--^^^^^..-! norrr pffpct,ror Test (BankAccount)
appel

1e SavingsAccount

Withdraror

()

pour effectuer Test(SavingsAccount)

appelle SavingsAccount, Withdraw( trrn JPI (noialSaleACCOUnt Poccono I roDqc u ulr pour ef fectuer Test (Bankccount) appelle Savingsccoun!.Wi lhdraw

()

pour effectuer Test (SavingsAccount)

272

Ouatrime partie : La programmation oriente obiet

Les langages orients obiet expriment cette relation d'hritage en permettant une classe d'hriter d'une autre. C'est cette caractristique qui permet aux langages orients objet de produire des modles plus Proches du monde rel que les langages qui ne disposent pas du principe de I'hritage.

Hrter d'une classe


Dans I'exemple InheritanceExanple suivant, la classe SrbCiass hrite de la classe BaseClass :

/l

tnheritanceExample
Systern;

- offre la
p

dnionstration

1^ .i--1^ l^ t:op Id -1.,^ Pfu> bJilurtr u lrhr.i -.------o-

using
{

nqmcnrn Tnhpri i:nr,pF,xemnl

^ Ufd ^1-^^ D-"eC1ass -..L1.: u PUU.LfU


{

in ini nrrhf y|\vLLv


t

nflnl4lvlenbef

nuhlic void

SomeMethod0

Console.l,/riteline ("SomeMethod 0 " ) ; l

l
nrrhlic class SubClass
{

BaseClass

n'h1in'rnil VVrU PUUTTL


{

(nmo0thorMpthodfl lrlvs\/
UVI.vv

Conso1e.l,lriteline ("SoneOtherMethod
]

")

]
nrrhlin { nlecc'l'oql

public static int Main(stringlJ args)


t
(

l l er6e rrn ohiet de la classe de base Console.l,lriteline ("Utilisons un objet de BaseClass bc = new BaseClass0;

la classe de base :");

1; bc.nDataMenber bc, SomeMethod0 ; erons naintenant un 1ment d'une sous -classe

Console.l^Iriteline ("Utilisons un objet d'une sous-classe


SubClass sc = new SubClass sc.nDataMember = 2i sc.SomeMethod{);

ll

:");

Ghapitre 13 : 0uel est donc ce polymorphisme

32

Redmarrer une hrarchie de classes


virtual peut aussi tre utilis pour dmarrer une nouvelle hirarchie d'hritage. Examinez la hirarchie de classes montre dans le programme Inhe rit a ircr e'ies t l
Le mot-cl
h

#"u$3 l_frrl,i

// tnheritanceTest

examine comment
narrf ycuL fra sL!c rrtiljc uLrrtrE

le

mot-c1

virtual

.,''

n^'r. yvu!

lih^ts ldIM!

une nouvelle hirarchie d'hritaee


nanespace TnheritanceTest
{

using Systen;

public class Class1


t

public static
t

int Main(stringIJ strings)


/''\
;

'Iest.t tDaj

Console.hlriteLine("\nPassage d'un BankAccount") BankAccount ba = new BankAccount 0 ;


;

Console.lrlriteline ("\nPassage d'un SavingsAccount") SavingsAccount sa = new SavingsAccount 0 ;

lestt

<f

(sa,/

; ;

Test2 (sa)

it eline ( " \ nPa s sa ge d'un SpecialSaleAccount") SpecialSaleAccount ssa = new SpecialSaleAccount ( ) ;


Cons o1 e . l.lr
a

Testl (ssa) lesIz tssa/


\ ^/

;
;

lest3 (ssa)

Console.l{riteline("\nPassage d'un SaleSpecialcustomert') SaleSpecialCustomer ssc = new SaleSpecialCustomer0 ; Testl (ssc) ;


?est2 (ssc) Test3 (ssc) Test4 (ssc)
;

;
;

//

attend confirmation de 1'utilisateur sur Entre pour terminer,..");

Console. 1{riteline ( ) ; Console. 1,lriteLine ( "Hit Appuyez


Consol,e. Read

return l

0;

public static void Testl

(BankAccount account)

27 0

Ouatrime partie : La programmation oriente objet

tre auare de ses objets


On ne peut pas construire un objet sans un constructeur corresponclant. votre propre constructeur, C# retire le sien. En combinant ces deux aspects, vous pouvez crer une classe qui ne peut tre instancie que localement.
Si vous dfinissez

Par exemple, seule une mthode dfinie dans le mme espace de nom que BankAccount peut crer un objet tsank.,rccount avec Ie constructeur dclar comme internal (pour en savoir plus sur les espaces de nom, reportez-vous au Chapitre l6) :

i/
{

BankAccount

- simule un simple

compte bancaire

public class
I

BankAccount

I 7es numros de compte comnencent i000 et augmentent / squentiellement partir de 1 static int nNextAccountNumber = 1000; // tient jour le numro de compte et le solde int nAccountNunber:
i

double dBalance; i/ invoque 1e constructeur spcifique en fournissant I I des valeurs par dfaut pour 1es arguments nanquants

internal
{

BankAccount

{)

nAccountNumber dBalance 0:

= **nNextAccountNumber

public string GetString0


{

rturn String.Format("#{Oi = {1:Ni",


nAccountNumber, dBalance)
]
;

Chapitre 13 : 0uel est donc ce potymorphisme

?3|I

// Output - classe abstraite qui affiche


ahctrent
( ctri nd nrrhl i n rrn'i d Otrtnrrt LyuL \oLrarr

une chane
ri.. \
'

en11+hr1(+ vuLyuLULrfrr6/'

//
t

SubClassl

- inplmentation concrte

de AbstractBaseClass

nublic class SubClassl : AbstractBaseClass


override public void Output(string sSource)
{

ctrino
] ]
l,uurr
{

c = cSr1rcp ToTTnnpr{l'
! ' rvuytJ u! \ / '

Console.lliriteLine("Appe1 SubClassl,0utput0 depuis

[0]",

s)

*"L1';^ Lro c"Lnlass2 : AbstractBase0lass ^1^^^ uuuuJ

l/

SubClass2

- autre implmentation concrte

de AbstractBaseClass

override public void Output(string sSource)


i

strins - -- -"o s = sSource.ToloverO;


Console.l,./riteLine("Appe1 SubClass2,0utput0 depuis
l

{0}",

s)

class Classi
t

public static void Test(AbstractBaseClass


{

ba)

ba.0utput("Test");
l

pubiic static void Main(string[] strings)


{

* 0n ne peut pas crer d'objet AbstractBaseClass car c'est une * classe ahstraite. Cli gnre une erreur 1a compilation si vous * ne mettpz nas en commentaire 1a lisne oui srrit // AbstractBaseClass ba = new AbstractBaseClass 0 ; // rpte la nme exprience avec Subclassl
Console.IrIriteLine("Cration d'un objet Subclassl") SubClassl sc1 = ner,r Sub0lassl0 ;
Test (sc1)
;

// et enfin un objet

Subclass2
;

Console,WriteLine("\nCration d'un objet Subclass2")

/ lesr (scrl
l Console.
]

SubClass2 scT
;

= new SubClass2 0

l attend confi.rmation de I'utilisateur ..") ;


Read

Console,i{riteLine("Appuyez sur Entre pour terminer,

Chapitre l6 : Manipuler des fichiers en

C#

397

arabe, hincli, ni de toute autre langue. Le format de fichier Unicocle, plus souple, bnficie d'une compatibilit ascendante avec des caractres ANSI, et offre un assez grand nombre d'autres alphabets. Unicocle exi.ste en plusieurs formats, mais UTFS en est le format par dfaut pour C#. Le programme Friel,lrir-e suivant lit des lignes de donnes sur la console et les crits sur un fichier choisi par I'utilisateur :

,\

."u3
Ha;'l I ] \.Y

// Fileidrite - crit dans un fichier texte ce qui. est saisi sur 1a console II
using Systen; using System. I0; namespace Filel^lrite
t

public class
{

C1ass1

public static void Main(string[] args)


t | | cre 1'objet de non de fichier - 1a boucle while nous permet // de continuer essayer avec diffrents noms de fichi.ers // jusqu' ce que nous russissions

StreanWriter

srd

= nul1:

string
{

sFileName

r'rr '

vhile (true)

try
{

Console.ldrite("Entrez un non de fichier " * "(Entrez un nom vide pour quitter):");


sFileName

//

saisie du non du fichier de sortie (Entre pour quitter) = Console.Readline0 (sFileName.Length *= 0)

if
t
I

i
]

I pas de non de fichier / r,ihile par scurit

fait psser au-de1 de 1a boucle

break:

// /l II II II II II II II I| II

ouvre le fichier pour y crire ; envoie une exception si 1e fichier existe dj : FileMode.CreateNew pour crer un fichier si i.l n'existe pas dj, ou envoie une exception si 1e fichier existe FileMode.Append pour crer un nouveau fichier ou ajouter quelque chose un fichier existant FileMode.Create pour crer un nouveau fichier ou pour tronquer un fichier existant 1es possibilits de FileAccess sont :
FileAccess.Read,

35

0uatrime partie : La programmation oriente obiet

Console.tr'lriteline ("Va1eur donne par


i.l 'l'^\trlnl ll.

il111nl1tF11nat1^n vuLPu

{ll}" tvr

] //I tnStri no - fnrrrnit luJ L! -r-llt ruul^^- rrno c'imnlo fnnnt'inn I Tn(trinof) nrrorridp nrrhl in ctrino u Lt frr6 rvu Lt arr6 \,/
{

r1o t\;n ctr' -,,- -- 1fl8

return "Classl du prograrrne StructureExample";


] ]

Ce programme met l'preuve la structure Inr32.

i de type int. i"iain O utilise le constructeur par dfaut Int32 O (mais vous pourriez dire le corlstructeur int ( )) pour initialiser i 0. Le programme poursuit en assignant une valeur 1. Il est vident que cela diffre lgrement du format que vous utiliseriez pour crer vous-mme une structure.
Main O commence par crer un objet

i Or-itputFunctior O, qui est dclare pour accepter un objet implmentant I'interface IFornattabl e. Celle-ci est la mme que I'interface IDispiayable eue j'ai dfinie dans d'autres programmes (la seule mthode de IFormattabie est ToStrirg). Toutes les classes et toutes les structures hritent par 0bject de I'interface IFcrr.,:.'1 t:blc.
Main O passe la variable

OutputFunction O dit I'objet IFormattable de s'afficher lui-mme (la variable Int3 2 n'a aucun problme parce qu'elle a sa propre mthode ToSt ring ( )). Cela est dmontr encore plus clairement dans I'appel OutputFunction (2 ) . tant de type Int3 2, la constante 2 implmente galement IFormattable. Enfin, rien que pour vous le mettre sous les yeux, Maln O invoque directement 3 . ToString O. La sortie de cette premire section de Maln O est :
Yaleur donne par OutputFunction = I Valeur donne par OutputFunction = 2

Extrait directement *

Le programme entre maintenant dans une section sans quivalent. Main ( ) dclare un tableau d'objets de type 0b j ect. Il stocke un objet string dans le premier lment, un objet int dans le deuxime, une instance de C1ass1 dans le troisime, et ainsi de suite. Cela est autoris,

parce que String, Int3 2, et Ciassl drivent tous d'Object.


Le programme fait alors une boucle sur les objets du tableau. Main O est capable d'aller chercher les entiers en demandant chaque objet s'il

Chapitre 16 : Manipuler des f ichiers en

C#

39 5

Les classes d'l/O sont dcrites dans I'espace de nom S1-stem. r,-. La classe de base des I/O de fichier est FileSrrean. Autrefois. le programmeur ouvrait un fichier. La commande c.pen prparait le fichier et retournait un handle. En gnral, ce handle n'tait rien de plus qu'un numro d'identification. Chaque fois qu'on voulait faire une opration de lecture ou d'criture sur ce fichier, il fallait prsenter ce numro.
C# utilise une approche plus intuitive. Il associe chaque fichier un objet

de la classe Fi it,St,rcarn. Le constructeur de FiieStream ouvre le fichier. Les mthodes de Frt eStream effectuent les oprations d I/O sur le fichier.

.s\)G / t\9, Y

Fr

leSirean n'est pas la seule classe qui peut effectuer des oprations d'l/O

Hsurdesfichiers,maisc,eStbienellequireprsentenotrebonvieuxfichier

de base, qui correspond 90 %, de nos besoins d'l/O sur les fichiers. C'est la classe racine qui est dcrite dans cette section. Si elle est satisfaisante pour C#, elle I'est aussi pour moi.

Fi leS i- re:rir, est une classe trs basique. Ouvrir un fichier, fermer un fichier, lire un bloc et crire un bloc, c'est peu prs tout ce qu'elle vous donne. Heureusement, I'espace de nom -Svsten.IO contient un ensemble de classes qui compltent FileS ti aiir pour vous donner un accs plus facile aux fichiers. et un sentiment de confort douillet
:

Fea 1r- :. , i .-L. riteEinar r' : Ce sont deux classes de flux qui contiennent des mthodes permettant de lire et d'crire tous les types valeur : FeadCrLar (), riteChar O, ReadB.,'t-e O, i^lriteBl''te O, et "i pour crire un objet en format binaire (non ainsi de suite. C'est utile lisible par un tre humain).

Te;<tPeader,'l'li:,'lr-iter : Deux classes permettant de lire et d'crire des caractres (du texte). Elles se prsentent en cleux versions: Si r:rLgF,:ader l:trirrglir-i*,er et S:reanrReadei ,
S

-.

r'e

anil i^, i. -o :

,/ t/

:l':r lng,F,e zter,/-i-rlngr'^/rirer : Une simple classe de flux qui se contente cle lire et d'crire des chalnes.

S:reanll.c:::lei-/-ctreanh''ri:er : Une classe plus sophistique de lecture et d'criture de texte pour ceux qui en veulent plus.

Cette section fournit les programmes suivants, clui montrent comment utiliser ces classes : Filetirite et Fi iel.eai.

35

ouatrime partie : La programmation oriente objet

exemple, ini- n'est que I'autre nom de la structure int3 2, double est I'autre nom de la structure flor,ib-re, et ainsi de suite. Le Tableau 14.1 donne la liste complte des types et leur nom de structure correspondant.

Tabfeau 14.1 : Les noms de structure des types de variable intrinsques.


Nom de type Nom de structure

bool
byt
cha
e

llcolearr
l-.+,-.
S

sbyte

P,',-: e

decimal double

r Decinai
Cha

Doub I

float int
ui nt
1

Singie Int32
UInt-12
T-+ I Li I U A

ong

u1

ong

Uint64
0b-ject

ohicet .-"J---

short
usho

rt

Int 16 Uinr,6

Comment le sqstme de ttlpes est-il unif par des structures communes ? Un exemple
n'St que I'autre nom cle la structure Int32. Comme toutes les structures d'Cb,j ect, lnt doit en driver comme les autres. Cela conduit quelques rsultats fascinants, comme le montre le programme suivant :

int

drivent

i
I

I I

TypeUnification
System;

montre comnent

sont en
TypeUnification

int et Int32 fait la nme chose

using
{

namespace

Ghapitre 16: Manipuler des fichiers en

G#

393

rraurqPaLc

nLLtr ^^^^';sControlLib

..^.i*^ utrrrB C,,^+^-, JyLtrnr,

public class
{

Cl.ass2

public void
t Console
l . I

A_pub1ic
(

0
"C1ass2 . A_pub1ic " )
;

i,iriteline

nrnientcd
{

rrni d R nrnf antori i l

Console,
1

i{riteline

("

Clas

s2.

B_protect

ed "

private void C_privateo


IL

Console

l,'/riteline

("

Clas

2 . C*pri-vate "

internal
{

voi-d D internal o
.

Console

Ialriteline ( "Class2 . D_internal" ) ;

'i-+^--^1 -"otected rlr Ltrr rrar yr


{

void E internalorotectedo
,

Console .I,rlriteLine ("Class2

E_internalprotected" ) ;

Le programme AccessContror est constitu de Ciassl et Ciass3, qui sont contenues dans I'espace de nom ccessCon:ro1, et de la classe C1ass2 de I'espace de nom AccessConr-roilii. Les appels aux mthodes dans Clas s I .l,Iain ( ) mettent en vidence chaque type d'accs
:

t/

Les mthodes dclares comme pubiic sont accessibles toutes les mthodes de tous les espaces de nom. Aussi, Ciassl peut

invoquer directement C1ass2 . A pu'ciic (, .

t/

Les mthodes dclares comme protected sont accessibles depuis la classe C1ass2 et toute classe qui hrite de Classl. L'appel classl . B_pr otected () est autoris, parce que C1ass1 hrite de C1ass2. L'appel c1ass3.B_p.ctectei est illicite. Les mthodes dclares pri.,,ate ne sont accessibles qu'aux autres membres de la mme classe. Aussi, I'appel clas s- .1 p: i ,,? ) n'est pas autoris, alors que I'appel ciassi.C pri-".are O est

t/

correct.

35

Ouatrime partie : La programmation oriente objet

// ChangeReferenceFunction - passe 1a struct par rfrence public static void ChangeReferenceFunction(ref Test t, int
{

newValue, double dNewValue)

t.N =
] / I
{

newValue;

Test. D

dNewValue;

j nn / 0r:tnrrtl'rrnnt oui imolmente vsLPu LrrvuL - aff iche toute mthode Yur

ToString0
D

yUUalL

LdLIL

)]rtn]ltF11nal'1nl VUaU vuLyu

I lllcnlr\rrhl \re+VroJaur

1d ru/

Console. WriteLine

(r'id =

{0} "

id. ToString 0 ) ;

l
S'- r ric i-.rr eEx.rnpi e dfinit tout d'abord une interface, IL)ispia',.a'1, e, puis une simple structure, Tes*., eui implmente cette interface. T'es'- clfinit galemer-rt deux membres:un membre instance, et un membre statique, .r. Un initialiseur statique assigne la valeur 20 Toutefois, le rnembre instance ii n'a pas droit un initialiseur.

Le programme

n, d.

La structur Te -l dfinit un constructeur, une proprit instance li, et une proprit statique I
Tes

t dfinit

sa propre mthode, Changel,iethod

(),

de la mthode ToSt: irrg O. En fournissant ToSt-ring

ainsi que la redfinition (), Test implmente

I'interface i D i s p 1 a-v-ab I e.
La fonction i'Ia,;; ( ) met Te s | l'preuve. Tout d'abord, elle cre un objet test dans Ia rnmoire locale, et utilise le constructeur pour initialiser cet

espace.

|lair i ) appelle alors OutputFunctlon () pour afficher

I'objet.

Main O appelle ensuite la fonction ChangeValueFunctlon ( ) , lui passant test avec deux constantes numriques. Changeri a lue!'unciion . ) assigne ces deux valeurs aux membres n et ci de Te.st. Une fois que cette fonction a retourn ses rsultats, ,iui-n-rtFuncr-1cr, () rvle que d a t moclifi, alors que n ne I'a pas t. iil,.Le!'Lrnctlcn i) passe par valeur I'objet tesi- de type I'intrieur .structure. de cette fonction, I'objet t est une copie du test original, et non I'objet lui-mme. Aussi, I'assignation t . N change la copie locale, mais n'a aucun effet sur *iSi de retour dans i'I,rin O. Toutefois, tous les objets de la classe Test partagent le mme membre statique d. Aussi, I'assignation Te s r. .Ir change . pour tous les obiets, y compris ts t.

L'appel

Cha:rger.i

Chapitre 16 : Manipuler des f ichiers en

C#

39 |

Contrler l'accs auN classes aee les espaces de nom


Les espaces de nom autorisent un certain niveau d'indpendance dans des ensembles de classes qui n'ont pas grand-chose voir entre elles. Par exemple, si vous travaillez sur un ensemble de classes mathmaticlues, vous pouvez utiliser une classe comme conteneur pour y stocker des ensembles de valeurs.

d'indpendance est appelle niueou de couplage. Une classe qui -grf{c accde aux membres internes d'une autre classe est dite fortement couple. 9-/ -i: \ =( ItY O"t qui n'accdent I'une I'autre que par des mthocles publiques classes ) sont dites faiblement couples. \=-/
Le niveau

Le Chapitre 11 montre comment des descripteurs publlc. r t',')...crei1 et prl,raie- sparent les classes dans un mme espace de nom. L'ajout du mot-cl internai spcifie qu'un objet est accessible partir clu mme espace de nom mais pas aux classes externes. Les membres spcifis comme interna L protected sont accessibles la fois aux classes du mme espace de nom et aux sous-classes. Le programme Acces sCont ro1 suivant montre le fonctionnement de I'ensemble complet des contrles d'accs :

*e"*lil

l-----H:\;ir ?,-J

// II
{

AccessControl

- nontre 1es diffrentes


de contrle d'accs

fornes

nnespce AccessControl

using

System;
;

using AccessControllib

public class Classl:


{

Class2

public static
{

int Main(string[]
;

strings)

Class1 c1ass1 = nev Class1 0 Class2 c1ass2 = new C1ass2 0 C1ass3 c1ass3 = nelr C1ass3 0

l
;

ll II
|

les

mthodes publiques sont accessibles dans d'autres espaces de nom


^ In r puuffL /\ \,,r '

par d'autres classes

I Llo4

| 7es mthodes protges sont accessibles I I ta hirarchie d'hritage


(

travers

c1ass1 . B*protected

i /c1ass3 . B-protected ( ) ; I 7es rnthodes prives ne

sont accessibles que par

350

ouatrime parrie: La programmarion orienre objer

.rflkc %H\ =\S\f ) \-/

Toutes les classes hritent d'Ob-iect qu'elles le disent explicitement ou non. Vous pouvez redfinir les mrhocles cl'C;b i ec:. En teimes pratiques, la seule mthode que vous pouvez vouloir reclfinir e.sr ro.Srr ri,. ir. Celleci permet I'objet de crer une reprsentation affichable de lui-mme.

l,lettre une structure l'preut/e par |exemple


L'exemple de programme suivant montre les diffrentes caractristiques d'une structure :

// I

structureExample
System;

montre 1es diffrentes proprits

d'un objet struct

System. Collections; namespace StructureExample


{

using using

public j_nterface IDisplayable


{

string ToString0;
j

public struct Test : IDisplayable


{

I I II
I

I/ ll
{

private int n; private static double d = 20.0;


on peut

struct peut avoir des nenbres donne objet er de classe (statiques) 7es menbres statiques peuvent avoir des initiariseurs
une

utiliser

un constructeur pour

les

initialiser

nembres donne

d'une struct

publi"c Test(int n)

this.n = n;
l
I
{

ll une struct peut avoir des proprits d,objet I et des proprits de classe (statiques)
N

public int

get { return n; set { n : value;


J

}
D

l public static double


{

get
i

set { d = value;

{ return d; l
l

Ghapitre 16: Manipuler des fichiers en

C#

389

namespace
{
L

Paint

public class PaintColor


i

public PaintColor(int nRed, int nGreen, int nBlue) {l public void Paint0 {l public static void StaticPaintO {l
l
I
J

nmesnce MnthRoutines
IL

public class
{

Test

static public void Main(stri.ngll


r t

args)

I I

I cre un objet de type Sort dans 1'espace de nom I o nous nous rrouvons et invocue une fonction

Sort obj =

new Sort 0 ; obj . SoneFunct ion ( ) ; ll cre un objet dans un autre espace de nom, renarquez que ll Ie non de 1'espace de nom doit figurer explicitement dans toute I I rf.rence de classe

Paint.PaintColor black = new Paint.Pai"ntColor(0, 0, 0);


black. Paint 0
]
1

Paint. Paint0olor. StaticPaint ( ) ;


J
1 J

Dans ce cas, les deux class Sort et Test sont contenues dans le mme espace de nom, l/iat.hRcul-ines, bien qu'elles apparaissent dans des dclarations diffrentes dans le module.

Normalement. Sort et Test seraient dans des modules source C# diffrents que vous pourriez gnrer ensemble pour en faire un seul programme.

fonctior 'ie..:t . i"lair' ( r peut rfrencer la classe Sort sans spcifier I'espace de nom parce que les deux classes sont dans le mme espace de nom. Toute fois, Liain O doit spcifier I'espace de nom Pa in-, quand elle se rfre l'ai n',Co'or, comme dans I'appel Paiir'- . PalltCo Lor . StaticPalnr (1.
La

Remarquez que vous n'avez pas besoin de prendre des prcautions spciales en vous rfrant ir,ark. Paint 0 , parce que la classe et I'espace de nom de I'clbjet b iai:k sont connus.

3 48

0uatrime partie : La programmation oriente objet

de rfrence ncessite que le programme invoque 101 fois pour le tableau et une fois pour chaque objet) :
MyClass t
urc

ne,,v

(une fois

[] mc = new MyClass ItOO] for(int i = 0; i ( ms.Length; i++)


;

Ii] = new MyClass 0 ;

nc[0] .n = 0; Ce tableau est galernent un gros consomrnateur de ressources, de temps comme d'espace. Tout d'abord, chaque lment du tableau mc doit tre assez vaste pour contenir une rfrence un objet. En outre, chaque objet MyCiass fait une consornmation invisible de ressources au-dessus et au-del du ser-rl rnembre donne n. Enfin, il y a le temps que prend le programme pollr effer:tuer toutes les manceuvres ncessaires afin de rduire cent fois cle suite un petit bloc de mmoire.

La mmoire clestine aux objets de type structure est alloue en tant que partie du tableau :

.i*+ i^+^-^-^ = IlW .:-+ rrrL fl int f1n^l Ll rrrL!r [100]

ll

delaration d'un tableau du simple type valeur int


.

integers lOl = O; ll La dclaration d'un tableau de struct est tout aussi sinple Mrr(trrnt[1 .o = new MyStructitO0];
rvLLl

ms[0]

.n

.av

0;

Le constructeur de structure
Une structure peut tre initialise en utilisant une syntaxe semblable celle des classes, ce qui est intressant : public struct MyStruct
t
hllhl1^ thf n. I

MyStruct ms

* new MyStruct 0

En dpit des apparerlces, cela n'alloue pas un bloc de mmoire partir du tas, mais initialise seulement n et d la valeur zro.

Chapitre 16 : Maniputer des fichiers en

C#

387

Un fichier de projef contient des instructiorrs sur les fichiers qui constituent le projet et la manire dont il.s se combinent.

Vous pouvez combiner des fichiers de projet pour produire des combinaisons de programlne qui dpendent des rnmes classes dfinies par I'utilisateur. Par exemple, vous pouvez vouloir associer un programme d'criture avec le programme de lecture correspondant. De cette manire, si I'un des deux change, I'autre est automatiquement rgnr. L'un des projets dcrirait le programme d'criture, et I'autre clcrirait le programme de lecture. On appelle solution un ensemble de fichiers de projet.
.a\\G -UnprogrammeurVisuaIC#utilisel'ExplorateurdeVisualStudiopour

fl?It \7

combiner en projets des fichiers source C# clans I'environnement Visual


Studio.

Runir des fichiers source dans un espace de nom


Vous avez la possibilit de runir des classes communes dans un espace auquel a t assign un nom significatif. Par exemple, vous pouvez compiler toutes les routines dont la signification est mathmatique dans un espace de nom lulatfrRoutine-c.
Il est possible, mais trs improbable, cle diviser un mme fichier en plusieurs espaces de noms. Il est plus courant de regrouper plusieurs fichiers dans un mme espace de nom. Par exemple, le fichier Poin -.cs peut contenir la classe Point et la classe Th:eeDSpac e . c s pour dcrire les proprits d'un espace euclidien. Vous pouvez combiner Pr-,int . r:s, Thl eeDspace .,- s, et d'autres fichiers source dans I'espace de nom MathRouti nes.
Un espace de nom sert plusieurs choses. C'est d'abord une runion de classes. En tant que programmeur, vous pouvez raisonnablement supposer que les classes qui constituent I'espace de nom llathRoutiries ont toutes quelque chose voir avec des fonctions mathmatiques. Par voie de consquences, si vous recherchez une certaine fonction mathmatique, c'est dans les classes qui constituent ilathP,our-lnes que vous pouvez aller la chercher en premier.

Un espace de nom vite les possibilits de conflit de nom. Par exemple, une bibliothque d'entres/sorties pour fichiers peut contenir une classe Convert qui convertit une reprsentation cl'un type de fichier en un autre type. En mme temps, une bibliothque de traduction peut contenir une classe du mme nom. L'assignation des espaces de nom Filero et

346

0uatrime partie : La programmation oriente objet

Mais une rfrence un objet est dclare et initialise d'une manire compltement diffrente :
public class
t MyClass

public int
l
MyClass mc;
lul{J

n;

, M,,a1^^ rrew r'tyurass \,)

La variable de classe nc est appele un type rfrence, parce qu'elle se rfre une zone de mmoire potentiellement distante. Une variable intrinsque detype ini ou dc,.LL-.ie est appele uariable de type ualeur.

Toutefois. si vous examinez plus attentivernent n et n., vous verrez que la seule vritable diffrence est que C# alloue autornatiquement la mmoire pour utte variable de type valeur, alors que vous devez allouer la mmoire pour un objet cle classe. N'y at-il rien qui puisse runir les deux dans une Thorie unifie des classes ?

La structure C#
C# clfinit un troisime type de variable, appel une sfructure, qui comble le foss entre les types rfrence et les types valeur. La syntaxe d'une dclaration de structure ressemble celle d'une classe
:

public struct MyStruct


t

nrrhlin

'i

nt n,
d;

public double
j

public class
{

MyClass

nrrhli^ yuuarL nrrhl 'i

'i n+ rrrL

ul

n' rrt nrrhl o d.

l On accde un objet structure comme un objet cle classe, mais I'allocation est identique celle d'un type valeur :

ll dclare et accde une variable int n:

d'un type valeur simple

Chapitre 16

Manipuler des fichiers en C#


Dans ce chapitre :
Grer plusieurs fichiers source pour un mme programme.

Lire et crire des fichiers de donnes.

n C#, l'accs oux fichiers signifie deux choses diffrentes. La plus vidente est I'enregistrement et la rcupration de donnes sur le disque. Il y a toutefois une autre signification qui concerne la manire dont le code source C# est regroup dans des fichiers source.
Les fonctions permettent de diviser une longue chane de code en units sparables et maintenables. L'organisation en classes permet de regrouper les donnes comme les fonctions de faon pertinente afin de rduire encore la complexit du programme. C# offre un autre niveau de regroupement : il vous permet de regrouper des classes similaires dans une bibliothque spare.

Duiser un meme programme en plusieurs fchers source


Les programmes de ce livre ne sont faits que dans un but de dmonstration. Ils ne dpassent pas quelques dizaines de lignes de long et ne contiennent qu'un petit nombre de classes. Un programme de niveau industriel, avec tous les aspects dcoratifs ncessaires, peut comporter des centaines de milliers de lignes de code, rparties dans plusieurs centaines de classes.

Il devient rapidement impraticable de stocker toutes ces classes dans un mme module. Pour commencer, il y a I'exigence de maintenir les classes bien en ordre. Ensuite, le travail de ralisation de grands programmes est gnralement rparti entre de nombreux programmeurs. Le mme fichier

344

Ouatrime partie : La programmation oriente obiet

]
htthl'^ yuuIfL Laa ^l^ f'rF^l ura

public static
t

int Main(stringIJ strings)

SubClass sc1 : new SubClass(10) ; SubClass sc2 = new Subtlass(20); MyFunc (sc1, sc2) ;

// attend confirmati.on de 1'utilisateur Console,i.lriteline("Appuyez sur Entre pour terminer, . .")


Console,Read

return
l

0;

- utilise 1es mthodes fournies par f interface ICompare pour afficher 1a valeur de deux objets, puis f indication de celui qui est 1e plus grand (se1on I 'oDJ et lu1-nemel public static void MyFunc(ICompare ic1, ICompare ic2)
l,tyFunc
lI ^ \

// II ll tt
{

Console.lrlriteline("La valeur de ic1 est i0l et cel1e de ic2 est {11",


ic
1 . GetValue (

ic

2 . GetValue ( )

string
{

s; CompareTo(ic2)
)

switch (ic1,
case
0l

s = rrest ga1 ";

'

break;

case -1: s = rrest plus


break;

petit

que";

case

s = I'est plus grand que";

1:

break;

default r s * "quelque chose qui cloche";


break;

l
Console
,

l,lriteline

"Les objets eux-mmes considrent que

ic1 t0J ic2", s);

Abstractlnterface
simple.

est encore un programme un peu long, mais relativement

L'interface IConpare dcrit une classe qui peut comparer deux objets et aller chercher leur valeur. ICompare hrite de I'interface IConparal-t:

Ghapitre 15: Ouelques exceptions d'exception

383

Le remplacement de ces fonctions signifie que mme les fonctions conues pour attraper les exceptions de la classe gnrique Exceprion n'ot-lt qu'un accs limit aux nouveaux membres donne. En commenant par |Ialr, ( ) , le programme cre un objet |1at-lrClas s dont la valeur est 0, puis essaie astucieusement d'en prendre I'inverse. Je ne sais pas si vous avez dj essay, mais je n'ai jamais vu beaucoup d'inverses de 0, et si rna fonction tait cense retourner Lln nombre, a me

rendrait un peu n'rfiant.

rt9{Qa. Le processeur Intel retourne effectivement une valeur pour 1.0/0.0 : Irrf iniry. existe plusieurs valeurs spciales en virgule flottante pour traiter de tels cas ^v7q \ Il :(dq9 plutt que d'envoyer une exception, car tous les langages n'ont pas la capacit ) \/ de traiter cles exceptions. Parmi ces valeurs spciales, ily a I'infini positif et ngatif, et le s),mbole iJaii Q,lot-a-Number - pas un nombre) positif et ngatif.
Dans les circonstances normales, la mthode In.,rerse i ) retourne le rsultat attendu. Quand on lui passe une valeur nulle, cette mthode aux ides larges envoie une CustomException, passant une chalne d'explications avec I'objet fautif.

Travaillant I'envers, Main () attrape I'exception, puis affiche un bref message destin expliquer o en est le message dans son traitement : "Erreur f atale inc onnue" signifie probablement que le programme est sur le point de fermer boutique et de rentrer chez lui. Main O donne ensuite I'exception la possibilit de s'expliquer en invoquant sa mthode ToStrlng O. ffoyez l'encadr "ToStringQ, la carte de visite de la classe" dans ce chapitre.)
Comme dans ce cas I'objet exception est effectivement un Cu s tornEv-c epr- I on, le contrle passe Cus t ornExc ept i on . To S t ring ( ) . Cette mthode T'oStrirrg O affiche le message de I'exception avec la mthode cible initiale et le numro de ligne correspondant.

O est une mthode virtuelle d'Exception, dont toute classe ^tK Message d'exceptions personnalise doit hriter. =Qg, Plutt que de faire des hypothses hasardeuses, Ves sage ( ) permet galement I'objet l,iathClass de s'afficher lui-mme en utilisant sa mthode Tcstrlng r. ). Iiat-hCiass . ToSrring () retourne une chane contenant la valeur et la description de l'objet.
Ne supposez rien de plus que ce que vous savez. C'est sur la mthode

ToSt'ring O d'un objet que vous devez compter pour crer une version st ring de celui-ci, plutt que d'essayer d'accder I'objet lui-mme pour en extraire Ies valeurs afficher.

3 42

Ouatrime partie : La programmation oriente objet

Tri de la Alouette
Corbeau

liste

des oiseaux

tourneau

Grive
Hirond el I e

Tourterelle
Vautour Appuyez

sur Entre pour terminer.,,

Les tudiants et les oiseaux sont tris, dans la logique de leurs catgories

respectives.

Hritage et interface
Une interface peut "hriter" des mthodes d'une autre interface. Je mets des guillemets autour clu rnot hriter, parce qu'il ne s'agit pas d'un vritable hritage, mnre s'il en a pourtant I'air :

* interface capable de se comparer e1le-nrme et d'afficher sa propre valeur public interface lCompare : IConparable // II
I |

IConpare
-a

// getValue - rerourne int GetValue 0


;

sa propre valeur sous forne d'un

int

L'interface illorLr,,:ir e ltrite de iComparabl e I'exigence d'implmenter la mthode Cr-,r;1por e io (). A Cela, elle ajoute I'exigence cl'implmenter Get v'aiue t'). LIn objet lf ,olPare peut tre utilis comme un objet iComparab Le, car, par clfinition, le premier implrnente les exigences du second. Toutefois, il ne s'agit pas l d'un hritage complet au sens C#, orient objet, de ce terme. Le polymorphisme n'est pas possible. De plus, les relations de constructeurs ne s'appliquent pas.
Je donne une dmonstration de I'hritage d'interface dans le programme

Abstract Inter-J ace. dans la section suivante.

Rencontrer une interface abstraite


Afin d'implmenter urte interface, une classe doit redfinir chaque mthode de celleci. Toutefois, une classe peut redfinir une mthode d'une interface par une mthode abstraite (naturellement, bien sr, une telle classe est abstraite) :

Ghapitre 15 : 0uelques exceptions d'exception

38 |

usLng Systen;
namespace CustonException
{

public class CustornException :


{

Excepti.on

private MathClass mathobject; private string sMessage; public CustomException(string sMsg, MathClass mo)
{

nathobject =
sMessage

mo;

sMsg; Message
jllcbSd8e -ao-^^^ gbL ^.lInl\ \tuJ /, lrnh jat - _-L oqt []Jrl

override public string


{

petf rpttrrn 6Lr!sLurrr

(+"ina Fara+/rtT^! uLL1116tru!ltroL\

gl'fsssnoo mrthnhiant rr1g1ling0 ) ;]


]

override public string ToString0


{

string s =
rt1rrn c.

Message;

s *= "\nException s *= TargetSite;
l

envoye par";

// II
{

MathClass

- collection de fonctions
MathClass

mathnatiques

public class

de nia cration (pas encore grand-chose montrer)

private int nValueOfObject; public MathClass(string sDescription,


{

--i,,^+^ ^+-.:-- -_-J__ chianfllocnrintint pr.J_v.1Le sLrLltg rl,.__.1

int

nValue)

nValue0f0bject = nValue; s0bjeetDescription : sDescription;


]

public

// II
t

int

l{essage

Value {get {return nValueOf0bject;}} - affiche le nessage avec 1a valeur

de

l'objet
Message

Math0lass attach

public string
get
{

return String.Fornat(''({0J = {1J)",


s0bjectDescription, nValue0f0bject);

l
]

// ToString -

combj.ne 1e nessage

personnalis avec

3 40

Ouatrime partie : La programmation oriente obiet

] I I :*^1 r-^-+^-+ | | lurPfErurrLsrrL

| i-+^-f^^^ 1 r lrrL!lLE

1n.i.^1 ayrPro) ^Vab1e

// CetStrins - retourne 1e non de 1'oiseau public string GetStringo


{

return
l
]

liane;

La class Student (elle est peu prs au milieu de ce listing) implmente les

interfaces ICornparabie et IDispl ar.able que nous avons dcrites plus haut. ConpareTo O compare les tudiants par "grade", ce qui a pour consquence que les tudiants sont tris par grade. GetString O retourne le nom et le grade de l'tudiant. Parmi les autres mthodes de Stu'lent, il y a les proprits en lecture seule et Grade, un constructeur simple, et une mthode CreateStudentlist (). Cette dernire retourne simplement une liste fixe d'tudiants (au dpart, j'avais pens permettre I'utilisateur d'entrer au clavier les noms des tudiants, mais le listing devenait si gros qu'on n'y voyait plus I'essentiel).
Name

La classe Bird, en bas du listing, irnplmente galement les interfaces IComparable et IDispla;'ab1e. Elle implmente CornpareTo () en comparant des noms d'oiseaux I'aide d'une mthode similaire intgre la classe String. Ainsi, un oiseau est "plus grand" qu'un autre si son nom est plus grand. Cette mthode permet de trier les oiseaux par ordre alphabtique. La mthode GetName O retourne simplement le nom de I'oiseau.

Nous sommes maintenar-rt prts revenir dans l"lai n ( ) , au bon endroit. La mthode CreateStudentli st ( ) est utilise pour retourner une liste non trie, qui est stocke dans le tableau students.

-sq4k P-/ j5: \

Pour nommer une collection d'objets, comme un tableau, utilisez un nom


au Pluriel'
Ce tableau d'tudiants est d'abord introduit dans un tableau comparabl e0b,i ect s. Celui-ci est diffrent des tableaux utiliss dans

V '

les autres chapitres (en particulier ceux du Chapitre 6). Ceux-ci sont des tableaux d'objets d'une classe particulire, par exemple un tableau d'objets Srudenr, alors que conf.,ar-abieCrbi ec+;s est un tableau d'objets qui implrnentent I'interface ICoinparable, indpendamment de la classe
laquelle ils appartiennent. Le tableau conparabief l-,-ject-c est pass la mthode Array . Sor: | ), qui en trie les lments sur la base clu grade.

Chapitre 15 : 0uelques exceptions d'exception

37I

n'rhli^
t

"^.i'1 f1 /)
vvrs !r \/

try
t

f20;
l
I I attrape une erreur catch(MyException ne)
{

une partie de 1'erreur i'/riteLine ( "Exception MyException attrape dans f1 0 " ) ; gnre maintenant une nouvelle exception II I I pour remonter la chane de transmission throw nev Exception("Erreur envoye parf10") ;
I

..

. traite
.

Console

l
l

Envoyer un nouvel objet exception permet une classe de formuler un nouveau message d'erreur avec des informations supplmentaires, tout en clarifiant ce qu'il pouvait y avoir d'approximatif au dpart. Passer un objet Exception gnrique la place d'un objet spcialis MyException garantit que I'exception sera traite un niveau situ au-dessus de f 1 O.
Envoyer une nouvelle exception prsente I'inconvnient que I'indication de pile redmarre au point du nouvel envoi. La source de I'erreur originale est donc perdue, moins que f 1 ( ) n'ait pris des prcautions spciales pour la conserver. Une commande throw seule, sans argument, renvoie le mme objet

exception :
public void fl o

try
{

t20;
l

ii attrape une erreur catch(Exception e)


t
I | . . . traite une partie de l'erreur Console.l.lrireLine ("Exception attrape dansfl () ") poursuite du cheninement de l'exception ll .
+1^-^,,. Llltuw
r ;

338

0uatrime partie : La programmation oriente objer

public string
{

Nane

of D''
{

rt1r rn sNemo

l
nrrhlic dorrhle
{

Grade

ger
{

return l
l

dGrade;

//
|| |

implmente

f interface

I0omparable

(nnc. e o | vvruyeriu anraraTn - LvltrHat nrrtro nh-iet o rrn ull auL! vLi _^^rqnrr

,^aa.

I II II
I
{

des objets Student)

et dcide lequel
dans le

vient aprs l'autre


tableau tri

public int CompareTo(object rightObject)

I conoare 1'obiet Student courant (aooelons-le --J - I 'celui de gauche' ) 1 tautre student (appelons - 1e ll 'celui de droite'), et gnre une erreur si aucun des deux // n'est un obiet Student Student leftStudent = this; ( :r ^1^+L..i^^t is student)) ar \; | t-: \!rrrLwuJcu
I I
{

Console

l,lriteLine

("I"Ithode de conparaison return 0;

laquel1e est pass un nonStudent");

I I if
/ I
{

Student rightStudent = (Student)right0bject; // gnre maintenant -1, 0, ou I sur 1a base du // critre de tri (1a moyenne des points d'UV de 1'tudiant)

Oa classe Double contient une mthode CompareTo0 que nous aurions pu utiliser 1a place)
(rightStudent.Grade
rotrrrn -1.

leftStudenr.grade)

if
{

(rightStudent.Grade

leftStudent.Grade)

return
l

1;

return

0;

] l l inpTnentent

f interface IDisplayable : // GetString - retourne une reprsentati.on de 1'tudiant

Ghapitre 15 : Ouelques exceptions d'exception

37 7

Console.Read0;

j cre un objet C l as s i et l'utilise immdiatement pour invoquer la mthode r i r r, Cette mthode appelle f2 l),qui appelle f,t ( ), qui appelle f + ) . La fonction ; -. ) effectue cles vrifications d'erreur extrmement ' sophistiqttes, clui la conduisent envoyer soit un objet i'lyExceprrcr-r, soit un objet Er,: - 1, i- , r n gnrique, selon la valeur de I'arqument bclolen. L'exception est cl'abord passe f -i I ). L, C# ne trouve aucune instruction catch, et le contrle remonte donc r2 (), qui attrape I'objet I'lvException. Comme I'objet trxcep.- r on gnrique n'a pas encore trouv d'instruction catci.r corresponclante, le contrle continue remonter. Finalement, c'est t 1 r') qui cclntient une instruction ':ai ( 1r Corrsponclant I'objet envoy.
Ma:'-n (

Le deuxime aptrtel de r{aini; pysvoque I'envoi par f+() d'un objet iuli-E>rc e 1r*- I rr r, qtri esl attrap par f :l ( ) . Cette exception n'est pa.s envoye f I i), parce qu'elle a t attrape et traite par f 2 i). Le progranrme clonne la sortie suivante
Bnvoie d'abord une exception gnri"que Dxception gnrique attrape dans f1 0 Exception gnrique envoye dans f4 0
:

Envoie d'abord une exception spcifique Bxception MyException attrape dans f2 0


MyException envoye dans
nnrrrroz <ttr Fnt ro rnttr

f40

+arminor

3 ( ) , qui ne contient aucune instruction catch,, n'a rien d'inhabituel. Je pourrais mme dire que la plupart des fonctions ne contiennent aucune instruction catch, mais je pourrais tout aussi bien ne pas le dire. Une fonction n'a aucune raison d'attraper une exception si elle ne contient rien qui lui permette de traiter I'erreur d'une manire pertinente. lmaginez une fonction mathmatique ComputeX ( ) qui appelle Factorial () pour effectuer certains de ses calculs. En supposant que son code interne soit correct, si Fact,rrial O envoie une exception, c'est parce que la fonction appelante lui a pass une valeur incorrecte. ComputeX o peut ou non tre capable d'identifier pourquoi cette valeur tait incorrecte, mais en tous les cas, elle ne peut certainement pas rsoudre le problme.

Une fonction comme f

Une fonction comme f2 ( ) n'attrape qu'un seul type d'exception. Elle recherche une certaine classe d'erreurs. Par exemple, l"fyException peut faire partie des

336

0uatrime partie: La programmation orienre objet

Comparez cet exemple t'algorithme de tri du Chapitre 6. L'implmentation de cet algorithme demandait pas mal cle travail et ncessitait beaucoup de code. Il est vrai que rien ne vous garantit que Ai'ra.,i. Sor:r | ) est meilleur ou plus rapide que cet algorithme. Il est seulement plus facile.

Assembler le tout
Voil le moment que vous attendiez tous : le prograrnme Sc,r.i Inte r-t'a c e complet, construit en utilisant ce que nous avons dcrit plus haut dans ce

chapitre

// Sortlnterface - montre conment l on peut utiliser 1e principe de f interface pour offrir II une meilleure souplesse pour 1e factoring /I et f implmentation des classes
l

using
{

System;

nanespace

Sortlnterface
lui-mme en

I
'i

// fnisplayable - objet capable de se convertir t une chaine affichable


ntprf :no
Ti-l'i

c.1 ayable

string GetString0;
]

// Getstring - retourne votre reprsentation

sous forme de chane

class C1ass1
{

public static void Main(stringlJ args)


r
L

I I trie 1es tudiants par leur moyenne de points d'UV... tonsole.Ltriteline(',Tri de 1a liste des tudiants"); /l reoit un tableau d'tudiants non tri Studentll students = Student.CreateStudentlist0 ; // utilise f interface IComparable pour trier I

7e tableau

[] comparableObjects Array. Sort (comparable0bjects) ;


IComparable
I

(IComparable [] ) students;

II

IDisplayable
I

Bird[] birds = Bird.CreateBirdlist0 I I renarquez qu'il n'est pas indispensable de faire un cast
;

I trie maintenant par nom un tableau d'oiseaux en utilisant I Les mmes routines, bien que 1es classes Bird I I et Student n'aient pas de classe de base comnune Console.lfriteline("\nri de la liste des oiseaux"):
I

DisplayArray (displayable0bjects ) ;

interface IDisplayable affiche maintenant 1es rsultats [] displayableObjects = (lnisplayable ll ) students;

Chapitre 15 : 0uelques exceptions d'exception

37 5

doigts

Lasser quelques enlois t/ous fler entre les

Et si C# part la recherche d'une instruction carch dans la fonction appelante correspondant I'objet exception envoy, et n'en trouve aucune qui corresponde ? Et si la fonction appelante ne contient aucune instruction carch ? Que faire ? Examinez cette simple succession d'appels cle fonctions
i
:

I ltyException - nontre comment une nouvelle classe d'exceptions peut ll tre cre, et comment des fonctions peuvent ll attraper exactement ce qu'e1les sont faites nolr tr'itpr tnut en laissant passer les autres II
using
name
{

System;

space l'lyExc ept ion

// i-ntrodui"t un certain type de 'MyClass ' public class MyClassi) i I Uynxception - - ajoute la classe Exception standard
une rfrence MyClass

public class MyException


{

Exception

private lly0lass myobject; public MyExcepti-on(stri-ng sMsg, MyClass no)


{

base(sMsg)

nyobject
]

mo;

// pernet aux classes extrieures d'accder une classe d'information publ.ic I'ly0lass My0ustom0b ject { get Ireturn nyobject; ] ]
l public class tlassi
t
I I tt - - attrape tout objet Exceprion gnrique public void fl (bool bExceptionType)
{

try
{

f2 (bExceptionType)
]

catch(Exception e)
t

console.l*Iriteline ('rException gnrique attrape dans f1

)")

33 4

0uatrime parrie : La programmarion orienre objet

Bart Lisa Maggie


lYrv,/

:50 :100 :30

fr-( llr9r,
Y

Que c'est beau ! Voyez comme les rsultats sont aligns sur les noms complts par des espaces pour faire tous la mme longueur.

Interfaces t rdfinies
De mme, vous trouverez dans la bibliothque stanclard de C# des interfaces intgres en abondance. Par exemple, I'interface IComparabie est dfinie de la faon suivante :

interface
{

IComparable

I I conpare 1'objet courant 1'objet ,o, ; retourne ll 1 s'i1 est plus grand, - s'il est plus petit,0 dans int CornpareTo(object o)
;

1es autres cs

Une classe implmente I'interface IC.-.r,parab1e en implmentant une mthode CorLpar-,oTo i). Par exemple, Sr,:ing f ) implmente cette mthode en comparant deux chalnes. Si les chalnes sont iclentiques, elle retourne 0. Si elles ne le sont pas, elle retourne soit I soit un signe -, selon la plus

"grande" des deux chalnes.

si vous voulez savoir comment une chaine peut tre "plus grande" qu,une autre, voyez le Chapitre 9.
N'y voyez aucune intention darwinienne, mais vous pouvez dire qu'un objet Student est "plus grancl" qu'un autre objet Student si sa moyenne de points d'uv (grade point average) est plus grande. c'est soit un tudiant plus brillant, soit un meilreur courtisan, peu importe.

Implmenter la mthocle compa:eTo O implique que les objets peuvent tre tris. Si un tudiant est "plus grancl" qu'un autre, vous devez pouvoir trier les tucliants du "plus petit" au "plus grand". En fait, la classe Array implmente dj Ia mthode suivante :
Array. Sort (ICornparable

ll

objects)

Chapitre 15 : Ouelques exceptions d'exception

37 3

De retour dans l{ai n O, I'instruction catch spcifie qu'elle attend un objet llyException. Une fois I'exception attrape, Ie code de I'application peut

encore demander n'importe quelle proprit d'une Excep*.ion, comme dans I'appel toString O. Cette instruction catch peut galement invoquer des mthodes de I'objet l"iirCiass fautif stock dans I'envoi (throw).

Assgner plusieurs blocs

catch

Le fragment de code de la section prcdente dcrit le processus par lequel un objet irlyE:,ception localement dfini est envoy et attrap. Mais examinez nouveau I'instruction catch utilise dans cet exemple : public void
{

SorneFunction0

try
{

Some0therFunction

] catch(MyException
{

me)

l i Et si SomeOtherFunction O avait envoy une simple Exception ou une exception d'un type autre que Mylxc eption ? Autant essayer d'attraper un ballon de football avec un filet papillon. Le catch ne correspond pas I'envoi. Heureusement, C# permet au programme de dfinir toutes sortes d'instructions catch, selon le type d'exception attraper. Les instructions catch doivent figurer I'une aprs I'autre aprs le bloc try, de la plus spcifique la plus gnrale. C# teste chaque bloc catch en comparant squentiellement les objets envoys au type d'argument de

I'instruction catch.
public void SoneFunction0

i
+ {

rtty Lr

SomeOtherFunction
]

catch(MyException
{

me)

i/

tous 1es objets MyException sont attraps

ici

332

0uatrime partie : La programmation oriente objet

Puis-je tuir un programme qu pEtlT-TREUTI LISE_C0tl,lI,lE un eremple t


Sortlnterface ci-dessous est une offre spciale. Ses capacits, qui vous sont apportes par deux interfaces diffrentes, ne pourraient jamais tre obtenues par une relation d'hritage. Une fois implmentes, les interfaces se tiennent prtes votre service.
Le programme
Je veux

toutefois diviser le programme Sorti:rtrf ace en sections, pour mettre en vidence diffrents principes. Je veux simplement faire en sorte que vous puissiez voir exactement comment fonctionne ce programme.

Crer tlotre interface "fates-le tlous-mme"


il- sDia.o'a'Die suivante sera satisfaite avec toute classe contenant une mthode GetSt.ring O. Cette mthode retourne une reprsentation sous forme de st rinq de I'objet qui peut tre affich en utilisant ,^u'riteL j ne ( ) :
L'interface

I TDisplayable - objet qui implmente I| la nthode GetStrine0


I
I I -^ .

interface IDisplayable
t
{

I
'1

retovne votre description

string GetString0;
J

La classe Student suivante implmente s1 ciass btuoent : IDisolavable


i

Idisplayable

private string sName; private double dGrade = 0.0; /l accde aux mthodes en lecture seule
-+-.:-^ -..L1.i^ L! rrr yuvf,ru
t
Name

get
{

return
l
]

sName:

public double Grade


{

Ghapitre 15 : Ouelques exceptions d'exception

37 I

Le reste de cette sortie est ce que I'on appelle une indication de pile (stack trace). La premire ligne de I'indication de pile indique I'endroit partir duquel I'exception a t envoye. Dans ce cas : Factorial (int ) (plus prcisment, la ligne 23 du fichier source C1ass1 . cs. Factorial ( ), ou la ligne 52 du mme fichier, a t invoque dans la fonction Main (strlng IJ ). L'indication de pile s'arrte I'tain ( ), parce que c'est le module dans lequel I'exception a t attrape.
t\\u-rr|r

If Cff I Y

L'indicationdepileestdisponibledansl'unedesfentresdudbogueur
de Visual Studio. Je la dcrirai au Chapitre
16.

.1

Il faut bien admettre que c'est plutt impressionnant. Le message dcrit le problme et identifie I'argument qui en est responsable. L'indication de pile vous dit quel endroit I'exception a t envoye, et comment le programme y est arriv. Avec ces informations, vous pouvez vous jeter sur le problme comme la foudre.

Crer tutre propre classe d'erceyrtions


La classe Exception standard fournie par la bibliothque de classe de C# est

capable de dliwer beaucoup d'informations. Vous pouvez demander I'objet exception quel endroit il a t envoy, ainsi que toute chne demande par la fonction qui signale I'erreur. Dans certains cas, toutefois, la classe Exception standard ne convient pas. Il peut y avoir trop d'informations pour qu'elles puissent tenir dans une seule chalne. Par exemple, une fonction d'application peut vouloir se faire passer I'objet incrimin pour I'analyser.

Une classe localement dfinie peut hriter de la classe Exception, comme de n'importe quelle autre classe :
i
I
{

/ CustomException - ajoute 1a classe e Exception standa standard I une rfrence MyClass


:
Exception

public class CustomException private Mvclasss

,,1::i':::.
:
base(sMsg)

Custonnxception(string sMsg, M.,cl.dd MyClass no)

{
tyob3'..a = *0,

//

public

permet aux classes extrieures d'accder une classe d'information MyClass MyCustomObject{ get {return myobject;J}

330

Ouatrime partie: La programmation oriente objet

t_ ^$sq 7X

tsp, Y

Par convention, faites commencer le nom d'une interface par la lettre 1, et accolez-lui un adjectif. comme d'habitude, ce ne sont l que les suggestions qui ont pour but de rendre vos programmes plus lisibles. C# vous permet d'utiliser les noms que vous voulez.
La dclaration suivante indique que la classe PIrA implmente I'interface I recor'iabie :
nrrhlin
{

nlncc

PDA: IRecordable
(

nuhf in rrni ri TakeANote


t

string

sNote)

il

fait cuelsue ..*-'1.-.Y

chose Dour enresistrer

la

note

l
]

II n'y a pas de diffrence de syntaxe entre la dclaration d'une classe de base ThingsThatReccrci et la dclaration qui implmente une interface
IRec o rd ab 1 e.

Voil la principale raison d'tre de la convention sur les noms d'interfaces elle permet de reconnatre une interface d'une classe.
La conclusion est qu'une interface clcrit une capacit. En tant que classe, j'obtiens mon brevet iF.er:or-dabi,-: lorsque j'implrnente la capacit TakeAl'loi e.

Pourriez-uous tne donner un enemple sinple

Une classe implmente une interface en fournissant une dfinition pour chaque mthode de celle-ci : nublic class Pen : IRecordable
{

public void TakeANote(string


{

sNote)

I ]

prendre une note sur un bout de papier

l
PUUJr-U UJ-dbb
{

PDA

: ElectronicDevice,
(string
sNote)

IRecordable

PUUTTU VVtU TakeANote

Chapitre 15 : Ouelques exceptions d'exception

369

nValue);

throw new Exception{s)


]

// commence par donner 1a valeur 1 un "accumulateur" double dFactorial : 1.0; I I tait une boucle partir de nValue en descendant de 1 chaque fois
tttt 11r nrl
I I

ltrnl rpr I'aCCUmUlateUf II ^^i^ ..-1^,.- L rbtenue LO VOf gU! PAL

do
{

] while ( - -nValue ) l) // retourne 1a valeur stocke dans 1'accumulateur


;

dFactorial *=

nValue;

return dFactorial;
]

nrrblic c1ass Class1


{

public static void Main(string[] args)


{

try
{

tt /l appelTe en boucle 1a fonction Factorial for(inti=6;i)-6;i--) {


lt /i calcule 1a factorielle du nombre

de 6 -6

double dFactorial = MyMathFuncti-ons.Factorial(i) ; // affiche 1e rsultat chaque passage Console.1^lriteLinel"i = {0}, factorielle = {l}", i, MyMathFunctions.Faetorial(i) ) ;
]

catch(Exception e)
f

u0ns0le.l'/r1teL1ne("Irreur ratale :"J Console.Writeline (e.ToString ( ) ) ;


]

l/ attend confir:nation de 1'utilisateur


Console.WriteLine("Appuyez sur Entre pour terminer. , . ") Console.Read0; ]
]
;

Presque tout le contenu de cette version "exceptionnelle" de l"lain O est plac dans un bloc trv.

32 8

0uatrime partie : La programmation orienre

ob

jer

Je peux donc dire que ces trois objets (le stylo, le PDA et I'ordinateur portable) implmentent I'opration TakeAlJote. En utilisant la magie de I'hritage, je pourrais implmenter la chose en C#, de la faon.suivante

abstract class lhingsThatRecord


{

nh<f ]

rrnt

nrrhl

'i

n "oid

TakeANOte

(string

SNOte)

public class Pen :


{

ThingsThatRecord

override public void TakeANote(string sNote)


{

prendre une note sur un bout de papier

i
]

public class
{

PDA

ThingsThatRecord

override public void TakeANote(String sNote)


t

I ,

prendre une note sur son

pDA

public class Laptop :


t

ThingsThatRecord

override public void TakeANote(String sNote)


/ l

ce que vous voulez

]
-rl

>7V Ifcilt l\zt


\

Si le terme abstract vous remplit de perplexit, revenez au Chapitre 13. Si la notion meme cl'hritage n'voque pour vous que mystre, il vous faut passer un peu de temps dans le Chapitre 72.

Cette solution reposant sur I'hritage semble fonctionner trs bien pour ce qui ne concerne que I'opration TakeAl{ote O. Une fonction comme RecordTask O peut utiliser la mthode TakeANote O pour noter une liste de commissions sans se soucier du type d'appareil utilis : void RecordTask(ThingshatRecord thi.ngs)
{

// cette nthode bstrite est implmente par toutes tt I I q:i hritent de ThingsThatRecord
things.TakeANote("Liste de comnissions,') IL et ainsi de suite l
;

1es classes

Chapitre 15 : Ouelques exceptions d'exception

36 7

t/

Il mlange le code normal et le code de traitement des erreurs, ce qui obscurcit le chemin d'excution normal, sans erreur.

Ces problmes ne paraissent pas si graves dans cet exemple simple, mais ils ne font qu'empirer avec la complexification du code de Ia fonction

appelante. Le rsultat est que le code de traitement des erreurs n'est jamais crit pour traiter autant de conditions d'erreur qu'il devrait. Heureusement, le mcanisme des exceptions rsout tous ces problmes.

Utliser un mcansme d'erceptions t our signaler


les erreurs
C# introduit un rncanisme entirement nouveau, nomm exceptions, pour identifier et traiter les erreurs. Ce mcanisme repose sur les mots-cls t ry, thrcw, caT-ith, et f 1r'ai. Dans les grandes lignes, il fonctionne de la faon

suivante': une fonction va essayer (t ry) d'excuter une portion de code. Si cette portion de code dtecte un problme, elle envoie (thr:ow) une indication d'erreur, que la fonction peut attraper (catch), et, quoi qu'il arrive, elle excute finalement ( f ina i) un certain bloc de code la fin :
public class Mytlass
{

nrrhlic vnid SomeFunction0


urrv L4vrr

\ /

/l
try
I I
I

ceci est

fait

pour ttraper une erreur

appelTe une fonction


;

SomeOtherFunction ( )

ll
l
t

autant d'autres appels que vous voulez


e)

catch(Exception
I

I te contrle passe par ici en cas // <i'erreur en un point quelconque du bioc try ou ll de toute fonction appele par celui-ci il 1'objet Exception donne 1a description de 1'erreur
;

l
l
nrrhf PueffL

in vnid Ss6sQtherFUnCtiOn0

i
|

l'erreur se produit

quelque

part

dans 1a fonction

326

Ouatrime partie: La programmation oriente objet

Sceller une classe empche un autre programme, lequel peut d'ailleurs se trouver quelque part sur Internet, d'en utiliser une version modifie. Le programme distant peut utiliser la classe telle qu'elle est, mais il ne peut en hriter ni en redfinir aucun lment.

Chapitre 15 : Ouelques exceptions d'exception

36 5

i = 6, factorielle 1 = ), tactorl.el_le i = 4, factorielle


-i = I fentnriallo

- tzv - rlv

:6
-L -1

i i

= 2, factorielle - 1, factorielle

1 = u, racl0r1elle -U lactoriaL 0 a reu un nombre ngatif Appuyez sur Entre nntrr torminor

L'indication d'une condition d'erreur I'aide d'une valeur retourne par une fonction n'est autre que la manire dont le traitement d'erreur a toujours t ralis depuis les premiers jours de FORTRAN. Pourquoi changer ?

/e

suis l pour sgnaler ce qui me parat ncessaire

Quel est I'inconvnient de retourner des codes d'erreur ? C'tait trs bien pour FORTRAN ! C'est vrai, mais cette poque les tubes vide taient aussi trs bien pour les ordinateurs. Malheureusement, I'approche des codes d'erreur prsente plusieurs inconvnients.

Tout d'abord, cette solution dpend de la possibilit de retourner une valeur normalement illicite, mais il existe des fonctions pour lesquelles toutes les valeurs qu'il est possible de retourner sont licites. Toutes les fonctions n'ont pas la chance de ne retourner que des valeurs positives. On ne peut pas calculer le logarithme d'un nombre ngatif, mais un logarithme peut tre positif ou ngatif.

1t$Qa"

{cg)

Bien que I'on puisse contourner ce problme en utilisant la valeur retourne par une fonction pour une indication d'erreur et un argument de type out pour retourner une donne, cette solution est moins intuitive et fait perdre une partie de la nature expressive d'une fonction. Quoi qu'il en soit, lorsque que vous aurez vu comment fonctionnent les exceptions, vous vous dbarrasserez bien vite de cette ide.

D'autre part, un entier ne permet pas de stocker beaucoup d'information. La fonction Far:tor:iai O retourne -1 si I'argument qui lui est pass est ngatif. L,'identification du problme pourrait tre plus facile si nous savions exactement ce qu'tait cette valeur ngative, mais il n'y a pas de place pour retourner cette inforrnation.
Troisimement, le traitement des erreurs retournes est optionnel. Vous ne gagnerez pas grand<hose en faisant retourner par Factorial O un code

32 4

Ouatrime partie : La programmation oriente objet

appelle SavingsAccount.Withdraw( ) pour effectuer Test (SpecialSaleAccount) appelle SpecialSaleAccount. Withdraw ( ) Passage d'un SaleSpecialCustomer
pour effectuer Test (BankAccount)

appelle SavingsAccount. Withdraw( ) pour effectuer Test (Savingsccount) appelle SavingsAccount .Withdraw ( ) pour effectuer Test {Specia1Saleccount)
appel 1e SaleSpecialCus toner.
appe
1

Wi

thdraw

pour effectuer Test(SaleSpecialCustomer)


I

Sa1 e S pe c

ia1 Cust

ome

r . l,lithd

rar,r ( )

Appuyez sur Entre pour terminer... J'ai mis en gras les appels clui prsentent un intrt particulier. Les classes tsankAcccuni t Sa.;ir,gsAccount fonctionnent exactement comme on peut I'attendre. Toutefois, en appelant Test (Sa,,'ings.Account ), SreciaiSa lesA.:i:ount t SaieSpecialCurri-rn: se passent eux-mmes corlme un SavirrgsAc.orlnt. Ce n'est qu'en regardant le niveau immdiatement infrieur que la nouvelle hirarchie Sa-cSlecialC'rsr.,.riili peut tre utilise la place de SpeclalSale:cc,ir-'..

Crer une nouvelle hirarchie


PourquoiC# permet-ilde crer une nouvelle hirarchie d'hritage il pas dj assez compliqu comme a ?
? Le

polymorphisme n'est-

C# a t cr pour tre un langage "netable", au sens o les classes excutes par un programme, mme les sous-classes, doivent pouvoir tre distribues sur Internet. Autrement

dit, un programme que j'cris peut utiliser directement des classes venant de dpts
accessibles par Internet.

Je peux donc tendre une classe que j'ai tlcharge sur Internet. La redfinition

des

mthodes d'une hira rc hie de c la sses sta nda rd, teste, peut avoir des effets q u i n'taient pas voulus. L'tablissement d'une nouvelle hirarchie de classes permet mon programme de bnficier du polymorphisme sans risque de briser le code existant.

Ghapitre 15 : Ouelques exceptions d'exception

363

public class
{

MyMathFunctions

I I ce cui suit renrspntp lps veleurs illicites public const int NEGATIVB_NUMBER = -1; nublic const int NON INTEGER VALUE = -2; // Factorial - retourne 1a factorielle d'une valeur
I

fournie
dValue)

public static double Factorial(double


i

// interdit les if (dValue ( 0)


{

nombres

ngatifs

return
]

NEGATIVE_NUMBER

// vrifie si 1e nombre est bien entier int nValue = (int)dValue; if (nValue l= dValue)
{

return
l

N0N_INTEGER_VAIUE

// commence Dar donner 1a valeur 1 un "accumulateur" double dFactorial = 1.0; I I tait une boucle partir de nValue en descendant de I I nol:r mrrl tinlier 1'accunulateur
I

chaque

fois

I par 1a valeur

obtenue

do
{

dFactorial *= dValue; dValue -= 1.0;


1'accumulateur

I while(dValue ) l); // retourne 1a valeur stocke dans


return dFactorial;
l
h'!hlr^ yuurrL { ^t^^^ Lld / t^d^1 uI4D

public static void Main(stringll args)


t

I I appelle en boucle 1a fonction Faciorial de 6 -6 for(inti=6;i)-6;i--)


{

// calcule la factorielle if (dEactorial


I Console
.

du nombre
;

double dFactori-a1 = MyMathFunctions.Factorial(i)


== MyMathFunctions.NEGATIVE-

NUMBER)

}trriteLine

("Factoriai0 a reu un nombre ngatif");


break; l

32 2

Ouatrime partie : La programmation oriente objet

Console.Writeline
account . l,lithdraw

" pour effectuer

Test (BankAccount) ")

( 1 00) ;

l
public static void Test2(SavingsAccount
{

account)
;

Console,WriteLine(" pour effectuer Test(SavingsAccount)")


account . Withdraw ( 100)
] nrrbl
{
r

ir" stat'i c

vo'i

Tpsf

i l'Snpeial SaleAccount account)


Test (SpecialSaleAccount) ")
;

Console,WriteLine

(" pour effectuer


;

account, Withdraw( 100)

l public static void Test4(SaleSpecialcustomer


{

account)
;

Console.Writeline(" pour effectuer Test(SaleSpecialCustomer)")


account . withdraw
]
(
1

00)

// II II
{

BankAccount

- simule un compte bancaire

possdant

un numro de conpte (assign 1a cration du compte) et un solde


BankAccount

publie class
i

il
{

Withdrar,'ral

- tout retrait est autoris jusqu' 1a valeur du solde ; retourne 1e montant retir
Withdraw(double dWithdraw) BankAccount.Withdraw()")
;

virtual public void


]

Console.Writeline(" appelle
l

//
{

SavingsAccount

compte bancaire

qui rapporte des intrts

public class SavingsAccount :

BankAccount

override public void Withdraw(double dHi-thdrawal)


{

Console.i,{riteline(" appelle
]

SavingsAccount.Withdraw0")

nrrhf
{

// SpecialsaleAccount - compte utilis uniquement en priode de soldes ie elass SneeialSaleAccount : SavinssAccount


new

virtual public void

Withdraw(double dWithdrarral)

Chapitre 15 : 0uelques exceptions d'exception

36t

rnnol I e on uvuaE^ |/ |I ofyf,rL rr hnrrnl

II

1a fonction Factorial de 6 -6
1-|

for(inti=6;i)
t
I

I affiche 1e rsultat chaque i,

passage

Console.Writeline("i = {01, factorielle = [].1",


MyMathFunctions.Factorial (i) ) ;
') J

I I attend confirmation de 1'utilisateur Console.ldriteLine("Appuyez sur Entre pour terminer,. .")

Console.Read0;

La fonctiot Fei i,::iti.- I . commence par initialiser 1 unevariable qui va servir d'accumulateur. Elle entre ensuite dans Llne boucrle. mirltipliant I'accumulateur par des valeurs successivement de plus en l)lus petites, depuis ra.lrl. jusqu' ce que n\Ia1,re atteigne 1. La valerlr rsultante cle I'accumulateur est retourne au point cl'appel de la for-rctirrn.

L'algorithme de F: :rc,r r ar (t semble satisfaisant jusqu' ce (lue lon voie comment elle est appele. I'lain i I entre dans une boucle en colttntenant Ia valeur ad hoc pour la factorielle, et dcrmente cette valelrr i) chaque passage, jusqu' 1. Toutefois, au lieu de s'arrter, l.i.: r r cor-rtinrre jusqu' -6. Je sais bien que -6 est une valeur surprenante, mai.s il farrt bien .s'arrter cluelque part. L'excution de cette fonction produit la sortie suivante
:

factorielle = 72A factorielle = i20 factori.elle = 24 factorielle = 6 factorielle = 2 factorielle = 1 factorielle = 0 factorielle = factorielle = -2 factorielle = -3 factorielle = -4 factorielle = -5
1

srrr F,ntrp norrr terminef . ,

Un simple coup d'il avis ces rsultats permet de voir qu'ils n'ont aucun sens. Pour commencer, le rsultat d'une factorielle ne peut pas tre

320

0uatrime partie: La programmation oriente objet

Ce programme commence par dfinir la classe AbstractBaseClass avec une seule mthode abstraite, Output O. Comme elle est dclare abstraite, 0'L1r.put ( ) n'a pas d'implmentation.

Il y a deux classes qui hritent de AbstractBase-Class : SubClassl et SubClass2. L'une et I'autre sont des classes concrtes, car elles redfinissent la mthode Outpr-rt () par des mthodes "relles". Une classe peut tre dclare abstraite, qu'elle comporte ou non des ^e,sc _a membres abstraits ; mais une classe ne peut tre concrte que lorsque fitc toutes ses mthodes abstraites ont t reclfinies par des mthodes lf$rf Y relles. Les deux mthodes Output O des deux sous-classes sont diffrentes, de faon triviale. Toutes deux acceptent une chalne d'entre, qu'elles rgurgitent vers I'utilisateur. Mais I'une convertit I'ensemble de la chalne en majuscules, et I'autre convertit I'ensemble en minuscules. La sortie de ce programme met en vidence la nature polymorphe de la classe Abst ractBaseClas s
:

Cration d'un objet Subclassl


Appel SubClassl.Output0 depuis
TEST

Cration d'un objet Subclass2


^ t\l: t\\vl

\---Il \Y' -

Appel SubClass2.Output0 depuis test Appuyez sur Entre pour terniner...


-a

Une classe abstraite est automatiquement virtuelle.

Crer un objet d'une classe abstraite : nnn

A propos du programme Abstractlnheritance, remarquez qu'il est illicite de crer un objet de la classe AbstractBaseCiass, mais que I'argument de Test O est dclar tre un objet de la classe AbstractBaseClass ou de I'une de ses sous-classes. C'est ici la clause concernant les sous-classes qui est cruciale. Les objets de SubClassi et SubClass2 peuvent tre passs, car I'une et I'autre sont des sous-classes concrtes de Ab st ractBas eClas s.

Chapitre 15

Ouelques exceptions

d'exception
Dans ce chaptre :

Traiter des erreurs avec les codes retourns. Utiliser plutt le mcanisme des exceptions.
Crer votre propre classe d'exceptions.

Renvoyer I'envoi de I'extrieur. Redfinir des mthodes critiques dans la classe des exceptions.

e sais que c'est difficile accepter, mais il arrive qu'une mthode (ou

une fonction) ne fasse pas ce qu'elle est cense faire. Mme celles que j'cris (surtout celles que j'cris) ne font pas toujours ce qu'elles doivent faire. Il est notoire que les utilisateurs aussi ne sont pas fiables. II suffit que vous demandiez un int pour qu'il y en ait un qui entre une chane de caractres. Parfois, une mthode poursuit son chemin joyeusement, ignorant voluptueusement qu'elle est en train de produire n'importe quoi. Il y a toutefois de bons programmeurs qui crivent leurs fonctions de manire anticiper sur les problmes et les signaler lorsqu'ils se produisent.

f "9tt$ ilfi ) qu" C# vous envoie la tte lorsque vous essayez de gnrer votre , U-/ Programme.

Je parle ici des erreurs I'excution, et non cles erreurs de compilation

Le mconisme des exceptions est un moyen de signaler ces erreurs de la telle manire que la fonction appelante peut comprendre et traiter le problme au mieux.

Ghapitre 16 : Manipuler des f ichiers en

C#

3I I

Le programme commence dans llain O par une boucle-"i,- re cottnant un bloc try. Ce n'est pas rare pour un programme de manipulation cle fichiers (dans la section sur Stre:rnP.eade;:, c'est une approche un peu diffrente qui aboutit au mme rsultat).

YK

e,

Placez toutes les fonctions d'l/O dans un bloc i-r-i.avec une instructiotr caT-r.r.r qui gnre un message d'erreur appropri. Il est gnralement considr de mauvaise pratique de gnrer un message d'erreur inappropri. La boucle whi 1e srt deux choses diffrentes. Tout d'abord. elle perrnet au programme de revenir en arrire et d'essayer nouveau en cas d'chec d'une llO.Par exemple, si le programme ne trouve pas un fichier que I'utilisateur peut lire, il peut demander nouveau le nom du fichier pour tre str de ce

qu'il fait avant d'envoyer promener I'utilisateur. Ensuite. I'excution d'une commande break dans le programme vous fait sortir d'un air clgag du bloc try, et vous dpose la fin de la boucle. C'est un mcanisme trs pratique pour sortir d'une fonction ou d'un programme.
Le programme Filei;r ite lit sur la console Ie nom du fichier crer. Il se termine en sortant de la boucle-"^il.: ie si I'utilisateur entre un nom de fichier nu11. L'essentiel du programme se produit dans les deux lignes suivantes.

Pour commencer, le programme cre un objet Fi l eSt: -.an qui reprsente le fichier de sortie sur le disque. Le constructeur:'r-eStrearn utilis ici accepte trois arguments :

t/

Le nom du fichier: Il est clair que c'est le nom du fichier ouvrir. Un simple nom de fichier comme f i L e:,ane . :xt est suppos tre dans le rpertoire courant. Un nom de fichier qui commence par une barre oblique inverse, comme \ur-- rlerioir,:\loncief icirier. i.x*, st suppos tre le chentin d'accs complet au fichier sur la machine locale. Un nom de fichier qui commence par deux barres obliques inverses, par exemple \ \ ..--- r re n.ach i ne \, un r nerto i r c \'lr: aLit r rpertoir e\nondef lchier. rxi. est suppos tre le chemin d'accs un fichier rsidant sur une autre machine. partir de l, le codage du nom de fichier devient rapidement compliqu. Le mode de fichier: Cet argument spcifie ce que vous voulez faire avec le fichier. Les modes d'criture de base sont la cration (CreatelJew), I'ajout (Aopeni), et la rcriture (Cr eate). \'.' ,:z '- -l{er,,' cre un nouveau fichier. Si le fichier existe dj, rlreateile.,L envoie une IOExceotion. Le mode Create simple cre le fichier s'il n'existe pas dj, mais l'crase (le remplace) s'il existe clj. r.ppeni ajoute quelque chose Ia fin d'un fichier s'il existe dj, et cre un nouveau fichier dans le cas contraire.

tz

Chapitre 16 : Manipuler des f ichiers en

C#

40 I

fichier que vous avez entr (lath est une classe conue pour manipuler des informations sur les chemins d'accs).
Le chemin d'ctccs (path) est le nom complet du dossier dans lequel se trouve le fichier. Dans le nom de fichier complet c:\user\tenD dlreC*rct'1.\tex',.lxt, le chemin d'accs est la partie c:\user\i,i-lr oireC*;orv.

.
1e$!Qa.

=qE/

^.v7q

La mthocle ilrnbiner i ) est capable de se rendre compte qu'un fichier comme c: \tes:.txt, PathO n'est pas dans le rpertoire courant.
En rencontrant la fin de la boucle whiie, soit en excutant tout le bloc tr.1, soit en y tant envoy par I'instruction catci'r, le programme revient tout en haut pour permettre I'utilisateur d'crire un autre fichier.

Voici un exemple d'excution de ce programme. Ce que j'ai saisi apparalt


en gras
:

Entrez un nom de fichier (Entrez un nom vide pour quitter) :TestFilel.txt Entrez du texte ; une ligne blanche pour arrter 'Je tape quelque chose Et encore a
E

puis encore

Entrez un nom de

fichier

(Entrez un nom vide pour quitter):TestFiLel.txt

Erreur sur 1e fichierC: \Clfrograms\Filei,lrite\bin\lebug\TestFilel . txt Le fichier existe,


Entrez un nom de fichier (Entrez un nom vide pour quitter) :lestFile2.txt Entrez du texte ; une ligne blanche pour arrter C'est ici que j'ai fait une erreur. J'aurais d I'appeler
TestFiLe2.

Entrez un non de

fichier

(Entrez un nom vide pour quitter)

Annttvcz <rrr Fntr6p nnrrr tprminor

j'entre un texte quelconque dans TestFilel . txt, tout se passe bien. Mais lorsque que j'essaie d'ouvrir nouveau le fichier TestFi ie1 . -rx:, le programme m'envoie le message Le f ichier existe, avec le nom du fichier. Le chemin d'accs au fichier est un peu tourment, parce que le "rpertoire courant" est celui dans lequel Visual Studio met le fichier excutable. En corrigeant mon erreur, j'entre du texte en spcifiant le bon nom de fichier (iestFile2 . txt), sans protestation du programme.
Si

Chapitre 16: Manipuler des fichiers en

C#

403

break;

\ nrl.n l,r)F.Ynn11 \-----'-r --' on Iel


{

ll

erreur envoye indique 1e non du fichier et 1'erreur

Console ]

I,]riteline

" {0J

\n\n" , fe.

Message)

I I lit 1e contenu du fichier Console.i,lriteLine("\nContenu du

fichier :")

try
t I
I I

1:- ..-^ | l-J.L 1.:--: Urltr f fBrrt 1a fOiS


(true)
une ligne

r,vhile
{

I lit

string slnput = sr.Readline0; // cuitte si nous n'obtenons rien en retour


'1

T tStnnllr

=:

nul_LJ

break; l

ll crit
Console
.

sur la console ce
)
;

qu'il a 1u dans 1e fichier

LIriteline ( slnput

l l catch (l0Exception fe)


{

I I

*sisnale I artreoe toute erreur de lecture/criture et - la "*b..*-(ce aui fait aussi sortir de 1a boucle) ;

Console . l,Jrite (fe. Message)

l
I I
{

I I

f.erne le fichier maintenant que nous en avons (en ignorant toute erreur)

fini

avec 1ui

try
sr.
]
C1ose

catch i l // attend confirnation de 1'utilisateur Console.I,lriteLine("Appuyez sur Entre pour terminer. . .")
Console.Read0;

Fiieheaci a une autre approche des noms de fichier. Dans ce prograrnlne, I'utilisateur ne lit qu'un fichier. Il doit entrer un nom de fichier valide pour

Chapitre 16 : Manipuler des fichiers en

C#

40 5

Readline O. Le programme affiche cette ligne sur Ia console avec I'omniprsent appel Coirsole. i,JrlteLi:icr (t avant de revenir au dbut de la boucle pour lire une autre ligne cle texte. L'appel F eaC'iiie ( ; retourne un nu11 lorsque le programme atteint la fin du fichier. Quand cela se produit, le programme sort de la boucle de lecture, ferme I'objet, et se termine.
Remarquez comment I'appel cicse (, est insr dans son propre petit bloc try. Une instruction caici, sans arguments attrape tout ce qui passe sa porte. Toute erreur envoye par C,cse L, est attrape et ignore. L'instruction catch est l pour empcher I'exception de se propager vers le haut de la chalne et d'arrter I'excution du programme. L'erreur est ignore parce que le programme ne peut rien faire en cas cle 1,,,.-sil invalide, et parce qu'il va de toute faon se termir-rer la ligne suivante.

.$sC ,. Je ne donne I'exemple de catch sans arguments qu' des fins de clmonsHtration.Laprsenced,unseulappeldansSonproprebloc'iaVeCune instruction carcfi "attrape-tout" vite qu'un programme s'arrte cause tt9, Y d'une erreur sans importance. N'utilisez toutefois cette technique que
pour une erreur vraiment sans importance, ne pouvant causer aucun
dommage.

Voici un exemple d'excution de ce programme


Entrez le
Could not
nom

d'un fi-chier texte lire :TestFilex.txl


"C:

find fi1e
nom

\C#Prosrams\FileRead\TestFilex.txtr'

Entrez 1e

d'un fichier texte


;

lire:TeslFilel.txt

Contenu du
,Te

fichier

tane

nrrel nrre chose

Et onnnro nn Et a\ ""'*-r-r" c nr.r Y* -" nrri Annrrrroz <rrr Fnfro nnrrr term'inpr

Sauf erreur de ma part, c'est la mme entre qu'avec le fichier

TestFilel . txt

que nous avons cr avec le programme Fr-ei'ir

lte.

=(t ^*\

Ce n'est toutefois pas le mme fichier. J'ai dt copier Ie fichier cr avec Test'rnJrite du rpertoire Trst-ll: !: \1 ii\ie5ug au rpertoire TestRead\b,r\deb.ig. Si vous voulez que ce soit le mme fichier dans les deux cas, il vous faut donner le chemin d'accs complet, comme c:\test.txt (j'aurais pu le faire pour ces deux exemples, mais je ne voulais pas mettre de dsordre dans votre rpertoire racine).

Ginquime partie

Programmerpour Wi ndowsavecVisual Studio

"On vient nettoyer le code."

Chapitre 17

Grer une application Wi ndows: le ramage

et le plumage
Dans ce chapitre :

Trouver un problme rsoudre.


Concevoir une solution.
Dessiner la solution avec la souris.

omprendre C# ne suppose pas d'apprendre crire des aptrllications Windovvs pleinen-rent fonctionnelles. Avant de vou.s mettre la programmation sous Windows en C#, vous devez avoir cle solicles notions de la prograrnmation en C#, ce qui ne peut s'acqurir qu'au prix de r1r-relques mois de programmation d'applications console.

^dK cr des applications :Hqfl {/ /) comme C++.

Je dois nuancer quelque peu l'affirmation qui prccle si vous avez clj

Windows dans un langage de progranlmation

Toutefois, vous pouvez vous familiariser avec la programnration pour Windows en passant par les tapes successives de la ralisation d'une application simple. Ce chapitre va vous guider travers les tapes qui permettent de "dessiner" les applications en utilisant le Concepteur de formulaires de Visual Studio. Le Chapitre l8 prsente les tapes qui permettent d'effectuer des oprations suggres par les formulaires, menus, bannires, boutons, et autres merveilles que vous allez raliser dans ce chapitre.

- Ghapitre 17:Crer une application


2.

Windows: le ramage et le plumage

4l I

Faites une description visuelle de la solution.

Tout programme doit tre dot d'une interface raisonnablernent humaine, faute de quoi un tre humain raisonnable ne pourra pas s'interfacer avec lui. Dans le cas d'une application Windor,l's, cela signifie dcider des accessoires utiliser, et o les placer. Choisir les bons accessoires suppose d'avoir au moins fait connaissance avec ceux qui sont disponibles, mais aussi d'avoir un peu cle talent artistique (ce qui me met en dehors du coup). et encore d'avoir envie de travailler sur le problme concern. Pour remplir cette fonction, il vous suffit de vous asseoir devant votre ordinateur. l-e Concepteur de formulaires pour Windows est d'une telle souplesse que vous pouvez I'utiliser comme un outil de dessin.

3.

Concevez la solution sur la base de sa prsentation et de la

description du problme.
La conception d'une grande application doit tre dfinie dans les

plus grands dtails. Par exemple, je travaille en ce moment sur un systme de rservation pour une grande compagnie arienne. Le travail de conception de ce programme occupe quinze personnes pendant peu prs six mois, aprs quoi le travail de codage et de dbogage prend encore douze mois. Cependant. une petite application Windows est souvent largement dfinie par son interface. C'est plus ou moins le cas avec SimrleECi:oi.

Concetur la prsentaton
SimpleEditor est un diteur, et c'est un diteur simple, Il doit avoir une grande fentre dans laquelle I'utilisateur peut entrer du texte. Comme cette fentre est la partie la plus importante cle n'importe quel diteur, elle doit occuper pratiquement tout l'cran.
Toute application Windows ncessite un menu Fichier, imrndiatement suivi sa droite par un rnenu clition. Les autres lments de la barre cle menus dpendent de I'application. sauf pour I'aide qui en est le clernier.
Dans le menu Fichier, il nous faut un moyen d'ouwir un fichier (Fichier/Ouvrir), un moyen d'enregistrer un fichier (Fichier/Enregistrer), et un moyen de sortir (Fichier/Quitter). Le petit bouton de fermeture dans le coin suprieur droit de la fentre doit avoir le mme effet que Fichier/Quitter. Nous n'avons pas besoin d'une commande Fichier/Fermer. C'est trs joli, mais comme nous n'en avons pas besoin, c'est une chose que nous pouvons garder pour la version 2.

Ghapitre 17:Grer une application Windows: le ramage et le plumage

4l3

Comme je I'expliquerai dans les sections qui suivent, ces tapes ne sont pas mal faites si vous les suivez I'une aprs I'autre.

Crer le cadre de tranal de l'applcaton Windouls


Pour crer le cadre de travail de I'application Windows
:

l. 2. -grfl(c -i: \ -.-/ =( fA ) \t/ 3.

Slectionnez Fichier/Nouveau/Projet.
La fentre Nouveau projet apparalt.

Au lieu de I'icne Application console, cliquez sur I'icne Apptication Windows, et entrez comme nom SimpleEditor.
Dans la fentre Nouveau projet, le champ Emplacement spcifie le rpertoire dans lequel seront stocks les fichiers de SimpleEditor. Autrement dit, Visual Studio va mettre tous les fichiers que je vais

crer dans c:\Programmes c/\simpleEoitor.

Cliquez sur OK.


Visual Studio travaille quelques instants pour gnrer I'affichage montr par la Figure 17.2.

Ei(htr/ Edilr

traW PJol Gn Sdqs

.!' -L'-t
I i!. EO
::'::

:.t ,r i.r remt.r [&99]

l -' '!'

?i"ffi,,:li#"i:'l;|,t2,';',. -inl:J

rm8ordr5i/

5,rable

FqhtToLeft

li.

tigure 17.2:
L'affic h age initial pour toutes les

Ir--t ina,."r.

t.,:.1
FisF

AIc\4tro! ::t te lW, nnd ImetlDde B t;tytt :, ... Ie*


LE

l3qrri
t te

l,n:!nt.r
.

Ei iorEniPnpr' lexind6u

applications Windows.

&ffiGcdrdt

Ce curieux affichage s'appelle le Concepteur de formulaires (ou, plus simplement, le Concepteur). Le cadre de vous voyez gauche est le formulaire, qui va tre la base de notre programme SirnpleEdltor.

- Ghapitre 77 : Crer une application


a

Windows: le ramage et le plumage

4l 5

lgnorez ce ttlpe qui se cache derrire le rideau


Avant d'aller plus loin, je veux jeter un coup d'il au code C# gnr par le Concepteur. Je veux savoir ce qui se trame l-dedans. L'Explorateur de solutions montre que le fichier source de ce programme se trouve dans un fichier nomm F,.--rm1. c:s, ce qui correspond au nom qui se trouve audessus du formulaire dans la fentre du Concepteur.
Slectionnez Affichage/Code pour faire apparaltre une nouvelle fentre contenant le code source C# de F o rin I . .' -c, que voici
:

.'^"i-^ urrr

C,,a+^-. eJLEur,

using System.Drawing; using Systern. Collections using System, ComponentModel using System.Windows . Forms; using System.Data;
;

nanespace SimpleEditor
{

//l
II

(sumnary)
,

I Snnnerv dpsc ri nti on for T'orml lll (lsunnary) nrhl j^ nlrcc l'61911 : SyStenr.WindOvS.FOrmS.FOrm
t
I

(lsmnary\ private System. public Forml 0


{

/i/ III III

(sunmary)

Required designer variable.


ConponentModel,

Container components = nul1;

lt / lt
i
tt

Required

for

Windows Form Designer support


(

InitializeConponent

lt
i

lt
l

TOD: Add any

constructor code after InitializeComponent call

I III
II t
r

lll

kunnary)
clean up any resources being used.

(lsumary)
)

protected override void Dispose( bool disposing

if(
{

disposing

if
t
f

(conponents != nu11)

4t6

Ginquime partie: Programmer pour Windows avec Visuaf Studio

components.Dispose0; l

l
cnncinheca D'i cnnco ( v4s . urDyvoc uIDyu rrI \ rli I ,,
.
t

#region Windows Form Designer generated code

for Designer support - do not modify the contents of this nrethod r+ith the code editor. llsunnary) private void InitializeConponent0
Renrrirerl method
{

I /// III lll


//

(sunrnary)

For:nt

this.AutoScaleBaseSize

this.ClientSize =
this.Name

new System.Drawing.Size(5, 13) new Systen.Drawing.Size {292, 273);

= "Forml";

this.Text
l

"Sirnple Editor";

/// III lll


{

(summary)

fhe main entry point (lsunnary)


Main0

for the application.

ISTAThread]

static void
Annl 4ytl!rLo

inatinn rrvtl

. '^rml i/\ Rln /nor., .l\ulr I ulIlI \rrgw \ / ,/ ,

'

static Main O, qui se trouve ici tout en bas du listing. Voil ce qui nourrit ma conviction que c'est ici qu'il faut commencer. La seule instruction que contient Main O cre un objet
Je sais que le programme doit cornmencer par

Forml ( ) et le passe une mthode Application. Run (). Je ne suis pas de ce que fait F.un O, mais je souponne fortement que la classe I'orm1 correspond la fentre Forml que j'ai vue dans le Concepteur.

str

1e${Qa. En fait, Application. Run O lance I'objet Form sur son propre thread 7^l \ d'excution. Le thread initial s'arrte aussitt que le nouveau Forml est =lf \ff / cr. Le thread Fornl se poursuit jusqu' ce qu'il soit intentionnellement Y,/ arrt. Le programme SirnpleEditor lui-mme poursuit son excution aussi longtemps que des threads dfinis par I'utilisateur sont actifs.
Le constructeur de Formi invoque une mthode InitializeComponent O. Tout code d'initialisation du programmeur doit tre plac aprs cet appel (tout au moins, c'est ce que dit Ie commentaire).

4l 4

Cinquime partie : Programmer pour Windows avec Visual Studio

Un formulaire est une fentre contenant une barre de titre, et optionnellement des barres de dfilement. Dans la terminologie de C#, une fentre n'est rien d'autre qu'un cadre rectangulaire dans lequel vous pouvez placer des images ou du texte. Une fentre n'a pas ncessairement des menus ou des tiquettes, ni mme ces petits boutons Fermer, Rduire et Restaurer.

4.

Gnrez Ie programme que Windows vient de crer sur la base du modle. Vous pouvez me traiter de paranoaque, mais je veux tre certain que toutes les erreurs qui pourront apparaltre par la suite seront rellement de mon fait et ne viendront pas de Visual Studio. Sans aucun doute, la solution va se gnrer sans encombre ce stade. L'excution de ce programme ne rvle rien d'autre qu'un formulaire vierge, dot de l'tiquette Forml.ll suffit de cliquer sur le bouton Fermer pour arrter le programme.
Le volet qui occupe la partie droite de I'affichage est la fentre

Proprits. a ne saute peut-tre pas aux yeux, mais son contenu est en relation directe avec le formulaire qui est dans la partie gauche de I'affichage. Par exemple, vous pouvez voir que la proprit Text est Fo rrnl . Vous pouvez la modifier pour vous rendre compte de I'effet produit.

5.

Slectionnez la proprit Text, et donnez-lui Ia valeur Simple Editor.

L'tiquette Forml contenue dans la barre de titre du formulaire devient Simple Editor.

6.

Gnrez nouveau I'application, et excutez-la.


Le nom du formulaire a chang, comme le montre la Figure 17.3.

Text du

Figure 17.3 Changer la proprit

formulaire change le nom qui a ppa rat oans sa barre de titre.

4l 2

Cinquime partie : Programmer pour Windows avec Visual Studio

Le menu dition a besoin des trois grandes options d'dition : Couper, Copier et Coller. D'autre part, tous les diteurs comprennent les raccourcis clavier de ces trois options : Ctrl+X, Ctrl+C, et Ctrl+V, respectivement.

SimpleEditor aura galement besoin d'un menu Format, comportant les options Gras et ltalique pour mettre en forme le texte.
Fournir une aide vritable est une tche difficile - beaucoup trop complique pour un diteur simple comme SimpleEciitor. Le menu d'aide de cette application devra se contenter du minimum absolu : I'option propos de. Dernire exigence : il nous faut un moyen de contrler la taille de police. Voil une chose qui laisse la place un peu de fantaisie. En plus d'une simple fentre dans laquelle I'utilisateur peut entrer la taille de police souhaite, SimpleEdltor y ajoutera une sorte de barre munie d'un index que I'on peut faire glisser, que nous appellerons TrackBar. Pour obtenir 8 points, faites glisser I'index I'extrmit gauche. Faites-le glisser I'extrmit droite, et vous clbtenez 24 points. (J'ai une autre raison de procder ainsi : je veux vous montrer comment relier deux objets d'l/O de manire qu'un changement dans I'un soit rpercut dans I'autre.)

Itla soluton
Avec les paramtre.s que j'ai dcrits dans la section prcdente, je suis arriv la solution montre par la Figure 17.1. Vos propres rsultats peuvent tre diffrents selon vos gotts personnels.
W:"
-lol
Frrrt
?

t{J

Fichier Edition

r:I

-ll IirJ+ *rr 1 -

p trI: t -'rn

=rl

lus grand Figure 17.1 Ma solution du problme


:

SinpleEditor.

ralb

s-

'Je

police

jf14

)|

Dessner la soluton
Comme vous pouvez I'imaginer, j'ai dt passer par de nombreuses tapes pour arriver en partant de zro l'uvre d'art montre par la Figure 17.1.

4l 0

Cinquime partie : Programmer pour Windows avec Visual Studio

Quel est le problne )


Il m'a fallu une longue et difficile rflexion (au moins un quart d'heure) pour imaginer un problme qui mette en lumire la puissance de C# sans me faire prendre du poids. Le voici : crer un diteur .simltle que nous appellerons S LnpleEciit-or. Il aura les caractristiques suivantes :

t/ t/

L'utilisateur peut entrer et effacer du texte (sinor-r, ce rle serait pas vraiment un diteur). L'utilisateur peut couper et coller du texte. non .seulement clans SrrpieEdi*.o1, mais aussi entre il ::;;, '.:i1,,1i., r ct d'etutres applications, par exemple Word.

t/ t/

SinltcEcr*.or supporte les polices en gras, en italique ou les deux.


L'utilisateur peut slectionner une taille cle police cle B 24 points. Ces limites sont arbitraires, mais il s'agit ici cle ne pas aller trop loin en nombre de points.

t/

SrnpleEclitor ne doit pas vous permettre de quitter sans vous avoir demand poliment d'enregistrer le fichier que vous venez de modifier (mais vous restez libre cle quitter sans enregistrer si c'est bien ce que vous voulez).

Erposer le problme
Chaque fois que vous tes devant un problme rsoudre, vous devez commencer par vous mettre devant le tableau noir et rflchir srieusement aux obstacles franchir. Dans le cas d'une application Windows, cette tche se divise en trois tapes :

l.

Dcrivez le problme en dtail.


Ces dtails sont les spcifications auxquelles doit se conformer

I'application. Au cours de la programmation. vous pourrez tre tent d'ajouter une fonctionnalit ici ou l. Rsistez. Cette maladie s'appelle fonctionnalite. Tout en avanant, notez les amliorations possibles pour une version future, mais l'ajout de fonctionnalits en cours de route fait courir le risque de crer une application qui finit par tre tout fait autre chose que ce qu'elle tait cense tre au dpart.

grrs rctte layt&

"*

orttlircrr<llt. i"# et iilrEt r,lr<)se. apl)t'(:n<lre ii ri<:rire Lllle alipiil:irtlrrrr \i'irrrirlrvr; <:oirrplte ar,/ec totrs sc:s assernrlages et ses cld:ct>r;iti<ltts lrit'-tt ett illiir'..'r'r) est utre atttre. flien (]lle pour le plaisir, l;r cirrriiiii'iiir 1r;u iiet \-()u$ grricle pras a. pas clarrs I'utilisation cle C# avec j'!ritrrfi:.r:c Visiurl Studio afin rle crer" urie lt;rplication Winclorv:i 'rpri rrr.: soit p;i"s trivi;r.le". Vorrs serez fier rlrr r:sultat, rnme siv<ts t.'liflurl.s lr';ri:pelieiit pas leurs cotrr;rins trtour le voir.

40 4

Ouatrime partie : La programmation oriente objet

que le programme donne la sortie attendue. Une fois que le programme lu le fichier, il se termine. Si I'utilisateur veut lire un autre fichier, il lui suffit d'excuter nouveau le programme.
Le programme commence par une boucle rihr

le, coilffie son cousin Dans cette boucle, il va chercher le nom de fichier entr par I'utilisateur. Si le nom de fichier est vide, le programme envoie un message d'erreur:\Ious a\r?.z entr un nom ie rlchiel ,,'ide. Dans le cas contraire, le nom de fichier est utilis pour ouwir un objet FrleSt-reai. en mode cle lecture. L'appel Frle.LJpen ( ) est ici le mme clue celui utilis dans i -..-r',:ire :
Filel^i

rite.

/ t/

Le premier argument est le nom du fichier. Le deuxime argument est le modle du fichier. Le mode Fil-.Mode. Ctpen dit : "Ouvrir le fichier s'il existe. sinon envoyer une exception." L'autre possibilit est r-lprq1,11ew, qui cre un ficl-rier de longueur nulle si celui-ci n'existe pas dj. Personnellement, je n'ai jamais rencontr le besoin de ce mode (qui veut lire un fichier vide ?), mais chacun mne sa barque comme il I'entend.
Le dernier argument indique que je veux lire

t/

partir de ce

File-tream. Les autres solutions sont

ririte et R..aiLi^ir ite.

L'objet FileS:rean f s rsultant est alors insr dans un objet StreairP.r:ac1er sr qui offre des mthodes pratiques pour accder au fichier texte. Toute cette section d'ouverture de fichier est enchsse dans un bloc t r.,.,
lui-mme enchss dans une boucle whiie, insre dans une nigme. Ce bloc t ry est strictement rserv I'ouverture de fichier. Si une erreur se produit pendant le processus d'ouverture, I'exception est attrape, un message d'erreur est affich, et le programme reprend au dbut de la boucle pour demander nouveau un nom de fichier I'utilisateur. Toutefois, si le processus aboutit un objet nouveau-n St reanReader en bonne sant, la commande break fait sortir de la logique d'ouverture de fichier et fait passer Ie chemin d'excution du programme la section de lecture.

.ssG ./ Y

FileRead et Frle'ririte reprsentent deux manires diffrentes de traiter


bien vous pouvez donner son propre bloc I r_,. la section d'ouverture de fichier. Cette dernire solution est gnralement la plus facile, et elle permet de gnrer un message d'erreur plus prcis.
Une fois le processus d'ouverture de fichier termin, le programme FileRead lit une ligne de texte dans le fichier en utilisant I'appel

Hdesexceptionsdefichier.VouspouVeZinsrertoutleprogramrrrede traitement de fichier dans un mme bloc rr-,,, comme dans Fi le,.,r rte, ou tTg,

402

0uatrime partie:La prog;ramrnation oriente ohjet

Amtiorez $tre crnpr(tensnn et otre Uitesse de f ectmre aec S t- r e anii.l r: a ,l e r


Il est trs a.qr;,rhle rl'<'rire sur ull fichier. mai.s c'est plr-rtt inutile si vous ne pouvez l)a$ lirt'lr: fir.ririer i,lar la suite. i,e proglAmte r i ii.,,eacl suivant affir:he'sur l;i c(-jn-c{)Jr.r r* rlll'rl lit clans ie fichier, ( e lrrogramnte lit un
.,:

fichier tt-rxtt: (:{-i:rinrr".'r:ltri {ltrr:' cr} L,

.' r .

// FileRead 1it un fichier texte et l'crit sur la console II


,,.i-^ urrr6 rrc"ino *- "'^b C"^+^*. uJ Lclr Svctonr
t

0'

irtucy4Lg
{
yuufrL

f -Lgr\u
vaq ^ ..^ ,'.ss] tra

pubric static voiii Mai;r(stringI args)


{

l/ i1

nous

faut un objet pour lire le fichier

streamReaoer sr; strins sF:leName -

= ""; "'" ii continue essayer de lire un noin de fichier jusqu' ce qu'il // trcuve un (1a seule naaire de quitter pour I'utiiisateur est I I d'arrter 1e programne en appuyant sur Ctrl + C)
t

en

while {true)
f-- r\r J

i
I I Iit le non du fichier d'entre Console,Write("Entrez le nom d'un

fichier texte lire :");

sFileName

= Console. Readline 0

ll 1'utilisteur n'a rien entr ; envoie une erreur ll pour 1ui dire que ce n'esr pas satisfaisant if (sFileName,Length == 0)
t

throw
]

rLew

IOException("Vous avez entr un non de

fichier vide");

l/ ouvre un flux de fichier pour la lecture ; ne cre pas // le fichier s'i1 n'existe pas dj FileStream fs = Fi1e.0pen (sFileName,
Fj.ieHode.0pen,

FileAccess . Read) ;

// convertit ceci en StreanReader - ce sont les trois premiers // octets du fj-chier qui seront utiliss pour indiquer i / 1'encodage utilis (mais pas 1e langage) sr = nev StreamReader(fs, true);

400

Ouatrime partie : La programmation oriente objet

,/

Le type d'accs : Un fichier peut tre ouvert pour la lecture, l'criture ou les deux.

^1'sC

Hpondpardfautunoucleuxclesargumentsdemodeetd'accs.Toutefois, mon humble avis, il vaut mieux spcifier explicitement ces arguments, car ils ont un effet important sur le programme.

t(9, Y

Dans la ligne suivante, le programme insre dans un objet Streaml^/riter,


sw, I'objet FileStream qu'il vient d'ouvrir. La classe Streamrudriter permet d'insrer les objets FiieStream, afin de fournir un ensemble de mthodes pour traiter du texte. Le premier argument du constructeur Stream\,,/riter est I'objet FlleStrearn. Le deuxime spcifie le type d'encodage utiliser. L'encodage par dfaut est UTF8.

rg9{Qa. Il n'est pas ncessaire de spcifier I'encodage pour lire un fichier. S7^Hl\ Stream'riri:er inscrit le type d'encodage dans les trois premiers octets =\f \ff / d, f ichier. A l'ouverture du fichier, ces trois octets sont lus pour dter-

("

miner I'encodage.

Le programme f iie',^,'rlte commence alors lire sous forme de chanes les lignes saisies sur la console. Le programme arrte de lire lorsque I'utilisateur entre une ligne blanche, mais jusque-l il continue absorber tout ce qu'on lui donne pour le dverser dans I'objet StreamWriter sw en utilisant la mthode iiriteLine ( ). 1t-c!!Qa^

^rv7q :/dqf, v,/

La similitude entre St ream\.,iriter n'est pas qu'une coincidence.

t.iriteLine ( ) et Console
sw . C1o
s

.t^i

riteLine

Enfin, le fichier est ferm par I'instruction

e()

b'

Remarquez que le programme donne la rfrence sw la valeur nu11 Ia fermeture du fichier. Un objet fichier est parfaitement inutile une fois que celui-ci a t ferm. II est de bonne pratique de donner la rfrence la valeur nu11 une fois qu'elle est devenue invalide, afin de ne pas essayer de l'utiliser nouveau dans I'avenir. Le bloc catch qui suit la fermeture du fichier est un peu comme un gardien de but : il est l pour attraper toute erreur de fichier qui aurait pu se produire en un endroit quelconque du programme. Ce bloc met un message d'erreur. contenant le nom du fichier qui en est responsable. Mais il ne se contente pas d'indiquer simplement le nom du fichier : il vous donne son chemin d'accs complet, en ajoutant I'aide de la mthode Path. combirre O le nom du rpertoire courant avant le nom de

39 8

Ouatrime partie : La programmation oriente

ob

jet

I |

FileAccess.Write,
FileAccess.ReadWrite

FileStream

fs = File.0pen (sFileName,
FileMode . CreateNew,

FileAccess.l.rlrite) ; // gnre un flux de fichier avec des caractres UTFS sw = new StreamWriter (fs , System. Text , Encoding. UTIB) ; I I lit une chalne 1a fois, et envoie chacune au // FileStrean ouvert pour criture Console.l'lriteLine("Entrez du texte ; ligne blanche pour arrter"); while (true)
{

I |it 1a ligne suivante sur la console i/ quitte si 1a ligne est blanche string slnput = Console,ReadLine0 if (slnput.Length :: 0)
I
;

break;

ll crit sur le fichier de sortie la lisne oui .rient d'tre


sw.

lue

l,friteLine ( slnput ) ;
que nous avons cr

l
I

I ferne 1e fj-chier
0
;

sl^r. C1ose

sw

= null;

i catch(IOException fe) i

I une erreur s'est produite quelque part pendant // 1e traitement du fichier indique 1'utilisateur // le nom eomnlet du fichier I ^: ^..-^ 4U ^'. -om drr !cy! rnprto.i rc Dar rrvru uu | |t dJUULC Lvr!c dfaut f IT / / celui du fichier strino cDi r = Di roctorv CotCrrrrpntDi rpntorv( uvrJ \ /) string s = Path. Conbine (sDir, sFileName) Console.l,/riteLine("Xrreur sur 1e fichier{01", s) // affj.che naintenant 1e message d'erreur de 1'exception
I
: : , ; ;

Console ] ] I

Writeline (fe

Message)

I attend confirmation de 1'utilisateur Console.l'lriteLine("Appuyez sur Entre pour terminer.


Console.Read O
;

. . ")

Fil-e\n'rite utilise I'espace de nom Systen.l(l ainsi que S','srerr. S-,,'stern.lO contient les fonctions d'l/O sur les fichiers.

39 6

0uatrime partie : La programmation oriente

ob

jet

U0 asynchrones : est-ce que a vaut la peine d'attendre ?


Normalement, un prCIgramme attend qu'une requte d'l/0 sur un fichier soit satisfaite avant de poursuivre son excution. Appelez une mthode read O, etvous ne rcuprerez gnralement pas le contrle aussi longtemps que les donnes du fichier ne seront pas installes bord en scurit. C'est ce que l'on appelle une l/0 synchrone.

Avec C#, les classes de System. r0 supportent galement les f/0 asynchrones. En les utilisant, l'appel read ( ) restitue immdiatement le contrle pour permettre au programme
de poursuivre son excution pendant que la requte d'l/0 est satisfaite I'arrire-plan. Le pr0gramme est libre de vrifier l'tat d'un indicateur pour savoir s1 la requte d'l/0 a abouti.
C'est un peu comme de faire cuire un hamburger. Avec des l/0 synchrones vous mettez la viande hache cuire sur la plaque chauffante, vous la surveillez jusqu' ce qu'elle soit cuite, et c'est seulement partir de l que v0us pouvez vous mettre couper les oignons qui vont aller dessus.

Avec des l/0 asynch rones, voLrs p0uvez couper les oignons pendant q ue la viande hache est en train de cuire. De temps en temps, vous jetez un coup d'il pour voir si elle est cuite. Le momentvenu, v0us abandonnez un instantvos oignons, etvous prenez la viande sur la plaque chauffante pour la mettre sur le pain.

l/0 asynchrones peuvent amliorer significativement les perforrnances d'un programme, mais elles ajoutent un niveaur supplmentaire de complexit.
Les

Utilser S t

i:"

r-r

ili,{r i t e r

[-es programmes gnrent cleux sortes de sortie. Cert;rins programrnes crivent cles tiiot:s tlc rlortnes clans un pur fornrat binaire. Ce type cle

sortie est trtile porrr stocker cles objets cl'urie nranire efficace.
Beattcoup cle pro{tar}}ines, sirron la plupart. liseni r:t crivent des chalnes cle texte, lisibles par ult :tre lrurrrairr. Les classes cle flux Sr: eai:l,,rrirer et StrcanP,e,:;,.r,1,-:r s()ut les plus souples cles classes arccueillantes pour I'homnte.

:(dqfl
\/

Ae/'!-t\

rs9Ka^ Les dounes lisiltie.s ;iar un tre hurnain taierrt antrieurement


)

des

chalnes ASCII, ou, i.rrI peu plus tarc1, NSL Ces deux sigles se rfrent aux organisati<lns cle stanciardisation tlrri ont rlfiiri c:es formats. Toutefois, le codage ANSI lte pennet 1r;ts cl'inttigrer les alphabets venant de plu.s loin que l'Autriclre l't-st, et tie plir.s loin que [{arvai' I'Or:e.st. Il ne peut contenir que I'allrhabett latin. Il ne clispose pas de I'alphabet r:yrillique, hbreu,

39 4

ouatrime partie : La programmation orienre objer

t/

Une mthode dclare internal est accessible par toutes les classes du mme espace de nom. Aussi, I'appel class2. D internal O n'est pas autoris. L'appel cl ass3. c internal O est autoris parce que Ciass3 fait partie de I'espace de nom AccessControl.

t/

Le mot-cl internai protected combine I'accs interr:a1 et I'accs protected. Aussi, I'appel class L E-internalprotected est autoris, parce que C1ass1 tend Class2 (c'est la partie protected). L'appel cf ass3 . E_internalprotecreci ( ) est galement autoris, parce que C1ass1 et C1ass3 font partie du mme espace de nom (c'est la partie inrernal).

t/

La dclaration de c1ass3 comme internal a pour effet de rduire I'accs celle-ci 1nterna1, ou moins. Aussi, les mthodes pub11c deviennent internal, alors que les mthodes protected deviennent

ir iernal

DLofecleJ.
:

Ce programme donne la sortie suivante


C1ass2.A-public Class2.B_protected C1ass1.C-private C1ass3.D_internal
C1ass2 C1ass3
. .

E*internalprotected
E_internalprotected sur Entre pour terminer...

Appuyez

rsq- I lX [f9, Y

Dclarez toujours les mthodes avec un accs aussi restreint que possible. Une mthode prive peut tre modifie volont sans inquiter de I'effet que cela pourrait avoir sur d'autres classes. Une classe ou une mthode interne de Ma thRoutines est utilis par d'autres classes de nature mathmatique. Si vous n'tes pas convaincu de la sagesse du couplage faible entre les classes, allez voir le Chapitre 1 1.

Rassenbler des donnes dans des fichers


Les applications console de ce livre reoivent essentiellement leurs entres de la console, et y envoient de mme leur sortie. Les programmes des autres sections que celle-ci ont mieux faire (ou autre chose) que de vous embter avec des manipulations de fichiers. Je ne veux pas les obscurcir avec la question supplmentaire des entres/sorties 0/O).Toutefois, les

applications console qui n'effectuent pas d'opration d'entre/sortie sur des fichiers sont peu prs aussi courantes que les phoques dans la Seine.

392

guarrime parrie : La programmation oriente obiet

| la

mme

classe
;

C-private () c1ass1.C-private0 ;
I I class2. I I

I les mthodes internes ne sont accessibles I les classes du mme espace de nont

que par

I I c|ass2. D-internal ( ) ; c1ass3.D -internal 0 ; I I es mthodes internes protges

sont accessibles
par

// soit par 1a hirarchie d'hritage soit I I toute classe ciu mme espace de nom
class I . E-internalprotected class3 . E-internalprotected
(
(

; ;

// attend confirmation de 1'utilisateur


Console,WriteLine("Appuyez sur Entre pour terminer. . , ")
uons0ie. Kea0
1/\ ;

(,r

return
l
-r,hr-^ yuurru

0;
| --lvatel/ w_y!

,r^rd vvfu

/\
1,

Console,

!riteline ( "Class

C-private" ) ;

l j

// Classl - une classe interne est accessible aux autres classes du mme espace de nom, mais II pas aux classes externes qui utilisent cet II espace de non II internal class Class3
{

I I

I Ia dclarati-on d'une classe comme interne force toutes I les mthodes publiques tre galement internes
Console

nrrhlin vnid A nrrblic0


t
.

l,Jriteline ( "C1ass3 .A-pub1ic " ) ;


vnid B nrotectpdo rlv!s\/ vvru
;

nrntpntpd y!vLLLu
{

Console.hlriteline ( "Class3 . B-protected")


]

internal
t

voi.d D-internal o
("

Console . 1,,Iriteline

Class3 . D*internal" )
v LvL uee

l
^,rhlin
yuurJL

,r^i,l E' inf arnr'lnrntontod


v vfu u_frr

\ /

Console

|jritel,ine

"C1ass 3 . E-internalprotected"

39 0

Ouatrime partie : La programrnation oriente ohiet

Utilser un esle de nom atlec le wot-cl usirrg


Se rfrcrr rrnr: r"lasse

p;lr son noni pleinentent qualifi plcut clevetiir utt peufasticlieux. Lernot"cli.r::,:rg_cleC#vousperntetd'vitercepetlstltlt"
,,,;ijotrte I'espace rJe norn s1rcifi ture liste d'esl)aces tlirf;iut rlrre (.# <.:rlrrsrrlte p)()ur essayer cle rscx-rclre utt noltt cle classe. L'exc.inple rle l)r()qr;unlne.suivarrt se corntrlile sitns une plzrintrt: l.a c<>rnrnncli' ,,
cle nclrn par
namespace Paint
{

,-i

public cl.ass PaintColcr


{

public PaintCol"or(int nRed, int nGreen, int nBLue) i] public vcid Paint0 {l
publ"ic
]

static vcid StaticPaint0 {l

i
namespac
{

llathP.out ines

I /

I ajoute ?aint alx / automatiquement

espaces de nom dans lesquels on cherche

using Paint; public class Test


{

static public
t

voj.d

Main(stringll

args)

I I cre un cbjet dans urr autre espace de non - i1 n'est ll pas ncessaire Ce faire figurer 1e nom de 1'espace de nom, car // celui -ci est inclus dans une instruction "usins" PaintColor bl-ack = new PaintColor(0, 0, 0);

b1ack. Pai.;.t ()
]

;
;

PaintColor, StaricPaint 0

l La courrnancle ..,r i r. ,, rlit : ' Si volls ne trouvez pas ia c'lasse spcifie dans I'espace d' rrorn courant. voyez si vous la trouvez (lans celui-ci." Vot.ls pouvez spcifier arrtarrt tl'espaces cle norn que vous voulez, mais toutes les cornrnanrlc-s ',r.r , - ; cloivent apparaltre I'une aprs I'autre tout fait au dbut clu pr()qrallll].

9i51 I(Bfl \SZl -

Tous les proqramme.s (:onrmencent par la crltumande iising SJ,.ten: , Elle donne au proqr:i.lnrne rrn accs autotratique trtutes les fonctions cle la

bibliothque systillte, conll

;,ir

r i :',:

r-

388

0uatrime partie : La programmation oriente objet

Ti-ansiati onLibr.r :--. respectivernent ces deux ensembles de classes vite le problnre : FileIO. Ccn,rert ne peut pas tre confondu avec
-f,...,,
I GtLi . )

,,i

-il,
L -Ir l

f--.- f r'.' .-:'

Dclarer un espace de nom


On dclare un espace de nom en utilisant le mot-cl nan.-:pce, suivi par un nom et un bloc d'accolades ouvrante et fernrante. Les classes spcifies dans ce bloc font partie de I'espace de nom.
namespace MyStuff
{

class MyClass {i class UrClass {J


l

Dans cet exemple,

etl

I r ;,.,,s

font partie de I'espace de nom

l'{ySruff.

,,s$G t

L'Assistant Application de Visual Stuclio place chaque classe qu'il cre dans
nez tous les programmes cle ce livre : ils ont tous ts crs I'aide de I'Assistant Application. Par exemple, le programnle AliqnOutput a t cr dans le dossier --t1!.ni;u-f lrL1*.. Le nom du fichier source est Class i . cs, qui corresponcl au nom de la classe par dfaut. Le nom de I'espace de nom dans lequel se trouve Class l . t-:: est le mme que celui du dossier:Ai,gnoLitpl-1...

HuneSpaCeclenomportantlemrnenOmquelerpertoirequ'ilcre.Exami-

t\7, V

Si vous ne spcifiez pas une dsignation d'espace de nonr, C# place

votre

classe dans I'espace de norn global. C'est I'espace de nom de base pour tous les autres espaces de nom.

Accder des modules du mme espace de nom


Le nom de l'espace de norn (l'rrne'classe est une partie du rtonl cle la classe tendue. Voyez I'exemple suivant
:

namespace MathRoutines t

class Sort
{

public void

SomeFunction0

{l

386

Ouatrime partie:La programmation oriente obiet

ne peut pas tre modifi par deux programmeurs en mme temps. Chacun d'eux a besoin de son propre fichier source. Enfin, la compilation d'un module de grande taille peut prendre beaucoup de temps (on peut toujours aller prendre un caf, mais il arrive un moment o votre patron devient

souponneux). Recompiler un tel module parce qu'une seule ligne d'une seule classe a t modifie devient intolrable. Pour toutes ces raisons, un bon programmeur C# divise son programme en plusieurs fichiers source .CS, qui sont compils et gnrs ensemble afin de former un seul excutable.
Imaginez un systme de rservation de billets d'avion : il y a I'interface avec les agents de rservation que les clients appellent au tlphone, une autre interface pour la personne qui est au comptoir d'enregistrement, la partie Internet, sans parler de la partie quivrifie I'occupation des siges dans I'avion, plus la partie qui calcule le prix (y compris les taxes), et ainsi de suite. Un programme comme celui-ci devient norme bien avant d'tre termin.

Rassernbler toutes ces classes dans un mme fichier source C1ass1 . cs est remarquablement draisonnable, pour les raisons suivantes :

t/

Un fichier source ne peut tre modifi que par une seule personne la fois. Vous pouvez avoir vingt trente programmeurs travaillant en mme temps sur un grand projet. Un seul fichier pour vingtquatre programmeurs impliquerait que chacun d'eux ne pourrait travailler qu'une heure par jour, supposer qu'ils se relaient vingtquatre heures sur vingtquatre. Sivous divisiez le programme en vingtquatre fichiers, il serait possible, bien que difficile, que tous les programmeurs travaillent en mme temps. Mais sivous divisez le programme de telle manire que chaque classe a son propre fichier, I'orchestration du travail de ces vingtquatre programmeurs devient beaucoup plus facile.

,/

Un fichier source unique peut devenir extrmement difficile comprendre. Il est beaucoup plus ais de saisir le contenu d'un

module comme ResAgertlnterface . cs, GateAgentlnterface . cs, Resirgent.cs, GateAgent. cs, Fare. cs ou Aircraft. cs.

t/

La rgnration complte d'un grand programme comme un systme de rservation de billets d'avion peut prendre beaucoup de temps. Vous n'aurez certainement pas envie de rgnrer toutes les instructions qui composent le systme simplement parce qu'un programmeur a modifi une seule ligne. Avec un programme divis en plusieurs fichiers, Visual Studio peut rgnrer uniquement le fichier modifi, et rassembler ensuite tous les fichiers objet.

38 4

Ouatrime partie : La programmation oriente obiet

Le programme CustonException donne la sortie suivante


Erreur fatale inconnue
:

Le nessase est (Tmnossihle d'inverser CustomExc eption . MathClass Exception envoye parDouble Inverse0
Annrrrraz crrr Fntro nnrtr tprminor

0). I'ohiet est

(Value = 0)

Jetons un coup d'il cette sortie : le message lifc,,.ir faia L.r i rc.or.nue : vient de l'{a: i-' i l. La chane -,t n's'r- -.il'. ( -in:ossi l-,-i-' r,l 'in ,,.erser 0,\, I'c,l iet esi <--) vient de ilu.-"t,-.11E:tcLrti.l. Le message Value 0 vient cle I'objet l"1ai-irCiass lui-mme. La dernire ligne, Excep tion en.".ove parDouble In'rerse, vient de C,-Lsl-r,'t:Fl,ce 'rti cn.

ToString{}, Ia carte de visite de la classe


Toutes les classes hritent d'une classe de base commune, judicieusement nomm 0b j ec t. C'est au Chapitre l7 que j'explore cette proprit qui unifie les classes, mais il est utile de mentionner icique Ob j ect contient une mthode, ToString ( ), qui convertit en string le contenu de l'objet. L'ide est que chaque classe doit redfinir la mthode ToSt ring O par une mth0de lui permettant de s'afficher elle-mme d'une faon pertinente. Dans le chapltre prcdent, j'ai utilis la mthode GetStringO parce que je ne voulais pasy aborder les

questions d'hritage, mais le principe est le mme. Par exemple, une mthode

Srudent.ToString O pourrait afficher le nom et le numro d'identification de l'tudiant.


La plupart des foncti0ns, mme les fonctions intgres de la bibliothque C#, utilisent la mthode ToString O pour afficher des objets, Ainsi, le remplacement de ToString O a pour effetsecondaire trs utile que l'objetsera affich dans son propre format, quelle que soit la fonction qui se charge de I'affichage.
Comme dirait Bill Gates, "C'est cool."

382

Ouatrime partie:La programmation oriente objet

/
t

le

message standard Exception. ToString

override public string ToString0

string s = Message * "\n";


s *= base,ToStrine0;
]

//
t
I

Inverse

retourne

lix
0)

public double Inverse0

if (nValue0fObiect ::
{
'I

throw new CustomException("Inpossible d'inverser


)

0", this)

return i.0 / (double)nValue0fOb.iect;


]

public class C1ass1


{

public static void Main(string[] args)


{

try
t I I prend f inverse de 0 MathClass math0bject = new MathClass("Va1eur", 0);

Console.Writeline("L'inverse de d.Value esti0J",


math0bject. Inverse 0 ) ;
1

catch (Exception e)
t

Console.Writeline("\nErreur fatale inconnue : \n{0}", e.ToStrine0);


'I

//
l
]

attend confirmation de 1'utilisateur


;

Console.tr{riteLine("Appuyez sur Entre pour trminer., .") Console.Read0;

Permettez-moi de faire une remarque : cette classe Cusl-onli:rcept-.on n'est pas si remarquable que cela. Elle stocke un message et un objet, tout comme }li'f-xcrepr j-.rn. Toutefois, au lieu de fournir de nouvelles mthodes poqr accder ces clonnes, elle remplace la proprit lle s s a ge existante qui retourne le message d'erreur contenu dans l'exception, et Ia mthode To-String O qui retourne le message plus I'indication de pile.

380

ouatrime partie : La programmarion orienre objer

Renvoyer le mme objet exception prsente un avantage et un inconvnient. Cela permet aux fonctions interrncliaires d'attraper des exceptions pour librer ou fermer des lments allous par elles, tout en permettant I'utilisateur final de I'objet exception de suivre I'indication de pile jusqu' Ia source de I'exception. Toutefois, une fonction intermdiaire ne peut pas (ou ne doit pas) ajouter des informations supplmentaires I'exception en la modifiant avant de la renvover.

Redfinir une classe d'evceptions


La classe d'exceptions suivante dfinie par I'utilisateur peut stocker cles informations supplmentaires qui ne pourraient pas l'tre dans un objet Exc ept ion conventionnel :

I Mytxception - ajoute la classe standard I une rfrence MyClass publi"c class Myfixception : Excepti.on
I I t

Exception

private
I
r

MyClasss myobject;

MyException(string sMsg, MyClass mo)


nyobjec-u = moi
]

base(sMsg)

ll
]

public My0lass My0bjectI get Ireturn myobject;))

pernet aux classes extrieures d'accder une classe d'information

Yoyez nouveau ma bibliothque de fonctions BrilliantLibrar;,. s5 fonctions savent comment remplir ces nouveaux membres de la classe MyException et aller les chercher, fournissant ainsi uniquement les informations ncessaires pour remonter la source de toute erreur connue et de quelques autres restant dcouvrir. L'inconvnient de cette approche est que seules les fonctions de Ia bibliothque BrilliantLibrar-.,r peuvent recevoir un bnfice quelconque des nouveaux membres de MyException.
Le remplacement des mthodes dj prsentes dans la classe Exception peut donner des fonctions existantes autres que I'accs BrilllantLibrary aux nouvelles donnes. Considrezlaclasse d'exceptions dfinie dans le programme Cusr omExc ep tion suivant :

/ I II
i
!

CustomException

cre une exception personnalise qui

affiche

1es inforrnations que nous voulons, srais dans un fornat plus agrable

37 8

Ouatrime partie : La programmation oriente obiet

types d'exception dfinie pour la brillante bibliothque de classe que je viens d'crire (c'est pour a que je I'appelle r j liiantli l-,rar'.'). Les fonctions qui composent Briillan+-Llbrarv envoient et attrapent des exceptions
l'{.,'Exc

eption.

Toutefois, lesfonctionsdelabibliothque:r--ll;:r:..r:.rar', peuventaussi appeler des fonctions cle la bibliothque gnrique S'.'s,r'.:r,. I-es prernire.s peuvent ne pas savoir comment traiter les exceptions de la bibliothque Si.sten, en particulier si elles sont causes par une entre errone.

q/\

f\

e,

vous ne savez pas quoi faire avec une exception, laissez-la passer poLlr qu'elle arrive la fonction appelante. Mais soyez honnte avec vous-mme : ne laissez pas passer une exception parce (lue vous n'avez simplernent pas le courage d'crire le code de traitement cl'erreur correspondant.
Si

Relancer un objet
Dans certains cas, une mthocle ne peut pas traiter entirement une erreur, mais ne veut pas laisser passer I'excepti<)n sans y mettre son grain de sel. C'est comme une fonction mathmatique qui appelle:a.i )rral ,) pour s'apercevoir qu'elle renvoie une exception. Mnte si la cause premire du problme peut tre une donne incorrecte. la fonction rnathmatique est peut-tre en mesure de fournir des indications supplmentaires sur ce qui s'est pass. Un bloc catch peut digrer partiellement I'exception envoye et ignorer le reste. Ce n'est pas ce qu'il y a de plus beau, mais a existe.

L'interception d'une exception d'erreur est une chose trs courante pour les mthodes qui allouent des lments. Par exemple, imaginez une mthode F O qui ouvre un fichier quand elle est invoque, et le referme quand elle se termine. Quelque part dans le cours de son excution, F () invoque G O . Une exception envoye de G ( I passerait directement travers r ( ) sans lui laisser la moindre chance cle fermer le fichier. Celui-ci resterait donc ouvert jusqu' ce que le programme lui-mme se termine. Une solution idale serait que F r ) contienne un bloc catch qui ferrne les fichiers ouverts. Bien entendu, F O est libre de passer I'exception au niveau suprieur aprs en avoir fait ce qu'il fallait pour ce qui la concerne.
Il y a deux manires de renvoyer une erreur. La premire consiste envoyer une deuxime exception, avec les mmes informations ou ventuellement des informations supplmentaires :

37 6

Ouatrime partie : La programmation oriente obiet

Console.
] I
{ t rrt

Writeline

(e. Message)

f?/h^^1 !' bExceptionType) ^,,11.i^',^;,r VVaU \wvv


PUUIrL

I tZ ' -

prparez-vous attraper une exception MyException

i
f? thFvnant; ^.Type)
rJ \vu.r!vyL4vrrr

catch (MvExceotion
{

me)

Console

Console.liriteline("Exception MyException attrape dans f20") . Writeline (me. Message) ;

l
l
I I tl - - n'essayez pas d'attraper des exceptions publ1c vold tJ(00o1 D.Lxceptlontype/

f4 (bExcepti.onType)
] I
{

I t+ - - envoie

des exceptions d'un type ou d'un autre


bExceptionType)

public void f4(boo1

I nous travaillons avec un MyClass mc = new MyClass0; if (bExceptionType)


{

objet

1oca1

ll une erreur se produit - I'objet est envoy avec 1'exception throw nev MyException("MyException envoye dans f40",
rac);

l
throw new Exception ( "Exception gnrique envoye dans f4 ( ) " ) ;
]

public static void Main(string[] args)


{

// envoie d'abord une exception gnrique Console.Writeline("Envoie d'abord une exception gnriQue") ; nev C1ass1 ( ) . f1 (fa1se) ; I I envoie maintenant une de nes exceptions Console.l,IriteLine ("\nEnvoie d'abord une exception spcifique")
new Class1 ( ) . f1

(true)

// attend confirmation de 1'utilisateur Console.i,iriteLj.ne("Hit Appuyez str Entre pour terminer. . ' ")

374

Ouatrime partie : La programmation oriente objet

l
catch (Exception e)
{

l
]

les utres exceptions non encore attrapes sont attrapes

ici

suivante : caT-ch (Exception).

Si SorneOr-herFunctlon | ) envoyait un objet Exception, celui-ci ne serait pas attrap par I'instruction catch (MyExceprion) car une Exception n'est pas de type l"1''rException. Il serait attrap par I'instruction catch

Toute classe qui hrite de MyExceprion EST_uNE tqyException:


class MySpecialException : MyBxception
{

instructions quelconques . ..

Si elle en a la possibilit, I'instruction catch lul.,,Exception attrapera objet I'lyS p e c i alExc epr i on envoy.

tout

Af\ =(t

gnrale

Faites toujours se succder les instructions catch de la plus spcifique Ia plus gnrale. Ne placez jamais en premier I'instruction catch Ia plus
:

public void
t

SomeFunction0

try
{

SomeOtherFunction{); l
catch (Exception
t me)

// tous
l

1es objets MyExcepti.on sont attraps

ici

catch(MyException e)
{

I I

I I

auctne exception ne parvient jamais jusqu'ici paree qu'e1le est attrape par un instruction catch plus gnrare

l
]

Dans cet exemple, I'instruction catch la plus gnrale coupe I'herbe sous le pied de Ia suivante en interceptant tous les envois.

37 2

Ouatrime partie : La programmation oriente obiet


Cette classe C,l-*:rnEx.-ept,irrir est faite sur mesure pour signaler une erreur au logiciel qui traite avec la tristement clbre i"l.,.C i as s. Cette sous-classe d'Exceprion met de ct la mme chlne que l'original, mais dispose en plus cle la possibilit cle stocker dans l'exception la rfrence au fautif.

L'exemple suivant attrape la classe ses informations sur I'l', illass


;

(lrs*:rr:l,li!t'-,i''

1,

et nlet en utilisation

public class Classl


i publi.c void SomeFunctiono
t

try
{

I
I

I I

oprations pra1ab1es 1a fonction exemple


;

SomeOtherFunction ( )

autres oPrations.
me)

l
catch (MyExcePtion
{

// vous avez toujours accs

aux nthodes d'Exception


nTthodes

strj.ng s = me.ToString0; I I nais vous vez aussi accs toutes 1es proprits et // de votre pr0pre classe d'exceptions
MyClass mo = me.MYCustom0bject; I I par eremple, denandez 1'objet MyClass de

s'affi-cher

lui-mme

string s = mo'GetDescriPtionO;
]

public void Sone0therFunction(


{

I I

I I

MyClass myobject

cratian de mYobject = ner^r MyClass0; signale une erreur concernant myobject

throw nev MyBxception("Erreur dans 1'objet de MyClass", myobject); reste de la fonction II . l

l
Dans ce fragment de code, SoineFunction

) invoque Sc,neCthelFuncli tlll () de I'intrieur d'un bloc rr-v. SorneOtherFunction O cre et utilise un objet ( rnyobi ect. Quelqu" puit clans Sr-rneOthe r-Funct ron ) , une fonction de vrificapour signaler qu'une condition d'erreur se prpare envoyer une exception simple Exi-e'i:i01, une que crer de tion d'erreur vient de se produire. Plutt
r

se sert de la toute nouvelle classe l'l'u.E:;cepti:'tr, pour envoyer non seulement un message cl'erreur, mais aussi I'objet 11'\rrb -i ec t fautif
SomeF

unction

( ,r

37 0

Ouatrime partie : La programmation oriente objet

1t$\a. S/^1 \ =(!,\7 f Y'/

Cotnme l'Iairi ir est le point cle clpart clu programme, il est bon de toujours en placer le contetru clans un bloc t r ,-. Toute exception qui ne sera pas "attrape" ailleurs renrontera finalernent jr-rsqu' I'lain ( r . C'est clonc votre

dernire opportunit cle rcuprer Llne erreur avant qu'elle aboutisse Windows, dont le rnessage d'erreur sera beaucoup plus difficile interprter.

I attrape I'objet E:,cip*r.ic,n et utilise sa mthode Jr-.!r-:rir i', pollr afficher sou.s fonne d'une simple chalne la rnajeure partie des inforrnatiotts sur I'erreur contenues dans I'obiet exi: eDlir,r.
.($-_.-

Le bloc catch situ la fin cle Iii:1r i

Ilorf Y

LapropritE>lile.1l1-t-.l..'l],':]:,.:1;l.retOurnleunSouS-enSemblepluslisible, f J-^t mais moins descriptif cles informations sur I'erreur.


.

Cette version de la fonction i'ri:r-i-,r j a i i' contient la mme vrification pour un argument ngatif que la prcdente. Si I'argument est ngatif, Fac i-orf a L o met en forme un message d'erreur qui clcrit le problme, incluant la valeur incrimine. Facr:cr'1ai l.,r regroupe ensuite ces informations dans un objet Exception nouvellement cr, qu'elle envoie la fonction appelante. La sortie de ce programme apparalt comme suit

('ai un peu arrang les


:

messages d'erreur pour les rendre plus lisibles)

i = 6, factorielle : 720 i = 5, factorielle = 120


1=

4. factorieTTe = 24

i = 3, factorielle = 6 i = 2, factorielle = 2 i = 1, factorielle : i * 0, factorielle = 0


1

Erreur fatale

System.Exception: Argument ngatif

illicite pass Factorial -1 at Factorial (Int32 nValue) in c: \cifprograrn\Factorial\classl . cs;1ine 23 t FactorialException.Classl.Main(String [] args) in c: \c/fprogram\Factorial\ class1 . cs : line 52
sur Entre pour terminer...

Appuyez

Les premires lignes affichent les vritables factorielles des nombres 6


0. La factorielle de -1 gnre un message commenant par ErT. eur f a-

ta1e, ce qui est susceptible d'attirer I'attention de I'utilisateur.


La premire ligne du message d'erreur a t mise en forme dans la fonction Factcrial O elle-meme. Cette ligne dcrit la nature du problme, en

indiquant la valeur incrimine

-1.

368

Ouatrime partie:La programmation oriente objet

throv nev Exception("Description de 1'erreur")


I

suite de 1a fonction

La fonction SoneFunction O contient un bloc de code identifi par le motcl try. Toute fonction appele dans ce bloc, ou toute fonction qui I'ap-

pelle, est considre comme faisant partie du bloc

try.

Un bloc Lr.,r est immdiatement suivi par le mot-cl catcl-r, lequel est suivi par un bloc auquel le contrle est pass si une erreur se produit en un endroit quelconque dans le bloc try. L'argument pass au bloc catch est un objet de la classe Exception ou d'une sous-classe de celle-ci. un endroit quelconque dans les profoncleurs cle SomeOtherFunction O, une erreur se produit. Toujours prte, la fonction signale une erreur I'excution en envoyant (throv;) un objet Frception u premier bloc pour que celui-ci I'attrape (catch).

Puis-je atuir un exemple .)


FactorlalException suivant met en vidence les lments cls du mcanisme des exceptions :
Le programme

_-"-ffi

// FactorialException - cre une fonction factorielle qui indique FactorialQ 1es arguaents illicites I en utilisant un objet Exception It
I

using
t

System;

namespace

FactorialException

i/ ll

MyMathfunctions

collection de fonctions

mathmatiques

de ma cration (pas encore grand-chose montrer)


l'lyMathFunctions

public class
i

// lactorial II
t

retourne fournie

la factorielle

d'une valeur

public static double Factorial(int

nValue)

i/ interdit les nombres ngatifs if (nValue ( 0)


{

//

signale un argunent ngatif


"Argument ngatif

string s = String.Forrnat(

illicite

pass

Factorial {0J",

366

0uatrime partie: La programmation oriente obiet

d'erreur que la fonction appelante ne teste pas. Bien str, en tant que programmeur en chef, je peux me laisser aller profrer des menaces. Je me souviens d'avoir lu toutes sortes de liwes de programmation regorgeant de menaces de bannissement du syndicat des programmeurs pour ceux qui ne s'occupent pas des codes d'erreur, mais tout bon programmeur FORTRAN sait bien qu'un Iangage ne peut obliger personne vrifier quoi que ce soit, et que, trs souvent, ces vrifications ne sont pas faites. Souvent, mme si je vrifie I'indication d'erreur retourne par Fa: r .r i '.I ou par toute autre fonction, la fonction appelante ne peut rien faire d'autre que de signaler I'erreur. Le problme est que la fonction appelante est oblige de tester toutes les erreurs possibles retournes par toutes les fonctions qu'elle appelle. Bien vite, le code commence avoir cette allure l
r

I appelTe SomeFunction, 1it 1'erreur retourne, 1a traite // et retourne


I

errRtn = someFuncO; if (errRtn == SE*ERR0R1)


{

Console.1,Iriteline("Erreur de type 1 sur appel someFunc0");

return
l

MY_ERROR_1;

if (errRtn == SF-ERROR2)
{

Console.l'IriteLine("Erreur de type 2 sur appel someFunc0");

return
l
I

My_ERROR,2;

errRtn : some0therFunc0 if (errRtn == S0F*ERRORI)


{

appelle SomeOtherFunetions,
;

1it 1'erreur,

retourne, et ainsi de suite

Console.l,lriteLine("Erreur de type

sur appel someFunc0");

return
l

MY*ERROR-3;

if
{

(errRtn == SOF-ERROR2)

Console.liriteline("Erreur de type return MY_ERROR_4;


)

sur appel someFunc0");

Ce mcanisme prsente plusieurs inconvnients

t/ t/

Il est trs rptitif. Il oblige le programmeur inventer de nombreuses indications d'erreur et en maltriser I'emploi.

36 4

Ouatrime partie : La programmation oriente obiet

if
t

(dFactorial =: l4vMathl'unctions
\eL ev ev ++ --J

NON*INTEGER*VALUE)

Console.

I^Iriteline

("Factorial0 a reu un nonbre


break;
]

non

entier");

I af.f.iche le rsultat chaque passage Console.I^IriteLine("i = {0}, factorielle = {1}",


I

i, MyMathFunctions.Factorial(i)
1
J

//

Console.l^lriteLine("Appuyez Console.Read{);

attend confirnation de 1'utilisateur sur Entre pour terniner.. . ")

de tests. Le parce qu'il (0 est accept passe ngative est premier regarde si la valeur

Faciorial O comnence maintenant par effectuer une srie

donne un rsultat raisonnatlle). Si oui, la fonction retourne immdiatement une indication d'erreur. Si non, la valeur de I'argument est compare sa version entire : si elles sont gales, c'est que la partie cicimale de I'argument est nulle.
t:{ain ( ) teste le rsultat retourn par Faci-cri al 0 , la recherche de I'indication ventuelle d'une erreur. Toutefois, des valeurs Comme -1 et-2 n'ont gure de significat,ion pour un programmeur qui effectue la maintenance de

son code ou qui I'utilise. Pour rendre un peu pius parlante I'erreur retourne, la classe t4yMarhFirncl, ions clfinit deux constantes entires. La constante NEGATIVE_|JUi'{BER reoit la valeur -1, et NOI'I IhITEGER VALUE reoit la valeur -2. Cela ne change rien, mais I'utilisation des constantes rend le programme beaucoup plus lisible, en particulier la fonction appelante Maln O.

Dans la convention sur les noms Southern Naming Convention, les noms des constantes sont entirement en majuscules, les mots tant spars par un tiret de soulignement. Certains programmeurs, plus libraux, refusent de faire allgeance, mais Ce n'est pas la Convention qui a des chances de changer.

Les constantes cclntenant les valeurs d'erreur sont accessibles par la classe, Comme dans l"l;,:l"1rlhCl as s .IIEGATIVE i'lUl'iBER. Une variable de type cons*, st automatiquement statique, ce qui en fait une proprit de classe partage par tous les objets' La fonction Facr i r- j a t ( ) signale nraintenant qu'une valeur ngative lui a t passe conlnle argument. Elle le signale Main O Qui se termine alors en affichant un message d'erreur beaucoup plus intelligible :

362

Ouatrime partie:La programmation oriente objet

ngatif. Ensuite, remarquez que les valeurs ngatives ne crclissent pas de la mme manire que les valeurs positives. Manifestement, il y a quelque chose qui cloche.

=(dg,

^"ffi

Les rsultats incorrects retourns ici sont assez subtils par rapport ce qui aurait pu se produire. Si la boucle de Factc,r'iai O avait t crite sous la forme do i . . . I whiie (dValue l: 0), le programme se serait plant en passant un nombre ngatif. Bien str, je n'aurais jamais crit une condition comme,"hile (dValue !: 0), car les erreurs dues I'approximation auraient pu faire chouer de toute faon la comparaison avec zro.

Retourner une ndication d'erreur


Bien qu'elle soit assez simple, il rnanque la fonction Fa - - r,a - '. une importante vrification d'erreur : la factorielle d'un nombre ngatif n'est pas dfinie, pas plus que la factorielle d'un nombre non entier. La fonction Facl-c:' :il i) doit donc comporter un test pour vrifier que ces conditions sont rernplies.

Mais que fera la fonction Factorial O avec une condition d'erreur si la chose se produit ? Elle connaltra I'existence du problme, mais sans savoir comment il s'est produit. Le mieux que r'ar:roria,1 ,I puisse faire est de signaler les erreurs la fonction qui I'appelle (peut-tre celle-ci sait-elle d'o vient le problme).
La manire classique d'indiquer une erreur dans une fonction consiste retourner une certaine valeur que la fonction ne peut pas autrement retourner. Par exemple, la valeur d'une factorielle ne peut pas tre ngative. La fonction Factorial O peut donc retourner -1 si un nombre ngatif lui est pass, -2 pour un nombre non entier, et ainsi de suite. La fonction appelante peut alors examiner la valeur retourne : si cette valeur est ngative, elle sait qu'une erreur s'est produite, et la valeur exacte indique la nature de I'erreur.

Le programme ncessaires :

FactorialErrorReturn suivant contient les ajustements

// FactorialErrorReturn - cre une fonction factorielle qui II retourne une indication d'erreur ouand quelque chose ne va pas II
.. ^.: * ^ uf]r

C.,^+ ^- , Jy b Lsnl,

nalnespace FactorialErrorReturn
{

li II

Myl"lathFunctions

collection de fonctions

mathrnatiques

de ma cration (pas encore grand-chose uiontrer)

36 0

Ouatrime partie : La programmation oriente

ob

jet

Traiter une erreur l'ancienne mode : la retourner


Ne pas signaler une erreur I'excution n'est jamais une bonne ide. Je

dis bien jamais.' si vous n'avez pas I'intention de dboguer vos programmes et si vous ne vous souciez pas qu'ils marchent, alors seulement c'est peut-tre une bclnne ide.
Le programnle !'ar:'-or i alErrcr suivant montre ce qui arrive quand les erreurs ne sont pas cltectes. Ce programme calcule et affiche la fonction factorielle pour de nombreuses valeurs, dont certaines sont tout juste licites.

La factorielle du rtornbre N est gale N * (N-1) . (N-2) 1. Par exemple, la factorielle cle 4 est 4* 3n 2* l, soit 24.La fonction factorielle n'est valide que pour les nombres entiers naturels (positifs).

I I II
I
I

Factorial}JithError

crer et

utiliser

une fonction
aucune

factorielle qui ne contj.ent vrification

using
t I

System;

nnespace FactorialWithError

// I
t

Uyttathfunctions

collection de fonctions

nathmatiques

de ma cration {pas encore grand-chose montrer)


MyMathFunctions

public class

i/ I
t

Factorial

retourne 1a faetorielle d'une valeur fournie

public static double Factorial(double 0varuel

/l conrnence par donner 1a valeur 1 un ttccumulateurtt double dFactorial = 1.0: I I f.ait une boucle partir de nValue en descendant de 1 chaque fois
I I

I I

norr

mrrl

ti nlier

'accumulateur

par 1a valeur obtenue

do
t

dFactorial *= dValue; dValue -= 1.0;

J while(dValue ) 1); // retourne la valeur stocke dans 1'accumulateur

return dFactorial;
l
]
t t^ddI rwr-d.55.1. \-r-d.t'U ^t^dd ^rrhli^ PUUr.r-U {

public static void MainistringIJ args)

4t6

Cinquime partie : Programmer pour Windows avec Visual Studio

components.Dispose ( )
J

l
haee Di(b^<( --urDyvrrri \ ,lian^ain. ). ,/
'

liregion Windovs Form Designer generated code lll kunnary) /// Required method for Designer support - do not nodify I I I rhe contents of thi.s method r+ith the code editor. lll llsunnary)

private void InitializeComponent0


{

//

Formt
;

anr..OurorcaleBasesize + ne!, Systern.Drawing.Size(5, 13) this.Nane = t'Fornl";


l /lendregion

this. ClientSize =

new Systen. Drawing. Size

( 292

273)

this.Text = "Sinple ditor";

/// III lll


I

(summary)

ftte main entry point for the application.


(lsunnary)
]

stlthread

static void
r

Main0
.

Annl i eeti on

Rrrn

(new Forml

))

doit commencer par static Main O, qui se trouve ici tout en bas du listing. Voil ce qui nourrit ma conviction que c'est ici qu'il faut commencer. La seule instruction que contient Main O cre un objet Fornl O et le passe une mthode Application.Run O. Je ne suis pas str de ce que fait Run O, mais je souponne fortement que la classe Forml correspond la fentre Forml que j'ai vue dans le Concepteur.
Je sais que le programme

^tK
'(dE,)

O lance I'objet Form sur son propre thread d'excution. Le thread initial s'arrte aussitt que le nouveau Forml est cr. Le thread Forml se poursuit jusqu' ce qu'il soit intentionnellement arrt. Le programme SimpleEditor lui-mme poursuit son excution aussi longtemps que des threads dfinis par I'utilisateur sont actifs.
En fait,

Application.

Run

Le constructeur de Forml invoque une mthode

Initiali

zeComonent

).

Tout code d'initialisation du programmeur doit tre plac aprs cet appel (tout au moins, c'est ce que dit le commentaire).

Ghapitre 20

Les dix plus importantes diff rences entre C#et G++


Dans ce chapitre :
Pas de donnes ni de fonctions globales.

Tous les objets sont allous partir du tas.


Les variables de type pointeur ne sont pas autorises.

Vendez-moi quelques-unes de vos proprits. Je n'inclurai plus jamais un fichier.


Ne construisez pas, initialisez.

Dfinis soigneusement tes types de variable, mon enfant.


Pas d'hritage multiple.

Prvoir une bonne interface.


Le systme des types unifis.

e langage C# est assez largement bas sur C++. Cela n'a rien d'tonnant, puisque Microsoft avait dj fait Visual C++, qui a t le langage de

que temps que C++ voue son ge.

programmation le plus rpandu pour I'environnement Windows. Tous les meilleurs accros de la programmation s'en servaient. Mais a fait dj quel-

C# n'est pas une couche de peinture sur une carcasse rouille. Il comporte de nombreuses amliorations, la fois par I'ajout de nouvelles fonctionnalits et par le remplacement de fonctionnalits dj satisfaisantes par de meilleures. Voici les dix meilleures amliorations de C# par rapport C++.

4|4

Ginquime partie : Programmer pour Windows avec Visual Studio

=($9,
4.

-K

Un formulaire est une fentre contenant une barre de titre, et optionnellement des barres de dfilement. Dans la terminologie de C#, une fentre n'est rien d'autre qu'un cadre rectangulaire dans lequel vous pouvez placer des images ou du texte. Une fentre n'a pas ncessairement des menus ou des tiquettes, ni mme ces petits boutons Fermer, Rduire et Restaurer.

Gnrez le programme que Windows vient de crer sur la base du modle. Vous pouvez me traiter de paranoiaque, mais je veux tre certain que toutes les erreurs qui pourront apparaltre par la suite seront rellement de mon fait et ne viendront pas de Visual Studio. Sans aucun doute, la solution va se gnrer sans encombre ce stade. L'excution de ce programme ne rvle rien d'autre qu'un formulaire vierge, dot de l'tiquette Forml. Il suffit de cliquer sur le bouton Fermer pour arrter le programme.
Le volet qui occupe la partie droite de I'affichage est la fentre

Proprits. a ne saute peut-tre pas aux yeux, mais son contenu est en relation directe avec le formulaire qui est dans la partie gauche de I'affichage. Par exemple, vous pouvez voir que la proprit Text est Form1. Vous pouvez la modifier pour vous rendre compte de I'effet produit.

5.

Slectionnez la proprit Text, et donnez-lui la valeur Simple Editor.

L'tiquette Forml contenue dans la barre de titre du formulaire devient Simple Editor.

6.

Gnrez nouveau I'application, et excutez-la.


Le nom du formulaire a chang, comme le montre la Figure 17.3.

W
Figure 17.3 Changer la proprit 1'-^xt 0U
:

formulaire
change le nom qui a ppa rat dans sa barre de titre.

Chapitre 19:Les dix erreurs de gnration les plus courantes...

47t

t/ t/

Votre chien a mang votre manuscrit. Plus simplement, vous avez oubli cette mthode ou vous n'en connaissiez pas I'existence. Soyez plus attentif la prochaine fois.
Vous avez fait une faute de frappe dans le nom de la mthode ou vous lui avez pass de mauvais arguments.
:

Examinez I'exemple suivant Interface


{
Me

void aFunction (f1oat)


l

public class MyClass :


t

l'Ie

public void aFunction{double


{

d)

La classe l"lyC1ass n'implmente pas la fonction aliunction (f loat r de I'interface. La fonction aFunction (double) ne compte pas, parce que les arguments ne correspondent pas.

Sur le mtier remettez votre ouvrage, et passez en revue chacune de vos mthodes jusqu' ce que toutes les mthodes de I'interface soient correc-

tement implmentes.

=(&"*.

1e$\a" \ ^v7q

Ne pas implmenter compltement une interface est essentiellement la mme chose que d'essayer de crer une classe concrte partir cl'une

classe abstraite sans redfinir toutes les mthodes abstraites.

'methodNatne' : tous les chemns de code ne retournent pas ncessairement une hleur
Par ce message. C# vous dit que votre mthode a t dclare non-void et que un ou plusieurs chemins d'excution ne retournent rien. Cela peut se produire de I'une des deux manires suivantes :

t/ t/

Vous avez une instruction

if

avec un

return sans valeur spcifie.

Plus vraisemblablement, vous avez calcul une valeur et vous ne I'avez jamais retourne.

4|2

Cinquime partie : Programmer pour Windows avec Visual Studio

Le menu dition a besoin des trois grandes options d'dition : Couper, Copier et Coller. D'autre part, tous les diteurs comprennent les raccourcis clavier de ces trois options : Ctrl+X, Ctrl*C, et Ctrl+V, respectivement.

SimpleEditor aura galement besoin d'un menu Format, comportant les options Gras et Italique pour mettre en forme le texte.
Fournir une aide vritable est une tche difficile - beaucoup trop complique pour un diteur simple comme SimpleEditor. Le menu d'aide de cette application devra se contenter du minimum absolu : I'option propos de. Dernire exigence : il nous faut un moyen de contrler la taille de police. Voil une chose qui laisse la place un peu de fantaisie. En plus d'une simple fentre dans laquelle I'utilisateur peut entrer la taille de police souhaite, SlmpleEditor y ajoutera une sorte de barre munie d'un index que I'on peut faire glisser, que nous appellerons TrackBar. Pour obtenir 8 points, faites glisser I'index I'extrmit gauche. Faites-le glisser I'extrmit droite, et vous obtenez 24 points. (J'ai une autre raison de procder ainsi : je veux vous montrer comment relier deux objets d'l/O de manire qu'un changement clans I'un soit rpercut dans I'autre.)

Ila

soluton

Avec les paramtres que j'ai dcrits dans la section prcdente, je suis arriv la solution montre par la Figure 17.1. Vos propres rsultats peuvent tre diffrents selon vos gotts personnels.

wf#ij

Fichier EditEn FDrmt

Figure 17.1 Ma solution du problme SimpleEdi:or.


:

,d-l-

Talle,3e police

{*
24

Dessner la solution
Comme vous pouvez I'imaginer, j'ai d passer par de nombreuses tapes pour arriver en partant de zro l'uvre d'art montre par la Figure 17.1.

Ghapitre

l9:Les dix erreurs de gnration les plus courantes...

469

cloche entre Visual Studio et le rpertoire du programme. Fermez la solution, quittez Visual Studio, redmarrez, puis ouvrez nouveau la solution. Si a ne marche pas, je suis sincrement dsol.

Le mot-cl neut est re(us sur 'subclassName. nethodNaffil| car il masque le membre hrte
'

as e c I as s N am e. m eth o dN atn

e'

Avec ce message, C# vous dit que vous avez surcharg une mthode dans une classe de base sans la redfinir par une mthode qui la cache (voyez le Chapitre 13 pour en savoir plus ce sujet). Regardez I'exemple suivant : public class
{ BaseClass

public void Functiono


t

l
nublic class Sub0lass:
{

BaseClass

public void Functiono


{

l
l

public class
{

MyClass

public void Test0


t

SubClass sb = new SubClass 0

sb.Function0;
]
'l
J

La fonction Test O ne peut pas accder la mthode BaseClass . Function partir de I'objet sb d'une sous<lasse, car elle est redfinie par SubClass.

I'unction O. Vous aiez I'intention de faire I'une des choses suivantes

t/

Vous vouliez redfinir la mthode de la classe de base. Dans ce cas, ajoutez le mot-cl new dans la dfinition de SubCias s, comme dans I'exemple suivant :
public class Sub0lass
{

BaseClass

4l 0

Cinquime partie : Programmer pour Windows avec Visual Studio

Quel est le problme

.)

Il rn'a fallu une longue et clifficile rflexion (au rnoins un quart cl'heure) pour imaginer un problme qui mette en lumire la puissance de C# sans me faire prendre du poids. Le voici : crer un cliteur simple que nous appellerons Sinp-eEd ir ,r. Il aura les caractristiques suivantes:

t/ t/ t/ ,/ ,/

L'utilisateur peut entrer et effacer du texte (sinon. ce ne serait pas vraiment un diteur). L'utilisateur peut couper et coller du texte, non .serulenrent dans SlnnLeEciit-c,r, mais aussi entre 5.trr ri,r t:t d'autres applicatior-rs, par exemple Word.
I rlrLl a

Sinir,eEclit-c-r supporte les polices en gra.s, en italiqlle ou les deux. L'utili.sateur peut slectionner une taille cle police cle 8 24 points. Ces limites sont arbitraires, mais il s'agit ici de ne pas aller trop loin
en nombre de points.

SimpleEr.litcr ne doit pas vous permettre de quitter sans vous avoir demancl poliment d'enregistrer le fichier que vous venez de moclifier (mais vous restez libre cle quitter san.s enregistrer si c'est bien ce que vous voulez).

Exposer le problme
Chaque fois que vous tes devant un problme rsoudre, vous devez commencer par vous mettre devant le tableau noir et rflchir srieusement aux obstacles franchir. Dans le cas cl'une apltlication Windows, cette tche se divise en trois tapes :

l.

Dcrivez le problme en dtail.


Ces dtails sont les spcifications auxcluelles cloit se conformer I'application. Au cours de Ia programmation, vous pourrez tre

tent d'ajouter une fonctionnalit ici ou l. Rsistez. Cette maladie s'appelle fonctionnalite. Tout en avanant, notezles amliorations possibles pour une version future, rnais I'ajout de fonctionnalits en cours de route fait courir le risque de crer Llne application qui finit par tre tout fait autre chose que ce qu'elle tait cense tre au dpart.

Chapitre 19: Les dix erreurs de gnration les plus courantes...

t+67

Par dfaut, un membre d'une classe est';l'l-,,.ate, et une classe est internal. Aussi, nPrivateMember est toujours priv dans I'exemple suivant : class
t MyClass

public void
t

SomeFunction0

YourClass uc = netr YourClassu; I I ceci ne fonctionne pas correcremenr parce que MyClass I I ne peut pas accder au nernbre priv uc,nPrivateMember = 1:

l
]

public class
{

YourClass

int
l

nPrivateMenber

: 0; //

ce membre est toujours priv

En outre, mme si SoineF,lr:.-ti.-iril est clclare pribirc, on ne peut pas y accder partir d'une classe d'un autre module, car i'1','Ciass elle-mrne est interne.

La morale de I'histotre est la suivante : "Spcifiez toujours le niveau de protection de vos classes et de leurs membres." Et nous avons un lemme qui dit : "Ne dclarez pas de membres publics dans une classe qui ellemme est interne. a n'apporte que de la confusion."

Utilisaton d'une t/ariable locale non assgne 'n'


Comme il le dit si clairement, ce message indique que vous avez dclar une variable mais que vous ne lui avez pas donn de valeur initiale. C'est en gnral un simple oubli, mais a peut aussi se produire lorsque vous voulez vraiment passer une variable comme argument our une fonction : public class
t

MyClass

nrrhlic yqurru
{
Irt L

rrn'i qrr! L*vrr Y vv d SomrrE'rrnntinn(l uwu! \ /

tl t

I ceci fonctionne parce que C# ne retourne une valeur // i1 ne psse pas une valeur dans la fonction
I

que dans

Some0therFunction

(out n) ;

public void Some0therFunction(out


t

int

n)

l)ams *ette {raytie

o.

onrllrerrrlle ('# r:si:-rrre clrose, apllrettclrtt ai tir:tire une allplicatirlrr \t'iIirirx'1,':i r:orrtilltr: a.,/ec lotrs ses assentblages et ses dc<;r;r.lion$ l.rilrr eri irlirt'o en e.st tute atttre. I{iert que l)our le plai.sir. lir rirrrjr iir''iirt par.tie 1"sr-r$ qtricle pras p;rs datts I'utilisation cle C# a'u'cr I'irrTi'riar:r: \'isiuil Stuclio ;rfin cle cr(:er rrne;lpplication Wincknvs rtili rrr.: soit tr:;rs Triviale". Vous serez fier cir r rd':sultat. mme si vr,ls :nfani.s rr';r,uilellent pas leurs copr;lirts pour le voir.

Chapitre 19:Les dix erreurs de gnration les plus courantes...

465

float fResult = 2 * f;
return fResult;
]

ceci fonctionne trs bien

La constante 2 est cle type in*-. Un int multipli par un f -:ar clonne un f 1oat, qui peut tre stock dans la variable fResu;t-, de type f l,,r:rf . Le message d'erreur de conversion implicite peut aussi apparaltre lorsque vous effectuez des oprations sur des types "rlon naturels". Par exernple, vous ne pouvez pas additionner deux variables de type char, mais C# peut convertir pour vous une variable de type char en une valeur de type i:ri_ lorsque c'est ncessaire pour raliser I'opration. Ce qui conduit ceci : class
i
MyClass

static public void


t

SomeFunctiono

char c1 = ta'; char c2 = 'b'; // ie ne sai s mme ns c nrrp npr.i oourrait vouloir dire ; c'est illicite
ll

char c3 = c1 * c2;
]

neis nc n1lr 1.,*-

*a ra]-son
1

que vous croyez

Aclditionner deux caractres n'a en soi aucun sens, mais C# essair: quancl mme. Comme I'addition n'est pas dfinie pour le type rlra r, il convertit c 1 et c 2 en valeurs j nt vc lesquelles il effectue I'addition. Malheureusement, la valeur lrit Qui en rsulte ne peut pas tre convertie nouveau en type char sans intervention extrieure.
La plupart des conversions, mais pas toutes, se passent trs bien avec un cast explicite. La fonction suivante fonctionne sans se plaindre :
class
{

MyClass

static public float FloatTimes2(f1oat f)


t
(

float fResult = (float) (2.0 - f);


return fResult:
j

cecz fonctionne

trs bien avec 1e cast explicite

Chapitre 19:Les dix erreurs de gnration les plus courantes...

463

cf oii

n nlhl

rrni rl MrrFrrnni'i nn f StUdent \vguuurrL

S)
e/

Console.Writeline("Nom de 1'tudiant
Console.WriteLine("Nunro

l
)

: r * s.sStudentName) d'identification de 1'tudiant = " *


;

s.nTd);

Le problme est ici que I'l7F:rnc tion i ) fait rfrence un membre donne nlct au lieu du vritable membre donne nrD. Vous voyez la ressenlblance, mais C# ne Ia voit pas. Le programmeur a crit nId, mais il n'y a pas de rrld,

et puis c'est tout. Un peu moins populaire, mais galement dans le Top 10, vous avez aussi la possibilit que la variable ait t dclare dans une porte diffrente :
class
t
I

MyClass

static public void Averagelnputo


I

int
{

nCount

0;

while (true)
I

I tit

un nonbre

string s = Console.Readline0l

int n = Int32.Parse(s); rt quitte si 1'utilisateur // if(n(0)


{

entre un nombre ngatif

break;

l
I

nSun
1
J

I ajovte la valeur entre f= n;

nCount#;

// affiche naintenant 1es rsultats total est r' * nSuur) ; tonsole.Writelinet"ta moyenne est " * nSun / nCount); I I ceci produit un rxcssage d'erreur de gnration Console.Writeline("La valeur finale tait " + s);
Console.Wri.teline("Le
t
)

La dernire liqne de cette fonction est incorrecte. Le problme est qu'une variable est limite la porte dans laquelle elle est dfinie. La variable s n'est pas dfinie en dehors de la boucle whrle ().

40 4

Ouatrime partie : La programmation oriente objet

que le programme donne la sortie attendue. Une fois que le programme lu le fichier, il se termine. Si I'utilisateur veut lire un autre fichier, il lui suffit d'excuter nouveau le programme.

Le programme commence par une boucle-;h rle, comme son cousin File\^rrite. Dans cette boucle, il va chercher le nom de fictrier entr par I'utilisateur. Si le nom de fichier est vide, le programme envoie un nlessage d'erreur : \Ious ave-z cntr un iLom de f ici,i er -' l de. Dans le cas cclntraire. le nom de fichier est utilis pour ouwir un objet Fi-Le-S:i e a:. en mode de lecture. L'appel File . Open ( I est ici le rnme que celui utilis clans F i i:::r rte

/
t/

Le premier argument est le nom clu fichier.


Le deuxime argument est le modle du fichier. Le mode

Filel{ode.0pen dit : "Ouvrir le fichier s'il existe, sinon envoyer une exception." L'autre possibilit est Cpenlile-,,,, qui cre un fichier de longueur nulle si celui-ci n'existe pas dj. Personnellement, je n'ai jamais rencontr le besoin de ce mode (qui veut lire r-rn fichier vide ?), mais chacun mne sa barque comme il l'entencl. t/
Le dernier argument indique que je veux lire

partir de ce
F.e'acj,'ir

Fi-ieStream. Les autres solutions sont ,,.,'r.ie et

it...

L'objet FileStream f s rsultant est alors insr dans un objet ST- rcair,lir:ac1er s r qui offre des mthodes pratiques pour accder au fichier texte. Toute cette section d'ouverture de fichier est enchsse dans un bloc
.. r-,,',

lui-mme enchss dans une boucle v;hi1e, insre dans une nigme. Ce bloc try est strictement rserv I'ouverture de fichier. Si une erreur se produit pendant le processus d'ouverture, I'exception est attrape, un message d'erreur est affich, et le programme reprend au dbut de la boucle pour demander nouveau un nom de fichier I'utilisateur. Toutefois, si le processus aboutit un objet nouveau-n St reamReader en bonne sant, la commande break fait sortir de la logique d'ouverture de fichier et fait passer le chemin d'excution du programme la section de lecture.

.r\!G ./ t(?, Y

FileRead et Fiiei^i

rite

reprsentent deux manires diffrentes de traiter

Hdesexceptionsdefichier.VouspouVeZinsrertout|eprogrammecle

traitement de fichier dans un mme bloc t i'\,, comme dans Fi 1er'rr: j te, ou bien vous pouvez donner son propre bloc i- r-,. la section d'ouverture de fichier. Cette dernire solution est gnralement la plus facile, et elle permet de gnrer un message d'erreur plus prcis.
Une fois le processus d'ouverture de fichier termin, le programme FileRead lit une ligne de texte dans le fichier en utilisant I'appel

Chapitre 19

Les dixerreursi de gnration

les plus courantes

(et commentyremdier)
Dans ce chapitre :
'className' ne contient pas cle dfinitiol) pour'memberName'.

Impossible de convertir implicitement le type'x' en 'y'.


'className.memberName' est inaccessible en raison de son niveau de protection.

Utilisation d'une variable locale non assigne 'n'. Le fichier'programName.exe'ne peut pas tre copi dans le rpertoire d'excution. Le processus ne peut pas...
Le mot-cl new est requis sur'subclassName.metl"rodNarne', car il masque le

membre hrit'baseclassName.methodNarne'. 'subclassName' : ne peut pas hriter de la classe scelle 'baseclassName'.


'className' n'implmente pas le rrembre d'interface 'methodName'.

'methodName' : tous les chemins de code ne retournent pas ncessairement une valeur, ) attendue.

e faon trs scolaire, C# fait de son mieux pour trouver des erreurs

dans votre code. Il se iette sur les fautes de svntaxe comme un fauve sur sa proie. En dehors des erreurs vraiment btes, comme essayer de compiler votre liste de commissions, on a I'impression d'entendre toujours le mme cri de protestation, inlassablement.
Ce chapitre prsente dix messages d'erreur cle gnration que I'on rencontre souvent, mais quelques avertissements s'imposent. Tout d'abord, C# est

402

Ouatrime partie: La prCIuramrnation oriente ohjet

Amliorez tutre cnmprhension et tutre (itesse de {ectwrs eec S t r. e ii m ii r: a,l e r


e.st tr.s aqr';rl>Jr: qlit'rire slrr un fichier. nrais c'est plrrtt inutile si votrs ne pouvez !);rs lirt, lr: fir-.irier pitr la suite. i,e progran)nre ,'i 1.,'i,.rrr,: suivartt affic:hr.. sur l;r r()n,c(ile i'r <1r-r'il lit clans ie fichier, ('e pr()qramrrre lit un

Il

fichielr te-.xte C{,}!lnrt't'c[tri {ll-re (re !

]-' : ''-.:

//

FileRead
Q"-+^-' uje!rr uy;!r,a!,

1it un fichier texte et 1'crit


srrr 1e console

,,^.i*^ uarr5 urrr

irarrg)ydL:
{

f rf Ei\cou

public class Classl


i
rrrhl ir
{

ct:-ie

rrni.i ]nai. /c.ri.vvatl !larrr \Lr4l!

fl args) LJ

// il

nous

faut un objet

pour

lire

1e

fichier

StreamReaoer sr; strins - *- -" sFiieName

= ""; // continue essayer de lire un nom de fichier jusqu' ce qu'il // trcuve un (i.a seule manire de quitter po:r I'utilisateur esr I I d'arrter 1e prograinne en appuyant sur Ctrl + C)
{

en

while (true)

try
{

ll lit le non du fichier d'entre Console.Wrj.te("Entrez 1e nom d'un


sFileName

fichier texte lire :");

Console. Readline

ii 1'utilisateur n'a rien entr ; envoie une erreur ll pour 1ui dire que ce n'est pas satisfaisant if (sFileName,length == 0)
t

throw ne*- IOException ("Vous ar,'ez entr un nom de f ichier vide" ) ;


l

ll owre un flux de fichier pour 1a lecture ; ne cre pas // le fichier s'i1 n'existe pas dj FileStream fs = Fi1e.0pen (sFileNane,
Fil
ei{ode . 0pen
,

1/ convertit ceci en StreamReader - ce sont les trois preniers

FileAccess. Read)

i/ octets du fichier qui seront utiliss pour indiquer // i'encodage utilis (nais pas 1e langage) sr = ner,,i StreamReader (f s , true) I

Sixime partie

Petits supplments par paquets de dix

400

Ouatrime partie: La programmation oriente obiet

tz
S

Le type d'accs: Un fichier peut tre ouvert pour la lecture, l'criture ou les deux.

.s$9_ ^. !'i l e t r e an dispose de nombreux constructeurs, dont chacun corresHpondpardfautunoudeuxclesargumentsdemodeetd'accs.Toute-

llgrf Y

fois, mon humble avis, il vaut mieux spcifier explicitement ces arguments, car ils ont un effet important sur le programme.
Dans la ligne suivante, le programme insre dans un objet StreamWriter, sw, I'objet FileStrearn qu'il vient d'ouvrir. La classe StreamWriter permet d'insrer les objets FlleStream, afin de fournir un ensemble de mthodes pour traiter du texte. Le premier argument du constructeur Stream\,n/riter est I'objet FlleStream. Le deuxime spcifie le type d'encodage utiliser. L'encodage par dfaut est UTF8.

5e !!\ :HW ) 'J'/

,tggler^ Il n'est

pas ncessaire de spcifier I'encodage pour lire un fichier. Srream'r,n/riter inscrit le type d'encodage dans les trois premiers octets Ou fichier. I'ouverture du fichier, ces trois octets sont lus pour dterminer I'encoclage.

Le programme Fi 1e',.rtrite commence alors lire sous forme de chanes les lignes saisies sur la console. Le programme arrte de lire lorsque I'utilisateur entre une ligne blanche, mais jusque-l il continue absorber tout ce qu'on lui donne pour le dverser dans l'objet Stream\,nlriter sw en utilisant la mthode i,ririt elin-. ( )
,

1t$!Qa.
^<r7q :(dw

La similitude entre StreamWriter

\/

n'est pas qu'une coincidence.

"n/riteLine

) et Console .\r/ri*reLine o

Enfin, le fichier est ferm par I'instruction sw. Close

()

Remarquez que le programme donne la rfrence sw Ia valeur nul1 la fermeture du fichier. Un objet fichier est parfaitement inutile une fois que celui-ci a t ferm. Il est de bonne pratique de donner la rfrence la valeur nu11 une fois qu'elle est devenue invalide, afin de ne pas essayer de I'utiliser nouveau dans I'avenir.
Le bloc catch qui suit la fermeture du fichier est un peu comme un

gardien de but : il est l pour attraper toute erreur de fichier qui aurait pu se produire en un endroit quelconque du programme. Ce bloc met un message d'erreur. contenant le nom du fichier qui en est responsable. Mais il ne se contente pas d'indiquer simplement le nom du fichier : il vous donne son chemin d'accs complet, en ajoutant I'aide de Ia mthode Path. Corlbirre ( ) le nom du rpertoire courant avant le nom de

Ghapitre 18: Achever votre application

Windows

457

private void ApplieationWindowClosing(object


Systern. ComponentModel. CancelEventArgs e)
{

sender,

1r (!IsChangeOK0)
t

e.Cancel = true;
]

5. 6.

Gnrez le programme et excutez-le" Entrez du texte et cliquez sur le bouton de fermeture de la fentre.
Vous voyez apparatre le mme message d'avertissement que celui produit par la comrlande Fichier/Quitter.

Ralser uos propres applications Wndouts


La cration d'un programme comme SimpieEdiror ncessite de nombreuses tapes, et c'est une application relativement simple. Or, il est en fait beaucoup plus facile de crer une application Windows avec Visual Studio .NET qu'avec les outils que nous connaissions auparavant. Il y a quatre ou cinq ans, ntme une petite chose comme I'affichage d'une simple bote de message tait di