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#

2r

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

22
22
23
24
25
26
26
27

La substance du programme .............

Deurime parte

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

Chapitre 3 : Dclarer des variables de type

valeur

Dclarer une variable ..........


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

........

3l

..........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

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

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

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

Instructions if imbriques
Les commandes de boucle

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 ........

70
71

74
75
76
79

80
84
85
86
90
91

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

Pourquoi auriez-vous besoin d'une autre boucle ?..............


Des boucles imbriques .......
L'instruction de contrle switch
Le modeste goto

Troisine parte

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

9l
92
93
97
100

l0l

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

104

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 ..........

Une structure de contrle de flux pour tous les tableaux : foreach


Trier un tableau d'objets

116

tt7
r20
r24
r27

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

Accder I'objet courant

175
175
177
177

179

r82
183

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

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

195

200

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

Chapitre

I : Rendre

une classe responsable ......

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

;; ";;;,pi; :

239

240
243
244

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

272
274

275
278
278
279
280

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

Contenir BankAccount pour y accder


La relation A_UN

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

28r

Autres considrations

282
282
283
284
286
286

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

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

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

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

.298

IX

G#

pour les Nuls

Revenir la base

303

Le polyrnorphisme

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

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

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

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

362
365
367

368

37r
373
375

378
380

Somma ire

Chapitre 16 : Manipuler des fichiers en

C#.........

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

.......... 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

StreamReader

Cin4uime partie : Programtner lrour Windouls a(ec Uisual Studio

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

.394
. 396

...... 402

.... 407
409

Quel est Ie problme ?


Exposer le problme .............
Concevoir la prsentation

410
410

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

411

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

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

435

439
439

440
442
442

444
446
446
448

XI

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

'methodName' : tous les chemins de code ne retournent pas


ncessairement une valeur
) attendue

471

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

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

474
474
475
475
476
477
478
478
478
479

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

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.

XU

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,

Deuxitne parte
en C#

ntroduction

: 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

XUI

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.
F.

.\

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

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%|

Cette icne indique des aspects techniques que vous pouvez ignorer en

\7 / premire lecture.
\/

= td

Introduction

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

l----Fr:fn

l
|

\-.Y

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.

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.

XIX

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

,/

www.

t/

csharpindex.

t/

r^/ww.

codeguru. earthweb . com/ c sharp


com

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

Ilr
tt:
t I
V

Dans cette parte...

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.

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.

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.

{cg)

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/

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.

t/

Puissant : C# dispose essentiellement du mme jeu d'instructions


que C++, mais avec les angles arrondis.

t/

Facile utiliser: Dans C#, les commandes responsables de la plupart


des erreurs dans Q+r ort t modifies pour les rendre plus stres.

t/

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.

t/

Prt pour Internet : C# est le pivot de la nouvelle stratgie Internet


de Microsoft, nomme .NET (prononcer point net).

,/ 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.

^tK

'qg,

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.

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

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

ffi
P66 de

trvd

'-l
,.!

omarage

.fu

xptor*eur Windows

Arcessoires

,..& Internt

,#

.p
:!

t*il,***

Explorer

outlookExpress
outils Microsoft office

"|ffi, Mi..rlt1utlod.,

Windws Update
Nouveu dscumffit Of f ic

uvrir un dscrfient Office

ffi
ftl
j
-:i
,$

NicroscftExrd

tticrosdt t4ord

xn"t relp workshop


F4'(r')sdt

,NET

Frm$,rk5D{

mfrosoft np6ftion CBrfer Teit


Mi(losaft Offir Tols
visud stldi,NET Enterpris Feturs
visud Studio.NEl lools

lP

Figure 1.1:
La ligne

N5ml for Visual studio,t'ET 7,0

droite n'est

-pas le plus

court chemin
du Bureau
c#.

;fioemaner

3.

ir
3J

tlglts

rl^6

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

Figure 1.3

L Assistant
Applications

-de Visual

studio est
prt crer
p0ur v0us un
nouvea

pr0gramme

Windows.

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

I
| lll
:J1

<t

Prijet de

':reatron

Emplacement

ffi

@
Fibtiottreowde

-*

A5P,FIET

Eibliothque de
contrles U/eb

AFliration
AsF'.rJE

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

d'une pplirtion vec une interfare utrliEateur Windoi+s

,-

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

;|

Parcourir

,..

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

vtlus

5.

T- "l --l

Annuhr

Aid*

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

t\r

Gnrer et excuter tlotre premier ritable


programme u'Uindouls
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.

^"9FK

7 ,31ll )

\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

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

Forrn5.Ffr w

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

j
E

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

exc ita nt.

Gerrrat rorl

5izble

Rightl0teft
Text,

Forml

No

:8 l. +rr rt*rI rtzzt,tl


: AllowDrF
antextllenu
i Enbled
ImeMode
E ! .tq rIr.tr r' ,tti::t*r

False

{aucunl
True

Nofontrl

El (Dyndfri.Propertr
:

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

-,

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

Le txte cor*cnu dnr ce csntrle,

{
La gnration a rursi

FrmBsrder5tl,l

Coll

Chl

tl

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

La fentre du

modle

-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

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

tatel

gll

E.rttc'n

Fc,inteur

LinLLabel

iii,r-

Te:<tBo:r

$
lv
(.

Marnf"lenu

Figure 1.6:
La Bote

-dl

Picture8x

outils de
Visual Studio
contient une
quantit de
contrles

!
.9
ll
:g
;:-

utiles.

Presi-F,piers cirr:ulire

LneaxDox
HlOtrUtf0Tl

Grupuox

-.j

;:

h'Anel

natacrid
ListBctx

checkedlist8ox
L0mBoErlx
Llslvre({

Gnerni

t3

t4

Premire partie : Grer vos premiers programmes

C#

.$$G ^/

Si vos fentres ne sont pas aux mmes

t(7,
V

droite ou au milieu de l'cran. Vous pouvez dplacer chaque fentre o


vous voulez dans la fentre Visual Studio.

endroits que les miennes, ne vous

Hinquitezpas.VotreBo1teoutilspeuttrsbiensetrouvergauche,

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.

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

la premire.

3.

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

deux zones de texte.

4.

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

Figure 1.7
Mon

Wi
''

formulaire
0.
est srement
plus beau

+1lx

Iter:tE'rr:1

...

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.

SIectionnez le bouton en cliquant dessus.

2.

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.

t5

t6

Premire partie : Crer vos premiers programmes

button

f-'=l
s;
| .- I dr

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

-t

frl,a1
lt9 | /

G#

Butt'ln

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


n
P

Default

n'
Copier

FlaL5lyle

5tandard

l-aJTtl:

l'licrrrsoFt 5ns 5erifj tl,Z5pt

Ftrefol,:r

!
[--l

Inr,rqe

fontrolText
(nurun)

Inr,r'lelgn

Ivliddlefenter

Intaqelrr,le;r

l-l

RighlT,:Lel't

No

lnucun)

f"liddle,lerrter

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

E | ..,,,i.,,,;r..

'

llr,r+Dr,:p

IdIS

aonLe:rtlt:rrU

I UCUn

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.

D.

Dans la barre d'outils de Visual Studio, cliquez sur Ie bouton

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

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).

-ltrl

Figure 1.10:
La fentre du

pr0gramme

lrEEtrEE@

formulaire

lle

-est le

que v0us
venez de

crer.

programme copie ici ce qu vous avez tap

t7

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.

Dans le Concepteur de formulaires, slectionnez le bouton Copier.

2.

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.

4.

textBoxl

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

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

1r${Qa^

ftg)

minuscules).

F.,:hiet Edition Affrchage

ll 'i

.3:
t'.

l?f

I'tt

PJojet

' L - .,! rai'I#

-.

,' Forml,cs*

Ferr
:':)',

!bog,:er

utils

:l

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

:J

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

l.:'T-.Tl1r 1ll

:-lf,l1r

!r:,lal llil]til

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

Figure 1.11

La fonction

de sugges-

tion automatique affiche


les noms des

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

1r

>l

+r

5rne

proprits au
fur et
mesure que
vous tapez.

:t*
Z

; :,.;,, .. E

ssrtie

Pret

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

tg

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.

^tK

'qE,

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'

me le programme windows le plus lmentaire peut tre

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.

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'

parties) est de
Le principal but de ce livre (en tout cas des premires
pour
qui
aura un succs
ySP*E
faire en c# un ieu
\ ;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\
/

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.

e,
.--).

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#

4.

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.

5.

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)

||

Suwary description

|
III

for

Classl.

4tsw*nary)

class

Class1

static void Main(string[1


{

ll
/l
il

args)

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.

23

24

Premire partie : Grer vos premiers programmes C#

Visual Studio rpond par le message suivant

Dbut de

la gnration: Projet: Hel1o, Configuration:

Prparation des ressources. . .


Mise jour des rfrences...
Compilation principale en cours. . .

Debug ,NEf -

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


^rtC

,hv-

I a russi.

Unerglegnraledelaprogrammationestque''aruSSi''Veutdire,,a

f fxll

va", et "a chou" veut dire "a ne va pas".

tL]r,
-

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.

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

-rr
r\V-

^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#

// Dernande son non 1'util-isateur


Console.Writeline("Entrez votre nom: t') ;
ll Lit le nom entr par 1'utilisateur
sNane - Console.Readline0;

string

/i

Accueille 1'utilisateur par son non

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.

4${ea^

Vous pouvez aussi excuter votre programme partir de la ligne de

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.

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.

25

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

le

que coruaenc

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#

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

console.l,lriteline("Entrez votre

ll tit
string

/l

nogl:

1e non entr par 1'utilisateur


sNane = Console.Readline0;

Accueille 1'utilisateur par son

Console

")

.l,Jriteline ("He11o

,"*

nom

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

27

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-$,j
-i
il
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

si k:

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

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

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.

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

^tK
=({g,

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

crit

- rl. t

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

33

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

nf

Dc1are une

variable entire

avec 1a valeur

int

et f initialise

m = ?'

Assigne

la variable n la valeur stoeke dans m

n=
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

int

de dffrents

tapes
int. C# en offre un certain
nombre de variantes pour quelques occasions particulires.
La plupart des variables simples sont de type

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^

; Lt 'rilliards.

't

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

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

\J/

;l-\

\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 ./

Autrement dit, un entier iong occupe deux dossiers d'une capacit d'un

HintclansvotrearmoiredossierSSuSpendus'Commecettemtaphore

t(7,
Y

d'armoire dossiers suspendus commence tre un peu use, je parlerai


partir de maintenant en octets.

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

ment de -1Ots 101e.


1e${Qa.

^.rr7p7\ L'tendue

'qg,

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#.


Type

Taille

sbyte

byte

(octets)

tendue

Exemple

-128 127

sbyte sb = 12;

0255

byte b = 12;

short

-32,768 32,767

short Sr = 123456',

ush ort

0 65,535

ushort usil = 62345678;

int

-2 milliards 2 milliards

1nt

uint

0 4 milliards

uint un = 3234567890U

long

-101s

ulong

1020

101e

(beaucoup)

n = 1234567890;

long | = 1234567890121
long ul = 123456789012U1

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

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
i(-9t4K
ilfi ) entier de 37,78 est 37. Cette manire d'escamoter la partie dcimale d'un
nombre pour le faire tenir clans une variable entire s'appelle tronquer.
\g)

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({",'

=l

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.

37

38

Deuxime partie : Programmation lmentaire en

G#

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.

:HW

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

-sq4<o

./

i:

=( UA )

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

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

float
double

-$tf,

Taille [octets]

Etendue

1.5

16

*
5.0 10324

* 10'45

3.4

* 1038

x
1.7

10308

Prcision

Exemple

chiffres
15 - 16 chiffres

float f

6-

= 1.2F;

double d = 1.2:

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 ?

39

40

Deuxime partie : Programmation lmentaire en

G#

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

0.tt555555555555555 iB2323
Le type double possde quant lui entre

.st 1

l5 et

16 chiffres exacts.

Comme en C# une variable virgule flottante est par dfaut en double

Hprcision(letypedoub1e),vouspouveZutilisercetype,moinsd'avoir

Itgrf
s---'r
-

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

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

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.

4l

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).

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

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.

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

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/

Comme une variable en virgule flottante, pouvoir reprsenter une

fraction.

t/

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:

= 100;
rn3 = 100M:
m2

/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.)

43

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.

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#.
\/

:(dw

Une variable boo 1 se dclare de la faon suivante


bool variableBool = true:

Chapitre 3 : Dclarer des variables de type valeur

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.

45

46

Deuxime partie : Programmation lmentaire en C#

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

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

tO /
V,,/

si

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'

tabulation

'\0'

caractre null

'\r'

retour chariot

'\\'

barre oblique inverse

Le trlpe

string

srring est galement d'usage courant. Les exemples suivants


montrent comment dclarer et initialiser une variable de type st ring
Le type

I dclaration puis initialisation

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

I initialisation avec 1a dclaration

string soneString2 = "ceci est

une chaine";

Chapitre 3 : Dclarer des variables de ttpe valeur

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

'rteci est

un nembre de uhrase

string

s la chalne suivante

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

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

ring est trs

diffrent du type char. Naturellement, il existe quelques diffrences


triviales. Un caractre est plac entre apostrophes, comme ceci :
l-

47

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

^"tK

'qE,

En fait, ce code peut presque se compiler, mais avec une signification


compltement diffrente de I'intention initiale. Ces instructions provo-

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

^1
r

il^ilr
a

a+r'.ina
L!rrr

1
4

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

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

49

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


Bien qu'il y ait trs peu d'absolus dans la vie, je vais vous dire quelque
chose d'absolu sur C# :

-of4q
^-/ -t- \ ':-/
-i
) I oute expressron
II

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
onod
.),'.- * int

iU

rinqi

1L

long int

1.0

d o ui:,

1e

Ghapitre 3: Dclarer des variables de type

Constante

Type

1.0F

f!IUAL
I ^-r_

1M

decimal

t rue

bool

^1^^
I.1 LbC

bool

r-l
a

^t^-

'\n'

char (caractre de nouvelle ligne)

'\x123'
"a string"

char (caractre dont la valeur numrique est hex

valeur 5 |

LllAt

123)

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

Toutefois, une conversion dans la direction oppose peut poser des


problmes. Par exemple, ce qui suit est illicite :
long lValue =

int

10;

nValue;

nValue =

lValue;

ceei est

illicite

lnt

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

int

1Va1ue

10;

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.

1t$!ea.

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

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.

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

Signification

- (moins unaire)

prendre le ngatif de la valeur


multiplier

diviser

additionner

- (moins binaire)

so ustra i re

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 \

'qE,

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

7u

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

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 ?

-efr{c
j5:

S/
=(

\
$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;

55

56

Deuxime partie : Programmation lmentaire en

Pour arriver au rsultat final

C#

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 * 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

trois instructions ci-dessus sont quivalentes. Chacune incrmente


l.

de la valeur

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

++n;

n
rt = Ij. t

int

= n**;

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

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

;;; ;;;;

_
ib=10M)12M;
i

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,.
-.,-l
qu
plus
qros rr--1I oar !I
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 =

f.I |

3i

bool b1 = (3
1/

tt

* f2)

==

fl;

4.

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.

lrpU{a"

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
\/ )
:

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 oprateurs logques

bool disposent d'un autre ensemble d'oprateurs


logiques, dfinis rien que pour elles, montrs par Ie Tableau 4'3.
Les variables de type

Tableau 4.3: Les oprateurs logiques.

Oprateur

Betourne t rue si...

!a

a est

a&b

a et b sont

alb
a^b

a ou b ou les deux sont

a&&b

a et b sont

true

avec une valuation en court-circuit

all

a ou b sont

true

avec une valuation en court-circuit

a est

false

true

t rue

ou b est

true

true

(aussi appel a eVou b)

rTtis pas les deux (aussi appel a

xor

b)

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.

^dK

'qg,

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

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)

ll

(boolExpression2);

boolExpressionl est true, il n'y a aucune raison d'valuer booiExpression2,


car le rsultat sera t rue de toute faon.
Si

Chapitre 4 : Les oprateurs sont sympas

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 :
illlL
n+ n
ll

.
,

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

63

64

Deuxime partie : Programmation lmentaire en

G#

La plupart des oprateurs admettent diffrents types. Par exemple,


I'oprateur de multiplication :

int
uint
long
float

* int
* uint

* long
* float

{9---2 1nt
@- -

-) uint

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

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

Ainsi,2*
type

decimal
double

3 utilise la version

int * int

de I'oprateur

* pour produire 6, de

int.

Connersion de tqpe

inltlcite

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.

sympas 6 5

Chapitre 4 : Les oprateurs sont


;1t0lv.r

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

n1

1;

double 2 = 5.0;
int nResult = nl

Le cast de

int * int

2 en l

- (int)d2;

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
^f\1|

\!!_/

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 -

1o;

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.

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

Et je rendrai les choses encore plus confuses avec un exemple

sympas 67

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

nl{ax

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

//de

mme

b)

que ceci

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

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

static

lcl

voi.d Main(string[l args)

denande son non

//tit

1'utilisateur

Writeline ( "Entrez votre non: " ) ;


te nom entr par 1'utilisateur

Console

string sNane - Console. Readline ( ) ;


//accueille 1'utilisateur par son nom

tonsole.l,lriteli.ne("1{e11o, " * sNane) ;


//attend confirnation de 1'utilisateur
Console.I.lriteLine("Appuyez sur Entre pour terminer...")
Console.Read0

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.

fr
\(Ll

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.

Controler le flux d'excution


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

if

1f

(bool expression)

/l

1'excution est oriente

ici si

1'expression est vraie

// 1'excution se nottrsttit iei.

nrrp

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

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

I garantir
ll si st
ir(a()
I

tl
a

n'est pas infrieur 0


infrieur 0.

que a

. alors,

assigner

0;

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

"if (expression

boolerne)

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.

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

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.
La premire quation multiplie le principal,

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 :

7t

72

Deuxime partie : Programmation lmentaire en G#

_-"-ffi
,

//
II
ll
II
II

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


r

o-

using

v--ve-.

System;

Interest

nanespace Calculate
L

public class

Class1

public static

int Main(string[J args)

//demande 1'utilisateur d'entrer


Console, Write ("Entrez le principal

le principal initial

: ");
string sPrincipal = Console.ReadLine0 ;

decirual nrPrincipal = Convert,ToDecinal(sPrincipal)


| 1,,rifia nrrs 1e principal n'est r-pas ---ongatif
---

1I tnrr]-nclpalr \/

^\
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

(mTntprest
\Ir+..

Lv-

vv

0)

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

nlnterest

0;

i/""1.ut* la valeur du principal


IIplvs f intrt
decinal mlnterestPaid ;
nlnterestPaid = mPrincipal

le total
decirnal motal = nPrincipal *
//affiche rsultat

(nlnterest

tOO)

//ca1cu1e maintenant

mlnterestPaid;

Console.l^lritelineO ; /i skip a line


Console. i,iriteLine ("Princi.pa1 = "

Console.l,lriteline("Taux d'intrt

Console

llriteline

* nPrincipal)
* " * nlnterest *

"%");

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.

Console

Console.Read0;

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

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 =

Zlol

rntert pay =

1t,
259.14

1234

73

74

Deuxime partie : Programmation lmentaire en

Total
Appuyez

G#

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

7X
t(9,
Y

1234

Intert pay =

Total

1234

Appuyez

^$$G t

d'intrt = 0%

sur Entre pour terminer..,

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

mar

conserr/

conne maxinun

= ai

I si a est infrieur ot gal b,

Ghapitre

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

if (a (= b)
I

max

conserve

conne maxinum

= b;

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

.conserve a conne naxinum; sinon


a;

e1s e
I

max

conserve b conne naxinum

= b;

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 hsuppose que d
PJ-u l.duu

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

nec lo

n.c

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

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

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.

if

est dtect avec les valeurs entres.

I
I
||
II
II
I
I

est

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

Le programme suivant,

instructions

if

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

et n'effectue pas le

using

programme 7

ca1cu1.

System;

natuespace CalculatelnterestWithBmbeddedTest
t

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.
^\
1t lmrrlncr_pal{ \/ u./
;

IL

gnre un message d'erreur.

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


l

else
t
I

sinon,

demande 1e

taux d'intrt

Console.Write("Entrez 1e taux d'intrt :");


string sTnterest = Console. ReadLine 0 ;
decimal nlnterest = Convert.ToDeciraal(sTnterest)

I lsi

d'intrt est ngatif ou trop 1ev.


if (nlnterest ( 0 ll rnlnterest ) nMaximumlnterest)
7e taux

.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;
t

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;
t
ar,
1
r.
rsultat
/affiche

f I

Consol"e.l,lriteline0

; //

ConsoLe.I,lriteLine()

skip a line

Console,1{riteline("Principal = " r mPrincipal);


Console.Writeline("Taux d'intrt = tr f nlnterest *

"%"):

Deuxime partie : Programmation lmentaire en

onsole.Writeline("Intrt
Console.l,lriteline

('tTotal

C#

pay = tr + mlnterestPaid);
=

rt + nlotal)

llattend confirmation

de

1'utilisateur

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

. . ")

Console.Read0;

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

if

f utilise un test compos

(mlnterest

( 0 ll

nlnterest

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.

t\\vl

dclar nl'laxirnurillnteres'1- en haut du programme au lieu de le coder


localemenl sous forme de constante.
-a

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

Le taux

d'intrt doit tre positif et pas suprieur 50.

Appuyez

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,

Intert pay = 154.25


= 1388.25

Total

Appuyez

sur Entre pour terminer.

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.

79

80

Deuxime partie : Programmation lmentaire en C#

Commenons qtar la boucle de base, wh


Le mot-cl de C#
simple :

;l:rie

1
--1-

\-

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.

cfq-'.'

=,:c|,,)

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

urrr JyLcur,
nmpsn2c
l.qirrLUPsu!

Ce

r^rrlatelntereStTable

"^
u!lr
"-^

Q"^+^-.
/Lcl4,

programme

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

public class

C1ass1

public static void Main(stringIJ args)


{

//demande 1'utilisateur d'entrer 1e principal


Console.hlrite("Entrez 1e principal : ") ;

string sPrincipal = Console,ReadLine0

initial

decimal mPrincipal = Convert.ToDecimal (sPrincipal)


tl
l
/ Dr r
/llal

1r {mHr1nc1nl \
.

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

nrln.1h.1
yrarrLfyof
a

uJ
^\

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 =

0;

l
e1s e
{

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

vaLides

//demande donc 1e nombre d'annes


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

:")

int

nDuration = Convert.ToInt32 (sDuration)

llv.rifie la valeur entre


Console.l,JriteLine0; I I crit

("Principal

une

ligne blanche

= " -l- nPrincipal) ;


Console,I,,Iriteline("Taux d'intrt = 'r + mlnterest + rtt/rt)'
Console.1rlriteLine

Console.Writeline("Dure ='tf

Console.l,,lriteLi.ne

lleffectue
inf

nerr

nDuration

"ans");

une boucle selon 1e nombre d'annes spcifi

= l'

while(nYear (= nDuration)
{

l/ca1cule 1a valeur du principal


ll
.
I'intrt
l/plus

deci.mal mlnterestPaid

mlnterestPaid = mPrincipal

* (mlnterest I

100);

8l

82

Deuxime partie:Programmation lmentaire en C#

/lealcu1e naintenant 1e nouveau principal en ajoutant

//f intrt

au principal prcdent

mPrincipal = mPrincipal * nlnterestPaid;


//arrondit 1e principal" au centirue le plus proche

nPrincipal : decimal.Round (nrPrinci-pa|,


I I atfiche le rsultat
Console . l.Jriteline (nYear

2)

+ rr - rr + nTPrincipal

I I passe 1'anne suivante


nYear = nYear * li

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

II

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

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

Taux

- 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

1ep!Qa"

{dg)

La plupart des valeurs comportent deux dcimales pour les centimes.

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.

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

=\l\y j
\/

Round ( )

arrondit la valeur calcule au centime le

plus proche.

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,

83

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.

>/

tl ^,,\I

\t/

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;

vn1le lnlear \ lu,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

nYear

= J;

do

t
I

instructions.

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

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.
rarement utilis continue dans ma carrire de programmeur, et je

-q\!C ^,

J'ai

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.

Hdoutequ'ilyaitbeaucoupdeprogrammeursquisesouviennentseulement

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

(mPrincipal

(maxPorrer

n0riginalPrincipal)

t!

break;

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.

',.t$f$

'./
F

ha"

f-fiT.X.)

I li'/

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,

Dure

= 100 ans

85

86

Deuxime partie : Programmation lmentaire en C#

si la valeur initiale

Arrter

est nultiDlie nar

10

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
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

//

1'utilisateur le nrincin:l initial;


ValeUf valide soit entre

denande

continue

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

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

//

maintenant

demande

1'utilisateur le taux d'intrt

decinal nlnterest:
whi.1e

(true)

Console,l^lrite("Entrez 1e taux d'intrt :")


string sTnterest : Console.ReadlineO ;

mlnterest : Convert.ToDecirnal (slnterest) ;


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

break;
]
I

.et

gnre aussi un message d'erreur

Console.l^lriteLine("Le taux d'intrt doit tre positif " *


"et pas suprieur " + nMaximumlnterest)
Console,l^lriteLine ("Veui.11ez reconnencer" ) ;

Console,illriteline0;
]

ll I'int|rt corune 1e principal sont valides,


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

Console.

:"

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 =
;

,'

Console.}lriteline ("Taux d' intrt


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

Console

// effectue

+'

mPrrnnlh
srr!frj!rHufl'

I t.

" * mlnterest * "%") l


" * nDuration f " ans");

une boucle sur 1e nombre d'annes spcifi

87

88

Deuxime partie : Programmation lmentaire en C#

int nYear = 1;

while (nTear (= nDuration)


{

/l
i/
ll

calcule 1a valeur du principal


Dlus

l'lnteret

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

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

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

nPrincipal)

proche

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

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.

{f\

q/

e,
z<r- \

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.

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

Veuillez

recomnencer

Entrez le taux d'intrt :10


Entrez 1e nombre d'annes :5

Princinel

000

50

8g

90

Deuxime partie : Programmation lmentaire en

Taux

G#

d'intrt = 10%

Dure

=5

ans

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)

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

nDays

n\/c1rro
= llvoauc

LLUqJ D t
I/ nllrrr<.

instructions

= 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

0mpf9ttr ilre la boucle la plus utilse.' f

programme

or

wl,ile est la plus simple des structures de boucle cle C#. et la


plus utilise aprs f or.
La boucle

Une boucle f or a la structnre suivante


f.or

(initflxpressioni condition

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

whiie suivante

or peut tre convertie dans la boucle

initExpression;
hile (condition)
t

instructions.

incrementExDression;
l

Un evemlrle de boucle f

Un exemple vous permettra de mieux comprendre le fonctionnement

d'une boucle f or

1/ instructions
a=

1;

C#

9l

92

Deuxime partie : Programmation lmentaire en

G#

I I et naintenant une boucle


for(int nYear = 1; nYear ( nDuratj,on; nYear = nYear *

1)

IL

corps de

la

boucle

]
I

I Ie programme continue ici

a^ = 1,
L.

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

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

nYear

= l;

nYear

( nDuration; nYear*i)

i
I

.corps de 1a boucle

.q$q ,.,

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

Hdansunebouclefor,pluttquel'oprateurcleprincrmentation,bienque

t(9,
Y

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(

,condition

.)

fnr l'
!vr

,r1tr

conditi.on

.)

t
I

corps de 1a boucle

l
l

-.sg4le

t(

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

U"" boucle incluse

1g )

dans une autre boucle est appele une boucle imbrique.

93

e4

Deuxime partie : Programmation lmentaire en G#

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

ce qui suit n'est pas possible

I ldbut d'une boucle

do

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 :

// boucle for A
.condition
for(

L,

i'r.?,'r'.

fait sortir de la boucle B, et

.)

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

,)

II
if (.

.
.

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.

1t${a.\

^v7-y
eHq9
\/

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

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

Hl

programme

Le saugrenu programme Display'XWithNestedLoo;:s utilise deux boucles


imbriques pour afficher un grand X sur la console de I'application :

//
I

DisplayXWithNestedloops

- utilise

deux boucles iurbriques


pour dessiner un X

using

System;

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 Ie

caractre par dfaut est un espace

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

char c
I

IL
='\\':

renplace 1'espace par un backslash

c
l

ll si 7a colonne est du ct oppos de la ligne...


int nMirrorColunn = nConsoleWidth - nRovNum;
if (nColunnNun == nMirrorColumn)
{

',
'l

renplace 1'espace par un slash

c o'/';

I
I

affiche 1e caractre correspondant I'intersection


de la ligne et de la colonne
Console.l4rrite(c);
I

Console.l.lriteline 0

ll attend, confirmation de 1'utilisateur


Consol.e.l.lriteline("Appuyez sur Entre pour ternriner...")
Console,Read0;

I5

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.
Ce programme utilise une boucle f

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).

=Qt
"t\

Souvenez-vous que le backslash est utilis pour indiquer les caractres


re caractre de nouverre rigne Le caractre

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

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

\
\
\
\
Appuyez

sur Entre pour terminer...

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

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

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

I t\t

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 =:

0)

I ldoit tre clibataire


I
. instructions.

I
]

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

97

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:

instructions si clibataire.

break;
1.

^^^^

LA

l.

instructions si mari.

break;

case 2:

instructions si divorc.

break;

case 3:
I

instructions si veuf.

break;

case 4:
I

al1ez vous rhabiller.

break;

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

//c'est
break;
break;
]

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

s)

case ttDavistt:
I

le contrle

passera par ici..

break;
case "Srnith":
I

instructions si mari.

1^-^^1,.

cse "Jones
I

"

instructions si divorc.

ring

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

break;

case "Hvidsten":

tl

instructions

si veuf.

break;

default:

/l

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

break;
j

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:.,.

,/

Les valeurs en virgule flottante sont exclues.

t/

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


sr,,ui

t ch.

t/

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

1a mme chose pour Davis ou Hvidsten

I lcar leur si.tuation est la

mme

break;

case "Smith":
I

instructions si. nari.

break;

default:

reak

passe
;

ici

quand aucun cas ne corresponi

99

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/

Une tiquette.

t/

Un case d'une instruction sw. rch.

t/

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

le contr1e

IL

passe de goto

1'tiquette spcifie

goto exitlabel;
]
I

exitlabel

quel que soit

1e

r"odc

nrri s trnrve ici,

//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 ./

L'utilisation de goto a dclench quasiment des guerres de religion. En

Hfait,lelangageC#lui-mmeatcritiqupouravoiradoptcetteinstruc-

t\?,
Y

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.

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

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

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;

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.

.gtt\

o/

Yi

.'-<) \

pubiic c 1ass, suivis

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.

| 05

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

int

h11m.

nun

1,

num

Chapitre

6: Rassembler des donnes: classes et tableaux

[.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

1r!!{a"

Aa,/

1;

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.

tarl \ valeur. L'objet

e(dw
\.,

De mme, le code suivant stocke une rfrence aux chalnes dcrivant le


modle et le nom du constructeur de myCar :
nyCar.sManufacturer =
myCar,sModel

I'BMI,I"; // ne perdez pas espoir


I / c'est une poque disparue

= "Izeta";

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

107

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

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

tl

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

Un exemple de programmes base d'objets


Le trs simple programme suivant, VehicleDataOnlr'

t/

Dfinit la classe ehic1e.

t/

Cre un objet nyCar.

t/

Assigne les proprits de myCar.

t/

Rcupre ces valeurs dans I'objet pour les afficher.

|/ lr"hinlalaraonlrr

cre un objet de type Vehicle,

valeur ses nembres partir des


saisies de 1'utilisateur, et affiche 1e tout

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

"^:-,uDrrr

namespace VehicleDataOnly
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

ici que commence 1e programme


voj-d Main(stringIJ args)

C'est

static
tt

//

denande son non

1'utilisateur

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


I

cre une instance de Vehicle

Vehicle

myCar

= new Vehicle0;

// utilise

une variable pour donner une valeur un membre


Console.Write("Notn du rnodle = ") ;

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

myCar. sModel

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{) ;

= ");

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

myCar.nNumOfDoors

s = Console.ReadlineO;

109

I |0

Troisime partie : Programmation et objets

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

myCar.nNum0fl{hee1s

/l

Console.Writeline(myCar.sManufacturer *' rr + myCar.sModel) ;


Console.I,iriteLine("avec " * myCar.nNun0fDoors t " portes, "

* "sur " { my0ar.nNum0fWheels


* " rouesrt)
;

/l

attend confirmation de 1'utilisateur


Console.lJriteline("Appuyez sur Entre pour terminer. . .")

Console.Read0;

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

avec 2 portes, sur 4 roueg


sur Entre pour terniner..,

Appuyez

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

Distinguer

tableaux

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

= "Studebaker" ;

carl. sModel = "Avanti"


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.

I I I

| |2

Troisime partie:Programmation et objets

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

6${ea^
-!3\ Le compilateur

ler

:(d

W )
\/
rfrence non initialise met fin immdiatement

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

..--yourcut\

La relation

entre deux

-rf ren ces


au mrre
objet.

iii?i1:J, />tr"h'*l
l-....--l
"Rambler"
yourSpo usalCar-/
Y

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';

I ^11
'+rsJ11
Ir dPydrLrrIL

II I

vvLl
fenme
!uus
aussi q votre
duDf

Ghapitre 6 : Rassembler des donnes : classes et tableaux

Vehicle yourSpousalCar = your0ar;


ll ryand e11e s'en va avec votre voiture.
yourCar = nu1l;
I I yourCar rfrence maintenant "1'objet

IL

NULL"

le mme vhicule
Console.l^lriteLine("Votre voiture tait une " * yourSpousalCar.sModel)
yourSpousalCar rfrence toujours

L'excution de ce programme affiche le rsultat "Votre voiture tait une


Ford T", bien que la rfrence vou rCar ne soit plus valide.

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.

frg)

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

;-+
rlrL
yu!ffL
^"L1.i^

-T'.^^f\nJheelS;
rrrrul[v

public

int

nrrhl
yuvlrL i^

lnrrhla
uvuua

nPower;
licnln.'pmpnt.
Urryrq!Lu!rrL,

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
fI
n"hlin
yuVI rL

int
lll L nDnr.rar.
lMWq!

/i

puissance du moteur (Chevaux-Vapeur)

n3

I I tl

Troisime partie : Programmation et obiets

public double displacernent;

/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

objet de 1a classe

Motor

new Motor0;
largerMotor.nPower = 230:

t'lotor LargerMotor

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

Ou alors. v accder directement

"*

m.displacenent);

"*

sonsCar.motor.displacernent);

Console.Idriteline("La cylindre du noteur est

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

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 ;

slicensePLate = "48C123" ;
incrnente le nombre de voitures pour
. nNurnbe r0fCars** ;

new0ar.
I

Car

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.

| |5

| |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

[]

nMaxTemperatures

for(int

index =

new

0; index (

int

[nDaysnYear]

nDayslnYear; index**)

I
.additionne la temprature naxinale pour
/l jour de 1'anne.

chaque

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

lfAn-,-r-,
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

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

n7

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

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 :

une boucle

calcule 1a moyenne d'un

FixedArrayAverage

de valeurs en

utilisant

nombre dternin

une boucle

nanespace FixedArrayAvera ge
{

-^
'.^.1
urrr5

C,,^+^-.
uJ
LIr,

public class Classl


{

public static int Main(stringll args)


{

double[] dArray
f5

7
r,

? 5

JrJ

R 1

I tait la sonme des valeurs


I I dans 1a variable dSum
I

double

dSum

1
.,

?].

Jt

du tableau

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

Console.hlriteline("Appuyez sur nntre pour terminer.


Console. Read

return
l
]
]

0;

rr).

Chapitre 6 : Rassembler des donnes : classes et tableaux

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

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.
Le programme

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.

ng

Chapitre

6:

Rassembler des donnes: classes et

tableaux

I I dclare un tableau de 1a tail1e correspondante


double[] dArray : new doublelnumElenrents];
I I rennf it ie tahleau avec les valeurs

for (int i = 0; i (

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

double

dSum

": ");

valeurs

0;

for (int i = 0; i (

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

moyenne

+ dArray [o] )

for (int i = 1; i (

de ("

nuniElements; i++)

Console.Write(rr ''j- rt + dArrayIi]);


l

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

Entrez la valeur ne1: I


Entrez 1a valeur ne\: 2
Entrez 1a valeur ne3: 3
Entrez la valeur neA: 4

calculer

l2 |

Chapitre 6:Rassembler des donnes:classes et

// 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] =

tableaux

": ");

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.

123

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";

lil

students

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

- calcule
d'UV

1a moyenne des points

(GPA)

d'un certain nombre d'l r:liants.

("^+^-.
uJLrlrl

',^.i-^
urrr5

nanespace AverageStudentGPA
{

public class

Student

public string
nrrhlin

sName;

//

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)

Studentl] students = nel^r StudentlnNunber0fStudents]

I rennlit maintenant 1e tableau


for (int i: 0; i ( students.Length;

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

,l^
F,ntrez.
s
DttLLL
luvjllrl
q mvn1
uE -^'i-+^
yvfrrL

Entrez 1e

n-+-^-

lllL!4

I rTTlt
. j,5
uv

de 1'tudiant 3: Carrie
de yvrrrLo
ooints d'UV
: 4.0
nlUJClI-lC
uL
u uv
nom

La noyenne gnrale des 3 tudiants est 3.5


sur Entre pour terminer...

Appuyez

^$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

c el nss Strrdent

{
nrrhl i n <trino
nrthli^

cT\^*^ '
uriOlltE,
rlflP'

rlnrrhla

//

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

double

= 0.0;
= 0; i ( students,Length;

for (int i
{

dSun

du tableau

dSum

*= students Ii]

.dgPn;

l
double dAvg = dsum/students.Length;
II
.utilise le tableau.

1fT

t27

I 28

Troisime partie : Programmation et objets

La boucle

- o r-

effectue une itration sur tous les membres du tableau.

-/ \
;/
-('^..L \ students. i,enpth contient le nombre d'lments du tableau.
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

double

l^
rd

*^,,^-^^
lruyetllte

dSum

^uej

tudiants du tableau

= 0.0;

foreach (Student stud in students)


{

dSun

*= stud.

double dAvg

dGPA;

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
stud..i A chaque passage successif, I'instruction f oreach va chercher
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"

frg)

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

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

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]

studentsIj]

/l

net de ct 1'tudiant noi

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

deux objets'

-signifie en
ra lit

"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

29

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

/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

description d'un tudiant

{
-,.L1.i^
puD-r-LC
Lrrng sflame;
s^+-.i-^
^N

nrrhlic riorrhlp dGrade : 0.0;

(nom

et rsultats)

Chapitre 6 : Rassembler des donnes : cf asses et

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 r'^+e*-)

^rvertit
Lf L en
crr Lrrdrlrc
chane 1'obiet

II I UsLLrarr

LUllvc!

Student

en cours
public string GetString0
I

{
a++-ia^
^:
Lrrrr

[il.
,

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

|
I

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 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

lindex]

from;

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

// soient dans le bon ordre)


bRepeatloop = true;
l
]

tableaux |

3 |

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

Li.sa

3-

Marge

3.5 2

0-

Maggie

Bart
Honer

nnttt'ao
nyyuJL

o'rr
ur

Fn+r6a
lrrL!s

nnrrr
Pvu!

tarminar
LErluJllc!,,.

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

Homer

Lisa

Bart

-le tri en

Marge

bul I es.

Maggie

Figure 6.3

Avant de
c0mmencer

Figure 6.4

Aprs le
premier

-passage

du

tri en bulles.

Figure 6.5

Lisa
Bart

4
2

Marge

Maggie

3.5

Homer

O <-

Lisa 4 <:

Aprs le
deuxime
passage du
tri en bulles.

6: Rassembler des donnes: classes et tableaux

Marge

Maggie

3.5

Bart 2 <-

Homer finit par se retrouver tout en bas.

Lisa reste

tout en haut.

Bart est descendu, mais reste au-dessus d'Homer.

Homer A

Figure 6.6

Aprs
I'ava nt-

-dernier

Lisa

passage, la
liste est

E
_
) Maggie
4
Marge 3
Bart 2

trie. Le
passage final
met fin au tri
en constatant que rien
ne change.

Maooie

4
3.5

Homer A

et Marge sont permutes.

133

|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

rrn nhiot

/lutilise 1'objet pour initialiser

ovrmnlo nTnt = l.

//un

membre donne

//utilise

Exanrple.nStaticlnt = 2i

//.,//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 ( )

Exanple. ClassFunction

I ltttil

icp I'nhict

nnrrr inrrnnrtpr

/ 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 |

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.

Lorsquejeclcrisdesfonctions,jemetslesparenthsespourqu'elles

f >,1

llglf

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

37

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.

t/

Une section d'affichage des donrres entres, afin que I'utilisateur


puisse les vrifier.

t/

Une section finale qui calcule et affiche les rsultats.

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

,/

Le principal.

t/

Le taux d'intrt.

tz

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

- gnre une table d'intrts


semblable d'autres programmes de

CalculatelnterestTablel,lithFunctions

table d'intrts. nais utilise


certaine division du travail
entre plusieurs fonctions.

une

Chapitre 7 : Mettre en marche quelques fonctions de grande

using Systen;
namespace

CalculatelnterestTablel,lithFunct ions

public class

C1ass1

public static voj.d Main(stri.ng[] args)


{

//Section L - saisie des

decimal mPrincipal : 0;
decimal mlnterest = 0;

deci.mal mDuration

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

- Ifr.i+L q !a!-"-tir
neina.l
/ Inoutlntere.+n.+.
Lv! v L!q Lq
L1!
IE!
1e y!nr.iarrulyal
f,tr
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

eoinel mPrinninal

ryqsq\!

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") ;
;

//
II

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-,-

l/i\ s'agit de saisir un nonbre


//vtttLer gu,il est positif)

dcimaL

et

de

classe | 39

| 40

Troisime partie : Programmation et objets

public static decimal InputPositiveDecinal(string

sPrompt)

I(

eont

inrre irscu' ce cue

1'utilisateur entre

une valeur valide

while (true)
I
t

ll denande une valeur


Console. Write ( "Entrez 'r
I

I Lit

1'utilisateur
+ sPrompt * " : t') ;

une valeur dcimale saisie au clavier


Console.Readline0 ;

string slnput :

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

dcirnale valide entre par 1'uti.lisateur

mValue;

'I

I/ I/

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") ;

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

nYear

= 1;

nYear (= mDuration; nYear**)

//
II

calcule 1a valeur du principal


plus f intert

decimal nlnterestPaid

* (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

]
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.

i(^$tlK
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

Entrez 1e taux d'intrt:


Entrez 1a dure:10

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

8-

214 .37

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

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

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

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

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

Example

{
nrrhl
puurlLin

rrnir{
vuru

<f
af in
LeLfL

f\rrf
ntr* llctrin^
vuLyuL
\DL!rtl

"n^(+ri-^'l
tutrLJL!rir/

Console.Writeline("Output0 a reu lrargument


+ funcString);

"

]
1

J'aurais pu invoquer cette fonction depuis la mme classe de Ia faon


suivante :
rrf nrrf f\ rlTJal
i
rrlfrv

^rr

)
/

.
t

Et j'aurais reu le mme mmorable rsultat


"+-"+t'l
uurpuL|./

a reu I1|^-^.'*^-+
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

string upperString =
Output (upperString)

"He11o";

Ghapitre 7:Mettre en marche quelques fonctions de grande

classe I

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

L'invo cation

Out

put

- rnnorStri nc
fr

upperString
:r Jrr il r9 =-\

copie la
valeur de
rrnnor(t.i
nr
-rr. _" -_ _.,t

Output

\,/
Y/

------>

"Hello"

(funcString)/

da ns

frnntr:

nc

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

+-l:,;\
I
tti
I
|

C'
,./

AverageAndDisplay

using

System;

namespace Exanple
t

nrrhlic elnss Classl


t

public static void Main(string[] args)


(

// accde 1a fonction menbre


AverageAndDisplay("UV 1", 3.5, ''lJV 2rr, 4,0)

//

attend confirmation de 1'utilisateur


Console.l,lriteLine("Appuyez sur Entre pour terminer..,")

Console.Read0;

l
I

I
/

AverageAndDisplay

- fait

1a moyenne de deux nombres associs

leur

nom et affiche 1e rsultat


public static void AverageAndDisplay(string s1, double d1,
string s2, double d2)
I

45

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 valeur est 3,5 et de


dont 1a valeur est 4 est ga1e 3.75

La moyenne de UV 1 dont
nnlrroz

crrr

Fntr6o

nnrrr

UV

1'orm'inor

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

',--l*^
uf lrB C,,^+^*.
)/ Lxl ,

namespace AverageWithComp
{

ilerError

n'c

Ghapitre 7: Mettre en marche quelques fonctions de grande

ctasse | 47

public class C1ass1


{

public static void t'lain(string

ll

args)

l/ accde la fonction membre


AverageAndDisplay("UV 1", rrUV 2",

3.5, 4.0)

/l

attend confirmation de 1'utilisateur


Console.firiteLine ("Appuyez sur Entre pour terniner.

..")

Console.Read0;
l
I

I
|

AverageAndDisplay

- fait

1a moyenne de deux nonbres associs

nom et affiche 1e rsultat


public static void AverageAndDisplay(string s1, double d1,
string s2, double d2)
I

leur

double dAverage

= (d1 + d2) I

2;

Console.l,lriteline("La noyenne de " * sl


t t' dont la valeur est " *

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
5>}qS,
il \
\gt/

-f

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

I nvE!acnllu!f,yrqjvvrrvqu9u
ore
lltvlr--montr
LLLE
version
vsrDrvrr
a-^"^-^ ^^ ^-rn"r snl rvOverl o,adpd - cette

/ /

1r fnnntinn
rurr
Lfv!r

Arrpr:snn,1T1.i.^l
rrvu!u;CnllUUfyroJ

ro

o-'

peut tre surcharge


rrqino
Srr<tpm'
*- -"b
nanespac

Ave

rageAndDisplayOver loaded

njthltn

crec<

{ le.gg]

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

public static void AverageAnriDisplayfstring s1, double


srring s2, double

d1,
d2)

doub:e dAverage

= (dl + d2) I

?.;

" T s1
* 'r dont 1a yaleur est " *

Console,WriteLine("La moyenne de

Console,lrlritel,ine("et de

d1);

+^1

"

t ",lorri:1a vaieur est " * d2


* " est gaie " + dAverage);
l
i
^"L1
^
PUVTTL

a+^+.i
n
LALaL

void AverageAndDispJ-ay(double di, doubie

double dAverase

: (Ci + d2) I

d2)

2:

" * d1
+d2
*"et"
* " est gale " + dAverage);

Console.hiriteLine ("La rnoyenrre de

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

r^ "^^ -^i-+^

^+ UC VU' PUrrlLb
L
r!o^ -^,,^--^
ruvJsrrlr
Annlt\to"

cltr

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.

.P\

En pratique, quand on parle de "l'utilisateur" d'une fonction, il s'agit

"Qt ;:iffili,'il:i:ilJr'"iit

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
/

- offre des variantes de la mme


fonction, certaines avec des arguments par
dfaut, en surchargeant 1e nom de 1a fonction

FunctionsWithDefaultArguments

| 50

Troisime partie : Programmation et objets

using

System;
nanespac e FunctionsWithDefaultArormpnts

public class C1ass1


t

public static void Main(string[] args)


{

/l

accde 1a fonction nenbre

. Writeline ( " { 0 } ", DisplayRoundedDec inal ( 1 2 . 34567 8i'{,


attend confirrnation de 1'utilisateur
Console. t^lriteLine ("Appuyez sur. Entre pour terminer , . . " ) ;

Console

ll

3)

Console.Read0;
l

//
I

DisplayRoundedDecimal

tl

- convertit

en chane une valeur


dcimale, avec le nombre spcifi
de chiffres significatifs

public static string DisplayRoundedDecimal(decimal

mValue,

int nNunberOfSignifieantDigits

//

//
tt

arrondir 1e nombre sur 1a base du nonbre


spcifi de chiffres significatifs

commence pas

decinal

mRoundedValue =
dec

imal . Round (nValue

nNunb erOfS

//

Convertit en chalne

string s *

ignif ic antDigits

le rsultat

obtenu
Convert.?oString(rnRoundedValue)

reurn s;
]

public static string DisplayRoundedDecinal(decirnal

mValue)

//
I

invoque DisplayRoundedDecimal(decinal,

int),

en spcifiant 1e nonbre de chiffres par dfaut

string s =

DisplayRoundedDecinal(mValue, 2)

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

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.

*fR.''

=oi

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

using

montre 1a smantique du passage par valeur

System;

namespace PassByValue
{

t5t

I5

Troisime partie : Programmation et

ob

jets

public class Classi


{

I I Update
II
I

..

- essaie

de modifier 1a valeur

des arguments qui

lui sort

public static void Update(int i,

passs

doubLe

C)

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,

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'nfrave

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

7: Mettre en marche quelques fonctions de grande classe I

Ghap itre

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.

//

_-"-ffi

@t

PassByReference
C.,^+ ^*
JLc$,

,, ^.: - ^
urrr

demonstrate pass by reference semantics

namespace PassByReference
(
L

public elass Classl


{
t

I
I

- essaie

de nrodifier 1a valeur
des argunents qui 1ui sont passs
public static void Update(ref int i, out double
I
I

Update

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

53

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
r

^--^r

L1 dPvL

^--:^

nyrs

d iinriate(ref
uyuoLs\rr
rrrLr

int. out
uuL uuuuItr,/,
double)

i = 10, d = 20
Appuyez

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

situation drerrur potentielle


appelle une fonction avec
des arguments passs par rfrence

montre une
quand on

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

int

nVar2)

Console.ltlriteline("La valeur initiale de nVarl est


nVarl = 10;
Console,l^Iriteline("La valeur

initiale de nVar2 est

nfar} = 20:
'I

public static void Main(string[] args)


{

dclare deux variables

et

1es

initialise

" I rrYqrr,/,
tr

I nvrlJ:

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

pas chang

Console.I,lritelineO;
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):

n=1
La valeur
La valeur

initiale de nVarl est


initiale de nVar2 est

Aprs 1'appe1 Update(ref

10

n, ref n):

n=20
nnrrrran
nyyuJ4

df\
=(D

arrr
u!

Fnfpf,6
lltL!

nnttr
yvur

torm"inar
L!llrrrl!

t!

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.

int

nVariable;

Console.l{riteline("ceci est une erreur " * nVariable) ;


nVariable = 1;
Console.l{riteline('rnais ceci n'en est pas Itn " * 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

Retourner une

classe I 57

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.

return ttour retourner

Utilser

une

hleur

L'exemple suivant montre une petite fonction qui retourne la moyenne


des arguments qui lui sont passs :
public class

Example

public static double Average(double d1, double

d2)

double dAverage

return

(d1 + d2)

z:

dAverage:

public static void

Test

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

v1

u' " * est "

I, vcrdctur,
^..::-i: ,'.',.,r v2));
]
]

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

A9L\

"(o)

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

, 'i i,::r-l:r i r clans la fonction

-",

senrble iclenticlue n'importe

quel autre appel de fonction. mais la valeur de type .


r1-.'.:i':.i;t: i , est stocke clans la variablet I
: :., , i,

,., , 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 :

t tt^)^+^
^^^^t^
uPuoLc - trDditr

It r

de nrodifier 1a valeur
des arguments qui 1ui sont passs
public static void Update(ref int i, out double
I

C)

f
!
d

rv
=

/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

public class

classe I

Example

public static void Average(out double dResults, double d1, double


{

dResults = (dt
]
nrrhf in ctatin

d2)

rrnid 'test

d2)

z;

()

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.

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.

frg)

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.

59

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

affiche un message d'erreur et...

Console , WriteLine
I

classe | 6l

"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

nValue;

I I cre une rfrence


Exanple refl;

ll

null refl

cre naintenant une rfrence un objet de valeur nu11e


ref2 = new 8xample0;

Example

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 -

nanespace

modules dp

test

norrr

rrti.ljser la bibliothque TestLibrary

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;

// passe naintenant la fonction une chaine vide


Console.Hriteline("Passage d'une chane vide :") ;
exanpleObject . TestString ( " ") ;

Console.Writeline0;

i I enfin, passage d,une vritable chane


Console.l,lriteline("passage d'une vritable chane :")
exanple0bject.TestString(',chane de test',) ;
Console.

ll

Irlriteline

attend confirmation de

ConsoLe.i,lriteline(,,Appuyez

-t
I

Console. nead
return 0;

()

l,utilisateur
sur Entre,pour terniner. . .,,)

Chapitre 7: Mettre en marche quetques fonctions de grande

nl acc

cfasse | 63

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 ;

// puisque tout va bien, affiche 1a chane


Console.iljriteLine("sTest se rfre : "' * sTest + rrIir)

'

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^

Appuyez

.
d..

vritable chaine
t+an1
LcL

qfrjnsl
u,!rlrb

sur Entre pour terminer.,.

t64

Troisime partie : Programmation et objets

La question de Ma i ii
a un pr0qramme

) : 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

s'agit

public static void Main(stringIJ


t
I

emplacement de

votre

args)
programme.

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

affiche
au

srrr5
".{-^

1es arguments

qui sont

passs

programme

(rra+^m '
pJ
Lu,

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

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 :

lc argl argZ
a 3 argunents

DisplayArgunents
Ce programne

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.

t65

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/.

L'invite devient

Utilisez le vtre si vous en avez choisi un autre)

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.
Si vous ne le

Figure7.2:
L'utilitaire de
recherc he

-de Windows
est une aide

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 |

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.

,;-;;,

-li:l

Fi,:hier Edrl:rm tfirhaqe Fs'/ils utrl5 |

''
Adresse IlJ

Flultill,l

;
1..

)
fi:f

$recf en:ter -jDcssierr J


r

t't

{". ,']l

:!

Re(her.her

t{ Noveru

- \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:]

,"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

lJ.

:r,t r,:r

Le voil !
Le nom du

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

la Figure 7.4.

doit rpondre en affichant les rsultats montrs par

67

| 68

Troisime partie : Programmation et objets

Figure 7.4

L'excution
de

Dlsplay

-Argunents
partir de
I invite du

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

lr;---;
I

i-r*n,i;;,*ntt

_)

t-t

[ro55rr

P,Fte de tra\.3i1

"&)

r-l-

F\t.rris iJEu

!,: -

Figure 7.5

Jirhrrre

drer:t iJl-[r,1

iJ

Ft!fJfr:.ll
trj,triL -'r
I ilr] t:t i'

+ 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;: ,--#

rt:

internt E:{plrr

Dans

Windows,
-vous p0uvez
excuter un

!j'."Dnarrr ,.]ullr,l

Elpfert

pr0gramme

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

-l

lirrL,r';r,-fir'r

4,'.

Windows,

f,m3frtsr utlok

Elpress

VOUS OOUVEZ

faire olisser
et dposer un

fichier sur un

''

programme
consote pour
dclencher

iorbeille

s0n

execuuon.
i

-l

]i

c_!l

:rnl'l. rFr ::

[,t::Ft:i'ir

]u1f

|:5.i5r:t

<trsFl: ,{r,:rnr,t:: ,:5[r ]r

At,6t13 qr'trfr1ill! ilr

[,r::t]3i qr lUfi{!:!: :.rj:

r::E

ri'lr

classe

169

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

l4e5 dc,:umPrrls

Figure 7.7
Faire glisser
des fichiers
sur le nom
:

'l- FTf.drl

,-l

Adr5ri I

,l

JFe,:f,er,:her -j!ur::,rer: .,!

)
l-r ,i

s:1
Pste d trd?il

d'un
programme

produit le
t+,

mme

l\.-.

rsultat que
si vous
l'aviez

excut
pa rtir de la
ligne de

lntrrr;t ExFlor?i

tk,

-'

Dmrrer 'Sutloal
ErFrBJ5

commande
en lui

passant les
noms des

- l

tsl

rlr :r,nl

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.

ste\
=(t

ot,iEtl

jl

5lilnnr,iJl

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,

Eichir Edition {fhchaqe

J'

__1

Elasst.cs

. .-:

,*:.i

r,E.ur

r.i:1rl lllrstErr,

rr!u:'!-,f

! r.

!a -.

,,,1 I r,-

'1
i'r

:J

r j -aiLr.U1rrr-::

:.=-

T:=r

; t) , r - :'i'ri

lr.:

IIalr:i:',

1Lr._il]

::{:

i/d r'r u ,{r t,5Eli. :r'trfrirr'1 i I

ttr rr: :
hltTffi
iPtSujtgllit*.ilJJli]llr
+ _J Flrerr:i
'Li. lenettt
tj] A:serrl'lr Ir,f :.:
l'leferef
{l - :ss1.':::
Ajlrllei

1t1i

-pro prits

rril!rlnt

= tl:

i:,:i1:lr'siIllr:1

l,rrler un reiererrce'{tt,
[:eftrrr,:rJnrnrt t,rc]el

ill

EJJ

3.

Erteqrst:rer f,'5F 3rAf lJf.nL5

Y. 1t'g

de,lerr;rt;le

let,lluf
-

.1r:f

d'un projet,
cliquez du
bouton droit

l:?l

,lji,uter rrre reIerentt

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

Figure 7,8 :
Pour
accder aux

Help

- i$

@re

sur son nom.

Pritet nref Qbaguer qutjls FeBtfe

net

lW.

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

.+,l-

?f
I

t,isFl

-. -a q

Classt,cs

jet

Ge*rer

Qbcguer

aa
.'

Help

' ,:a"* :

L1trlrl

Explorateur de soh:tions - Disp{,,.

:)

:'Ar',: rn'er't': Te:t:

!-0nfi,rurttiDn

F.h--rj-,tr-d'

Flate-forme

---:
8....

.,

tI-,i:,iF"

Figure 7,9
Dans le
champ

gutil! Feqh*

GestionnderonfiE:ratim:.,.

rl:,r.r

[]enr,rrt r l-lFL

F.rr lP d.,,r,,!,1P

A:F
A:,F.llET

Fil5E

':l:,rer e Jehr,tq

de la ligne de

,:tr/Ef l dEt,r!l,l,l

n:n

Fi

comma n0e
de la fentre
Pages de

:l:rr'f l d--b,tl.:,1 :l(,lL

rlr.'r l 'lEt'i,,t3tt

..,

a,

3ra,-tE
_ir

rer

Flse
s

F,l15

.l'

EtrG@EGIEE!E@/t'irlrlrrqil
FFerl:Ie J lfd'r]rl
T,:rllr:!rj rtlt]tSet InlfTl Erfr rlrE Ttita

proprits,

'Lr5Flni,Arqrrrnfrt5' l1 Frr,jel

,r:i;

-Arguments

entrez les
a rguments
du programme.

4 .X

.,..

[]nr,3r r r l',lF ir:

'***!

, Fr;tr*

-.,r -a)
fr :olrt'on

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 ,,,

f tr- -l

Annul?r

,,

, I tit

Fl

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

5. Cliquez sur OK, puis excutez le programme normalement

en

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

ttrtils

Feqte

Figure 7.10

classe | 73

Help

etu,1

Visual Studio
peut passer

-des argu-

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

()

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

Vous avez peut-tre remarqu que l'instructiofi

Console.Writeline("ce;:i est un appel de fonction");

I,'Iriteline ( ) est I'une des nombreuses


C#.

fonctions prdfinies offertes par l'environnement


fa console de l'application.

Console est une classe prdfinie qui se rfre

| 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

Tout ce que voit

r'*

* " et j'ai t'+ 3 + "ang.")

Writeline ( ) dans cet exemple

Dans une autre forme,

Writeline

est "Je m'appelle Sarah et j'ai 3 ans."

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

Pass0bjecr

montre comment passer un objet


une fonction

rrcino Sv<tom'
nmsDc PassOhr'ect

i
nrrl^rlic r. lnss Strrdent
{

pubiic string

sName;

l
nl.hlr^
yuurrL

t'-SS1
uf,d.

^ ^^n
Lad'

public static void Main(string[] args)


{

Student student = new Student 0

// dfinit

1e nom en

Console.ldriteLine("La premire
student, sName
OutputName
I

accdant directement

fois :")

= "Madeleine";

(student)

utilisant une fonction


l,IriteLine ( "Aprs avoir t modifi

change 1e nom en

Console

SetName

(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

est

{0} "

student. sNane)

/l SetName nrodifie le

non de

1'objet tudiant

public static void SetNane(Student student, string

sName)

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

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

using

des fonctions nenbres statiques

pour manipuler des


System;

nanespace Pass0b j ectToMemberFunction

I'obiet

champs

t77

| 78

Troisime partie : Programmation et objets

public class

Student

public string

/l

OutputNane

sliame;

- affiche 1e non de 1'tudiant

public static void 0utputName(Student student)


t

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

non de

1'objet

SetName(Student

student

student, string

sName)

student,sNane = sNane:

l
l
-..11.:
^1^-PUUITL^ LrA

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";

Student, OutputNane ( student) ;


I I ehange 1e non en utilisant une

fonction
Console.l,lriteline("Aprs avoir t nodifi :")
Student.SetName(student, "l.Jil1a")
Student . OutputName ( student )

attend, confirmation de 1'utilisateur


Console.]rfriteLine("Appuyez sur Entre pour terniner. . . ")
Console.Read0;
I

]
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

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

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

t79

I80

Troisime partie : Programmation et

public void SetName(string


I

sFirstNane
slastName

:
:

jets

sFNane,

string

slName)

sFNane;

slNane:

//
II

ob

ToNameString

- converti en chane pour affichage


1'obiet

student

public string toNameString0


{

string s = sFirstNane

tr rr + slastName;

return s;
)

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.\'lriteline("Appuyez sur Entre pour terminer., .")

Console,Read0;
l
1

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

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.

:712
lfdll
lL-r^t
\Zl

Le deuxime appel met jour I'objet sarah.

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.

| 8l

| 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

i,.

r.

l:ss T,etter

string

sAddress;

//rnet de ct 1'adresse
publi.c void Address(string

sNewAddress)

sAddress

= sNevAddress;

l
l

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

AP\
\tt

=l ,,1
-t'

\
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

Student

llle

nom de
jn ctrinc
nrrhl
L!rrr
yuurrL
.in
nrrh'l
yuUlaL

//

1'tudiant dcrit 1'obiet student


.F'i-.+\1-*^.

!t!DLL\dUlY,

clrinc
L!ilr

SetName

^T^a+trI^-^.
IrL.lIdllltj'

met de ct

le

public void SetName(string

non de 1'tudiant
sFName,

string

slName)

sFirstName = sFNane;

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") ;

I 83

|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'ir! r"ctNrmo'
D Llrell
PUVTTL L! f rr r
-,.1^1.i
^T
^
^+-.:-^
^^+[1^-^.
LtArr
!ALrrdrl,
PUUaJL

public void SetName(string sFirstName, strlng

slastNarne)

SetFirstName ( sFirstName)
SetlastName ( slastName) ;

public void SetFirstNane(string

sName)

sFirstNane =

sNane i

public void SetlastName(string

sName)

slastNane =

sName;

l
]

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

#\

=(d

this est un mot-cl, et ne doit

8: Mthodes de classe

clonc tre utilis pour rien d'autre.

On pourrait donc crire I'exemple prcdent de la faon suivante


public class

Student

t
yswLLw
^,,L1i^
ntrhl in
yss4+s

a+r.i-^
aD"i *-+1^*^.
o u. rrr$ sFirstNarne
nT aa+1\T^n^.
rei-rinc'
L. rrri !d.
Lllclllle ,

public void SetName(string sFirstName, string

slastName)

ll rfrence expliciterent "1'objet courant" rfrencs par this


this. SetFirstNane (sFirstName) ;
this . SetLastNane (slastName) ;
]

public void SetFirstName(string

sName)

this.sFirstName =

l
public void

SetlastNane

this.

sName;

slastName

(string

sNane)

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

Address

class

- dfinit

un "cadre de base" pour une adresse aux

Person

public string

sName;

USA

| 85

| 86

Troisime partie : Programmation et objets

yuuf

rL

nrrhli^
yu!arL

rll

rltu

r'n'izl
vvlu

Tni+(o1r'i
IllaL
\L!arr6 1g

s1\ame,

r-nr ntu/
.

Yh\

t
L

this.

sName

this . nID =

sName;

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
/

conment

n'i t lrr - n
nr
montre
-- riogramme
utiliser explicitement 1a rfrence this
'i

rrs'ino Svctpm'
namespace Ref erenc ingTh j.sExplic

itly

public class

C1ass1

public static int Main(string[] strings)


(

I lcre un objet student


Student student = new Student ( ) ;
student. Init("Stephen Davis", L234)
//inscrit 1'tudiant un cours

Console. ItlriteLine
.

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


lttt

!^'rr\

student. DisplayCourse

//

attend confirmation de 1'utilisateur


Idriteline ( "Appuyez sur Entre pour terminer.

Console.

Console.ReadO;

return

0;

..")

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


Courselnstance courselnstance

ll Init - initialise 1'objet student


public void Init(string sName, int nID)
{

this.sNane =

sName;

this.nID : nID;

courselnstance = nul1;
]

/l Enro1l - inscrit 1'tudiant courant un


public void Enro1l(string sCourseID)

cours

courselnstance = new Courselnstance 0

Init (this ,

courselnstance.

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

//
II

Courselnstance

associe 1'tudiant au cours


auquel i1 est inscrit

n^,.rselnstance
-..L1.r^
^1 ^^- VVU
PUUTTL LID
{

oublic Student student;


public string sCourselD;
I

I tnit - tablit le lien

entre l'tudiant et 1e cours

public void Init(Student student, string


i

this. student = student;


this. sCourseID = sCourseD;
]

Display - affiche f
public void Display0

//

intitul

Console.l,lriteline (sCourseID)
l
l
tt

du cours

sCourseID)

| 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 :

- mlanger des fonctions de classe et


des mthodes d'objet peut causer des problnes

//
II

l'tixingfunctionsAndl'lethods
C,,^+^*.
y
Lctrl,

',^i-^
ufrr6

nane space MixingFunct ionsAndMethods


I

t
nrrb'1

ic

cl ass Strrdent

public string sFirstName;


public string slastNane;

// lnitstudent - initialise 1'objet

student

public void InitStudent(string sFirstName, string

slastName)

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

I Console.l,/riteLine(? quel objet student utilisons-nous ?);

public void

OutputBannerAndNarre

()

// 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)


{

ll iei,1'objet student est rfrenc explicitement


(
"Le non de 1'tudiant est {01 "

Console.l.]riteLine

student . ToNameStrins

))

// ToNaneString - va chercher 1e
n,rhl i n ctri-1g ToNameString 0

nom

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

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

nom

student . OutputBannerAndName i ) ;
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.

189

| 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

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

de saisie

-automatique
de Visual
Studio est
une aide

prcieuse
pour choisir
la bonne

r
:it llrlE 851 ,-rrf
r:

,l

"' n"rd
:,ti:r,t i Ftldlrrr
tti-=,:' Q F f rrr-eE,l!Jl5
C :letErr':t
O:,eLIn

e ili 'rl
Q'.\rit:.:

Entr.:

t rlr 1llf

. ''

I
r
I
T

-&

mthode.

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

t9t

| 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

14,tt,.
-'lr
-+

lenrer Qeboguer q,!til: FanLre

&*&q4

r,ebu,,l
)rplorteur dE scdulfrns - t4ixin..,

lt!llr:,rn,tF:nttr'rr:nlt4elh:,l5., lii:1

Figure 8.2

$e{p

1&.-

tr _

"|'

Elassl.cs+

Efqjt

a"

;l

:l

tlr,trn,i:trn,'t[] ar't:t

{:J

$
-

Ji

5,rlrrl:rr'r'll irr'tFrrr:i.,rsAr'llrElh:,15

VixinqfunctronAndMethods

La fonction

de saisie

-utomatiq ue
a

affiche aussi
la liste des
arguments

. I j I I - .'rrl ir'rs:le

pour la
version de
votre choix

'JlrtrteLirr

istring format,

t,.3f.jnt5

,tt,je':f[] :rl1l

de la

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

rtcnter

toriln

l--, ,,, .
..' + 4,
'l'-,

t'

'

lrr,1e!

Qenrer Qboguer Qutils Fe8tre

&
ii4:{
,ii.t

rl-lw

flasst.cs*

{fFichage

'

"&'&

'P -

1*:

oetu!
Erploraleur ,je ,:lutions -

l"liin.,. +

.v.

;l

Figure 8.3

Help

l.& -

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

A chaque
tape, Visual

-Studio

affiche en
gras la
desc ription
du prochain
argument

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 ;

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

str, cette aide est disponible pour toutes les rnthodes


intgres de la bibliothque standard C# utilises par votre programme.

saisir. Bien

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.

| 93

t94

Troisime partie : Programmation et obiets

" .::'
Pfrlel

$nrer

*7*

Qeboguer

Qutils

Fe1he

Help

,"6 "& -

,tr

v, tel:tt_t
EaplorterJr desdulirn:: -

;i

ll:rnsrrrr':[]

:,;:'

:J

*j
,:l

)*"

lL'li{rr.,.

4 rl

:',: :| rr'l,lr inqF!fr :ir : n:r|Jl lEi:frt,,J:'


- -@HixinqFunctionsAndMethodt
|

rE

lrrilLE:

1ll :;errl,l,'irri: ::
r_!l d;s:1,cr

arlt:

-l:'

:Li::-

Figure 8.4
La saisle
automatique
est galement
disponible

1,r,1rl

9
4

eEl,lfir, rr;

DOUT VOS

pr0pres

^r\|'
,-f,

t\\vl

\z

E,tr:ij
,lHi::l-',ri
,.eL fr
ir t;t uleri

/::Lt:ll,llriE
S li,fjtrn:,1:rrr,l
e T,t5lrrr,l

,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

n hiet

Editin

':.:,..

4a:,,

;t,

',J':"1 :

,t,+J
flassl.cs*
I

Figure 8.5

ti lii, ir,QFur rlEr,srdlfethi'd:

'-l ls:r

-{
-t

!;il}:

I1- !:'Ii::r:: : In:rS

I'rrif i :.i:t ii

de saisie
a

E) ,liil.3lpur

'le sDlrjhns -

Mr{n,,, 4

]'-:r'lutrrr, l'' rr,lFun,:lDn:Andtiel:h'rd::'


- -@HixrngrunctronsAndMethods

La fonction

- utom atiq

classe |

de Visual
Studio donne
bea ucoup

d'information, pour les

r..,tr,:l lliitlt i-:lr1tr,.r[]

aL!{;rl

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

ir,rr-rr 1 E,uu1.

:i i

( ur:ie

i,,utt,ult8ar,r,er

ll'ttulea

i,'

trt

rtl I ix i il,J,l trrl :f

Jerlf

mthodes
d'o bjet

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.

95

t96

Troisime partie: Programmation et objets

1t$!Qa^ Pour tre honnte, c'est le langage Java qui a introduit cette ide.

Java

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.

Tableau 8.1 : Balises communes des commentaires de documentation.


Balise

Signification

<pa ra m></pa ra m>

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.

<su mma ry></summ

ry>

<returns></returns>

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.

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.

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.

4i7q \

'Qg,

L'exemple suivant est une version commente du programme


Mixj-ngFunctionsAndl"{ethod s :

//
II

I'li"xi.ngFunctionsAndMethods

mlanger des fonctions de cl"asse et des


d'objet peut causer des problnes

mthodes

using

System;

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

double dSun = 0.0;

for (int i = 0; i (

students.Length; i++)

dSum

*=

students

Ii]

dGPA;

]
rlnrrhlo
uvuure

Tarnfh.
dAtro
unv6 = /(116/o+rr,.lanfa
u!ud/ LUUstlLD,!slrLrl

/ 1 nrrtnrrt

fho

error age
--

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

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

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

num

rfrences des objets de la

Studenl[] students =

gs\

e,

S/

/-.)

nerr/ St.udentfnu:rl;

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 (

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

',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

using Systen;
public class Classl
t

sraric int Main(string[]

-.,L1.i^
puorr.c ^+-+

args)

//
II

commence

que

par

lire

1e nonbre de types double

1'utilisateur a f intention d'entrer

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)

calculer

: ");

Chapitre

I : Mthodes de classe I 97

(lsunnary)

ll/

uublic class

Student

| | / (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

/l

slastName;

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)
II

l
/

this. sFirstNane : sFirstNane;


this. slastName = slastNane;

/ n,.+-,,+n
LPU Llclllls!

I M

(orr^ rr.r)

lll

lll
II

affi.che f introduction

a.tiche une bannire avant d'afficher les nons des tudiants

(lswnary)

nrrhf i n ctati

n 1ri 11 a)lltnlrtRennor
s usrrr.!

()
\ /

Console,l,lriteLine("Regardez comme je suis malin :") ;


// Console.IdriteLine(? quel objet student utilisons-nous ?);
]
/

0utnrrtBannerAndNane

lll
lll

affiehe une bannire suivie par

nrrh l

Gunnary)

lll <l*r*m"rrr)
i

//

e voi

11

0rrtnutBannerAndNane

non de

1'objet student courant

c'est la classe Student qui est

p<t nnss
rrtnrrtRannpr f l '
/ / I 'nhict

ll
//

le

'l

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

/l affiche la bannire et le
0

Student. OutputBanner

string s =

nom

Student.0utputNanre(student,

5);

Console.I^IriteLine0;

// affiche nouveau

1a bannire
student . OutputBannerAndName ( ) ;
I

et

1e

nom

I attend eonfirmation de 1'utilisateur

Console

Writeline ( "Appuyez sur Entre pour terminer


0;

il\,

Console,Read

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.

nnref

EE80n

i:'; ;.,::,.. t

ffiirha,1e
;,:!?

it.l
tlassl,c;*
/lt"t
|

1.

Piet

"..

enrer

&*&

Qbrguer
n&. /6

Sutili Fettre

'tr -

:tt't:t

Lebu'l

nlfrr :lr.nrr llnpffri'i

-.:srl

| tt:lnstrrnl[]arqsi

:l

pr0gramme
XML,

de bien
mieux

E,rurls

C
E

uLF|JLEnrnr

FeiErEn,:Eluls

dcrire la
fonction
et ses
a rguments.

,t

IJ

-document
EN

/s,rmnrar_y'),

Eeh

Avec un

Visual Studio
est capable

'

-J
:J

Figure 8.6

-'.tri,- ' rl r,tts x'-r ', .a il'--f


.Hixinqrun(tionrAndMethods
+ jl ifEr,rt
(-| tinrL,t'lni ,:r
rjf Clr'l.cs

l:

199

200

Troisime partie : Programmation et objets

2.

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.

3.

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.

^"tFJk

=Qg,

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

tZX
It9r,
L-,{t

I
-

Vous pouvez aussi accder aux proprits clu projet en slectionnant


Projet/Proprits.

,-t

-.

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

est trait comme une classe

string s1 * nev String0;


string g2 = "abcd";

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).

.t9!I{0?r

7^H\
=(,l\y /
\/

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#

Voyez cet exernple simple

- les m+hodes fottrnies

,.
^
^
II I| -.MotifrrS+rinn

nar la claSSe

String ne modifient pas l'objet

I
II
II
ll
I

lui-mme (s.ToUpper| ne nodifie pas s,


mais retourne une nouvelle chaine

qui a t convertj.e)

'.^.i-^
uarr

C.'^+^!jLslu,

'

namespace Example

i
LfdS

urdnl

I
t

public static void Main(stringIJ args)


{

ll

objet student
si = new Student0;
s1. sName = "Jenny";
I I cre maintenant un nouvel objet
ct.e un

Student

Student s2 = new Student 0


92. sName

= sl.

avec

le

nme nom

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

Console.l^Iriteline("sl - [0], s2 - {11",


s1. sNane,
s2. sName)
I

I attend confirmation de 1'utilisateur

Console.l^IriteLine("Appuyez sur Entre pour terminer., .")

Console.ReadO;
'I

ll Student class

nous avons besoi-n d'une classe eontenant une chane

Student

I(

public String

sName;

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.

203

204

Troisime partie:Programmation et objers

La sortie de ce l)rogramme est fort simple

sl'Jenny
Appuyez

s2 -

JENNY

sur Entre pour terminer...

19!llQp" L'invariabilit des chalnes est galentent importante pour les constantes de

7^[ \

V /
\-l

=(f

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.

type

s Lr

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

Si la chalrte de gauche est suprieure la chalne de clroite, iJriilpar e ()

retourne

tl

.Si

la chane de gauche est infrieure la ctra-re de droite, L)oni;:rr c ()

retourne

t/

1.

-1.

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#

I I est plus longue, alors e11e est plus grande


si s1 contient encore des caractres

return

return

-1

I si s2 est plus longue, alors e11e est plus grande


si s2 contient encore des caractres
I

I si tous les caractres correspondnt et si les deux chaines


l l ont 1a mnre longueur, alors e11es sont "gales"
I

return

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 :

Srd$STa itl/ nuitalsentence - 1e progranrne

La- L-|ffi
r I r,in

ffil|
-

t
ll
ll
II
II

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

I
L

publi.c class

C1ass1

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;

205

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;
]

// sinon, ajoute 1a phrase 1a chane saisie


: String.Concat(sSentence, sline) ;
I I dit 1'utilisateur o i-l en esr

sSentence

Console.WriteLine("\nVous avez entr : {01", ssentence)

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

"QIJTT"
r

conpare 1a chane entre chacune


des chanes de fin licites

It

qUit,, l

foreach(string sTerm

in

sTerms)

/1 retourn true si

if

1es deux chanes sont ga1es


(String.Compare(source, sTerm) == 0)

return true;
)

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#

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'

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

()

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.
La mthode

4f\
=)

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

.a

jL'itrationSuruntableauestuntrsbonmoyendetestersiunevariable

ff-^l

lforl
l-t

correspond une valeur parmi plusieurs.

207

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

Vous avez

entr :

Progranrner avec

C/l

Programmer avec

C1,

qw4r

Entrez une chane

, crest

amusant

Vous avez

entr :

c'est

amusant

Entrez une chane

(plus ou
Vous avez

uroins)

entr ;Programner avec Cll, crest amusant (p1us ou

moins)

Entrez une chane


EXIT

Phrrso

nnmnT

tp

Programner avec
Annrrrroz
v!

[yHeJ

crrr

Cil, c'est amusant (p1us ou noins)

F'nfro

nnnr

forminar

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.

//

Iserminatestring - retourne true si 1a chane source string


ga1e l'une des chaines de fin

ll est

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

is

equal

Chapitre

I : Jouer avec des chanes en C#

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

-l-

a- '-i
bW-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 )

IsTerminateString - retourne true si 1a chane source


est ga1e l'une des chanes de fin

//
II

public static bool TsTerrninateString(string

source)

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.

209

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

accde aux caractres d'une chalne


coffne si 1a chane tait un tableau

^
StrlngloUnarAccess

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

:" f

sRandon);

// affiche maintenant sous forme de suite de caractres


l,Irite ( "Votre saisie affiche en utilisant foreach
foreach(char c in sRandom)
ConsoLe.
T

:"

Ghapitre 9 : Jouer avec des chanes en C#

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

Console.l'irite(rrVotre saisie affiche en

for(int i = 0; i (

utilisant for :t');

sRandom.Length; i++)

tr

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

Votre saisie

conme

: au hasard)

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.

2n

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 ( ).

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).
To I

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.

Atry(

=(\r'r
^'

\?_/

\/

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#

// IsAllDigits - retourne true si tous les caractres


sont des chiffres
II
public static bool IsAllDigits(string
t

de

la

chane

sRar^r)

// 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

//

effectue une boucle sur 1a chaine


index = 0; index ( s.Length; index**)

for(int
i

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)

chane

return false;
l

l
I

tous 1es caractres sont des chiffres, 1a chane doit tre un

nombre

return true:
l

IsAllDigirs O commence par supprimer tout caractre

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

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(
.

o$'ARGFf,
$'l, t- T__^1
La.

t]

!!t rsAllDigits
t-t1 1ni-ir -

using

).

1!^-- dmonstration
de 1a mthode IsAllDigits

Systemt

namespace Exanple
t

class Classl
{

public static
{

int Main(string[]

args)

2|3

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

(!

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#

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

Pr(sn1rnnpWithSnlir - 'l it ripg Srie de nOmbres


spars par des virgules, les transforme en

ttlI
II
II

nombres

nanespac

entiers. et en affiche 1a somne

e ParseSequencelllithSplit

using
class

System;
C1ass1

public static

int

Main(string[1 args)

1'utilisateur de saisir une srie de nornbres


illriteLine (
'rEntrez une srie de nombres spars par des virgules"

//

derrande

Console

I lit

):
une

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

(saute tout segment vide)


(r.T,eneth ) 0)

ll
if
{

sawe les chanes qui ne sont pas des nombres

(IsAllDieits

s)

2l 5

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
= {0}", nSun) ;
I attend confirmation de 1'utilisateur

Console.WriteLine("Sonne
I

Console.WriteLine("Appuyez sur Entre pour terminer.


Console.

return

Read

..")

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

que 1a chane

return false;
l
]
I

I tous 1es caractres sont des chiffres, c'est

rt1trr)

sans doute un nombre

l-rt1.

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#

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

7J,A

Q'

Nouveau
Nouveau
Nouveau
Nouveau
Sonme

Appuyez

nombre

nonbre =
nonbre =
nonbre =

1
2

10

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

HDansunprogrammedestinunusagevritable,vousnevoudreZSanS

l[Orf
Y

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

2t7

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 :

'odsg9

,,1{J\,-

|[-T''?
l*

//

justifie

Ali8n0urput

gauche et aligne un ensenble

de chanes pour enbellir


nnespace Align0utput

la sortie

du programme

{
,'-.i-^
urrr5

Q.'a+a*
9JLclu,

'

class C1ass1
{

public static int Main(string[] args)


t

strinsil

Y_

+..b

!J

nanes

= ["Christa ",
t' Sarah" ,
"Jonathan"

,rsant,,

" 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

foreach(string s in

Liff ranfocrr).
Lvu
/

vr!

'

nanes)

Console.lllriteline("Ceci est Le non

'{0J' initia1", s);

Console

I^lriteline ( ) ;

// nodifie maintenant 1es chanes,


/i justifies gauche et qu'elles

de rnanire qu'e1Ies soient


nme longueur

aient toute la

strlngIJ sAlignedNames = TrinndPad(names)


// affiche enfin 1es chaines modifies,
ll irstifips

pt sl'ion6oq

Console..|,trriteline("Voici 1es

foreach(string s

mmes noms"

"affichS SUr 1a baSe de 1a nme lono'rorrrrr)'

in

sAlignedNames)

Chapitre 9: Jouer avec des chanes en C#

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;

//
I
|/
II

lrim.qndPad

- partir d'un tableau de chanes, supprine


les

espaces chaque

extrnit,

puis

insre 1es espaces ncessaires pour les aligner


toutes sur la plus longue
public sttj.c string[] trirnlndPad(string[] strings)

//
|

copie l-e tableau source dans un tableau


rye vous pourrez manipuler
stringll stringsloAlign = new StringIstrings.Length]
I

l/
//

conrrence

par suppriner 1es espaces inutiles

chaque

extrmj.t de chaque non

for(int i = 0; i ( stringsToAlign.Length;

i++)

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 )

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

Ii]

stringsToAiign[i] .PadRight(nMaxlength + l)
l

//

retourne 1e rsultat 1a fonction appelante

return stringsToAlign
]

2|I

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

est le

est
est

est
est

nom
le nom
1e nom
1e nom
le nom

'Christa
'Sarah
'Jonathan
'Sam

'
'
'
'
'Hildegarde '

la

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#

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 :
o = ttI'\anor

vqrlbe

cf rino
L!rrl

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

System;

public class

C1ass1

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

22 |

222

Troisime partie : Programmation et objets

I affiche 1a chane sans les caractres blancs


Console.WriteLine("aprs :" I
I

RemoveSpeci"al0hars

(s,

cWhiteSpace) )

attend confirmation de 1'utilisateur


Console,WriteLine("Appuyez sur Entre pour terminer. ..")
/

Console.Read0;

return

0;

//
I

nrhl

jn

srrnnlimp dp 1a chane toute


occurrence du caractre spcifi
cfefi n ctri.lg RemoveSpecialChars (string slnput,

RemovpSnpcialChnrs

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)

boucle

break;
]

// divise la chane en la partie qui prcde


I I Ie caractre et la partie qui 1e suit
qtr.i no cRpforo : vuLyuL.UuUDLrIrri\v,
f ca+') .
c0rrtnrrt Srrhctrjnn /0 nf
lMrDcL/t
-+-i^- snl
^^r+^c011tn11t JuuLlr116\r.v!rrLL
Srrhctrino (nflf f <pt *' !)I)
= vuLyuL,
LI

bLrlil

I
//

'

runit maintenant 1es deux sous-chanes et le


caractre manquant entre 1es deux
s0utput = String.Concat(sBefore, sAfter) ;
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#

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

-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

Le programme Femo-,,ei,ihir,eSpar

chane

initiale : ceci est

une

chane

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

- supprine

de 1a ehane toute occurrence


du caractre spcifi
*..L1.:^
^+^+)^
^+-)
puDlr_c
srrlng
RenoveSpecialChars (string slnput,
srarj-c
/

KenoveSpeclalunars

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

223

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

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
La classe

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#

Tableau 9.1 :Gontrles de mise en forme utilisant

Contrle
C

- monnaie

Rsultat

Exemple
{0:C} avec

123,456

123,45

String.Format O.

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

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

123

456,19

Ajoute le sparateur de milliers (dpend


du paramtre de localisation) et arrondit
au centime le plus proche.

123456.789

123

456,8

Contrle le nombre de chiffres aprs la


virgule.

{0:N1}

457
X
OxFF
- hexadcimal {0:X}
012,30
{0:000.00} 12,3
{0:0...}
{0:###.##}12,3 12,3
{0:#...}
{0:N0}

123456.789

123

ldem.
0xFF est gal 255.

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,0

Un signe # impose un espace, et un 0


impose l'affichage d'un chiffre, mme si

celui-ci est
{0:# or

0%}

{0:#00.#%l

,1234

12,3%

Le % affiche le nombre sous forme de


pourcentage (multiplie par 100 et ajoute

le signe %).

{0:#00.#%]10234

02,3o/o

0.

225

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 :
1I

a
{Jlrtn11th'rm,atn^-+-^1
vuLyuLrv!llraLvvrlL!vr

t/ t

I
II

^ - ^^er^+
yrnrL
1'UtiliSateUr de redfinir 1e fornat
des nonbres saisis en utilisant l'excution
divers codes de contrle de format

nanespace OutputFornatControls
t

using

System;

public class

C1ass1

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

| . ' I
\ r , /

1^r
rv!

//
I
II

commence

par lire un nobre,

et se termine lorsque 1'utilisateur n'entre rien


qu'une ligne blanche

Console.lilriteline("Entrez un nombre de type double");

string

if

sNumber

Console.Readline0

(sNumber.Length

0)

break;
I
J

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

"

sFornatString. Split separator) ;


effectue une boucle sur les codes de contrle 1'un aprs 1'autre
(

/l

foreach(string s in sormats)
t

if

(s.Length !=

0)

{
I

cre une co$mande de format complte

Chapitre 9 : Jouer avec des chanes en G#

// 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)

227

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

Entrez 1es codes de contr1e spars par un

c E F1 N0
La

espace

0000000.00000

commande

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

commande

de fornat {0:N0l donne

La

commande

de format {0:0000000.000001 donne 0012345,67g90

IZ

346

Entrez un nombre de type double


,L2345

Entrez 1es codes de contr1e spars par un

espace

00.0%

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/

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.

t/

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.

t/

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.

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

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.

237

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

en

utilisant une variable


le solde du compte

de type double pour stocker

le solde dans une variable prive

(conserve

pour masquer son inplmentation


monde

au

extrieur)

C"^+^*.
9J
D L!r,

u9rrr6
"^i-^

nanespce DoubleBankAccount
{
nrrhra^
yuurru

t'^SS1
vl

Lra
^rdd

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
//

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

Console.liriteli-ne("Au cas o vous arriveriez jusqu'ici"

* "\nCe qui suit est cens produire"


* I'une erreur 1a compilation");

t= 10
// attend confirmation de 1'utilisateur

ba. dBalanee

Console.l'IriteLine("Appuyez sur Entre pour terminer,..")


Console,Read

ll

Banknccount

nrrhl
{

ie class

- dfinit

une classe

qui reprsente un compte

siurple

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;

//

GetBalance

- retourne 1e solde

courant

public double GetBalance0


i

return

dBalance;

//

AccountNumber

public

int

GetAccountNumber0

return

nAccountNunber

public void SetAccountNumber(int

nAccountNumber)

this.nAccountNumber = nAccountNumber
l

I/ I/ n^-^^'r+
r,/trPUlL

LUUL dnt
uyuL nositif
- +^!it
PvorLr!

public void Deposit (double

est

autoris

dAmount)

if

(dAnount

0.0)

dBalance

*=

dAmount;

//
I!

Withdrar,{

- tout retrait est autoris jusqu' la valeur


du solde ; retourne 1e nontant retir

public double l,lithdrar,r(double dWithdrawal)

24

242

Ouatrime partie : [a programmation oriente objet

(dBalance (= dWithdrawal)

if
{

dWithdrawal = dBalance;

l
dBalance -= dWithdrawal
return dWithdrawal;

/i

GetStrins
-- *..
-_,-

retourne dans une chane les informations


srrr
lpr.onnt
s Lrvrr
uL
4c
LvxlP L

public string GetString0


t

string s = String.Fornat("/{Ol = {t:C}",


GetAccountt{umber

GetBalance 0 )
ratll

rn

]
]

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

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

Je ne sais pas pourquoi

problme.

{q./
n)il
lS/
Y

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

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/

Un membre

t/

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).

t/

Un membre

public est accessible par toutes

internal protected

les classes du programme.

est accessible par la classe dans

laquelle il est dclar et toutes ses sous-classes, ainsi que par les
classes du mme module.

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.

43

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.

t/

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.

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

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


Si vous examinez plus

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

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.

245

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

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

nqnocn:no

extrieur)

Toct

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

double dAddition = 0.002;


Console,!,IriteLine("Ajout de {0:C}", dAddition)
.i
hn - Dpnosi t { rlAddition) ;
// solde rsultant
Console,Writeline{"Compte rsultant = {0}",

\s:'su4

ba.GetString0);

ll

attend confirmation de L'utilisateur


Console,Writeline("Appuyez sur Entre pour terminer.
Console.Read0;

return

0:

rr

I'

Ghapitre 11 : Rendre une classe responsable

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

Ajout de 0,00 F
Compte rsultant
nnrrrraz
nPyuJ

arrr
u!

1'n+ro
lltL!cc

#tOOt
nnrrr
PUur

123,45

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

//
II

Dec

lrnalBankAccount suivant

DecimalBankAccount

- cre un compte bancaire

en

utilisant

une

variable decinal pour stocker 1e solde du conpte

ttsino Svstom'
nanespace

Dec

irralBankAccount

public class

C1ass1

-,,r"1.i^
void
VUaU
LdLrL
PUUafL ^+^+)^

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)

247

2 48

Ouatrime partie : [a programmation oriente objet

ba. Deposit (dDeposit )

.."1dp drr ..\mnt

ll

= {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

(dAddition)

I solde rsultant

Console,I^lriteLine("Compte rsultant

: [0]",

ba.GetString0):

// attend confirmation de 1'utilisateur


Console.WriteLine("Appuyez sur Entre pour terminer.,.")
Console. Read 0

//

BankRccount

public class

- dfinit

une classe

qui reprsente un compte

simple

BankAccount

private static i.nt nNextAccountNumber =


private int nAccountNumber;

//

conserve 1e solde dans une seule vari.able de tvoe decirnal

private decimal
I

1000;

nBalance;

I tnit - initialise le compte avec le prochain


et un solde de 0
1 .:

-.,1"
yuuf

fllrLUo
r^ i*!rnkACcoUnt 0
^ ,.^:
vvru

rL

nAccountNunber = **nNextAccountNumber
mBalance = 0;

GetBalance

- retourne le solde courant

public double GetBalance0


{

return

(double)nBalance

//

AccountNumber

public

int

GetAccountNumber

return nAccountNumber
l
public void SetAccountNumber(int
;

nAccountNumber)

this . nAccountNumber =

nAccountNumber

// Denosit - 1'orr dnt nositif

public void Deposit(double


{

if

(dAnount

0.0)

esr autoris

dAmount)

numro de compte

Chapitre 11 : Rendre une classe resp0nsable

//
//

arrondit la variable double au centime le plus proche


d'effectuer 1e dpt

decimal
mTemp

mTemp

avnt

(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

public decimal Withdraw(decimal

dWithdrar,val)

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 =

{1:C1",

GetAccountNunber ( )
GetBalance 0 ) ;
rtlrrn

a.

]
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


Dpt de 123,45

conpte - lltoot = r23,45


Ajout de 0,00 F

Conpte rsultant = #toot


Appuyez

= r23,45 E

sur Entre pour terniner.

2 49

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,

Les moclifications internes une classe ncessitent quand mme toujours


diverses portions de code, mme si celles-ci n'ont pas

n,;iilL::.'"""u

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

int

AccountNumber

set Ireturn nAccountNumber;


--_..------,,J
set {nAccountNunber = value: }

Chapitre 11 : Rendre une classe responsable

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

(double)mBalance

l
]

I'utilisation, ces proprits apparaissent de la faon suivante


BankAccount ba

//

stocke 1a proprit numro de compte

ba.AccountNumber
I

new BankAccount

1001

I tit les deux proprits

Console.writeLine("lf[0] = {l :c}",
ba.AccountNumber, ba.Balance)

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.
<$-_--

xParconvention(cen'estpaSuneobligationdeC#),lenomd'uneproprit

Ilof
l
L-.t

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

5I

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

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

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-lll

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

53

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

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

== null)

(
L

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

MyObject

internal int n;
internal MyObject next0bject

l
l

Ce programme dfinit une classe MyOb i

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

produit la sortie suivante

1oca10bject,n est

f1^^^1^L.'^^+
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

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)

BankAccount ba

l
public class

= new BankAccount O ;

BankAccount

II
/i

Ies numros de compte coruencent 1000


squentiellement partir de 1

static int

augmentent

nNextAccountNunber = 1000:
'l p
nrrmrn rlo nnmnf o at 'l o cn1rl o nnrrr nhrnrro nhipt

| | nat inrrr

int

et

nAccountNumber:

double dBalance:

25

256

Ouatrime partie : La programmation oriente objet

/ constructeur BankAccount

public

BankAccount

()

nAccountNunber

dBalance

: 0.0;

= *tnNextAccountNumber

l
-^-L-^^
llElllu!cD.

duL!c

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/

Le constructeur porte le mme nom que la classe.

,/

Le constrtrcteur n'a pas de type retourn, mme pas void.

t/

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

nontre

le

fonctionnenent

des constructeurs par dfaut ; cre une classe


avec un constructeur, puis excute
quelques scnarios
ttcino
*- -" Svctpm.
namespce DemonstrateDefaultConstructor
It

//

Hy0bject

il

cre une classe avec un constructeur bruyant

et rrn

oh i

et i nterne

publie class My0bject


{

// ce membre est

une proprit de

ce membre

Mrr0thorhiant

public

a+a+i nflhi
LLlUVUJ

la

classe

}ly0ther0bject0;
est une proprit de 1'objet

ctnt.i
Mrrfltharh.inn+
LqLrL n rrJvLrrcrvuJtrLL

drrnnmi nh i

My0bject

new

Console.lJriteline ("Dmarrage du constructeur ilyObject")


dynanicObj : new My0ther0bject0;
Console.llriteline (r'Fin du construeteur MyObject") ;

Chapitre 11 : Rendre une classe resp0nsable

/i
II

My0ther0bject

- cette

classe a aussi un constructeuf bruyant

nais pas de nembres j.nternes

Mrrfltherflh jont
^1... r'rjvLrrrvuJLL
^"h1i ^ UId
PUUTIL
L

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

Console. trlriteLine ("Dmarrage de Main ( ) " )


I
cr.e un ob jet
new My0bject0;
My0bject localObject

/l

attend confirmation de 1'utilisateur


Console.l{riteLine("Appuyez sur Entre pour terminer.. .r') ;
Console.Read0;
J

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.

Le programme dmarre, et Main ( ) affiche le message initial.

2.

Main ( ) cre un localObject, de type My0b j ect.

3.

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

57

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.

5.

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

con-structeur

I"h'Ot he rOb

J1

ec

6.

Le contrle revient au constructeur i'll-,'lli', 1eci, eui retourne i"lain O .

7.

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.

Gnrez nouveau le prograrnme

2.

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.

slectionnez Gnrer/Gnrer.

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
3.

l.l

montre I'affichage avec le point d'arrt.

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.

6.

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.

Ghapitre 11 : Rendre une classe responsable

Figure 11.1:
La ligne en
surbrillance

-sur fond

rouge dans
le constructeur
I Fr rtl orll

r-

indique la
prsence
d'un point
d arrt.

w *. :. .';

Ii'r-] t,tr

Frqrrrrffle

._).
flassl.rs

.::,i

,,": "-..: rle,': fr) -

,:rrrL',rLet,i

tfi

ThfEad

:1, ).:,,

- r,"i.i.., a"

[];trl .::,in! nrnr:r ?

(i.1

,tr.

t,4

ag ., '..,

Au.g.&&

Ffam de Frle t,rrrnslr,ilet)Birull,,,:n5lrutl::r

r,ehu,] -

- -

rS

1'1,:ln

,l

strir,l[] rrq:l

*1

:J

-:'
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
du

-dbogueur
de Visual

studio, juste
avant de
passer au

constructeur.

Exlortur d.

l'+om
I'rr :1,-rt'tttl:

!,11uf

Fpls
ttrr

Lar

I er']n5l:rEtEDetrlll:i:r,n5lrU:l:r{,e:,el[,trf]rrslr ll:e[]Ef,3U l:r-LrrrslfUlof

:#

Pile de5

(:r

ntl

nAuromitiquel
L gnertirn a

x
J

AutEmtiqLe

rBssi

tr

ffipiluout
Ln 38

tr;,
ir

Col

f l ::,:t.t

a;

5g

260

Ouatrime partie:La programmation oriente obiet

ficfrier Edilir-,n Atfi{h,3qe qrjet rarrer Qhoqrer gutils


' : He.
. - -."".';)- '.:.
?
w i: ,' ;-'
"r]1

ll!:l

r,t)t amr

tflassl.cs

t,frr: n5l:r

.,...,,

i/:et, - lln ed Iir!],: i:n: rrn,

:;l;:;I 't

,t:t.

,:::r,

, '

,tr

:.

r'rt,r,l

,!a
Er

:i
:j

Figure 1 1,3 :
Le contrle
passe 0ans

I.TIrr--Llr_:

!riJ:1.r,: r:lti:

-le construc-

"'-

'.::r i ll.ir[

''- I:: :' LF.'Ir r: -L -r.= | "l

teur

I_=f

r.l r t:r|l

,.. f

,,

i' .r' -'_: . . :. its , ' ,_ .. ,::.


.p DenronstrateDefaultf onstructor

:i

l1'rr

.-!::]r':j[]
FluaLr.:1:{=

-il

l"h;t'}|horlhioe

avant de se
diriger vers

Autrntiqu

trleut
itnarjlrllaIal]rll :,rjlrr,:l:r l1

l!fi
l+i lfi:

T|pe

S;r
*}

:,r:frr [,r,:rr:|:.

le constructeur

l,Ii'Object.

Fr,3m d

Frie

Iaf r]rr!lrnl[ii]ll,

['etu,1

,rTr!ff!:Lrr

'

t,

:J
--J

:l

Figure 11.4 :
Le construc-

teur

M-,'Obi ec

reoit le
contrle une
fois que
l'o

jet

l,J,ln

statique
Itf.rlthonlh-i.e

+il

utontigue

TYpe
-

1f,,.

['f-,]rrilr ltEIrf,:!li]:itrr!ff rJ:l:r,r ll,/:-t,jE,:ll trnr:rrslr

tr'rn

tu-ol

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,:#

L4l ,rlhtrqui |
L gnerarln

F l d:: FF,l!

n t6
c0nstru it,

$rsl

reusii

)
l#

EJ

Fre des ar,,ets

r.tl
Ln

{-,rl

,t

i-l',

---1

_,lr_-:

t,L,rt!r de s,:|:lron: - trenl:r;tr

Chapitre 11 : Rendre une classe responsable

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

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

int

nAccountNumber

= **nNextAccountNunber

chaque objet

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.

261

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

// ce membre est

une proprit de

<tnt'i n Mvthorhiont

// ce membre
MvthprChiont

public

My0bj

la

classe

ctetirnhi

= new My0ther0bject0;
est une proprit de 1'objet
rlrrn-aminOhi = now Mrrthorh'iont
vvJ

ect

f)

L \ /

.
t

()

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

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 :

#"*$3
ffir]
I t"'7

f\

//
tl

BankAccountWithMultipleConstructors

fournit notre compte

ll

bancaire

un certain nonbre de construeteurs,


un pour chaque occasion

using

System;

nmespac

e BankAccountWithMult

ip1 eConstructors

using Systen;
public class

C1ass1

public static

int Main(stringil args)

cre un conpte bancaire avec des valeurs

BanhAccount ba1

nerrr Bankccount

Console.l,rlriteline (bal . GetString(


ankAccount ba?

))

initiales

valides

= 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...")

BankAccount ba3

Console.Read0;

return

0;

l
]

//

BankAccount
simule un
BankAccount

sinple conpte bancaire

public class
t

I I les numros de compte coffrencent 1000 et


- ,l^
// DLyusrrLlcrrulclIL
souentiel I ^-^-+ a ^^-+i
uE 1
PaL Lal
static i.nt nNextAccountNumber = 1000;
t t

augmentent

2 63

264

ouatrime partie: La programmarion oriente objer

.'^..- re numro
//Il +4^^+:
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)

// reprend une partie du code du constructeur par

dfaut

= #nNextAccountNumber ;
/l et nai"ntenant, le code propre ce constructeur
l/ commence avec 1e solde initial, condition qu'i1 soit positif

nAccountNumber

if (dlnitialBalance (

0)

dlnitialBalance =
l
dBalance

0;

: dlnitialBalance;

public

BankAccount

(int nlnitialAccountNumber,
double dlnitialBalance)

II
if

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

Cette version du programme BankAccountWithMuitipleCcnstruciors

comporte trois constructeurs

t/

Le premier constructeur assigne un numro de compte, et dfinit


un solde gal 0.

t/

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.

t/

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.

)t

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

26 5

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

using Systen;
e BankAc c ount Cont ruct

name spac

r sAndFunct

ion

{
-^
uDrrr6

"^.;

Q"n+an.
ujLru,

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

Console.Writeline

(ba2 . GetString

100)

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

..")

0;

j
l

//

BankAccount

public class

- simule un simple

compte bancaire

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

dans une fonctj.on conventionnelle spare


BankAccount0

public
{

Init(**nAceountNumber, 0'0)

public

(double dlnitialBalance)

BankAccount

Tnit (**nAccountNumber, dlnitialBalance)

ll c'est 1e constructeur le nlrrs sneifinrre orii fait


I I 7e vritable travail"
public

tout

(int

nlnitialAecountNunber,
double dIni.tialBalance )

SankAccount

Init (nlni.tialAccountNumber, dlnitialBalance)

nrirrato,,nid lnil (int nlnitialAccountNUrnber,


double dnitialSalance)
t

= nlnitialAccountNunber :
1e solde initial, condition qu'i1 soit positif
1r (0ln1t141alance \ u/

nAccountNunber

/l

I F

comnence avec
I

r-

^\

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-^

Q"a+an'

ublrrB

nare

oy b Ltrnl,

space BankAce ountC0ntructo rsAndhis

t
,,^;-^
urll

C,"^+^-,
Jy
Lcnr,

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

public

BankAccount

(int

doub

nlnitialAccountNumber,
1e dlnitialBalance)

t
II
//
if

ignore 1es numros de compte ngatifs ; un numro de cornpte nu1


utiliser 1e prochain nunro disponible
(nlnitialAccountNurnber (= 0)

indique que nous devons

nlnitialAccountNumber = **nNextAccountNunber
l

nAccountNunber

= nlnitialAccountNumber

Chapitre 11 : Rendre une classe responsable

//
if

commence

avec

le solde initial, condition gu'il soit positif

(dTnitialBalance
\v-.r+!rs,!**-nce

(\

0;

dTnitialBalance

0)
uJ

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

269

270

Ouatrime partie : La programmation oriente objet

ue a(lare de

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.
Si vous

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

public class

simule un simple compte bancaire

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;
I

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

using

- offre 1a dmonstration
la plus simple de 1'hritage

System;

nanespace InheritanceExanPle
{

public class

BaseClass

public int nDataMenber;


public voi-d Somel'lethod0
t

Console.!'lriteline ("SoneMethod

")

l
l

public class SubClass:

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;

//

crons naintenant un lment d'une sous - classe

Console.lJriteline ("Utilisons un objet d'une sous-classe


SubClass sc = ne.,/ Sub0lassO;
sc.nDataMernber
sc. SoneMethod(J

=
;

2;

:");

Ghapitre 12: Acceptez-vous l'hritage

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.

? 273

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.

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

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.

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.

? 27 5

Ghapitre 12 :Acceptez-vous l'hritage

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

*\

e,

q/\

--"-ffi

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.

l/
ll

SinplesavingsAccount

d'un

using

inplnente un SavingsAccount

BankAccoTJnt

coiture

forne

; n'rrtiLise pas de mthode virtuelle

System;

nanespace SimpleSavingsAccount

I
I
II
I

BankAccount

public elass

sinule un conpte bancaire possdant


un numro de compte (assign La cration
du conpte) et un solde

BankAccouni

t
II
//

les numros de conpte connencent 1000


squentiellement partir de 1

public static

int

nNextAccountNunber
I
net jour 1e numro de cornpte et
pubLic int nAccountNunber;

public
deciural
-l

mBalance

et

augnentent

1000;

Ie solde pour

chaque objet

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)

public void InitBankAccount (decimal mlnitialBalance)


{

nAccountNunber

**nNextAccountNumber

nBalance = mlni"tialBalancei

//

Balance (so1de)

public decimal

Balance

get I return

nBalance;

//

Deposit

- tout

public void

dpt

positif est

autoris

Deposit (decimal mnount)

if

(mAmount

0)

nBalance *= mAnount;

l
]

1/ I'lithdraw
I

- tout retrait est autoris jusqu' la valeur


du solde ; retourne 1e nontant retir

public decimal Withdraw(decinal

mWithdrawal)

if

(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

public class SavingsAccount

qui rapporte des intrts

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

InitSavingsAccount

(0,

mlnterestRate)

? 277

nrrhl.in .,ni.{ fnjtSavincsccount(decimal mlnitiai,


decimal mlnterestRate)
y su r:v

Ini-tBankAccount (mInitia1 )

this.mlnterestRate : mlnterestRate /

tO0:

// Accurnulatelnterest -

invoque une

fois par

priode

public void Accumulatelnterest()


t

mBalance

= mBalance * (decimal) (mBalance * mlnterestRate)

//

ToStri-ng

- met le

compte sous

forne de chane

public string ToSavingsAccountString0


t

return String.Format(" [0]

{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,

sa.Accumulatelnterest

12. 5M)

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

class Classl

/ fti rpetttennsi f -

ef f

ectue automaticuement
su Lvruo
1e rint 11 'rrn

public static void DirectDeposit(BankAccount

ba,

chnrrp
LrtcYu

Chapitre

2:

rlpnim:i
ba. Deposit (mPay)

Acceptez-vous l'hrit age

? 27I

mPnrr)

public static

int Main(stringIJ args)

t
I

cre un conpte bancaire

et 1'affiche

BankAccount ba = new BankAccount


ba. InitBankAccount ( 100) ;

DirectDeposit(ba, 100) ;
Console.liriteline("Compte
/

I et naintenanr un compre

{0J

",

ba.ToBankAccountString0 ) ;

rnunr

SavingsAccount sa = ne!/ SavingsAccount0;


sa. InitSavingsAccount ( i2 . 5M) ;

DirectDeposit(sa,

100) ;
sa, Accumulatelnterest ( )

i,lriteline ( "Conpte

{0J " , sa, TosavingsAccountString 0 ) ;


// attend confirmation de 1'utilisateur
tonsole.T,Iriteline("Appuyez sur Entre pour terniner...',) ;

Console,

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

//

- compte bancaire
SavingsAccount_

SavingsAccount

public class

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

public void InitSavingsAccount(BankAccount


dec

et

100)

bankAccount,

imal mlnterestRate)

this.bankAccount = bankAccount ;
this.mlnterestRate = mlnterestRate

100:

//

Accumulatelnterest - invoque une


ie void Accumulatelnterest0

nrrbl

uv!ve

fois par

priode

\/

bankAccount.mBalance

= bankAccount.mBalance

(bankAccount.nBalance

* mlnterestRate) ;

// Deposit - tout dpt positif est autoris


public void Deposit (decimal

mAmount)

bankAccount . Deposit (rnAnount )

i
I

/
I

Withdraw

- tout retrait est autoris jusqu' la valeur


du solde : retourne 1e montant retir

public double Withdraw(decimal

rnWithdrawal)

I
t

return

bankAccount, Withdraw(mWi.thdra,,+a1 )

'l
.J

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'

La relation

rii:'t

A_UN BanitAccounl--.

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

I et y

dpose

sa.Deposit(100)

cent euros
;

0;

Ghapitre 12: Acceptez-vous l'hritage

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

-]

ba.Deposit (nPay)

void SoneFunction0
t

// 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 |

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 =

netll

SavingsAccout

ll

ox:
une conversion vers

ba
ba

: sa;
= (BankAccount)sa;

II
1o n^ct
avnl
ini+^
II tI r!
!qoL
yrfLrLc

sa

ba;

sa

// Nont
I I Ia conversion vers
I I ceci est correct

(SavingsAccount)ba;

1e

bas

imo l 'i c'i

te est

admise

cDL
^^* rrf r

1e haut

impiicite est i.nterdite

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".

l'excution

Des casts inalides

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

ProcessAmount(BankAccount bankAccount)

t
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;
^^^..-+
^^,,.:---rrrnLLuurt,
ov

^^.,-..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) ;

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

Af\

e,

s/\

ob

jet

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

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

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.

^qa/
ft?ll
\1l/

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

GenericFunction(MyBase01ass

mc)

t
I I si 1'objet est vrainent
if (nc is !'lySubClass)

ll ...alors

1e

MySubClass msc
I

traite

une sous-classe..,

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

en utilisant le mot-cl

as

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

oublic
class
'(

Test

la

? 28 5

286

Ouatrime partie: La programmation oriente obiet

public static void GenericFunction(object

o)

if

is l'IyClass1)

(o

MyClassl mcl

(MyClass1)o;

tttl
)

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

ll Inhpri+inoAConstructor - montrp orre lp consTrl{"teur


de 1a classe de base est invoqu
{

|
I

autonatiquenent

,,^.:-^
urlr

e.,-+^JJ
D LEIT,'

namespace

InheritingAConstructor

public class
t

C1ass1

Chapitre 12 : Acceptez-vous I'hritage

public static

int Main(string[]

? 287

args)

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

public class

BaseClass

public

BaseClass

Console.liriteline ( "Construction de BaseClass")

publie class SubClass

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',)

scl = new SubClassO;

SubClass

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

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

n11n | 1a

\11n1

Iteq

? 289

/\

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.
la manire dont un constructeur en invoque

procd

est trs similaire


Ce
;(^$tf$
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

Par exemple, examinez le petit programme I1.",3l.,sl3seConstructor

//
II
II

InvokeBaseConstruetor

using

coTnment une sous-classe peut


invoquer le constructeur de 1a clagse de base
de son choix en utilisant 1e mot-cl base

nontre

System;

nanespace TnvokeBaseConstructo r

290

0uatrime partie:La programmation oriente objet

public class BaseClass


t

public

BaseClass

()

Console.l,iiriteLine("Construction de BaseClass (default) ")

l
public BaseClass(int i)
{

Console.I^IriteLine("Construction de BaseClass(

[0])", i) ;

l
l

public class SubClass:

BaseClass

public

SubClass

()

Console.l'/riteline("Construction de SubClass (default)")


l

public SubClass(int

i1, int i2) : base(i1)

Console.lrlriteLine("Construction de SubClass(i01, {1J)",

il,

i2);

]
]

public class

C1ass1

public static

int Main(stringIJ args)

Console,lllriteLine("Invocation de
SubClass sc1 = new SubClass0;

SubClass 0

")

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

0;

l
]

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

ConstructorsavingsAccount

implmente un SavingsAccount
forme d'un SankAccount : n'utilise

colnme

aucune mthode

virtuelle,

mai.s implmente

correctenent 1es constructeurs

using

System;

namespace ConstructorSavingsAccount

/i
|
II

BankAccount

- simule un compte bancaire

possdant

un numro de compte (assign 1a cration


du compte) et un solde

public cLass

BankAccount

tL

II
/l

tes numros de conpte conmencent 1000 et augnentent


squentiellenent partir de 1

public static int nNextAccountNumber = 1000;


ll net jour le nunro de comntp pt le solrle nour
public int nAceountNumber;
public decimal nBalance;

//

tonstrueteurs

public

BankAccount

/\) this /^\

-(

(0

public

Bankccount

(decinal nlnitialBalance)

chaoue obiet

292

0uatrime parrie:La programmarion orienre objer

nAccountNumber = finNextAccountNunber
mBalance = mlnitialBalance ;

l
I

mme

chose

ici

//

savingsAccount

public class

compte bancaire qui rapporte des


Savi_ngsAccount : BankAccount

intrts

public decimal nlnterestRate;

//
I

tnitsavingsAccount

* 1it 1e taux d'intrt,

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

public SavingsAccount(decimal mlnitial,


decimal mlnterestRate)

: base(nInitial)

this.mlnterestRate = nlnterestRate

tOO;

l
I

mme

chose

ici

public class

C1ass1

n;-^^*n^-^^..ir
//// u].recrueposlr
- 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)

Console,l,Iriteline("Conpte {0}", ba.ToBankAccountString0 ) ;


// et naintenant un compte rmunr
SavingsAccount sa = neu, SavingsAccount(12,5M) ;

Di.rectDeposit (sa, t00) ;


sa.Accumulatelnterest ( ) ;

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

BankAccount(0)

d'un objet

Savings Account (125%,

(.errino<Arnn mt

en utilisant le
c0nstructe u r
par dfaut.

nurr" le solde la classe de base

gl

donne au solde

la valeur par

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.

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,

En

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

vous avez besoin de vous rafralchir la mmoire sur I'hritage des


classes, relisez le Chapitre 14.

Si

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

(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.
PY

Les arguments d'une fonction font partie de son nom complet, comme le
montre I'exemple suivant :
oublic class

l4vClass

nrrhlin

ct:f in rrnid I'rrnnti^n1)

I f.aire

quelque chose

nrrhl in cfrtin

rrnirl rrnntinn/int\
srr

urv.r

\Jrrr/

I faire cirelnue chose d'autre

public static void AFunction(double

d)

I faire encore quelque

chose d'autre

l
public static void Main(stringll args)
t

Ghapitre 13 : Ouel est donc ce polymorphisme

AFunetion0;

Alunction(l);
AFunction(2.0);

#5

e,

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.

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 7

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

public decimal Withdrav(decimal

nAmount)

I I si Ie compte est un conpte rnunr


if (isSavingsAccount)

i
ll

.,

.alors soustrait 1.50

mBalance

-:1,50M:

// poursuit avec le mme code pour le retrait


if (mAnountToWithdraw ) mBalance)
{

mAmountToWithdrar,v

mBalance

mBalance

return

-=

nAmountToWithdraw;

mAnountTo}.fithd

rar^r

Ghapitre 13 : 0uel est donc ce polymorphisme

'I

class Mytlass
{

public void

SomeFunction0

t
I

I le veux ne crer

BankAccount ba

un conpte rrnunr

= 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

t,

I ....HidinsWithdrawal - redfinit la mthode de retrait de la


II
classe de base avec une mthode de La
II
sous-classe du mne non
i

using Systen;
nnespace Hidi.ngl,lithdrawal
t

// BankAccount - un compte bancaire trs ordinaire


pub1lc class BankAccount
f

protected decirnal nBalance;

public

BankAccount

(decimal mlnitialBalance)

mBalance = mnitialBalance

public decimal

Balance

get { return nBalance;

nrrhl

r'c decimal Withdraw(decimal


\sv!+rx

mAmount)

t
mAmountToWithdraw = nAmount;
(nAurountoWj.thdrai,s
mBalance)

decinal

if

mAmountoWithdrarar

= mBalance

l
mBalance

return

-= nAnountTol,lithdraw;

mAnountTol,Ii.thdraw

2gg

300

0uatrime partie:La programmation oriente objet

//

SavingsAccount

compte bancaire

public class SavingsAccount

qui rapporte des intrts

BankAccount

public decimal mlnterestRate;

//

SavingsAccount

- 1it

1e taux d'intrt, exprim en


pourcentage (valeur comprise entre 0

SavingsAccount (decimal

public

dec

base

et 10)

mInitialBalance,

irnal mlnterestRate)

(mInitialBalance)

this.mlnterestRate = mlnterestRate /

100;

// Accumulatelnterest -

invoque une

nrrhl i c voi rl Anr^trmrri atoTntpro<t


!L-

rI

mBalance

mBalance

!v

i
\

fois par

priode

)
/

(mBalance

mTnterestRate);

//

Withdraw

- tout retrait est autoris iusou' 1a valeur


du solde ; retourne le montant retir

public decimal Withdraw(decinal mWithdraval)


{

// soustrait 1.50
base. 'vlj-thdraw(
| | VVU UVUVgL
I I "^"^

return

l . 5t't) ;
maintenant
lnaLLLLgllIlL
EtlELLUtrl
effectuer
un
Ull

base . Wi.thdraw (mi^iithdrawal )

retrait
!CL!1L

nrri
restp
avec ce
re
!sLC
yur
dVLu

l
]
nlrhri^
PUUTfL

^t^^a
Urdb

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{

00M)

// essaie de faj-re la

mme

chose avec un cornpte rmunr

sa = new SavingsAccount(200M, 12);


sa.llithdraw(100M)

// affiche 1e solde rsultant

Chapitre 13 : 0uel est donc ce polymorphisme

Console.l,iriteLine("Quand il est invoqu directenent ;")


Console.l,rlrj-teLi.ne("Le solde de BankAccount est {0:C1",

? 30 I

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
'

le

membre

trtlj.thdrarv

(decimal)', car i1

hrit

HidingWithdrarral . BankAc count . t.,lithd raw

dec

imal

)'

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

n'est qu'un avertissement. Vous ne le remarquerez mme pas,

iNmoinsdepasser|afentreSortiepourvoircequiyestaffich'Dans

t\7,
Y

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 :

//

plus de problmes avec withdraw0


public decimal Withdraw(decinal dWithdraval)

new
{

pas de modifications internes.

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.

:(dw

Ghapitre 13:0uel est donc ce polymorphisme

ReUenr

? 303

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)

double dAnountllithdrawn = Withdraro(dltithdrawal)

if

(++nNunber0fl^lithdrawalsThisPeriod

1)

dArnountl,lithdravn

t=

l,lithdraw (1 . 5 ) ;

return

dAnountl{ithdrar+n

Cet appel a le mme problme que celui-ci


vo]-d tn
{

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.

//

tlithdraw

ll

new

- cette version accde 1a rnthode redfinie dans 1a classe de

base en dfinissant explicitement 1e cast de 1'objet this


public double Withdrar,r{double dWithdrawal)

{
I

I cast du pointeur this

dans un

objet de la classe

BankAccount

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/
{

f=

dAnountWi.thdralrn

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

With&aw

new

- tout retrait est autoris jusqu' la valeur


du soide ; retourne l-e montant retir

public decimal Withdraw(decimal nWithdrawal)

t
II

/ sousrrait 1 .50 F

base. l,lithdraw(

.5M)

// vous pouvez maintenant effectuer un retrait


return

base.Withdrai,r(mWithdrawal)

avec ce

qui reste

L'appel base.,,iit-hdra-,

(;

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 ,

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);

(ba,

MakeAWithdrawal

100M)

sa = new SavingsAccount(200M, L2);

(sa, 100M) ;
//tl affiche 1e solde rsultant

MakeAWithdrawal

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.

return
]

Read

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.

{:/rr

=qE)

? 307

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,

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
La

de

se

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.

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.

'(dE,)

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

s)

quoi qu'el1e puisse faire.

s.CalcTuition0;
continue

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

sa . Withdraw (urAmount )

(SavingsAccount)ba;
;

el se
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"' ( )
.

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

=[J\y
\/

programmes crits par des gens qui aiment faire de I'esbroufe.

Ghapitre 13:0uel est donc ce polymorphisme

En fait, I'approche

? 309

"is" fonctionne, mais c'est vraiment une

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.

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

I I"\7

ry

//
II

folynorphiclnheritance

redfinir

using

/i

e Polymorphic Inheritanc

BankAccount

public class
i
I

polynorphi.sne pour
une mthode dans la classe de base

System;

nmespc

* utilise le

L ,

- un compte bancaire trs ordinaire

BankAccount

la

mne chose

ici

public virtual decimal Withdraw{decinal


{

decinal

if
I

nAmountToWithdrarrr

(nAmountToWithdraw

rnAnount

mBalance)

mAnount)

3 |0

Ouatrime partie : La programmation orienre objer

mAmountTol^lithdraw

mBalance

l
nBalance -= mnountToWithdraw;

return

mAmountToWithdraw

//

SavingsAccount

compte bancaire

public class SavingsAccount

qui rapporte des intrts

BankAccount

l ,
la mme chose ici- aussi
/ tllthdraw * tout retrait est autoris jusqu' la valeur
II
du solde ; retourne le nontant retir
l

override public decimal Wj.thdraw(decirnal nrWithdrawal)


t
tl

// soustrait 1.50 F
base.l{ithdrav(1,5M);
I

vous pouvez naintenant effectuer un

return

base. Withdrarq(rirWithdraval)

retrait

avec ce qui reste

public class

C1ass1

public static void

MakeAWithdrawal(BankAccount ba,

decimal

mArnount)

I
t

ba. Withdraw(mAmount)

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

-r,r'i

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

Ghapitre 13 : Ouel est donc ce p0lymorphisme

^$C .z
7X
t(?,
Y

? 3l I

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
:

et Unive r

University
- numStudents

- numStudents
+ nAvgSAT

Student

+ Enroll 0

+ 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

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.

tl

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.

tl

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

HighSchcol

-simplifie la
c

+ Enroll 0

lasse

Univer

sity,

mais

introduit
quelq ues

problmes.

- numStudents

il

University

+ nAvgSAT
+ 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 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

+ Enroll 0

Figure 13.3

Hl ghS cho
9t
s

Univer

ity

- {abstract}

o1
-

doivent

I'une et
I'autre tre
bases sur
une classe
c0mmune

School.

+ nAvgSAT

+ 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

r--

Figure 13.4:
Le factoring
prod u it
g n ra lement des
c0uc nes

L{"+,q

;r
Lqi:e

supplmen-

taires dans
la hirarchie
d'hritage.

Suprieur

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
a

flasses
dpend en
partie du
problme

i
l
I

rsoudre.

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

ll
II

C,,a+^*.
yLtrilI

'

AbstractBase0lass

cre une elasse abstraite de contenant rien d'autre


qu'une mthode OutputQ

abstract public class AbstractBaseClass


I

3 I8

Ouatrime partie : La programmation oriente obiet

directeur de la banque, j'imagine que la


conversation pourrait ressembler ceci :

Si vous demandez son aide au

"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

cre une classe abstraite de contenant rien d'autre


qu'une mthode 0utPut0

abstract public class AbstractBaseClass


t

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:

factoring
produit
Le

Suprieur

-gnra lement des


couc hes

supplmen-

taires dans
la hirarchie
d hritage.

'---3__,
1ry.":_l

E."b;pd;l

Universit

Classes prparatoires

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

HlghSchool

-simplifie la

- numStudents

+ Enroll 0

classe

Univer

sity,

mais

il

introd uit
quelq

es

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

etUrrlver

+ Enroll 0
+ GetGrant

Student

+ nAvgSAT

ci trr

re9!Qa.
zo^w7-7

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

//

SavingsAccount

compte bancaire

public class SavingsAccount :

qui rapporte des intrts

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
I

/1 soustrait 1.50 F
tl

base.Withdrari( 1 . 5M) ;
// vous pouvez maintenant effectuer un

return

base.Withdrarr(mWithdra,,ral)

retrait

avec ce

qui reste

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

( ) 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.

La fonction \^rithdraw

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

MakeAWithdrawal(BankAccount ba,

deci.mal nAmount

if

ba

is

SavinssAccount

SavingsAccount sa

(SavingsAccount)ba;

sa . Withdraw (mAmount )

e1 se

ba

Withdrav

(mAmount

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 ( ) .

MakeA'uJi

1t$!Qa.

7^H\
V /
\/

= \

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

EST-UN

Int32

en utilisant le mot-cl 1s. La sortie de cette

programme se prsente ainsi


Bxtrai.t d'une liste les
L'lment numro 1 est
L'lment numro 3 est

portion clu

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 (?)


pc1L (fl1p."1
0biets[2]Lal
\ur4t
0hipts f?l pct (4)
vvJL

rlhr^+ll,r
wuJLDLTJ

Appuyez

^^+
CL

1(
\J.J

(trrrr^trrroFvemnle)

rl,r nr^^..-*o
p!u!dill[E
uu

\)

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.
,

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

dK d'excuter son tour de

'qE,

peut contenir des indications {n} de contrle de format).

57

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

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

des types

Console.I/riteLine("\nAffichage de tous les objets de 1a 1iste");

int

nCount = 0;

(nhiont
fnroanh
!VIgOLI!\VUJLL

ir VVJULD/
ahiantol
V
^ IIT

Console.l^lriteLine("0b3'ets

[{0}] est <{1})",

nCount**, o.ToString0 ) ;
]
I

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)
{

35 5

30 4

Ouatrime partie : La programmation oriente obiet

= (BankAccount)this;
// invoque inlithdraw0 en utilisant cet objet
I I appelle 1a fonction BankAccount.Withdrav0

BankAccount ba

BankAccount

double dAmountl^lithdrawn = ba.Withdraw(dlJithdrawal)

if

(+*nNuniberOfi,lithdrawalsThisPeriod

*= ba.Withdraw(1.

5)

1)

dAmountWithdrarrn

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

new

- tout retrait est autoris jusqu' 1a valeur


du solde ; retourne 1e montant retir

public decimal Withdraw(decimal

mhrithdrawal)

//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

( .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

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.

1t$!Qa" Cette unification des types de variables est trangre aux autres langages

!,/t-t

'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

353

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

plus de problrnes avec withdraw0


public decimal Withdrar,r(decinal dl^lithdrar,ral)

new
(

t
I

pas de modifications internes,

U
1e$\a.

^v7|

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

\ trouve

'qE,

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

I I une strrlct neut avoir une mthode


public void ChangeMethod(int nNewValue, double
{

dNewValue)

n : nNevValue;
d : dNewValue;

//
tt

^.
ToString

- 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
tHI 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

(ref test , 100, 200.0)


test aprs 1'appe1" +
;

Console.}lriteLine("Va1eur de

" 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"

'

ChangeMethod(1000, 2000.0) ")

OutputFunction (test ) ;

// 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
{

t.N =

newValue;

Test.D = dNewValue;
l

newValue, double dNer.vValue)

35 |

300

0uatrime partie : La programmation oriente obiet

//

SavingsAccount

compte bancaire

public class SavingsAccount

qui rapporte des intrts

BankAccount

public decimal mlnterestRte;


i
I

/
I

SavingsAccount

1e taux d'intrt, exprim en


pourcentage (valeur comprise entre 0

- iit

SavingsAccount (decimal

public

et

100)

mnitialBalance,

decimal mlnterestRate)

base

(nInitialBalance)

this.mlnterestRate = mlnterestRate

100;

//

Accumultelnterest

invoque une

fois par

priode

nrrblie void Accrrmulatelnterest0


i
nBalance = mBalance

(niBalance

mTnterestRate);

/i
II

Withdraw

- tout retrait est autoris jusqu' 1a valeur


du solde ; retourne 1e montant retir

public decimal Withdraw(decinal

mWithdrar+al)

/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

ic gtar-ie voiC

MakeAWithdrawal(BankAccount ba,

decimal nAmount)
a

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

faire

1a mme chose avec un conpte rniunr

SavingsAecount (200M, t2)


sa.Withdraw(100M);
i affiche 1e solde rsultant

sa =

nel,,r

Chapitre 14 : Ouand une classe n'est pas une classe: l'interface et ta structure

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

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.

349

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

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

isSavingsAccount;

public decimal l,iithdraw (decimal mAmount)


i
l

l si

'i

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

return

-:

nAmountToWithdrar'r;

nrAnountToWithdraw

Chapitre 14 : Ouand une classe n'est pas une classe: l'interface et la structure

n=

1;

ll \a dclaration
MyStruct

ns.n =

d'une struct ressenble

la

dclaration d'un sinple int

ms;

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

new MyClass;

mn n = ?.

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

SITUCtUI mS

-rside" dans

1
I

la mme
zone de
mmoire que

3.0

la variable de

type valeur rr,


alors que la
zone dans
laquelle
rside I'objet
mc vient d'un
espace
mmoire

l-n
l.+ms
I

mc

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

347

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.

n'est (u'une (uestion de surcharge de foncton

Ce

-rf4k\
-6:
tl

S/

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

My0lass

public static void AFunction0


{

I faire cuelcue
a---a

chose

i
public static void AFunction(int)
{

t r^:
^..^1-Ue
rda!e-^ qusrq

I t

ChOSe

d'aUtre

public static void AFunction(double d)


{

ll

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

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
-

t-

d.U>

- *,.
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

345

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

I Abstractlnterface -

montre comment une interface


peut tre implmente avec
,,-^
utrv

^l -^^^
Lroc

^l^+-^)
ouo
u, ort

C,,^+^-.
"--i^^
u1116 uj
Lfl,

Abstractlnterface

namespace
I

//

ICornpare

- interface capable de se comparer e11e-mme


a- U
Jlaff.i^hor
EL
aJ- LIIC!

public interface

.o '1rnr
a
P!vt,!

ICompare

rtelprrr

ICornparable

// GetValue - retourne s propre valeur sous forme d'un


int GetValue O ;

; -+L

f,tl

BaseClass

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

nValue;

public

(int nInitialValue)

BaseClass

i
nValue = nInitialValue;
]

//lt
II

implmente d'abord f interface Compare


avec une nthode concrte

public int

GetValue

return

nValue;

//

complte

eh<trrnf
quoLq!L

nrrhl.in
yuuraL

interface
jr+
rrrL

abstraite

ICompare avec une mthode

l.nmaa-^T^/^h.innruLJtrrPdrtrru\uuJuL

rinlr+hi..nt\
rlrILvuJerLL/,

l
(rrhfll

/ /

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:

BaseClass

I
L

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

Tflnmnnrrhl e ' ri-^rrrna

une indication disant si un objet d'une sous'classe


esl plus granq qu un autre
na+

orrprridp
vv!!ru

nrrhl
yuvarL

ic int
irrL

',n

^-^^

n',

riahthiant)
CnmnnrpTn(nhiont
vvrxyutrv\vuJsuL
trSrrLVUJrL/

BaseClass bc

= (Base0lass)right0bject;

return GetValue0 .ConpareTo{bc.GetValue0

343

292

0uatrime partie : La programmation oriente objet

nAccountNumber

= **nNextAccountNumber

nBalance = rnlniti-alBalance

l
I

mme

chose

ici

//

SavingsAccount - compte bancaire qui rapporte des intrts


(errinncnnnrrn#
nlacq
,*rrgsAccounr :. Ra.Ll
DanKAccount

nrrhlin

r*-**,
{

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

l
^,.L1
yuurrLi

c-..-irrr6n\
-^^ ^:count
uqv

(decimal mlnitial ,
decimal mlnterestRate)

: base(minitial)

thi.s.mlnterestRate = mlnterestRate

tOO;

mme

chose

ici

l
hllhlr^
PUUTfL

l,^^,j1
Urdi

^l^FLJd

//

Directltenosit - effectue automatiouement Ie dot d'rrn


n <r.ati n 'oid DirectDeposit (BankAccount ba,

chcrrp
urrrYu

nrrhl i

decimal npay)
{

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,

100)

Accunulatelnterest ( ) ;
Console.Writeline("Compte [0J", sa.ToSavingsAccountString() ) ;
l/ attend confirmation de 1'utilisateur
Console.w*riteLine("Appuyez sur Entre pour terniner...") ;
sa.

Console.Read0;

return
l
]

0;

Chapitre 14 : Ouand une classe n'est pas une classe: l'interface et la structure

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

L1SA

: IUU

Marge
Bart

:50

Maggie

:30

Homer

:0

85

des tudiants

34 |

290

0uatrime partie : ta programmation oriente obiet

public class

BaseClass

public

BaseClass

()

Console.}lriteline("Construction de BaseClass (default)")

public BaseClass(int i)
t

i)

Console.ItiriteLine("Construction de BaseClass([0])",

l
l

public class SubClass

BaseClass

public

SubClass

()

Console.l^Iriteline("Construction
l

public SubClass(int
i

it, int i2) :

de SubClass (default) ")

base(i1)

Console.I{riteLine("Construction de SubClass({01, [1])",

il,

i2);

]
]

public class Classl


{

public sratic

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

0;

l
]

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

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)

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/

appelante

DLALTL

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

-..L1.ipuDl1C

^+-.i -Srrlng

[T^
Name

get
{

return

sName;

]
]

/itt
/l
II

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)

339

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.

urt

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

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.

return

Read

0I

l
]

public class

BaseClass

public

BaseClass

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

// explicite des objets...


Arrav. Sort l.hi rds)
vv!

\vf

DisplayArray(birds);
ll attend confirmation de 1'utilisateur
Console.WriteLine("Appuyez sur Entre pour terminer..,")

Console.Read0;
l
/

| \:
^' 1 -"4 --^" - dLIILiIE
d'ohipts
nrri
!f,>Pr/ll)
affiche un
Ull tablearr
LdUfc@u
u vuJL
yur
inplmentent f interface lDisplayable
I

| |

nrrhlin

ct:tir

vnirl DicnlrrrArrev

(iDisplayable
{

I displayables)

.int
lpnoth:
___ _ _
___o ___ dr^-1-,.^L1^rrsplaya0res . T^-^*l
Lengrn;
for(int index = 0; index ( length; index**)
i

IDisplayable displayable = displayables Iindex]


", displayable.GetString0
:

Console.l{riteLine(" i0J

l
l
]

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
{

orivate strins
r"'*

sName;

r^..L1^ UrGrade = 0.0;


--"i--^+^
UUUUlC
PLAVOLg
// Constructor - initialise un nouvel objet student
public Student(string sName, double dGrade)
I

ll net de ct les
= sName;

donnes de

1'objet

this.sName

this.

dGrade

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

Student[] sArray = ner/ StudentIsNames.Length]

for (int i = 0; i (

sNames.Length; i++)

sArray

Ii]

ner,r Student (sNames

lil ,

l
F^+1rFh
tgLuLll

^A--^,,,
drL4j/,

/l

accde aux mthodes en lecture seule

dGrades

Ii]

337

286

0uatrime partie : La programmation oriente obiet

public static void GenericFunction(object

o)

if (o is

MyClassl)

MyClassl mc1

(MyClass1)o;

1t
'T

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

using

System;

namespace

InheritingAConstructor

public class
t

Class1

Chapitre 14 : 0uand une classe n'est pas une classe:l'interface et la structure

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

nom

er ses points

d'UV

private double dGrade;

//

accde aux mthodes en lecture seule

oublic dorrble

Grade

get
{

return

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)
^r\
1t {rrphtstrrrlnt.braoe r/1 rett>tudent.Grade)
{

return
i

-1

(rightStudent. Grade ) leftStudent. Grade)

if
{

rol-rrrn

I.

Llg!!l

return

0;

Le

tri d'un tableau d'objets St,.rdent est rduit

[]

void MyFunction(student

un simple appel

students)

I trie le tableau d'objets

Array . Sort ( students)

l0omparable

Vous fournissez le cornparateur, et Array fait tout le travail.

335

284

Ouatrime partie:La programmation oriente objet

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.
\!!-/

=f

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'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

g1
I
L

return

dGrade:

]
]
I

I r^-o+-]
-^ - rtrrrn
unp
r LVU!rr
Ull
rro0rsentation
de 1'tudiant
Ug LO Ll Irr
-

I I

(
-',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

length = displayables.Length;

for(int

index =

0;

index

length; index*+)

tI

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

Marge

:85

333

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

crire

une note sur

Tan+as
!oPLUP

., /r^-^,'+^vVtuPULtr!,

le

PDA

l
PUUfAU LIdUU

IRecordable

vvf,u
yu9ltL
^,,1-1.i ^ ,,^i,1

TakeANote

string

sNot

il

raper une note sur 1e clavier

l
l

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)

erer une

liste

de commissions

string slist = GenerateShoppinglist0


I

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.

33 |

280

0uatrime partie: La programmation oriente obiet

pourcentage (valeur comprise entre 0

public void InitSavingsAccount

et

100)

(BankAccount bankAccount,
dec irnal mlnt erestRate)

this.bankAccount = bankAccount ;
this,mln:erestRate : mlnterestRate

tOO;

// Accumulatelnterest -

invoque une

fois par

priode

public void AccumulateTnteresto


I

bankAccount.mBalance = bankAccount,mBalance
t (bankAccount.mBalance * mlnterestRate)

l/ Deposit - tout dpt positif est autoris


public void Deposit(decimal

mAmount)

i
bankAccount . Deposi.t (mAmount ) ;

j
i
I

I
I

Withdraw

- tout retrait est autoris jusqu' la valeur


du solde ; retourne 1e montant retir

public double Withdraw{decinal ml'iithdrawal)


t

return

bankAccount . Withdrar^' (mi,lithdrawal )

l
]

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 et y

(ba,

dpose cent euros


, /. ^^\
sa.ueposltll.uul;
I

Ghapitre 14 : Ouand une classe n'est pas une classe:l'interface et la structure

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

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.

329

278

0uatrime partie:La programmation oriente objet

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

cre une chalne qui donne la description du compte.

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

C1ass1

t
L

// Direetltenos'i t - effectue

automat-i 0rement

public static void DirectDeposit(BankAccount

le dnr r1 trrn r.hnnp


ba,

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)

nrrhl in rrnid Tn'i tRanlrAnnnrrnt

1)

InitBankAccount (0)

public void InitBankAccount (decimal mlnitialBalance)


{

nAccountNunber = **nNextAccountNumber
mBalance * mlni-tialBalance;

// Balance (so1de)
tl

oublie deeimel

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

(mAmount

0)

nBalance *= mAnount;
]
]

//
||

Wittdrar,I

- tout retrait est autoris jusqu' la valeur


du solde ; retourne 1e montant retir

public decimal lti.thdraw(decinal mlJithdrawal)


t

if

(mBalance

(-

$lJithdrawal)

nlrlithdrawal = nBalance

mBalance -- ntrli"thdrawal
return mWithdrawal;

]
I J q^<+-:^
rvuL!rrr
tt

public string

uet le

compte sous forme de chane


ToBankAccountString ( )

return String.Format("{0i
nAccountNunber, mBalance)

[1:CJ",
;

l
I

SavingsAccount

compte bancaire

public class SavingsAccount

qui rapporte des intrts

BankAccount

public decimal nnterestRatei

/l
II

- lit

1e taux d'intrt, exprim en


pourcentage (valeur comprise entre 0
public void InitSavingsAccount (decimal nlnterestRate)
InitsavingsAccount

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

BankAccount

/l
II

Withdrawal

* tout retrait est autoris jusqu' la valeur


du solde : retourne le montant reti.r

virtual public void Withdraw(double

dt/ithdraw)

Console.l,{ri-teLine ( "invoque BankAccount.Withdraw(

)")

oublic sealed class SavinssAccorrnt

BankAccount

override public void l{ithdrar^r(double dWj.thdra'la1)


tr
COnSole . ldr j tai.i

of"j

nvnnrr" CavingSAcco1nt . Withdrar^r

)")

i
]

public class SpecialSaleAccount :

SavingsAccount

override public void l^lithdraw(double dWithdraval)


t
Console

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

Console.!JriteLine(" appelle SpecialSaleAccount.!'lithdravr0")

]
l

//
I

SaleSpecialCustomer

- compte utilis

pour des clients particuliers

pendant 1a priode des soldes


public class SaleSpecialCustoner : SpecialSaleAccount
I

i
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. RemarqUeZ tOutefoiS que Si-.e.:lai-Salel'r:.LruLr. i^,:it-horali() a t marqUe

',,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

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

d'un BankAccount
p,^f rrcr Test (BankACcOUnt) \
appelle BankAccount . Withdraw ( )

Passage

nnttr pff

r uu

L \yg4rr!rr!uvqrr

L /

Pncs:op
_-_*D_ d- rfin
_-. sn..i--^^^^^..-!
_*vflrl1uLUultL
norrr pffpct,ror Test (BankAccount)
_

appel

1e SavingsAccount

v urr

L /

Withdraror

()

pour effectuer Test(SavingsAccount)

appelle SavingsAccount, Withdraw(


(noialSaleACCOUnt
Poccono
Iu trrn
roDqc
ulr
JPI
pour ef fectuer Test (Bankccount)
appelle Savingsccoun!.Wi lhdraw

()

pour effectuer Test (SavingsAccount)

? 323

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

- offre la

dnionstration

1^
.i--1^
l^
Id -1.,^
Pfu> bJilurtr u lrhr.i
-.------o-t:op

using

Systern;

nqmcnrn Tnhpri i:nr,pF,xemnl

^ Ufd
^1-^^ D-"eC1ass
-..L1.:
u
PUU.LfU
{

in ini
nrrhf
y|\vLLv

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)


(

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

ll

Console.l^Iriteline ("Utilisons un objet d'une sous-classe


SubClass sc = new SubClass
sc.nDataMember = 2i
sc.SomeMethod{);

:");

Ghapitre 13 : 0uel est donc ce polymorphisme

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

#"u$3
l_frrl,i

// tnheritanceTest

examine comment

narrf
ycuL

.,''

fra
sL!c

rrtiljc
uLrrtrE

le

mot-c1

n^'r.
yvu!

virtual

lih^ts
ldIM!

une nouvelle hirarchie d'hritaee


nanespace TnheritanceTest
{

using Systen;

public class Class1


t

public static

int Main(stringIJ strings)

/''\

Console.hlriteLine("\nPassage d'un BankAccount")


BankAccount ba = new BankAccount 0 ;

'Iest.t tDaj

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

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

Console. 1{riteline ( ) ;
Console. 1,lriteLine ( "Hit Appuyez
Consol,e. Read

return

sur Entre pour terminer,..");

0;

l
public static void Testl

(BankAccount account)

32

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

public class

- simule un simple

compte bancaire

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

// Output - classe abstraite qui affiche


ahctrent

( ctri nd
nrrhl i n rrn'i d Otrtnrrt
LyuL
\oLrarr

une chane

en11+hr1(+
vuLyuLULrfrr6/'

ri..

'

//

SubClassl

- inplmentation concrte

de AbstractBaseClass

nublic class SubClassl : AbstractBaseClass


t

override public void Output(string sSource)


{

ctrino

c = cSr1rcp ToTTnnpr{l'
!

'

rvuytJ

u!

\ /

'

Console.lliriteLine("Appe1 SubClassl,0utput0 depuis

[0]",

s)

]
]

l/

SubClass2

- autre implmentation concrte

*"L1';^ Lro
c"Lnlass2 : AbstractBase0lass
^1^^^ uuuuJ

de AbstractBaseClass

l,uurr
{

override public void Output(string sSource)


i

strins
- -- -"o s = sSource.ToloverO;
Console.l,./riteLine("Appe1 SubClass2,0utput0 depuis

{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")


SubClass2 scT

/
lesr (scrl
l

= new SubClass2 0

l attend confi.rmation de I'utilisateur

Console,i{riteLine("Appuyez sur Entre pour terminer,


Console.
]

Read

..") ;

?3|I

Chapitre l6 : Manipuler des fichiers en

C#

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

string

srd

sFileName

= nul1:

r'rr '

vhile (true)
{

try
{

//

saisie du non du fichier de sortie (Entre pour quitter)

Console.ldrite("Entrez un non de fichier "


* "(Entrez un nom vide pour quitter):");

= Console.Readline0
(sFileName.Length *= 0)

sFileName

if

t
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,

397

35

0uatrime partie : La programmation oriente obiet

Console.tr'lriteline ("Va1eur donne par


i.l

'l'^\trlnl

il111nl1tF11nat1^n
vuLPu

{ll}"
tvr

ll.

]
no - fnrrrnit
luJ L! -r-llt
ruul^^- rrno c'imnlo fnnnt'inn
I//I tnStri
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#

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 /

Fr

leSirean n'est pas la seule classe qui peut effectuer des oprations d'l/O

Hsurdesfichiers,maisc,eStbienellequireprsentenotrebonvieuxfichier

t\9,
Y

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 :

,/

:l':r lng,F,e zter,/-i-rlngr'^/rirer : Une simple classe de flux qui se


contente cle lire et d'crire des chalnes.

t/

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.

39 5

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

llcolearr

byt

l-.+,-.

sbyte

decimal

r
Decinai

double

Doub I

float
int

Singie
Int32

ui nt

UInt-12

cha

P,',-: e

Cha

T-+
I Li I U A

ong

ong

Uint64

ohicet
.-"J---

0b-ject

short

Int 16
Uinr,6

u1

usho

rt

Comment le sqstme de ttlpes est-il unif par


des structures communes ? Un exemple
int

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 :

drivent

i
I

I
I

TypeUnification

using

int et Int32
fait la nme chose

montre comnent

sont en
System;

namespace
{

TypeUnification

Ghapitre 16: Manipuler des fichiers en

rraurqPaLc

G#

nLLtr
^^^^';sControlLib

..^.i*^
utrrrB C,,^+^-,
JyLtrnr,

public class

Cl.ass2

public void

A_pub1ic

t
Console

i,iriteline

"C1ass2 . A_pub1ic " )

nrnientcd

rrni d R nrnf antori i l

Console,

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.

t/

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

correct.

393

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 =

newValue;

Test. D
]
/
I

dNewValue;

j nn
/ 0r:tnrrtl'rrnnt
oui
imolmente
vsLPu
LrrvuL
- aff iche toute mthode
Yur

ToString0

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

(),

ainsi que la redfinition


(), Test implmente

de la mthode ToSt: irrg O. En fournissant ToSt-ring

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#

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.
est appelle niueou de couplage. Une classe qui
-grf{c accde auxd'indpendance
membres
internes
d'une autre classe est dite fortement couple.
9-/ -i: \
=( ItY
O"t
qui
classes
n'accdent
I'une
I'autre que par des mthocles publiques
)
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

fornes

de contrle d'accs

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

ll
II

l
;

les

mthodes publiques sont accessibles


dans d'autres espaces de nom

I
Llo4

^ In

puuffL

/\
\,,r

'

| 7es mthodes protges sont accessibles


I I ta hirarchie d'hritage

c1ass1 . B*protected

travers

i /c1ass3 . B-protected ( ) ;
I 7es rnthodes prives ne

par d'autres classes

sont accessibles que par

39 |

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

montre 1es diffrentes proprits

d'un objet struct

using
using

System;

System. Collections;
namespace StructureExample
{

public j_nterface IDisplayable


{

string ToString0;
j

public struct Test : IDisplayable


{

I
I
II
I

struct peut avoir des nenbres donne


objet er de classe (statiques)
7es menbres statiques peuvent avoir des initiariseurs
une

private int n;
private static double d = 20.0;

I/
ll

on peut

les

utiliser

un constructeur pour

nembres donne

initialiser

d'une struct

publi"c Test(int n)
{

this.n = n;
l

ll une struct peut avoir des proprits d,objet


I et des proprits de classe (statiques)

public int

get { return n;
set { n : value;
J

l
public static double
{

get

{ return d; l

set { d = value;
i

Ghapitre 16: Manipuler des fichiers en

C#

namespace

Paint

{
L

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

args)

r
t

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

Paint. Paint0olor. StaticPaint ( ) ;


]
1

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.

389

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) :

ne,,v

(une fois

[] mc = new MyClass ItOO]


for(int i = 0; i ( ms.Length; i++)

MyClass

t
urc

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 :

ll

delaration d'un tableau du simple type valeur int

.i*+
i^+^-^-^ = IlW .:-+
rrrL fl
int f1n^l
Ll rrrL!r
[100]

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
I

t
hllhl1^

thf

MyStruct ms

n.

* 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#

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

387

346

0uatrime partie : La programmation oriente objet

Mais une rfrence un objet est dclare et initialise d'une manire


compltement diffrente :
public class

MyClass

public int

n;

l
MyClass mc;
lul{J

rrew, M,,a1^^
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,

public double

d;

public class

MyClass

nrrhli^
yuuarL
nrrhl 'i

'irrrL
n+

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

f'rF^l
ura

Laa
^l^

int Main(stringIJ strings)

public static
t

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, . .")
0

Console,Read

return

0;

//
II
ll
tt

- 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

Console.lrlriteline("La valeur de ic1 est i0l et cel1e de ic2 est {11",


ic

string

1 . GetValue (

ic

2 . GetValue ( )

s;

switch (ic1,

CompareTo(ic2)

case

0l

s = rrest ga1 ";

break;

'

case -1:
s = rrest plus

petit

que";

break;

case

1:

s = I'est plus grand que";

break;

default r
s * "quelque chose qui cloche";
break;

l
Console

l,lriteline

"Les objets eux-mmes considrent que

ic1 t0J ic2", s);

Abstractlnterface

est encore un programme un peu long, mais relativement

simple.

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

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 \ Ilplutt
:(dq9
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.

383

3 42

Ouatrime partie : La programmation oriente objet

Tri de la
Alouette

liste

des oiseaux

Corbeau

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

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;

override public string

Message

(+"ina
Fara+/rtT^!
uLL1116tru!ltroL\

petf rpttrrn
6Lr!sLurrr

jllcbSd8e
-ao-^^^

gbL
^.-

lInl\
\tuJ

/,

lrnh jat
- _-L

oqt

gl'fsssnoo mrthnhiant rr1g1ling0 ) ;]


]

override public string ToString0


{

string s =

Message;

s *= "\nException
s *= TargetSite;

envoye par";

rt1rrn c.

//
II

MathClass

public class

- collection de fonctions

mathnatiques

de nia cration (pas encore grand-chose montrer)


MathClass

private int nValueOfObject;


--i,,^+^
^+-.:-- -_-J__
chianfllocnrintint
pr.J_v.1Le sLrLltg
rl,.__.1

public MathClass(string sDescription,

int

nValue)

nValue0f0bject = nValue;
s0bjeetDescription : sDescription;
]

public

//
II

int

l{essage

Value {get {return nValueOf0bject;}}


- affiche le nessage avec 1a valeur

l'objet

public string

de

Math0lass attach

Message

get
{

return String.Fornat(''({0J = {1J)",


s0bjectDescription,
nValue0f0bject);

l
]

// ToString -

combj.ne 1e nessage

personnalis avec

[]Jrl

38 |

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

liane;

l
]

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: \

V '

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

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

n'rhli^

"^.i'1 f1 /)
!r

vvrs

\/

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)
I

t
I | . . . traite une partie de l'erreur
Console.l.lrireLine ("Exception attrape dansfl () ")
poursuite du cheninement de l'exception
ll .
+1^-^,,.
Llltuw

37I

338

0uatrime partie : La programmation oriente objer

public string

Nane

of
D''
{

rt1r rn sNemo

l
nrrhlic dorrhle

Grade

ger
{

return

dGrade;

l
l

//

implmente

f interface

I0omparable

(nnc. e o
| vvruyeriu
anraraTn - LvltrHat
nrrtro
nh-iet
o rrn
ull
auL!
vLi
_^^rqnrr

|| |

I
II
II
I

,^aa.

des objets Student)

et dcide lequel

vient aprs l'autre

dans le

tableau tri

public int CompareTo(object rightObject)


{

I conoare
Student courant (aooelons-le
--J
- -1'obiet
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");

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)

I
I
if
/

Oa classe Double contient une mthode CompareTo0


que nous aurions pu utiliser 1a place)
(rightStudent.Grade

leftStudenr.grade)

leftStudent.Grade)

rotrrrn

-1.

if

(rightStudent.Grade

return

1;

return

0;

]
l l inpTnentent

f interface IDisplayable :
// GetString - retourne une reprsentati.on de 1'tudiant

Ghapitre 15 : Ouelques exceptions d'exception

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

Une fonction comme f

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 f2 ( ) n'attrape qu'un seul type d'exception. Elle recherche
une certaine classe d'erreurs. Par exemple, l"fyException peut faire partie des

37 7

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

// fnisplayable - objet capable de se convertir


t
une chaine affichable

lui-mme en

I
'i

ntprf :no

Ti-l'i

c.1 ayable

// Getstring - retourne votre reprsentation

string GetString0;

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

II

(IComparable [] ) students;

interface IDisplayable affiche maintenant 1es rsultats


[] displayableObjects = (lnisplayable ll ) students;

IDisplayable

DisplayArray (displayable0bjects ) ;

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

Bird[] birds = Bird.CreateBirdlist0


I I renarquez qu'il n'est pas indispensable de faire un cast
;

Chapitre 15 : 0uelques exceptions d'exception

Lasser quelques enlois t/ous fler entre les

doigts

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 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
i

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

)")

37 5

33 4

0uatrime parrie : La programmarion orienre objet

Bart
Lisa
Maggie
lYrv-

fr-(
llr9r,
Y

:50
:100
:30

,/

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

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
Lrrtty

+
{

SomeOtherFunction

catch(MyException

me)

i/

tous 1es objets MyException sont attraps

ici

37 3

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

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.

Je veux

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

-^

interface IDisplayable
{

retovne votre description

string GetString0;
'1

La classe Student suivante implmente


s1 ciass btuoent : IDisolavable
i

private string sName;


private double dGrade = 0.0;
/l accde aux mthodes en lecture seule
-..L1.i^ -+-.:-^
L! rrr
yuvf,ru

Name

get
{

return

sName:

l
]

public double Grade


{

Idisplayable

Ghapitre 15 : Ouelques exceptions d'exception

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.
rr|r

t\\u--

.1

L'indicationdepileestdisponibledansl'unedesfentresdudbogueur

If Cff I
Y

de Visual Studio. Je la dcrirai au Chapitre

16.

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 classee Exception standa


standard
I
une rfrence MyClass

public class CustomException

Exception

private Mvclasss

,,1::i':::.

Custonnxception(string sMsg, M.,cl.dd


MyClass no)

base(sMsg)

{
tyob3'..a = *0,

//

permet aux classes extrieures d'accder une classe d'information


MyClass MyCustomObject{ get {return myobject;J}

public

37 I

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

il

string

fait
..*-'1.-.Ycuelsue

sNote)

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)

prendre une note sur un bout de papier

l
PUUJr-U UJ-dbb

PDA

: ElectronicDevice,

PUUTTU VVtU TakeANote

(string

sNote)

IRecordable

Chapitre 15 : Ouelques exceptions d'exception

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

ltrnl rpr I'aCCUmUlateUf


II ^^i^ ..-1^,.- Lrbtenue
LO VOf gU!
PAL

I I

do
{

dFactorial *=

nValue;

] while ( - -nValue ) l)
// retourne 1a valeur stocke dans 1'accumulateur
;

return dFactorial;
]

nrrblic c1ass Class1


{

public static void Main(string[] args)


{

try
{

/ltt appelTe en boucle 1a fonction Factorial


for(inti=6;i)-6;i--)

de 6 -6

/ilt calcule 1a factorielle du nombre


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.

369

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

prendre une note sur son

pDA

public class Laptop :

ThingsThatRecord

override public void TakeANote(String sNote)


/

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

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


L4vrr

urrv

\ /

/l

ceci est

fait

pour ttraper une erreur

try
II
I

appelTe une fonction

SomeOtherFunction ( )

ll

autant d'autres appels que vous voulez

catch(Exception

e)

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
I

l
l
nrrhf
PueffL

i
|

in vnid Ss6sQtherFUnCtiOn0

l'erreur se produit

quelque

part

dans 1a fonction

36 7

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

i = 6, factorielle
1 = ), tactorl.el_le
i = 4, factorielle
-i = I

i
i

fentnriallo

= 2, factorielle
- 1, factorielle

- tzv
- rlv

:6
-L
-1

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

36 5

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.

Wi

thdraw

pour effectuer Test(SaleSpecialCustomer)


appe

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

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

public static double Factorial(double

dValue)

// 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

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

I par 1a valeur

obtenue

do
{

dFactorial *= dValue;
dValue -= 1.0;

I while(dValue ) l);
// retourne 1a valeur stocke dans

1'accumulateur

return dFactorial;
l
h'!hlr^
yuurrL

^t^^^
Lld

/uI4D
t^d^1

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

du nombre

double dFactori-a1 = MyMathFunctions.Factorial(i)

if (dEactorial

== MyMathFunctions.NEGATIVE-

NUMBER)

I
Console

}trriteLine

("Factoriai0 a reu un nombre ngatif");


break;
l

chaque

fois

363

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

ir" stat'i c

vo'i

Tpsf

i l'Snpeial SaleAccount account)

(" pour effectuer

Console,WriteLine

account, Withdraw( 100)

Test (SpecialSaleAccount) ")

l
public static void Test4(SaleSpecialcustomer

account)

Console.Writeline(" pour effectuer Test(SaleSpecialCustomer)")


account . withdraw

00)

//
II
II

BankAccount

- simule un compte bancaire

possdant

un numro de conpte (assign 1a cration


du compte) et un solde

publie class

BankAccount

il

Withdrar,'ral

- tout retrait est autoris jusqu' 1a valeur


du solde ; retourne 1e montant retir

virtual public void

Withdraw(double dWithdraw)

Console.Writeline(" appelle

BankAccount.Withdraw()")

//

SavingsAccount

compte bancaire

public class SavingsAccount :

qui rapporte des intrts

BankAccount

override public void Withdraw(double dHi-thdrawal)


{

Console.i,{riteline(" appelle

SavingsAccount.Withdraw0")

// SpecialsaleAccount - compte utilis uniquement en priode de soldes


ie elass SneeialSaleAccount : SavinssAccount

nrrhf
{

new

virtual public void

Withdraw(double dWithdrarral)

Chapitre 15 : 0uelques exceptions d'exception

II

rnnol I e on
uvuaE^
|/ |I ofyf,rL
rr hnrrnl

1a fonction Factorial de 6 -6

for(inti=6;i)

1--

t
I

I affiche 1e rsultat chaque

passage

Console.Writeline("i = {01, factorielle = [].1",

i,

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

36t

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.
Je parle ici des erreurs I'excution, et non cles erreurs de compilation

f
"9tt$
ilfi ) qu" C# vous envoie la tte lorsque vous essayez de gnrer votre
,
U-/ Programme.

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#

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.

tz

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.

3I I

Chapitre 16 : Manipuler des f ichiers en

C#

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.

^.v7q

=qE/

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

40 I

Chapitre 16: Manipuler des fichiers en

C#

break;

ll

erreur envoye indique 1e non du fichier et 1'erreur

\
nrl.n l,r)F.Ynn11
\-----'-r --' on Iel
{

Console

I,]riteline

" {0J

\n\n" , fe.

Message)

I I lit 1e contenu du fichier


Console.i,lriteLine("\nContenu du

fichier :")

try
t
I

1:- ..-^
| l-J.L
Urltr f1.:--:
fBrrt 1a fOiS

I I

r,vhile

(true)

I lit

une ligne

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

LIriteline ( slnput

qu'il a 1u dans 1e fichier

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

403

Chapitre 16 : Manipuler des fichiers en

C#

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

d'un fi-chier texte lire :TestFilex.txl

Entrez le

nom

Could not

find fi1e

Entrez 1e

nom

Contenu du
,Te

tane

"C:

\C#Prosrams\FileRead\TestFilex.txtr'

d'un fichier texte

fichier

lire:TeslFilel.txt

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

=(t
^*\

que nous avons cr avec le programme Fr-ei'ir

lte.

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).

40 5

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.
Je dois nuancer quelque peu l'affirmation qui prccle si vous avez clj

^dK cr des applications


:Hqfl
{/ /) comme C++.

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

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.

4l I

Ghapitre 17:Grer une application Windows: le ramage et le plumage

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.

Slectionnez Fichier/Nouveau/Projet.
La fentre Nouveau projet apparalt.

2.

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

-grfl(c
-i: \
-.-/
=( fA )
\t/

crer dans c:\Programmes c/\simpleEoitor.

3.

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

::'::

l -' '!'

:.t ,r i.r remt.r [&99]

?i"ffi,,:li#"i:'l;|,t2,';',. -inl:J

tigure 17.2:
L'affic h age
initial pour
toutes les

applications
Windows.

rm8ordr5i/

5,rable

FqhtToLeft

li.

Ir--t
ina,."r.

t.,:.1

AIc\4tro!
::t te lW,
nnd
ImetlDde
B t;tytt :, ...

FisF

l3qrri
t te

l,n:!nt.r
.

Ei iorEniPnpr'

Ie*
LE

lexind6u

&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.

4l3

- Ghapitre 77 : Crer une application

Windows: le ramage et le plumage

lgnorez ce ttlpe qui se cache derrire le rideau


a

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

(sumnary)

I Snnnerv dpsc ri nti on for T'orml


lll (lsunnary)
nrhl j^ nlrcc l'61911 : SyStenr.WindOvS.FOrmS.FOrm
II

/i/
III
III

(sunmary)

Required designer variable.

(lsmnary\
private System.
public Forml 0

ConponentModel,

Container components = nul1;

lt
/
lt
tt

Required

for

Windows Form Designer support

InitializeConponent

lt
i

TOD: Add any

constructor code after InitializeComponent call

lt
l

lll

kunnary)

II

clean up any resources being used.

I
III

(lsumary)

protected override void Dispose( bool disposing


r

if(

disposing

if
f

(conponents != nu11)

4l 5

4t6

Ginquime partie: Programmer pour Windows avec Visuaf Studio

components.Dispose0;
l

l
cnncin-rrI
heca
D'i cnnco (\ rli
v4s . urDyvoc
uIDyu

I
,,

.
t

#region Windows Form Designer generated code

I
///
III
lll

(sunrnary)

//

for Designer support - do not modify


the contents of this nrethod r+ith the code editor.
llsunnary)
private void InitializeConponent0
Renrrirerl method

For:nt

this.AutoScaleBaseSize

this.ClientSize =
this.Name

this.Text

new System.Drawing.Size(5, 13)


new Systen.Drawing.Size {292, 273);

= "Forml";

"Sirnple Editor";

///
III
lll

(summary)

fhe main entry point


(lsunnary)

for the application.

ISTAThread]

static void

Main0

Annl
4ytl!rLo

inatinn
rrvtl

.
i/\
Rln /nor.,
.l\ulr
I'^rml
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.

Figure 17.3
Changer la
proprit

-Text du

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

Fichier Edition

r:I

Frrrt

t{J

-ll IirJ+ *rr 1 -

p trI: t -'rn

=rl

lus grand
Figure 17.1
Ma solution
du problme
:

-SinpleEditor.

ralb

s-

'Je

police

jf-

)|

14

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/

L'utilisateur peut entrer et effacer du texte (sinor-r, ce rle serait pas


vraiment un diteur).

t/

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/

SinltcEcr*.or supporte les polices en gras, en italique ou les deux.

t/

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, 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 :
Le programme commence par une boucle rihr

Filel^i

rite.

Le premier argument est le nom du fichier.

t/

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.

t/

Le dernier argument indique que je veux lire

File-tream. Les autres solutions sont

partir de ce

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 ./

FileRead et Frle'ririte reprsentent deux manires diffrentes de traiter

Hdesexceptionsdefichier.VouspouVeZinsrertoutleprogramrrrede
traitement de fichier dans un mme bloc rr-,,, comme dans Fi le,.,r rte, ou
tTg,

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

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

C"^+^*.
uJ
Lclr

rrc"ino
*- "'^b

Svctonr

irtucy4Lg

0'

f -Lgr\u

vaq
^ ..^ ,'.ss]

yuufrL

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)

en

while {true)
t

f-- r\rJ

i
I I Iit le non du fichier d'entre
Console,Write("Entrez le nom d'un

sFileName

= Console. Readline 0

fichier texte lire :");

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

Enfin, le fichier est ferm par I'instruction

b'

sw . C1o

e()

.t^i

riteLine

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

I
|

ob

jet

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 ) ;

l
I

I ferne 1e fj-chier

sl^r. C1ose

sw

que nous avons cr

= 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
rrvru uu
| |t dJUULC
Lvr!crc Dar
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

Writeline (fe

Message)

]
]
I

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.

rs9Ka^ Les dounes lisiltie.s ;iar un tre hurnain taierrt antrieurement

Ae/'!-t\

:(dqfl
\/

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

I I c|ass2. D-internal ( ) ;
c1ass3.D -internal 0 ;
I I es mthodes internes protges

sont accessibles

// soit par 1a hirarchie d'hritage soit


I I toute classe ciu mme espace de nom
class I . E-internalprotected
class3 . E-internalprotected

que par

par

// attend confirmation de 1'utilisateur


Console,WriteLine("Appuyez sur Entre pour terminer. . , ")

1/\

uons0ie. Kea0

return

0;

l
-r,hr-^
yuurru

(,r

,r^rd
vvfu

/\

|w_y!
--lvatel/

!riteline ( "Class

Console,

1,

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

nrrhlin vnid A nrrblic0


t
Console

l,Jriteline ( "C1ass3 .A-pub1ic " ) ;

nrntpntpd
y!vLLLu

vnid
B nrotectpdo
rlv!s\/
vvru

Console.hlriteline ( "Class3 . B-protected")

internal

voi.d D-internal o

t
Console . 1,,Iriteline

("

Class3 . D*internal" )

l
^,rhlin
yuurJL

,r^i,l E' inf arnr'lnrntontod


v

u_frr

v vfu

LvL

uee

\ /

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


p;lr son noni pleinentent qualifi plcut clevetiir utt
peufasticlieux. Lernot"cli.r::,:rg_cleC#vousperntetd'vitercepetlstltlt"

Se rfrcrr rrnr: r"lasse

,-i

,,,;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
{

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

-I-

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

HuneSpaCeclenomportantlemrnenOmquelerpertoirequ'ilcre.Exami-

t\7,
V

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...

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

0). I'ohiet est

(Value = 0)

Annrrrraz crrr Fntro nnrtr tprminor

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

le

message standard Exception. ToString

override public string ToString0


t

string s = Message * "\n";


s *= base,ToStrine0;
]

//

Inverse

lix

retourne

public double Inverse0


I

if (nValue0fObiect ::

0)

throw new CustomException("Inpossible d'inverser

0", this)

'I

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

//

attend confirmation de 1'utilisateur

Console.tr{riteLine("Appuyez sur Entre pour trminer., .")


Console.Read0;

l
]

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

Exception

private

MyClasss myobject;

MyException(string sMsg, MyClass mo)

base(sMsg)

nyobjec-u = moi
]

ll

pernet aux classes extrieures d'accder une classe d'information

public My0lass My0bjectI get Ireturn myobject;))


]

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.

f\

e,

q/\

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.

Writeline

(e. Message)

]
I

I tZ ' -

prparez-vous attraper une exception MyException

f?/h^^1
!'
bExceptionType)
^,,11.i^',^;,r
VVaU
\wvv
PUUIrL

{
t rrt

i
f? thFvnant; ^.Type)
rJ

\vu.r!vyL4vrrr

catch (MvExceotion

me)

Console.liriteline("Exception MyException attrape dans f20")


. Writeline (me. Message) ;

Console

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

public void f4(boo1

bExceptionType)

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)
{

les utres exceptions non encore attrapes sont attrapes

ici

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

suivante : caT-ch (Exception).

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.

Af\
=(t

tout

Faites toujours se succder les instructions catch de la plus spcifique


Ia plus gnrale. Ne placez jamais en premier I'instruction catch Ia plus

gnrale

public void

SomeFunction0

try
{

SomeOtherFunction{);
l
catch (Exception

me)

// tous

1es objets MyExcepti.on sont attraps

ici

l
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
{

oprations pra1ab1es 1a fonction exemple

SomeOtherFunction ( )
I

autres oPrations.

l
catch (MyExcePtion

me)

// vous avez toujours accs

aux nthodes d'Exception

strj.ng s = me.ToString0;
I I nais vous vez aussi accs toutes 1es proprits et
// de votre pr0pre classe d'exceptions

nTthodes

MyClass mo = me.MYCustom0bject;
I I par eremple, denandez 1'objet MyClass de

lui-mme

s'affi-cher

string s = mo'GetDescriPtionO;
]

public void Sone0therFunction(

cratian de mYobject
= ner^r MyClass0;
signale une erreur concernant myobject

MyClass myobject
I

throw nev MyBxception("Erreur dans 1'objet de MyClass", myobject);


reste de la fonction
II .
l

) 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

Dans ce fragment de code, SoineFunction

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.
Le bloc catch situ la fin cle Iii:1r i

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.
.($-_.-

fLapropritE>lile.1l1-t-.l..'l],':]:,.:1;l.retOurnleunSouS-enSemblepluslisible,
J-^t
mais moins descriptif cles informations sur I'erreur.

Ilorf
Y

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.

('ai un peu arrang les

La sortie de ce programme apparalt comme suit

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

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

System.Exception: Argument ngatif

Appuyez

sur Entre pour terminer...

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

System;

namespace

FactorialException

i/
ll

MyMathfunctions

collection de fonctions

mathmatiques

de ma cration (pas encore grand-chose montrer)

public class

l'lyMathFunctions

// lactorial II

retourne
fournie

la factorielle

public static double Factorial(int

d'une valeur

nValue)

i/ interdit les nombres ngatifs


if (nValue ( 0)
{

//

signale un argunent ngatif

string s = String.Forrnat(
"Argument ngatif

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

MY_ERROR_1;

if (errRtn == SF-ERROR2)
{

Console.l'IriteLine("Erreur de type 2 sur appel someFunc0");

return

My_ERROR,2;

l
I

appelle SomeOtherFunetions,

errRtn : some0therFunc0
if (errRtn == S0F*ERRORI)

1it 1'erreur,

retourne, et ainsi de suite

Console.l,lriteLine("Erreur de type

return

sur appel someFunc0");

sur appel someFunc0");

MY*ERROR-3;

if

(errRtn == SOF-ERROR2)

Console.liriteline("Erreur de type
return MY_ERROR_4;
)

Ce mcanisme prsente plusieurs inconvnients

t/

Il est trs rptitif.

t/

Il oblige le programmeur inventer de nombreuses indications


d'erreur et en maltriser I'emploi.

36 4

Ouatrime partie : La programmation oriente obiet

if

(dFactorial =: l4vMathl'unctions
\eL

ev

ev

++

--J

NON*INTEGER*VALUE)

Console.

I^Iriteline

("Factorial0 a reu un nonbre

non

entier");

break;
]

I af.f.iche le rsultat chaque passage


Console.I^IriteLine("i = {0}, factorielle = {1}",
I

i, MyMathFunctions.Factorial(i)

1
J

//

attend confirnation de 1'utilisateur


sur Entre pour terniner.. . ")

Console.l^lriteLine("Appuyez
Console.Read{);

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.

^"ffi

=(dg,

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

Factorial}JithError

crer et

using

utiliser

une fonction

factorielle qui ne contj.ent


vrification

aucune

System;

nnespace FactorialWithError
t

//
I

Uyttathfunctions

collection de fonctions

nathmatiques

de ma cration {pas encore grand-chose montrer)

public class

MyMathFunctions

i/
I

Factorial

retourne 1a faetorielle d'une valeur


fournie

public static double Factorial(double 0varuel


t

/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
]
trwr-d.55.1.
t^ddI
\-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 ( )

l
haee

urDyvrrri
\ ,lian^ain.

Di(b^<(
---

).
,/

'

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. ClientSize =

new Systen. Drawing. Size

( 292

this.Nane = t'Fornl";

273)

this.Text = "Sinple ditor";


l
/lendregion

///
III
lll
I

(summary)

ftte main entry point for the application.


(lsunnary)

stlthread

static void

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

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,

^tK
'(dE,)

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

programmation le plus rpandu pour I'environnement Windows. Tous les


meilleurs accros de la programmation s'en servaient. Mais a fait dj quel-

que temps que C++ voue son ge.

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

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.

-K

=($9,
4.

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...

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.

t/

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)

public class MyClass :

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/

Vous avez une instruction

t/

Plus vraisemblablement, vous avez calcul une valeur et vous ne


I'avez jamais retourne.

if

avec un

return sans valeur spcifie.

47t

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...

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

469

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/

L'utilisateur peut entrer et effacer du texte (sinon. ce ne serait pas


vraiment un diteur).

t/

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 r-

lrLl

t/

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...

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

MyClass

public void

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

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

MyClass

nrrhlic
yqurru

rrn'i
qrr! L*vrr
Y vv d SomrrE'rrnntinn(l
uwu!
\ /

{
Irt

tl t

I ceci fonctionne parce que C# ne retourne une valeur


// i1 ne psse pas une valeur dans la fonction
I

Some0therFunction

(out n) ;

public void Some0therFunction(out


t

int

n)

que dans

t+67

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...

ceci fonctionne trs bien

float fResult = 2 * f;
return fResult;
]

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

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

neis nc n1lr
1.,*-

*a ra]-son
1

que vous croyez

char c3 = c1 * c2;
]

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
I

cecz fonctionne

trs bien avec 1e cast explicite

float fResult = (float) (2.0 - f);


return fResult:
j

465

Chapitre 19:Les dix erreurs de gnration les plus courantes...

cf oii

n nlhl

rrni rl MrrFrrnni'i nn f\vguuurrL


StUdent

S)
e/

: r * s.sStudentName)
d'identification de 1'tudiant = " *

Console.Writeline("Nom de 1'tudiant
Console.WriteLine("Nunro

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

MyClass

static public void Averagelnputo


I

int

nCount

0;

while (true)
{

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

I ajovte la valeur entre


f= n;

nSun

nCount#;
1
J

// 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 ().

463

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

Le premier argument est le nom clu fichier.

t/

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

Fi-ieStream. Les autres solutions sont ,,.,'r.ie et

F.e'acj,'ir

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 ./

FileRead et Fiiei^i

rite

reprsentent deux manires diffrentes de traiter

Hdesexceptionsdefichier.VouspouVeZinsrertout|eprogrammecle

t(?,
Y

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 !

//

]-' : ''-.:

1it un fichier texte et 1'crit

FileRead

srrr 1e console
,,^.i*^
uarr5

Q"-+^-'
uje!rr

urrr

uy;!r,a!,

irarrg)ydL:

f rf Ei\cou

public class Classl


i
rrrhl ir

ct:-ie

rrni.i
]nai.
/c.ri.vvatl
!larrr
\Lr4l!

flLJ args)

// 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

Console. Readline

fichier texte lire :");

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

FileAccess. Read)

1/ convertit ceci en StreamReader - ce sont les trois preniers

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

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.TouteS

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.
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.

,tggler^ Il n'est

5e !!\
:HW )
'J'/

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

private void ApplieationWindowClosing(object

Windows

sender,

Systern. ComponentModel. CancelEventArgs e)


{

1r (!IsChangeOK0)
t

e.Cancel = true;
]

5.

Gnrez le programme et excutez-le"

6.

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 difficile. Au temps anciens de Windows 3.1,
c'tait une montagne.

Quoi qu'il en soit, ne vous laissez pas dcourager. Pensez la prsentation et au fonctionnement que vous attendez de votre application. Mettez
tout cela par crit. Alors seulement, vous pouvez utiliser le Concepteur
de formulaires pour dessiner les lments qui la composent. Utilisez la
fentre Proprits pour identifier les proprits, statiques et dynamiques,
que vous voulez dfinir afin que I'application fonctionne exactement
comme vous voulez.

457

398

Ouatrime partie : La programmation oriente obiet

|
I

|
/

FileAccess.Write,

fs

FileStream

FileAccess.ReadWrite
= File.0pen(sFileName,
FileMode. CreateNew,

l/

gnre un

FileAccess.lJrite) ;
avec des caractres

flux de fichier

UTFS

sv = new Strean,I,.Iriter(fs, System.Text,Encoding.UTFS) ;


I I lit une chaine 1a fois, et envoie chacune au
// FileStream ouvert pour criture
Console.1,rlriteline("Entrez du texte ; ligne blanche pour arrter");
while (true)
t

/ |it 1a ligne suivante sur 1a console


// quitte sj.la ligne est blanche
string slnput = Console.ReadLine0 ;
i.f (s Input . Length == 0 )
I

break;
l
I

I crit

sw.

l
I

lerne

sw. C1ose

sw

sur 1e fichier de sortie la ligne qui vient d'tre

|Iriteline

le fichier
0

1ue

slnput ) ;
que nous avons cr

= null;

l
catch(IOException fe)
{

ll une erreur s'est produite quelque part pendant


ll 7e traitement du fichier indique 1'utilisateur
// Ie nom eomnlet du fichier
I | ^:^,.-^ AU
drr !Pc!LVi!
rnerfnirp Dar dfaut
^.' -m
rrutr!
UU
I I JVULS
// celui du fiehier
:

l.

ctrino

cDir = flircctnrv

GptCttrrpntDirpetorvO

string s : Path.Conbine(sDir,

Lv!J

sFileName)

\ /

:,

Console.l,lriteLine("Erreur sur 1e fichier{01 ", s)


I I af.fiche naintenant 1e nessage d'erreur de I'exception
Console . Writeline (fe.Message) ;
;

i
l
I

attend confirmation de 1'utilisateur

Consol-e.l.lriteLine("Appuyez sur Entre pour terminer. . . ")


Consoie.Read0;

File\nirite utilise I'espace de nom Sirstem. i0 ainsi


contient les fonctions d'l/O sur les fichiers.

QU S-,'sten.

!i.,'sten.IO

Chapitre 18:Achever votre application

Windows

contenu de la zone de texte. Dans le Concepteur de formulaires, slectionnez


encore une fois le composant Rr. ],Te:<r-Br-:. pttis, dans la fentre Proprits,
attribuez le nom de mthode T.-:':tr-harq,,:r la proprit Te>:tChanged. Cette
mthode ne fait rien de plus que d'assigner la valeur voulue notre indicateur

// cette urthode est appele lorsque 1e texte est nodifi


private void TextChanged(object sender, System.EventArgs e)
t

bextChanged = true;
]

ll est temps d'essayer le rsultat

1.

Dans le programme pralablement rgnr, ouvrez un

fichier

RTF.

2.

Faites une modification quelconque dans la zone de texte.

3.

Slectionnez Fichier/Quitter.
La bolte de dialogue d'avertissement apparalt, comme dans Ia
Figure 18.8. Vous pouvez pousser un soupir de soulagement.

Figure 18.8 :
(i mn l oFrl'i ,: r, i^

fait apparai
- un
tre

eci est le te:rte original.

avertissement lorsque
| 'utilisate u r

essaie de

J'ai crit ceci deputs le


dernier en registremenl du

faire quelque
Le

chose qui
provoq uera it

texle

a t nradifis, i:liquer 5uf

l-JNi

pour ignrrrer os mndifrr;tilns

aille de p

la perte de
ses dernires modifications.
J

4.

Cliquez sur Oui, et le programme se ferme.

D.

Rptez le processus en commenant par l'tape l. Cliquez sur


Non, et le programme continue comme si rien ne s'tait pass.

655

39 6

Ouatrime partie : La programmation oriente objet

l/0 asynchrones : est-ce que a vaut la peine d'attendre

Normalement, un programme attend qu'une requte d'l/0 sur un fichier soit satisfaite avant
de poursuivre son excution. Appelez une mthode read ( ), et vous ne rcuprerez gnralement pas le contrle aussi longtemps que les donnes du fichier ne seront pas installes
bord en scurit. C'est ce que I on appelle une l/0 synchrone.

Avec C#, les classes de System.I supportent galement les l/0 asynchrones. En les
utilisant,l'appel read ( ) restitue immdiatementle contrle pourpermettre au programme
de poursuivre son excution pendant que la requte d'l/0 est satisfaite l'arrire-plan. Le
pr0gramme est libre de vrifier ['tat d'un indicateur pour savoir si 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 quivont aller dessus.

Avec des 1/0 asynchrones,vous p0uvez c0uperles oignons pendantque la viande hache est
en train de cuire. De temps en temps, vous jetez un c0up d'il pour voir si elle est cuite. Le
momentvenu, vous abandonnez un instantvos oignons, etvous prenez la viande sur la plaque
chauffante pour la mettre sur le pain.
Les l/0 asynchrones peuvent amliorer signif icativement les perforrnances d'un programme,
mais elles ajoutent un niveau supplmentaire de complexit.

Utlser Sj r r:eijilr\,\iriter
Les programrnes gnrent cleux sortes de sortie. Certains programrlres
crivent cles tlloc:s cle clorrnes clans un pur fornrat binaire. Ce type cle
sortie est utiie polrr stocker cles objets cl'une nranire efficace.
Beaucoup de prograrlnres. sirron la plupart. lise'nt et crivent des chanes de
texte. lisibles pilr rrn tre lturrrairr. Les classes cle flux S-,i eaiiiii:i'.er et
St-rearI'.eri,,,::r sont les 1>lus sc.ruples cles classes accueillantes pour I'homnte.

4$!Qa^ Les donnes lisibles lrar tin tre htrmain taient antrieurement cles
chalnes ASCII, (.)ri. url per-r plus tarcl, r\NSL Ces cleux siglt-'s se rfrent aux
^<v7q\
de sTandarrJisation clrri orit dfiiri t:es formats. Toutefois, le

:/dw

Y/ )

organisations

codage ANSI r)e penuet lras cl integrer les alphabets venant cle plus loin
qtre I'Ar.rtriclre l'l-st, ert cle prlrrs loin que: Harvai l'[)Lrest. Il ne peut contenir que I'alphabert l;rtin. II ne clispose pas de I'alphatret cyrilliclue, hbreu,

Ghapitre 18:Achever votre application

Windows

cet indicateur lorsqu'un fichier est lu (rien n'y a encore t rnodifi). Saisir du
texte. collper clu texte ou coller du texte dans la zone de texte assiqne - r',re

cet inclicateur. L'excution de la cornmande Enregistrer assigne r'rouveau


f a.L se I'indicateur. l)onnons celui-ci le nom bT+::'ihar,ie r.

Naturellement, I'utilisateur peut toujours quitter le prograrnme, nrme si


la dernire version de son travail n'a pas t enregistre, mais il clevra
maintenant dcider consciemment de le faire (atrtrement dit. cliqLrer.sur
un bouton OK dans une fentre d'avertissernent).
C'est ce que fait avec simplicit la mthode *sC1,.r:rqeOi'i,

i suivante

ll Ia mthode suivante garantit que 1'utilisateur ne perdra pas


ll ses modifications par inadvertance, en affichant un nessage
| | si la RichTextBox n'est pas "propre"
bool bTextChanged = false;
private bool IsChangeOK0
{

// i1 est toujours sans inconvnient de quitter


ll si rien n'a t modifi
if (bTextChanged == false)

1e programme

return true:
]
I
I

I nais quelque chose a t modifi ; le programme denande


I I'utilisateur ce qu'i1 veut en faire

DialogResult dr = MessageSox.Show("Le texte a t modifi. "


t "Cliquez sur 0K pour i.gnorer vos modi.fic.ations, ",
"Texte nodifi",
MessageBoxButtons

YesNo)

return dr == DialogResult.Yes;
l

IsChange0K

() retourne true si I'utilisateur est d'accord pour perclre

les

dernires modifications du contenu de Ri chText6r,.x. Mais avant tout, si


I'indicateur de modification a pour valeur f a1se, c'est que rien n'a chang
depuis la dernire commande Fichier/Enregistrer, et que rien ne peut donc
tre perdu.
Si quelque chose a t modifi, la

fonction ouvre une bolte de clialogue


l'{essageBox pour demander I'utilisateur ce qu'il veut faire:continuer et
perdre ses modifications. ou annuler et les conserver. La clasSe i.ir: - s r J.,.lr,rrr..
est aussi simple que cette bolte de dialogue. La mthode Sh,:rr, () r-ruvre une
bote de dialogue avec le titre et le message spcifis. La proprit i esl,tr dit
"Faire cette bolte de dialoeue avec un bouton Oui et un bouton Non." Si

453

39 {f

Ouatrime partie : La programmation oriente objet

,/

Une mthode dclare internal est accessible par toutes les classes
du rnme espace de nom. Aussi, I'appel c1ass2. D_internal () n'est

pas autoris. L'appel c1ass3. C_internal O est autoris parce que


Ciass3 fait partie de I'espace de nom AccessControl.

t/

Le mot-cl irrtei nal protected combine I'accs internal et


I'accs protectecl. Aussi, I'appel classl. E_internalprotected o
est autoris, parce que C1ass1 tend C1ass2 (c'est Ia partie
iLJtocr ed). L'appel class3.E_in.erna.J pr ocecred () est galement autoris, parce que Classl et Ciass3 font partie du mme
espace de nom (c'est Ia partie internal).

t/

La dclaration de Class.J comme

internal a pour effet de rduire


I'accs celle-ci internal, ou moins. Aussi, les mthodes public
cleviennent internal, alors que les mthodes protecteci deviennent
inte rnal Lr rotected.

Ce programme donne la sortie suivante

Class2 . A_public

B-protected
C_private
Class3 . D*internal"
C1ass2

C1ass1

C1ass2

E-internalprotected

Class3 . E-internaLprotected
Appuyez sur Entre pour terniner.

^1.$t

lX
K7,
Y

..

ble. 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 t'tathRouri nes 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.

Rassembler des donnes dans des fichiers


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 (l/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.

Chapitre 18:Achever votre applicarion

4.

Figure 18.6
Donner le

Faites la mme chose pour I'option de menu Fichier/ouvrir, en


utilisant Ie nom de fonction FileOpen.

,tii

fichier Edition {ffir:hage pJ.rieL rrer Qbnquer Dqnnes lutil:

n0m

FileSave

Windows

-*filffi

la proprit

.=!-..1

Click

Forml.cslDesign]*

du

sous-menu

tai!/i!::11:;

Enregistrer
gnre une

:i

Fe1ke

itit

Help

trEtul

1,,,

:,,,,,

Fichier Edltion Frnt


u\/flf

nouvelle

fonction, qui
sera
invoque
chaque fois
que I'utilisa-

teur
slectionnera Fichier/

prrFilCiloql

,:!:l i r!

eFileC,ilrtrl

1fll

. Erp,op,,.trf-

Enreg istrer.

5.

Implmentez les mthodes FileOpen O et FlleSave O comme suit

private void File0pen(object sender, System.EventArgs

e)

0penAndReadFile

private void FileSave(object sender, Systen,EventArgs

e)

SaveSpecifiedFile 0

Ce sont ces deux fonctions simples qui permettent sirrrpleEditor


d'tre un vritable diteur : il peut maintenant lire et crire des
fichiers. Par exemple, bien que a ne se voie pas, le texte que montre
la Figure 18.7 a en fait t crit dans Word (enregistr au format RTF,
bien sr), et lu en utilisant la commancle Fichier/Ouvrir.

451

39 2

Quatrime partie : La programmation oriente obiet

//

1a:nrne classe

C-private ( ) ;
c1ass1.C_privateO;
I I elass2,
I
I

I
I

Les nthodes internes ne sont accessibles que par


Les classes du mme espace de nom

I I class2.D*internal ( ) ;
c1ass3.D-j-nternal0;

I les mthodes internes protges sont accessibles


// soit par 1a hirarchie d'hritage soit par
ll tome classe du mme espace de nom
I

E-internalprotected

class3 . I-internalprotected

c1ass1

l/ attend confirnation de I'utilisateur


Console.l,IriteLine("Appuyez sur Entre pour terTniner' . .")
Console.ReadO;

return

0;

l
nrrhl i

r. voi d C nrivate 0

hlriteline ( "Class1 . C-private"

Console.

l
]

// Class: - une classe interne est accessible aux autres


classes du mme espace de nom, maj.s
ll
pas aux classes externes qui utilisent cet
II
espace de nom
II
internal class Class3
t
I

I Ia dclaration d'une classe comme interne force toutes


I Les mthodes publiques tre galement internes

public void

A_pub1ic

Console. l^lriteLine

"CLass3 .A-

public" ) ;

nrntontod

vnid R nrntantpd{)

Console.l.lriteline ("Class3 .B-protected" ) ;


]

internal void D-internal 0


{

Writeline

Console.

"C1ass3

D-internal

")

l
-"h1
yuurrLi.

.r^i
vLveeus\/ ( )
!_rrrinf orna l nrntontod
vvrurl

t
Console
]

Writeline ( "Class3 . B-internalprotected" )

Chapitre 18 : Achever votre application

Windows

(
i.

ll

aff.iche 1e message d'erreur dans 1a fentre de texte e1le-rnne


lire 1e fichier\n";
richTextBoxl . Text *= e.Message;

richTextBoxl.Text = "Impossible de
bReturnValue = true:
]

return bReturnvalue:
l

Cette fonction commence par afficher une bolte de dialogue


0penFileDialog. Si la bolte de dialogue retourne OK, la fonction essaie
d'ouvrir le fichier slectionn en utilisant la rnthode rlpenFi 1e ( ) . Si cette
mthode retourne un objet St:ean valide, OpenAnCPeadFile O insre
I'objet Stream dans un StreanP.c'adi:r, plus pratique. Elle lit ensuite tout le
contenu du fichier, puis elle le copie dans P,rchTexiBo:< e I'assignant sa
proprit P.tf. Enfin, Oirer,Arrdi'l.e:,lF-r-r'. : ferme le fichier.

AP\
=qt

Un diteur qui lit tout le fichier en nrmoire est beaucoup plus facile
crire qu'un diteur qui err laisse la rnajeure partie sur le disque. En tout
cas, C# ne limite pas son cornposant i :lr-,-extB,rx au tampon n-rmoire si
frustrant de 64 Ko du Bloc-notes.

erreur d'UO de fichier se produit. 0penAnctP.eadFlie I ) envoie le


message erreur dans la zone de texte i i:hTe){tBcx elle-mme.
Si une

Nous avons ajout le composant .-'pen!'iie Dialog


faisant glisser depuis la Bote outils.

SinpleEditcr en le

crire un ficher RTF


La classe SaveFiieDraiog offre des mthodes qui sont tout aussi pratiques que celles qui servent ouvrir le.s fichiers :

//
ll
//

le fichier sur le disque ; retourne true en cas de


(cette fois, ne pas essayer d'a--traper une exception - je ne
saurais de toute faon pas quoi en faj-re)
private bool SaveSpecifiedFile0
enregistre

succs

bool bReturnValue = false:


l l ce code est construit sur le

if (saveFileDialogl

ShowDialog

nme mod1e que OpenAndReadFile

) == DialogResult,0K)

System,I0.Stream strOutput

= saveFileDialogl.0penFile0

449

390

ouatrime partie ; La programmation oriente objet

Utilser un espece de nom a(/ec Ie mot-cl usi-ng


s1;11 nonr pleinentent rlualifi prertt deveriir utt
peu fasticlieux f ,e rnot"r:l ,r:-- i:-r' de C# vous pertrtet cl'viter ce penslltrl"
La cornrnancle , r:i ; a!oute I'espace de rtorn spcifi urte liste r:1'esliaces
cle norri par tlf:rrrt rlirr. ('# <ronsrrlte p()ur essayer cle rsourlre un rtorn cle
classe. I-'exc'rnple rle 1)r()r{rarnrne suiv;urt se conr;rile sans r-rne plainte :

Se rfrer a url(' r'l;rsse l)ar

nanespace Paint
t

public class PaintColcr


{

public Paj.ntCoior(int nRed, int nGreen,


public vcid Pa j,nt 0 | l
publ"ic static vcid StaticPaint0 {l

int

nBLue)

{l

l
rrqil

y4L

v^+.".D..-.i;.ss
-^tr l-1o
Lrrl\!] Lf L

T
L

o nom

dnns

static public void Main(string[]

args)

ll

aiorto

Peint-

:':x

sn2n(

r1

'l

esfi1r

s on cherche

^,,+^^^+.i ^,,^*^
/I /I auromarrquenent
,,^.i-^
uDrrr6 D.;-+.
r arll L ,

nlrbI1c clss l'st


{

I I cre r:n ohipt denc l1n lrtr pcnace de nom - l1 n'est


ll pas ncessaire de faire figurer ie non de 1'espace de non,
/ / celrr j. - ci e st inclus dans une instruc*,ion "using"
PaintColcr black = new PaintColor(0, 0, 0);

black. Paint 0

car

PaintColor. StaticPaint ( ) ;
j
J

La comnilncle r,,.i..,. cJit:"Si vorrs ne trollvez pas la classe spcifie dans


l'espace d' rront courrtt, voyez si vous la trouvez dans celui-ci." Vous
pouvez spcifielr autant d'espaces de rloilr que vous voulez, mais toutes
les commanries ., , :: cloivent apparaltre I'une aprs I'autre tout fait au

dbut clu programrlt)"

Tous les progrilnrf nes (:onlnlencellt par la cotumande i.,sing S.r'sieiri;, Elle
dr>nne aLr proqrilnln)l rrn accs alltomatique toutes les fonctions cle la
bibliothque systr'rnre, contlne irrrtei,: rE i r.

Chapitre 18:Achever votre application

Windows

appelle des filtres. La dernire option de la liste des filtres est


toujours *.*, qui signifie tous les fichiers.

Figure 18.5
Dposer sur

iiil,..:

E{hier Edition 'firhaqe fr,ljet Q;,nret pboquer :,!nE6

E,r:ils FeDh{-

BelF

(i'rnloF.li+;r

-un c0mp0sant
'lp.-rFiieJialcg

.=j:-:J.*tti&
fornrl.cs IDesign]+

. { tr:nrr:
Lfrt,osirrts

et un
comp0sant

E,lilrn

Frml

Sa-re-Filei!:loq
a

pp0re

l'essentiel du
dialogue

ncessaire
I'ouverture et
l enregistrement de
fichiers.

1l

I
Fotns
I "t'innonos
I lf;l t-onte; ll'lenr:
i:rJ TnelEat
|
5tatusEar
I
1,,.,

t^hlil'trlcon

E
5

oFBnFiletl'rE

q
-j[J

,-.,
-FntL,r'3lah]

SiveFiletrl,1

E rrlrro,rt4
PfE:s-F6Erers circulir

iper,FitrDr.:1r,11 B:xr./E:rlDttrql

Gin:rl

$ eoli* : ruiils i@

Les filtres du composant OpenFileDi alog sont stocks dans sa


proprit Fiiter. La syntaxe utilise dans ce champ est un peu
dconcertante : un filtre est dfini par un couple form de son nom
et de I'extension de fichier correspondante, spars par un caractre l, deux filtres conscutifs tant galement spars par un
caractre l.

3.

Assignez la proprit
les fichiers | *.*".

Filter

la chane "Fichiers RTFI *.rtf lTous

Cette opration indique 0penFileDialog de ne rechercher initialement que les fichiers RTF, mais laisse I'utilisateur la possibilit
de rechercher tous les fichiers.

"Tous les fichiers | *.*" doit toujours tre la dernire entre de la


Iiste des filtres. Pour I'utilisateur. c'est la slection de la dernire
chance.

4.

Faite de mme pour la proprit


SaveFi1eDia1og.

Filr,pr du composant

447

388

0uatrime partie : La programmation oriente objet

Transiariorilibrai,;

ces deux ensembles de classes


peut pas tre confondu avec

vite le problme : f i
Tra i'rs iat- ic l.ili b r a l'.-

Dclarer un eslrace de nom


On dclare un espace cle nom en utilisant le mot-cl nan.spa.e, suivi par
un nom et un bloc cl'accolades ouvrante et fermante. Les classes spcifies dans ce bloc font ltartie de I'espace de nom.
nanespace MyStuff
t

class MyClass {}
class UrClass ll
l

Dans cet exemple.

1,.,

font partie de I'espace de nom

il , :.i,. s t

MySt'.rff.
^1'$q

]X
IrT9,
Y

un espace de nom portant le mme nom que le rpertoire qu'il cre. Examinez tous les programmes cle ce livre: ils ont tous ts crs I'aicle cle
I'Assistant Application. Par exemple, le programme Ai ignOrir:pl1t a t cr
dans le dossier,t lipnr-)ir'f.1r,: t. Le nom du fichier source est (llassl . cs, qui
correspond au nom de la classe par dfaut. Le nom de I'espace de nom dans
lequel se trouve C,lassl. cs est le mme que celui du dossier:Aligro'.rrp1,1t.
Si vous ne spcifiez pas ur-re dsignation d'espace de nom, C# place

votre

classe dans I'espace de nom 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 I'espace cle norn cl'une classe est une partie du nom cle la
classe tenclue. Voyez l'exenrple suivant :
namespace MathRoutines
{

class Sort
{

public void

SomeFunction

Chapitre 18 : Achever votre application

Windows

I ignore toute erreur de conversion qui pourrait se produire

'. J

ll s'il y a quelque

chose ici ...


//if (sText.Leneth ) 0)
ll ... le convertit en entier
int nFontSize = Int32.Parse(sText)
I

l'

I / si la valeur est dans 1'tendue valide ...


if (nFontSize )= trackBarl.Minimum && nFontSize (=

trackBarl.Maximun)

ll .., net jour 1a trackbar et


trackBarl.Value = nFontSize:

ll ... ajuste la police (SetFont0 1it sa tai11e de


/i police direetement sur 1a TrackBar
tl

SetFont

l
l

catch I i ;

df\
=)

Cette mthode est invoque pour chaque caractre envoy dans


cette zone de texte, et non pollr le dernier uniquement. Par exemple,
la saisie de 24 gnre deux appels : un pour le 2, et un pour la valeur
24.Dans cette application, c'est sans consquence, mais sachez-le.

FontSlzeErrtered O commence par lire le contenu de TextBox, puis elle


entre dans un bloc rr.,'. [2 fonction inr3 2 . Parse O convertit le contenu
de TextBox en une valeur irir. Cette fonction de conversion envoie une
exception si la chalne trouve ici ne peut pas tre convertie en un entier
valide. L'instruction catch universelle place en bas de cette fonction
bloque les problmes, mais ignore I'exception et ne modifie pas la taille
de la police. De mme, si la chne entre par I'utilisateur peut tre
conve-tie en un nombre entier mais que celui{i se trouve en dehors de
l'tendue autorise (8 24 points), elle est ignore.

FontSlzeEntered () met jour


TrackBar en assignant cette nouvelle valeur sa proprit Va1ue.
Par exemple, supposez que I'index de TrackBar soit situ la valeur
12. Ds que I'utilisateur entre la chalne 22, I'index passe directement
la position 22, presque I'extrmit droite de la barre.
Si la taille saisie est autorise,

La fonction Fonr ( ) met jour la police elle-mme.

445

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 cornpils 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 qui vrifie 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.

Rassembler toutes ces classes dans un mme fichier source Clas s I


est remarquablement draisonnable, pour les raisons suivantes :

.cs

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 si vous 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 ResAgentlnterf ace. cs, GateAgentln*r-erf ace. cs,
ResAgent.c s, GateAgent . cs, Fare . cs ou Ai rcraf t . 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.

Windows

Ghapitre t8 : Achever votre application

l.
,

Dans le Concepteur de formulaires, slectionnez la TrackBar.


Dans la fentre Proprits, slectionnez l'vnement

r oI

l.

Comme nom de fonction, entrez FontSizeControl.


Passez dans I'affichage du code source, et implmentez les nou-

3.

velles fonctions comme suit


li

invoque quand

I'utilisateur dplace f index de 1a TrackBar

private void FontSizeControl(object sender, System.EventArgs

e)

I I Lit 1a nouvelle taille de police directement sur 1a TrackBar


fontSize : trackBarl..Value ;
I
I

I Ia convertit en chane et la
I \a TextBox pour accorder les

copie dans
deux

textBoxl.?ext = String.Eornat (" {0J ", I0nt51zeJ;

// ajuste naintenant la police


SetFont ( )

For.itSizeCon'Lro1O est invoque chaque fois que l'utilisateur


dplace I'index dans la barre. Cette fonction lit la nouvelle valeur
(un nombre entier compris entre 8 et24, inclusivement) dans
I'objet TrackBar. FontSj-zeControl O utilise la mthode
St ring . F o rmat ( ) pour convertir ce nombre en une chalne de texte
qui est alors copie dans la TextBox Taille de police. Cela fait,
FontSizeControl O invoque SetFont O pour modifier la police
utilise dans la fentre d'dition.
Je donne la description de SetFont O dans la section "Changer de

police et de taille", plus haut dans ce chapitre.

4.

Gnrez le programme et excutez-le.

5.

Entrez du texte dans la fentre et slectionnez-le avec la souris.

6.

Faites glisser vers la droite et vers la gauche I'index de la taille

de police.
La Figure 18.4 montre le rsultat. Il est vrai qu'une simple image n'est pas trs
parlante, mais la valeur qui appart dans la zone de texte Taille de police est
instantanment mise jour selon la position de I'index dans la barre, pendant
que le texte slectionn est agrandi ou rduit en consquence.

443

38 4

0uatrime partie : La programmation oriente obiet

Le programme CustomException donne Ia sortie suivante


Erreur fatale i-nconnue
Le message est (Iinpossibie d'inverser 0), I'objet est (Va1ue =

CustomExc ept ion . MathCl

a s

0)

pnvove narDorrble Inverse0


Exnpntion
!.rey
Annrrrroz crrr Entrp nottr rcrm'i npr.

d'il Cette sortie:le mesSage !.ir:r-ir f ,l:'.ie i:ir r:,tr


nue : vient de ]"1air: i). La chalne -e r:: rsas. s1. ( 'in:cssiSl e d'in,./-rser 0), i'cb-jet ?s1- (--) Vient Cle r,rrst,.'rnE:.c-.pticr. Le message
Valrle C vient de I'objet l"la.,.hClass lui-mme. La dernire ligne, Frcep
tion en.",cve DarDouble Inverse, vient de Crjsl-ltrf,xcapt ic:1.

JetOnS Un COup

ToString{}, la 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 17 que j'explore cette proprit qui unifie les classes, mais il est utile de
mentionner ici que Ob j ect contient une mthode, To S t r ing ( ), qui convertit en st r ing le
cgntenu de l'objet. L'ide est que chaque classe doit redfinir la mthode ToString ( ) par
une mthode lui permettant de s'afficher elle-mme d'une faon pertinente. Dans le chapitre
prcdent, j'ai utilis la mthod GetString ( ) parce que je ne voulais pas y 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 pfupart des fonctions, mme les fonctions intgres de la bibliothque C#, utilisent la
mthode ToString ( ) pour afficher des objets. Ainsi, le remplacement de ToString O a
pCIur effetsecondairetrs utile que I'objetsera affich dans s0n propre format, quelle que soit
la fonction qui se charge de I'affichage.
Comme dirait Bill Gates, "C'est cool.'

Ghapitre 18 : Achever votre application

4.

Windows

Implmentez les nouvelles fonctions de la faon suivante

private void FornatBold{object sender, System.EventArgs

e)

isBolded = !isBolded;
nenultemi0.thecked - i.sBolded;
SetFont

private void Formatltalics(object sender, System.EventArgs

e)

isltalies = !isltalics;
isltalics

nenultenli.Checked =
SetFont ( )

Chacune de ces fonctions inverse l'tat de I'indicateur correspondant,


et invoque SetFcnt () pour modifier la police en consquence.

ll

rr-I-al-1cs O assignent true ou false


pour placer ou non une coche devant l'option
de menu correspondante afin de montrer si elle est active
(rnenultenil0 correspond I'option de menu Format/Gras, et
menulteni 1 I'option Format/ltalique).
Formaltsiid

et

t- -,t

menuf ten.i 0. Checkeo

Les noms de vos options de menu pourront tre diffrents si vous


ne les avez pas mis dans le mme ordre que moi. Pour savoir quel
est le nom cl'un composant particulier, slecticlnnez celui-ci dans le
Concepteur de formulaires. Le nom et le type du composant apparaissent en haut de la fentre Proprits.
Rien ne vous oblige vous en tenir aux noms assigns par le Concepteur de formulaires. Vous pouvez changer votre guise la proprit
lJarne lorsque vous crez l'objet. Vous pouvez ainsi choisir des noms
plus parlants et plus faciles retenir.

5.

Par acquit de conscience, ajoutez dans le constructeur un appel


SetForrt O pour dfinir correctement la police initiale :

public

Forml

()

//

Required

for

Windows Form Designer support

lT.lirL^rr,
(

"cornponent

441

382

Ouatrime partie: La programmation oriente obiet

le

message standard Exception.ToStringO

override public string toString0


I

string s = Message * "\n";


s *: base.ToStrine0
return s;

l/

Inverse

retourne 1/x

public double Inverse0


t

if (nValue0f0bject :=

0)

throw new CustomException("Impossible d'inverser

0", this)

l
)

return 1.0

(double)nValueOf0bject;

l
l

public class

Class1

public static void Main(stringIJ args)


{

try
I

//

prend

inverse de

MathClass nathObject = new MathClass("Va1eur", 0);

Console.!^IriteLine("L'inverse de d.Value est{0}",


niath0bject, Inverse 0 )

catch(Exception e)
{

Console.Writeline("\nErreur fatale inconnue : \n{01 ",

e.ToStrins0);
1
J

//

attend confirmation de 1'utilisateur

Console.I,.IriteLine("Appuyez sur Entre pour terminer.


Console. Read 0

..")

l
]

Permettez-moi de faire une rerrrarque : cette classe CrrstomException


n'est pas si remarquable que cela. Elle stocke un message et un objet, tout
comme l'lyExce prioi. Toutefois, au lieu de fournir de nouvelles mthodes
poqr accder ces donnes, elle remplace la proprit Message existante
qui retourne le message d'erreur contenu dans I'exception, et la mthode
ToStringO qui retourne le message plus I'indication de pile.

Ghapitre 18 : Achever votre application

Windows

SimpleEditor. Le format RTF conserve les informations sur la mise en forme


la premire ligne tait en style Titre 1, la deuxime en style Normal, et la
troisime en stvle Code.

Editiffi

Figure 18.2

Fofmt

;t - . 1l a

p tp.z+- a

',rn.
lt4(g

,le W: rd
v!

'

texte
venant de
Word a
conserv sa
Le

mise en

forme.

.9tlfl
-=(fl,

Tala de

poke

[---

8*24

C'est le composant RichTextBox eui fait tout le travail de mise en forme


qu'il fallait autrefois faire soi-mme au prix de mille difficults.

tlvlettre hardinent en gras et en italque


SimpleEd iror peut maintenant couper et coller du texte mis en forme,
mais il ne sait pas encore modifier la police. Pour cela, il nous faut introduire les contrles du menu Format et du champ Taille de police.

Changer de police et de

taille

Changer de police, mettre le texte en gras ou en italique est en fait presque la mme fonction. Vous pouvez donc vous pargner quelques efforts
en crant une seule fonction capable de raliser ces trois oprations,

comme ceci

ll----

---'--l'lise en forme et tai1le de la police

bool isBolded = false;


bool isltalics * false;
float fontSize * 12.0F;

private void SetFont{)


{

FontStyle

if
t

fs *

(isBolded)

FontStyle.Regular;

439

38 0

0uatrime partie : La programmation oriente obiet

Renvoyer le mme objet exception prsente un avantage et un inconvnient. Cela permet aux fonctions intermdiaires 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' la 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 renvoyer.

Redfinir une classe d'exceptons


La classe d'exceptions suivante dfinie par I'utilisateur peut stocker des
informations supplmentaires qui ne pourraient pas l'tre dans un objet
Exc

eption conventionnel

/i
I

MyException

- ajoute 1a classe standard Exception

une rfrence MyClass


publi-c class MyExeeption : ExceptJ.on
I

private

MyClasss myobject;

MyException(string sMsg, MyClass no)

base(sMsg)

myobject =

mo;

l/

pemet aux classes extrieures d'accder une classe d'infornation

public

MyClass MyObject{

get {return

nryobject;

))

Voyez nouveau ma bibliothque de fonctions BrilliantLibrarr'. Ces


fonctions savent comment remplir ces nouveaux membres de la classe
l4vException 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 la bibliothque BriliiantLib rary peuvent
recevoir un bnfice quelconque des nouveaux membres de MyEr,ception.
Le remplacement des mthodes dj prsentes dans la classe Exception
peut donner des fonctions existantes autres que I'accs Br:illiarrtLibrary
aux nouvelles donnes. Considrezla classe d'exceptions dfinie dans le
programme CustomExc eption suivant :

//
II
II

CustomException

- cre

une exception personnalise qui


que nous voulons, mais
dans un fornat plus agrable

affiche les informations

Ghapitre 18 : Achever votre application

//

et voil,

nous avons ce

return (string)

qu'il

Windows

nous faut

o;

ReadClrpboard O commence par essayer de rcuprer un objet dans le


Presse-papiers. Si elle n'y trouve rien, elle ne retourne rien. Elle essaie
alors de lire dans I'objet une chalne RTF. Encore une fois, si I'objet
clipboard n'est pas une chalne RTF, elle retourne nu11. Enfin, s'il y a
quelque chose dans Ie Presse-papiers, et si c'est une chalne RTF,
ReadClipboard O retourne la chane la fonction appelante.

l.

Dans I'Explorateur de solutions, double-cliquez sur Form1. cs


pour afficher le code source C# du projet.

2.

Dans Ie code source, insrez les mthodes l,JriteClipboar

l ( ) et

ReadClipboard ( ).
Utilisez pour cela dition/Couper, dition/Copier, et dition/Coller.

3.

Dans le Concepteur de formulaires, slectionnez I'option de


menu aition/Coller.

4.

Dans la fentre Proprits, cliquez sur le bouton contenant un


clair pour afficher les proprits actives Qes vnements).
Slectionnez l'vnement Ci i ck.

5.

Dans le champ qui se trouve droite de c1ick, entrez un nom de


fonction significatif. Pour que ce soit cohrent avec I'option de
menu, j'ai mis EdltPaste.
Le Concepteur de formulaires cre une mthode vide, qui est lie

I'option de menu de telle sorte que c'est cette mthode qui est
invoque lorsque I'utilisateur clique sur celui-ci.

6.

Rptez les tapes 3 5 pour les options de menu Couper et


Copier. J'ai nomm ces mthodes EditCut et EditCopy.

7.

Vous devez ajouter manuellement le contenu de ces mthodes,


comme suit :

private void EditCut(object sender, Systen.EventArgs


t

string rtfext * rich?extSoxl.SelectedRtf;


liriteClipboard ( rtfext)

e)

437

37 8

ouatrime partie : La programmation oriente objet

types d'exception dfinie pour la brillante bibliothque cle classe que je viens
d'crire (c'est pour a que je I'appelle tsr-iI lrantLi i r a:'.). Les foncticns qui
composent Bri i I iant -rhraii- envoient et attrapent des exceptions
llyExc ept ion.

Toutefois, les fonctions de la bibliothque:l:- j i r-arr--i:,rai'i.peuvent aussi


appeler des fonctions cle la bibliothque gnriqu S.,':,,i -.r. I-es premire.s
peuvent ne pas savoir comment traiter les exceptions de la bibliothque
Systen, en particulier si elles sont causes par une entre errone.

#i
(l-,

vous ne savez pas quoi faire avec une exception, laissez-la passer pour
qu'elle arrive la fonction appelante. Mais soyez honnte avec vous-mme :
ne laissez pas passer une exception parce que vous n'avez simplernent pas
le courage d'crire le code de traiternent (l'erreur correspondant.
Si

Relancer un obiet
Dans certains cas. une mthode ne peut pas traiter entirement une
erreur, mais ne veut pas laisser passer l'exception sans y mettre son grain
de sel. C'est comme une fonction mathn-ratique clui appelle jr':r,r'',,r-.,.. i ()
pour s'apercevoir qu'elle renvoie une exception. Mme si la cause premire du problme peut tre une donne incorrecte. la fonction mathmatique 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 cle son excution, F' )
invoque G O . Une exception envoye de G ( I passerait directement
travers F O sans lui laisser la moindre chance de fermer le fichier. Celui-ci
resterait donc ouvert jusqu' ce que le programme lui-mme se termine.
Une solution idale serait que F O contienne un bloc catch qui ferme les
fichiers ouverts. Bien entendu, r (l 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 ccinsiste
envoyer une deuxime exception, avec les mmes informations ou ventuellement des informations supplmentaires
:

Ghapitre l8 : Achever votre application

lichrr dtEs Afh.hage ErrFt

6ena

!bDrr tgnnier

QdilJ

Windows

Fetbe

.J. :,t. ,-: Izlt ffi


Forml,cs[Desiqn]

*"'i;.#..iilt'.':,;

F[hPr edtion

f-l
L:)

::
Formt

o,
[l -;/
r L:--l
..

....

a.

::iirb

el,-irr!

.ii5rb EllsmE

i,frit
.,,

E:

.r1,}inl
8:r lr:if

I
r

rrd,:I
'n:t'

lEenr

Figure 18.1

Ia,lle de

p(Jrcdq

E Lrrr
F

r,r,::rre,i

Ibleu 5trinq[

tliT:Lfl

!i rllf:i,::
Tii

m0deste

-c0mposant
TextBox

o-a-.

Mme le

....

des dizaines
de proprits
a ctives.

.t

r}
r{rl
7
11 )

l
l

Un mentl garanti lrour dter le menu dton


Comme il n'y a aucune raison particulire de commencer par un composant plutt que par un autre, pourquoi ne pas aller du plus facile au plus
difficile ? Dans les premires versions de Windows, I'une des fonctions les
plus difficiles manier tait le Presse-papiers, mais la bibliothque de C#
fait de la copie et de la rcupration de donnes dans le Presse-papiers
un jeu d'enfant.
Le Presse-papiers est une zone d'arrire-plan de Windows dans laquelle
est stock ce qui a t copi ou coup en attendant d'tre coll par la
suite. C'est dans Windows que doit se trouver le Presse-papiers, parce

que I'utilisateur peut y copier un objet, par exemple du texte, dans une
application, et le coller dans une autre.
La mthode SetDataOb j ect de la classe Clipboard (le clipbocrd est le
Presse-papiers) crit un objet Data0b ject dans le Presse-papiers. L'objet
DataOb ject contient les donnes stocker et la description de leur type.

dPi
-Q)

L'identification du type de donne est trs importante. Par exemple, I'utilisateur peut essayer de couper le contenu d'une feuille de calcul et de le coller
dans la fentre de Si inpl e-ECitor. S'il n'y avait pas un moyen de filtrer ce
contenu, SimpleEditor afficherait une chne de n'importe quoi (au mieux).

435

37 6

Ouatrime partie : La programmation oriente obiet

lJriteline

Console.

(e. Message)

I tZ - -

uurrL
f^,,hli^

,,^;

prparez-vous artraper une exception MyException


+1i'h^"'
. r --..,f 'DExceP10n1YPe,

{
L!.I

IJ ( bIxceptlonlype/

\
;

l
catch (MyException

me)

Console.tr^lriteline("Exception MyException attrape dans f20 ")


Console

l^Iriteline

(ne. Message)

l
]
I I tl - - n'essayez pas d'attraper des exceptions
public void f3 (boo1 bExceptionType)
{

f4 (bBxceptionType)

ll t4'-

envoie des exceptions d'un type ou d'un autre


public void f4(boo1 bExceptionType)
t

I I nous travaillons avec un objet 1ocal


My0lass mc = new l{yClass 0 ;

if

(bExc eptionType

I'objet est envoy avec 1'exception


une erreur se produit
envoye dans f40",
MyException("MyException
new
thron
mc);
I

throw new Exception("Exception gnrique envoye dans

f40");

public static void Maj.n(stringll ares)


{

I I envoie d'abord une exception gnrique


Console.I.,lriteline("Envoie d'abord une exception gnriQue")

new C1ass1

0 . f1 (fa1se)

// envoie maintenant une

de mes exceptions

Console.Idriteline("\nEnvoie d'abord une exception spcifique")


nerv C1ass10 .f1 (true)
;

// attend confirriation de 1'utilisateur


Console.l.lriteline("Hit Appuyez sur Entre pour terminer...")

Ghapitre 18

Achever votre a pp I ication


Wi ndows
Dans ce chapitre :

Implmenter les options des menus.


Copier des donnes dans le Presse-papiers et les rcuprer.
Faire des manipulations simples sur les polices.

Lier ensemble deux contrles pour que les modifications effectues dans I'un
soient refltes dans I'autre.

Lire et enregistrer le contenu de l'diteur.


Utiliser des boltes de dialogue.

..R,:r^
uRttqFg

;ar

i-l#
,

u Chapitre 17, nous avons cr une jolie petite application SimpleECitor.


Malheureusement, au point o nous en sommes, elle ne fait encore rien.
Dans ce chapitre, nous allons ajouter les couches ncessaires pour la faire
fonctionner. Vous allez transformer SimpleEditor en quelque chose d'utile : un
diteur capable de lire et d'crire des fichiers texte, de mettre du texte en gras et
en italique, de changer Ia taille de la police, d'crire dans le Pressepapiers et d'en
lire le contenu, et que I'on peut redimensionner volont.

Le code complet de SimpreEditor St sur le site Web.

Ajouter des actons


Le Concepteur de formulaires simplifie le travail de cration d'une application
Windows. Il permet d'ouwir une Bote outils regorgeant d'accessoires
comme des boutons, des zones de texte ou des tiquettes (de faon plus

37tl

Ouatrime partie: La programmation oriente objet

'I

catch(Exception

e)

t
I

Les autres exceptions non encore attrapes sont attrapes

ici

l
Si ScmeOtherFunct-t on ( ) envoyait un objet Ex-cepr:ion, celui-ci ne serait
pas attrap par I'instruction catch (l'lyException) car une Exception
n'est pas de type l1','E:icepti;n. Il serait attrap par I'instruction catch

suivante : ca:ch (Exception).

.)

Toute classe qui hrite de MyException EST_UNE MyException:


class MySpecialException : MyException
{

ll

instructions quelconoues ..,

Si elle en a la possibilit, I'instruction ca'1-ch l"lyException attrapera tout

objet

=(t
9D'\

pec

l"fr;-

i a lExc e pt io n envoy.

F'aites toujours se succder les instructions carch de la plus spcifique


la plus gnrale. Ne placez jamais en premier I'instruction catch la plus

gnrale
nlln

I 1n

lrn1d

\m81lhn11n
grrl !:vrr

I I
\ /

+ rlr
LI
Y

Some0therFunction

l
catch (Exception

me)

It

I tols

1es objets MyException sont attraps

ici

1
J

catch (MyException e)

,-

//
II

parvient jamais jusqu'ici parce qu'el1e


est attrape par une instruction catch plus gnra1e

aucune exception ne

l
Dans cet exemple, I'instruction catch la plus gnrale coupe I'herbe sous
le pied de la suivante en interceptant tous les envois.

- Chapitre 17 : Grer une application


^sst .t
7X
ttgrf
Y

Windows : le ramage et le plumage

Les proprits que vous n'avez pas modifies sont les proprits par
dfaut de I'objet, qui n'apparaissent donc pas dans le code, Par exemple,
vous pouvez voir que trackBarl est ancre sur AnchorSryles. Bottom,
AnchorSty Les. Lef t et AnchorStyles. Right. D'autre part, les proprits
llaxirnum et Minimum reoivent les valeurs 24 et 8, et la proprit Val ue, la
taille de police initiale, reoit la valeur 12.

InitializeComponents O est trs long, mais suit la


mme logique qui consiste assigner une valeur chaque proprit moclifie.
Le reste de la mthode

Comment aplrrendre connatre les cnmlrosants ?


L'une des questions souvent poses par les nouveaux programmeurs C#
pour Windows est : "Comment sait-on quels composants sont disponibles et
ce que fait chacun d'eux ?" Un bon moyen de faire connaissance consiste
jouer avec eux : choisissez un composant, faites-le glisser sur le formulaire,
slectionnez-le, et commencez passer en revue ses proprits.
Un autre moyen consiste rechercher les composants dans I'aide de Visual
Studio. Le nom de la classe est le mme que celui qui apparat dans la Bolte
outils. Ainsi, si vous voulez savoir comment utiliser un RadioButton,
vous pouvez commencer par entrer RadioButton dans I'index de I'aide.
L'aide de Visual Studio fait apparaltre une fentre contenant une description du composant, et parfois un exemple de code.

Enfin, dans tous les exemples que vous pourrez trouver, explorez la mthode
InitializeCornpcnenrs 0 jusqu' ce que vous arriviez comprendre ce qu'a
fait le Concepteur de formulaires. Ce procd vous permettra de dcouwir
de nouveaux composants et leurs proprits, et vous donnera une ide de ce
quoi ils servent.

Avec I'exprience, il devient de plus en plus facile de trouver le bon


composant.

Et maintenant

N'oubliez pas que le programme SlmpleEditor que nous avons cr dans


ce chapitre est trs simple. Il est trs joli, mais il est si simple qu'en ralit
qu'il ne fait rien. Au Chapitre 18, nous allons ajouter le code ncessaire
pour faire de SinrpleEdltor un vritable diteur.

43 I

37 2

0uatrime partie : La programmation oriente obiet

Cette classe Crsr-omExcepticn st faite sur mesure pour signaler une erreur
au logiciel qui traite avec la tristement clbre I'i',.C i: ss. Cette sous-classe
d'Exc -aption met de ct la mme chalne que I'original, mais dispose en
plus de la possibilit de stocker dans I'exception la rfrence au fautif.

L'exemple suivant attrape la classe iils*.cnrE;<,- -Jti,rr t met en utilisation


ses information.s sur l"li-Ciass
:

public class

C1ass1

public void

SomeFunction0

try
t

oprations pralables la fonction

exemple

SoneOtherFunction0;
I

autres oprations.

l
catch (MyException

me)

// vous avez toujours accs

aux mthodes d'Exception

string s = me.ToString0;

I nais vous avez aussi accs toutes les proprits et


// de votre propre classe d'exceptions
I

MyClass no

mthodes

= ne.MyCustonObject;

I par exemple, demandez 1'objet


string s = mo.GetDescriptionO;
I

MyClass de

s'afficher

1ui-mrie

l
l

public void

SomeOtherFunction0

I I eration de nyobject
l'lyClass myobject = new MyClass 0 ;
signale une erreur concernant myobject
II
throw new l'lyException("Erreur dans 1'objet de MyC1ass", myobject);
I

I .

reste de 1a fonction

Dans ce fragment de code, SoneFunctlon 0 invoque Sonefit,herFulic*'ion ( ) de


I'intrieur d'un bloc ir,\'-. soneotherFunction ) qvs" et utilise un objet
'
myob j ect. Quelque part dans SoleOtherFunctror O, une fonction de vrification d'erreur se prpare envoyer une exception pour signaler qu'une condition d'erreur vient de se produire. Plutt que de crer une simple Excepiiori,
SoneFunction ( ) se sert de la toute nouvelle classe 1"1''iExcep r-ron, pour envoyer non seulement un message d'erreur. mais aussi I'objet r.r;r.,11- e,- i fautif.

Chapitre 77 : er une application Windows : le ramage et le plumage

429
'n

Figure 11.12:
Une fois
convenablement ancrs,
les composants de

Fkhier Editin FDfrnal I

-i

mnl oFrJit,rr

en suivent

fidlement le

Taille de p,:fice

redimensionnement.

l--

Qu'atlons-nous fabriqu

Le listing ci-dessctus montre un soll.s-ensemble de la mthode


Inltiall zeC.r,tD(;rrent (t cre par le Concepteur de formulaires. Puisque
cette mthode est trs volumineuse, je n'en donne ici qu'un petit extrait :
nanespace SimpleEditor

///
///
l//

(summary)
Sunrnary

description for Forni.

(lsunnary)
public class I'orrnl

System,l,Jindows.Forms,Form

private System.Windows.Forns.RichTextBox richTextBoxl ;


private Systern.Windovs. Forns .l'lainMenu mainMenul ;
private System. Windows. Eorms.Menulten menulteml ;
II

#region !{indor,rs Form Designer generated code


private void InitializeComponent0

t
this.mainMenul = new System.Windows.Forns.MainMenu0
this.menulteml = nev System.Windovs.Forns.Menulten0
this.trackBarl = nev System.ldindovs.Forns,Track3ar0

/l

;
;
;

II
/

il

naintlenul

this.mainMenul.MenuTtems,AddRange(new

System.Windows,Forns.l'lenuTtem[] {

this,menuItenl.

37 0

ouatrime partie : La programmation oriente objet

l,'lai:' ir est le point cle clpart clr-r programme, il est bon cle toujours
1t!BIQa^ Comme
en placer le contenu dans un bloc i i:-,-. Toute exception qui ne sera pas
"attrape" ailleurs remontera finalernent jusclu' i'1a j n r. ) . C'est donc votre
)
\/
dernire opportunit cle rcuprer une erreur avant qu'elle aboutisse
Windows, dont le message d'erreur sera beaucoup plus difficile ir-rterprter.

avz-; \

:(dq9

Le bloc cetciL situ la fin cle

ilalri r. I attrape I'objet

E>:,:r1,'r.

ic

et utilise sa

mthode'laS'-rlr. 'r porlr afficher sous frlrme d'une sirnple chane la rnajeure
partie des informations sur I'erreur contenues dans I'objet exc epi i ,.r.
,.t\v-_

,a

tLapropritExce.:i.ltiil:.i].::.-::]i:E]retclurneunSouS-ensenrblepluslisible.
J-^t
:

llcf
Y

mais moins descriptif cles informatictns sur l'erreur.

Cette version de la fonction i':..c:.-.: , a I , , contient la mme vrification pour


un argument ngatif que la prcdente. Si I'argument est ngatif, rla.tcr ia-l ()
met en forme un message d'erreur clui dcrit le problme, incluant la valeur
incrimine. Fac1-oi iai i; 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
i - 4, factorielle = 24
i = 3, factorielle = 6
i = 2, factorielle *
i = 1, factorielle =
i = 0, factorielle = 0
2
1

Erreur fatale

Systen.Bxception: Argument ngatif illicite pass F'actorial -1


at Factorial(Int32 nValue) in c:\c#program\Factorial\c1ass1.cs:1ine 23
at FactorialException.Classl.Main(StringlJ args) in c:\c#prograrn\Factorial\
c1ass1 , cs:

Appuyez

line

52

sur Entre pour terniner...

Les premires lignes affichent les vritables factorielles des nornbres 6


0. La factorielle de -1 gnre un nessage commenant par Er

reur

fa-

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 Facl,orial O elle-mme. Cette ligne dcrit la nature du problme, en
indiquant la valeur incrimine -1.

_ Ghapitre

Figure 17.9
Laisser aux

17 :

er une application Windows: le ramage et le plumage

mmes

-endroits les
composants
lorsque le
formulaire est

Iarlle

redimensionn
n'est sans
doute pas ce

'ie

p.rlice

l-_-

qu'attendent
les utilisate u rs.

En somme, la l:aci',l,ar- cloit tre ancre aux bords infrieur, droit

et gauche du formulaire.

2.

Pour dfinir I'ancrage, slectionnez la Tr ac.:E:,rr, et cliquez sur


Anchor dans la fentre Proprits.
La proprit d'ancrage apperrat sur la droite, avec une petite flche
pointant vers le bas.

3.

Cliquez sur la flche.


Une petite fentre apparalt. contenant quatre bras formant une
croix, chacun d'eux reprsentant un ancrage. Vous pouvez voir que
I'ancrage par dfaut est le coin suprieur gauche du formulaire (ce
qui explique pourquoi la Tr ackBar ne bougeait pas quand on

redimensionnait le formulaire).

4.

Slectionnez les bras du bas, de droite et de gauche, et


dslectionnez celui du haut.
La Figure 17.10 montre Ie rsultat.
Si vous prfrez, vous pouvez aussi taper manuellement Bottom, Top,

Left, ou Right dans le champ

5.

r:i,,.r

ir,-,i sns utiliser la fentre d'ancrage.

Dfinissez I'ancrage sparment pour chaque composant.


Le Tableau 17.2 indique I'ancrage qui convient pour chaque composant.

427

368

Ouatrime partie : La programmation oriente objet

throw new Exception ( "Description de 1'erreur" ) ;


II
suite de 1a fonction
j

La fonction SomeFunction O contient un bloc de code identifi par le motcl try. Toute fonction appele dans ce bloc, ou toute fonction qui I'appelle, est consiclre comme faisant partie du bloc tr';'.
Un bloc tr',r est immdiatement suivi par le mot-cl catch, 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 obiet de Ia classe E;<ception ou d'une sous-classe de celle-ci.
un endroit quelconque dans les profondeurs cle SomeOtherFunction O,
une erreur se produit. Toujours prte, la fonction signale une erreur
I'excution en envoyant (thror.) un objet Exception au premier bloc pour

que celui-ci I'attrape (catch).

Puis-je atuir un exemple.)


Le programme FactorialException suivant met en vidence les lments cls du mcanisme des exceptions :

f"-fJ
f--t'I.y.)
I |",1

CI

//
ll
II

FactorialException

cre une fonction

factorielle qui

indique Factorial
en

usrrr8

',^:^-

utilisant

1es arguments

lllicites

un objet Exception

(.,a+^a.

y s Lerii;

nanespace FactorialException
t

//
II

l,tyltatnfunctions

collection de fonetions nathnatiques


de na cration (pas encore grand-chose montrer)

-.,L1.:
^ -1^^^
yuurrL

"rooo MyMthFunctions

ll
It

Tactorial

retourne 1a faetorielle d'une valeur


fournie

public static double Factorial(int

nValue)

l/ interdit les nonbres


if (nValue ( 0)

ngatifs

signale un argument ngatif

string s = String.Format(
'rArgument

ngatif

illicite

pass Factorial {0J",

- Chapitre 17:Grer une application

Windows:le ramage et le plumage

l. Pour rsoudre ce problme,

placez un composant Label gauche


de la TextBox, et entrez "Taille de police" dans sa proprit Text.
Une police Arial en l0 points gras conviendra bien l'tiquette
de la TextBox.

3.

Ajoutez maintenant une tiquette I'extrmit gauche de la


TrackBar. DonnezJui galement une police Arial en l0 points
gras. Entrez 8 dans la proprit Text, puisque c'est la valeur que
nous avons donne la proprit Minimum de la TrackBar.
Faites de mme pour l'tiquette de I'extrmit droite de la
TrackBar. Entrez 24 dans sa proprit Texte, eui est la valeur de

la proprit l,laximum de la TrackBar.

Au Chapitre 18, je vous montrerai comment les valeurs de ces


tiquettes peuvent tre dfinies automatiquement en fonction des
proprits de la TrackBar, mais pour le moment nous allons nous
contenter de le faire manuellement.

^dK

=qg,

4.

Encore une fois, gnrez nouveau SimpleEditor pour tre sr


que tout va bien.
Personne ne pourrait tre plus heureux avec le rsultat montr par
la Figure 17.8.

Figure 17.8

SimpleEditor
est prt pour
aller danser.

Taille de

po[ce

l-*

425

366

ouatrime partie: La programmation oriente obiet

d'erreur que Ia 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 Iu 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 c1u'un
langage 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': t ,, r -a L 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:

// appelle SomeFunction, 1it 1'erreur retourne,


// et retourne
errRtn = someFunc0;
if (errRtn == SF*ERRORI)

1a

traite

Console.WriteLine("Erreur de type 1 sur appel someFunc0");

return

MY_ERROR_1;

if (errRtn == SF-ERROR2)
{

Console.i.lriteLine("Erreur de type 2 sur appel sorneFunc0"):

return
l
I

My_ERROR-2;

appelle SomeOtherFunctions,

errRtn = someOtherFunc0
if (errRtn == SOF ERROR1)

1it 1'erreur,

Console.l'trriteline("Erreur de type

return

retourne, et ainsi de suite

sur appel someFunc0");

MY_ERR0R*3;

if

(errRtn == SOF-ERROR2)

Console.lfriteline("Erreur de type 1 sur appel someFunc0");

return

MY-ERROR-/*;

l
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.

Ghapitre 17

: Crer une application

Windows: le ramage et le plumage

Au lieu de faire apparatre les sous-proprits de Font dans la fentre


Proprits, vous pouvez ouwir la bolte de dialogue Police : dans la bote
de dialogue Proprits, cliquez sur Fonr, puis cliquez sur Ie petit carr
gris contenant des points de suspension qui apparalt dans ce champ.
2.

Dans la fentre Police, slectionnez la police Arial, une taille de


12 points, et le style Gras.

3.

Faites glisser le coin infrieur gauche de la zone de texte afin de


la redimensionner pour deux chiffres dans la taille et la police
que vous venez de slectionner.

Eichhr gclitim Sfi&qe ProiEt ffnrer bua Donftics gutdr Fe!&r

=!'I-fiSHffi
::

:)';: i'.

: ., , jt :

i ?:t?l l-,-

:j..1. crm I .cs [Design]t |

,' :

Hdp

t et!'l

text0oxl

Figure 17.7
Certaines
proprits,
c0mme
Font, sont

,-l

{n

5vstem.!Vrndws,Forms

Bn.kr:lo1 f Wrndow
Sorders[yle F]red3D
aursor
l8eam
E Font
llrcrosorl 5n5
ForCr,lor I wrndowTe, t
El Lines
lableau String[

RrghiToleft
5crollBf'

en fait un

No
lJone

ftfltextao*t

TexlA|gn
Left
B i.urt2rv|ur.t't
AcceFtsPtlrr, Fal.e
Ac.eFtsTb
False
llt,Dro
Fils

ensemble de
SOUS-

proprits,
que vous
devez dfinir

Text
Le lexl conteru dn! ce cmtr6l,

individuellement.

4.

Assignez Center Ia proprit TextAlign, supprimez le contenu


de Text, et vous y tes.
Au cas o vous ne sauriez pas trs bien ce que vous permet de faire
un certain composant, la liste des proprits est l, dans la fentre
Proprits, pour vous en donner une ide. Slectionnez simplement
le composant, et parcourez le contenu de la fentre Proprits en
dfinissant les proprits selon ce qui vous convient. Vous ne
pouvez pas faire de dgts : si vous n'aimez pas le rsultat, vous
pourrez toujours modifier la proprit. Si vous avez modifi tant de
choses que vous ne vous y retrouvez plus, slectionnez le composant, appuyez sur la touche Suppr, et il a disparu.

423

364

Ouatrime partie: La programmation oriente obiet

if (dFactorial =:

l'lyMathFunctions.NON-INTEGER-VALUE)

Console

}lriteLine

("Factoria10 a reu un nonbre non entier");


breah;
l

a.f.iche 1e

rsultat

chaque pssge

Console.l'Iriteline(r'i = {0}, factorielle = {1}",

i, MyMathFunctions.Factorial(i)

..

// attend confirnation de 1'utilisateur


Console . l,JriteLine

Console.ReadO

"Appuyez

sur Entre pour terminer

de tests. Le
parce qu'il
(0
accept
est
premier regarde si la valer-rr passe est ngative
une
immdiatement
retourne
fonction
donne un rsultat raisonnable). Si oui, la
sa
version

compare
est
I'argument
indication d'erreur. Si non, la valeur de
entire : si elles sont gales, c'est que la partie dcimale de I'argument est nulle.

Factorlal O commence maintenant par effectuer une srie

Facto:ia1O, la recherche de I'indicades valeurs Comme -I eI -2 n'ont


Toutefois,
erreur.
tion ventuelle d'une
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 MyMathFiriict j cns dfinit deux constantes entires. La constante
NEGATTVE lJUl"iBER. reoit la vaieur -1, et NOI'I-II'JTEGER-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 Main O '
l"lain O teste le rsultat retourn par

U
=)

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 contenant les valeurs d'erreur sont accessibles par la


classe, comme clans l"1vi"1athCiass. i'jEGATIVE IJUMBER. Une variable de
type const est autornatiquement statique, ce qui en fait une proprit de
classe partage par tous les objets.
La fonction Fa.-rrJ,- lal O signale ntaintenant qu'une valeur ngative lui a
t passe conlnle arguntent. Elle le signale Main O qui se termine alors
en affichant un nlessage d'erreur beaucoup plus intelligible :

- Ghapitre 17 : Crer une application

Windows : le ramage et le plumage

Cliquez sur I'un des lments du ntenu, et examinezlaliste des propri


ts. On y trouve bien str une proprit T'ext - cette fois, c'est l'tiquette
du menu. Le champ Shortc rit St aussi une proprit intressante,
montre par la Figure 17.6. Si vous cliquez dessus, une liste droulante
apparalt, contenant tous les raccourcis clavier que vous pouvez utiliser
pour slectionner cet lrnent.
&9.::::':',::i;:

t'

::::'' " .,

Eichier EdiliEn Afr.hg erojt 5nrer !bgrer Dlnne.

lriil5 Fentr ielp

Figure 17.6:
La proprit

Shortcut

-permet de

spcifier le
raccourci
clavier que
vous v0ulez

assigner

| lment de
menu.

Appuyer sur un raccourci clavier permet d'excuter la mme


commande qu'en slectionnant I'option de menu associe. Par
exemple, appuyer sur Ctrl*C clonne le rnme rsultat que clition/
Copier, mais c'est plus rapide.

7.

Dans le formulaire, cliquez sur l'lment de menu Fichier/Ouvrir,


et assignez CtrlO sa proprit Shorrcu*;, corme le montre la
Figure 17.6. En rptant ce processus, assignez un raccourci

clavier tous les autres lments de menu, selon le Tableau 17.1.

8.

Comme d'habitude, gnrez nouveau le programme et essayez-le.

Vous pouvez taper dans la zone de texte et ouvrir les diffrents menus
principaux. Naturellement, au point o nous en sommes, simpleEditor
est encore plus que simple. C'est une coquille vide. Si vous slectionnez
Fichier/Quitter, il ne se passe rien du tout. II vous faut ajouter du code
derrire chacun de ces lments de menu pour qu'ils fassent quelque
chose. C'est le sujet du Chapitre 18.

42 I

362

Ouatrime partie:La programmation oriente objet

ngatif. Ensuite, remarquez que les valeurs ngatives ne croissent pas de


la mme manire que les valeurs positives. Manifestement, il y a quelque
chose qui cloche.
par rapport ce qui
1e$!Qa" Les rsultats incorrects retourns ici sont assez subtils
aurait pu se produire. Si la boucle de Facior:.-a,()avait t crite sous la
forme do i . . . I whiie (dVa1,re l: 0), le programme se serait plant en
passant un nombre ngatif. Bien str, je n'aurais jamais crit une cclndition
comme whiie (ciValue I: 0), car les erreurs dues I'approximation
auraient pu faire chouer de toute faon la comparaison avec zro.

{cg)

Retourner une indication d'erreur


Bienqu'ellesoitassezsimple,il rnanquelafonctionFacii'i,aii', uneimportante 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 Fact.jf ia. ( )
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 : ac--or.a i , .; puis.se 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 Ia valeur exacte indique la nature de I'erreur.

Le programme

Fac

tori

a1E

rr

r Re

t u r n suivant contient les ajustement.s

ncessaires :

I
I
II
I

actatialErrorReturn

using

cre une fonction fctorielle qui


retourne une indication d'erreur quand
quelque chose ne va pas

System;

nanespace FactorialErrorReturn
{

//
II

MyMathFunctions

collection de fonctions mathmatiques


de *ia cration (pas encore grand-chose montrer)

Ghapitre | 7 : Crer une application Windows : te ramage et le plumage

c'est le texte qui apparalt dans la zone de texte. Pour un bouton,


c'est l'tiquette qui apparat sur le bouton. Cette proprit a toujours le mme sens, mais elle est interprte selon le contexte.
.a

Ehhier Edion Affithge

.:rl'

u' e

ffi

go,et nra $bogua 09nne For06t QLik Fere


'N -u::

Heb

'as*':

'i'

i ..)t. t: ').t':rt: .": Fqml.cJ fDesignl*

q \

Boitewtils
Donres

aompNt5

Flrm
. T-"t

Windows

...

..

. . ....

..

*l'splitlr

fi oomsnupcbwn
ll? Numsrl-FDm
F lfskBa

Figure 17.4:
La zone

t,-Progr5igd

RlchTextBox

-est I endroit

Qf nthrextaox
''ttj rfr4d.st

o l'utilisaeur p0urra

l Helpprovider
3r rootp
El contextf'lenu
:j loobr
Prtr-ppias ccrrire

diter son
texte dans
![Prslua

6en

Lv!.

4.

Pour voir o vous en tes arriv, gnrez et excutez nouveau


I'application.
SimpleEditor appart, avec la zone de texte au milieu. Vous pouvez y
taper du texte, dplacer le curseur dans le but d'insrer du texte o
vous voulez, et mme slectionner du texte. Bien str, vous ne pouvez
rien faire de ce texte, mais SimpleEditor a dj fait pas mal de progrs.

Construre les menus


Rien n'oblige placer ces tapes ici, mais j'ai choisi d'ajouter maintenant
les menus et leurs options. Pour cela, il vous faut un composant MainMenu

l.

Dans la Bote outils, cliquez sur le composant MainMenu. Dans le


formulaire, cliquez I'emplacement de l'Iment le plus gauche
du menu principal.
Un petit cadre apparalt, contenant les mots Tape z

ici.

4l I

360

Ouatrime partie: La programmation oriente obiet

Traiter une erreur l'ancenne mode : la retourner


Ne pas signaler une erreur I'excution n'est jamais une bonne ide. Je

dis bien jamois.' 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 bonne ide.
Le programnle !-ar:tur i alErrcr suivant mclntre 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.
1. Par
La factorielle du nonrbre N est gale N " (N-l) . (N-2)
exemple, la factorielle de 4 est 4 * 3 * 2 * 1, soit 24. La fonction factorielle
n'est valide que pour les nombres entiers naturels (positifs).

ll
II
II

using

crer et

Factoriall.{ithError

utiliser

une fonction

factorielle qui ne contient aucune


vrification

System;

namespace

Factoriall,IithError

li
/

MyMathFunctions

public class

collection de fonctions rnathmatiques


de na cration (pas encore grand-chose nontrer)

MyMathFunctions

I
t

//
II

Factorial

retourne 1a factorielle d'une valeur


fournie

public static double Factorial(double

dValue

// commence Dar donner la valeur 1 un "accumulateur"


double dFactorial = 1.0;
I I tait une boucle partir de nValue en descendant de
ll
II

norr mrilt'i nlier 1'accumulateur


par la valeur obtenue

do
{

dFactorlal *= dValue;
dValue -= 1.0;
J while(dValue ) 1);
// retourne 1a valeur stocke dans

1'accumulateur

return dFactorial;
J

nrrhlie class Classl


t

public static void Main(string[] args)

chaque

fois

- Ghapitre l7 : Crer une application

Windows : le ramage et f e plumage

C'est dans la mthode InitializeComponent que sont crs les composants Windows. Le commentaire spcial plac iuste avant cette fonction
dit en effet : "Ne touchez pas cette section du code, parce que c'est l
que moi, le Concepteur de formulaires, je fais mon boulot." En fait, le
Concepteur gnre le code situ entre les commentaires lf t
et
"gton
/endregion, rponse ce que je dessine.
Dans ce cas simple, I'application commence par dfinir le membre
AutoScaleBaseSize de I'objet this. Je suis pas trs str de ce qu'est cette
proprit. Heureusement, comme c'est le Concepteur de formulaires qui
s'en occupe pour moi, je n'ai pas besoin de le savoir, mais je sais que thls
est I'objet Form lui-mme. En continuant jusqu' la dernire ligne de
Initiali zeComponent, je peux voir que "Simple Editor" est assign

this. Text.
Prenez le temps d'tudier soigneusement ce point, car c'est l I'essentiel
du Concepteur de formulaires. Le Concepteur affiche les proprits du
formulaire. L'une de ces proprits est Text, laquelle j'ai donn la valeur
"Simple Editor". Le Concepteur a ajout une ligne de code qui assigne en
consquence cette valeur la proprit Text du formulaire.
La mthode lispose est invoque lorsque Forml est ferm. Elle n'est pas
particulirement intressante dans ce cas, parce que la fermeture du
formulaire ferme aussi l'diteur.

dter la fentre d'dition


La proprit la plus importante de SimpleEditor est la fentre d'dition

l.

Ouvrez la Bote outils en slectionnant Affichage/Bote outils.


La Bolte outils contient une collection d'objets graphiques en C#, que

I'on appelle aussi parfois composonfs. On y trouve divers ensembles


d'accessoires, dont un ensemble nomm Windows Forms, contenant les
accessoires dont nous avons besoin pour raliser SimpleEditor.

U
^tK
'qE,)

Le terme composonf ne s'applique pas seulement aux objets graphiques, mais tous les objets graphiques sont des composants. C'est
donc ce terme que nous utilisons ici.

Les accessoires de donnes sont utiliss pour raliser facilement


des liens avec des bases de donnes externes. Les composants
grent le multitche. La section Gnral de la Bote outils est
I'endroit o vous pouvez stocker les accessoires que vous ralisez

4 |7

Ghapitre 19: Les dix erreurs de gnration les plus courantes...

C# rsout ce problme en allouant tous les objets partir du tas. Mieux


encore, C# retourne la mmoire au tas pour vous. Plus d'cran noir parce

que vous avez envoy au tas le mauvais bloc de mmoire.

Les hriables de tupe qtointeur ne sont pas


autorises
L'introduction des pointeurs par le langage C a beaucoup fait pour son succs.
Les manipulations de pointeur taient une fonctionnalit puissante. Les
vtrans de la programmation en langage machine pouvaient y reproduire les
astuces de programmation qui leur taient familires. C** a consery de C,
sans modifications, les fonctionnalits sur les pointeurs et le tas.

Malheureusement, ni le programmeur ni le programme ne peuvent distinguer


un bon pointeur d'un mauvais. Lisez un bloc de mmoire avec un pointeur non
initialis et, sivous avez de la chance, votre programme se plante. Si vous
n'avez pas de chance, le programme poursuit son petit bonhomme de chemin
en traitant comme un objet valide le bloc de mmoire trouv au hasard.
Les problmes de pointeur sont souvent difficiles identifier. Un programme
qui contient un pointeur invalide se comporte en gnral de faon diffrente
chaque excution.

Heureusement pour tous ceux qui sont concerns, C# a cart les problmes de pointeur en se dbarrassant des pointeurs en gnral. Les rfrences qu'il utilise la place sont indpendantes du type et ne peuvent pas
tre manipules par I'utilisateur pour en faire quelque chose qui pourrait

dmolir le programme.

Uendez-mo quelques-unes de t/os proprts


Tout bon programmeur sait que I'accs un membre donne doit tre
soigneusement contrl l'aide d'une mthode get O pour en retourner
la valeur, et ventuellement d'une mthode set O pour lui assigner une
valeur. Tout programmeur qui a dj utilis les fonctions get O et set o
est conscient du fait que leur emploi n'est pas une chose trs naturelle :
using Systen;

nublic class

Student

47 5

Chapitre 19 : Les dix erreurs de gnration les plus courantes...

fichiers "include", qui sont alors utiliss par les modules ; toutefois, il
peut devenir trs compliqu de placer tous ces fichiers include dans le
bon ordre pour que votre module se compile correctement.
C# se dbarrasse de cette absurdit en recherchant lui-mme les dfinitions de
classe, et en les trouvant. Si vous invoquez une classe StuCen*., C# recherche

et trouve lui-mme la dfinition de cette classe pour s'assurer que vous I'utilisez correctement. Il n'a pas besoin pour cela que vous lui donniez un indice.

Ne construisez pns, initalisez


J'ai trouv vidente I'utilit des constructeurs la premire fois que j'ai jet
les yeux sur I'un d'eux. Fournir une fonction spciale pour s'assurer que
tous les membres donne ont t dfinis correctement ? Quelle bonne
icle ! Le seul inconvnient, c'est que j'ai fini par ajouter un constructeur
trivial chaque fois que j'crivais une classe :
public class

Account

private double balance;


private int numChecksProcessed;
private CheckBook checkBook;
public Account ( )
{

balance

0.0;

numChecksProcessed

checkBook

0;

= new CheckBook0;

l
]

Pourquoi ne pourrais-je pas initialiser directement un membre donne en


laissant le langage gnrer le constructeur pour moi ? C++ demande
pourquoi, C# r;rsn6 pourquoi pas ? C# se dbarrasse des constructeurs

inutiles en autorisant I'initialisation directe


public class

Account

private double balance = 0.0;


pri-vate int

orivate
1

numChecksProcessed

CheckBook checkBook

= 0;
new CheckBook0;

47 7

Chapitre 19: Les dix erreurs de gnration les plus courantes...

Les programmeurs ont ensuite ralis que I'interface, plus lgre, pouvait
rendre les mmes services. Une classe qui implmente une interface,
comme I'exemple suivant, promet C# comme tout le monde qu'elle
offre les mthodes read ( ) et r,rrite ( ) correspondantes :
interface IPersistable
{

void

read O

void writeO;
i

Le sqstme des tqpes unif is


En C++, la classe est une fonctionnalit fort sympathique. Elle permet aux
donnes et leurs fonctions associes d'tre rassembles dans un ensemble propre et net, fait pour reproduire la manire dont les gens voient les
choses dans le monde rel. Le seul inconvnient est que tout langage doit
offrir de la place pour les types de variable simples comme les entiers et
les nombres en virgule flottante. Cette ncessit a produit un systrne de
castes. Les objets de classe vivaient d'un ct, et les variables de type
valeur comme irt et f loat vivaient de I'autre. Bien str, les types valeur
et les types objet taient autoriss jouer dans le mme programme,
mais le programmeur devait maintenir cette sparation dans son esprit.
C# abat le mur de Berlin qui sparait les types valeur des types objet.
Pour chaque type valeur, il y a une "classe de type valeur" correspondante, que I'on appelle une sfructure. Ces structures faible cott peuvent
se mlanger librement avec les objets de classe, permettant aux programmeurs d'crire des instructions comme celles-ci :
MyClass

/i

ny0bject = new MyClass0;

affiche un "myObject* nis sous forme de chane

Console.trtrriteline (my0bjeet. ToString 0 ) ;

int i *

5;

int sous forne de chane


Console.Writeline (i. ToString0 ) ;
/l affiehe 1a constante 5 sous forme de chane
Console.l.Iriteline (S . foString 0 ) ;
i

affiche un

Non seulement je peux invoquer la mme mthode sur un lnt que sur un
objet de M1'Cias s, mais je peux aussi le faire avec une constante comme
"5". Ce scandaleux mlange des types de variable est une fonctionnalit
puissante de C#.

479

Index
NET

description 3, 5, 7

A-UN 280
quand utiliser 281

Abstract 318
Abstractlnheritance 3 I 8
Abstractlnterface 343
Abstraction 231, 232
Accs
des membres de
classe, restreindre 239
contrle de l', 237 244, 246

AccessControl 391
Accesseur 250
Addition sur des chalnes.
oprateur 202
Aide
en cours d'dition 196

plus

195

saisie automatique 190

AlignOutput 218
Ancrer un contrle dans un
formulaire 426
Application
ajouter des actions l8
commentaires 26
console
cadre de travail 26
crer 24
crer un modle de 22
dessiner 12

dossier dans lequel


enregistrer 22
excuter 19
partir de la ligne de
commande DOS 25
gnrer 17
nom par diauT22
o sont les instructions 27
Application Windows
afficher le code source 415

ajouter
des actions 433
des contr6les 422
des tiquettes 424
concevoir la
prsentation 4l 1

connaltre les
composants 431
construire les menus 419
crer le cadre de

travail 413
dfinir 410
dessiner 412
redimensionner le

formulaire 426
Argument(s)
accorder dfinition et

utilisation

partir d'une
fentre 168
un programme 164

une fonction 144

par rfrence une

fonction

154

plusieurs une

fonction

145

qui sortent mais n'entrent


pas 156

surcharger une

fonction

147

Arithmtique (oprateurs) 53
Array (classe) 116
syntaxe I 17

Arrondir

37

Assembleur 4
Assignation, oprateur de 56
Assistant Applications 8
Asynchrone (l/O) 396
Automatique, saisie 190
AverageAndDisplay 145
AverageAndDisplayOverloaded 148
AverageStudentGPA 125

AverageWith

CompilerError

146

146

d'un type valeur, passer


une fonction 151

par dfaut,

implmenter

149

passer
I'invite de DOS 165
MainQ 164
partir de Visual

Studio .NET 170

Balise des commentaires de

documentation

196

BankAccount 240,291
BankAccountC ontructors-

AndFunction 266

lndex
String 201
Classif ication 234, 235,

273,311
Clipboard 435
Commandes de boucle 79
Comment aire 26
de documentation 195

balise 196
Comparaison, oprateurs
de 59

Continue 85
Contrle
ancrer dans un formulaire 426
d'accs 237,244,246
dans une application

I'invite de 165
Dossier de classement d'une
22

Double 39
DoubleBankAccount 246

15

Conversion

CompareQ 204
avec majuscules et
minuscules 208
Comparer des nombres 41
Compteur 44, 45, 47,52
utiliser une variable
comme 41
Concatnation 47,221
Concepteur de formulaires 12

implicite 464
invalide, viter en
utilisant is 284
Conversion de temprature 40
Convert 212
Couplage 391

CustomException 380

252

comment se fait la

construction

262

de la classe de base,
passer des arguments
au 288
de structure 348

et hritage 286

viter les duplications


entre les 265
excuter partir du
dbogueur 258
exemple 256
par dfau|253,26l
de la classe de base,

invoquer 286
surcharger 263
ConstructorSavingsAccount 291

quitter 452
codes d'erreur 365

retourner 360. 362


utiliser un mcanisme

application, crer un
modle de22
classe 173
Const 116
Constante numricue
dclarer 50

Early binding 306


Enregistrer, avant de
Erreur

Console,

type 50
Constructeur(s)

classe 136
DOS, passer des arguments

application

Windows, ajouter 422


mettre en place 13

proprits

Donne membre d'une

d'exceptions 367
Espace de nom
accder des modules
du rnme 388
contrler I'accs aux
classes avec 391

Decimal 42

limitations

44

vitesse de calcul 44
DecimalBankAccou nt 247

Dclaration
tableau 123
Dclarer
constante numrique 50
variable 32
DemonstrateDefault-

Constructor 256
Dessiner une application

12

Destructeur 293
Dterministe 294
DisplayArguments 164
DisplayRoundedDecimal 149

DisplayXWithNestedloops 95
Distribu (dveloppement) 5
Do... while 84
Documentation
commentaire de 195
balise 196
XML, gnrer 200

dclarer 388
runir des fichiers
source dans 387
utiliser avec using 390
EST-UN 278

quand utiliser 281


tiquette clans une application Windows, ajouT.er 424
vnement 18,434
Exception
classe de. redfinir 380
crer une classe de 371

exemple 368

intercepter et
renvoyer 378
laisser passer 375
utiliser un mcanisme
cle 367

Excutable 4

483

Index
instructions if imbriques 76

Incrmentation, oprateurs
de 57, 58

Logique
oprateurs 61
type bool 44
de comparaison 59

hrite, surcharger 296


nom complet 182
proprits actives 434
redfinie, Accder 308

Index d'un tableau 118


InheritanceExample 2 72
InheritanceTest 321

InheritingAConstructor 286
Initialisation, rfrence non
initialise 112
Instance 106,234
Int 33, 35
Interface
crer soi-mme 332
abstraite 342

description 329
et hritage342
exemple 330
prdfinie 334
InvokeBaseConstructor 289
InvokeMethod 179
Is 284, 308

IsAllDigits 213
Italique (mettre en) 439

7-K
Java 5

redfinir
accidentellement 302
ou ajouter un test 301

rles
MainQ, passer des
arguments 164
Majuscules 208
Masquer Toir redfinir
Membre
de classe, restreindre
I'accs 239

donne 136
d'un objet, accder 107

fonction

136

statique d'une classe 115


Mmoire, stocker un obiet
en 107

d'une application
Windows, construire 419

implmenter les

options 440
Mthode 181
abstraite 318
accs 394

dclare
comme virtuelle 309

machine 4
Late binding 306

Length 122
Liaison
prcoce 306

tardive 306
Lire les caractres saisis au
clavier 210

complet
d'une mthode 182
d'une fonction 296
conventions sur 48
de classe 105
de fichier ,lire 446
de fonction,

ajouter des actions 435

Langage(s)
d'assemblage 4
de haut niveau 4
Java 5

Nachos 231
New 258, 469
Nom

Menus

Label425

c#3,5

181

Minuscules 208
MixingFunctionsAndMethods 188, 196
Modle 8
ModifyString 203

147

d'une fonction 207

internal 394
private 393
protected 393
public 393
dfinir 179
diffrente selon la
classe 297

d'objet, dfinir 177


d'une classe de base,

redfinir

surcharger

de variable 127

298

d'une structure 349

espace de 387

Nombre(s)
comparer des 41
en virgule flottante,
comparer 60
entr au clavier 215

format de sortie

224

rels 38
Notation hongroise 49

Null

111

rfrence 161
Numrique
entre, analyser 212

485

lndex
Replace 221

utiliser 396

Return 157, 158


RichTextBox 418
RTF 4I8

String 46,202
classe 201

convertir en un autre

crire un fichier 449

lire un fichier 448

type 212
StringReader 395
StringToCharAccess

StringWriter

TextReader 395
2I0

395

Struct 346
Structure 346

constructeur de 348
Saisie automatique 190
sur les fonctions de la

bibliothque
standard 191
sur vos propres fonctions et mthodes 193
SavingsAccount 279
Sceller une classe 325
Sealed 325

Scurit
niveaux de 243
SetX 250

Signe (variable) 37

SimpleEditor 410
enregistrer avant de

et classe 345
exemple 350
mthodes d'une 349
types structure
prdfinis 353
StructureExample 350
Surcharger Voir aussr

redfinir
constructeur 263
fonction I47,296
une mthode
d'une classe de base 298

hrite 296
Switch 97
pour tester une chane 209

quitter 452
fentre d'dition 417
SimpleSavingsAccou nt 27 5

T
t

Sortie d'un programme


contrler manuellem ent 2I7

Sortlnterface 336
SortStudents 130
Source 4

fichiers

diviser un programme
en plusieurs 385

runir dans un espace


de nom 387

split

fixe I 17
variable 120
dclaration 123
dpassement de taille 119

251

StreamReader, utiliser 402

StreamWriter 395

124

foreach 127

index

215, 223

Statique
membre d'une classe 115

proprit

Tableau 116 Voir cussi Array


longueur

d'objets

118

longueur 123
proprit Length 122

trier

Test 162
TextBox 422
changer la taille de
police en utilisant 444

128

Taille, de police, changer 439


Temprature (conversion) 40

TextWriter 395
This 184
absent 188

explicite
Throw 367

185

Tolnt32 212
Tolnt64 212
ToString 384
TrackBar 424
changer la taille de

police en utilisant 442


Trier un tableau 128
Trim 212,217
Tronquer 37
True 44

Try 367
Type
assigner un 65
bool 44
char 45

const

116

conversion de 45
conversion de, le cast 51
conversion explicite,
le cast 65
conversion implicite 64
de longueur fixe 49
de rfrence, oprateurs

sur

111

decimal 42
decimal, int, et float
(comparaison) 44
dclar, utiliser chaque
fois 306
dfini par le programmeur 50
d'expression, accorder 63
double 39
d'une constante 50
d'une opration,
calculer 63

487

erlt?ll .sr^E-I

'V'd'S tuudo8J red


7697 rar,ruul ue

raurudur.p 9^r{JV

488

c# pour tes Nuts

entiers divers 35
tendue 42
valuation par 107
float 38
fonction avec ou sans 160

int

33

intrinsque 49

string 46
convertir

212

et char, comparais on 47

struct 346
structure prdfinie

353

type valeur 49
unifier le systme de 353
vitesse de calcul selon
le 42

TypeUnification 354

UML (Unified Modeling


Language) 312
Using, utiliser un espace de
nom avec 390

en virgule

flottante 38
flottante, comparer 60
flottante, dclarer 38
flottante, limitations 40
flottante, prcision 39
initialiser 467
logique 44
nom 127
non dclare 462
passer par rfrence
une fonction 154

ou par valeur

151

rgles de

dclaration 34
porte 90
signe oLr non signe 37
.structure 346
utiliser comte
cornpteur 41
VariableArrayAverage 1 20
VehicleDataOnly 109
Virgule flottante 38
comparer des nombres
en 60

format

cle

Pentium

calcul du
41

limitations 40
prcision 39
Virtuelle, mthode, dclarer
Valeur, valuation par, 107
Variable
de longueur fixe 49
decimal, dclarer 43
dclarer 32

comme 309
Visual Basic 20
Visual Studio
fentres de 13
interface utilisateur 13
plus d'aide 195
saisie automatique 190

Visual Studio .NET 7


Vitesse de calcul, 42 44

Void

16i)

While 80
Windttws
crer une application
avec C#

dfinir une
application 410
gnrer et excuter un
premier programme 11
Presse-papiers 435

WriteBinary

Writeline

395
173

X-Z
XML 196
documentation,
gnrer 200
Zro, rfrence 161

486

c# pour tes Nuts

excutable 4
excuter en dposant un

fichier dessus
Obiect (classe) 285
Objet(s)
accder
, mthodes pour 245
aux membres d'un 107
changer de classe 282

constructeur 252
courant 181

177

programmation
oriente 231
110

de
stocker en mmoire

PassByReferenceError I 54
PassByValue 151

PassObject 176
PassObiectToMember177

Pentium
calculs en virgule

flottante

41

vitesse de calcul 42
PEUT_TRE-UTILISCOMME 327
Police, changer de 439

107

string 202
structure 346

Prcision 39,41
Presse-papiers 435

Programmation
fonctionnelle 233, 235

tableau de I24
Oprateur(s)
d'addition sur les

d'incrmentation 57.

accder aux 434


avec effets de bord 252
de classe, dfinir 250
des contrles 15

d'objet

115

Length 122
statique 251,434
Protection, niveau de 466

Public 105,239
exemple 240

g Etl

58

logiques 6l
ordre d'excution 55
111

simples 54

231

OutputFormatCont r ols 226

Quitter, enregistrer avant


de 452
Ramasse-miettes 294
ReadBinary 395

langages de 3

Readline

oriente objet 231

Redfinir
accidentellement 302
une mthode d'une

abstraction

chanes 202

d'assignation 56, 111


de comparaison 59

ternaire 66
Oriente objet
(programmation)
Out 153, 158

Polymorphisme 305,306
accder une mthode
redfinie 308
Porte des variables, rgles

Proprit(s)
active 177,434

de 90

115

point

2I5

Polymorphiclnheritance 309

passer une fonction 175

proprit(s)

ParseSequenceWithSplit
PassByReference 153

Function

accder 183
courant, this 184
distinguer les uns des
autres I I I
d'une classe abstraite 320
fonctions et mthodes
de, dfinir
null 111

Pad 217

168

interface 236
modle de 8
passer des arguments
164

231

classification 234, 235


contrle d'accs 237
implmentation en

c#

238

interface utilisable 236


Programme Voir cussi

application
contrler manuellement
Ia sortie 217
dfinir une application
Windows 410

dfinition 4
diviser en plusieurs
fichiers source 385

212

classe de base 298

ou ajouter un test 301


Redimensionner le formulaire d'une application
Windows 426
Rel (nombre) 38
Ref 153
Rfrence
null et zro 161

non initialise 112


ReferencingThisExplicitly I 86
Registres (du processeur) 49
RemoveWhiteSpace 221

48 4

G# pour

les Nuts

Excuter un programme en
dposant un fichier
dessus 168

conditions mutuellement
exclusives,

contrler,

Expression
accorder les types 63
valuation 55

70

ancrer les composants 426

exemple, 71
foreach, 127
goto, 100

switch,

Concepteur de

formulaires

97

Fonction
appele mthode 181
d'accs 250
dfinir et utiliser 135

FactorialErrorRetur n 362
FactorialException 368

FactorialWithError 360
Factorielle 360
Factoring 311
Fahrenheit 40
False 44

partir d'une
Proprits

168

15

Rsultats 11
Fermeture (bouton de),
implmenter 456
Fichier(s)
Iire le nom du 446
rassembler des donnes
dans 394
RTF 418

crire 449

lire

d'objet, dfinir 177


exemple 137

448

source 4

diviser un programme
en plusieurs 385

runir dans un espace


de nom 387
FileRead 402

FileWrite 397
Final 367

FixedArrayAverage 118
Float 38
FIux d'excution

12

redimensionner 426
Four micro-ondes 231
Fraction
prcision 39
reprsenter 37
FunctionsWithDefaultArguments 149

indiquer une erreur


dans 362

membre
d'une classe 136
statique d'une classe,

Fentre
Code 18
de Visual Studio 11
passer des arguments

ajouter un contrle
dans 14

74

dfinir

177

11

une application l7
GetX 250

nom 147,207
complet 296
passer
des arguments 143
des arguments d'un

type valeur

Gnrer

Goto 100
Gras (mettre en) 439

151

des arguments par

rfrence 152,154
des arguments par

valeur

151

un objet 175

pourquoi ? I42
qui ne retourne pas de
valeur, dfinir 160
retourner une valeur 157
sans type 160
surcharger 147,296
type ou non type 160
utiliser return 157
void ou non-void 160

Writeline

Formulaire

12

utilit 274
HidingWithdrawal 299
Hirarchie de classes 321
Hongroise (notation) 49
HTML 196

173

For 91
Foreach 127,210
Format 224
RTF 418

Hritage 272
et constructeur 286
et interfac e 342
exemple 275

I,
I/O asynchrones 396
If, 70

viter le else

75

482

G#

pour tes Nuts

BankAccountContructorsAndThis 267
BankAccountWithMultipleConstructors 263
Base 288, 303

Bote outils 13
Bool 44
conversion 45
Bord, effets de252
Boucle(s)
break et continue 85
briser 85
commandes de 79

do... while
for 91

84

quoi sert-elle ? 92

exemple 91
imbriques 93
while 80
Boxing 159

Caractre

non imprimable 46,211


de retour la ligne 47
saisis au clavier, lire 210
variable de type 45
Cast 5l

invalide I'excution 283


Catch 367
assigner plusieurs
blocs 373
laisser passer des
exceptions 375
sans arguments 405
Celsius 40
Chalne 47 Voir oussi string
analyser des caractres

d'une 210
Compare 204
concatnation 47,221
contrler manuellement
la sortie d'un
programme 217
convertir en un autre
Iype 212

Break 85

Build Voir gnrer


BuildASentence 205
Bulles (tri en) 129

de contrle 224

description 3, 5,

Cacher l/oir redfinir


Cadre de travail d'une
application Windows,
crer 413
Calculatelnterest 72
CalculatelnterestTable 80, 137
CalculatelnterestTableMoreForgiving 86

CalculatelnterestTablewithFunctions 138

CalculatelnterestWithEmbeddedTest 76

classes avec les espaces de nom 391

crer un objet d'une, 106


de base, passer des
arguments au constructeur de 288

clfinition 105, 106,


destructeur 293

110

d'exceptions
crer 371

redfinir

380

et structure 345

tendre 323
factoring 3l I
faiblement couple 391
fonction membre 136
statique d'une,

dfinir

177

fortement couple

391

invariabilit 204
lire 210
oprateur

historique

*47
crer une application
Windows avec 7

contrler I'accs aux

hritage 272
hirarchie de
crer une nouvelle 324
redmarrer 321

Format 224

C#

classification 273, 31 I
Console 173
contenant d'autres
classes 1 13

d'addition

108

instance 234
d'une 106

202

Parl217

membre(s)

Replace 221

Char 45

donne 136
statiques d'une 115
ne pouvant tre
instancie que
localement 270
nom, majuscules et
minuscules 105

Classl

object

Split 215,223
suite de chiffres taps au
clavier, traiter 215

Trim2IT
utiliser srvitch avec

209

164

285

proprits de, dfinir 250

Classe 104

abstraite

restreindre l'accs des

utiliser 318
Array I 16

scelle 470

changer la classe d'un

sceller 325

objet

105

282

membres 239

sous-class e 234

47I

Sixime partie : Petits supplments par paquets de dix

Dfinis soigneusement tes ttlttes de t/ariable,


t
mon enfant
C++ est trs politiquement correct. Il ne marcherait aucun prix sur les
plates-bandes d'un ordinateur en exigeant qu'un type de variable particulier
soit limit une tendue de valeurs particulire. Il spcifie qu'un int fait
peu prs "telle taille" et qu'un long est "plus grand". Cette dcision conduit
des erreurs obscures quand on essaie de dplacer un programme d'un type
de processeur un autre.

C# n'y va pas par quatre chemins. Il dit qu'un int fait 32 bits et qu'un long
fait 64 bits, et que c'est comme a. En tant que programmeur, vous pouvez envoyer cela votre banque ou un autre ordinateur sans qu'il en

rsulte d'erreurs inattendues.

Pas d'hritage multple


C++ autorise une mme classe hriter de plus d'une classe de base. Par
exemple, une classe CanapLit peut hriter de la classe Lit et de la
classe Canap. a ne semble pas manquer de rigueur, et a peut effectivement tre trs utile. Le seul inconvnient, c'est que I'hritage de plusieurs
classes de base peut provoquer des erreurs de programmation qui sont
parmi les plus difficiles identifier que I'on connaisse.

C# se met en retrait, et vite des erreurs supplmentaires en cartant


I'hritage multiple. Toutefois, ce choix n'aurait pas t possible si C#

n'avait pas remplac I'hritage multiple par une nouvelle fonctionnalit


I'interface.

Prtloir une bonne interface


Quand les gens ont pris un peu de recul pour raliser dans quel cauchemar ils s'taient mis avec I'hritage multiple, ils se sont rendu compte que
dans 90 % des cas, la deuxime classe de base n'tait l que pour dcrire
la sous-classe. Par exemple, une classe parfaitement ordinaire pouvait
hriter d'une classe abstraite Persistable, avec une mthode abstraite
read O et une autre write O. Cela obligeait la sous-classe implmenter
Ies mthodes read O et'*,r-ite O et dire au monde extrieur que ces
mthodes taient disponibles si on voulait s'en servir.

47 6

Sixime partie : Petits supplmenrs par paquets de dix

private string sName;


public void set(string
{

this.

sName

sName)

sName;

public string

get

IL

retourne une copie du

rtrrrn

St-jno

nOpy(SName)

nom
;

class {yClass
t

public void AddlastName(Student student)


{

cirrdonf cot/<lr{g1t.get0
've\/

+ " Kringle");
L'LbL'

1
J

La notion de Proprit en C# permet cl'implmenter les fonctions get O et


set O d'une manire compltement naturelle dans le programme :
usirig

System;

public class

Student

i
n+g.r'ar
pr].vare
^-.i,'^+^
srrlng

public string

-t\T^*^.
sr\ame;

Nane

set{sNane=value;}
get { return String.Copy(sName); l
l
l

class

MvClass

public void AddlastName(Student student)


t

student.Nane = student.Nane

"Kringle"

]
]

/e n'inclurai plus jamais un fichier


C++ impose une cohrence rigoureuse des types. C'est une bonne chose.
Il le fait en vous obligeant dclarer vos fonctions et vos classes dans des

47 4

Sixime partie : Petits supplments par paquets de dix

Pas de donnes ni de fonetions globales


pour un langage orient objet, et il I'est, au sens o vous
pouvez I'utiliser pour programmer d'une manire oriente objet. Vous
pouvez aussi mettre de ct les objets en plaant simplement les donnes
et les fonctions dans un espace global, ouverts tous les lments et
tout programmeur dot d'un clavier.
++ psse

C# demande au programmeur de lui faire allgeance : toutes les fonctions


et tous les membres donne doivent faire partie d'une classe. Si vous
voulez accder cette fonction ou ces donnes, vous devez passer par
I'auteur de cette classe. Il n'y a pas d'exception cela.

Tous les objets sont allous partr du tas


C** nplerisent I'allocation de mmoire de trois manires diffrentes, chacune avec ses propres inconvnients :
C comme

tz

Les objets globaux existent du dbut la fin de I'excution du


programme. Un programme peut facilement allouer plusieurs
pointeurs au mme objet global. Si vous en modifiez un, ils sont
tous modifis, qu'ils soient prts pour cela ou non.

t/

Un objet de pile est propre une fonction (ce qui est une bonne
chose), mais son allocation disparat lorsque la fonction retourne
son rsultat. Tout pointeur qui pointe vers un objet dont I'allocation
mmoire a t supprime devient invalide. Ce serait trs bien si
quelqu'un avait prvenu le pointeur, mais le malheureux s'imagine
toujours qu'il pointe vers un objet valide, et le programmeur aussi.

tz

Les objets du tas sont allous en fonction des ncessits. Ces


objets sont prop.res un thread d'excution particulier.

Le problme est qu'il est trop facile d'oublier quel type de mmoire se
rfre un pointeur. Un objet du tas doit tre retourn quand on en a fini avec
lui. Oubliez-le, et votre programme aura de moins en moins de mmoire
disponible, jusqu' ce qu'il ne puisse plus fonctionner. D'un autre ct, si
vous librez plus d'une fois le mme bloc du tas pour "retourner" un bloc de
la mmoire globale ou de Ia pile, votre programme est parti pour une longue
sieste. Il faudra peuttre Ctrl+Alt+Del pour le rveiller.

47 2

Sixime partie : Petits supplments par paquets de dix

Ces deux possibilits sont mises en vidence dans la classe suivante

public class

I'lyClass

public string ConvertoString(int

n)

// convert the int n into a strins


string s = n.ToString0;

public string

ConvertPositiveNurnbers

(int

n)

I I oy positive
if(n)0)
t

string s
tLulll

numbers

are valid for conversion

n.ToString0;

Console.Hriteline("the argunent {0J is inva1id", n);


l

ConrrertToString O calcule une chalne retourner, mais ne la retourne


jamais. Ajoutez simplemnt rt:u::, -.: er bas de la mthode.
Convertpositivelrl'_rrnbers ( ) retourne la version chalne de I'argument 1nt
n lorsque celui-ci est positif. En outre, il gnre correctement un message
d'erreur lorsque n n'est pas positif. Mais mme si n n'est pas positif, la
fonction doit retourner quelque chose. Dans ces cas-l, retournez soit un
nu11, soit une chalne vide "". La solution qui conviendra le mieux dpend

de I'application.

3 attendue
Ce message indique que C# attendait encore une accolade fermante Ia
fin du listing. Quelque part dans le code, vous avez oubli de fermer une
dfinition de classe, une fonction ou un bloc if . Reprenez votre code et
appariez soigneusement les accolades ouvrantes et fermantes jusqu' ce
que vous trouviez la coupable.

.9\..4
I(/ll
\p1/
-

Ce message est souvent le dernier d'une srie de messages d'erreur


souvent idiots. Ne vous proccupez pas des autres avant d'avoir remdi
celui_ci.

47 0

Sixime partie : Petits supplments par paquets de dix

new

public void Function0

l
i

t/

Vous vouliez vraiment hriter de la classe de base par polymorphisme, auquel cas vous auriez dt dclarer les deux classes de la

faon suivante
public class

BaseClass

public virtual void Funetion 0


{

l
]

public class SubClass:

BaseClass

public
overrides void Function0
-l
L

l
]

Voyez le Chapitre 13 pour en savoir plus.


.

alllllfa

rlr,,/t \ Colr
o-/nhd{ \ ""'* n'g5t

=\d\y /
\/

paS Une

erreqr, maiS SeUlement Un avertisSement danS la

fentre Liste des tches.

'subclassName' : ne peut pas hriter de la


classe scelle'baseclassName'
Ce message indique que la classe est scelle et que vous ne pouvcz donc
pas en hriter, ni en modifier les proprits. Typiquement, seules les

classes des bibliothques sont scelles. Vous ne pouvez rien y changer.

'className' n'mplnente pas le membre


d' i nterface'meth odN ame'
L'implmentation d'une interface reprsente une promesse de fournir une
dfinition pour toutes les mthodes que comporte cette interface. Ce
message vous dit que vous n'avez pas tenu cette promesse car vous n'avez
pas implment la mthode cite. Il peut y avoir plusieurs raisons cela :

468

Sixime partie : Petits supplments par paquets de dix

n=

1.

Dans ce cas, aucune valeur n'est assigne n dans SomeFunction O, mais


elle en reoit une dans SomeOtherFunction (). Cette dernire ignore la
valeur d'un argument cut comme s'il n'existait pas, ce qui est le cas ici.

Le fichier'programName.exe' ne peut pas tre


copi dans le rpertore d'excution. Le processrls
ne lreut pas...
En gnral, ce message se rpte de nombreuses fois. Dans presque tous
les cas, il signifie que vous avez. oubli d'arrter le programme avant de le
gnrer nouveau. Autrement dit, voil ce que vous avez fait :

l.

Vous avez gnr votre programme avec succs (supposons que


ce soit une application consoleo bien que cela puisse se produire
avec n'importe quelle sortie en C#).

2.

Vous avez vu Ie message "Appuyez sur Entre pour terminer",


mais, dans votre hte, vous ne I'avez pas fait. Votre programme
est donc toujours en cours d'excution, et vous tes retourn

dans Visual Studio pour modifier le fichier.

3.

Vous avez essay de gnrer nouveau le programme avec vos


modifications. C'est l que vous avez obtenu ce message d'erreur.

Un fichier excutable .EXE est verrouill par Windows jusqu' ce que le


programme termine effectivement son excution. Visual C# ne peut pas
craser la version prcdente du fichier excutable .EXE avec la nouvelle
version tant que le programme n'a pas termin son excution.
Revenez I'application, et faites le ncessaire pour qu'elle se termine.
Dans le cas d'une application console, appuyez simplement sur la touche
Entre. Vous pouvez aussi mettre fin I'excution d'un programme dans
Visual Studio en slectionnant DbogueriArrter le dboguage.
Une fois que la version antrieure du programme a termin son excution,
gnrez nouveau I'application.
Si vous n'arrivez pas vous dbarrasser de I'erreur en

mettant fin
I'excution du programme, il est possible qu'il y ait quelque chose qui

466

Sixime partie : Petits supplments par paquets de dix


* f est toujours de type ciouble, mais le programtneur a
indiqu qu'il voulait que le rsultat soit converti en type f 1oat, ntme au
cas improbable o il en rsulterait une perte d'informations.
Le rsultat de 2.A

Une autre approche consisterait s'assurer que toutes les constantes


sont de mme type :
class

MyClass

static public float FloatTimes2(f1oat f)


r

I I ceci fonctionne bien parce


float fResult = 2,0F * f;

que 2.0F est une constante de type

float

return fResult:
l

Cette version de la fonction utilise une constante 2.0 de type f 1o.r au lieu
du type dor-Lbie par dfaut. Un f ioat multipli par un f loat st ur r i,,-.r.

'className.memberName' est inaccessble en


tl
raison de son nieau de qtrotection
Ce message indique qu'une fonction essaie d'accder un membre auquel

elle n'a pas accs. Par exemple, une mthode d'une classe peut essayer
d'accder un membre priv d'une autre classe (voyez le Chapitre l1) :
public class

MyClass

public void

SomeFunction0

YourClass uc = new YourClass 0

I ceci ne fonctionne pas correctement prce


// ne peut pas accder au membre priv
uc.nPrivateMember = 1;
I

que MyClass

I
)

public class

YourClass

private int

nPrivateMember

0;

En gnral, I'erreur n'est pas aussi flagrante. Bien souvent, vous avez simplement laiss le descripteur hors de I'objet membre ou de Ia classe elle-rnme.

464

Sixime partie : Petits supplments par paquets de dix

lmpossble de cnnuertir mplicitement le ttlpe 'x'

en

"/'

Ce message indique gnralement que vous essayez d'utiliser deux types


de variable ctiffrents dans la mme expression. Par exemple :
int

naa

= lfl

// gnre un message d'erreur


int nFactoredAge = 2.0 * nAge;
Le problme est ici que 2.0 est une variable de type doubie. La variable nAge
de type int multiplie par le 2.0 de type doubl e produit une valeur de type
double. C# ne va pas automatiquement stocker une valeur de type doubie
dans la variable de type int nFactoredAge, car il pourrait en rsulter une
perte d'information (en particulier, la partie dcimale de la valeur doubie).

Certaines conversions ne sont pas aussi videntes, comme dans I'exemple


suivant :
class

MyClass

^ -"11'r
^
^r^r'r
Ld.LJ.
PUUrr(:

float

FloatTimes2

(float

l/ ceci produit une erreur de onr:tion


float fResult = 2.0 * f;
return fResult;
)

On pourrait croire que multiplier par deux un type f loat ne pose pas de
problme, mais c'est justement l qu'est le problme. 2.0 n'est pas de type
f loat mais de type double. Un f loat multipli par un double donne un
double. C# ne va pas stocker une valeur de type double dans une variable
de type f 1oat, cause - vous avez devin - de la perte d'informations qui
pourrait en rsulter (dans ce cas, plusieurs chiffres de prcision).
Les conversions implicites peuvent dconcerter plus encore le lecteur
dsinvolte (c'est mon cas, dans les bons jours). Cette version de
Floa*.-Times2 ( ) marche trs bien
class

MyClass

static public float Floatimes2(float f)


{

46 2

Sixime partie : Petits supplments par paquets de dix

parfois affreusement bavard. J'ai rcluit certains des messages d'erreur pour
les faire tenir sur une page. En plus, il y a dans un message d'erreur diffrents
endroits o apparat le nom d'un membre donne offensant ou d'une classe
irrvrencieuse. J'ai remplac ces noms par variableflane, memberName ou
c

las sNane.

Enfin, C# ne se contente pas de cracher le nom de la classe. Il prfre


mettre sur la table I'espace de nom au grand complet (au cas, bien str, o
le message aurait t trop court avec la premire solution).

'className' ne contient pas de dfintion pnur


'memberName'
Ce message d'erreur peut signifier que vous avez oubli de dclarer une
variable, comme dans I'exemple suivant :

for(index = 0; index
{

( 10; index++)

instructions

La variable index n'est dfinie nulle part (pour savoir comment dclarer

les variables, reportez-vous au Chapitre 3). Cet exemple devrait avoir t


crit de la faon suivante :

for(int
t

index =

0; index ( 1t;

index++)

instructions

La mme chose s'applique aux membres donne d'une classe (voyez le

Chapitre 6).
Il y a en fait plus de chances que vous ayez fait une faute de frappe dans
un nom de variable. En voici un bon exemple :
class Student
i

oublic
strine sStudentName;
r.--------..o
public int nID;
l
^1^^^
r D M.,a1^^^
|rJ/ vrd
u

Dans cette

partil...

serait complet sans notre


dix ? C# est trs dou pour
partie
des
traclitionnelle
lorsque VouS
programmes
vos
dans
trouver cles erreurs
remarqu.
doute
SaI-lS
l'avez
gnrer.
Vous
les
essayez cle
tre assez
peuvent
gnre
qu'il
d'erreur
ntessages
Mais les
Le Chapiaussi.
remarqu
doute
sans
I'avez
Vous
obscurs.
gnrations
de
d'erreur
messages
les
dix
revue
passe
en
tre 19
les plus courants et ce qu'ils signifient le plus souvent. Et comtne
savoir c'est pouvoir, VouS y trouverez aussi des suggestions de
correction pour les problmes correspondants.
Beaucoup cles lecteurs cle ce livre seront venus C# par le
plus rpanclu cle tous les langages orients objet, C++. Le
chapitre 20 clonne la liste des dix diffrences principales
entre ces deux langages.

1t
V

uel livre Pour

Les nuls

456

Cinquime partie : Programmer pour Windows avec Visual Studio

lmplmenter le bouton de fermeture de la


fentre
Il reste encore un petit problme. Il est toujours possible de quitter
I'application en fermant la fentre :

l.

Dans le Concepteur de formulaires, slectionnez le cadre de la

fentre du programme.

2.

Dans la fentre Proprits, slectionnez l'vnement Closing.

3.

Entrez le nom de fonction ApplicationWindowClosing.


C'est la proprit Cios irrg qui est invoque lorsque I'utilisateur
clique sur Ie bouton de ferrneture de la fentre (le x dans le coin
suprieur clroit). Il est facile d'associer une mthode cette proprit. La difficult est de savoir quoi faire quand elle reoit le
contrle. La rponse est donne par un aspect des mthodes de
proprit, que j'ai pass sous silence jusqu'ici.

Lorsque C# appelle une mthode en rponse un clic sur un


bouton ou la saisie d'une valeur, il lui passe deux arguments. Le
premier de ceux-ci est appel le sender. Cet objet est le composant
qui est I'origine du stimulus. Les mthodes que nous avons
gnres ne pouvaient tre invoques que par une seule source.
Toutefois, il peut tre utile de diffrencier les senders, car cela
rcluit Ia quantit de code crire [e suis toujours d'accord pour
crire moins de code). Par exemple, une mme mthode peut tre
utilise pour traiter plusieurs boutons radio, le sender indiquant
quel est le bouton sur lequel a cliqu I'utilisateur.
Le deuxime argument contient d'autres informations sur l'vnement, qui ont trait la raison pour laquelle la mthode a t appele. Toutefois, la classe CancelE,ientArgs passe notre mthode
la suite d'un clic sur le bouton de fermeture de la fentre contient
une proprit Cancel (Annuler). Si vous donnez cet indicateur la
valeur i ru, I'opration de fermeture de la fentre est tue dans
l'uf

4.

Modifiez Ia mthode .rpplication'riindowClosing O sur la base de


la mme logique "ne pas perdre les modifications" que nous
avons utilise avec succs pour la commande Fichier/Quitter:

45 4

Ginquime partie : Programmer pour Windows avec Visual Studio

retourne DialogP.esur t . Ies, c'est que I'utilisateur dit qir'il ust


d'accord pour ne pas enregistrer ses moclifications.
l"iessaq'Br. x

C'est ce que ralisent les modifications suivantes aux mthocles

Fileltperi l, f 'ieSa,.'er) et FiieExjt O


orivatp

void !Filpnpn|,nh'ipnt
44vvrv-.

conrlor

(rrci-om Frrontl
-, _.^,,\rgs e)

I I n'crase pas 1e prcdent fichi-er en ouvrant 1e nouveau


if (IsChangeOK0 == false)
I
L

return:
l
/

I tout va bien. dit-i1

OpenAndReadFile

// \a

zone de texte est naintenant dans

LT^--+nL^--^)
urcLUlld.lLu = false;

1'tat

non modifi

private void FileSave(object sender, System.EventArgs

e)

/1 donne la valeur false f indicateur


I I si I'enregistrement a fonctionn
1r ( )avebpecltl_ed.L 11e U /

de modificati.on

bTextChanged

= false;

l
]

private void FileExit(object'sender, System,EventArgs

e)

//
ll
if

ne quitre pas si les modifications n'ont pas t dj enregistres


ou tant que 1'utilisateur ne dit pas qu'i1 est d'accord
(IsChange0K0 == false)

return;
]
I

Ia voie est libre

Application.Exit 0

Dans tous les cas, la mthode J s rlhang-e0K r. ) s51 invoque avant d'excuter
une opration qui pourrait provoquer la perte d'une modification.
Il nous manque encore quelque chose : il faut que I'indicateur bTextthar,e ert
reoive la valeur r rLre chaque modification apporte dans la zone de texte.
Ir.icl'rTe;iiii.,.x nous donne ce qu'il nous faut : la proprit dynamique
Tertchange,j i,i, qui est invoque chaque fois que quelque chose modifie le

452

Cinquime partie : Programmer pour Windows avec Visual Srudio

Figure 18.7

SlnpleEdit rr
est maintenant un

r--l t:it+
,t t+ ,-:.r++
,,la|,-, rliffr?frt+:, i,lr _ 1,.:

diverses tailles

divers styles

vrita ble
diteur,

capable de
lire d'un seul
coup de gros

fichier

Taille de police

[E*

RTF

Ne lrerdez pas mes modifcations en quifiant

L'implmentation de la commande Fichier/Quitter est une chose facile

l.

Dans le concepteur de formulaires, slectionnez I'option de


menu Fichier/Quitter.

2.

Dans la fentre Proprits, slectionnez l'vnement (liick. et


entrez le nom de fonction FileExit.

3.

Implmentez de Ia faon suivante Ia mthode


vous venez de crer :

private void FileExit(object sender, System,BventArgs

pileExir i. j

que

e)

Application.Exit0;
]

C'est la classe Appiication qui contrle I'ensemble du programnre.


Comme son nom I'indique, sa mthode E;<ir O fait directement sortir de
la scne. L'inconvnient de cette solution est que quitter le programme
sans avoir commenc par faire Fichier/Enregistrer provoque la perte cle
vos dernires modifications. ce n'est pas une chose faire.
Heureusement, les mthodes de lecture et d'criture nous donnent ce clont
nous avons besoin pour viter cette catastrophe. Sa'reSpecifiedFil-e ()
retourne un bcol qui indique si la donne a effectivement t enregistre.
Nous n'avons besoin ici que d'un indicateur "grossier", disant s'il y a ou non
dans RichTextBox quelque chose enregistrer. Nous pouvons assigner f aise

450

cinquime partie : Programmer pour Windows avec Visual Studio

if

(str0utput !=

nu11)

I0. Streaml'lriter stri'ltr =


new Systern. I0. Streaml,lriter (strOutput)
st r!{tr . I/Jrite ( richTextBoxl . Rtf) ;
strl,Jtr. Close 0 ;
Systeur.

bReturnValue = true:
l
]

return bReturnValue;
l
Cette fonction suit exactement le mme chemin que la mthode
OpenAndReadFlle O que nous avons vue plus haut. Pour commencer, elle
ouvre la bolte de dialogu SaveFileDiaiog, et attend qu'elle lui retourne
OK. Le programme essaie alors d'ouvrir le fichier. Si I'opration aboutit, le
programme crit dans le fichier tout le contenu de la proprit Rtf de la
zone de texte R.ichTexttsox.
Nous avons ajout le composant Sa-,'eFiieDialog SirnpleEditor: en le
faisant glisser depuis la Bolte outils. Aucun ajustement n'a t ncessaire.

ll,lettre Lre et Ecrire dans une bote, a(/ec un


t
menu par-^essas
Les mthodes OpenndP,eacll'j 1e O et Sar,'eSpecif iedFile O sont bien
jolies, mais totalement inutiles tant qu'elles ne sont pas lies chacune
une option de menu.

Pour implmenter les options de menu Fichier/Ouvrir et Fichier/Enregistrer, suivez ces tapes :

iedFile

l.

Entrez les mthodes CpeirAndReaolile O et SarreSpecif


dcrites dans les sections prcdentes.

Dans le Concepteur de formulaires, slectionnez I'option de

()

menu Fichier/Enregistrer.
3.

Dans la fentre Proprits, slectionnez l'vnement Click, puis


entrez le nom de fonction FileSave, comme le montre la Figure 18.6.

448

Ginquime partie : Programmer pour Windows avec Visual Studio

Lre un fichier RTF


0penFileliaiog

est tonnamment facile utiliser. Lzr


mthode Srro"i)r aiog i ) ouvre la bolte de dialogue. SinpieEoir(Jr n'a rien
faire pendant que I'utilisateur fait clfiler le contenu de cette bote de
dialogue la recherche du fichier ouvrir. Une fois qu'il a termin, il
clique sur OK (ou sur Annuler s'il n'a rien trouv). C'est seulement alors
que le contrle est restitu la fonction appelante. La valeur retourne
par Shor.,,Dia-Lcg r. ) est Dialc.,gF.esu1t.0K si I'utilisateur a cliqu sur le
bouton OK. S'il a cliqu sur autre chose, a ne nous intresse pas.
La bolte de dialogue

La mthode r,rpenF; ie i) retourne soit un iir. S,trearn valide qui permet de


lire le fichier, soit un ruli si le fichier spcifi ne peut pas tre lu pour
une raison ou pour une autre. Ces deux mthodes sont combines dans la

fonction 0leir-n:FeaciFi 1e O suivante

ll |it

dans 1a RichTextBox

le fichier spcifi par 1'utilisateur

(retourne true si 1a RichTextBox est rnodifie)


private bool 0penAndReadFile o
I

bool bReturnValue

false:

try
{

I I lit le non de fichi.er entr par 1'utilisateur


if (openFileDialogl . ShowDialog ( ) == DialogResult.0K)
{

owre le fichier

System,I0.Stream

if (strlnput

!=

strlnput

openFileDialogl.0penFile0

nu11)

I si I'ouverture du fichier
// un lecteur de flux

a russi. 1ui associe

System. I0.StreamReader

strRdr

(strlnput)

new System. I0. StreamReader


I

I 7it tout

string

1e contenu du fichier

sContents = strRdr.ReadoEnd0

richTextBoxi . Rtf = sContents

/l nous avons modifi la fentre de texte


bReturnValue = true;
// assurons-nous de fermer 1e fichier pour que d'autres
/l

puissent 1e lire
strRdr. Close O ;
l
l
]

catch (Exception e)

446

Cinquime partie: Programmer pour Windows avec Visual Studio

4.

Gnrez le programme et excutez-le.

5.

Entrez du texte et slectionnez-le.

6.

Entrez une taille de police entre 8 et 24.


La taille du texte slectionn change, et I'index de la barre se
dplace la position correspondante.

Enregistrer le texte de l'utlisateur


Sans la possibilit de lire et d'crire des fichiers,

SiinpleEditor ne serait

rien de plus qu'un jouet.

Lire le nom du fichier


Pour lire un fichier, il faut savoir lequel il faut lire. Pour cela, C# fournit
une bolte de dialogue spciale. nomme 0penFileDiaiog. Tt ou tard,
I'utilisateur voudra enregistrer sur le disque le texte qu'il vient de saisir et
de mettre en forme. Vous avez devin:vous avez besoin pour cela de
0penFileDiaiog. Associez ces deux botes de dialogue les fonctions de
lecture et d'criture de fichier que nous avons vues au Chapitre 16, et
vous avez un diteur complet.

Ajouter une bote de dialogue OpenFileDiaiog est un jeu d'enfant:

l.

Dans le Concepteur de formulaires, faites glisser un composant


OpenFileDialog de la Bote outils jusqu' la zone qui se trouve
au-dessous de la fentre d'dition 0a zone dans laquelle il y a

dj MainMenul).

2.

Faites la mme chose avec un composant SaveFileDialog.


Le rsultat doit ressembler la Figure 18.5.
Le composant OpenFileli:-rioe ne contient qu'une seule proprit
statique vraiment intressante. Quand on utilise cette bolte de
dialogue pour ouvrir un fichier, il y apparalt gnralement dans
une liste cles fichiers de divers types. Par exemple, le Bloc-notes

commence par rechercher les fichiers *.txt, alors que Word


commence par rechercher les fichiers *.doc. C'est ce qu'on

444

Cinquime partie : Programmer pour Windows avec Visual Studio

Figure 18.4:
Grce au lien
tabli entre

-CES dEUX

ri: i'elr,ri,:,lerriann"

composants,
la valeur qui
apparat dans
la

,* ;"i
::r:rer

CgftineS paftieS

$ fA nd iS
l'irrdi{,lu

lr,t,:,1.:

er, t'-,,',rnt

E,;r

lextBrx

est mise

jour en
fonction de

la

position de
l'index dans la

sllle rle Pollce I

lE

TrackBar.

Changer de

taille en utilisant la TextBox

L'utilisateur peut aussi entrer directement une taille de police dans la


zone de texte Taille de police (sinon, elle ne servirait pas grand-chose).
Cette fonction doit donc fonctionner dans la direction oppose la
fonction FontSizeControl O, mais elle est galement un peu plus complique car elle doit prendre en compte les erreurs de saisie ventuelles de
I'utilisateur. Mais au bout du compte, FontSizeEntered O doit excuter la
mme opration : lire la nouvelle valeur, modifier la taille de police, et
ajuster la position de I'index de la barre en consquence.

l.

Slectionnez I'objet TextBox de la taille de police.

2.

Dans la fentre Proprits, slectionnez l'vnement


TextChanged, et entrez le nom de fonction FontSizeEntered.
C'est cette mthode qui sera invoque par C# lorsque I'utilisateur
entrera une nouvelle valeur dans la zone de texte Taille de police.

3.

Dans le code source, implmentez la nouvelle fonction comme suit

/l invoque quand 1'utilisateur tape quelque chose dans la


/i utilis pour dfinir la taille de la police

TextBox

private void FontSizeEntered(object sender, System.EventArgs


{

I t:-t

1e contenu de 1a TextBox

string sext = text8oxi.ext;

e)

442

Ginquime partie : Programmer pour Windows avec Visual Studio

il

^--- uOflstructor
^
rvuv. nuu
tr// mnnn.
^lt arry

//

COde

la police 1e style et

donne

tiet.E-ont t I

after InitializeComponent call


l^

+a1 | lo

nr

.lf ,Ut

6.

Gnrez le programme et excutez-le.

7.

Entrez du texte, et slectionnez-le avec la souris.

8.

Slectionnez Format/Gras et Format/Italique dans un ordre

quelconque.
La Figure 18.3 montre du texte mis en gras et en italique dans la fentre de
-qirnpleEdrtor" Remarquez aussi que les options actives I'endroit o se
trouve le curseur sont prcdes d'une coche dans le menu, qui vous permet
donc de savoir guelle sera la mise en forme du texte que vous allez taper
partir de l. C'est le rsultat de notre dfinition de la proprit Checked.

Wi":
Firhier dilim

-lal-xl
FBrmt

Figure 18.3:

SinpleEditor
permet
maintenant
de saisir du
texte en gras

Taille de

et en
ita liq

policr

l--

e.

Chosir la taille de police


Le changement de la taille de police utilise la mme fonction SetFont O,
mais avec une petite complication, car elle peut tre dfinie par deux

composants diffrents.

Changer de

taille en utilsant la TrackBar

C'est une opration qui se fait assez directement

440

Cinquime partie : Programmer pour Windows avec Visual Studio

if

fr l:

FontStyle.Bold;

{isTtalics)

fs t-| = FontStyle. Italic

Font font = nelr Font(richextBoxl.Font.FontFamily, fontSize, fs);


richTextBoxl. SelectionFont = font
;

La base de cette fonction est le constructeur Font ( ). Il en existe de nombreuses versions, mais celle-ci admet les arguments qui nous intressent

la police courante, la nouvelle taille, et la nouvelle police. FontStyle


rassemble sous forme de bits des proprits comme gras, italique, barr et
soulign. Commencez par FontStyle. Regular et ajoutez celles que vous
voulez de ces proprits en utilisant I'oprateur C# OR ( I l). Les deux
indicateurs isBolded et isltalics stockent les informations disant si le
texte est ou non en gras ou en italique.
Le premier argument du constructeur spcifie la police courante Qa possibilit
de changer de police ne fait pas partie des fonctionnalits que nous avons
retenues pour SimpleEditor). La commande richTextBoxl . Font retourne
une description de la police courante. La proprit FontFamily retourne le
type de police (par exemple, "Arial" ou "Times New Roman"). Le constructeur

cre donc un nouvel objet Font, avec la mme police mais dans une nouvelle
taille, et dont les attributs gras et italiques ont ventuellement t changs.

La dernire assignation modifie la police du texte slectionn ou du texte


qui sera tap partir du point o se trouve le curseur.

.j99q

SZ' .-\

,-

v-/

,,
L'expression richTextBoxl . Font f ont; modifie la police de tout le
texte qui se trouve dans la zone de texte.

lnplmenter

les optons du menu Format

Les tapes ci-dessous implmentent les options du menu Format

l. Dans les menus, slectionnez Format/Gras.


2. Dans la fentre Proprits, slectionnez l'vnement

Click, et

entrez le nom FormatBold.

3.

Rptez le mme processus pour I'option de menu Format/

Italique, en utilisant le nom Formatltalics.

438

Ginquime partie : Programmer pour Windows avec Visual Studio

//tl efface ce qui est dj 1


richTextBoxl.SelectedRtf = ""

private void EditCopy(object sender, System.EventArgs

e)

strins rtfText = richTextBoxl.SelectedRtfl


14lr

j.teC1 ipboard

rtfText)

private void EditPaste(object sender, System.EventArgs

e)

string s = ReadClipboardO;

if (r

l= null)

richTextBoxl. SelectedRtf = s;

l
]

La proprit SelecteoRtf contient chaque instant le texte qui est


slectionn. La mthode EditCopi.O passe cette proprit
l',;ri t-er-llipboard (). La mthode EditCrit () fait la mme chose, mais
en supprimant le texte slectionn en assignant une chalne vide
cette proprit. La mthode EditPaste O lit dans le Presse-papiers
une chalne RTF, par laquelle il remplace le texte slectionn (ou
insre cette chalne I'endroit o se trouve le curseur s'il n'y a pas de
texte slectionn).

Double-cliquer sur le nom d'une proprit dans la fentre Proprits vous conduit directement la fonction correspondante. Cette
astuce peut faire gagner pas mal de temps.

8.

Gnrez nouveau SimpleEditor. Vous avez maintenant un


programme qui peut vraiment couper, copier et coller.

9.

Excutez le programme en slectionnant Dboguer/Dmarrer.

10.
I

l.

Tapez quelques lignes de texte dans la fentre d'dition.


Slectionnez une portion de texte, slectionnez dition/Couper
(ou appuyez sur Ctrl+X), placez le curseur I'endroit que vous
voulez, slectionnez dition/Cotler (ou appuyez sur Ctrl+\), et
voil ! Le texte a t dplac.

Plus impressionnant encore, SimpleEditor peut changer du texte par Couper


et Coller avec d'autres applications, par exemple Word. La Figure 18.2 montre
une portion de texte coupe dans un document Word et colle dans

436

Cinquime partie : Programmer pour Windows avec Visual Studio

La fonction suivante stocke dans le Presse-papiers une chalne de texte


identifie comme de type RTF (Rich Text Format) :
-\
private void l'lriteClipboard(string rtfText)
{

Data0bject data = new Data0bject0;


data . SetData (DataFormat s . Rtf , rtfText ) ;
Clipboard. SetData0bject (data, true)

La mthode tJriteClrpboard ( ) accepte un argument strlng pour le


copier dans le Presse-papiers. Elle commence par crer un objet
DataOb j ect () , dans lequel elle stocke la chalne et I'indication que le texte
est en fait une srie de commandes RTF, et non un objet de type feuille de
calcul ou base de donnes. Laclasse DataForrnats n'est en fait rien de
plus qu'un ensemble de descripteurs de diffrents formats de donnes,
DataForrnats , Rtf tant celui qui nous intresse ici. La mthode
SetDataOb j ec'. O copie la chane RTF dans le Presse-papiers.
La lecture des donnes dans le Presse-papiers est le mme processus en
sens inverse, mais il vous faut y ajouter quelques tests pour garantir que
la requte de lecture sera ignore si la donne contenue dans le Pressepapiers n'est pas de type chalne :
private string Readtlipboard0
{

ii

rcupre

le

contenu du Presse-papiers

Tn-+^nL.i^^+
I
= Clipboard.GetDataObject0
r,eLqvvJ.",
ut

if

(data == nu11)

return nul1;
]

ll we fois 1es donnes


I au format RTF

rcupres,

vrifie qu'el1es

nh'iont n = dat.GetData(DataFOrmatS,Rtf , trUe)


evsug\vq

if

sruv/

(o == nul1)

return

nu11;

// nous avons quelque chose, mais assurons-nous


I I que c'est bien une chaine
if ( (o is string) == false)
L"t

return nul1;
l

rurvv/

t;

sont

43 4

Cinquime partie : Programmer pour Windows avec Visual Studio

formelle, ces accessoires sont appels des composonrt. il suffit de choisir un


composant dans la Bote outils, et de le faire glisser pour le dposer sur le
formulaire. Vous pouvez ensuite le personnaliser en ajustant toutes les pro
prits que vous voulez dans la fentre Proprits, judicieusement nomme.
La fentre Proprits liste deux types fondamentalement diffrents de
proprits. Le premier de ces ensembles, que j'appelle les proprits statiques, comporte la police, la forme, la couleur d'arrire"plan, et le texte initial.
Ce sont galement des proprits du point de vue du langage C#. @our en
savoir plus sur la structure Proprit de C#, reportez-vous au Chapitre 11.)

La fentre Proprits contient aussi un ensemble de proprits compltement diffrent, qui correspondent plutt aux mthodes de C#. Je les

appelle propriets octiues.

1c${Qa. Les proprits actives correspondent en fait ce que I'on appelle un


%H\ dtgu. Un dlgu est une rfrence un couple objet/mthode. Dans ce
=\\ff / cas, I'objet est le composant slectionn, et la mthode est la "proprit"
\/
de la liste des proprits actives.

^tK

'(dg,

Les proprits dynamiques sont plus communment appeles des vnements. La mthode qui est invoque lorsque l'vnement se produit
s'appelle un gesfionnoire d'unemenl. Mais je ne veux pas introduire de
complications inutiles.
Les proprits actives d'un objet sont les mthodes invoques par C#
lorsque certaines circonstances particulires se produisent. Par exemple,
la proprit Button. Click est invoque lorsque que I'utilisateur clique
sur un bouton. Mais ces proprits actives offrent un contrle bien plus
prcis que cela. Par exemple, si vous voulez diffrencier le fait d'enfoncer
un bouton ou de le relcher, vous avez une proprit diffrente pour
chacune de ces deux actions. Une proprit active est dclenche quand
le pointeur se trouve sur un bouton, que I'utilisateur clique ou non, et
c'est une autre proprit qui passe I'action lorsque la souris va ailleurs
(c'est gnralement ce qui est utilis pour changer la couleur d'un bouton
quand le pointeur passe dessus).
Pour accder aux proprits actives, slectionnez le composant, et cliquez sur
le bouton contenant un clair en haut de la fentre Proprits. La Figure 18.1
montre une partie des proprits actives d'un composant TextBox.

Afin que le programme SinipleEditor fasse ce que I'on attend de lui, vous
devez dfinir une ou plusieurs proprits actives pour chacun de ses
composants. SimpleEdi tor paralt soudain moins simple.

430

Ginquime partie : Programmer pour Windows avec Visual Studio

this . nenutem5
this . nenultemg
this . nenulteml2 ] ) ;
,
,

ll
II

trackBa

Ittl

this. trackBarl . Anchor =

( (System. Windows,

System. Windows . I'orms . AnchorStyles


I

Left

Forns.AnchorStyles.lottom

h'

System,I,lindorrs.Forns.AnchorStyles.Right) ;
this.trackBarl.Location = ner+ System.Drar+ing.Point(40, 248)
^

this.trackBarl.MaximrLn = 24;
this . trackBari .Minimum = 8;
this.trackBarl.Nane = "trackBarL" ;
this.trackBarl.Size = new Systern.Drauing.Size(208,
this . trackBarl . TabIn dex = 2;
this . trackBarl . Value = 12 ;

42) ;

l
/lendregion
]
I

J'ai supprim toutes les sections qui ne concernent pas le menu principal
(MainMenu), I'une des options du Menu, et la TrackBar. Chacun de ces
objets est un membre donne de la classe Form1. Le Concepteur de
formulaires cre les noms de ces membres donne en concatnant le type
de I'obiet avec un numro.
pu vous faire dfinir des noms dans la fentre Proprits pour
^s$c ^.. J'aurais
quelque
obtenir
chose de plus parlant, mais c'tait sans importance. Pour
7X
programmes
de grancle taille, la dfinition de vos propres noms peut
des
ttg||
V
rendre le code qui en rsulte beaucoup plus facile lire.
La mthode

Initial.i

zoe nmnnnt I

) Commence par Crer un objet de

chaque type.

.*a/
ft?ll
\SZt
llE!l

Ne vous tonnez pas du fait que le Concepteur cle formulaires donne le

nom complet de haque clasie, y compris son espace de nom


(Systen.I,^/indows.Forrns).
Dans I'une des sections suivantes du programme,
InitializeCoinponent O assigne chacun de ces objets les proprits
que vous avez dfinies dans la fentre Proprits.

428

Cinquime partie: Programmer pour Windows avec Visual Studio

Si vous gnrez nouveau SinpleEditor, le redimensionnement


fonctionne cornme vous pouvez I'attendre, comme le montrent le
"petit SirnpleEditor" de la Figure lT.Il et le "grand SimpleEditor"
de la Figure 17.12.

Tabfeau 17.2: Type d'ancrage pour chaque composant.


Composant

Ancrage

RichTextBox

Haut, bas, gauche, droite

Zone de texte "Taille de police'

Bas

tiquette "Taille de police"

Bas

Tra ckBa r

Bas, gauche, droite

tiquette de I'extrmit gauche de la TrackBar

Bas, gauche

tiquette de I'extrmit droite de la TrackBar

Bas, droite

Figure 17.10:
Cliquez sur les
bras de la

fentre
d ancrage
p0ur

slectionner
(gris fonc) ou
dslectionner

(blancl
I

ancrage

dans chaque

direction.

Figure 17.11
Le oetit

SinpleEdit,:r.

Taille de

police

[-

426

Cinquime partie : Programmer pour Windows avec Visual Studio

Redinensonner le fornulaire
Les utilisateurs aiment que les fentres soient redimensionnables. Pour
tre plus prcis, les utilisateurs peuvent avoir envie de redimensionner le
formulaire de SimpleEditor. Par dfaut, un formulaire est
redimensionnable, mais les objets qu'il contient ne le sont gnralement
pas. C'est une chose laquelle vous pouvez remdier, mais la solution n'est

pas triviale.
Si vous voulez faire simple, ne permettez pas aux

utilisateurs de

redimensionner le formulaire. Ce n'est pas forcment vident, mais le


redimensionnement est une fonction du cadre. L'assignation de la valeur
Fixed3D la proprit FormBorderStyle met le formulaire hors d'atteinte
du zle des souris.

l.

Slectionnez le formulaire. Identifrez la proprit FornBorderSty'1e.


S izable. Cliquez sur cette proprit pour
faire apparatre une liste droulante des valeurs possibles. Slectionnez Fixed3D pour interdire le redimensionnement.
Sa valeur par dfaut est

P\

g/

e,

La suite de cette section, consacre la manire de rendre

SimpleEditor redimensionnable, peut tre considre comme


appartenant au domaine technique. Vous pouvez I'ignorer et
continuer avancer. Vous y reviendrez quand vous voudrez.
Pour rendre un formulaire redimensionnable, la difficult est de dire
aux composants qu'il contient comment ils doivent rpondre. Par
dfaut, la plupart des composants ne sont pas redimensionnables.
Si vous redimensionnez le formulaire, ils resteront l o ils taient,
comme le montre la Figure 17.9.
Si SimpleEditor doit tre redimensionnable, ses composants
doivent savoir quoi faire. Par exemple, si le cadre est agrandi, la
TrackBar doit se dplacer vers la droite, tout en suivant le bord
infrieur du formulaire. Autrement dit, la TrackBar est ancre en
bas du formulaire. Si le formulaire est allong verticalement, la
TrackBar reste sur le bord infrieur.
En plus, la TrackBar doit s'tirer horizontalement entre le bord
gauche et le bord droit du formulaire. Cet effet est produit par son
ancrage sur les bords droit et gauche. Quelle que soit la largeur
donne au formulaire, la TrackBar ne doit jamais dpasser du
formulaire, par la droite ou par la gauche.

424

Ginquime partie : Programmer pour Windows avec Visual Studio

Encore un dtail : il faut centrer horizontalement la zone de texte


dans le formulaire. Vous pouvez le faire visuellement, mais il y a
une meilleure solution.
J.

Slectionnez la zone de texte, et cliquez sur I'outil Centrer horizontalement dans la barre d'outils Disposition.
La zone de texte est centre horizontalement dans le formulaire.

6.

Venons"en maintenant notre TrackBar. Slectionnez le composant TrackBar dans la Bote outils, et placez-Ie tout en bas du

formulaire

imp 1 eEci

it o r.

La taille verticale d'un composant TrackBar est fixe, mais vous


pouvez l'tirer horizontalement pour lui donner la longueur que
vous trouverez raisonnable. Encore une fois, "raisonnable" est une
question de prfrence personnelle, et vous pourrez modifier cette
longueur lorsque vous aurez vu ce qu'elle donne en pratique.
Une TrackBar possde plusieurs proprits de comportement

intressantes. En fonctionnement, SimpleEditor aura besoin de


demander la TrackBar la valeur qu'elle contient. Ce qui soulve la
question : " quelle valeur correspondent la position la plus
droite et la position la plus gauche de I'index ?" Ces deux valeurs
sont dfinies respectivement par les proprits Mininum et l,laxinun
de TrackBar. Nous avons dit que la taille de la police peut aller de 8
24 points.

t. Slectionnez la TrackBar (si elle ne I'est pas dj). Assignez Ia


valeur 8 la proprit Minlrnum, t la valeur 24 la proprit
Maxirnun. Assignez la valeur l2 la proprit Value.
8.

Centrez la TrackBar : slectionnez-la, et cliquez sur le bouton


Centrer horizontalement dans la bane d'outils Disposition.

Encore un coup de penture et nous tl sommes


Les composants dont nous avions besoin pour la taille de la police sont
en place et prts fonctionner, mais ils pourraient tre plus sympathiques. Vous et moi, nous savons quoi ils servent, mais personne d'autre
ne va le deviner. SimpleEditor a besoin de quelques tiquettes pour
indiquer quoi servent les diffrents champs.

422

cinquime partie: Programmer pour Windows avec Visual Studio

Tableau 17.1 : Raccourcis clavier des lments de menu.


lment de menu

Raccourci

Fichier/0uvrir

Ctrl0

Fichier/Enregistrer CtrlS

Fichier/0uitter

Ctrl0

dition/Couper

CtrlX

Copier

CtrlC

ditioni

dition/Coller
Format/G

ras

CtrlV
CtrlB

Format/ltalique

Ctrll

?F1

Ajouter

les contrles d'ajustement de la yrolce

SimpleEditor doit aussi tre capable de modifier la police, dans certaines limites arbitraires. Dans cette section, vous allez ajouter une zone de
texte dans laquelle I'utilisateur pourra taper la taille qu'il veut donner la
police. SirnpleEditor disposera aussi d'un contrle analogue, que I'on

.ffi

appelle TrackBar, dans lequel I'utilisateur peut faire glisser un index


d'une extrmit I'autre pour augmenter ou diminuer la taille de la police.

SimpleEditor plus facile utiliser, cette fonctionnalit permet aussi de montrer comment relier deux contrles.
En dehors de rendre

l.

Ouwez la Bote outils, et faites glisser un contrle TextBox en bas


de la fentre SirnpleEditor. Comme la taille par dfaut est un peu
grande pour deux chiffres, vous pouvez la rduire horizontalement.
Il y a beaucoup de choses dans la proprit Font (police) : la police
elle-mme et sa taille, ainsi que des proprits comme Bold (gras),
Italic (italique), et Str,keout (barr). C'est pour cette raison
qu'il y a un petit signe plus gauche de la proprit Font. Cliquez
sur ce signe plus, et vous voyez apparaltre toutes les proprits
que contient Fonr, comme le montre la Figure 17.7. (Et le signe plus
devient un signe moins - si vous cliquez sur le signe moins, vous ne
voyez plus que la proprit Font, comme avant.)

420

Ginquime partie : Programmer pour Windows avec Visual Studio

2.

Suivez les instructions simples qui appamissent dans le Concepteur


cliquez sur les mots Tapez ici, puis tapez Ie nom de votre premier
lment de menu : Fichier.
Le Concepteur rpond en affichant un autre cadre Tapre z icl audessous, et encore un autre droite du premier, comme le montre
la Figure 17.5. C'est tellement excitant que je ne sais pas par lequel

commencer.
/,
lirhier Edihon

Affihqe

eriet enrr qboguer Dgnnet qii5 Fettre

iffi
' Forml,ci [Derig"]"

tr-

eLei1

'

Uelp

'-$."1'l

.-s

|'

bite

^l
-.l

out'l.j

oonnees
connosent

Figure 17.5
Le composa nt

I
I

Potnte'rt

t"t'"t
I A
I A tir*taua
I J. *uon
t=lstr rex(uor

ll(1a111lqL1U

VOUS

propose de
taper I option
du menu

:J

I
i di

l'4nMeru

r".t**..

l'

Rdio&tton
6roupBo.

43 PEtureBo!
i.-l tun"t

principal et
celle du

oatecrid

Fres-ppier5

qtrcul

Gnrl

s0us-menu.

Wind6$rs Forft,s

*eoil"u*t,tr[d-T

3.

Cliquez dans le cadre Tape z ici au-dessous de Fichier, et entrez


les trois options du menu Fichier : Ouvrir, Enregistrer, et Quitter.

4.

Cela fait, cliquez dans le cadre Tape z

D.

Dplacez-vous nouveau d'un cran vers la droite, et ajoutez


Format et ses options : Gras et ltalique.

6.

Enfin, ajoutez ? Q'aide) la barre de menus.

ici droite du menu


: Couper, Copier, et Coller.
options
Fichier, et entrez dition et ses

Remarquezla nouvelle zone ouverte par le Concepteur au-dessous


de la zone de dessins avec la cration de votre premier menu
principal. Vous pouvez cliquer sur I'objet rnainMenul qui y apparalt
pour dfinir les proprits d'ensemble de ces menus. Vous pouvez
aussi utiliser cette zone pour y placer les objets qui ne sont pas
directement visibles (par exemple, une bote de dialogue qui
apparat seulement dans certaines circonstances).

4|8

Ginquime partie : Programmer pour Windows avec Visual Studio

vous-mme. Tous ces sujets sont fort intressants, mais, comme


vous vous en doutez. sortent du cadre de ce livre.
En faisant dfiler vers le haut et vers le bas le contenu de la Bolte

outils, vous dcouvrez une plthore de composants. On y trouve des


tiquettes, des boutons, des zones de texte, des menus, et une quantit d'autres objets graphiques. Il y en a strement un qui est ce dont
nous avons besoin pour la fentre d'dition. On pourrait penser que le
composant TextBox st ce qu'il nous faut, mais une zone de texte est
plutt adapte la saisie d'un texte simple (en gnral, une ligne). Par
exemple, vous utiliserez une zone de texte pour permettre I'utilisateur d'entrer un simple nombre entier pour la taille de police.
En fait, le meilleur choix pour la fentre d'dition est le composant
RichTextBox. Celui-ci permet de saisir et d'afficher du texte dans le
format nomm RTF (Rich Text Format). Un fichier RTF est semblable un fichier au format Microsoft Word (.DOC), cette diffrence
que RTF est plutt un standard. Ce format a toutes les proprits
dont nous avons besoin : italique, gras, diffrentes tailles de police,
et il est pris en compte par la plupart des traitements de texte pour
Windows, dont Word, et d'autres traitements de texte crits pour
d'autres systmes d'exploitation, par exemple pour Unix.

Afin de crer la fentre d'dition, cliquez sur le symbole


Rj-chTextBox dans la Bote outils. Placez le pointeur dans le
coin suprieur gauche du formulaire Simple Editor, puis maintenez enfonc le bouton gauche de la souris pour le faire glisser
vers le bas et vers la droite, crant ainsi une zone d'dition
comme celle que montre la Figure 17.4.

trop de la taille et de I'emplacement exact


de la zone R.ichTextBox. Vous pourrez toujours Ia dplacer et la
redimensionner autant que ncessaire.
Ne vous proccupez pas

3.

Je n'aime pas beaucoup le texte initial de richTextBoxl. Pour le


modifier, ouvrez la fentre Proprits, et remplacez le contenu
de la proprit Text par rien. Autrement dit, effacez ce qui s'y
trouve pour laisser ce champ vierge.
Le texte disparat de la zone RichTextBox.

La mme proprit peut tre interprte de faon diffrente par


deux composants diffrents. La proprit Text en est le meilleur
exemple. Pour un formulaire, c'est l'tiquette qui se trouve dans la
barre de titre. Pour une zone de texte (TextBox ou RrchTextBox),

vous voici confront un micro-ordinateur - prus par ncessit que par


got, avouons-le -, sans savoir par quel bout prendre cet instrument

barbare et capricieux. oubliez toute apprhension, cette nouvelle


collection est rellement faite oour vous
!

Grce ce livre, vous allez rapidement crire vos premires applications


en c#, sans pour autant devenir un gourou de la programmation. c#, c'est
le nouveau langage de programmation dvelopp par Microsoft, et qui se

prsente comme la pierre angulaire de la solution .NET du gant du


logiciel. Rassurez-vous, on ne vous assommera pas avec toutes res
subtilits du langage, mais vous possderez les bases ncessaires oour
utiliser la panoplie d'outils du parfait programmeur C#.

Au vu de ce symbole,

si

vous tes allergique la


technique, passez votre
chemin.

b'

-'ii
21,90

llungryMinds-

First

(143,6s

F)

Lfr
Cette icne signale une

manipulation qui va vous


simplifier la vie.

(n
FU

retenir ceci.

nJ
2

r)1, t.J ):l

Retrouvez First Interactive sur Internet

www.efirst.com

f-\

Dsol, il faut quand ntcm,

65 3303 B
tsBN-2-84427-2s9

"'i

n
,,j
p-(
J

,llilIxilrililill[|iltlll

tJ

Finst

Vous aimerez peut-être aussi