Académique Documents
Professionnel Documents
Culture Documents
C# Pour Les Nuls
C# Pour Les Nuls
F-rst
Y,
ii/;'''!i:
;,, ::.
C#
Third Avenue
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
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
2r
22 22 23 24 25 26 26 27
Deurime parte
3l
Dclarer une variable .......... Qu'est-ce qq'un int ? ........ Les rgles de dclaration de variable............. Variations sur un thme : des int de diffrents types
..........32
.............. 33
...34
.....' 35
ut
G#
Reprsenter des fractions Utiliser des variables en virgule flottante Dclarer une variable virgule flottante Convertissons encore quelques tempratures ....... Quelques limitations des variables en virgule flottante Utiliser le type decimal, hybride d'entier et de virgule flottante Dclarer une variable de type decimal ......... Comparer les types decimal, int, et float Soyons logique, examinons le type bool Un coup d'il aux types caractre La variable de type char Types char spciaux Le type string Comparer string et char Qu'est-ce qu'un type valeur ?......... Dclarer des constantes numriques
Changer de type : le cast
37 38
38
40 40 42 43
44 44
45 45 46 46
47 49
50
5l
53
53 54 55 56 57 59 ?......... 60
61
L'oprateur d'assignation et ses variantes L'oprateur d'incrmentation Faire des comparaisons - est-ce logique ? .............. Comparer des nombres en virgule flottante : qui a le plus gros float Encore plus fort : les oprateurs logiques Trouver les mes surs : accorder les types d'expression Calculer le type d'une opration Assigner un type L'oprateur ternaire, le redoutable
63 63 65 66
Instructions if imbriques
Les commandes de boucle
74 75 76 79
Commenons par la boucle de base, while Et maintenant, do... while Briser une boucle, c'est facile Faire des boucles jusqu' ce qu'on y arrive Les rgles de porte des variables Comprendre Ia boucle la plus utilise : for ........
80 84
85 86 90
91
sommaire Ul
Un exemple de boucle for ..........
9l
92 93 97
100
Pourquoi auriez-vous besoin d'une autre boucle ?.............. Des boucles imbriques ....... L'instruction de contrle switch Le modeste goto
Troisine parte
: Programmaton et obiets...............................
l0l
104
r07
111 Pouvez-vous me cionner des rfrences ?.............. Les classes qui contiennent des classes sont les plus heureuses du monde.. 113 115 Les membres statiques d'une classe 116 Dfinir des membres de type const 116 Les tableaux : la classe Array
Les arguments du tableau Le tableau longueur fixe Le tableau longueur variable .......... Des tableaux d'objets ..........
116
tt7
r20 r24
r27
Une structure de contrle de flux pour tous les tableaux : foreach Trier un tableau d'objets
r28
r37
t46
r47
t49
l5l
157
r57
158 158 160
164
165
r68
t70
Ul
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 ...........
195
200
20r
202 202 204 208
209 210
212 215
2r7 2r7
22r
223
224
Sommaire
IX
Chapitre
I : Rendre
239
239
Restreindre I'accs des membres de classe Un exemple public de public BankAccount ......... Allons plus loin : les autres niveaux de scurit ............ Pourquoi se proccuper du contrle d'accs ? Des mthodes pour accder des objets Le contrle d'accs vole votre secours , Et alors ? ...,.......... Dfinir des proprits de classe Donner un bon dpart vos objets : les constructeurs Le constructeur fourni par C# Le constructeur par dfaut .............. Construisons quelque chose Excuter le constructeur partir du dbogueur Initialiser un objet directement : le constructeur par dfaut Voyons comment se fait la construction avec des initialisations Surcharger le constructeur viter les duplications entre les constructeurs tre avare de ses obiets
240 243
244
;; ";;;,pi; :
245
:
246
250 250
252
258
261
262
28r
282 282 283 284 286 286
..
Autres considrations
Changer de classe
Des casts invalides I'excution..........
viter les conversions invalides en utilisant Ie mot-cl is L'hritage et le constructeur Invoquer le constructeur par dfaut de la classe de base Passer des arguments au constructeur de la classe de base : le mot-cl base La classe BankAccount modifie .........
288
29r
293
295
...............296
Surcharger une mthode hrite Ce n'est qu'une question de surcharge de fonction classe cliffrente, mthode cliffrente Redfinir une mthode d'une classe de base
.......296
...........297
.298
G#
Revenir la base
Le polyrnorphisme
303
305
Qu'y a-t-il de mal utiliser chaque fois le type dclar? Accder par le polymorphisme une mthode redfinie en utilisant is Dclarer une mthode comme virtuelle La priode abstraite de C# Le factoring entre classes Il ne me reste qu'un concept : la classe abstraite Comment utiliser une classe abstraite ? .......... Crer un obiet d'une classe abstraite : non ! Redrnarrer une hirarchie de classes
Sceller une classe
306
.... 308
309
311 311
3r7
318 320
32r
325
Chapitre l4 : Quand une classe n'est pas une classe : I'interface et la structure........... .............327
327 Qu'est-ce que PEUT_ETRE_UTILISE_COMME ? 329 Qu'est-ce qu'une interface ? ............. Pourriez-vous me donner un exemple simple ?........... 330 Ptris-je voir un programme qui PEUT-TRE- UTILIS-COMME un exemple ? ...... 332 Crer votre interface "faites-le vous-mme" 332 Interfaces prdfinies 334 Assembler le tout 336 Hritage et interface ............ 342 Rencontrer une interface abstraite 342 Une structure n'a pas de classe 345 La structure C# 346 Le constructeur de structure.......... 348 Les mthodes d'une structure sont ruses............ 349 Mettre une structure l'preuve par I'exemple .......... 350 Rconcilier la valeur et la rfrence : unifier le systme de types 353 Les types structure prdfinis .............. 353 Comment le systrne de types est-il unifi par des structures communes ? 354 Un exemple ............
..............
........... 359
360
Traiter une erreur I'ancienne mode : la retourner ......... Retourner une indication d'erreur Je suis l pour signaler ce qui me parat ncessaire ............. Utiliser un rncanisme d'exceptions pour signaler les erreurs ............. Puis-je avoir un exemple ?.......... Crer votre propre classe d'exceptions .........
Assigner plusieurs blocs catch Laisser quelques envois vous filer entre les doigts Relancer un objet Iiedfinir une classe d'exceptions
362
365 367
368
37r
373 375
378
380
Somma ire
XI
C#.........
.......... 385
... 385
................ 387 ............... 388 ......... 388
Contrler I'accs aux classes avec les espaces de nom........ Rassembler des donnes dans des fichiers ............ Utiliser StreamWriter ........ Amliorez votre comprhension et votre vitesse de lecture avec
Diviser un mme programme en plusieurs fichiers source Runir des fichiers source dans un espace de nom Dclarer un espace de nom Accder des modules du mme espace de nom Utiliser un espace de nom avec le mot-cl using
........... 390
........ 391
.394
. 396
StreamReader
...... 402
.... 407
409
410 410
411
Quel est Ie problme ? Exposer le problme ............. Concevoir la prsentation Ma solution .......... Dessiner la solution ............. Crer le cadre de travail de I'application Windows Ignorez ce type qui se cache derrire le rideau diter la fentre d'dition Construire les menus ........... Ajouter les contrles d'ajustement de la police Encore un coup de peinture et nous y sommes ............. Redimensionner le formulaire ........... Qu'avons-nous fabriqu ? .............. Comment apprendre connaltre les composants ? Et maintenant ?
412 412
413
415
417
419
422
424 426
429 431
431
433
433
Un menu garanti pour diter le menu Edition Mettre hardiment en gras et en italique Changer de police et de taille Implmenter les options du menu Format Choisir la taille de police Changer de taille en utilisant la TrackBar Changer de taille en utilisant la TextBox......... Enregistrer le texte de I'utilisateur Lire le nom du fichier
435
439
439
440
442 442
Xl I
G#
.. 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
'className' ne contient pas de dfinition pour'memberName' ........... 462 ......... 464 Impossible de convertir implicitement le type 'x'en 'y' 'className.memberName'est inaccessible en raison de son niveau de protection .. 466 .... 467 Utilisation d'une variable locale non assigne 'n' Le fichier'programName.exe' ne peut pas tre copi dans le rpertoire ... 468 d'excution. Le processus ne peut pas.. Le mot-cl new est requis sur'subclassName. methodName', car il masque le membre hrit 'baseclassName.methodName' ............ ....... 469 'subclassName' : ne peut pas hriter de la classe scelle'baseclassName' ......... 470
'className'n'implmentepaslemembred'interface'methodName' .. 470
471
'methodName' : tous les chemins de code ne retournent pas ncessairement une valeur ) attendue
472
Chapitre 20 : Les dix plus importantes diffrences entre C# et C++ ....... 473
Pas de donnes ni de fonctions globales Tous les objets sont allous partir du tas Les variables de type pointeur ne sont pas autorises.............. Vendez-moi quelques-unes de vos proprits ............. Je n'inclurai plus jamais un fichier 474 474 475 475 476 477 478 478 478 479
Ne construisez pas, initialisez ....... Dfinis soigneusement tes types de variable, mon enfant Pas d'hritage multiple Prvoir une bonne interface Le systme des types unifis
Index
481
lntroduction
u fil des annes, les langages de programmation ont beaucoup volu. Dans les premiers temps, les langages taient malcommodes et les outils volumineux. Ecrire un programme qui fasse quoi que ce soit d'utile tait une chose difficile. Au fur et mesure des progrs de la technologie, des langages plus avancs apparaissaient sur le march. Il y eut donc, assez rapidement, le langage C, et par la suite Q++ (prononcer "C plus plus"). Les outils s'amlioraient aussi. Trs vite, il apparut des environnements de dveloppement intgr, avec des diteurs, des concepteurs, des dbogueurs et Dieu sait quoi d'autre, runis dans des ensembles faits pour vous accompagner du berceau la tombe. On pourrait croire que ces nouveaux outils avaient rendu la programmation plus facile, mais il n'en tait rien : les problmes n'en taient que plus compliqus. C'est juste au moment o je pensais que les programmeurs allaient enfin rattraper ce processus qu'est apparu le dveloppement pour le Web.
Avec I'avnement du Web, le monde s'est divis en deux camps : les adeptes des solutions bases sur le systme d'exploitation Windows, et "les autres". Au dbut, ce sont "les autres" qui prirent I'avantage. Leurs outils, bass sur le langage Java, permettaient d'crire des programmes distribus sur le Web.
C'est en juin 2000 que Microsoft a prsent sa rponse, sous la forme d'une famille de langages et d'outils appele .NET (prononcer "point net", ou "dot net" pour faire amricain), avec son emblmatique langage de
programmation C# (prononcer "C sharp", autrement dit "do dise"). Bientt peut-tre, on pourra programmer en si bmol majeur !
Les buveurs de Java en revendiquent la supriorit, mais les NETitiens ont aussi leurs arguments. Sans prendre part leur polmique, on peut dire qu'une bonne partie de la diffrence peut se rsumer en une phrase : Java vous dit qu'il vous suffit de tout rcrire en Java, et vous pourrez excuter
KIU
G#
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
Toutefois, au moment o j'cris ces lignes, il n'existe qu'un seul fournisseur qui propose un compilateur C# : Microsoft. En outre, Visual C# n'est propos que d'une seule manire : en tant qu'lment de la suite d'outils Visual Studio .NET. Aussi, pour vous dcrire C#, je ne pourrai viter de vous parler de Visual Studio, au moins jusqu' un certain point ; j'ai donc essay d'en maintenir l'vocation un minimum raisonnable. Je pourrais me contenter de vous dire : "Ouvrez votre programme de la manire qui vous plaira" ; mais je vous dirai plutt : "Lancez C# partir de Visual Studio en appuyant sur la touche F5." Je veux que vous puissiez-vous concentrer sur le langage C# sans avoir vous casser la tte sur des questions mineures.
Introduction
XU
D'un autre ct, je suis conscient du fait que beaucoup de lecteurs, sinon la
plupart d'entre eux, voudront utiliser C# dans le but d'crire cles applications pour Windows. Bien que ce ne soit pas un livre sur la programmation sous Windows en tant que telle, j'ai consacr une partie montrer comrnent C# et Visual Studio forment, ensemble, un puissant environnement de programmation pour Windows.
utilisateurs se serviront de C# afin de crer des applications distribues pour le Web ; mais comme on ne peut pas tout mettre dans ce livre, il me faut bien dfinir une limite quelque part. C# pour les lYuls ne s'attaque pas aux questions de .NET et de la programmation distribue.
Je sais aussi que certains
Hrlpothses qratutes
Avant de pouvoir commencer programmer en C#, il vous faut avoir install sur votre ordinateur un environnement de dveloppement C# ; autrement dit, au moment o j'cris, Visual Studio de Microsoft. Pour construire les programmes de ce livre, vous devez avoir install Visual Studio .NET. Pour pouvoir seulement excuter un programme gnr avec C#, il faut avoir le Common Language Runtime (CLR). Au cours de sa procdure d'installation, Visual Studio .NET copie le CLR sur votre machine. D'autre part, Microsoft a I'intention d'inclure le CLR dans les versions ultrieures de Windows, mais ne I'a pas encore fait pour le moment.
A
XUI
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.
Premire partie
proqrammes c#
Dans votre vie future de programmeur C#, vous allez crer beaucoup de programmes. Quelle meilleure manire de commencer que d'crire une petite application Windows amusante ('ai bien dit petite) ? Cette partie va vous montrer, tape par tape, comment crir'e la plus petite application Windows possible en utilisant l'interface Visual Studio .NET. Vous apprendrez aussi crer le cadre de base C# que nous allons utiliser dans le reste du livre,
ntroduction
XUI
Deuxitne parte en C#
: Programmaton lnentaire
Dans sa dfinition la plus lmentaire, une pice de Shakespeare n'est rien d'autre qu'un ensemble de sries de mots, lies les unes aux autres. D'un point de vue tout aussi lmentaire, 90 "/n de l'criture de n'importe quel programme C# consiste en cration de variables, en oprations arithmtiques et en instructions de contrle du chemin d'excution du programme. Cette partie est consacre ces oprations lmentaires.
Troisitne parte
: Programmation et objets
Dclarer des variables ici et l et faire avec elles des additions et des soustractions est une chose, crire de vritables programmes pour de vritables utilisateurs en est une autre. La troisime partie est consacre la manire d'organiser vos donnes pour les rendre plus faciles utiliser dans la cration d'un programme.
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.
XUlll
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.
.\
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++.
S%|
= td
\7 premire lecture. \/ /
Cette icne indique des aspects techniques que vous pouvez ignorer en
Introduction
XIX
U
.
L'icne Truc signale une information qui peut vous pargner pas mal de temps et d'efforts.
f\ =(D
*-f*li3
F
Souvenez-vous aussi de ce qui est indiqu par cette icne. C'est le genre de chose qui vous tombe dessus au moment o vous vous y attendez le moins et qui peut produire un bogue vraiment difficile dbusquer. Cette icne identifie le code que vous trouverez sur le site des ditions First. Vous y gagnerez quelques efforts de frappe au clavier, mais n'en abusez pas. Vous comprendrez mieux C# en saisissant les programmes vous-mme.
l
| | \-.Y
l----Fr:fn
public class
{
MyClass
Chaque listing est suivi par une explication subtile et profonde. Les programmes complets sont en tlchargement sur le site des ditions First, ce qui fera votre bonheur, mais les petits fragments de code n'y sont pas. Enfin, vous verrez des squences d'ouverture de menus comme dans "Slectionnez Fichier/Ouvrir avec/Bloc-notes", ce qui signifie : cliquer sur le menu Fichier, puis, dans le menu qui apparalt, sur Ouvrir avec, et enfin, dans le sous-menu qui apparalt, de slectionner Bloc-notes.
XX
C# pour
les Nuls
0 aller mantenant
Naturellement, la premire tape est de comprendre le langage C#, idalement en lisant C# pour les [t'luls. En ce qui me concerne, je m'accorderais quelques mois pour crire des programmes C# simples avant de passer l'tape suivante qui est d'apprendre crer des applications Windows. La cinquime partie pourrait faire paratre les choses faciles, mais il y a pas mal de piges. Essayez tous les composants disponibles dans la bolte outils de Visual Studio. Son systme d'aide en ligne, trs complet et pratique, les dcrits tous. Accordez-vous un bon nombre de mois d'exprience de cration d'applications Windows avant de vous lancer dans l'criture de programmes destins tre distribus sur Internet. Entre-temps, vous disposez de plusieurs endroits pour vous tenir au courant de I'actualit de C#. Pour commencer, tournez-vous vers la source officielle I nsdn . microsof t . com. Il existe aussi de nombreux sites Web de programmeurs qui contiennent des lments trs complets sur C#, et qui permettent aussi de participer des discussions en ligne sur les sujets les plus divers, de la manire d'enregistrer un fichier source aux mrites combins des ramasse-miettes (garbage collectors) dterministes et non dterministes. Voici quelques grands sites sur C#, sans ordre
particulier
,/ t/ t/
www.
csharpindex.
r^/ww.
c- sharpcorner. com
J'ai aussi mon propre site Web, wlvw. stephendavls . com, qui contient une liste de questions frquemment poses (FAQ, Frequently Asked Questions). S'il y a une chose que vous n'arrivez pas comprendre, Ia rponse ce qui vous proccupe s'y trouve peut-tre dj. J'y ai aussi ajout une liste de toutes les erreurs qui ont pu se glisser dans le livre. Enfin, il y a un lien vers mon adresse de messagerie qui vous permettra de m'envoyer un mail si vous ne trouvez pas ce que vous cherchez.
Premire partie
"Avont d'aborder fes ospects avancs comme la fonction' Eject erLesTouristesQuiNaSuiventPos', nous sllons commence por es principes de bese."
f
ici ce que vous ayez maltris C#, vous avez pas mal de chemin faire. Autant commencer par vous amuser un peu. Cette premire partie va vous montrer les tapes de la cration d'une application Windows aussi simple que possible en utilisant I'interface de Visual Studio .NET. Vous y apprendrez aussi crer le cadre de travail de base en C# pour les exemples de programmes qui apparaissent tout au long de ce livre.
Ilr tt: t I V
Chapitre
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#.
C#
Malheureusement, un ordinateur ne comprend rien de ce qui ressemble un langage humain. Vous pouvez toujours me dire : "Mon tlphone compose le numro de la personne que je veux appeler si je lui dis son nom. Je sais qu'il y a un petit ordinateur qui pilote mon tlphone. Donc, cet ordinateur parle franais." En fait, c'est un programme qui interprte ce que vous dites, pas I'ordinateur lui-mme.
Le langage de I'ordinateur est souvent appel longage machine. Pour un tre humain, il est possible, mais extrmement difficile et fertile en erreurs, d'crire en langage machine.
{cg)
1t$!Qa. Pour des raisons historiques, le langage machine est aussi appel langage d'assemblage. Chaque constructeur fournissait avec ses machines un programme nomm assembleur qui convertissait des mots particuliers en instructions du langage machine. Ainsi, vous pouviez crire des choses vraiment cryptiques du genre l'{OV AX, Ci{ (c'est une vritable instruction pour processeur Intel), et I'assembleur convertissait cette instruction en une suite de bits correspondant une seule instruction machine.
Les tres humains et les ordinateurs ont dcid de se rencontrer quelque part entre les deux. Les programmeurs crivent leurs programmes dans un langage qui est loin d'tre aussi libre que le langage humain, mais beaucoup plus souple et plus facile utiliser que le langage machine. Les langages qui occupent cette zone intermdiaire (par exemple C#) sont appels langages de hout niueau (le terme haut a ici un sens relatif).
Qu'est-ce que C#
Le langage de programmation C# est I'un de ces langages intermdiaires qu'utilisent les programmeurs pour crer des programmes excutables. C#
comble le foss qui existait entre le puissant mais compliqu C+* et le facile mais limit Visual Basic. Un fichier de programme C# porte I'extension .CS.
C# est
:
t/ t/ t/ t/
Souple : Un programme C# peut tre excut sur la machine sur laquelle il se trouve ou bien transmis par I'intermdiaire du Web pour tre excut sur un ordinateur distant. Puissant : C# dispose essentiellement du mme jeu d'instructions que C++, mais avec les angles arrondis.
Facile utiliser: Dans C#, les commandes responsables de la plupart des erreurs dans Q+r ort t modifies pour les rendre plus stres.
Visuel : La bibliothque de C# fournit les outils ncessaires pour crer directement des fentres d'affichage labores, avec des menus droulants, des fentres onglets, des barres de dfilement et des images d'arrire-plan, entre autres.
Prt pour Internet : C# est le pivot de la nouvelle stratgie Internet de Microsoft, nomme .NET (prononcer point net).
t/
,/ Sr:
Tout langage destin une utilisation sur Internet doit contenir sous une forme ou sous une autre des outils de scurit pour se protger contre les hackers.
.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.
Quand un programmeur dit "distribu", il pense des ordinateurs disperss gographiquement, excutant des programmes qui se parlent les uns aux autres, dans la plupart des cas par Internet.
Microsoft a dcid de se lancer dans la course et a acquis une licence du code source de Java, crant sa propre version nomme Visual J++ (prononcer "J plus plus"). Microsoft obtint ainsi un accs instantan aux progrs accomplis par Sun et de nombreuses autres entreprises en dveloppant des utilitaires en Java. Il y eut toutefois quelques problmes lorsque Microsoft tenta d'ajouter des fonctions Java, car son contrat de licence du code source le lui interdisait. Pire encore, le contrat tait si simple qu'il tait impossible d'y lire autre chose que ce qu'on avait voulu y mettre. Sun avait russi bouter Microsoft hors du march Java. Il tait finalement aussi bien de se retirer de Java, parce qu'il avait un srieux problme : pour en tirer tous les avantages, il y avait intrt crire tout son programme en Java. Comme Microsoft avait trop de dveloppeurs et trop de millions de lignes de code source existantes, il lui fallait inventer un moyen de prendre en compte plusieurs langages. C'est ainsi que .NET vint au monde.
.NET est un cadre de travail, en bien des points semblable celui de Java. La plate-forme de la gnration prcdente tait constitue d'outils aux noms tranges, comme Visual C++ 6.0, COM+, ASP*, Dynamic Linked Libraries et Windows 2000 (et versions antrieures). .NET leur apporte Visual Studio .NET, une amlioration de COM+, ASP.NET, une nouvelle version de Windows, et des serveurs prenant en compte .NET. .NET quant lui prend en compte les nouveaux standards de communication comme XML et SOAP, plutt que les formats propritaires de Microsoft. Enfin, .NET prend en compte le dernier grand mot d'ordre qui fait fureur, comme en son temps I'orientation objet : les services Web.
'qg,
^tK
Microsoft revendique volontiers que .NET est trs suprieur la suite d'outils pour le Web de Sun, base sur Java, mais la question n'est pas l. Contrairement Java, .NET ne vous demande pas de rcrire vos programmes existants. Un programmeur Visual Basic peut se contenter d'ajouter son programme quelques lignes de C# afin de le rendre "bon pour le Web" (ce qui signifie qu'il sait se procurer des donnes sur Internet). .NET prend en compte tous les langages de Microsoft, plus une vingtaine de langages d'autres origines, mais c'est bien C# qui est le navire amiral de la flotte .NET. Contrairement la plupart des autres langages, C# peut accder toutes les fonctions de .NET.
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.
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.
*\
'e,
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.
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.
.-.il
I
'-l ,.!
.fu
Arcessoires
&5dnsr*s
t ---?t
t,-dI
ffi
P66 de
trvd
t*il,***
Windws Update Nouveu dscumffit Of f ic
,#
,..& Internt
"|ffi, Mi..rlt1utlod.,
NicroscftExrd
tticrosdt t4ord
Frm$,rk5D{
mfrosoft np6ftion CBrfer Teit Mi(losaft Offir Tols visud stldi,NET Enterpris Feturs visud Studio.NEl lools
lP
- le plus pas
c#.
droite n'est
ir 3J
tlglts
rl^6
3.
Dans le volet Types de projets, slectionnez Projets Visual C#, et dans le volet Modles, sIectionnez Application Windows. Si vous ne voyez pas la bonne icne de modle, ne vous inquitez pas. Faites dfiler Ie contenu du volet Modles pour la faire apparatre.
Ne cliquez pas encore sur OK.
4.
Dans le champ Nom, entrez un nom pour votre projet ou laissez le nom par dfaut. L'Assistant Applications va crer un dossier dans lequel il va stocker diffrents fichiers, notamment le fichier source initial C# du projet. L'Assistant Applications utilise comme nom de ce dossier le nom que vous avez entr dans le champ Nom. Le nom initial par dfaut est 'un/indowsAppllcationl. Si vous I'avez dj utilis pour un projet, il devient WindowsApplicat ion} ou rjlndowsAppiication3. et ainsi de suite.
Pour cet exemple, vous pouvez utiliser le nom par dfaut ainsi que I'emplacement par dfaut pour Ie nouveau dossier : Mes documents\Projets Visual Studio\Wi nd owsAp p 1 i c at i on 1 .
t0
C#
Figure 1.2 Crer un n0uveau projet vous met sur la voie d'une
:
a pplication Windows.
f;g iiJ
J Pr,:,lels !'i5uBl Eitsir --i Prrrits ltisul f# J Ffflett liituil !:++ J Fr ljts dt rrrniitturaLiorr et J lLrE5 r,liel:5 J -irilui:frrri !4:rt:l 5frt,Jir
w
de 'lepl,:ienre
@
Fibtiottreowde
Ert,liothque de ,:,lnlrlBs ",LlindDi!5
Figure 1.3
ffi
L Assistant Applications
- Visual de
nouvea
| :J1I lll
,
-*
A5P,FIET
AFliration
AsF'.rJE
<t
Prijet de
':reatron
j
,..
Emplacement
,-
;|
Parcourir
pr0gramme
Windows.
vtlus
T- "l --l
Annuhr
Aid*
5.
tl
t\r
7 ,31ll )
^"9FK \g/
On appel le gnrer(build) l'acte de convertir un fichier source C# en une vritable application Windows en tat de fonctionner. Si votre fichier source contient des erreurs, Visual C# les trouvera dans le processus de construction.
Slectionnez Gnrer/Gnrer. Une fentre de rsultats s'ouvre, dans laquelle dfile une succession de messages. Le dernier de ces messages doittreGnration : 1 a russi, O a chou, 0 a t ignor.
La Figure 1.4 montre quoi ressemble mon Bureau aprs la gnration de
I'application Windows par dfaut. Vous pouvez dplacer les fentres comme vous voulez. Les plus importantes sont la fentre Forml.csIDesign] et la fentre Sortie.
Dgnns
:
g'-til5
Debuq
Fentre
'
-#
Help
Forml
5ystem,Wrrdorys
Forrn5.Ffr w
i AccessibleRole Defaqlt ; h?rrn,:rttt dtrloror Ll Lontror ; EacksroundlmogtI iaucun) : aursor Oefault :E) nont l'rrosol't 5ans 5el ForeColor I Conhol1ext
FrmBsrder5tl,l 5izble
No
j
Figure 1.4 : Le modle de pr0gramme initial pour Windows n'a rien de bien
Rightl0teft Text, :8 l. +rr rt*rI rtzzt,tl : AllowDrF antextllenu i Enbled ImeMode E ! .tq rIr.tr r' ,tti::t*r
El (Dyndfri.Propertr
Forml
False
r
Gerrrat rorl
:
{aucunl
True
Nofontrl
-,
{
La gnration a rursi
Coll
Chl
l2
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.
La fentre du
- application d
Windows fonctionne, mais elle ne suffit pas convaincre
que Visual
- 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.
t3
Le Concepteur de formulaires est un outil trs puissant. Il vous permet de "dessiner" vos programmes dans le formulaire. Une fois que vous avez termin, cliquez sur Gnrer, et le Concepteur de formulaires cre le code C# ncessaire pour raliser une application avec le joli cadre que vous venez de dessiner. Dans les sections suivantes, vous allez gnrer une application avec deux champs de texte et un bouton. L'utilisateur peut saisir ce qu'il veut dans I'un des champs de texte (la source), mais pas dans I'autre (la cible). Lorsque I'utilisateur clique sur un bouton intitul Copier, le programme copie le texte du champ source dans le champ cible.
tl,lettre en place Quel(ues contrles L'interface utilisateur de Visual Studio est constitue de diffrentes fentres. Tous les lments comme les boutons et les zones de texte sont des contr1es. Afin de crer un programme Windows, vous allez utiliser ces outils pour en raliser l'interface utilisateur graphique (GUI), qui est gnralement la partie Ia plus difficile raliser d'un programme Windows. Dans le Concepteur de formulaires, ces outils se trouvent dans une fentre nomme Bolte outils.
Si la B1te outils n'est pas ouverte, slectionnez Affichage/Bolte outils. La Figure 1.6 montre cette Bote outils.
DDnns
,:ompsnts
'r/y'rndows Forn-rs
\ A A
gll
iii,r-
Fc,inteur
tatel
LinLLabel
E.rttc'n
Te:<tBo:r Marnf"lenu
LneaxDox
HlOtrUtf0Tl
Grupuox Picture8x
h'Anel
-.j
! .9 ll :g ;:-
natacrid
ListBctx
checkedlist8ox
L0mBoErlx Llslvre({
;:
t4
C#
.$$G ^/ t(7, V
Hinquitezpas.VotreBo1teoutilspeuttrsbiensetrouvergauche,
droite ou au milieu de l'cran. Vous pouvez dplacer chaque fentre o vous voulez dans la fentre Visual Studio.
La Bote outils comporte plusieurs sections, dont Donnes, Composants,
et Windows Forms (qui sera peut-tre devenue "Formulaires Windows" dans la version que vous aurez entre les mains). Ces sections permettent simplement d'organiser les contrles afin que vous puissiez les trouver plus facilement. La Bolte outils contient de trs nombreux contrles, et vous pouvez aussi crer les vtres. Dans la Bolte outils, cliquez sur Windows Forms. Ces contrles vont vous permettre d'amliorer vos formulaires. Vous pouvez utiliser les petites flches que vous voyez droite pour faire dfiler la liste.
Pour ajouter un contrle dans un formulaire, il suffit de le faire glisser et de le dposer I'endroit voulu. Essayez :
l.
Vous ne pouvez augmenter que la longueur d'une zone de texte, pas sa hauteur, car par dfaut une zone de texte ne comporte qu'une
seule ligne.
2. 3. 4.
la premire.
Faites maintenant glisser un bouton et dposez-le au-dessous des
t5
Wi ''
+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. 2.
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
t6
G#
button
Butt'ln
f-'=lI s; dr | .n P n'
Copier
frl,a1 lt9 | /
-t
5tandard
l'licrrrsoFt 5ns 5erifj tl,Z5pt
l-aJTtl:
Ftrefol,:r
Inr,rqe
! [--l l-l
No
fontrolText (nurun)
Inr,r'lelgn
Intaqelrr,le;r
Ivliddlefenter lnucun)
RighlT,:Lel't
f"liddle,lerrter
E | ..,,,i.,,,;r..
llr,r+Dr,:p IdIS
I UCUn
aonLe:rtlt:rrU
ttralogResult
l,l:rne
'
. l':l:l:l
':
Text
Le texte cntenu dans ce rontlle,
Vous pouvez utiliser la proprit Text d'un contrle zone de texte pour en changer le contenu initial. Pour les deux zones de texte de notre exemple, j'ai dfini cette proprit comme "Tapez quelque chose ici" et "Le programme copie ici ce que vous avez tap", afin que I'utilisateur sache quoi faire aprs avoir lanc le programme.
De mme, la proprit Text du formulaire lui-mme correspond au texte qui apparalt dans sa barre de titre, et vous pouvez la changer. Cliquez dans un endroit quelconque dans le formulaire, tapez ce qui vous convient dans la proprit Text, et appuyez sur Entre. J'ai fait apparaltre dans la barre de titre "Programme qui copie du texte." 4.
Slectionnez la zone de texte du bas, et faites dfrler ses prcprites de comportement pour faire apparare la proprit ReadOnly (lecture seule). Dfinissez cette proprit comme vraie en cliquant dessus et en sIectionnant True dans le menu droulant qui apparat, comme le montre la Figure 1.9. Dans la barre d'outils de Visual Studio, cliquez sur Ie bouton
D.
t7
Figure 1.9:
Dfinir une
zone de texte en lecture seule (Read0nly) empche
lTape:
,.i,t'ar:i .- :
progal]w
-r:.l,tf:r.,r
L. .:.l
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
Vous pouvez maintenant excuter le programme en slectionnant Dboguer/Excuter sans dbogage. Le programme ouvre un formulaire conforme celui que vous venez de crer, comme le montre la Figure 1.10. Vous pouvez taper ce que vous voulez dans la zone de texte du haut, mais vous ne pouvez rien entrer dans celle du bas ( moins d'avoir oubli de dfinir comme vraie la proprit ReadOnly).
Figure 1.10:
La fentre du
-ltrl
- le est
pr0gramme
lrEEtrEE@
lle
programme copie ici ce qu vous avez tap
formulaire
que v0us venez de
crer.
t8
l. 2.
Dans le Concepteur de formulaires, slectionnez le bouton Copier. Dans la fentre Proprits, cliquez sur le bouton contenant un clair, au-dessus de la liste des proprits, pour ouvrir un nouvel ensemble des proprits.
Ce sont les unements.lls dfinissent ce que fait un contrle au cours de I'excution du programme.
Vous devez dfinir l'vnement Click. Comme son nom I'indique, il dfinit ce que fait le bouton lorsque I'utilisateur clique dessus.
3.
Lorsque vous double-cliquez sur la proprit Click, Visual Studio affiche la fentre Code et cre une nouvelle mthode, laquelle il donne le nom descriptif buttonl*Click ( ) . Lorsque I'utilisateur clique sur le bouton Copier, cette mthode effectue le transfert du texte de textBoxl, la source, textBox2,la cible. Pour le moment, ne vous inquitez pas de ce qu'est une mthode. J'en donnerai la description au Chapitre 8.
Cette mthode copie simplement la proprit Text de dans la proprit Text de textBox2.
textBoxl
4.
textBox2.Text = textBoxl.Text;
tg
1r${Qa^
ftg)
Remarquez que C# essaie de vous faciliter la tche de saisie du code. La Figure 1.1 1 montre I'affichage au moment o je tape le nom de la dernire proprit Text de la ligne ci-dessus. La liste droulante des proprits de la zone de texte correspondante apparalt, vous offrant un aide-mmoire des proprits disponibles, avec une info-bulle qui vous dit de quoi il s'agit. Cette fonction vous permet de complter automatiquement ce que vous tapez, et c'est une aide prcieuse au cours de la programmation (pour que a marche, ne faites pas de fautes de frappe dans ce qui prcde - attention aux majuscules et
minuscules).
PJojet
Ferr
:':)',
ll 'i
I'tt
!bog,:er
.3:
t'.
utils
-.
l?f
:l
i
:J
i
:-lf,l1r
!r:,lal llil]til
Figure 1.11
La fonction
1r
5rne
>l
+r
proprits au fur et
mesure que vous tapez.
:t*
Z
Pret
; :,.;,, .. E
ssrtie
Col 40
5.
20
G#
zone de texte cible, comme le montre la Figure 1.12. Vous pouvez joyeusement rpter le processus autant que vous voulez avec tout ce qui vous passe par la tte, jusqu' ce que l'puisement vous submerge.
-ltrlxJ
lJ'ai tap r,ea darrr la zone de lerte t.rurrte lJ'ai tap ceci dan le asne de texte srrurce
Figure 1.12
a marche
En considrant le processus de cration, vous serez peut-tre frapp par son orientation graphique. Faites glisser des contrles, dposez-les o vous voulez dans le formulaire, dfinissez leurs proprits, et vous y tes. Il vous a suffi d'crire une seule ligne de code C#, et mme a n'tait pas
bien difficile.
'qE,
^tK
on pourrait objecter que ce programme ne fait pas grand-chose, mais je ne suis pas d'accord. Consultez simplement un manuel de programmation des dbuts de Windows, l'poque o les assistants d'application n'existaient pas encore, et vous verrez combien d'heures de programmation il fallait pour raliser une application aussi simple que celle-ci.
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
ne me dcourageant pour le programmeur c# dbutant. si vous programme du type croyezpas, alleisimplement voir Ie Chapitre 1. Un qu" t'on appelle application console gnre significativement moins de code c# et est beaucoup plus facile comprendre.
crer un modle Dans ce chapitre, vous allez utiliser Visual Studio afin de un peu manuelled,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'
SP*E Le principal but de ce livre (en tout cas des premires parties) est de y -.i \ ;J; aider comprendre c#. pour faire en c# un ieu qui aura un succs \ !g:I langage c#'
\ 5
l que vous connaissiez le I mondial, il faut d'abord
22
C#
l.
Visual Studio affiche une fentre contenant des icnes qui reprsentent les diffrents types d'application que vous pouvez crer.
2.
Application
console.
f\ /
.--).
e,
\
Faites attention bien slectionner le dossier des projets Visual C# dans la fentre Nouveau projet. Si vous en slectionnez un autre par erreur, Visual Studio peut crer une horreur comme une application Visual Basic ou Visual C*n. Avec Visual Studio, il est ncessaire de crer un projet avant de pouvoir commencer entrer votre programme C#. Un projet est un peu comme un tiroir dans lequel vous allez entasser tous les fichiers qui constituent votre programme. Lorsque vous demandez au compilateur de gnrer le programme, il extrait du projet les fichiers dont il a besoin afin de crer le programme partir de ceux-ci. Le nom par dfaut de votre premire application est
ConsoleApplicationl. L'emplacement par dfaut du fichier correspondant est un peu trop en profondeur mon gott dans le dossier Mes documents. Puisque je suis un peu difficile (ou peut-tre parce que j'cris un livre), je prfre classer mes programmes o je veux, et pas ncessairement l o Visual Studio veut les mettre.
3.
Pour changer le dossier par dfaut de vos programmes, cliquez sur le bouton Parcourir et naviguezjusqu' I'endroit voulu.
Pour le dossier dans lequel je place tous mes programmes, j'ai choisi le nom Programmes C#.
23
4. 5.
Dans le champ Nom, entrez le nom que vous voulez donner au projet que vous crez. Pour ce premier programme, nous allons nous en tenir la tradition avec Hel1o.
commentaires en anglais seront peut-tre en franais dans la version que vous utiliserez) :
using
System;
nanespace He11o
tt
lll
||
(rrmlnary)
| III
{
Suwary description
4tsw*nary)
Class1
for
Classl.
class
args)
ll /l il
]
'l
f\ =(t
Par rapport au programme excutable Windows que vous avez cr au Chapitre 1, Visual Studio a invers les lignes using Systern et namespace He11o. C# accepte ces commandes dans un ordre comme dans I'autre (il y a une diffrence. mais elle est subtile et bien en dehors du cadre de ce chapitre).
Tester le rsultat
Slectionnez Gnrer/Gnrer pour faire de votre programme C# un programme excutable.
24
Dbut de
Debug ,NEf -
Prparation des ressources. . . Mise jour des rfrences... Compilation principale en cours. . . Gnration termine -- 0 erreur, 0 avertissenent Gnration d'assenblys satellites en cours...
---Gnration
:I
I a russi.
^rtC
f fxll
tL]r, -
Unerglegnraledelaprogrammationestque''aruSSi''Veutdire,,a
va", et "a chou" veut dire "a ne va pas". Pour excuter le programme, slectionnez Dboguer/Dmarrer. Le programme se termine immdiatement. Apparemment, il n'a rien fait du tout, et en fait c'est bien le cas. Le modle n'est rien d'autre qu'une coquille vide.
.,-.71r{ LL?r, C
^1
Ne vous proccupez pas d'entrer un ou deux espaces ou une ou deux lignes blanches. En revanche, respectez les majuscules et les minuscules.
using Systen;
namespace IIe1lo
t
^!!hr.^ PUUTfL LrAD ^t^^a I,^.SS1 UrA
Chapitre
25
/i
Console.hlriteline("He1Lo,
"*
sNane)
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
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
At'^!\ commande DOS. Ouvrez une fentre DOS. Tapez cd \Programmes :HW ) c*rH"llo\bin\Debug. Puis, pour excuter le progra..", tapez Hello \/ et appuyez sur Entre. Le rsultat doit tre le mme. Vous pouvez aussi
naviguer jusqu'au dossier \Programmes C1\ue11o\bin\Debug dans I'Explorateur Windows et double-cliquer sur le fichier He11o. exe.
4${ea^
Eramnons ce programme
Dans les sections qui suivent, nous allons prendre part I'une aprs I'autre chaque partie de cette application console en C# afin d'en comprendre le
fonctionnement.
26
G#
nanespace Hel1o
C'est
ici
que coruaenc
le
prograrnme
//
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
// 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.
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.
27
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
1'utilisateur
nogl:
console.l,lriteline("Entrez votre
")
ll tit
string
/l
Console
,"*
sName)
L'excution du programme commence par la premire instruction C#: Console . WriteLine. Celle-ci crit dans la console la chane de caractres
Entrez votre
nom:.
L'instruction suivante lit la rponse entre par I'utilisateur et la stocke dans une "bolte de travail" nomme sName fi'en dirai plus sur ces emplacements de stockage au Chapitre 3). La troisime instruction combine la chne He11o et le nom entr par I'utilisateur, et envoie I'ensemble sur la console.
trois dernires lignes attendent que I'utilisateur appuie sur la touche Entre avant de poursuivre. Elles assurent ainsi que I'utilisateur a le temps de lire ce que le programme vient d'afficher :
Les
28
G#
/l Attend confirmation de 1'utilisateur Console.EriteLine ("Appuyez sur Entre pour terniner. . . ")
Console.Read0;
Cette tape peut tre importante selon votre environnement et selon la manire dont vous excutez le programme. Dans Visual Studio, vous avez deux manires d'excuter un programme. Si vous utilisez la commande DboguerlDmarrer, Visual Studio ferme la fentre de rsultats ds que le programme se termine. C'est la mme chose qui se produit lorsque vous excutez le programme en double-cliquant sur I'icne du fichier excutable dans I'Explorateur Windows.
"tq-$, -i il j
x
T .t
Quelle que soit la manire dont vous excutez le programme, attendre que l'utilisateur appuie sur la touche Entre avant de quitter rsout tous les problmes.
Deuxime partie
"Excusez-mai. Y a-t-il quelgu'un ici qui ne soit PAS en train de pcrlar de C# ?"
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
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
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
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).
exemple
x=yl
+zy+y
,, + 1 ^1^-^ I ' ! qrur
si k:
x=k2
Ici, le mathmaticien a crit une quation quadratique. Peut-tre les variables x et y ont-elles dj t dfinies quelque part. Toutefois, lorsqu'il voit apparaltre une nouvelle variable, k, le programme tombe des nues. Dans cet exemple, ft ne signifie pas essentiellement qu'il a la valeur de y plus 1, mais qu'il reprsente le concept de y plus 1. C'est une sorte de raccourci. Jetez un coup d'il n'importe quel manuel de mathmatiques, et vous veyyez ce que je veux dire. Je dis bien : jetez un coup d'cril. Vous tes ici pour lire mon livre et pas un autre. Un programmeur doit tre prcis dans Ia terminologie qu'il utilise. Par exemple, il peut crire le code suivant :
i nt
33
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."
^tK =({g,
dit : "n gale 1." Le programmeur C# le dit d'une manire plus prcise : "Stocker la valeur I dans la variable n." (Pensez I'armoire dossiers suspendus, et vous veyrez que c'est prfrable.) Les oprateurs C# disent I'ordinateur ce que vous voulez faire. Autrement dit, les oprateurs sont des verbes et non des descripteurs. L'oprateur d'assignation prend la valeur qui est sa droite et la stocke dans la
Le mathmaticien
Qu'est-ce qu'un i
rrt .)
Les mathmaticiens manipulent des concepts. Ils peuvent crer des variables quand a leur chante, et une mme variable peut revtir diffrentes significations dans la mme quation. Au mieux, un mathmaticien considre une variable comme une valeur sans forme fixe, au pire, comme un vague concept. Ne riez pas, c'est probablement de la mme manire que vous voyez les choses.
Si le mathmaticien
n 1l l. - rt
crit
n = .| . - t l. ..
n n
sont panni
nousn
Chacune de ces lignes associe la variable n une chose diffrente, et le mathmaticien n'y pense mme pas. Je n'y pense pas beaucoup moi-mme, sauf pour la dernire ligne.
d'offrir une telle souplesse. En C#, chaque variable possde un type fixe. Lorsque vous choisissez un nouveau dossier suspendu pour
C# est loin
34
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
nf
variable entire
2
et f initialise
avec 1a valeur
int n=
m = ?'
Assigne
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
\g/
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.
I DcIare une nouvelle variable int | et lui donne I eomne valeur initiale int o = 1l
/ |
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-
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
int
de dffrents
1t$!Qa^
; L 'rilliards. t
't
^.v7atr :(dqfl ) L'tendue exacte est de -2 147 483 648 2 147 483 647. \/
;l -\
\J/
36
Au cas o 2 milliards ne vous suffirait pas, C# offre un type d'entier nomm iong (abrviation de long int) qui peut contenir un nombre entier beaucoup plus long. Le seul inconvnient du type long est qu'il occupe plus de place dans votre armoire : un entier de type long consomme huit octets (64 bits), soit deux fois plus qu'un int ordinaire.
.r$c ./ t(7, Y
Autrement dit, un entier iong occupe deux dossiers d'une capacit d'un d'armoire dossiers suspendus commence tre un peu use, je parlerai partir de maintenant en octets.
HintclansvotrearmoiredossierSSuSpendus'Commecettemtaphore
'qg,
Type sbyte byte
^.rr7p7\ L'tendue
1e${Qa.
exacte d'un entier long est de -9 223 372 036 854 775 808
C# offre plusieurs autres types de variable entire montrs par le Tableau 3.1.
(octets)
tendue
-128 127
0255
-32,768 32,767 0 65,535 -2 milliards 2 milliards 0 4 milliards
-101s
short
ush ort
2 2
int
uint
long ulong
4 4
B B
1nt
n = 1234567890;
uint un = 3234567890U
long | = 1234567890121 long ul = 123456789012U1
101e
(beaucoup)
1020
Comme je I'expliquerai dans la section "Dclarer des constantes numriques", plus loin dans ce chapitre, une valeur fixe telle que 1 a aussi un type. Par dfaut, une constante simple comme 1 est considre de type int. Une constante de type autre que int doit tre marque par son type. Par exemple, l23U est un entier non sign (unsigned), de type ui irt.
37
La plupart des variables de type entier sont signes, ce qui signifie qu'elles ont un signe (+ ou -), et qu'elles peuvent donc reprsenter des valeurs
ngatives. Un entier non sign ne peut reprsenter que des valeurs positives, avec I'avantage de pouvoir contenir une valeur deux fois plus leve. Comme vous pouvez le voir dans le Tableau 3.1, les noms de la plupart des types d'entier non sign commencent par u (pour unsigned), alors que les types signs n'ont gnralement pas de prfixe.
// 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.
-9t4K Une variable int ne peut reprsenter que des nombres entiers. L'quivalent i( 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)
=l
tg9!Ra" Tronquer n'est pas la mme chose qu'arrondir. Tronquer consiste S7^l \ supprimer la partie dcimale, alors qu'arrondir consiste prendre la
Sf i({",'f
valeur entire la plus proche. Ainsi, tronquer 1,9 donne donne la valeur 2.
1.
Arrondir
1,9
Pour une temprature, 37 peut tre une approximation satisfaisante. On ne va pas mettre une chemise manches longues 37 degrs et une chemise manches courtes 37,7 degrs. Mais pour bien des applications, sinon presque toutes, il est inacceptable de tronquer un nombre.
38
G#
:HW
plus compliqu que a. Une variable de type int n peut pas 1r$!Qa" En fait, c'est \ stoker la fraction 519 qui correspond toujours pour elle la valeur 0. Il en ^v71t7 ) rsulte que l'quation de notre exemple ci-dessus calcule la valeur 0 pour \/ nCelsius pour toutes les valeurs de nFahr. On est bien oblig d'admettre que c'est inacceptable.
Sur le site Web, le dossier ConvertTernperatureWithRound0ff, contient un programme de conversion de temprature avec des variables de type int. ce stade, vous n'en comprendrez peut-tre pas tous les dtails, mais vous pouvez jeter un coup d'il aux quations et excuter le programme Ciassl . exe pour voir les rsultats qu'il produit.
entiers.
Dans bien des cas, vous aurez besoin de nombres dont la partie dcimale n'est pas nulle. Les mathmaticiens les appellent les nombres rels. J'ai
toujours trouv a ridicule. Y a-t-il des nombres entiers qui soient irrels
=( UA )
./
-sq4<o
i:
N7/
Remarquez que j'ai dit qu'un nombre rel peut avoir une partie dcimale non nulle, mais ce n'est pas obligatoire. Autrement dit, 1,5 est un nombre rel, mais 1,0 galement. Par exemple, 1,0 + 0,1 gale 1,1. Un nombre rel plus un nombre rel donne un nombre rel. Gardez cela en tte pour la suite de ce chapitre.
Heureusement, C# connalt les nombres rels. Il y en a de deux sortes : dcimaux et virgule flottante. Le type le plus courant est virgule flottante. Je dcrirai le type decimal un peu plus loin dans ce chapitre.
float f =
1.0:
39
Une fois dclare comme f 1oat, la variable f est de type f loat pour toutes les instructions qui vont s'y appliquer. que la virgule dcimale \eENrgr. Un nombre virgule flottante doit son nom au fait y est autorise "flotter" de gauche droite au lieu de se trouver un S/^T \ =ldl\y / emplacement fixe. Il permet donc de reprsenter aussi bien 10,0 que 1,00 \/ 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.
Taille [octets]
B
Etendue
1.5
Prcision
3.4
Exemple
float double
* 10'45
* 1038
10308
6-
16
* 5.0 10324
x 1.7
chiffres 15 - 16 chiffres
7
float f
= 1.2F;
double d = 1.2:
-$tf,
Le type par dfaut pour une variable en virgule flottante est double et
nonr :.--'
Dans le Tableau 3.2, la colonne Prcision contient le nombre de chiffres exacts pour chaque type de variable virgule flottante. Par exemple, la fraction 5/9 vaut exactement 0,555... avec une infinit de 5. Mais une variable de type f loat contient un certain nombre de chiffres exacts, ce qui veut dire que les chiffres qui se trouvent au-del du sixime ne le sont pas ncessairement. Aussi, exprim dans le type f loat, 5/9 pourrait trs
Vous savez donc que tous les chiffres apparaissant aprs le sixime 5 ne peuvent pas tre considrs comme exacts.
-rK
'qg,
Une variable de type f loat possde en fait 6,5 chiffres exacts. Cette valeur trange vient du fait que la prcision en virgule flottante est donne par un calcul qui fait intervenir 10 puissance un logarithme en base 2. Voulez-vous vraiment en savoir plus ?
40
G#
Avec une variable de type double, la mme fraction 5/9 pourra apparaltre de cette facon :
l5 et
16 chiffres exacts.
Comme en C# une variable virgule flottante est par dfaut en double une raison particulire de ne pas le faire. Toutefois, qu'il utilise le type ' ' douD 1e ou le type f 1oat, on dira toujours d'un programme qu'il travaille
Hprcision(letypedoub1e),vouspouveZutilisercetype,moinsd'avoir
en virgule flottante.
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.
4l
est vrai qu'elles utilisent plus de mmoire, mais de nos jours la nrmoire ne cotte pas cher. Alors, pourquoi pas ? Mais les variables virgule flottante ont aussi des limitations.
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
42
C#
La ttitesse de calcul
Les processeurs de la famille x86 utiliss par les PC un peu anciens fonctionnant sous Windows faisaient les calculs arithmtiques en nombres entiers beaucoup plus vite que les mmes calculs avec des nombres en virgule flottante. De nos jours, ce serait sortir de ses habitudes pour un programmeur que de limiter son programme des calculs arithmtiques en
nombres entiers. Avec le processeur Pentium III de mon PC, pour un simple test (peut-tre trop simple) d' peu prs 300 millions d'additions et de soustractions, le rapport de vitesse a t d'environ 3 1. Autrement dit, pour toute addition en type double, j'aurais pu faire trois additions en type int (les calculs comportant des multiplications et des divisions donneraient peuttre des rsultats diffrents).
Ae/!!\ viter les effets de cache. Le programme et les donnes taient mis en 9(dW ) .a.he, mais le compilateur ne pouvait mettre en cache dans les registres \/ du CPU aucun rsultat intermdiaire.
Une tendue pas si limite
Dans le pass, une variable en virgule flottante pouvait possder une tendue beaucoup plus large qu'une variable d'un type entier. C'est toujours le cas, mais l'tendue du type long est assez grande pour rendre la question pratiquement dpourvue d'intrt.
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.
43
tout simplement sauter la partie dcimale du nombre. Dans certains cas, il vous faudra une variable qui combine le meilleur de ces deux types :
t/ t/
fraction.
Comme une variable entire, offrir une valeur exacte utilisable dans des calculs. Par exemple, 12,5 est effectivement 12,5, et non
12,500001.
Heureusement, C# offre un tel type de variable, nomm decimai. Une variable de ce type peut reprsenter tout nombre compris entre 10t8 et 1028 (a fait beaucoup de zros). Et elle le fait sans problmes d'arrondi.
d e c 1ma 1
:
Une variable de type decimal se dclare comme n'importe quelle autre decinal decinal decinal
rn1:
m2
/l ll ll
Bien
Mieux Eneore nieux
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
44
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.
peut prendre deux valeurs : true ou f alse (vrai ou faux). Je parle srieusement : un type de variable rien que pour deux valeurs.
:(dw
tgq\Qa. Les programmeurs C et C** 6n1 I'habitude d'utiliser une variable i-nt avec ^t7q \ la valeur 0 (zro) pour signif ier f a 1 s :,r, et une valeur autre que zro pour ) signifier tru.e. a ne marche pas en C#. \/
Une variable boo 1 se dclare de la faon suivante bool variableBool = true:
:
45
Il n'existe aucun chemin de conversion entre une variable bool et tous les autres types. Autrement dit, vous ne pouvez pas convertir directement une variable bool en quelque chose d'autre (et mme si vous pouviez, vous ne devriez pas, parce que a n'a aucun sens). En particulier, vous ne pouvez pas transformer une variable bool en int (par exemple sur la base du principe que false devient zro), ni en string (par exemple sur la base du principe que false devient "fa1se"). Toutefois, une variable de type bool joue un rle important pour forcer I'excution d'un programme C# suivre tel ou tel cheminement, comme je I'explique au Chapitre 5.
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
46
^nu1$Q( +/ .--., \ trs bien y stocker un caractre Kanji que vous trouvez trs beau, mais
t^-.
Une variable de type char n'est pas associe une police. Vous pouvez
si
tO / V,,/
vous I'affichez, il ne ressemblera rien si vous ne le faites pas avec la bonne police.
Tqpes
char s1rciaur
Certains caractres, selon la police utilise, ne sont pas imprimables, au sens o vous ne voyez rien si vous les affichez l'cran ou si vous les imprimez avec votre imprimante. L'exemple le plus banal en est I'espace, reprsent par ' '. Il y a aussi des caractres qui n'ont pas d'quivalent sous forme de lettre (par exemple, le caractre de tabulation). Pour reprsenter ces caractres, C# utilise la barre oblique inverse, comme le montre le Tableau 3.3.
'v'
'\0'
'\r' '\\'
Le trlpe
Le type
string
:
srring est galement d'usage courant. Les exemples suivants montrent comment dclarer et initialiser une variable de type st ring
I
string soneStringl;
soneStringl = "eeci est une chane";
I
47
Une constante de type chne est une chalne de caractres entoure de guillemets. Une chalne peut contenir tous les caractres spciaux du Tableau 3.3. Une chalne ne peut pas s'tendre sur plus d'une ligne dans Ie fichier source C#, mais elle peut contenir le caractre de retour la ligne, comme le montre I'exemple suivant :
I I eeci est illicite string soneString * "Ceci est une ligne et en voil une autre"; I I nais ceci est autoris stri.ng sonestring = "Ceci est une ligne\net en voil une autre";
A I'excution, la dernire ligne de I'exemple ci-dessus crit les deux membres de phrase sur deux lignes successives :
Ceci est une ligne et en voil une autre
Une variable de type string ne peut pas tre utilise comme compteur, et ce n'est pas un type contenant une valeur. Il n'existe aucune "chalne" qui ait une signification intrinsque pour le processeur. Seul un des oprateurs
habituels est utilisable avec un objet de type string:l'opratur la concatnation de deux chalnes en une seule. Par exemple : string s
+ effectue
string
s la chalne suivante
'rteci est
un nembre de uhrase
t en voiL un autre'l
Encore un mot : une chane ne contenant aucun caractre (que I'on crira "") est une chalne valide.
^ Conparer
string t char
ring est trs
diffrent du type char. Naturellement, il existe quelques diffrences triviales. Un caractre est plac entre apostrophes, comme ceci :
la
|
48
Les rgles qui s'appliquent aux chalnes ne sont pas les mmes que celles qui s'appliquent aux caractres. Pour commencer, une variable char ne contient par dfinition qu'un seul caractre. Le code suivant, par exemple, n'a aucun sens :
char cl l1. char c2 = rht. char c3 =c1*c2 En fait, ce code peut presque se compiler, mais avec une signification compltement diffrente de I'intention initiale. Ces instructions provo-
'qE,
^"tK
quent la conversion de c1 en une variable int qui reoit la valeur numrique du caractre initialement contenu dans c 1, puis la mme chose pour c2, eT finalement I'addition de ces deux entiers. L'erreur se produit lorsque I'on essaie de stocker le rsultat dans c3. Une valeur numrique de type int peut pas tre stocke dans une variable de type char, par dfinition plus petite. En tout cas, cette opration n'a aucun sens.
Une chane, en revanche, peut avoir une longueur quelconque, et la concatnation de deux chalnes a un sens :
_+_.i-^ Lr{rr a+r'.ina L!rrr ^1 r 1 4 : = il^ilr a llhll u
, . t
strjnq
s3
"ab"
Il y a dans la bibliothque de C# une collection entire d'oprations sur les chalnes. Je les dcrirai au Chapitre 9.
49
La rgf e gnrale estque le nom d'un objet autre qu'une variable doit commencer par une lettre
majuscule. Choisissez des noms aussi descriptifs que possible, ce qui signifie bien souvent des noms composs de plusieurs mots. Chacun de ces mots doit commencer par une majuscule {sauf le premier dans le cas d'une variable}, et ils doivent tre colls les uns aux autres, sans tre relis par des tirets de soulignement, comme ceci : ceciEsrUnlcngNomDeVariable
J'ai aussi adopt la rgle supplmentaire selon laquelle la premire lettre du nom d'une variable en indique le type. La plupart de ces lettres sont assez explicites : f pour f 1oat, d pour double, s pour string, et ainsi de suite. La seule qui ncessite un petit effort de
mmoire est n pour int. ll y a toutefois une exception cette rgle : pour des raisons qui remontent au langage FOfiTRAN des annes soixante, les lettres i, j, et k sont couramment utiliss seules comme nom pour les variables de type int. Remarquezque j'aiditavoir"adopt" cette convention. Ce n'estpas moiquil'aiinvente, mais quelgu'un qui travaillait chez Microsoft l'poque du langage C. Comme il tait d'origine hongroise, rette convention a reu le nom de notation hongroise.
La notation hongroise semble ne plus tre en faveur. Je continue toutefois la prfrer, car elle me permet de connatre du premier coup d'il le type de chaque variable dans un programme sans avoir me rfrer la dclaration.
Toutes les instructions C# doivent tre traduites en instructions machine du processeur utilis. Dans le cas du PC, un processeur Intel. Ces processeurs utilisent aussi la notion de variable. Par exemple, le processeur Intel comporte huit emplacements internes, appels registres, dont chacun peut stocker un int. Sans trop entrer dans les dtails, je dirai simplement que les types dcrits dans ce chapitre, I'exception de decimal et strlng, sont intrinsques au processeur. Aussi, il existe une instruction machine qui signifie : "Ajouter un int un autre inr", et une instruction semblable pour ajouter un double un double. Comme ces types de variable sont intgrs au fonctionnement du processeur, on les appelle des types intrinsques.
En outre, les types de variable que je dcris dans ce chapitre sont de longueur fixe (encore une fois, I'exception de string). Une variable de longueur fixe occupe toujours la mme quantit de mmoire. Ainsi, si j'cris I'instruction a : b, C# peut transfrer la valeur de b dans a sans prendre
50
.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.
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
Type
Lnt rinqi onod* int .),'.-
iU
1L
long int
d o ui:,
1.0
1e
valeur 5 |
Constante
1.0F
1M
Type
f!IUAL I ^-r_
t rue
^1^^ I.1 LbC
r-l a
'\n'
char (caractre de nouvelle ligne) char (caractre dont la valeur numrique est hex
123)
string
s
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;
//
ceci fonctionne
int peut tre convertie en long car toute valeur peut tre stocke dans une variable de type long et que I'un comme I'autre type peut tre utilis comme compteur.
Une variable de type
lnt
Toutefois, une conversion dans la direction oppose peut poser des problmes. Par exemple, ce qui suit est illicite :
long lValue =
10;
I
int
nValue;
nValue =
lValue;
ceei est
illicite
52
Certaines valeurs que l'on peut stocker dans une variable de type long sont trop grandes pour une variable int (par exemple, 4 milliards). Dans ce cas, C# gnre une erreur, car des informations pourraient tre perdues dans la conversion. Ce type de bogue est trs difficile identifier. Mais si vous savez que la conversion est possible ? Par exemple, bien que lVa1ue soit de type 1cng, peut-tre savez-vous que sa valeur ne peut pas dpasser 100 dans ce programme. Dans ce cas, la conversion de la variable de type long iValue en variable nValue de type int ne poserait aucun
problme.
Vous pouvez dire C# que vous savez ce que vous faites en utilisant un
CqST:
long
1Va1ue
10;
int
nValue;
nValue
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;
Toute conversion de et vers le type decimal ncessite un cast. En fait, tout type numrique peut tre converti en n'importe quel autre type numrique par I'application d'un cast. Ni le type bool ni le type strlng ne peuvent tre convertis directement en un autre type, quel qu'il soit.
Ae/!!\ un caractre ou un type boolen en son quivalent de type string. Par :(dq9 ) vous po,rrr, convertir la valeur de type bool true pour en faire "r"-ple, \/ la valeur string "true", mais on ne peut pas considrer cela comme une
conversion directe. La valeur boolenne choses absolument diffrentes.
1t$!ea.
des
Chapitre 4
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.
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
Signification
prendre le ngatif de la valeur multiplier
I
+
diviser additionner
so ustra i re
- (moins binaire)
%
modulo
La plupart de ces oprateurs sont appels oprateurs binaires, parce qu'ils oprent sur deux valeurs : celle qui se trouve du ct gauche de I'oprateur et celle qui se trouve du ct droit. La seule exception est le moins unaire, mais il est aussi simple que les autres :
n2 a naintenant 1a valeur -5
L'oprateur modulo vous est peut-tre moins familier que les autres. C'est tout simplement le reste d'une division. Ainsi, 5 % 3 vaut 2, et 25 % 3 vaut 1e$!Qa. rqr,/-t \
1(25-3.8).
La dfinition stricte de I'oprateur
7u
'qE,
est : "x = (x I y) * x
Yo
y"
Les oprateurs arithmtiques autres que modulo sont dfinis pour tous les types numriques. L'oprateur modulo n'est pas dfini pour les types en virgule flottante, car une division effectue en virgule flottante n'a pas de reste.
55
intn:5
Est-ce que le programmeur veut dire "multiplier 5 par 3 et ajouter 2", ce qui fait 17, ou bien "multiplier 5 par la somme de 3 et 2", ce qui fait 25 ?
S/ =(
-efr{c j5:
\ $g ,
C# effectue I'excution d'une suite d'oprateurs de gauche droite. Le rsultat de I'exemple ci<lessus est donc I'assignation de la valeur 17 la variable n.
Dans I'exemple suivant, C# dtermine la valeur de n en commenant par diviser 24 par 6, puis en divisant le rsultat de cette opration par 2 (et non en divisant 24 par le rsultat de la division de 6 par 2) :
intn*241612
D'autre part, les oprateurs ont une hirarchie, ou ordre de priorit. C# commence par examiner I'expression, et excute les oprations en commenant par celle qui a le niveau de priorit le plus lev. Dans mes prcdents livres, je me suis donn le plus grand mal pour expliquer la priorit des oprateurs, mais je me suis depuis rendu compte que ce n'tait qu'une perte de temps (et de neurones). Il vaut toujours mieux se dbarrasser de la question de la priorit des oprateurs en utilisant les parenthses.
De cette faon, la valeur de I'expression suivante est claire, quel que soit
int
n = (7
3) * (4 + (6 / 3));
int
n = (7
3)
(4 + 2);
Cela fait, il remonte vers le bloc de parenthses le plus englobant, en valuant chaque bloc I'un aprs I'autre :
int n = 1 *
6;
56
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
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.
sympas 5 7
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;
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
Parmi toutes les additions que I'on peut avoir faire dans un programme, la plus courante consiste ajouter I une variable
n
- n * 1;
:
++n;
Les
/l
incrruente n de I n
de la valeur
58
L'oprateur d'incrmentation est plutt bizarre, mais, croyez-le ou non, C# en a en fait deux:ttn, et n*t. Le premir, **n, estl'oproteur de princrmentution, et le secod, n**, estl'oprateur de postincrmentation. La diffrence est subtile, mais importante.
Souvenez-vous que toute expression a un type et une valeur. Dans I'exemple suivart, *+n et rr** sont tous deux de type int i
int
n=
n;
1;
int int
o
m
++n;
n = Ij. t rt
= n**;
m
Mais quelles sont les valeurs qui en rsultent pour un indice : c'est 1 ou 2.)
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.
Gn
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
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
- est-ce logi4ue .)
retournent une valeur de type bool true ou false (vrai ou faux). Voici un exemple qui fait intervenir une comparaison logique
:
n;
60
Cet exemple assigne la valeur f alse la variable b, car 5 n'est pas plus grand que 6. Les oprateurs de comparaison logique sont dfinis pour tous les types numriques, notamment f 1oat, double, decimal, et char. Tout ce qui suit est licite
:
i_ i b=10M)12M;
;;; ;;;;
Un oprateur de comparaison logique produit toujours un rsultat de type booL. Il n'est pas valide pour une variable de type string (C# offre d'autres moyens de comparer des chanes).
Conparer des nombres en Uirgule flottante : ,^ r--1 I ^r,. -.,-l qu a Ie plus qros r I oar !
Comparer deux nombres en virgule flottante tient parfois un peu du jeu de hasard, et il faut tre trs prudent. Considrez les comparaisons suivantes
:
f.2 =
bool b1 = (3
1/ = tt I 4.
f.I |
3i
* f2)
==
fl;
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
sympas 6 I
devrait tre true si le rsultat du calcul est un peu dcal par rapport la comparaison.
At^tJ\ Pour faire un peu mieux, vous pouvez utiliser de la faon suivante la =HlW ) fonction de valeur absolue pour comparer f 1 et f 2 \/
:
lrpU{a"
Math.Abs(d1
- 3.0 -
dZ)
.00001; //choisissez
le niveau de prcision
rrue dans les deux cas. Vous pouvez utiliser la Epsilon la place de .00001 pour obtenir le niveau de constante Double. prcision le plus lev possible. Epsilon est la plus petite diffrence possible entre deux variables de type double qui ne sont pas rigoureuseCette fonction retourne
ment gales.
bool disposent d'un autre ensemble d'oprateurs logiques, dfinis rien que pour elles, montrs par Ie Tableau 4'3.
Oprateur
!a
false
t rue
a&b
a et b sont
alb a^b
a&&b all
b
true
true
ou b est
true
xor
b)
a et b sont a ou b sont
true true
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
G#
Ces trois oprateurs produisent comme rsultat une valeur logique de type boo1. Les oprateurs &, | , et n existent aussi dans une version que I'on appelle oprateur de bits. Appliqus une variable de type j-nt, ils oprent bit par bit. Ainsi, 6 &3vaut 2 (01102 &00112 donne 0010t, 6 I 3vaut 7 (01102 i 00112 donne 01112), et 6 ^ 3 vaut 5 (01102 ^ 00112 donne 01012). L'arithmtique binaire est une chose extrmement sympathique, mais sort du cadre de cet
'qg,
^dK
ouvrage. Les deux derniers oprateurs logiques sont semblables aux trois premiers, mais prsentent une diffrence subtile avec eux. Considrez
I'exemple suivant
bool b = (boolExpressionl) & (boolExpression2); Dans ce cas, C# value boolExpressiorrl et boolExpression2, en cherchant dterminer si I'une et I'autre sont true pour en dduire la valeur de b. Toutefois, cet effort pourrait tre inutile. Si I'une de ces deux expressions est fa1se, il n'y a aucune raison d'valuer I'autre, car quelle qu'en soit la valeur, le rsultat de I'ensemble sera f alse.
L'oprateur sions :
&&
Dans ce cas, C# value boolExpressionl. Si elle est fa1se, b reoit la valeur f alse, et le programme poursuit son chemin. Si elle est true, C# value boolExpression2 et stocke le rsultat dans b.
\',lix lfej| Y
llu
L'oprateur && utilise ce que I'on appelle une eualuation en court-circuit, car il court-circuite si ncessaire la seconde opration boolenne. L'oprateur I i fonctionne sur le mme principe
bool b = (boolExpressionl)
Si
:
ll
(boolExpression2);
boolExpressionl est true, il n'y a aucune raison d'valuer booiExpression2, car le rsultat sera t rue de toute faon.
63
n=5
*5t
7:
Ma calculatrice me dit que n vaut 32, mais cette expression a aussi un type.
64
G#
La plupart des oprateurs admettent diffrents types. Par exemple, I'oprateur de multiplication :
* long * float
* int * uint
{9---2 1nt
@- -
-) uint
decimal double
Ainsi,2*
type
3 utilise la version
int * int
de I'oprateur
* pour produire 6, de
int.
inltlcite
Connersion de tqpe
Tout cela est trs bien pour multiplier deux int ou deux f loat. Mais qu'arrive-t-il lorsque les deux arguments ne sont pas de mme type ? Par exemple, dans ce cas : int nl = 10:
double d2 = 5.0; double dResult =
nl *
d2;
Tout d'abord, C# ne comporte pas d'opration 1nt * double. Il pourrait se contenter de produire un message d'erreur, mais il essaie plutt de comprendre ce qu'a voulu faire Ie programmeur. C# dispose des versions int * :inr et dou'l1e * ,jouble de la multiplication. Il pourrait convertir d2 en son quivalent int, mais il en rsulterait la perte de la partie dcimale du nombre (ce qui se trouve droite du point dcimal). C# convertit donc en dor-rbie lavariable int n1 t utilise I'opration ciouble * ciouble. C'est ce que I'on appelle une promotion implicite.
Une promotion implicite est implicite parce que C# I'effectue automatiquement, et c'est une ptomotion parce qu'elle transforme une valeur d'un certain type en un type de capacit suprieure. La liste des oprateurs de multiplication donne Ia section prcdente apparat dans I'ordre de promotion croissante de irit dor,rble ou de int decimal.ll n'existe aucune conversion implicite entre les types en virgule flottante et le type iecinal. La conversion d'un type de capacit suprieure tel que double en un type de moindre capacit tel que int s'appelle une rtrogrodotion.
Une promotion est aussi appele cont.tersion uerc le hout, et une rtrogradation conuersion uers le bas.
sympas 6 5
le cast
Vous pouvez toujours changer le type d'une variable d'un type valeur en utilisant I'oprateur cast. Un casf consiste mettre entre parenthses le type dsir et le placer immdiatement avant Ia variable ou I'expression
concerne.
De cette faon, I'expression suivante utilise I'oprateur
int * int
int
n1
1;
- (int)d2;
Le cast de
2 en l
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
Assigner un tqtte
Le mme principe de compatibilit de types s'applique I'oprateur
d'assignation.
66
=f 1| ^f\
\!!_/
En gnral, une incompatibilit de type produisant un message d'erreur a" compilation se produit dans I'oprateur d'assignation, mais pas
10;
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 :
int nl -
1o;
expression2
sympas 67
L'oprateur ternaire est impopulaire pour plusieurs raisons. Tout d'abord, il n'est pas ncessaire. Utiliser le type d'une instruction if (que nous dcrirons au Chapitre 5) a le mme effet et est plus facile comprendre. D'autre part, I'oprateur ternaire donne une vritable expression, quel que soit son degr de ressemblance avec un type ou un autre d'instruction if . Par exemple, les expressions I et 2 doivent tre de mme type. Il en rsulte ceci :
int a:
1;
double b = 0.0:
intnMax=(a)b)?a:b;
Cette instruction ne se compile pas, alors que nMax aurait d recevoir la valeur de a. Comme a et b doivent tre de mme type, a St promu en doubl e pour tre compatible avec b. Le type qui rsulte de ?: est maintenant double, qui doit tre explicitement rtrograd en 1nt pour que I'assignation soit possible :
int a = 1;
double b = 0.0;
int
/I
nMax;
ceei fonctionne
mme
nl{ax
* (int)((a ) b) ? a :
que ceci
b)
nMax=(a)b)?a:(int)b;
Vous aurez rarement I'occasion de voir une utilisation de l'oprateur
//de
ternaire.
Chapitre 5
narnespac
, t,
/Je
prograrnme coru[ence
lcl
static
(
1'utilisateur
Console
tonsole.l,lriteli.ne("1{e11o, " * sNane) ; //attend confirnation de 1'utilisateur Console.I.lriteLine("Appuyez sur Entre pour terminer...")
Console.Read0
;
//tit
70
l l
]
En dehors du fait qu'il prsente quelques aspects fondamentaux de la programmation en C#, ce programme n'a pratiquement aucun intrt. Il
ne fait qu'afficher ce que vous avez entr. On peut imaginer un exemple un peu plus compliqu qui prendrait les donnes saisies par I'utilisateur, ferait quelques calculs avec elles et afficherait un rsultat (sinon pourquoi faire des calculs ?), puis se terminerait. Toutefois, mme un tel programme ne peut avoir qu'une utilit limite. L'une des caractristiques les plus importantes de n'importe quel processeur est sa capacit de prendre des dcisions. Par "prendre des dcisions", je veux dire que le processeur oriente I'excution du programme vers un chemin d'instructions si une certaine condition est vraie, et vers un autre chemin dans le cas contraire. Tout langage de programmation doit comporter cette capacit fondamentale pour contrler le flux d'excution des programmes. Il y a trois types de base d'instructions de contrle de flux : I'instruction if . la boucle. et le saut. L'une des instructions de boucle, foreach, est dcrite au Chapitre 6.
fr \(Ll
1f
if
{
(bool expression)
/l
]
ici si
nrrp
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
7t
I garantir ll si st ir(a()
I
que a
tl
a
. alors,
0;
assigner
l
Ce fragment de code permet de garantir que la variable a est toujours suprieure ou gale zro. L'instruction if dit : "Si a est infrieur 0,
alorsassigner0a."
.rK
=qE,)
exaCtement de la mme manire que "if (expres sion boolenne) { instruction ) ". Le consensus gnral (auquel je souscris) est de toujours utiliser les parenthses. Autrement dit, faitesle.
"if (expression
boolerne)
instruction"
Et si
i'a
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 :
du prineipaL
intrt
;
decinal nlnterestPaid ; mlnterestPaj.d = nPrincipal t (nlnterest / tOO) /1calcu1e naintenant le total -;;i".ip.i-i nnterestPaid ; u;.tni--t;;;i La premire quation multiplie le principal,
mPrincipal, par le taux d'intrt est gnralement pourcentage), pour obtenir I'intrt payer, mf nterestPaid. exprim en L'intrt payer est alors ajout au principal, ce qui donne le nouveau principal, stock dans la variable rnTotal.
d'intrt, mlnteresr (divis par
100, car le taux
Le programme doit tre capable de rpondre presque tout ce qu'un tre humain est capable d'entrer. Par exemple, on ne peut pas accepter un principal ou un intrt ngatif. Le programme Calculatelnterest ci-dessus contient des vrifications pour viter ce genre de choses :
72
_-"-ffi
,
// II ll II II
L
using
System;
nanespace Calculate
Interest
public class
t
Class1
public static
t
decirual nrPrincipal = Convert,ToDecinal(sPrincipal) | 1,,rifia nrrs 1e principal n'est pas ngatif r-- ---o- --r / 1I tnrr]-nclpal \ ^\ u/
l
//demande 1'utilisateur d'entrer le taux d'intrt Console,llrite("Entrez 1e taux d'intrt :") ;
strins sTnterest = Console.Readline0 ; decimal nlnterest = Convert.ToDecinal (slnterest) ; I lvrifie oue 1e taux d'intrt ntest pas ngatif.
if
I
I
(mTntprest
\Ir+.. Lvvv e
0)
;
nlnterest
1
0;
(nlnterest
tOO)
mlnterestPaid;
Console.l,lriteline("Taux d'intrt
.
Console Console
trlriteline ( "Intert pay = rr * mlnterestPaid); = tr * nlotal) ; .1{riteline ( "Total I / attend confirmation de 1'utilisateur Console.Writeline("Appuyez sur Entre pour terniner. . .t')
Console.
llriteline
"%");
Console.Read0;
73
return
0i
Int e rest commence par demander son nom I'utilisateur en utilisant I'instruction WriteLine O pour crire une chane sur la console.
Le programme Ca1 culate
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.
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
Taux
= Principal d'intrt =
rntert pay =
1234
Zlol
1t, 259.14
74
G#
Total
Appuyez
1493.14
Principal
Taux
1234
d'intrt = 0%
Intert pay =
Total
Appuyez
1234
^$$G t
7X t(9, Y
Pour que le source soit plus lisible, mettez en retrait les lignes d'une instruction if. C# ne tient pas compte de la mise en retrait. Beaucoup d'diteurs de code comporte une fonction de mise en retrait automatique : chaque fois que vous tapez la commande 1f , le texte correspondant est mis en retrait automatiquement. Pour activer cette fonction dans Visual Studio, slectionnez Outils/Options, et cliquez sur le dossier diteur de texte. Parmi les sous-dossiers de celui-ci, slectionnez C#, et, dans ce dernier, Tabulations. Dans cette page, slectionnez Mise en retrait Intelligente, et dans la zone Tabulations, spcifiez la taille du retrait que vous voulez (en nombre d'espaces). Pour ce livre, j'ai utilis une mise en retrait de deux espaces.
Certaines fonctions ont besoin de tester des conditions mutuellement exclusives. Par exemple, le segment de code suivant stocke le plus lev de deux nombres, a et b dans la variable max :
l/ stocke dans riax le plus lev de a et b int nax; I I si a est plus grand que b. if(a)b)
t
I
/
= ai
conserr/
conne maxinun
mar
J
Ghapitre
if (a (= b)
I
I
= b;
conserve
conne maxinum
max
if est ici inutile, car les deux conditions sont mutuellement exclusives. Si a est plus grand que b, alors il ne peut pas tre infrieur ou gal b. C'est pour les situations de ce type qu'il y a dans
La seconde instruction
C# une instruction e1se.
Le mot-cl else dfinit un bloc de code qui sera excut si I'expression logique contenue dans I'instruction if n'est pas vraie.
// stocke dans max le plus lev de a et b int max; I I si a est plus grand que b. if(a)b)
t II max
I
e1s e
I
I
= b;
max
J
Si a est plus grand que b, c'est le premier bloc de code qui est excut.
Dans le cas contraire, c'est le second. Au bout du compte, nax contient la valeur du plus grand de a ou b.
*...
et b
76
//
^ ^-! PJ-u --^-J rttp suppose que d L -1,.- l.duu l-- h nec lo
n.c
nax = 8;
I I si ce n'est if(b)a)
ll
max
Il y a des programmeurs qui vitent ce style comme la peste, et je peux les comprendre, mais a ne veut pas dire que je vais faire comme eux. Je me contente de les comprendre. Les deux styles, avec ou sans "e1se", sont couramment utiliss, et vous les rencontrerez souvent.
lnstructions
Le programme
if
inbrques
Caiculatelnterest prvient I'utilisateur en cas d'entre invalide, mais il ne semble pas trs pertinent de poursuivre le calcul de I'intrt si I'une des valeurs est invalide. a ne peut gure tirer consquence ici, parce que le calcul de I'intrt est pratiquement immdiat et parce que I'utilisateur peut en ignorer le rsultat, mais il y a bien des calculs qui sont loin d'tre aussi rapides. De plus, pourquoi demander I'utilisateur un taux d'intrt s'il a dj entr une valeur invalide pour le principal ? ll sait bien que le rsultat du calcul sera invalide, quelle que soit la valeur qu'il saisit maintenant.
Le programme ne devrait donc demander le taux d'intrt I'utilisateur que si la valeur du principal est valide, et n'effectuer Ie calcul de I'intrt que si les deux valeurs sont valides. Pour raliser cela, il vous faut deux instructions 1f . I'une dans I'autre. Une instruction if place dans le corps d'une autre instruction appele une instruction imbrique.
Le programme suivant,
if
est
instructions
if
C a 1 c u 1 at e I nt e r e s t\^li thEmb eci d e dTe s t, utilise des imbriques pour viter les questions inutiles si un problme
I I || II II
I I
intrt
ngatif, alors
programme 7
I
System;
et n'effectue pas le
ca1cu1.
using
t
natuespace CalculatelnterestWithBmbeddedTest
public class
{
Class1
naxinun
int nMaxinunlnterest = 50; //demande 1'utilisateur d'entrer 1e principal initial Console.l^irite("Entrez 1e principal :") ; string sPrincipal = Console.ReadlineO decimal mPrincipal : Convert,ToDecimal(sPrincipal) I lsi Ie principal est ngatif. { u./ 1t lmrrlncr_pal \/ ^\
; ;
IL
l
Console.Write("Entrez 1e taux d'intrt :"); string sTnterest = Console. ReadLine 0 ; decimal nlnterest = Convert.ToDeciraal(sTnterest)
sinon,
demande 1e
taux d'intrt
I lsi
.gnre un autre nessage d'erreur II Console.l,lriteline("Le taux d'intrt doi.t tre positif 'r + "et pas suprieur u + nMaximunlnterest) mlnterest = 0;
l erse
1
deux 'ralides
//p1us lrintrt
decimal mlnterestPaid ; mlnterestPaid * mPrincipal * (nlnterest / 100); /lca1eule naintenant 1e tstal decimal motal * nPrincipaL * mnterestPaid;
/
f I t ar, 1 r. /affiche rsultat
Consol"e.l,lriteline0
; //
skip a line
"%"):
C#
onsole.Writeline("Intrt
Console.l,lriteline
)
('tTotal
de
pay = tr + mlnterestPaid);
=
rt + nlotal)
;
llattend confirmation
Console.Read0;
1'utilisateur
. . ")
;
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
(mlnterest
nl'laxinumlnterest)
Cette expression est vraie si mI nt e r e s t est infrieur zro ou si nlnteresr- est plus grand que nllaximumnterest. Remarquez que j'ai
-r\0.
dclar nl'laxirnurillnteres'1- en haut du programme au lieu de le coder localemenl sous forme de constante.
-a
t\\vl
I/....fil l\rt
L
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
79
Le taux
Appuyez
Principal
Taux
r,
Total
Appuyez
ilyaunprogrs.
Ce qu'il vous faut, c'est un moyen pour I'ordinateur d'excuter plusieurs fois la mme squence d'instructions. C'est ce qu'on appelle une boucle.
80
1
--1-
\-
;l:rie
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
=,:c|,,)
cfq-'.'
Les programrneurs s'expriment souvent de faon un peu bizarre (il sont d'ailleurs bizarres la plupart du temps). Un programmeur pourrait dire qu'une bor-rcle est excute jusqu' ce qu'une certaine condition soit fausse. Pour moi, cela voudrait dire que le contrle passe en dehors de la boucle ds que la condition devient fausse, quel que soit le point o il en est de son excution ce moment-l. Ce n'est videmment pas comrne a que a se passe. l-e programme ne vrifie si la condition est vraie ou non que lorsque le contrle cle I'excution arrive effectivement en haut de la
boucle. Vous pouvez utiliser I'instruction ',;fri i e pour raliser le programme Cari:lll..tie-r.i.:,r ,jl'iab1e., Qui est une version en boucle du programme Ca i c,,r 1 a t e I r i e r e s *.. Ca i c u1 at e 1 i t e r e s t TaL i e calcule une table des valeurs du principal pour chaque anne, en mettant en vidence I'accumulation des ir-rtrts annuels :
/
/ CalculatelnterestTable - ca1cul de f intrt cumul pay sur la base d'un principe dtermin I sur une oriode de olusieurs annes II
I
Ce
urrr JyLcur,
nmpsn2c l.qirrLUPsu!
{
r^rrlatelntereStTable
Q"^+^-. /Lcl4,
programme
8l
public class
{
C1ass1
initial
;
;
1r {mHr1nc1nl \
t
ll
]
sinon,
denande 1e
taux d'intrt
Console.I,irite("Entrez 1e taux d'intrt :"); string sTnterest = Console,Readli-ne0 ; decimal nilnterest = Convert,ToDecimal(slnterest)
gnre un autre message d'erreur IL Console.i,iriteline("Le taux d'intrt doit tre positif")
nnterest = l
e1s e {
0;
vaLides
:")
;
int
("Principal
)
;
une
ligne blanche
nDuration
Console.Writeline("Dure ='tf
(
"ans");
Console.l,,lriteLi.ne
lleffectue
inf
{
nerr
= l'
while(nYear (= nDuration)
deci.mal mlnterestPaid
mlnterestPaid = mPrincipal
* (mlnterest I
100);
82
/lealcu1e naintenant 1e nouveau principal en ajoutant mPrincipal = mPrincipal * nlnterestPaid; //arrondit 1e principal" au centirue le plus proche
//f intrt
au principal prcdent
2)
+ rr - rr + nTPrincipal
l
II
Console.Read0;
Principal
Taux
- 1388.25
2-1561.78
3-1757
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.
83
1ep!Qa"
{dg)
Comme les zros de la partie dcimale ne sont pas affichs, certaines valeurs n'ont qu'un chiffre ou mme aucun aprs la virgule. Ainsi, 72,70 est affich comme 12,7. Vous pouvez y remdier en utilisant les caractres de rnise en forme dcrits au Chapitre 9. Le prograrnme Calcuiar-elnterestTabie commence par lire la valeur du
principal et celle du taux d'intrt entres par I'utilisateur, et par vrifier quelles sont valides. Il lit ensuite le nombre d'annes sur lequel effectuer I'itration. et stocke cette valeur dans la variable nDuration.
Avant d'entrer dans la boucle while, le programme dclare une variable nYear, qu'il initialise la valeur i. Ce sera "l'anne en cours", c'est--dire que cette valeur va changer "chaque anne" chaque boucle successive excute par le programme. Si le numro de I'anne contenu dans n r e a r est infrieur la dure totale contenue dans nDurati on, le principal pour "l'anne en cours" est recalcul en utilisant I'intrt calcul sur la base de "l'anne prcdente". Le principal calcul est affich avec le numro de I'anne correspondante.
=\l\y j \/
Round ( )
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.
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
Calc.rlatelnterestTable
doit tre dclare et initialise avant la boucle while dans laquelle elle est utilise. En outre, I'incrmentation de la variable nYear doit gnralement tre la dernire instruction de la boucle. Comme le montre cet exemple,
84
G#
vous devez prvoir de quelles variables vous aurez besoin. Ce procd vous sera plus facile manier une fois que vous aurez crit quelques milliers de boucls'v,'hi1e, comme moi.
>/
(
\t/
tl ^,,\I
\-----./
Lorsque vous crivez une boucle r'hi 1e, n'oubliez pas d'incrmenter la variable servant de compteur, comme je l'ai fait dans cet exemple :
int
{
nyer = 1;
/ a ^\
instructions,
J'ai omis I'instruction nYear : nYear * 1 ;. Sans I'incrmentation, la valeur de nlear est toujours l, et le programme continue excuter la boucle sans jamais s'arrter. C'est ce qu'on appelle une boucle infinie. La seule manire d'en sortir est d'arrter le programme (ou de redmarrer
I'ordinateur).
.l
Faites attention ce que la condition de sortie de la boucle puisse rellement tre satisfaite. En gnral, il suffit pour cela que la variable compteur soit correctement incrmente. Sans cette prcaution, vous tes bon pour
la boucle infinie et I'utilisateur rancunier. Comme une boucle infinie est une faute assez courante, ne soyez pas trop vex si vous vous y laissez prendre.
Et maintenan\
o...
wh
i 1e
Il existe une variante de while :c'est la boucle dc... whiie. Avec elle, la condition n'est value qu' la fin de la boucle :
int
do
nYear
= J;
instructions.
t
I
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.
85
Hdoutequ'ilyaitbeaucoupdeprogrammeursquisesouviennentseulement
-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. Par exemple, imaginez que vous vouliez rcuprer votre argent la banque ds que le principal dpasse un certain nombre de fois le montant initial, indpendamment du nombre d'annes coules. Vous pouvez facilement rsoudre ce problme en ajoutant ce qui suit dans la boucle
:
1t
t !
(mPrincipal
break;
(maxPorrer
n0riginalPrincipal)
j La commande b reak ne sera excute que lorsque la condition de I'instruction if sera vraie. Dans ce cas, lorsque la valeur calcule du principal sera suprieure maxPower multipli par la valeur initiale du principal. L'excution de la commancle break fait passer le contrle en dehors de la
',.t$f$
f-fiT.X.)
I li'/
ha"
Vous trouverez sur le site Web une version du calcul de table d'intrt qui comporte cette adjonction (il serait un peu long d'en donner le source ici).
Principal
Taux
100
d'intrt = 25i,
= 100 ans
Dure
86
Arrter
1-r25
2-156.25 3-195.31 4-244.14 5-305,1B 6-381.4B 7-476.85 B-596.06 9-745.08
si la valeur initiale
10
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.
// 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
leMo
reFors.ivins
87
//
denande
continue
decinal mPrincipal;
while (true)
{
; ;
;
u)
^\
break ]
I
onro
11n
mpsso
rl'errerrr si
val
Console.l'lriteline
1
//
{
demande
maintenant
decinal nlnterest:
whi.1e
(true)
;
mlnterest : Convert.ToDecirnal (slnterest) ; ll n'accepte pas un taux d'intrt ngatif ou trop grand'.' j.f (mlnterest >= 0 && mlnterest (= nMaxinunlnterest)
{
break; ]
I
Console.l^lriteLine("Le taux d'intrt doit tre positif " * "et pas suprieur " + nMaximumlnterest)
Console,l^lriteLine ("Veui.11ez reconnencer" ) ;
.et
Console,illriteline0;
]
:"
int nDuration = Convert. ToInt32 (sDuration) l/ vrifie la valeur entre Console.Writeline0; l/ crit une I i -.o h l rnnha . '\ Console.l,lriteline("Principal =
; ,' + ' mPrrnnlh srr!frj!rHufl'
I t.
Console
// effectue
88
int nYear = 1; /l i/
ll
l'lnteret
:
I I I'intrt au prcdent principal nPrincipal : mPrincipal * mlnterestPaid; // arrondit 1e principal au centine 1e plus mPrincipal = decinal . Round (mPrincipa! , 2) : I I af.lche 1e rsultat
decinal mlnterestPaid mlnterestPaid = mPrincipal n (mlnterest I t0O); l/ calcule naintenant le nouveau principal en ajoutant
proche
nPrincipal)
1;
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)
{
;
:
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 ( ) ;
]
8g
Cette portion de code reoit une valeur de I'utilisateur I'intrieur d'une boucle. Si la valeur entre est satisfaisante, Ie programme sort de la boucle et poursuit son excution. Si la valeur est incorrecte, un message d'erreur est envoy I'utilisateur, et le contrle repasse au dbut de la boucle de saisie.
|X IICff I \z
Vous pouvez le voir de cette faon : "Le programme ne sort pas de la boucle tant que I'utilisateur n'a pas rpondu correctement."
Remarquez que la condition a t inverse, car il ne s'agit plus qu'une rponse incorrecte produise un message d'erreur, mais qu'une rponse correcte fasse sortir de la boucle. Dans la partie concernant Ia saisie du taux d'intrt, par exemple, le test Principai < 0 | mF rincipal ) nl'laximumlnterest devient mlnterest >: 0 && mlnterest (: nMaximumlnterest. Il est clair que mlnterest ): 0 est le contraire de mlnterest ( 0. Ce qui n'est peuttre pas aussi vident est que le OR | | est remplac par un AND &&. Autrement dit : "Sortir de la boucle si le taux d'intrt est suprieur zro et infrieur au montant maximum."
Dernier point noter: la variable mPrincipai doit tre dclare en dehors de la boucle, pour des questions de rgles sur la porte des variables, que j'expliquerai dans Ia section suivante de ce chapitre.
q/
{f\
Vous allez peut-tre trouver cela vident, mais I'expression true est value
comme true. Par con-cquent, while (true) est I'archtype de la boucle infinie. C'est la commande break qu'elle contient qui fait sortir de la boucle. Aussi, si vous utilisez une boucle while (true), faites particulirement attention ce que la condition de break puisse tre satisfaite.
e,
z<r- \
Veuillez
recommencer
Entrez 1e principal :1000 Entrez 1e taux d'intrt : - l0 Le taux d'intrt doit tre positif et ps suprieur
50
Veuillez
recomnencer
Princinel
000
90
G#
Taux
d'intrt = 10%
=5
ans
Dure
t - 1100
z'!trv
1^i^ ^
3 - 1331
4-1464.r
5-1610.51 Appuyez sur Entre pour
terniner...
m'explique patiemment mon erreur chaque fois. Expliquez toujours exactement son erreur I'utilisateur avant de lui demander nouveau d'entrer une valeur.
hriables
:
Une variable dclare dans le corps d'une boucle n'est dfinie que dans
int
{
nDays
while(nDays
(
.
1;
nDuration)
= n\/c1rro llvoauc / LLUqJ I nllrrr<.D t
instructions
nDays
= nDays
1;
Lavariable nA."'eiage n'est pas dfinie en dehors de la boucle vrhile. Il ya diffrentes raisons cela, mais considrez celle-ci : lors de la premire excution de la boucle, le programme rencontre la dclaration lnt nA,,'ere,ge, et la variable est dfinie. Lors de la seconde excution de la boucle, le programme rencontre nouveau la dclaration de nAve r age. S'il n'y avait pas les rgles de porte des variables, ce serait une erreur, car la variable est dj dfinie.
^2K :(dqf
\/
Il y a d'autres raisons, plus convaincantes que celle-ci, mais je m'en tiendrai l pour le moment.
Il me suffit de clire que la variable nAverag disparalt, aux yeux de C#, ds que le programme atteint I'accolade fermante qui indique la fin de la boucle.
programme
9l
or
wl,ile est la plus simple des structures de boucle cle C#. et la plus utilise aprs f or.
Une boucle f or a la structnre suivante
f.or
{
:
(initflxpressioni condition
I
increnent[xpression)
instructions.
Lorsqu'une boucle
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
instructions.
incrementExDression; l
Un evemlrle de boucle f
d'une boucle f or
:
1/ instructions
a=
1;
C#
92
G#
1)
IL
] I
a = 1, L. ^
corps de
la
boucle
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.
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
Chapitre
93
L'oprateur d'incrmer-rtation est particulirement populaire dans Ies boucles f or fie dcris I'oprateur cl'incrmentation, ainsi que d'autres, au Chapitre 4). Une boucle f or pour notre exemple de calcul des intrts cumuls pourra s'crire ainsi :
for(int
i
I
nYear
= l;
nYear
( nDuration; nYear*i)
.corps de 1a boucle
Hdansunebouclefor,pluttquel'oprateurcleprincrmentation,bienque
I'effet en soit le mme dans ce cas. Il n'y a pas d'autres rai.sons cela que I'habitude et le fait que a a I'air plus cool. (On m'a dit que a marchait trs bien pour briser la glace. Rien n'est moins str, mais vous pouvez toujours essayer d'exiber votre code, tout hasard.)
La boucle f or a aussi une variante clont je ne peux pas faire semblant de comprendre la raison d'tre. Si la condition logique est omise, elle est considre comme vraie. Par consquent, f ol I : ;) produit une boucle
infinie.
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.
for(
t
,condition
\
.)
.)
fnr l'
!vr
,r1tr
.
conditi.on
t
I
corps de 1a boucle
l
l
t(
-.sg4le 1g )
Une boucle incluse dans une autre est entirement excute chaque passage de la boucle qui ia contient'
e4
do
for(
{
.)
] while (
]
.)
I I
ltin lfin
Je ne suis mme pas trs sr de ce que a voudrait dire, mais c'est sans
i'r.?,'r'.
.)
,)
II if (. t
. .
break:
]
] ]
B nais pas de
:, r'g.11.,
boucles.
^v7-y eHq9 \/
1t${a. \
)
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.
programme
I5
Hl
Le saugrenu programme Display'XWithNestedLoo;:s utilise deux boucles imbriques pour afficher un grand X sur la console de I'application :
// I
{
DisplayXWithNestedloops
System;
- utilise
using
nanespace DisplayXWithNestedloops
int nConsolel,lidth = 40; // itre sur 1es lignes du "cadre" for(int nRowNum = 0; nRowNun ( nConsoleWidth: nRovNum {= 2)
t
// itre naintenant sur les colonnes for (int nColumnNun = 0; n0olunnNun ( nConsoleWidth;
nColumnNum#') t I I
char c
I Ie
c
l
IL ='\\':
ll si 7a colonne est du ct oppos de la ligne... int nMirrorColunn = nConsoleWidth - nRovNum; if (nColunnNun == nMirrorColumn)
{
',
'l
c o'/';
I I
Console.l.lriteline 0 l
96
C#
l l
Ce programme commence par dfinir un nombre arbitraire de lignes et de colonnes, reprsentant la taille du X dessiner. Si vous augmentez ce nombre, le X sort de la fentre de I'application.
or pour raliser I'itration sur les lignes du X. I'intrieur de celle-ci, il entre dans une seconde boucle for qui ralise I'itration sur les colonnes de I'affichage. Ce procd dfinit une matrice d'affichage. Il ne reste plus qu' dcider quelles cellules de la matrice recevront un espace, ce qui les rendra invisibles, et lesquelles recevront un caractre. Remplissez les bonnes cellules, et vous aurez un X.
Le programme commence par dfinir une variable c de type char, qu'il initialise avec un espace qui sera sa valeur par clfaut. Il compare ensuite le numro de la ligne et celui de la colonne. S'ils sont gaux, il remplace I'espace par une barre oblique inverse (backslash).
=Qt "t\
:::l(rtllJ"il?'i;Ji'n""
Souvenez-vous que le backslash est utilis pour indiquer les caractres re caractre de nouverre rigne Le caractre
Par lui-mme, le remplacement de I'espace lorsque le numro de la ligne est gal au numro de la colonne trace une ligne du coin suprieur gauche de Ia matrice au coin infrieur droit. Pour obtenir un effet miroir, le prograrnme place une barre oblique ('/') lorsque le numro de la colonne symtrique est gal au numro de la ligne.
Ce qui donne le rsultat suivant
:
97
\ \ \ \
Appuyez
*-o"uli3
f--t-i..(.)
I t\t
voulez tre srieux, allez voir I'exemple DisplaySin, qui utilise le mme genre de logique pour afficher verticalement une ligne sinusoidale dans la fentre de I'application. Je suis peut-tre un excit (et mme certainement), mais j'aime beaucoup ce programme. C'est sur des programmes de ce genre qu'il m'est arriv de me casser les dents.
Si vous
chl
Il vous arrivera souvent de vouloir tester la valeur d'une variable. Par exemple, nl"laritaisiatus pourrait valoir 0 pour signifier "clibataire", 1 pour "mari", 2 pour "divorc", 3 pour "veuf", et 4 pour "c'est pas vos oignons". Afin de reconnatre ces diffrents cas, vous pouvez utiliser une srie d'instructions if :
if (nMaritalStatus =:
t
0)
I ]
e1s e t
if (nMaritalStatus == 1)
I
.utres instructions
Et ainsi de suite.
Vous pouvez vous rendre compte que la rptition de ces instructions if est un peu fastidieuse. Il est si courant d'avoir tester des cas multiples que C# offre une structure spciale pour faire un choix dans un ensemble
98
de conditions mutuellement exclusives. Cette instruction s'appelle switch et fonctionne de la faon suivante :
switch (nMari.talStatus
{
)
case 0:
I
^^^^ 1.
I I I I I
break;
LA l.
break;
case 2:
I
break;
case 3:
I
break;
case 4:
I
break;
default:
//c'est
break; break;
]
I lpasse ici quand aucun cas ne correspond probablement une condition d'erreur
;
L'expression qui se trouve en haut de l'instruction swit,-h est value. Dans ce cas, c'est simplement la variable nflaritaiS ratus" La valeur de cette expression est alors compare celle qui suit chaque mot-cl case. Si elle ne correspond aucun de ces cas, le contrle passe directement la condition,lefault. L'argument de I'instruction switcl'r peut aussi tre de type st string s = "Davis";
switch
{
(
ring
s)
case ttDavistt:
I
I I
"
:
le contrle
1^-^^1,.
cse "Jones
I
99
break;
case "Hvidsten":
tl
instructions
si veuf.
break;
default:
/l
j
o1t^r1n LAJ r.u passe i^.i.rro..l rLr 9UOllU AULUII n.c n anrrecnnnd
break;
,/
L'argument de
s-,,;i
cl'ur-r
ou de type str:iii:.,.
,/
t/ t/
t ch.
Les valeurs de r-S doivent tre des constantes au sens o leur valeur doit tre connue lors de la compilation (une instruction telle que case x st illicite, moins que x ne soit une constante).
t/
Chaque clause cese doit se terminer par une instruction breat", (ou autre commande de sortie dont nous n'avons pas encore parl, comme return). Cette commande de sortie fait sortir le
string s = "Davis";
switch (s)
{
lltait
case "Smith":
I
I I
passe
;
break;
default:
I
b
ici
reak
100
C#
Le modeste goto
Vous pouvez aussi transfrer le contrle d'une manire non structure en utilisant I'instruction goto. Elle est suivie par I'un des lments suivants:
t/ t/ t/
Une tiquette.
tion sw,tch.
Le fragment de code suivant montre comment est utilise I'instruction
gcto
IL
]
I
le contr1e
.
passe de goto
1'tiquette spcifie
goto exitlabel;
1e
r"odc
exitlabel
L'instruction goto est impopulaire, pour les mmes raisons qui en font une commande de contrle si puissante : elle est presque entirement dpourvue de structure. Si vous I'utilisez, il peut tre extrmement difficile de maltriser le flux de I'excution au-del d'un petit morceau de code particulirement trivial.
.$sc ./ t\?, Y
L'utilisation de goto a dclench quasiment des guerres de religion. En tion. En ralit, goto n'est ni si horrible ni ncessaire. Comme vous pourrez presque toujours viter de vous en servir, je vous recommande de vous en tenir bonne distance.
Hfait,lelangageC#lui-mmeatcritiqupouravoiradoptcetteinstruc-
Tioisime partie
Programmation et obiets
'yoil {Trzan en avoir rnrre I Encore mauvais message I Quoi a veut dire zt TarZan tout essayer lTrzan furieux
comrne Cheetah
t
Ghapitre 6
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
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.
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.
tableaux
| 05
public string sModel; public string sManufacturer; public int nNun0fDoors; public int nNum0fWheels;
j
I I // //
I I
non du nod1e non du construcreur nomhrp d nortcs du vhicule nombre de roues du vhicule
La dfinition d'une classe commence par les mots du nom de la classe. dans ce cas. \rehic1e.
Yi
.gtt\
o/
.'-<) \
C# fait la diffrence entre les majuscules et les minuscules dans les noms de classe, comme pour tous les autres noms utiliss en C#. C# n'impose
aucune rgle sur les noms de classe, mais il existe une rgle non officielle selon laquelle le nom d'une classe doit commencer par une majuscule.
Le nom d'une classe est suivi par une accolade ouwante et une accolade fermante. Entre ces deux accolades apparaissent les membre.s que comporte ventuellement cette classe. Les membres d'une classe sont des variables qui en constituent les lments. Dans cet exemple, la classe Vehicie commence par le membre string sModel, qui contient Ie modle du vhicule. Si c'est une voiture particulire, le nom du modle pourrait tre "Eldorado". Vous en voyez certainement tous les jours. Le second membre de notre exemple de classe Vehicle est str:ing sllanufacturer, qui contient naturellement le nom du constructeur. Enfin, les deux dernires proprits sont le nombre de portes et le nombre de roues du vhicule.
Comme pour toute variable, donnez aux membres d'une classe des noms aussi descriptifs que possible. Dans I'exemple ci-dessus, j'ai ajout des commentaires la dclaration de chaque membre, mais ce n'tait pas ncessaire. Le nom de chaque variable dit clairement de quoi il s'agit.
L'attribut publec eui prcde le nom de la classe rend celle-ci universellement accessible en tout endroit du programme. De mme, I'attribut pubiic plac devant le nom d'un membre de la classe le rend tout aussi accessible en tout endroit du programme. On peut galement utiliser d'autres attributs. Le Chapitre 11 traite en dtail la question de I'accessibilit.
106
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.
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.
num
int
nun
h11m. 1,
Chapitre
107
[.a prernire ligne dclare la variable num, et la deuxime ligne stocke dans I'emplacement dfini par la variable nr-iin une constante dj existante de
type int.
mmoire I'objet 1t${Qa^ Il y a en fait une diffrence dans la rnanire de stocker en \ intrinsque nun et I'objet mvCar. La constante 1 n'occupe pas de mmoire, ^.v7q car le CPU et le compilateur C# savent dj I'un et I'autre ce qu'est un "1". N{ais votre CPU ne sait pas ce qu'est un ehicle. L'expression ne,,r -,i ehic Le alloue l'espace mmoire ncessaire la description d'un objet ,,'-.hrcJ e pour le CPU, pour C#, et pour le reste du monde.
'(cg,
1;
e(dw
\.,
Aa,/
1r!!{a"
Toute opration en C# doit tre value par type aussi bien que par myCar est un objet du type ehic ie. La variable ','ei''ic1e. nliuinbe,rOf Doors est cle type int (voyezla dfinition de la classe Vehicle). Comme la constante 5 est aussi de type int, le type de ce qui est droite de l'oprateur d'assignation est le mme que celui de ce qui est gauche.
De mme, le code suivant stocke une rfrence aux chalnes dcrivant le modle et le nom du constructeur de myCar :
nyCar.sManufacturer =
myCar,sModel
= "Izeta";
(L'lzeta tait une petite voiture construite pendant les annes cinquante, dont I'unique porte constituait toute la face avant.)
I 08
,t
tl
Fortran (de la fin des annes cinquante au dbut des annes quatre-vingtl : pas de notion de classe.
C{delafindesannessoixante-dixaudbutdesannesquatre-vingtdix} :lesclasses
ne sont utilises qu' des fins d'organisation. ll est possible d'crire des progrrnmes
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#
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
int
Ghapitre
109
t/ t/ t/ t/
|/ lr"hinlalaraonlrr
"^:-,uDrrr
C.,^+^*. J D LErl,
namespace VehicleDataOnly
t
r
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
I t t
C'est
static
//
I
Vehicle
= new Vehicle0;
une variable pour donner une valeur un membre Console.Write("Notn du rnodle = ") ;
myCar. sModel
// utilise
string s = Console.Readline{); = s;
I I on peut ussi donner une valeur un menbre directenent Console,l,Jrite("Nom du constructeur = tr); myCar.sManufacturer = Console.neadtine0 ;
s = Console.ReadlineO;
I |0
myCar.nNum0fl{hee1s
= Convert.ToInt32 (s) ; affiche maintenant 1es rsultats Console .1,{riteLine (" \nVotre vhicule est une r') ;
/l
/l
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.
Nombre de roues
une
lan
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.
tableaux
I I I
Distinguer
Un constructeur automobile sait identifier sans erreur chaque voiture qu'il produit. De mme, un programme peut crer de nombreux objets de la mme classe :
Vehiele carl = nerr VehicleO;
ear1. sManufacturer
I
= "Studebaker" ;
I
ee qui
suit est
sans
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).
L'oprateur point et I'oprateur d'assignation sont les deux seuls oprateurs dfinis sur les types de rfrence :
I
fait
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
| |2
:(d
ler
C# est capable d'identifier la plupart des tentatives d'utiliser une rfrence non initialise, et d'afficher un avertissement lors de la gnration. Si une telle erreur n'est pas dtecte par le compilateur, tout accs une
I'excution du programme.
La deuxime instruction cre un nouvel objet Vehic 1e, et I'assigne yourCar. La dernire instruction de ce fragment de code assigne la rfrence yor-lrSpousalCar la rfrence yourCar. Comme le montre la Figure 6.1,Ie rsultat de cette instruction est que yourSpousalCar se rfre au mme objet que yourCar.
Figure 6.1
La relation
..--yourcut\
- ren ces rf
au mrre objet.
entre deux
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) :
n3
Vehicle yourSpousalCar = your0ar; ll ryand e11e s'en va avec votre voiture. yourCar = nu1l; I I yourCar rfrence maintenant "1'objet
NULL"
IL
L'excution de ce programme affiche le rsultat "Votre voiture tait une Ford T", bien que la rfrence vou rCar ne soit plus valide.
frg)
qt$!Qa" L'objet n'est plus cccessible partir de la rfrence yourCar. Il ne devient pas compltement inaccessible tant que )'olrrCar t yourSpousallla: ne sont pas "perdus" ou annuls.
Les classes qui contiennent des classes sont les plus heureuses du monde
Les membres d'une classe peuvent eux-mmes tre des rfrences d'ar-rtres classes. Par exemple, un vhicule a un moteur, qui a une puissance et cliff rents paramtres qui dfinissent son efficacit (mais un vlo n'a pas de cylindre). On peut introduire ces diffrents facteurs dans la classe, comme ceci :
-.,L1.: ^ Lr4 ^1^^- \r^L.: ^1e VErlfL PUUTTL
{
public
nrrhl i^ yuvlrL
int
nPower;
licnln.'pmpnt. Urryrq!Lu!rrL,
lnrrhla uvuua
I I // // // //
I I
non du modle nan du constructeur noinhrp d nortes du vhicule nombre de roues du vhicule puissance du moteur (Chevaux-Vapeur) cylindre du moteur (litres)
Toutefois, la puissance et la cylindre du moteur ne rsument pas toutes les caractristiques cle la voiture. Par exemple, la Jeep de mon fils est propose ivec deux moteurs diffrents qui lui donnent une puissance compltement riiffrente. La Jeep de 2,4litres de cylindre est un veau, alors que Ia mme voiture quipe du moteur de 4 litres est plutt nerveuse.
Autrement clit, le moteur est une entit lui seul et mrite sa propre classe
class Motor
f I
n"hlin rL yuVI int L nDnr.rar. lll lMWq!
,
/i
I I tl
/l
Vehicle
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
lcr.ons d'abord un
t'lotor LargerMotor
objet de 1a classe
Motor
"Cherokee Sport";
L'objet de la classe Vehicle vous offre deux moyens d'accder la cylindre de son moteur. Vous pouvez procder une tape la fois :
Motor m = sons0ar.notor; Console,Writeline("La cylindre du noteur est
"*
m.displacenent);
"*
sonsCar.motor.displacernent);
D'une manire ou d'une autre, vous ne pouvez accder la cylindre (displacemenl-) que par I'objet de la classe Motor.
,c$ARG'f
;..
ffi =,
Cet exemple fait partie du programme VehlcleAndMotor QUi se trouve sur le site \\:eb.
tableaux
| |5
nlnc< (ler
//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
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
]
Car
On accde au membre d'objet newCar. sLicensePiare par I'objet newCar, alors qu'on accde au membre de classe (statique) Car. nl'iumloerOfCars par la classe Car.
| |6
class C1ass1
{
// nonbre de jours dans 1'anne public const int nDayslnYear = 366; public static void Main(stringil args)
{
int
{
for(int
/
[]
nMaxTemperatures
index =
0; index (
new
int
[nDaysnYear]
nDayslnYear; index**)
chaque
Vous pouvez utiliser en n'importe quel endroit de votre programme la constante nDavs InYear la place de la valeur 366. L'utilit d'une variable de type const est de remplacer une constante dpourvue de signification telle que 366 par un nom descriptif comme nf avs InYear, ce qui rend le programme plus lisible.
Les
tableauv : la classe
An-,-r-, lf r r d_y
Les variables qui ne contiennent qu'une seule valeur sont bien pratiques, et
les classes qui permettent de dcrire les objets composites sont cruciales. Mais il vous faut aussi une structure capable de contenir un ensemble d'objets de mme type. La classe intgre 'lr ra.,/ est une structure qui peut contenir une srie d'lments de mme type (valeurs de type 1nt, double, et ainsi de suite, ou alors obiets de la classe \iehi,---e, i{or-or:, et ainsi de suite).
n7
propre stockage au format double (calculer une moyenne avec des variables int pourrait produire des erreurs d'arrondi, comme nous I'avons dit au Chapitre 3)
:
double d0 = 5. double dl = ), double d2 = J. double d3 double d4 = 5. d0uble d) * R, double d6 * I' double d7 = Qr double d8 = 1. double d9 = f .
Vous devez maintenant faire la somme de toutes ces valeurs, puis Ia diviser par 10 (le nombre de valeurs) :
double dsun = d0 + dl + d2 + d3 + d4 + d5 + d6 + double dAverage = dSum / 10;
di + d8 + d9;
Il est un peu fastidieux d'crire le nom de chacun de ces lments pour en faire la somme. Passe encore si vous n'avez que dix nombres, mais imaginez que vous en ayez cent ou meme mille.
-$ttK La classe Array prsente une syntaxe spciale qui la rend plus facile -f il ) utiliser. Les doubles crochets ll reprsentent Ia manire qui permet Wt/
d'accder aux diffrents lments du tableau
dArray[0] correspond dArray[1] correspond
d0 d1
:
I |8
1 d1,
Les nurnros cles lments du tableau (0, 1 ,2, eI ainsi de suite) constituent l'index.
#i e,
L'index d'un tableau commence 0, et non l. Par consquent, l'lment du tableau correspondant I'index I n'est pas le premier lment, mais l'lment numro 1, ou "l'lment I de I'index". Le premier lment est l'lment numro 0. Si vous voulez vraiment parler normalement, souvenez-vous que le premier lment est I'index 0, et le deuxime I'index 1.
dAr ray ne constituerait pas une grande amlioration sans la possibilit
une boucle
d'utiliser une variable comme index du tableau. Il est plus facile d'utiliser f o r que de se rfrer manuellement chaque lment, comme le montre le programme suivant :
i
FixedArrayAverage
nombre dternin
utilisant
une boucle
nanespace FixedArrayAvera ge
{ '.^.1 -^ urrr5
C,,^+^-. uJ LIr,
double[] dArray
f5
I
=
JrJ
7
r,
? 5
R 1
1
.,
Jt
?].
du tableau
dSum
0;
for(inti=0;i(11;i++)
dsum ]
dSun
-f dnrray[iJ
return
l
]
]
0;
ng
Le programme commence par initialiser 0 la variable dSum. Il effectue ensuite une boucle sur les valeurs stockes dans dArray, en ajoutant ctracune d'elles clSum. la fin de la boucle, dSun contient la somme de toutes les valeurs du tableau. Celle-ci est alors divise par le nombre d'lments pour obtenir la moyenne. Le rsultat affich par I'excution de ce programme est 4,6, comme on pouvait I'attendre fi'ai vrifi avec ma
calculatrice).
Et si vous dpassez la
Le programme
taille du tableau
FixedArrayAverage effectue une boucle sur un tableau de dix lments. Heureusement, cette boucle passe effectivement sur tous ces dix lments. Mais si j'avais fait une erreur dans l'itration ? ll y a deux cas envisager.
Sije n'avais itr que sur neuf lments, C# ne l'aurait pas considr comme une erreur : s vous voulez lire neuf lments d'un tableau qui en contient dix, de quel droit C# viendrait-il le contester ? Bien sr, la moyenne serait incorrecte, mais le programme n'aurait aucun moyen de le savoir. Etsij'avais itr suronze lments {ou plus} ? Maintenant, a regarde beaucoup C#.C#ne vous permet pas d'indexer au-del de la taille d'un tableau, de crainte d'craser une valeur importante dans la mmoire. Pour le vrifier, j'ai remplac le test de comparaison de la boucle
forpf cequisuit:for(int
i:0; i (
11; i++),enremplaant10par11.L'excution
:
du programme a produit I'erreur suivante {en franais et en anglais dans le texte) Exception non gre : Systen.Index0ut0fRangeException : une exeeption qui de type Systen.Index0ut0fRangeException a t 1eve. at FixedArrayAverage.Classl.Main(StringIJ args) in c : \c#programs\ fixedarrayaverage\class1 . cs: line l7
Au premier abord, ce message d'erreur parat imposant, mais on peut facilement en saisir l'essentiel : il s'est produit une erreur IndexOut0fRangeException. ll est clair que C# indique que le programme a essay d'accder un tableau au-del de ses limites (le onzime lmentd'un tableau qui n'en cornporte que dix). La suite du message indique la ligne exacte laquelle s'est produite cette tentative d'accs, mais vous n'avez pas encore assez avanc dans ce livre pour comprendre tout ce qu'il vous dit.
Chapitre
6:
tableaux
l2 |
for (int i = 0; i (
{
I I dclare un tableau de 1a tail1e correspondante double[] dArray : new doublelnumElenrents]; I I rennf it ie tahleau avec les valeurs
numElements; i++)
// demande 1'utilisateur une nouvelle valeur Console.Write("Entrez la valeur nerr + (1 + 1) + string sVal = Console.ReadLine0; double dValue = Convert.ToDouble(sVa1) ; // stocke 1a nouvelle valeur dans 1e tableau
dArrayIi] =
i
I I
": ");
dValue;
valeurs
double
{
for (int i = 0; i (
]
0;
nunELements; i++)
dsum=dSum*dArrayliJ;
// calcule maintenant 1a moyenne double dAverape = dSum I nunrElements; *"''-*b" // affiche 1es rsultats dans un fornat agrable
Console.i,.iriteline0;
Console
,
l,/rite (dAverage
moyenne
;
de ("
+ dArray [o] )
nuniElements; i++)
Console.l.IriteLine(") I u + numElements); // attend confirmation de 1'utilisateur Console,I,rlriteLine("Appuyez sur Entre pour terminer. ..")
Console.Read0;
return
0;
Voici un exemple de rsultats affichs, pour lequel j'ai entr cinq valeurs, de I 5, dont la moyenne calcule par le programme est de 3 :
Nonbre de valeurs pour 1a moyenne
calculer
Entrez la valeur ne1: I Entrez 1a valeur ne\: 2 Entrez 1a valeur ne3: 3 Entrez la valeur neA: 4
tableaux
123
// derrande 1'utilisateur une nouvelle valeur Console.hlrite("Entrez 1a valeur nett + (i. + t) * string sVal = Console.ReadlineO; double dValue = Double.FromString(sVa1) ; l/ stocke la nouvelle valeur dans 1e tableau
dArrayIi] =
]
dValue;
": ");
Le tableau dAr r a-; St dclar comme ayant une longueur de numElements. L'astucieux programmeur (moi-mme) a donc utilis une
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
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.
tableaux | 2 5
:
lil
dCPn
dMyGPA;
C'est cette merveille que vous pouvez voir dans le programme .Ar,erageStr-rc1er,t,,lFA ci-dessous, qui recueille des informations sur un certain nombre d'tudiants et affiche la moyenne globale des points de leurs units de valeur
:
I I
I I
AverageStudentGPA
("^+^-. uJLrlrl
- calcule
d'UV
(GPA)
',^.i-^ urrr5
nanespace AverageStudentGPA
{
public class
{
Student
sName;
public string
nrrhlin
inrrh'l ^ dp[.
//
moyenne des
points
d'UV
]
^..L1.: ^ ^1^^^ n115S1 PUUATU Ufd Urd
{
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
+(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;
t27
I rTTlt . j,5 uv
Entrez 1e
lllL!4
n-+-^-
Appuyez
La noyenne gnrale des 3 tudiants est 3.5 sur Entre pour terminer...
^$st
1^{t t\7, Y
singulier, cornme s:-dent. D'autre part, il doit inclure le nom de la classe, Comme clans baiSt':iclent ou goocistudeni, ou encore sex,vCoedSt,urlent. Le nom d'un tableau (ou de tout autre collection, vrai dire) doit de prfrence tre au pluriel, comme stuients ou phonelJunbers, ou encore p ho n eli,.irib e i s I nli;". P a 1mP I 1 o i. Comme d'habitude, ces suggestions ne refltent que I'opinion de I'auteur de ce livre et non de l'diteur, encore moins de ses actionnaires. C# ne se proccupe absolument pas de la manire dont vous dfinissez les noms de vos variables.
c el nss Strrdent
cT\^*^ ' uriOlltE, rlflP'
//
movenne
l
n'!hlr^ PUUTTL LrA ^t^dd r'r^dr1 VrdD
I I
du tableau
1fT
I
double
{
for (int i
dSun
= 0.0; = 0; i ( students,Length;
.dgPn;
*= students Ii]
l
double dAvg = dsum/students.Length; II .utilise le tableau.
I 28
La boucle
- o r-
-/ \ ;/ ^..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 l^ rd *^,,^-^^ lruyetllte ^uej
tudiants du tableau
double
{
dSum
= 0.0;
*= stud.
dGPA;
double dAvg
dSur/students.Length;
Lors du premier passage de la boucle, I'instruction r o re;ach va chercher le premier objet Student dans le tableau, et le stocke dans la variable .i stud. 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"
Les programmeurs C, C++ et Java ne se sentiront sans doute pas trs confortables au premier abord avec f oreach, car cette instruction est propre C#. Mais elle a la qualit de grandir en quelque sorte pour vous. Pour accder aux tableaux, c'est la plus facile utiliser de toutes les commandes de boucle.
La structure f oreach est en fait plus puissante que cet exemple ne le laisse paraltre. En dehors des tableaux, elle fonctionne aussi avec d'autres types de collection (les collections sont expliques au Chapitre 16). D'autre part, notre exemple de foreach ignorerait les lments du tableau qui ne seraient pas
frg)
du type Student.
Ghapitre
6:
tableaux I
29
tre dplacs, ni que I'on ne puisse en ajouter ou en supprimer. Par exemple, le fragment de code suivant permute deux lments de Student dans le tableau students :
Student tenp = students[i];
studentsIi]
/l
studentsIj]
studentsljl = tenp; Ici, la rfrence d'objet de I'emplacement i du tableau students est sauvegard, afin qu'elle ne soit pas perdue lorsque la deuxime instruction change la valeur de students Ii] . Enfin, le contenu de la variable temp est stock I'emplacement j. Dans une reprsentation visuelle, cette opration ressemble la Figure 6.2.
Avant:
studentslil
studentsljl
Aprs:
Figure 6.2 "Permuter
:
signifie en
ra lit
deux objets'
"permuter
deux
rfrences
au mme objet".
Le programme suivant donne une dmonstration de la manire d'utiliser la possibilit de manipuler les lments d'un tableau pour effectuer un tri. Cet algorithme de tri s'appelle "en bulles" (bubble sort). Ce n'est pas le
130
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
{
students It] : Student,NenSt,rCent("Lisa", 4.0) ; students IZ] = Student . NewStrrdent ( "Bart" , 2. 0) ; students [3] : Student . Newstudent ("l,{arge" , 3 .0)
;
:
rrl studentsl4i = Stuoeirt.NewStuIsn;("llagg-e", 3.5) // 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)
;
l/
{
in
students)
(
i
Console.llriteline (s.GetString
l l
] i / Student class Student
{ -,.L1.i^ puD-r-LC s Lrrng sflame; ^+-.i-^ ^N )
(nom
et rsultats)
tableaux |
3 |
ll Netrstudent - retourne un nouvel objet Student initialis public static Student Nerustudent(string sName, double dGrade)
{
return student;
l
I
II I UsLLrarr
I r'^+e*-)
LUllvc!
Student
s *=
dGrade; g f= tr - tr'
s f= sName; return s;
]
/l II
{
Sort
- trie
^
{
cet indicateur sera dfini comme vrai si un objet est trouv ma1 class bRepeatloop = false; i/ itre sur 1a liste des tudiants for(int index = 0; index ( (students.Length 1); index++)
| I
{
| I
studentsIindex + 1] ,dGrade)
t
i1s sont
permuts.
studentslindex + 1] = to; II . et f indj.cateur bRepeatloop dit si il faudra I I f.aire encore un passage sur 1a liste des tudiants I I (continue itrer jusqu' ce que tous les objets
lindex]
from;
32
I while (bRepeatloop);
j
Commenons par examiner ce qu'affiche le programrne, rien que pour nous faire une ide :
Avant de
0-
trier
Homer
Tri
en cours
:
4
2
30-
3.5 -
Bart
Honer
o'rr ur Fn+r6a lrrL!s nnrrr Pvu! tarminar LErluJllc!,,.
nnttt'ao nyyuJL
Afin de gagner du temps, le vtre comme le mien, j'ai cod localement la cration de cinq tudiants. La mthode lrlewS*r-udenr O alloue un nouvel objet Student, initialise son nom et son "grade", et retourne le rsultat. Le programme utilise la fonction OutpL,rtS tudentAr ra',1(. ) pour afficher le tableau des tudiants avant qu'il soit tri.
Le programme invoque ensuite la fonction Sort= (.i
.
Aprs le tri, Ie programme rpte le processus d'affichage dans le seul but de vous impressionner avec le rsultat maintenant tri.
str, la principale nouveaut du programme Scrr:Students est la mthode Sort O. Cet algorithme fonctionne en effectuant une boucle continue sur la liste des tudiants jusqu' ce qu'elle soit trie. chaque passage, le programme compare chaque tudiant son voisin. S'ils ne sont pas dans Ie bon ordre, la fonction les permute et met jour un indicateur pour signaler que la liste n'tait pas compltement trie. Les Figures 6.3 6.6 montrent la liste des tudiants aprs chaque passage.
Bien
Chapitre
133
Homer
Figure 6.3
:
a
4
2
3
Lisa
- tri en le
bul I es.
Avant de c0mmencer
Bart
Marge Maggie
Figure 6.4
:
Lisa Bart
Marge
Maggie
4
2
3
passage
Aprs le premier
du
3.5
tri en bulles.
Homer
O <-
Figure 6.5
:
Lisa 4 <Marge
Maggie
3
Lisa reste
tout en haut.
Aprs le
deuxime passage du tri en bulles.
3.5
Bart 2 <Homer A
Figure 6.6
:
Aprs
dernier
I'ava nt-
Lisa
Maooie
4
3.5
trie. Le
passage final met fin au tri en constatant que rien ne change.
|3
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.
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
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
36
h'rhl1^ yuvrrL
rh+
hln+,
()
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)
:
rrn nhiot
membre donne
Exanrple.nStaticlnt = 2i
//utilise
//.,//un
1a classe pour
initialiser
Le fragment de code suivant dfinit et invoque llemberFunction | ) et ClassFunction (1, presque de la mme manire :
Example exanple
I ltttil
icp I'nhict
nnrrr inrrnnrtpr
Exanple. ClassFunction
/ lune fonction membre //utilise 1a classe pour invoquer / lune fonction de classe
L'expression exanl le .l'Ien:^,r:i !,.r:,,:tl lr i' . passe le contrle au code contenu dans la fonction. Le processus suivi par C# pour Exan,ple. ClassFurrcr:ion ( ) est presque identique. L'excution de ce sirnple fragment de code produit
classe |
37
I'affichage suivant I'aide de l'instruction',r''riteLine () contenue dans chaque fonction : ceci est une fonction nembre ceci est une fonction de classe Une fois que la fonction a achev son excution, elle restitue le contrle au point o elle a t invoque.
f >,1
llglf
Lorsquejeclcrisdesfonctions,jemetslesparenthsespourqu'elles
soient plus faciles reconnaltre, sinon j'ai un peu de nral rn'y retrouver.
Ce petit morceau de code C# avec ses deux exemples cle fonctions ne fait rien d'autre qu'afficher deux petites chalnes cle caractres sur la console, rnais urte fonction effectue gnralement des oprations utiles et parfois cornplexes ; par exemple calculer un sinus ou concatner cleux char-res cle caractres, otr encore, envoyer subrepticement par rnail votre {.JI- Microsoft. Vous pouvez faire des fonctictns aussi longues et compliques que vous votrlez.
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
//demande
initial
38
t/
Une section initiale de saisie dans laquelle I'utilisateur entre les donnes, savoir le principal, le taux d'intrt, et la clure. Une section d'affichage des donrres entres, afin que I'utilisateur puisse les vrifier. Une section finale qui calcule et affiche les rsultats.
t/ t/
Ce sont de bons endroits o regarder pour trouver la bonne manire de diviser un programme. En fait, si vous examinez de plus prs la section de saisie de ce programme, vous pouvez voir que c'est essentiellement le
,/
t/ 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 I
CalculatelnterestTablel,lithFunctions
classe | 39
using Systen;
namespace
{
CalculatelnterestTablel,lithFunct ions
C1ass1
public class
{
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
/
(
OutputlnterestTable (nPrincipal, mlnterest, mDuration) ; I I attend confirnation de 1'utilisateur Console.i.trriteline("Appuyez sur Entre pour terminer. . . ")
Console.ReadO; l
r.i+ !a! L1! Uu LIAv IE! f,tr y! neina.l / Inoutlntere.+n.+. - If L q -"-tir du Clavier 1e nr.iarrulyal Lv! v L!q Lq II les informati.ons sur le taux d'intrt ll et 1a dure, ncessaires pour calculer II la table des valeurs futures / / (CeUte fonction inrplmente la Section 1 en 1a divisant
i
. ,
/ len Erots
conposanLs)
nrrhl'in stetin
rroid TnnrrtTntora<tlleiafrof
ryqsq\!
eoinel mPrinninal
I ta -
lecture du principal
;
mPrincipal = InputPositiveDecimal("1e principal") I I 7b - lecture du taux d'intrt mlnterest = InputPositiveDecimal("le taux d'intrt") I I 7e - lecture de la dure mDuration : InputPositiveDecimal ("1a dure") ;
]
lecture d'un nombre dcima1 nositif yvf ufr partir du clavier //(sai'sie du principal, du taux d'intrt ou de La dure
InputPositiveDecimal ---r-,-
// II
dcimaL
et
de
| 40
sPrompt)
I
I t
eont
1'utilisateur entre
while (true)
I Lit
decimal mValue = Convert.ToDecimal (slnput) I I sort de la boucle si la valeur entre est correcte
i,f
(mValue
)=
0)
{-.
// retourne 1a valeur
return
'I
mValue;
^.i -^^--^ pour -.i ^-^ l ^- 1 | ^--^.. Slnon, genere un message ^^,,- slgnaler l'erreur Console.\tlriteline(sPrompt * " doit avoir une valeur positive"); Console,l,{riteline ("Veui11ez reconnencer") ;
/ I/ I
Console.lJriteline0;
l
J
.I
// OutputtnterestTable - partir du principal et du taux d'intrt, gnre 1a table des valeurs futures pour II le nombre de priodes indiques par II mDuration. II / / (cecl inplnent.e la section 3 du progranure) public static void OutputlnterestTable(decinal rnPrincipal,
decimal mTnterest, decimal mDuration)
{
for (int
t
I
nYear
= 1;
// II
* (mlnterest / 100); // calcule naintenant 1e nouveau principal en ajoutant / I 7'intrt au prcdent principal mPrincipal = nPrincipal t mlnterestPj.d; I I arrondit 1e principal au centime 1e plus proche mPrincipal : decinal Round (rnPrincipal , 2) ll affiche 1e rsultat Console.l,rlriteline(nYear + rt-ti * nPrincipal)
mlnterestPaid: mPrincipal
, ; ;
decimal nlnterestPaid
]
I
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
^$tlK i( il ) \g)
Normalement, les commentaires en gras n'auraient rien faire l. Ils ne feraient eu'"n.ombrer inutilement le source. En pratique, de tels commentaires sont inutiles si vos fonctions sont bien conues. La section 1 appelle la fonction inputlnterestData O afin de saisir les trois variables dont le programme a besoin pour calculer les rsultats : mPrinclpal, mlnterest, et mDuration. La section 2 affiche ces trois valeurs de la mme manire que les versions antrieures du programme. La partie finale utilise la fonction OutputlnterestTable O pour afficher les rsultats.
Commenant par le bas et progressant vers le haut, la fonction OutputlnterestTable O effectue une boucle pour calculer les intrts successifs. C'est la mme boucle que celle qui est utilise dans le programme CalculateInterestTable, sans avoir recours une fonction, au Chapitre 5. Mais I'avantage de cette version est que lorsque vous crivez cette section du code, vous n'avez pas besoin de vous proccuper des dtails de la saisie et de la vrification des donnes. En crivant cette fonction, vous avez simplement penser : "tant donnes trois valeurs, le principal, le taux d'intrt et la dure, calculer et afficher la table des intrts." C'est tout. Une fois que vous avez termin, vous pouvez revenir la ligne qui a invoqu la fonction 0rrtnutTnteresf Table O, et continuer
partir de l.
C'est la mme logique de diviser pour rgner qui est l'uvre dans la fonction InputlnterestData O. Vous pouvez vous y concentrer exclusivement sur la saisie des trois valeurs dcimales. Toutefois. dans ce cas. on s'aperoit que la saisie de chaque valeur fait appel aux mmes oprations. La fonction InputPositiveDecimal O rassemble ces oprations dans un bloc de code que vous pouvez appliquer tout aussi bien au principal, au taux d'intrt, et la dure. Cette fonction TnputPositiveDecimal O affiche I'invite qu'elle a reue lorsqu'elle a t invoque, et attend la saisie de I'utilisateur. Puis, si cette valeur n'est pas ngative, elle la retourne au point o elle a t appele. Si la valeur est ngative, la fonction affiche un message d'erreur et demande nouveau la valeur I'utilisateur.
t42
Du point de vue de I'utilisateur. ce programme foncticlnne exactement cle la mme manire que la version nronolitl-riclue clu Chapitre 5, et c'est bien ce que nous vouli<lns :
Entrez 1e principal:100 Entrez le taux d'intrt: - 10 le taux d'intrt doit avoir une valeur nositive
Veuillez
recommencer
10
5-16i.05
6-171.16
7
8-
9-235.8r
1A - 259
.39
Appuyez
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
modeste pourunefonction aussi petite que f rspia,-t-tat jo O, mais unefonction peuttre beaucoup plus grande. En outre, une fonction d'usage courant comme r g peut tre invoque en des centaines d'endroits diffrents d'un mme prggramme.
,,ni
DisplayRatto O chaque fois que ncessaire, pratiquement dans le seul but de mettre plusieurs fois dans le programme le mme bloc de code. L'conomie peut paratre
une fonction
Lorsque le langage F0RTRAN a introduit la notion de fonction dans les annes cinquante, le seuf but en tait d'viter la duplication de code en rassemblant les portions identiques dans un seul f ment commun. f maginez que vous ayez eu crire un programme devant calculer et afficher des ratios en de nombreux endroits diffrents. Votre pr0gramme aurait pu appeler
itelile
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
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.
vnid 0rrtnrt o
|44
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.
public class
{ nrrhl in puurlL
{
Example
rrnir{ vuru f\rrf ntr* llctrin^ vuLyuL \DL!rtl "n^(+ri-^'l tutrLJL!rir/
<f af in LeLfL
"
)
/
.
t
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
string upperString =
Output (upperString)
;
classe I
45
L'assignation de la variable upperString lui fait rfrencer la chalne "Hello". L'invocation Output (upperString) passe la fonction I'objet rfrence par upFerStrrng, eui est notre vieille connaissance "Hello". La Figure 7.1 reprsente ce processus. partir de l, I'effet est le mme.
L'invo cation
- rnnorStri nc
copie la valeur de
da ns
put
Output
(funcString)/
\,/ Y/
------>
"Hello"
Vous pouvez dfinir une fonction comportant plusieurs arguments de divers types. Considrez I'exemple suivant, AverageAr:olrispla,u* O :
*rui3
,.r
AverageAndDisplay
System;
C'
,./
+-l:,;\ I tti I |
using
t
namespace Exanple
l
I I
nom et affiche 1e rsultat public static void AverageAndDisplay(string s1, double d1, string s2, double d2)
I /
AverageAndDisplay
- fait
leur
4aa
4O
double dAverage = (dl + d2) | 2: Console,Writeline("La noyenne de " * s1 f " dont 1a valeur est "
* dl ts2 *"etde" * " dont 1a valeur est " t d2 * rr est gale " + dAverage);
L'excution de ce petit programme produit I'affichage suivant (le saut de ligne ne vient pas du programme) :
La moyenne de UV 1 dont
nnlrroz crrr Fntr6o nnrrr
UV
La fonction ,..erageAndDispi at. ( I est dclare avec plusieurs arguments, dans I'ordre dans lequel ils doivent lui tre passs. Comme d'habitucle, I'excution de notre exemple de programme commence avec la premire instruction qui suit 1'{ain (). La premire ligne qui ne soit pas un commentaire dans i"iain 0 invoque la fonction AverageArrdDisplal'() en lui passant les deux chlnes "UV 1" et "uV 2", et les deux valeurs de type
d,I
e3.5et4.0.
La fonction i..'erageAndltisplai. O calcule la moyenne des deux valeurs double c1i et d2, qui lui ont t passes avec leurs noms respectifs contenus dans sl et s2. et cette movenne est stocke dans dA-,,eraqe.
/l
n'c
namespace AverageWithComp
{
ilerError
ctasse | 47
ll
args)
3.5, 4.0)
..")
nom et affiche 1e rsultat public static void AverageAndDisplay(string s1, double d1, string s2, double d2)
I |
AverageAndDisplay
- fait
leur
double dAverage
= (d1 + d2) I
2;
d1
*s2 *"etde" * " dont 1a valeur est " * d2 * " est ga1e " + dAverage);
l
J I
C# ne peut pas faire correspondre les arguments qui sont passs dans I'appel AverageAndDisplay 0 avec la dfinition de la fonction. La chane "LJV 1"
correspond bien au premier argument qui est de type string dans la dfinition de la fonction, mais pour le deuxime, la dfinition de la fonction demande un type double alors que c'est une chne qui est passe dans I'appel.
Il est facile de voir que j'ai simplement interverti le deuxime et le troisime argument dans I'appel de la fonction. Voil ce que je n'aime pas avec ces ordinateurs : ils prennent littralement tout ce que je leur dis. Je sais bien que je I'ai dit, mais ils pourraient comprendre ce que je voulais dire !
5>}qS, il \ \gt/
Vous pouvez donner le mme nom deux fonctions d'une mme classe, condition que leurs arguments soient diffrents. On appelle a surcharger le nom de la fonction.
t48
I nvE!acnllu!f,yrqjvvrrvqu9u rvOverl o,adpd - LLLE version lltvlr--montr ore cette vsrDrvrr a-^"^-^ ^^ ^-rn"r snl
ro
Arrpr:snn,1T1.i.^l rrvu!u;CnllUUfyroJ
o-'
Ave
rageAndDisplayOver loaded
{ le.gg]
njthltn
{
crec<
// accde la premire fonction membre AverageAndDisplay("mes points d'UV", 3.5, "vos points d'UV", 4.0);
Console.WritelineO;
I I
ArerageAndDisplay
doub:e dAverage
= (dl + d2) I
?.;
Console,WriteLine("La moyenne de
d1);
Console,lrlritel,ine("et de
+^1
d2)
double dAverase
: (Ci + d2) I
2:
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-,
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
^+ L UC VU' PUrrlLb
r ^ -^,,^--^ !o ruvJsrrlr Annlt\to" cltr
r^ "^^ -^i-+^
J'UV dont
ga1e
3,75
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.
comprendre.
"Qt ;:iffili,'il:i:ilJr'"iit
.P\
re
Une autre version de la mme fonction, bien qu'un peu fade, offrirait des performances acceptables, en remplaant certains des arguments par des valeurs par dfaut. La surcharge d'un nom de fonction permet d'implmenter facilement des valeurs par dfaut. Examinez ces deux versions de la fonction L)i spl31-p.eundeCDecina i O
:
// I II
/
FunctionsWithDefaultArguments
- offre des variantes de la mme fonction, certaines avec des arguments par dfaut, en surchargeant 1e nom de 1a fonction
| 50
using
i
/l ll
. Writeline ( " { 0 } ", DisplayRoundedDec inal ( 1 2 . 34567 8i'{, attend confirrnation de 1'utilisateur Console. t^lriteLine ("Appuyez sur. Entre pour terminer , . . " ) ;
Console
Console.Read0; l
I
I
// I
tl
DisplayRoundedDecimal
- convertit
int nNunberOfSignifieantDigits //
commence pas
//
tt
decinal
nNunb erOfS
string s *
reurn s;
]
//
ignif ic antDigits
;
Convertit en chalne
obtenu Convert.?oString(rnRoundedValue)
le rsultat
mValue)
// I
invoque DisplayRoundedDecimal(decinal,
int),
;
string s =
return s;
l l
]
La fonction DlspiayRoundedDecimal (decirnal , int ) convertit en chne la valeur dcimale qui lui est passe, avec le nombre spcifi de chiffres aprs la virgule. Comme les valeurs de type decimal sont trs souvent utilises pour reprsenter des valeurs montaires, on utilise le plus souvent deux chiffres aprs la virgule. Aussi, la fonction DisplayRoundedDecimal (decimal ) fait le mme travail de conversion, mais en utilisant le paramtre par dfaut de detx chiffres aprs la virgule, ce qui vite I'utilisateur d'avoir seulement se demander ce que veut dire le deuxime argument.
t5t
-gU!{c Rernarquez que la version gnrique (declrnai) de cette fonction fait son .-d/ :$: \ travail en appelant la version complte (Cecinal . j nt). Laversion =( lt[ ,) gnrique trouve toute seule les arguments que le programmeur n a pas \St-i envie de chercher clans la documentation.
=oi
*fR.''
Fournir des arguments par dfaut prsente plus d'intrt que d'pargner simplement quelques efforts un programmeur paresseux. Les recherches inutiles dans la documentation pour y trouver Ia signification d'arguments qui sont normalement dfinis par dfaut distraient le programmeur de sa tche principale et la lui rendent plus difficile, lui font perdre clu temps, et augmentent le risque de produire des erreurs. Le programmeur qui est I'auteur d'une fonction comprend les relations entre ses arguments. C'est lui que revient la charge d'en fournir une version simplifie, plus facile utiliser.
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
Contrairement une rfrence un objet, une variable d'un type valeur comme int ou iouble est normalement passe parvaleur, ce qui signifie que c'est la valeur contenue dans la variable qui est passe la fonction, et non la variable elle-mme.
Passer par valeur a pour effet que la modification de la valeur d'une variable d'un type valeur dans une fonction ne change pas la valeur de cette variable dans le programme qui appelle la fonction.
//
{
PassByValue
System;
using
namespace PassByValue
I5
ob
jets
I I Update II
I I ..
- essaie
de modifier 1a valeur
lui sort
passs
C)
doubLe
1 =
/t)'
d = 20.0:
I I dc|are int i = 1;
deux variables
et 1es ini.ti-alise
double d = 2.0; Console.WriteLine("Avant 1'appel 'Jpdate(int, double) :") Console.Writeline(!'i - 'r + i + ", d = " * d); /i invoque 1a fonction
Update(i, d);
ll renarquez que les valeurs 1 et 2.0 n'ont pas chang Console.lrlriteLine ("Aprs 1'appe1 Update (int, double) : ")
Console.Writeline("i = " + j- + 'r, d = " + d); // attend confirmation de 1'utilisateur Console.!'IriteLine("Appuyez sur Entre pour terminer,,.")
Console,
1
Read
i = i, d = 2
Aprs 1'appe1 Update(int, double):
nnrrrroz crrr F'nfra ve nnrrl|orminor
rvu!
L'appel -Lipria"- () passe les valeurs I et 2.0, et non une rfrence aux variables i et C. Aussi, Ia modification de ces valeurs I'intrieur de Ia fonction n'a aucun effet sur Ia valeur des variables dans la rclutine appelant la fonction.
hleur
Il est avantageux de passer par rfrence une fonction un argumeltt d'un type valeur, en particulier lorsque le programme appelant besoin de donner
Ghap itre
53
cette fonction la possibilit de changer la valeur de la variable. Le programme qui suit, PassByReference, met en vidence cette possibilit.
C# donne au programmeur la possibilit cle passer des arguments par rfrence en utilisant les mots-cls ref et cut. C'est ce que montre une petite modification de I'exemple PassByValue de Ia section prcdente.
//
PassByReference
C.,^+ ^* JLc$,
,
_-"-ffi
,, ^.: - ^ urrr
@t
namespace PassByReference
(
L
de nrodifier 1a valeur des argunents qui 1ui sont passs public static void Update(ref int i, out double
I I
I I
Update
- essaie
d)
i = l0; d = 20.0;
]
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) ;
. . ")
Le mot-cl ref indique que c'est une rfrence que C# doit passer i, et non la valeur contenue dans cette variable. Il en rsulte que les modifications apportes cette valeur dans la fonction sont exportes dans le programme qui I'appelle.
De faon similaire, le mot<l out dit "restituer par rfrence, mais peu m'importe quelle tait la valeur initiale, puisque de toute faon je vais la remplacer" (a fait
I5
beaucoup de choses en trois mots). Le mot<l out est applicable lorsque la fonction ne fait que retourner une valeur au programme appelant.
i = 10, d = 20
Appuyez
^--:^
1 L dPvL
^--^r
Notez que les valeurs initiales de i et de d sont crases dans la fonction Update O. De retour dans Main O, ces variables ont reu leur valeur modifie. Comparez cela la fonction Pas sByValue ( ) , dans laquelle les variables ne reoivent pas leur valeur modifie.
Ne passez pas une uariable par rfrence une fonction deux fois en mme temps
Jamais, sous aucun prtexte, vous ne devez passer deux fois par rfrence la mme variable dans un mme appel une fonction. La raison en est plus difficile expliquer qu' montrer. Examinez la fonction Update O suivante :
tt // ^ PassByReferenceError
montre une
quand on
situation drerrur potentielle appelle une fonction avec des arguments passs par rfrence
using
{
System;
namespce PassByReferenceBrror
Update
- essaie
int
nVar2)
" I rrYqrr,/,
tr
I nvrlJ:
nfar} = 20:
'I
et
1es
initialise
Chap itre
// invoque 1a fonction DisplayAndUpdate(ref n, ref n); // remarquez que 1es valeurs 1 et 2.0 n'ont
Console.I,lritelineO;
pas chang
Console.I,iriteLine("Aprs 1'appe1 Update(ref n, ref n):"); Console.Writeline('t1 = " * n) ; // attend confirmation de 1'uti-lisareur Console.WriteLine("Appuyez sur Entre pour terminer. ..") ;
Console.ReadO;
Update i r"i i:ri . ref in: ) est maintenant dclare pour accepter par rfrence deux arguments de type in*-, ce qui, en soi, n'est pas un problme. Le problme se produit lorsque la fonction l"lain O invoque Update O en lui passant la mme variable pour les deux arguments. Dans la fonction, --ipiate ' t modifie rrruiar,, ce qui, par rfrence n, remplace sa valeur initiale de I par la valeur 10. En mme temps, Upclate O mociifie nVar2, alors que la valeur de n, laquelle elle se rfre, a dj t modifie en recevant la valeur 10.
Ce qui est mis en vidence dans I'exemple suivant
Avant 1'appe1 Update(ref
:
n, ref n):
1
n=1
La valeur La valeur
10
n, ref n):
t!
!
n=20
nnrrrran nyyuJ4 arrr u! Fnfpf,6 lltL! nnttr yvur torm"inar L!llrrrl!
df\ =(D
Ce qui se passe exactement au cours de ce petit jeu de scne entre n, nVarl, et nVar2, est peu prs aussi vident que la danse nuptiale d'un oiseau
exotique. Ni I'auteur de la fonction Lpdate O, ni le programmeur qui I'utilise, n'ont prvu ce curieux rsultat. Autrement dit, ne le faites pas.
Il n'y a aucun inconvnient passer une mme valeur plusieurs arguments diffrents dans un meme appel de fonction sitoutes les variables sont passes par valeur.
56
Console.l{riteline("ceci est une erreur " * nVariable) ; nVariable = 1; Console.l{riteline('rnais ceci n'en est pas Itn " * nVariable)
int
nVariable;
Mais C# ne peut pas assurer la surveillance des variables I'intrieur d'une fonction
Console ]
savoirsinVarlable
initialise avantd'tre
:
int
nUninitializedVariable
(
SorneFunet ion
ref
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)
int nlnitializedVariable = 1;
SomeFunction
(out nTnitializedVariable)
LavafeurdenlnitiallzedlariableseracrasedansSomeFunctionO,maisiln'ya
aucun risque que soit passe une valeur dpourvue de sens.
classe I 57
Retourner une
hleur l'erltdteur
Dans bien des oprations clu monde rel, des valeurs sont cres pour tre retournes ;\ celui clui les a envoyes. Par exemple, sir' i' I accepte un argument pour lequel elle calcule la fonction trigonomtrique sinus, et retourne la valeur corresponclante. Une fonction dispose de deux moyens pour retourner une valeur celui qui I'a appele. La plus courante est I'utilisation clu champ r err.l::i, mais ily a une autre mthode qui utilise l'appel par rfrence.
Utilser
une
hleur
L'exemple suivant montre une petite fonction qui retourne la moyenne des arguments qui lui sont passs :
public class
t
double dAverage
(d1 + d2)
z:
return
l
dAverage: 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);
v1
Remarquez tout d'abord que je dclare cette fonction comme pub-iic Cc,ribLe rr,.,ei'ag: i,. Le dciLbie qui prcde le nom signifie que la fonction A.u'er:aBe () retourne une valeur en double prcision celui qui I'a appele. La fonction ur.rage i) attribue les noms d1 et d2 auxvaleurs en double prcision qui lui sont passes. Elle cre une variable oAi e rage laquelle elle assigne la moyenne de di et d2, puis elle retourne au programme appelant la valeur contenue dans dAvei'age.
I5
"(o)
A9L\
Dans ce cas, certains diraient que "la fonction retotrrne ,-,..,,'i :1!,:,". C'est un abus de langage, mais un raccourc'i rl'usagc courant. Dire clur: c,.'r,r-,r-r: ou t<lut autre variable est passe ou retourne r-r 11trr,: ce soit n'a aucun sens. Dans ce cas, c'est la valc'ttr cotttetttte cians i ",r ,,.'.,flui est retourne au programme appelant. L'appel
quel autre appel de fonction. mais la valeur de type . r1-.'.:i':.i;t: i , est stocke clans la variablet I : :., , i,
-",
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 ?
C)
f
! d = rv /t)
t
tt.
vcld, comme si elle ne retournait pas de valeur programme appelant, mais puisque la variable I est dclare r-ef et la au 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
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, :
classe I
59
public class
{
Example d2)
dResults = (dt
] nrrhf in ctatin
{
d2)
I
()
z;
rrnid 'test
v1,
v2)
v1
*ttetdett*v2*"est" f dAverageValue;
C'est le plus souvent par I'instruction retur:n que vous allez retourner une valeur au programme appelant, plutt que par la directive c,,1i:, bien qu'il soit difficile de contester que a revient au mme.
frg)
rloubie ncessite ogq{qa. Utiliser our- avec une variable d'un type valeur comme un procd supplmentaire que I'on appelle boxing, dont la description sort du cadre de ce livre. Toutefois, I'efficacit ne doit pas tre un facteur cl dans votre choix.
C'est typiquement quand une fonction retourne plusieurs valeurs au programme appelant que vous allez utiliser o'L1tr. Par exemple : public class i
Example
AverageAndProduct
dl,
double d2)
dAverage=(dl+62;
dProduct =
]
dl *
l2;
d2;
.r$9- 1 7X tsfl, Y
Une fonction qui retourne elle seule plusieurs valeurs est une crature que I'on ne rencontre pas aussi souve;t qu'on pourrait le croire. Une telle fonction est souvent encapsule dans un obiet de classe ou dans un tableau de valeurs.
I 60
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.
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.
li splayRatlo ( )
Exanple
double dDenoninator)
t
si le
if
t
classe | 6l
I I
Console , WriteLine
I
"Le dnoninateur d'un quotient ne peut pas tre 0"); retourne l"a fonction appelante
return:
l
I I ceci n'est excut que si dDenominator n'est pas double dRatio = dNumerator / dDenominator;
nu1
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
zro,la fonction affiche le ratio. La qui suit immdiatement I'instruction I,,,rriteLlne o parenthse fermante est celle qui indique la fin de la fonction Dispiar,P.atio ( ), donc joue le rle d'une instruction ret l,ir ll.
Si dnenominator rl'st pas gal
class
{
Example
int
I
nValue;
null refl
ll
Example
cre naintenant une rfrence un objet de valeur nu11e ref2 = new 8xample0;
ref2.nValue = 0:
t62
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
string
-qtrino
s1; ? = trlr'
C'est essentiellement la mme chose: s1 pointe vers I'objet nu11, et s2 pointe vers une chane vide, La diffrence est significative, comme fe montre la fonction suivante :
I
I Test -
modules dp
test
norrr
nanespace
{
lest
using Systeu;
int
..i-+
Console.lr'riteline("Ce prograurne uti.lise tr * "1a fonction TestString()',) Console.tlriteLine0; Exanple exanpleObject = new Sxarnple0;
Console.l.IriteLine0;
Console.Writeline0;
// passe naintenant la fonction une chaine vide Console.Hriteline("Passage d'une chane vide :") ; exanpleObject . TestString ( " ") ;
i I enfin, passage d,une vritable chane Console.l,lriteline("passage d'une vritable chane :") exanple0bject.TestString(',chane de test',) ; Console.
Irlriteline
ll t
I
attend confirmation de
()
l,utilisateur
sur Entre,pour terniner. . .,,)
;
ConsoLe.i,lriteline(,,Appuyez
Console. nead return 0;
cfasse | 63
nl acc
Fvamnl o
reurn:
J
// puisque tout va bien, affiche 1a chane Console.iljriteLine("sTest se rfre : "' * sTest + rrIir)
1
'
TestStrlng O utilise la comparaison sTest :: nul-l poursavoirsiune chane apourvaleurnuil.MaistestStringO doitutiliserlafonctionCompareO pourtestersi une chane vide {Compare O retourne un 0 si les deux chanes qui lui sont passes sont
Lafonction
gales).
Ce programme affiche les rsultats suivants
:
vritable chaine
t+an1 LcL qfrjnsl u,!rlrb
Appuyez
t64
La question de Ma i ii a un pr0qramme
s'agit
:
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
args)
programme.
emplacement de
votre
l'lair-' i t est une fonction statique ou une fclnctior-r de classe cle la classe Class -. dfinie par I'Assistant Applications cle \risual Stuclio. ;.1-r ne
retourne aucune valeur et accepte comme arguments un tableau d'objets de type sr r-n,1. Que sont ces chalnes ?
Pour excuter une application console, I'utilisateur entre le nont du programme. Aprs ce nom, il a la possibilit cl'ajouter des argumeltts. C'est ce que vous voyez tout Ie temps, par exenlple avec une conlmtrncle contme oi..)' ri"c nf irlrrei C: \inc,nCL,ts;.rier, Qui copie le fichier ilL.itf ,.:i -er dans le dossier noi,dCrSSier du rpertoire racine du lecteur C. Comme vous pouvez le voir dans I'exemple ,,rl spia.,'Ar gu;f,rrl t.s suivant, le tableau de valeurs de type st ring pass llal n i I constitue les arguments du programme :
/l
DisplayArguments
(rra+^m pJ Lu, '
affiche
au
1es arguments
qui sont
passs
programme
srrr5 ".{-^
namespace DisplayArgument
{ PsuIrL
{
Lr
rcL
//
Console.l,{riteLine("Ce programme
args . Length)
I I les arguments sont int nCount = 0; foreach(string arg in args)
: ;
a {0) arguments"
t65
ncount#, arg)
]
"")
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'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
l'nte de D0S
Pour excuter partir de I'invite de Dos le programme -,isp ial'^:g ln( rrs, suiVez ces tapes :
1.
I 66
respectable antiquit C:
2.
\)
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
Si vous ne le
c : \ C/Programs
\nisplayArguments).
trouvez pas, utilisez Windows pour rechercher le programme. Dans I'Explorateur Windows, cliquez du bouton droit sur le dossier racine C:\, et slectionnez Rechercher comme le montre la Figure 7.2.
Figure7.2: L'utilitaire de
- Windows de
est une aide
recherc he
prcieuse
p0ur
retrouver ses
f
ic h iers.
Dans la bolte de dialogue qui apparat, Entre DisplayArguments.exe, et cliquez sur Rechercher. Le nom du fichier apparalt en haut du volet de droite de la fentre Rsultats de la recherche, comme le montre la Figure 7.3. Ignorezle fichier DisplayArguments. exe qui se trouve dans le dossier obj. Vous aurez peut-tre besoin de faire dfiler horizontalement Ie contenu de la fentre pour voir le chemin d'accs complet au fichier s'il est enfoui profondment dans I'arborescence des dossiers. C'est souvent le cas sivous stockez vos fichiers dans le dossier Mes documents.
ctasse |
67
Visual Studio .NET place normalement les excutables qu'il gnre dans un sous-dossier bir'\'l-.hug,. Mais si vous modifiez la configuration, ce dossier peut tout aussi bien tre bin\r:elease ou un autre.
,;-;;,
Fi,:hier Edrl:rm tfirhaqe Fs'/ils utrl5 |
-li:l
''
Adresse IlJ Re(her.her
;
1..
)
fi:f
t't
@
{". ,']l
Flultill,l
:! - \tf: ] rfrtra! -# [ if i,. ri]jf arls'ban ['t!] r1f:rl:, ',-l:1-. 'r<:{ t.lr, - F\ :,1 ifrrr ' ,t [ ::t l.r Ar ]!ririi :,t I [ b:]
t{ Noveru
,"V
lI
i-rlr tr I ii l.f
U
Figure 7.3
Le voil ! Le nom du
:
lJ.
:r,t r,:r
ichier.
3.
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.
| 68
Figure 7.4 de
L'excution
Argunents
partir de
I invite du
Dlsplay
D0S affiche
les arguments que vous avez passs au programme.
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.
classe
169
lr;---;
I
i-r*n,i;;,*ntt _)
Jirhrrre
K trj,triL -'r I ilr] t:t i'
t-t
P,Fte de tra\.3i1
drer:t iJl-[r,1
[ro55rr
i Ft!fJfr:.ll
r-l-
"&)
F\t.rris iJEu
!,: -
+ J | - *l . l. lttl + I [r:,:]rl:rrlj lrl iii * _J ir,ar:t,rt + I Fr: tr.lr I tE! _,.] Frt'tr :r'li;: ,--#
iJ
rt:
Figure 7.5
Dans
internt E:{plrr
!j'."Dnarrr ,.]ullr,l
Elpfert
fichier
c0rresp0ndant.
|.7;\
;i
Fsvari: r:.eau
l:.:.|:'
Internet Er,k'rer
:rt:'el
lirrL,r';r,-fir'r
-l
ri'lr
4,'.
f,m3frtsr utlok
Windows,
Elpress
VOUS OOUVEZ
faire olisser
et dposer un
c_!l
:rnl'l. rFr ::
[,t::Ft:i'ir
fichier sur un
programme consote pour dclencher
s0n
''
]i
iorbeille
]u1f
|:5.i5r:t
r::E
execuuon.
i
-l
I 70
'--,
-.';
:,
' : Il
.,ri'r..- . :r,,,..
JJ
'l- FTf.drl
l4e5 dc,:umPrrls
)
l-r ,i
,-l
s:1
Pste d trd?il
Adr5ri I
,l
d'un
programme
produit le
mme
l\.-.
t+,
rsultat que
si vous l'aviez
lntrrr;t ExFlor?i
tk,
-'
- l
tsl
rlr :r,nl
passant les
noms des
oa'' . '1ii
Corbeille
+: :
li '
fichiers
correspon dants.
-
ot,iEtl
jl
5lilnnr,iJl
ste\ =(t
Remarquez que Windows passe les fichiers , , s;,ia. .:i-!r1ii?rr:,, dans un ordre quelconque.
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.
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.
2.
Cliquez du bouton droit sur Displar',.r.Linrnr-s, et slectionnez Proprites dans le menu qui apparat, comme le montre la Figure 7.8. Une fentre comme celle de la Figure 7.9 apparalt, montrant beaucoup de proprits du projet avec lesquelles vous pouvez jouer. S'il vous plalt, ne le faites pas.
n!.11!j.,,j,
:
Help
J'
__1
. .-:
l
,*:.i
r,E.ur
- i$
@re
rr!u:'!-,f !a -. i
Elasst.cs
r.i:1rl lllrstErr,
! r. i'r r j -aiLr.U1rrr-::
'1
:J
,,,1 I r,-
:.=-
; t) , r - :'i'ri
ttr rr: : hltTffi iPtSujtgllit*.ilJJli]llr + _J Flrerr:i 'Li. lenettt tj] A:serrl'lr Ir,f :.: l'leferef {l - :ss1.'::: Ajlrllei
- prits pro
r-: r:.r. j , :l: 1: .:!.aa:" .:a rf p l r,tq rur(': '. ,t.i - l . III i: L I rr- I ar :{-r . LEJI h; ; ri": : : :. .a. "l .1 ..1' .'- a 1t1i rril!rlnt = tl: .1r:f ill EJJ
j
de,lerr;rt;le
)
i:,:i1:lr'siIllr:1
Y. 1t'g
net
l:?l
lW.
3.
Dans Ie volet de gauche de la fentre Pages de proprits, sous Ie dossier Proprits de configuration, slectionnez Dbogage.
| 72
Dans le volet de droite, dans la rubrique Options de dmarrage, il a un champ nomm Arquments de la ligne de commande.
x
F_rcfief Edihon Al'l'ichge lr
jet
Ge*rer
Qbcguer
gutil! Feqh*
Help
.+,l-
!
I
-. -a q
aa .'
L1trlrl
' ,:a"* :
Explorateur de soh:tions - Disp{,,.
Classt,cs
4 .X
?f
I
t,isFl
:)
F.h--rj-,tr-d'
'Lr5Flni,Arqrrrnfrt5' l1 Frr,jel
!-0nfi,rurttiDn
---: 8....
tI-,i:,iF"
Flate-forme
, Fr;tr*
'***!
GestionnderonfiE:ratim:.,.
.,
,r:i;
.,..
rl:,r.r
Arguments
de la ligne de
comma n0e de la fentre Pages de
[]enr,rrt r l-lFL
F.rr lP d.,,r,,!,1P
rlr.'r l 'lEt'i,,t3tt
':l:,rer e Jehr,tq
,:tr/Ef l dEt,r!l,l,l
..,
A:F A:,F.llET
n:n
3ra,-tE
_ir
Fil5E
Flse
Fi
rer
F,l15
.l'
EtrG@EGIEE!E@/t'irlrlrrqil
FFerl:Ie J lfd'r]rl T,:rllr:!rj rtlt]tSet InlfTl Erfr rlrE Ttita
proprits,
entrez les a rguments du programme.
1..,
1
,,
Indique ler f'lunrents de l ligne de corrmande appeler lcrs de l'excution du prograrrrnre, oisF,nibl-s lorsque le mlde Dboqage est d#ini Four le d ,,,
t
Fl
f tr- -l
Annul?r
,,
, I tit
4.
Tapez les arguments que vous voulez passer votre programme quand il est lanc par Visual Studio.
Dans la Figure 7.9, ce sont les arguments passs au programme.
/c argl
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...
classe | 73
ttrtils
Feqte
Help
etu,1
Figure 7.10
- argudes
a
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
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 avec ce que l'on appelle une classe Console l
Console.Writeline("ce;:i est un appel de fonction");
| 74
O que vous avez utilise jusqu'ici dans divers exemples est une simple chane. L'oprateur "+" permet aux programmeurs de combiner des chanes, ou de combiner une chane et une variable intrinsque avant que le rsultat de cette opration soit pass Writeline ( ) :
L'argument de la fonction \r,lriteLine
string s =
"Sarh"
COnSOle,Writelin.i/'rTo -rennol1o
r'*
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.
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.
.a-2
IIO
fonction
// ll
i
Pass0bjecr
rrcino Sv<tom'
nmsDc PassOhr'ect
pubiic string
l
nl.hlr^ yuurrL ^ ^^n Lad' t'-SS1 uf,d.
sName;
// dfinit
OutputName I
1e nom en
accdant directement
Console.ldriteLine("La premire
student, sName
fois :")
= "Madeleine";
;
(student)
change 1e nom en
,
Console
SetName
:"
(student, "l{i1la")
OutputName(student);
// vuLPuLrrd.xrtr affiche le nom de 1'tudi"ant ^"+-"+T^-^ public static void OutputName(Student student)
rI
{
est
{0} "
student. sNane)
/l SetName nrodifie le
t
non de
1'objet tudiant
sName)
sNane;
l l
Le programme cre un objet de la classe Studenr, ui ne comporte rien d'autre qu'un nom. Ici, nous aimons la simplicit chez les tudiants. Le programme commence par dfinir directement le nom de l'tudiant, et le
Chapitre
I : Mthodes de classe
t77
passe la fonction d'affichage Out putlJane ( ) , qui affiche alors le nom de tout objet de la classe Student qu'elle reoit. Le programme change alors le nom de l'tudiant en appelant la fonction SetNanie O. Comme en C#, tous les objets sont passs par rfrence, le changement fait str.rclent est rpercut dans la fonction appelante. Lorsque Main O affiche nouveau le norn de l'tudiant, celui-ci a charrg
T ^ *-^-.: -^ !d u!Euf,!g
-^i ^ lurD.
Le non de 1'tudiant est Madeleine Aprs avoir t nodifi. : Le non de 1'tudi-ant est Wil1a Appuyez sur Entre pour terminer..
La fonction SetName ( ) change le nom de l'objet studerrt de la classe Srudenr, t cette modification est reprise par le programme appelant.
il
Pass0bj ectToMetnberFunction
utilise
dans
I'obiet
using
System;
| 78
public class
{
Student
sliame;
public string
/l
t
OutputNane
t0J",
student.sNane);
// SetNanre modifie le
public static void
t
non de
1'objet
student
sName)
SetName(Student
student, string
student,sNane = sNane:
l l
-..11.: ^ LrA PUUITL ^1^-t
Classl
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";
attend, confirmation de 1'utilisateur Console.]rfriteLine("Appuyez sur Entre pour terniner. . . ") Console.Read0;
]
]
l
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
t79
besoin d'afficher l'objet trouvera OutprrtlJane i) avec d'autres fonctions d'affichage cet endroit, car faisant partie de la classe. C'est galement une meilleure solution cl'un point de vrre philosophique. Clas s 1 ne doit pas avoir se proccuper de la rnanire d'initialiser le nom d'un objet Stu,-leni, ni de la manire d'afficher des lrnents importants. C'est la classe Srudent qui cloit contenir ces infornrations.
En fait, ce n'est pas fla 1n "Madeleine". EIIe devrait
(
) clui devrait comrnencer par initialiser le nom plutt appeler pour cela SetNarni: i).
Depuis la classe Stucient, une fonction membre peut en invoquer une autre sans avoir voquer explicitement le nom de la classe. Se ti{ame () peut invoquer t)utputl'larne ( ) sans avoir besoin pour cela de rfrencer le norn de la classe. Si vous omettez celui-ci, C# suppose que la fonction laquelle vous voulez accder appartient la mme classe.
t'Madeleine"
C# vous permet d'invoquer de la mme manire une fonction membre non statique
:
*-f,*i3 l----Hi';'f
I
trY"Y/
//
{
InvokeMethod
using Systen;
namespace InvokeMethod
class Student
t | | te nom de 1'tudiant dcrit 1'objet student pubLic string sFirstName;
I80
ob
jets
sFNane,
string
slName)
sFirstNane slastName
l
: :
sFNane;
slNane:
// II
{
ToNameString
string s = sFirstNane
return s;
)
tr rr + slastName;
Student student = new Student 0 ; student . SetName ("Stephen" , "Davis") ; Console.I,.Iriteline("Le non de 1'tudiant est'r
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.
| 8l
Pour des raisons historiques qui n'ont rien voir avec C#, une fonction membre non statique est communment appele une ntthode. J'utilise le terme ntthode pour une fonction membre non statique, et le terme fonction pour toutes les autres fonctions.
Regardez nouveau la fonction Setf,lame O qui met jour le nom et le prnom dans un objet de la classe Student. Quel objet modifie
Setl{arne
Student christa = nei," Student 0 ; Student sarah = new Student ( ) ; christa. SetName ( uChrista" , "Snith" ) ;
sarah. SetName ("Sarah"
"Jones")
christa.
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 de simples fonctions ne suffiraient-elles pas ? Les mthodes jouent deux rles diffrents mais importants.
La mthode SetName ( ) masque les dtails de la manire dont les noms sont stocks dans la classe Student. Ce sont des informations dont des fonctions extrieures Student ne
sont pas censes avoir besoin. C'est un peu comme la manire dont nous utilisons les boutons d'un four micro-ondes : ces boutons masguent le fonctionnement interne de
l'appareil, que nous n'avons pas besoin de connatre.
Le second rle d'une mthode est de reprsenter les proprits vritables de la classe. Un avion peut acclrer, virer, dcof ler et atterrir (entre autres choses). Une classe Al rplane
complte devrait donc comporter les mthodes Accelerate ( ), Bank ( ). Take0f f ( ), et Land ( ), reproduisant fidlement ces proprits. Mettre la reprsentation d'une classe en ccord avec son quivalent du monde rel permet de penser un programme dans des termes quisont ceux du vritable problme, plutt qu'en un vocabulaire ar-tificiel dict par le langage de programmation utilis.
| 82
nrrhl i
{
n cl ess
Pprson
Console.
l^lriteline ( "Hi" ;
l
nrrhl
t
i,.
r.
l:ss T,etter
sAddress;
sNewAddress)
string
sAddress l l
= sNevAddress;
Toute considration ultrieure sur la mthode.oclrsLr i I est maintenant ambigu. La rnthode Adoress O de Person lt'a rien voir avec la mthode ACdress (l cle l.etter. Si un ami programmeur me dit d'utiliser la mthode .,,idress (,, de quelle Address ( ) parle-t-il ?
Le problme ne vient pas des mthodes elles-mmes mais de ma description. En fait, il n'y a pas de mthode Address O, mais seulement une mthode Person. Ad.lress ( ) et une mthode Letter. Address O. Ajouter le nom de la classe au dbut du nom de la mthode indique clairement de quelle mthode il s'agit.
Cette description est trs semblable la question des noms propres. Dans ma famille, on m'appelle Stephen. Il n'y a pas d'autre Stephen dans ma famille, mais il y en a deux autres l o je travaille.
Si je djeune avec quelques collgues et que les deux autres Stephen ne sont
pas l, il est vident que le nom Sfep hen se rfre moi. Mais de retour dans les bureaux, si vous appelez le nom "Stephen", c'est ambigu car il peut se rfrer n'importe lequel de nous trois. Il vous faudra donc appeler "Stephen Davis" pour viter Ia confusion avec "Stephen Williams" ou "Stephen Leija".
Autrement dit, vous pouvez considrer Address O comme le prnom, ou le surnom. d'une mthode.
I 83
=l ,,1 -t'
AP\ \tt
\ I
Le nom de la classe est un autre moyen de diffrencier cles nom.s cle mthode surchargs, les autres tant les noms et le nombre de ses arguments de tonctron.
Student
llle
.F'i-.+\1-*^.
//
t
f
SetName
met de ct
le
non de 1'tudiant
sFName,
string
slName)
slastName = slNane;
l
l
Student studentl = nevr Student 0 ; studentl . SetNarne ("Joseph" , "Snith") Student student2 - new Student 0 ; student2. SetNarne("John", "Davis") ;
l
]
La fonction Main O utilise la mthode Setllame O pour mettre jour d'abord student 1, puis student2. Mais vous ne voyez de rfrence aucun objet de la classe Student dans Ia mthode SetlJame ( ) elle-mme. En fait, elle ne contient aucune rfrence un objet de la classe Student. Une mthode opre sur "l'objet courant". Comment fait-elle pour savoir quel est I'objet courant ? L'objet courant est pri de se lever.
La rponse est simple. L'objet courant est pass comme argument implicite dans I'appel la mthode. Par exemple
:
studentl
SetNane
("Joseph"
, "Snith") ;
|84
Je ne suis pas en train de dire que vous pouvez invoquer SetNane O de deux
manires diffrentes, mais simplement que les deux appels sont quivalents d'un point de vue smantique. L'objet qui se trouve juste gauche de "." (le premier argument cach) est pass la fonction tout comme les autres arguments. Passer un objet implicitement est facile avaler, mais que diriez-vous d'une rfrence d'une mthode une autre ? public class Student
t
ntrhli^ c*riro cE'i D Llrell PUVTTL L! f rr r r! r"ctNrmo' -,.1^1.i ^ ^+-.:-^ LtArr ^T ^^+[1^-^. !ALrrdrl, PUUaJL
t
slastNarne)
sName)
sFirstNane = l
sNane i
sName)
slastNane = l
]
sName;
Aucun objet n'apparat dans I'appel SetFirstName O. L'objet courant continue tre pass en silence d'un appel de mthode au suivant. Un accs n'importe quel membre depuis une mthode d'objet est cens concerner I'objet courant.
Qu'est-ce que
this
.)
Contrairement la plupart des arguments, toutefois, I'objet courant n'apparat pas dans Ia liste des arguments de la fonction et ne se voit donc pas assigner un nom par le programmeur. Au lieu de cela, C# assigne cet objet le nom this.
Ghapitre
8: Mthodes de classe
| 85
=(d
#\
)
Student
slastName)
sName)
this.sFirstName =
sName;
l public void
{
SetlastNane
(string
sNane)
this.
]
slastName
sName;
Remarquez I'introduction explicite du mot-cl this. Ajouter this aux rfrences au membre n'ajoute en fait rien, car this est implicitement suppos. Toutefois, quand Main ( ) effectue I'appel suivant, this rfrence stud ent I partout dans S erName ( ) et dans toute autre mthode que
Quand
.)
Normalement, il n'est pas ncessaire de se rfrer explicitement this, car il est implicitement compris par Ie compilateur l o il est ncessaire. Toutefois, il y a deux cas assez courants dans lesquels this est ncessaire. Tout d'abord, pour initialiser un membre donne:
1l
t
Address
- dfinit
USA
class
Person
sName;
public string
| 86
rL
rll
rltu
r'n'izl vvlu
s1\ame,
r-nr ntu/
. Yh\
t
L
this.
J
sName
sName;
;
this . nID =
1
nID
Les arguments de la mthode Init O sont nomms sllanc t nIi', ce qui est identique aux noms des membres donne correspondants. Cette disposition rend la fonction facile lire, car elle permet de savoir exactement o est stock quel argument. Le seul problme est que le nom si'i.,Lme dans la liste des arguments rend obscur le nom du membre donne.
L'introduction de this permet de savoir de quel -ql'Jane il s'agit : dans Init O, Ie nom sllame se rfre I'argument de la fonction, alors que thi s . sNane se rfre au membre donne.
Vous aurez galement besoin de this pour mettre de ct l'objet courant afin de I'utiliser ultrieurement ou de le faire utiliser par une autre fonction. Regardez I'exemple suivant, R,e f e r enc i n gTh i sExp 1 i c it i'i:
/ / Rpf ercnni noThi cEynl /
n'i t lrr - n nr -- riogramme montre utiliser explicitement 1a rfrence this
'i
conment
rrs'ino Svctpm'
namespace Ref erenc ingTh j.sExplic t
itly
public class
{
(
C1ass1
510tog1e tu1"/; student.Enro11 ("Biologie 101") /laffichage des cours auxquels est inscrit 1'tudiant Console.Iljriteline("Nouve11es caractristiques de 1'tudiant :")
!^'rr\
student. DisplayCourse
//
Console.
..")
Console.ReadO;
return
Chapitre
I : Mthodes de classe | 87
nID;
;
l/1e cours auquel est inscrit 1'tudiant ll Init - initialise 1'objet student public void Init(string sName, int nID)
{
Courselnstance courselnstance
this.sNane =
this.nID : nID;
]
sName;
courselnstance = nul1;
cours
Init (this ,
sCourseID)
courselnstance.Display ( ) ;
l
// II
Courselnstance
//
{
intitul
du cours
Console.l,lriteline (sCourseID)
l l
tt
| 88
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
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..."
// II
t
I
l'tixingfunctionsAndl'lethods
C,,^+^*. y Lctrl,
- mlanger des fonctions de classe et des mthodes d'objet peut causer des problnes
',^i-^ ufrr6
ic
cl ass Strrdent
student
slastName)
public void InitStudent(string sFirstName, string this. sFirstName = sFirstNane: this. slastNane = slastName I
.I
I/
{
OutputBanner affiche
introduction
Ghapitre 8:Mthodes de
classe
189
I ]
public void
{
// c'est 1a classe Student qui est suppose mais pas a i / 1'objet est pass 1a mthode statique
OutputBanner ( )
;
ll ce n'est pas 1'objet this qui est pass mais 1'objet i/ student courant qui est pass explicitenent
OutputNane ]
(this
//
{
Outputnane
Console.l.]riteLine
1
ll iei,1'objet student est rfrenc explicitement ( "Le non de 1'tudiant est {01 "
,
student . ToNameStrins
t 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
t
C1ass1
nom
Console.
Read
l
]
I
Commencez par le bas, avec |lain () , pour mieux voir le problme. Ce programme commence par crer un objet StLident t initialiser son nom.
| 90
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
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.
- la saise
Visual Studio .NET comporte une fonction de saisie automatique extrmement utile au programmeur. Lorsque vous tapez le nom d'une classe ou d'un objet dans votre code source, Visual Studio utilise les premiers caractres que vous tapez pour anticiper la suite et vous proposer un choix de noms parmi lesquels se trouve celui que vous voulez saisir.
Ghapitre
I : Mthodes de classe
t9t
Cette fonction de saisie automatique est plus facile dcrire par un exemple. J'utiliserai pour cela le fragment suivant du code source du
programme Mixi
ngFrrn c t, i on s Andi'lethod s
la bannire et 1e nom
student . OutputBannerAndName ( ) ;
Figure 8.1
La
fonction
r
:it llrlE 851 ,-rrf
r:
automatique
de Visual Studio est une aide
,l
de saisie
Entr.:
t rlr 1llf
. ''
I
r I
T
e ili 'rl
Q'.\rit:.:
-&
Il me reste complter le nom de la fonction, \ririteli ne. Ds que je tape la parenthse ouvrante, VisualStudio affiche une infobulle indiquant les arguments que comporte la fonction, comme le montre la Figure 8.2. Remarquez
| 92
que cette info-bulle commence par indiquer le numro de la version de la fonction parmi toutes celles qui existent, avec deux flches qui permettent de faire dfiler ces versions pour identifier celle que vous voulez.
Schier
E-ditron
Efqjt
$e{p
14,tt,.
-'lr -+ "|'
a"
!
&*&q4
1&.-
tr _
;l ]
Elassl.cs+
Ji
lt!llr:,rn,tF:nttr'rr:nlt4elh:,l5., lii:1
tlr,trn,i:trn,'t[] ar't:t
:l
{ :J
$ -
5,rlrrl:rr'r'll irr'tFrrr:i.,rsAr'llrElh:,15
VixinqfunctronAndMethods
La fonction
-utomatiq ue
affiche aussi la liste des arguments pour la version de votre choix
de la
. I j I I - .'rrl ir'rs:le
'JlrtrteLirr
istring format,
t,.3f.jnt5
,tt,je':f[] :rl1l
fonction
\,,rriteline
(1.
.($-:--rtC
lsz//
-
Vous n'avez donc pas besoin de taper le nom de la fonction. Imaginez que vous ayez tap Writel pour identifier exactement la mthode voulue. En voyant le nom iir:iteLine slectionn dans la liste, il vous suffit de taper une parenthse ouvrante pour que Visual Studio complte automatiquement ce nom pour vous, aprs quoi, il vous restera taper les paramtres que vous voulez passer, et la parenthse fermante. Pour faire apparaltre la description des arguments de la version de ',,iriteline 0 que vous cherchez, cliquez sur I'une des flches dans I'infobulle qui apparalt lorsque vous tapez la parenthse ouvrante. Dans cette info-bulle, la description du premier argument que vous avez saisir apparalt en gras, comme le montre la Figure 8.2. Aussitt que j'ai entr la chalne "chane", et une virgule. Visual Studio met en gras la description du prochain argument saisir, comme le montre la
Figure 8.3.
| 93
rtcnter
'
toriln t'
{fFichage
lrr,1e!
Help
'
ii4:{ ,ii.t 1*:
&
"&'&
l.& -
'P ;l I
oetu!
Erploraleur ,je ,:lutions -
flasst.cs*
l"liin.,. +
.v.
Cr,t:in strrnll;r,1:i
:J L :J
Figure 8.3
A chaque
Studio
tape, Visual
l^ I lE 1'l - ,/,r I ' ,rfsirl rriillLr.e lilrrrrl ir,fiill:, prdnrs obiect[] argl :- rl,-lrl.t .._]. Irl-L:ltrr.pr.i_rr,-l].J:illt I ;
Ds que vous tapez la virgule qui suit un argument aprs I'avoir saisi, Visual Studio affiche en gras dans I'info-bulle la description du prochain argument
saisir. Bien
str, cette aide est disponible pour toutes les rnthodes intgres de la bibliothque standard C# utilises par votre programme.
"chalne" pour la remplacer intentionnellement par une chalne vide : Console.'ririteLir:e i). Sur la ligne suivante, je tape "student.". Ds que j'ai tap le point, Visual Studio affiche la liste des membres de I'objet strLden.i-, comme Ie montre la Figure 8.4.
Remarquez les icnes qui prcdent les noms des mthodes dans la fentre d'aide : un petit rectangle qui penche vers la droite indiclue un membre donne ; un petit rectangle qui penche vers la gauche indique une mthode.
t94
" .::'
Pfrlel
$nrer
Qeboguer
Qutils
Fe1he
Help
*7*
,"6 "& -
,tr
v, tel:tt_t
EaplorterJr desdulirn:: -
;i
ll:rnsrrrr':[]
:,;:'
:J
)*"
|
lL'li{rr.,.
4 rl
*j ,:l
1ll :;errl,l,'irri: ::
r_!l d;s:1,cr
arlt:
-l:'
:Li::-
1,r,1rl
4 9 4
g
t\\vl
DOUT VOS
eEl,lfir, rr;
/::Lt:ll,llriE S li,fjtrn:,1:rrr,l e T,t5lrrr,l
pr0pres
\z
^r\|' ,-f,
,a
Ces icnes sont faciles reconnaltre. Celle d'un membre donne est en bleu clair. celle d'une mthode est en violet et prcde de trois traits
horizontaux.
Dans la fentre, il y a des mthodes que je ne reconnais pas. Ce sont des mthodes de base que reoivent d'office tous les objets. Dans ce groupe de
mthocles standard. vous voyez notre propre ilutpr-itBannerAn,ilrlane (). Ds que je tape le O, elle est mise en surbrillance, et I'info-bulle apparalt pour en dcrire les arguments, afin que je sache comment I'utiliser.
Encore une fois, il vous suffit alors de taper une parenthse ouvrante pour que le nom de la mthode, pralablement mis en surbrillance dans la liste. soit automatiquement complt. Cette aide marche aussi pour les fonctions. Lorsque j'entre le nom de classe S:,ident suivi par un point, Visual Studio affiche la liste des membres de SruCent. Si je tape ensuite OutputN, Visual Studio affiche I'info-bulle contenant la liste des arguments de Outputllan.e | ), comme le montre la Figure 8.5.
Ghapitre 8: Mthodes de
classe |
95
n hiet
Editin
,t,+J
4a:,,
':.:,..
;t,
',J':"1 :
|
flassl.cs*
I
'le sDlrjhns -
Mr{n,,, 4
-{ -t
Figure 8.5
!;il}:
:
La fonction
I'rrif i :.i:t ii e
aL!{;rl
- utom atiq
a
de saisie
ir,rr-rr 1 E,uu1.
-
( ur:ie
:i i
i,,utt,ult8ar,r,er
ll'ttulea
i,'
trt
Jerlf
Visual Studio ne peut fournir qu'une aicle limite pour les fonctions et les classes cres par I'utilisateur. Par exemple, il ne sait pas ce que fait la mthode Or,itpurlJame O. Heureusement, Visual Studio vous offre un moyen dtourn de dire la fonction de saisie automatique ce que fait la fonction, et mme un peu plus.
Pour indiquer une ligne de commentaire normal, vous utilisez deux barres obliques : / /. Mais Visual Studio comprend aussi comme un commentaire spcial ce qui est indiqu par trois barres obliques : I I I . Un tel commentaire de documentotion permet de donner Visual Studio des informations supplmentaires, utilisables par la fonction de saisie automatique.
t96
S%H\ dispose d'un programme supplmentaire capable d'extraire les commen=\J\y J taires marqus par ces trois barres obliques pour les rassembler dans un Y'/ fichier de documentation spar. C# a apport une amlioration cette innovation : I'aide en cours cl'dition.
Un commentaire de clocumentation petrt contenir n'importe quelle combinaison des contmandes montres par le Tableau 8.1.
1t$!Qa^ Pour tre honnte, c'est le langage Java qui a introduit cette ide.
Java
Signification
Description d'un argument de la fonction, affiche par I aide aprs la saisie du nom de la fonction et de la parenthse ouvrante, expliquant
ce que vous avez saisir.
a
ry>
Description de la fonction elle-mme, affiche en cours d'dition lors de la saisie du nom de la fonction. Description de la valeur retourne par la fonction.
<returns></returns>
une commande commence par ( c onmand ) et se termine par ( / c cmrnand ). En fait, on les appelle ordinairement bolises XML, du fait de leur relation
avec XML.
4i7q \
'Qg,
or$lQa^ Vous disposez de bien d'autres balises XML. Pour en savoir plus leur sujet, consultez I'aide en ligne de Visual Studio (plus officiellement connue sous le nom de MSDN pour Visual Studio) en slectionnant ?/lndex, et tapez "XML" dans le champ Rechercher.
L'exemple suivant est une version commente du programme Mixj-ngFunctionsAndl"{ethod s :
// II
{
I'li"xi.ngFunctionsAndMethods
System;
mthodes
mlanger des fonctions de cl"asse et des d'objet peut causer des problnes
using
namespace MixingFunctionsAndMethods
lll
II
I 26
for (int i = 0; i (
dSum
*=
students
Ii]
dGPA;
]
rlnrrhlo uvuure Tarnfh. dAtro = /(116/o+rr,.lanfa unv6 u!ud/ LUUstlLD,!slrLrl fho error age -;
,
/ 1 nrrtnrrt
Console.l{riteline0
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.
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
public double
i
dGPA;
//
moyenne
Cette classe ne contient rien d'autre que le nom de l'tudiant et la moyenne des points de ses "units de valeur" (ou U\). Je mets "units de valeur" entre guillemets parce que cet exemple (et tous ceux qui suivent jusqu' la fin du chapitre) repose sur le systme universitaire amricain, dans lequel la notion de "grade" correspond trs approximativement nos UV (GPA signifie Grade Point Average, autrement dit, dans notre exemple, moyenne des points d'UV). La ligne suivante dclare un tableau de classe S' u; rir
i
num
Studenl[] students =
nerr/ St.udentfnu:rl;
S/
gs\
/-.)
e,
\
new Student inunl ne dclare pos un tableau d'objets de la classe Student. Cette ligne dclare un tableau de rfrences des objets de la classe Student. Jusqu'ici, chaque lment srudents l1l rfrence I'objet nul1. On pourrait aussi dire qu'aucun des lments du tableau ne pointe vers un objet de Student. Il faut commencer par remplir le tableau, comme ceci :
for (int i = 0; i (
t
students,Length; i++)
students[i] =
new Students0;
t22
Entrez 1a valeur
ne5
3 est 1a noyenne de (1 + 2 + 3 + 4 + 5)
Appuyez
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
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];
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
I I II II II II II
I
',lariableArrayAverage fait 1a noyenne des valeurs d'un tableau dont la taille est dternine par l'utilisateur lors de l'excution. Remplir un tableau avec des valeurs pernet de les rfrencer aussi souvent qu. t'on veut. Dans ce cas, 1e tableau produit un affichage agrable,
namespace VariableArrayAverage
l
t
args)
// II
commence
que
lire
Console.l,lrite("Nombre de valeurs pour 1a moyenne a+riaa .IlmElements : Console.Readline0\ / ;, Lr frr rlr Ic .I\auuarr
: ");
(sNumElements)
Chapitre
I : Mthodes de classe I 97
ll/
{
(lsunnary)
Student
uublic class
| | / (orr-^orrr).
I t' tudiant reoit un nom I (lswnary) oublic strins sFirstName; /// (sumnary) I I I non de famille de 1'tudiant
II II \/UrUrtrdrj/ IJ IllI 1l^.,**^-.,\
oublic strins
slastName;
/l
II
lnitStudent
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)
{
l
/
I M
affi.che f introduction
lll
lll
II
{
(lswnary)
n 1ri 11 a)lltnlrtRennor s usrrr.!
() \ /
nrrhf i n ctati
Console,l,lriteLine("Regardez comme je suis malin :") ; // Console.IdriteLine(? quel objet student utilisons-nous ?);
] /
lll lll
i
0utnrrtBannerAndNane
Gunnary)
lll <l*r*m"rrr)
i
le
non de
nrrh l
0rrtnutBannerAndNane
//
ll //
ce nrest pas 1'objet this qui est pass nais 1'objet student courant qui est pass explicitement OutputName(this, 5);
t98
// 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
199
/l affiche la bannire et le
Student. OutputBanner
nom
string s =
Student.0utputNanre(student,
5);
Console.I^IriteLine0;
// affiche nouveau
I
et
1e
nom
Console
Console,Read
il\,
Les commentaires expliquent la finalit de la fonction, quoi sert chaque argument, le type de donne retourne, et la relation avec une autre fonction. En pratique, les tapes suivantes dcrivent ce qui est affich lorsque je saisis dans Ilain O la fonction -S:udent .0utputNanLe O :
1. Visual Studio
me propose une liste de fonctions. Une fois que j'ai mis en surbrillance celle que je veux, Outputlr'are ( ), Visual Studio en affiche une courte description, extraite de 'surnnai,: comme le montre la Figure 8.6.
?
'
/s,rmnrar_y'),
nnref
EE80n
ffiirha,1e
;,:!?
Piet
i:'; ;.,::,.. t
tlassl,c;*
/lt"t
|
1.
"..
enrer
&*&
Qbrguer n&. /6
Sutili Fettre
Eeh
it.l
|
:tt't:t
'tr -.:srl
Lebu'l
| tt:lnstrrnl[]arqsi
:l
I I
| l:
-J :J
-'.tri,- ' rl r,tts x'-r ', .a il'--f .Hixinqrun(tionrAndMethods + jl ifEr,rt (-| tinrL,t'lni ,:r rjf Clr'l.cs
Figure 8.6
Avec un
document
EN
pr0gramme XML,
C
E
E,rurls uLF|JLEnrnr
FeiErEn,:Eluls
dcrire la fonction
et ses a rguments.
IJ
,t
200
2. 3.
Une fois que j'ai saisi ou slectionn Ie nom cle la fonction, Vi.sual Studio affiche une description clu prernier paramtre, extraite dtr champ (par an)( lpa,,;rm), ainsi clue sorr type.
Bien c1r-r'ils sclient un peu fastidieux saisir, les comrnentaires cle clocumentation rendent les mth<ldes beaucoup plus faciles utiliser.
=Qg,
^"tFJk
I
-
Vous pouvez aussi accder aux proprits clu projet en slectionnant Projet/Proprits.
Slectionnez maintenant Gnrer/Rgnrer tout pour tre bien t gnr correctement.
str
que tout a
Regarclez dans le mme dossier que le fichier source ul asg I . cs (le fichier du projet est dans le mme clossier). Le nouveau fichier xrnl,rrrtf,r-Lt. rrni dcrit toutes les fonctions clocurlentes par les balises XML.
Chapitre
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
Pour d'autres aspects, un lment dfinie par I'utilisateur : string s1 * nev String0; string g2 = "abcd";
string
String s1 = "abcd"; // assigne une chane littrale un objet String string s2 = sl : I I assigne un objet String une variable string
202
Dans cet exemple, s-rl est dclar en tant qu'objet de la classe )^rring (avec un 5 tnajr-tscule), alors que s2 est clclar en tant que variable cle type s trr rrg (avec un.s minuscule). Mais ces deux assignatiorls montrent que str j rg et :i tr ing sont de mme type (autrernent dit, compatibles).
7^H\ =(,l\y / \/
.t9!I{0?r
En fait, cette proprit est galernent vraie pour les autres types de variable, mais clans Lrne mesure plus limite. Mme le type int- possde sa classe corresponclante, Ir,t,l2, douf-,ie correspond la classe t-)oub1e, et ainsi cle suite. La cliffrence est que ritrirrg et String, sont rellement une seule et mme chose.
sNarne);
rinu qui fournit cet oprateurspcial, mais elle offre galement cl'trutre.s rnthodes, plus clirectes, pour manipuler les chalnes.
C'est laclasse 5jt
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".
203
nar la claSSe
I II II ll
I
'.^.i-^ uarr C.'^+^!jLslu,
'
qui a t convertj.e)
namespace Example
i
LfdS I t urdnl
objet student si = new Student0; s1. sName = "Jenny"; I I cre maintenant un nouvel objet
ct.e un
ll
Student
avec
le
nme nom
= sl.
sName;
l "changer" le nom de 1'objet sl ne change pas I I I'obiet 1ui-mme, parce que ToUpper0 retourne l/ une nouvelle chane sans modifier 1'original
l
s2.sNarne
ll Student class
I (
Student
sName;
public String
]
l
]
Les objets Str-Lcli-rt :r, et s2 sont dfinis de telle manire que leur membre donne sfJarae pointe vers la mme chalne. L'appel la mthode ToUpper ( ) convertit la chalne s l. sr'lane pour la mettre entirernent en majuscules. Normalement, cela devrait poser un problme, car s I et s 2 pointent tous deux vers le meme objet, mais TcLIi per ( ) ne modifie pas sName : elle cre une nouvelle chalne er] maiuscules.
204
sl'Jenny
Appuyez
s2 -
JENNY
=(f
7^[ \
19!llQp" L'invariabilit des chalnes est galentent importante pour les constantes de
type
s Lr
V \-l /
i ng. Une cirane conlrne "c]eci est une chalne" est une forme cle constante cle type ::t r iirg. tout cornme I est une constante cle type j nt. De la tnlne matrire clrre je rre jette pas mes chemises aprs usage pour rcluire le volume cle ma garde-robe, un cornpilateur peut choisir de combiner tou.s les accs la mrne constatrte "ceci est une c:ha1ne". Le principe cle rutilisation d'une constartte cle type chalne permet cle rcluire la taille cl'un prograrnrle, mais il serait irnpossible si un objet de type:;rring pouvait tre moclifi.
tl tl
retourne
.Si
1.
retourne
t/
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
ll lous
si
1a chane
s1
205
I I est plus longue, alors e11e est plus grande si s1 contient encore des caractres
I si s2 est plus longue, alors e11e est plus grande si s2 contient encore des caractres
I
return
return
I
-1
I si tous les caractres correspondnt et si les deux chaines l l ont 1a mnre longueur, alors e11es sont "gales"
return
]
0
Ainsi, "abcd" est plus grand que "abbd", et "abcde" est plus grand que "abcd". Vous n'aurez pas besoin tous les jours de savoir si une chane est plus grande qu'une autre, mais il vous arrivera d'avoir besoin de savoir si deux chalnes sont gales.
Conpare O retourne 0 lorsque les deux chanes sont gales. Le programme de test suivant utilise cette caractristique de Compare O pour effectuer une certaine opration quand il rencontre une ou des chalne(s) particulire(s).
Eui ldAsentence demande I'utilisateur d'entrer des li.enes de texte. Chaque ligne est concatne avec la prcdente pour former une phrase, jusqu' ce que I'utilisateur entre les mots EXIT, exit, QILII ou quit : La- L-|ffi r I r,in
suivant construit
des phrases en concatnant 1es saisies de l'utilisateur, jusqu' ce que celui-ci entre 1'un des caractres de fin ce progranne donne un exenple de la ncessit de vrifier si deux ehanes sont ga1es
using
System;
namespac
e BuildASentence
C1ass1
publi.c class
{
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)
206
for(;;)
t
I I Lit 1a saisie suivante Console.Writeline("Entrez une chane") ; string sLine = Console.ReadLine0; ll sort de 1a boucle si c'est une chane de if (IsTerminateString (sLine) )
{
fin
break;
]
sSentence
// sinon, ajoute 1a phrase 1a chane saisie : String.Concat(sSentence, sline) ; I I dit 1'utilisateur o i-l en esr
;
, . ")
Console.Read0;
// IsTerminatestring - retourne true si 1a chane source ll est gale l'une des chanes de fin public static bool IsTerninateString(string source)
{
"QIJTT"
r
qUit,, l
It
foreach(string sTerm
{
in
sTerms)
if
{ )
/1 retourn true si
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
207
Les structures while (true) et f or (; ;) produisent une boucle sans fin, c'est--dire aussi longtemps qu'un break ou retur:n interne n'en fait pas sortir. Les deux boucles sont quivalentes, et dans la pratique vous rencontrerez les deux.
BuildAsentence demande I'utilisateur d'entrer une ligne de texte. qu'il lit avec la mthode Readline (). Puis il vrifie si la chalne entre est otr non le signal convenu pour la fin, en utilisant la chalne cre localement, TsTerminateString O. Cette fonction retourne true si sline est I'une des chalnes convenues pour la fin, et f alse dans le cas contraire.
b'
4f\ =)
Par convention, le nom d'une fonction qui teste une proprit et retourne true ou f aise doit commencer par Is. Dans notre exemple, le nom de la fonction I sTerminateString ( ) signifie la question : "sLine est-elle une chalne de fin ?" Bien str, ce n'est l qu'une convention humaine. Elle ne
sline n'est pas I'une des chalnes de fin, elle est concatne avec la partie de la phrase dj saisie, au moyen de la fonction St ring . Cciic a: Le programme affiche immdiatement le rsultat, afin que I'utilisateur sache o il en est.
Si La mthode
()
IsTernrnateString O clfinit un tableau de chalnes sTe:ns, dont chaque membre est I'une des chalnes de fin. Si la chalne teste est gale I'une des chalnes de ce tableau, cette mthode retourne true, ce qui conduit le programme s'arrter plus vite qu'un programmeur oblig crire en COBOL.
Le programme doit prendre en compte "EXIT" et "exit", car Conpare o considre par dfaut ces deux chalnes comme diffrentes. ( la manire dont le programme est crit, il ne connalt que deux manires d'crire exrr. Une chalne telle que "Exit" ou "eXit" ne serait pas reconnue comme chalne de fin.) La fonction IsTerminateString O effectue une boucle pour chacune des chalnes du tableau des chalnes de fin. Si Compare O retourne que la chalne teste est gale I'une des chalnes de fin du tableau, la fonction IsTerminateString O retourn true. Si aucune galit n'a t trouve la fin de la boucle, la fonction IsTerminateStrine () retourne false.
:t\vl
lforl
l-t
ff-^l
jL'itrationSuruntableauestuntrsbonmoyendetestersiunevariable
correspond une valeur parmi plusieurs.
.a
208
Vous avez
entr :
Progranrner avec
C/l
, crest
amusant
Vous avez
entr :
uroins)
Programmer avec
C1,
c'est
amusant
(plus ou
Vous avez
EXIT
moins)
Phrrso
[yHeJ
nnmnT
tp
Programner avec
Annrrrroz v! crrr
F'nfro
et "exit" comme cles chalnes diffrentes. Mais il existe une autre version surcharge de cette fonction qui comporte un troisime argument. Celui-ci indique si la comparaison doit ou non faire la diffrence entre les majuscules et les minuscules. L'argument Lr ue indique d'ignorer Ia diffrence.
Laversion suivante de IsTermirrateStringO retourne trlle si lachane qui lui est passe correspond une chalne de fin, qu'elle soit en majuscules, en minuscules ou dans n'importe quelle combinaison des deux.
//
t
ll est
Iserminatestring - retourne true si 1a chane source string ga1e l'une des chaines de fin
is
equal
nrrhl'in
sOurCe)
quit,
sans
tenir
conpte
Chapitre
209
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
Et s je tleux utlser
a- '-i bW-L L
-l-
ch
.)
Pour tester si une chalne est gale une valeur particulire. vous pouvez aussi utiliser la structure :;-",,;ri-,ri , r.
51}qS. ( il ) \U /
En gnral, on se sert de la structure s-,n 1'- r;11 r r pour comparer une variable utilise comme compteur un ensemble de valeurs possibles, mais cette structure fonctionne atrssi sur cles chalnes. La version suivante cle ,
sT-ollilaie:,-. r irr-
r..'
// II
{
IsTerminateString - retourne true si 1a chane source est ga1e l'une des chanes de fin
source)
case I'EXfT":
c
as
e ttexit
"
^a^ Lqc
il^ITT11||. qurr
case t'quit":
return true;
]
return false:
]
l Cette approche fonctionne parce que vous ne comparezici qu'un nombre limit de chalnes. Une boucle f or O offre un moyen beaucoup plus souple de rechercher des valeurs de type chalne. La version de Compare () qui ignore la distinction entre majuscules et minuscules donne au programme une plus grande souplesse.
2l 0
L'analyse des caractres que contient une chalne est aussi un sujet que je n'aime pas voquer, cle crainte que les programmeurs n'abusent de cette technique. Il arrive que les programmeurs aillent un peu trop vite sauter sur une chalne avant qu'elle soit entirement saisie pour en extraire ce qu'ils y trouvent. C'est particulirement vrai des programmeurs f ++, cr jusqu' I'introduction d'une classe de chalnes, c'tait la seule manire dont ils pouvaient manipuler les chanes.
structttre f r.-,r'each ou I'oprateur inder [], un programme peut lire une chalne conlme si c'tait un tableau de caractres.
En utilisant la
5lttrS, Bien str, une chalne n'est pas simplement un tableau cle caractres. Si on i( il ) n" peut lire une chalne qu'un caractre la fois, on ne peut pas l'crire de \ ln ./ la mme manire.
L'exemple sirnple clu programme St r in de cette technique
,
gToCh
rAc
ces
s montre l'utilisation
lt
^ StrlngloUnarAccess
using Systen;
namespace StringToCharAccess
{
I r . I ^l public class Classl t
L
t
I
I lit
:" f
sRandon);
// affiche maintenant sous forme de suite de caractres l,Irite ( "Votre saisie affiche en utilisant foreach foreach(char c in sRandom)
:"
2n
Console.Write(c);
]
suite de caractres
for(int i = 0; i (
r t
trois manires diffrentes une chalne saisie au hasard par I'utilisateur. Il commence par I'afficher en utilisant la mthode habituelle l,irir-el.ine (string), puis il I'affiche en utilisant la structure f oreach pour en extraire chaque caractre I'un aprs I'autre, et enfin. il se sert de I'index d'un tableau avec [] pour faire la mme chose.
Ce programme affiche de Ce qui donne le rsultat suivant
:
Entrez au hasard une chane de caractres (attention Stephen Davis est un beau garon
: au hasard)
Votre saisie
conme
Votre saisie affiche en utilisant foreach : Stephen Davj.s est un beau garon Votre saisie affiche en utilisant for : Stephen Davis est un beau garon sur Enrre pour terminer...
Appuyez
On ne se lasse pas d'une vrit. Dans certains cas, vous ne voudrez pas avoir un caractre non imorimable une extrmit ou I'autre de la chalne. Un coractre non imprimoble est un caractre qui n'est pas normalement affich l'cran : un espace, une nouvelle ligne, une tabulation, et quelques autres.
2I2
Pour purer de ces caractres les extrmits de Ia chalne, vous pouvez utiliser la mthode Trim O :
ll
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.
string s = Console.Readline0;
(s)
Les autres mthodes de conversion portent des noms plus vidents ToDouble ( ). ToFloat ( ). et ToBoolean ( ).
To I
nt 3 2 ( ) se rfre un entier sign de 32 bits (32 bits est la longueur d'un int normal). ToInt64O correspond un long (qui fait 64 bits). Lorsque Corvert O rencontre un type de caractre inattendu, il peut produire un rsultat inattendu. Vous devez donc tre str du type de donne que vous tes en train de manier.
La fonction suivante retourne true si la chane qui lui est passe n'est constitue que de chiffres. Vous pouvez appeler cette fonction avant de convertir la chalne en un type entier. Si une chalne de caractre n'est constitue que de chiffres, il y a des chances que ce soit un nombre licite.
=( ^' \r'r
Atry( \?_/
\/
Pour une variable en virgule flottante, il serait ncessaire de prvoir la virgule, ainsi que le signe moins pour les nombres ngatifs. Ne vous , laissez Pas surPrendre.
2|3
de
la
chane
// r.ommpne nar se dbarragser des caractres inutiles /l chaque extrnit ; s'iL ne reste rien, // c'est que la chane nrest pas un nonbre string s = sRaw.Trim0; i / supprime 1es espaces aux extrmits if (s.Length == 0)
t
return false;
l
//
i
for(int
|
ll si ce nrest pas un chiffre, c'est que la | n'est sans doute pas un nonbre if (Char. IsDigit (s Ij.ndex] ) == false)
{
return false;
l
l
I
nombre
return true:
l
non rien, c'est que 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(
.
t]
).
using
t
namespace Exanple
class Classl
{
public static
{
int Main(string[]
args)
2I4
I I lit une chane saisie au clavier Console.Writeline("Entrez un nonbre entier") ; string s - Console.Readline0; // conmence par vrifier si 1a chane entre peut tre un nombre
if
t
(!
IsAllnigits
(s)
!");
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);
]
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).
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").
2l 5
Le programnre suivant utilise Sp,r+ ,..) pour saisir une suite de nombres
additionner
#"Ti3 ,\ ffi..1
,
lI tt II II
Pr(sn1rnnpWithSnlir - 'l it ripg Srie de nOmbres spars par des virgules, les transforme en
nombres
nanespac t
e ParseSequencelllithSplit
System;
C1ass1
using class
{
public static
{
int
Main(string[1 args)
//
derrande
.
Console
1'utilisateur de saisir une srie de nornbres illriteLine ( 'rEntrez une srie de nombres spars par des virgules" ):
une
I lit
ligne de texte
// convertit la ligne en segnents ll en utilisant 1a virgule ou 1'espace conme sparateur charll cDividers = {',', ' 'J; string [] segnrents = input, Split (cDividers) ; /l convertit chaque segment en nonbre int nSonme = 0:
foreach(string s in
{
segments)
I if
I t
ll if
{
(IsAllDieits
s)
2I6
l/ converti.t 1a chane en un entier 32 bits int num : Int32.Parse(s); Console.I,lr j-teline ("Nouveau nonbre = {0J 'r , nun) ; /l ajoute ce nombre 1a sonme
nSum
*=
num;
// affiche 1a somne
Console.WriteLine("Sonne
I
return
l
i
0;
/ fsnllni.gits - retourne true si tous 1es I sont des chiffres public static bool IsAllDigits(string sRaw)
I
caractres
// comnence Dar se dbarrasser des caractres inutiles // chaque extrmit ; s'i1 ne reste rien // c'est que 1a chane n'est pas un nombre string s = sRaw.Trim0; if (r.Length == 0)
{
return false;
l
// effectue une boucle sur 1a chane for(int index = 0; index ( s.Length; index**)
{
I si ce nrest pas un chiffre, c'est I n'est sans doute pas un nombre if (Char. IsDigit (s Iindex] ) == false)
I
I
{
que 1a chane
return false;
l
] I l
rt1trr)
Le programme Par seSequence'rn/ithSplit commence par lire une chalne saisie au clavier. Il passe la mthode Split O le tableau cDirriders afin d'indiquer que la virgule et I'espace sont les caractres utiliss pour sparer deux lrornbres dans la chalne.
2t7
Le programme effectue une itration sur chacun des "sous-tableaux" crs par Spli: O en utilisant la structure foreach. Il ignore tous les sous-tableaux de longueur nulle (qui rsulteraient de la prsence de deux dlimiteurs conscutifs). Le programme vrifie ensuite que la chalne contient effectivement un nombre en utilisant la mthode IsAllDlgits o Chaque nombre valide est converti en entier puis ajout la variable nSun. Les nombres qui ne sont pas valides sont ignors fi'ai choisi de ne pas mettre de message d'erreur).
7J,A
nombre
1 2
Appuyez
Le programme parcourt cette liste, acceptant comme sparateurs Ia virgule, I'espace ou les deux. Il ignore le a et affiche le rsultat 10.
^r\C. :\\v-a
l[Orf Y
HDansunprogrammedestinunusagevritable,vousnevoudreZSanS
doute pas ignorer une donne incorrecte sans rien signaler I'utilisateur.
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
PaC ( )
Vous pouvez utiliser la mthode Trim O pour supprimer les caractres indsirables aux deux extrmits d'une chalne. Vous allez typiquement
2 |8
vous en servir pour supprimer des espaces afin d'aligner correctement les chalnes envoves la sortie.
Les fonctions Pad sont un autre moyen d'usage courant pour mettre en forme la sortie. Celles-ci ajoutent des caractres I'une ou I'autre extrmit d'une chalne pour lui donner une longueur dtermine. Par exemple, vous pourrez vouloir ajouter des espaces I'extrmit clroite ou gauche d'une chane pour I'aligner droite ou gauche, ou alors ajouter des "*" ou autres caractres pour signifier quelque chose de particulier. Le programme A1ignoutput suivant utilise ces deux fonctions pour extraire et aligner une srie de noms :
,,1{J\,-
'odsg9
| l* [-T''?
//
{
Ali8n0urput
justifie
la sortie
du programme
class C1ass1
{
strinsil
Y_ +..b !J
nanes
"Jonathan"
" Hildegarde "J; // cornnence par affieher les noms coame ils se prsentent I I (tout en se souvenant de la chane 1a plus longue)
Console.Writeline("Les nons suivants ont des "
{I ttl^-^,,^,,.t rUrr{JUt
nanes)
vr!
'
foreach(string s in
{
Console
I^lriteline ( ) ;
de rnanire qu'e1Ies soient nme longueur
;
aient toute la
mmes noms"
foreach(string s
in
2|I
Console.
]
return
]
0;
insre 1es espaces ncessaires pour les aligner toutes sur la plus longue public sttj.c string[] trirnlndPad(string[] strings)
// I |/ II
I
lrim.qndPad
extrnit,
puis
i
copie l-e tableau source dans un tableau rye vous pourrez manipuler stringll stringsloAlign = new StringIstrings.Length]
I
// |
for(int i = 0; i ( stringsToAlign.Length;
t
l/ //
conrrence
chaque
l/ trouve naintenant 1a longueur de la chane 1a plus longue, I I de faon que toutes 1es autres s'alJ.gnent sur el1e int nMaxlength = 0;
foreach(string s in stringsToAlign)
t
if (s.Length )
t
nMaxlength)
nMaxlength = s. Length; l
// enfin. iustifie toutes 1es chanes sur la base I I de 1a longueur de 1a chane 1a plus longue for(int i = 0; i ( stringsToAlign.Length; i++)
{
stringsToAlign
l
stringsToAiign[i] .PadRight(nMaxlength + l)
Ii]
=
;
//
]
return stringsToAlign
220
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
la
est le
est
est
est
est
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.
22 |
a.Replace(s, ",'l')
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 :
// RenoveWhiteSpace - dfinit une fonction RenoveSpecial0hars0 qui peut supprimer un caractre quelconque d'un I eertain ensenble drune chane donne. Utilisez II cette fonction pour supprimer 1es caractres II blancs dans une chane utilise comne exenpLe. ll
I
nanespace RemoveWhiteSpace
using
i
System;
C1ass1
public class
nrrhlin o,o,ic i.nt Main(strins['l strings] yuurrL cfafi6 int Main(stringIJ strinss)
t
ll aenntt 1es caractres blancs char[] c!hiteSpace = {' ', '\n', '\t'}; // cornmence par une chane contenant des caractres string s = " ceci est une\nchane'*; ionroi..lfriteline("chane initiale :" * s);
blancs
222
(s,
cWhiteSpace) )
return
l
I
0;
// I
RemovpSnpcialChnrs
nrhl
{
jn
srrnnlimp dp 1a chane toute occurrence du caractre spcifi cfefi n ctri.lg RemoveSpecialChars (string slnput,
char
[]
cTargets)
for(;;)
{
// trouve f index du caractre, sort de la I I s'iI n'en reste plus int n0ffset : sOutput . IndexOf (cTargets) ; if (nOffset =: -1)
t
boucle
break;
]
qtr.i no cRpforo : vuLyuL.UuUDLrIrri\v, nf f c0rrtnrrt Srrhctrjnn /0 lMrDcL/tca+') . -+-i^- snl LI ^^r+^Srrhctrino I) = c011tn11t JuuLlr116\r.v!rrLL(nflf f <pt *' !) vuLyuL,
runit maintenant 1es deux sous-chanes et le caractre manquant entre 1es deux s0utput = String.Concat(sBefore, sAfter) ;
I
I //
'
l
rt11rn crrinttt,
l
]
fonctior RemoveSpecialChars O qui constitue le cur de ce programme. Elle retourne une chalne qui est la chalne entre, s Input, mais dont tous les caractres contenus dans le tableau cTargets ont t supprims. Pour mieux comprendre cette fonction, imaginez que la chalne tait "ab,cd,e", et que le tableau de caractres spciaux supprimer contenait simplement le caractre','.
C'est la
La fonctio Remor,,eSpecialChars O entre dans une boucle dont elle ne sort qu'une fois que toutes les virgules ont t supprimes. La fonction Ind ex0f Any ( ) retourne I'index du tableau pour la premire virgule qu'elle peut trouver. Si elle retourne -1, c'est qu'aucune virgule n'a t trouve.
Chapitre
G#
223
sa premire invocation, inclexLlfArr.,, () retourne un 2 ('a'est {}, 'ir' est 1, et ',' est 2). Les deux fonctions suivantes dcomposent la chalne err morceaux I'endroit donn par I'index. Subs-ti i;:g i.,-t , 2 t cre une souschane compose de deux caractres, et commenant I'index (i : 'ab". Le deuxime appel Subst r ing i3 ) cre une chalne commenant I'irrder 3 et allant jusqu' la fin de la chalne initiale: "cd,e" (c'est le "* 1" qui fait passer aprs la premire virgule). C'est la fonction ',r-,r,r--,-.,-, ' iti recolle les deux sous-chanes pour crer "abccl,e".
Le contrle repasse en haut de la boucle. L'itration suivante trouve la virgule I'index 4.La chalne concatne est "abcde'. Comme il ne reste
-1.
affiche une cltalne cclntett;tttt plusieurs types de caractres non imprimables. Il r-rtilise ensuite la fonc:ticlrr F.emo.,Sp-.cia iChars () pour enlever ces caractres non itnprinialtles. La sortie de ce programme se prsente de la faon suivante
:
chane
chane
une
aprs : ceciestunechane
Annrtrroz crrr Fntro nnrrr tormi ner
ll,lettre
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
de 1a ehane toute occurrence du caractre spcifi *..L1.:^ ^+^+)^ puDlr_c srarj-c ^+-) srrlng RenoveSpecialChars (string slnput,
^
KenoveSpeclalunars
- supprine
cherfl r.Taropts)
{
// diviser 1a chane entre en uti.lisant les caractres l/ cible come dlimiteurs string li sSubStrings = slnput. Split (clargets) l/ s0utput contiendra 1es inforinations finales de sortie
;
<l-rino
// effectue
la division
22 t,
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.
o
:
String offre aussi la mthode Format O pour mettre en forrne la sortie, en particulier la sortie des nombres. Dans sa forme la plus simple, Forrnat O permet d'insrer une chalne, une variable numrique ou b<>cllenne dans une chalne de contrle. Par exemple, examinez I'appel suivant
String.Format("{0}
fois {l}
ga1e 12}"
, 2, 3, 2*3);
On appelle chane de contr1e le premier argument de !'ormat O. Les i :i r que vous voyez dans cette chane indiquent que le nirne argument suivant la chalne de contrle doit tre insr ce point. Zro correspond au premier argument (dans ce cas, 2), un se rfre au suivant (3), et ainsi de suite.
Il en rsulte la chalne
"2 fois 3 ga1e 6"
() utilise un format de sortie par dfaut pour chaque type d'argument. Format O permet de modifier le format de sortie en mettant des modificateurs aux emplacements voulus. Le Tableau 9.1 donne une liste de certains de ces contrles. Par exemple, | 0 : E6 I dit : "Afficher les nombres en notation scientifique, en utilisant six caractres pour la mantisse."
Sauf indication contraire, Format
225
String.Format O.
Contrle
C
Exemple
{0:C} avec
Rsultat 123,456
123,45
Notes
Le symbole montaire dpend du paramtrage de localisation, de mme que I'usage de la virgule ou du point comme sparateur de la paftie
- monnaie
dcimale.
{0:C} avec
D
E
-123,456
(123,45
F)
00123 - dcimal {0:D5} avec 123 - exponentiel {0:E) avec 123,45 1,2345E+02 {0:F2} avec 123,4567 123,45 - fixe - nombre
{0:N}
Entiers seulement.
Oue l'on appelle aussi "notation
scientifique".
Le nombre qui suit le F indique le nombre de chiffres aprs la virgule.
123456,789
123456.789 123456.789
123
456,19
Ajoute le sparateur de milliers (dpend du paramtre de localisation) et arrondit au centime le plus proche.
Contrle le nombre de chiffres aprs la virgule. ldem.
0xFF est gal 255.
{0:N1}
123
456,8
457 X OxFF - hexadcimal {0:X} 012,30 {0:000.00} 12,3 {0:0...} {0:###.##}12,3 12,3 {0:#...}
{0:N0}
123
Met un 0 s'il n'y a pas de chiffre. lmpose un espace blanc si le nombre n'occupe pas I'espace spcifi. Aucun autre champ ne peut enquter sur l'espace dfini par les trois chiffres avant la virgule, et les deux aprs (permet de maintenir l'alignement autour
de la virgule).
{0:##n.0#}
0 ,1234
0,0 12,3%
02,3o/o
celui-ci est
{0:# or
0.
0%}
{0:#00.#%l
le signe %).
{0:#00.#%]10234
226
Ces contrles de formats peuvent paraltre un peu dconcertants (et je n'ai mme pas parl des contrles dtaills de date et de format montaire). Pour vous aider apprivoiser ces options, le programme suivant, OutputFormatCcirtrcls, vous permet d'entrer un nombre en virgule flottante suivi par une squence de codes de contrle. Le programme affiche alors le nombre en utilisant I'instruction Fornar O avec la squence de contrle de format spcifie :
/ t t I
1I
a {Jlrtn11th'rm,atn^-+-^1 vuLyuLrv!llraLvvrlL!vr
I II
t
^ - yrnrL ^^er^+ 1'UtiliSateUr de redfinir 1e fornat des nonbres saisis en utilisant l'excution divers codes de contrle de format
nanespace OutputFornatControls
using
{
System;
C1ass1
public class
I lit les nombres saisis jusqu' ce que /l 1'utilisateur entre une liene blanche au lieu // d'un nombre
I
1^r rv! | . ' I \ r , /
t I
// I II
commence
string
if
{
I
J
Console.Readline0
(sNumber.Length
0)
break;
double dNurnber : Double.Parse(sNunber) ; I I lit maintenant 1es codes de contr1e, spars I I Les uns des autres par des espaces Console.l{riteline("Entrez 1es codes de contrle" * " spars par un espace"); char l] separator = {' 'J ; string sFormatString = Console.Readline0 ;
stringl]
sFormats
"
(
/l
t
sFornatString. Split separator) ; effectue une boucle sur les codes de contrle 1'un aprs 1'autre
foreach(string s in sormats)
if
{
I
(s.Length !=
0)
227
entrs
string sFormatConnand = 'riO' rr * g * nltt ' // affiche 1e nonbre entr en utilisant I I Ia connande de fornat reconstitue
Console . 1lIrite
(
try
{
Console.
I'lriteline
(sFormatConnand, dNumber)
l
catch (Exception)
{
.l,lriteline 0
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 )
228
Voici un exemple de sortie obtenue avec ce programme ('ai mis en gras ce que j'ai entr) :
Bntrez un nombre de type double
1,2345 ,67 89
c E F1 N0
La
espace
F
La comnande de format {0:E} donne 1,234568E+004 La connande de fornat {0:F1l donne IZ34S,7 La La
commande commande
IZ
346
espace
donne
L2,3',i,
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",
=
0uatrime partie
La programmation
oriente obiet
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
Abstraction et classification.
Comprendre I'importance de la programmation oriente objet.
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 ?"
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
trouvent sur la face avant. Quelques minutes plus tard, les nachos sont prts.
Maintenant, pensez tout ce que je ne fais pas pour utiliser le four
micro-ondes:
t/
Je ne change pas le cblage ni quoi que ce soit I'intrieur du four micro-ondes pour le faire fonctionner. Il a une interface (la face
avant avec tous ses boutons et I'affichage de I'heure) qui me permet de faire tout ce dont i'ai besoin.
t/ t/ /
pour piloter le fonctionnement du four, mme si c'est un autre plat que j'ai fait chauffer la dernire fois que je m'en suis servi.
Je ne regarde pas sous le capot.
Mme si c'tait mon mtier de tout savoir sur le fonctionnement interne d'un four micro-ondes, y compris sur son logiciel, je ne me proccuperais pas de tout cela pour I'utiliser dans le seul but de faire chauffer mes nachos.
Ce ne sont pas l des observations profondes. On ne peut vivre avec le stress que jusqu' une certaine limite. Pour rduire le nombre de choses dont on a se proccuper, on ne travaille que jusqu' un certain niveau de dtail. Dans la langue de la programmation oriente objet (OO), le niveau de dtail auquel on travaille est appel niueau d'abstraction. Autrement dit, pour faire chauffer mes nachos, j'ai foit obstroction des dtails du fonctionnement interne du four micro-ondes.
Lorsque je fais chauffer des nachos, je vois le four micro-ondes comme une bolte. Tant que je n'utilise que I'interface du four (les boutons de la face avant), rien de ce que je fais n'est susceptible de le faire entrer dans un tat instable et de le mettre hors d'usage, ou pire, de transformer mes nachos en une masse noire informe et d'y mettre le feu.
? 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.
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
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.
? 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.
;
t/
Trop complique : Je ne veux pas mlanger les cltaiis cle la fabrication d'un four micro-ondes avec ceux cle la prparation cles nachos. Si je ne peux pas dfinir les objets et les extraire de cette montagne de dtails pour les utiliser de faon indpendante, je suis oblig de prendre en compte tous les dtails de tous les aspects du problme en mme temps.
Dpourvue de souplesse : Un jour ou I'autre, je peux avoir besoin de remplacer le four micro-ondes par un four d'un autre type. Il clevrait tre possible de le faire tant qu'ils ont la mme interface. S'ils ne sont pas clairement dlimits et dvelopps de faon inclpendante, Lur objet d'un certain type ne peut pas tre simplement remplac par un autre.
t/
tz
Non rutilisable : Un four permet de faire de nombreux prlats diffrents. Je ne veux pas avoir crer un nouveau four polrr chaque nouvelle recette. Aprs avoir rsolu le problrne une fois, je veux pouvoir rutiliser la mrne .solution en d'autre.s endroits de mon programme. Et si j'ai vraiment de la chance, je pourrai nrrne la rutiliser plus tard dans d'autres programmes.
236
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
237
tz
Endommager I'appareil. Vous devez pouvoir placer I'appareil dans une sorte d'tat trange dans lequel il ne fera rien tant que vous ne I'aurez pas ranim, mais il doit tre impossible de causer un dommage quelconque I'appareil en utilisant les commandes de la face
avant.
,/
Mettre le feu I'appareil et par consquent la maison. Que I'appareil tombe en panne, c'est ennuyeux, mais qu'il prenne feu, c'est beaucoup plus grave. Nous vivons dans une socit trs procdurire. Il peut rsulter de ce genre de choses des procs trs curieux.
Toutefois, pour que ces deux rgles soient respectes, vous avez aussi votre part de responsabilit : vous ne pouvez f,aire aucune modification I'intrieur de I'appareil. Presque tous les appareils mnagers, de n'importe quel niveau de complexit, notamment les fours micro-ondes, comportent un petit sceau qui empche le consommateur d'accder leurs composants internes. Si le sceau est bris, la responsabilit du fabricant n'est plus engage. Si je modifie les composants internes d'un four, c'est moi qui suis responsable s'il met le feu la maison.
De mme, une classe doit permettre de contrler I'accs ses membres.
Aucune squence d'appels aux membres d'une classe ne doit provoquer le plantage de mon programme. La classe ne peut pas le garantir si des lments externes ont accs ses composants et son tat interne. La classe doit pouvoir maintenir ses membres critiques inaccessibles au monde extrieur.
238
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.
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.
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#.
240
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
en
extrieur)
u9rrr6 "^i-^
C"^+^*. 9J D L!r,
nanespce DoubleBankAccount
{
nrrhra^ yuurru Lra ^rdd t'^SS1 vl
ba. InitBankAccount
//on peut
I
I //
accder au solde par 1a mthode Deposit() car e1le a accs tous 1es
membres donne
ll Ia
conpilation
24
ba. dBalanee
ll
{
Banknccount
- dfinit
une classe
siurple
nrrhl
ie class
BankAccount
nrivatp statie int nNextAccountNunber : 1000; private int nAccountNumber; // conserve 1e sol-de dans une seule variable de type double nrivatp dorrhlp dBalance; // ln:-t - initialise 1e compte avec le prochain numro cie compte
nAccountNumber = **nNextAccountNumber
dBalance = 0.0;
//
i
GetBalance
- retourne 1e solde
courant
//
{
AccountNumber
public
int
GetAccountNumber0
;
return
l
nAccountNunber
nAccountNumber)
this.nAccountNumber = nAccountNumber
I/ I/ n^-^^'r+ r,/trPUlL
est
autoris
dAmount)
if
{
(dAnount dBalance
0.0)
dAmount;
*=
// I!
Withdrar,{
- tout retrait est autoris jusqu' la valeur du solde ; retourne 1e nontant retir
242
if
{
l
dBalance -= dWithdrawal return dWithdrawal;
;
/i
t
retourne dans une chane les informations uL 4c LvxlP L srrr lpr.onnt s Lrvrr
GetBalance 0 )
ratll rn c
.
]
]
Souvenez-vous que dans ce code, dBalance dhrithdrawat est la rnme chose eue dBalance : dBalance dwithdrawal. Les programmeurs C# ont tendance utiliser la notation la plus concise possible.
Dclarer un membre comme public le rend disponible pour n'importe quel autre code dans votre programme.
La classe BankAccount offre la mthode InitBankAccounr O pour initialiser les membres de la classe, la mthode Deposit O pour traiter cles dpts, et la mthode Withdraw O pour traiter les retraits. Les mthocles Deposit O et Withdr aw O fournissent mme des rgles rudimentaires, comme : "on ne peut pas dposer une valeur ngative", et "On ne peut pas retirer plus que ce que contient le compte." Vous convienclrez certainement que ce sont de bonnes rgles pour une banque. Toutefois, n'importe qui peut accder tout cela aussi Iongtemps que dBalance st accessible aux mthodes externes (dans ce contexte, externe signifie "externe la classe, mais dans le mme programme").
Avant de trop vous enthousiasmer, remarquez que ce programme ne se gnre pas. Une tentative de le gnrer gnre en fait le message cl'erreur
suivant
'DoubleBankAccount.BankAccount.dBalancer
niveau de protection.
43
il ne se contente pas de dire : "N'entrez pas, c'est priv", mais c'est essentiellement ce que a veut dire. L'instruction ba.dBalance +: 10; est illicite parce que dBalance n'est pas accessible Main O. Le remplacement de cette ligne par ba. Deposit (10) rsout le
problme.
Le type d'accs par dfaut est private. Oublier de dclarer un membre en tant que tel revient le dclarer comme private, mais il vaut mieux
=(t
fry\
Cette section suppose quelques notions sur l'hritage (Chapitre 12) et les espaces de noms (Chapitre 16). Vous pouvez I'ignorer pour le moment, mais vous Saurez qu'elle est l lorsque vous en aurez besoin.
C# offre d'autres niveaux de scurit, au-del de
public et private
t/ t/ ,/ ,/
Un membre
Un membre private n'est accessible que par la classe dans laquelle il est dclar. Un membre protected n'est accessible que par la classe dans laquelle il est dclar et par toutes ses sous-classes. Un membre internal est accessible par toutes les classes du mme espace de noms (essentiellement, par tout groupe de modules C# que vous aurez spcifi pour cela, c'est--dire tous les modules que vous aurez crits pour le programme, mais pas ceux crits par votre voisin de palier). Un membre
t/
laquelle il est dclar et toutes ses sous-classes, ainsi que par les
classes du mme module.
internal protected
C'est le masquage d'un membre en le dclarant private qui offre le maximum de scurit. Toutefois, dans de nombreux cas, vous n'aurez pas besoin de ce niveau de scurit. Aprs tout, comme les membres d'une sous-classe dpendent dj des membres de la classe de base, protected offre un niveau de scurit confortable.
244
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.
Dclarer les mernbres internes d'une classe comme public est une mauvaise ide. au moins pour les raisons suivantes :
public, vous ne pouvez pas savoir facilement quand et comment ils sont modifis. Pourquoi utiliser les mthodes Derosit () et i^/ithdraw ( ) pour traiter les chques ? En fait, pourquoi avoir besoin de ces mthodes ? N'importe quelle mthode de n'importe quelle classe peut modifier ces lments n'importe quancl. Si d'autres fonctions peuvent accder ces membres donne, elles le feront certainement.
Si tous les membres donne sont
Mon programme BankAccounr peut trs bien tourner pendant une heure ou deux avant que je ralise que I'un des comptes a un solde ngatif. La mthode \,,iithdraw O aurait d garantir que cela ne puisse pas arriver. De toute vidence, une autre fonction a dt accder au solde sans passer par Wi thd raw ( ) . Dcouvrir quelle fonction en est responsable et de quelle manire est un problme
trs difficile. t/
Exposer tous les membres donne d'une classe rend I'interface trop complique. En tant que programmeur utilisant Ia classe BankAccount, je ne veux rien savoir de ce qu'elle contient. Il me suffit de savoir qu'elle me permet de dposer et de retirer des
fonds. Exposer les lments internes conduit exporter les rgles de classe. Par exemple, la classe BankAccount ne permet en aucune circonstance que le solde devienne ngatif. C'est une rgle commerciale de la banque, qui doit tre isole dans la mthode I,,/irhd raw ( ) , faute de quoi il faudra ajouter la vrification de cette rgle en tout endroit du programme o le solde est modifi.
t/
Qu'arrive-t-il lorsque la banque dcide de modifier les rgles pour que les "clients privilgis" soient autoriss avoir un solde lgrement ngatif
245
sur une priode limite ? Il me faut maintenant rechercher dans tout le programme toutes les portions de code qui accdent au solde afin d'y adapter en consquence les vrifications correspondantes.
de nombreux calculs concernant la lecture des reus, des frais de gestion de compte, et tout ce que ma banque veut soustraire du solde de mon compte. Les fonctions externes n'en savent rien et n'ont rien en faire. Naturellement, je veux savoir quels frais on m'a fait payer, mais je ne peux rien y faire, moins de changer de banque.
Enfin, GetBalanre () offre un mcanisme qui permet d'apporter des modifications internes la classe sans qu'il soit ncessaire de changer les utilisateurs de BankAccount. Si le ministre des Finances demande ma banque de grer autrement ses dpts, cela ne doit rien changer la manire dont je peux accder mon compte.
246
// || II II II
t
DoubleBankAccount
en
utilisant
une variable
de type double pour stocker le solde du compte (conserve 1e solde dans une variable prive pour masquer son implmentation au
monde
Toct
extrieur)
nqnocn:no
I I cr6e un nouveau cotnpte bancaire Consol-e.Writeline ("Crati.on d'un objet compte bancaire")
\
I
I et voil le problrre
;
\s:'su4
double dAddition = 0.002; Console,!,IriteLine("Ajout de {0:C}", dAddition) .i hn - Dpnosi t { rlAddition) ; // solde rsultant Console,Writeline{"Compte rsultant = {0}",
ba.GetString0);
attend confirmation de L'utilisateur Console,Writeline("Appuyez sur Entre pour terminer.
Console.Read0;
ll
rr
I'
return
0:
247
La fonction llain i. ) cre un compte bancaire puis y dpose 123,454 F, montant qui contient un nombre dcimal de centimes. l'lain O ajoute alors une petite fraction de centinles au solde, et affiche le solde rsultant.
La sortie de ce programme se prsente de la faon suivante Cration d'un objet compte bancaire
Dpt de 123,45 F Compte = lftOOt - 123,45
E
#tOOt
nnrrr PUur
123,45
.
F
,
1'n+ro lltL!cc
torm.inor L!urllcr.
C'est l que les utilisateurs commencent se plaindre. Pour moi, je n'arrive pas mettre mes chquiers en accord avec les relevs de compte de ma banque. En fait, je suis trs content si je tombe juste 100 dollars prs, mais il y a des gens qui tiennent absolument ce que leur relev de compte soit bon au centime prs. Apparemment, il y a un bogue dans ce programme. Le problme, bien str, c'est que 123,454 F apparalt comme 123,45 F. Pour viter cela, la banque dcide d'arrondir les clpts et les retraits au centime le plus proche. Si vous dposez 723,454 F, la banque en retire les 0,4 centimes en excs. Comme elle fait la mme chose lorsque la diffrence est en votre faveur, a ne change rien dans la dure.
La manire la plus facile de raliser cela consiste convertir les comptes en deciniai et utiliser la mthode Round0f f O, comme le montre le
programme
Dec
lrnalBankAccount suivant
// II
r
DecimalBankAccount
en
utilisant
une
irralBankAccount
C1ass1
public class
{
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 ;
//
de
{0 : Ci "
dDeposit)
2 48
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
I
(dAddition)
I solde rsultant
ba.GetString0):
Console,I^lriteLine("Compte rsultant
: [0]",
//
I
I
BankRccount
- dfinit
une classe
simple
public class
BankAccount 1000;
//
I
private decimal
-.,1" yuuf
{
rL
i
{
GetBalance
return
l
(double)nBalance
//
{
AccountNumber
public
int
GetAccountNumber
nAccountNumber)
this . nAccountNumber =
]
nAccountNumber
esr autoris
dAmount)
if
(dAnount
0.0)
2 49
// //
avnt
decimal
mTemp
(decimal)dAmount;
= Decimal,Round(mTerrp, 2);
mBalance += nTenp; ]
// II
{
Wi"thdrar,v
- tout retrait est autoris jusqu' la valeur du solde ; retourne le montant retir
dWithdrar,val)
if
{
(mBalance
(= dWithdrawal)
dWithdrawal
]
mBalance;
// GetString - retourne dans une chane les infornations srrr le public string GetString0
{
c.)mnte
string s = String.Format("lf{Ol =
rtlrrn a.
{1:C1",
,
GetAccountNunber ( ) GetBalance 0 ) ; ]
J
J'ai converti toutes les reprsentations internes en valeur dec rnat, qui est dans tous les cas un type mieux adapt que dcubl au traitement de solde de compte bancaire. La mthode Deposit (,r utilise maintenant la fonction Decimal. Found O pour arrondir Ie montant des dpts au centime le plus proche avant d'effectuer le dpt correspondant. La sortie de ce programme est maintenant ce que nous sommes en droit
d'attendre
Dpt de 123,45
= r23,45 E
.
.
50
ob
jet
Et alors )
On pourrait toujours dire que j'aurais d crire ds le dpart le programme BankAccount en utilisant le type decimal pour les donnes saisies, et je serais probablement cl'accord. Mais ce n'est pas si vident. Bien des applications ont t crites en utilisant le type double comme moyen de stockage. Un problme s'est produit, mais la classe EankAccor-rnt tait capable de le rsoudre de faon interne sans ncessiter de modifications I'application elle.mme.
Dans ce cas, la seule fonction affecte a t ltlain O , mais les effets auraient pu s'tendre des centaines d'autres fonctions accdant aux comptes bancaires, et ces fonctions auraient pu se trouver dans des dizaines de modules. Si la correction avait t faite I'intrieur de la classe Ba nhAc c c, Lri-i1-, aucune de ces fonctions n'aurait dt tre modifie. Mais cela n'aurait pas t possible si les membres internes de la classe avaient t exposs des fonctions externes.
=O,
#\
n,;iilL::.'"""u
Les moclifications internes une classe ncessitent quand mme toujours diverses portions de code, mme si celles-ci n'ont pas
1)
C# dfinit une structure nomme une proprit qui permet d'utiliser beaucoup plus facilement les fonctions d'accs. Le fragment de code
int
AccountNumber
5I
La section get est implmente chaque fois que la proprit est lue, alors que la section set est invoque lors de l'criture. La proprit Balance cidessous est en lecture seule, car seule la section set est dfinie : public double
{
Balance
o1
t
return
l
]
(double)mBalance
new BankAccount
//
I
ba.AccountNumber
1001
Console.writeLine("lf[0] = {l :c}",
ba.AccountNumber, ba.Balance)
;
membres donne publics, par leur prsentation comme par leur utilisation. Toutefois, les proprits permettent la classe de protger ses membres internes (tsa1ance est une proprit en lecture seule) et de masquer leur implmentation. Remarquez que Balance effectue une conversion. Elle aurait pu aussi excuter les calculs les plus abondants.
<$-_--
Ilof l L-.t
v
xParconvention(cen'estpaSuneobligationdeC#),lenomd'uneproprit
commence par une lettre majuscule. Une proprit n'est pas ncessairement dpourvue d'effet. Le compilateur C# peut optimiser un simple accesseur pour qu'il ne gnre pas plus de code machine que I'accs direct un membre donne. C'est important, pas seulement pour I'application, mais aussi pour C# lui-mme. Toute la bibliothque C# fait un usage abondant des proprits.
Proprits staques
Un membre donne statique (de classe) peut tre expos par I'intermdiaire d'une proprit statique, comme le montre I'exemple simple suivant public class
I
BankAccount
252
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;
I
I I
1
int
AccountNumber
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.
53
dmarrer les choses, mais que se passe-t-il si I'application oublie d'appeler la fonction ? La classe commence avec de mauvaises initialisations, et la situation ne peut pas s'amliorer ensuite. Si vous voulez tenir la classe pour responsable de ce qu'elle fait, vous devez commencer par lui assurer un bon dmarrage.
C# rsout le problme en appelant la fonction d'initialisation pour vous.
Par exemple
MyObject mo
new My0bject0;
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.
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 :
t-l ll
tdt
Par comparaison, C# offre un constructeur par dfaut qui initialise le contenu d'un objet 0 pour une variable intrinsque, f alse pour une
25
variable boolenne, et nLr-11 pour une rfrence d'objet. Voyez I'exemple de programme suivant :
,1d; urrr ^^ Q*a+^* J) Llt, '
namespace DecimalBankAccount
{
Maj_n(string
[]
args)
/l courmence par crer un objet My0bject local0bject = new My0bject0; Console.i^IriteLine("1oca1Object.n est {0J", localObject.n) if (localObiect.next0biect
(
L
== null)
;
l l
public class
t
MyObject
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
1oca10bject,n est
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.
25
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
BankAccount ba
l public class
{
BankAccount
II /i
et
augmentent
static int
| | nat inrrr
nNextAccountNunber = 1000: 'l p nrrmrn rlo nnmnf o at 'l o cn1rl o nnrrr nhrnrro nhipt
int
nAccountNumber:
double dBalance:
256
/ constructeur BankAccount
BankAccount
()
public
L
nAccountNunber
dBalance
: 0.0;
= *tnNextAccountNumber
l
duL!c -^-L-^^ llElllu!cD.
Le contenu du constructeur Bankcccunt est le mme que celui de la mthode oriqinale Ir r*: . . ' ). Toutefois, une mthode n'est ni dclare nl utilise de la rnme manire
t/ ,/ t/
Le constructeur porte le mme nom que la classe. Le constrtrcteur n'a pas de type retourn, mme pas void.
l'{ai
rr r i n'a pas besoin cl'invoquer une fonction supplmentaire pour initialiser I'objet lorsqu'il est cr.
//
DenonstrateDefaultConstructor
des constructeurs par dfaut ; cre une classe avec un constructeur, puis excute quelques scnarios
ttcino Svctpm. *- -"
namespce DemonstrateDefaultConstructor
I t
nontre
le
fonctionnenent
il
{
//
Hy0bject
et rrn
oh i
et i nterne
// ce membre est
I
une proprit de
a+a+i nflhi LLlUVUJ
.
ce membre
la
classe
Mrr0thorhiant
public
t
My0bject
0
I
Console.lJriteline ("Dmarrage du constructeur ilyObject") dynanicObj : new My0ther0bject0; Console.llriteline (r'Fin du construeteur MyObject") ;
57
/i II
My0ther0bject
- cette
()
Console. trlriteLine ("Dmarrage de Main ( ) " ) I cr.e un ob jet new My0bject0; My0bject localObject
/l
1. 2. 3.
Le programme dmarre, et Main ( ) affiche le message initial. Main ( ) cre un localObject, de type My0b j ect.
ject contient un membre statique, static0b j, de la classe MyOther0bject. Tous les membres donne statiques sont crs avant que le premier MyOb ject soit construit. Dans ce cas, C# remplit static0b j avec un Ml,'Other0b j ect nouvellement cr, avant de passer le contrle au constructeur MyOb j ect. Cette tape correspond au second message.
|1y0b
258
4.
Le constructeur d l"i-r'ObJ ec t reoit le contrle. Il affiche son premier message : Dna rrage dLi colrstrr-icteur MyCb i ect. Le constructeur l"lyOir r'ect cre un objet cle la classe l'1'y0tirer0b j ect en utilisant I'oprateur new. et affiche le deuxime message du
5.
con-structeur
6. 7.
I"h'Ot he rOb
J1
ec
Mission accorlplie
l. 2.
slectionnez Gnrer/Gnrer.
Avant de commencer excuter le programme partir du dbogueur, dfinissez un point d'arrt I'appel Conso ie . i'iri-i eLine () dans le constmcteur i4y0therOb j ect.
Pour dfinir un point cl'arrt, clicluez <'lans la barre grise verticale qui constitue le bord gauche cle l;r ferrtre de code, en regard de la ligne pour laquelle vous voulez clfinir un point d'arrt.
La Figure
1
l.l
3.
Au lieu de slectionner Dtloguer/Dmarrer, selectionnez Dboguer/ Pas pas dtaill (ou, mieux encore, appuyez sur la touche Fl l). Vos fentres doivent s'agiter un peu pendant quelques secondes, puis I'appel Console.l',r j telin-o O doit apparaltre sur fond jaune.
4.
Appuyez nouveau sur la touche Fl l. Votre affichage doit maintenant ressembler ce que montre la Figure 11.2.
D.
Slectionnez Dboguer/Dmarrer ou appuyez sur F5, et le programme s'excute jusqu'au point d'arrt dans MyOtherCb_jecr, comme le montre la ligne en surbrillance dans la Figure 11.3. Appuyez encore deux fois sur la touche Fl l, et vous tes de nouveau au dbut du constrrcteur l{.,,tlb i ec+,, comme le montre la Figure I1.4.
6.
5g
Figure 11.1:
- fond sur
I Fr rtl orll
La ligne en surbrillance
F
Frqrrrrffle
w *. :. .';
Ii'r-] t,tr
- r,"i.i.., a"
t,4
ag ., '..,
Au.g.&&
4
- -
,:rrrL',rLet,i
._).
flassl.rs
j
.::,i
tfi
(i.1
J
|
,tr.
1'1,:ln
r,ehu,] -
rS
strir,l[] rrq:l
,l :J
*1
i.,
-:'
,$ + u' .r L.11]f5:! StauE.jrlt: _,:n!t.tE:] DenronstrateDefaultf onstructor 4 FfrETr r,ts: 1!] A::enrbl'lnf,:,,:r 1! Clasrl.cs
|
dbogueur
de Visual
du
Exlortur d. AutEmtiqLe
l'+om I'rr :1,-rt'tttl: !,11uf
x J
Pile de5
Fpls ttrr
I Lar
ntl
(:r
:#
constructeur.
nAuromitiquel
L gnertirn a
tr
ffipiluout
Ln 38
rBssi
Col
tr;, ir
f l ::,:t.t
a;
260
ficfrier Edilir-,n Atfi{h,3qe qrjet rarrer Qhoqrer gutils ' : He. . - -."".';)- '.:. ? w i: ,' ;-'
"r]1
F
r,t)t amr
ll!:l
.,...,,
I
t,frr: n5l:r
:.
tflassl.cs
:;l;:;I 't
,:::r,
, '
,tr
r'rt,r,l
,!a
Er
,.. f
,,
:i
---1
:j
:i
_,lr_-:
I.TIrr--Llr_:
"'-
I_=f
r.l r t:r|l
l1'rr
- construcle
teur
l"h;t'}|horlhioe
l
!riJ:1.r,: r:lti:
'.::r i ll.ir[
.-!::]r':j[]
FluaLr.:1:{=
-il
Autrntiqu
T|pe
S;r *}
:,r:frr [,r,:rr:|:.
le constructeur
l,Ii'Object.
Fr,3m d
Frie
Iaf r]rr!lrnl[ii]ll, t,
,rTr!ff!:Lrr
'
['etu,1
:J
--J
:l
teur
M-,'Obi ec
t
utontigue
l,J,ln
TYpe
-
jet
+il
['f-,]rrilr ltEIrf,:!li]:itrr!ff rJ:l:r,r ll,/:-t,jE,:ll trnr:rrslr
statique
Itf.rlthonlh-i.e
F l d:: FF,l!
tr'rn
( ttn,rr:Lr:l"t'rl.irfi,:rrlff,l:l,t,t 'elIif:f5]:tt|[,i:,t]:,_,:ir:l;!:]:r,l# 'rriir r.i-f i i l::f [nr:riifitE[i:r]ii ':,r!lftl[i[]lll_:fst:rtr:r':1,:#
tu-ol
] ]
1f,,.
t
L4l ,rlhtrqui |
L gnerarln
n t6
c0nstru it,
$rsl l# reusii
)
EJ
Fre des ar,,ets
r.tl
Ln
t
h
{-,rl
,t
i-l',
261
//
| |
l-es nunros de compte commencent I I g9uElrLftrarclucllL ^t ^,,^^.i ^1|pmpnf nert.i r dp 1 uc o yq! Lrr
1000
et
augmentent
static i.nt nNextAccountNunber = 1000; ll net jour 1e numro de compte et 1e solde pour
chaque objet
int
nAccountNumber
= **nNextAccountNunber
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
262
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.
fait la construction
auec
Dplacez maintenant I'appel rrew l"f.,'OtherObiect O du constructeur l1_1,f;5 ject la dclarettion elle-mme, comme ci-dessous, puis excutez nouveau le progranlnle :
nrrhf
{
ic class Mv0hiect
une proprit de
ctetirnhi
la
classe
<tnt'i n Mvthorhiont
f)
.
t
public
{
My0bj
ect
()
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 .
2 63
Surcharger le constructeur
On peut surcharger un constructeur, tout comme n'importe quelle autre
mthode.
Surcharger une fonction signifie dfinir deux fonctions portant le mme nom, mais ayant des arguments diffrents. Pour en savoir plus, voyez le
Chapitre
7.
Imaginez que vous vouliez offrir deux manires de crer un Bank-Account une avec un solde zro, comme le mien la plupart du temps, et une autre avec une valeur initiale :
f\
// tl
BankAccountWithMultipleConstructors
ll
t
bancaire
using
nmespac
e BankAccountWithMult
ip1 eConstructors
C1ass1
public static
{
nerrr Bankccount
0
;
))
= new BankAccount(100);
Console.lririteline (ba2 . GetString 0 ) ; = new BankAccount(Iz3|, 200); Console,Writeline (ba3, GetString 0 ) ; // attend confirmation de 1'utilisateur Console.i,ilriteLine("Appuyez sur Entre pour terminer...")
Console.Read0;
return
l
]
0;
//
t
public class
I I les numros de compte coffrencent 1000 et // souentiel I ^-^-+ a ^^-+i - ,l^ 1 DLyusrrLlcrrulclIL uE PaL Lal static i.nt nNextAccountNumber = 1000;
t t
augmentent
264
i.nt
nAccountNunber;
double dBalance;
// fournit
public
{
BankAccount
nAccountNurnber
#nNextAccountNurnber
dBalance = 0.0;
public
{
BankAccount
(double dlnitialBalance)
dfaut
if (dlnitialBalance (
{
= #nNextAccountNumber ; /l et nai"ntenant, le code propre ce constructeur l/ commence avec 1e solde initial, condition qu'i1 soit positif
0)
dlnitialBalance =
l
dBalance l
0;
public
{
BankAccount
II if t
nlnitialAccountNumber = *tnNextAccountNumber
]
nAccountNumber
= nlnitialAccountNumber
dTnitialBalance
]
0;
dBalance = dlnitialBalance;
I
26 5
t/ t/ t/
Le premier constructeur assigne un numro de compte, et dfinit un solde gal 0. Le deuxime constructeur assigne un numro de compte, mais initialise le solde du compte avec une valeur positive. Les valeurs de solde ngatives sont ignores.
Le troisime constructeur permet I'utilisateur de spcifier un
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
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
266
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
bancaire
name spac {
"^.; -^ uDrrr6
r sAndFunct
ion
publj-c class
{
C1ass1
I I cre un compte bancaire avec des valeurs BankAccount ba1 = new BankAccount 0 ;
initiales valides
Console.ItIriteline (bal.GetString 0 ) ;
BankAccount ba2
= new BankAccount
(ba2 . GetString
100)
Console.Writeline
0);
..")
return
j l
0;
//
{
BankAccount
- simule un simple
compte bancaire
public class
BankAccount
I I |es numros de compte commencent 1000 et augnentent l/ squentiellement partir de 1 static int nNextAccountNumber = 1000; // tient jour le numro de compte et le solde int nAccountNumber;
d'initiaiisation
Chapitre
I
{
public
.
]
{
Init(**nAceountNumber, 0'0)
public
BankAccount
(double dlnitialBalance)
;
tout
SankAccount
(int
/l
{
comnence avec
I rt
I F
^\
dlnitialBalance =
]
0:
dBalance = dlnitialBalance;
]
i
]
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
268
ublrrB
Q"a+an'
oy b Ltrnl,
nare t
,,^;-^ urll
initiales
valides
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 les numros de cornpte commencent 1000 et augmentent // squentiellement partir de 1 static int nNextAccountNumber = 1000; ll tient iour ie numro de comnte et le solde int nAccountNumber;
I
double dBalance; /l invoque 1e constructeur spcifique en fournissant I I des valeurs par dfaut pour les arguments manqunts
nrrhlin
Ranlrnn9111
lerrrrr.uvur.!\/
0 : thiS(0, 0) tl
. srr+v\v, w/
public BankAccount(double dlnitialBalance) : this (0, dlnitialBalance) {} // c'est le constructeur le plus spcifJ.que quL fait tout
I
I te vritable travail
BankAccount
public
t
II // if
t
I
(int
nlnitialAccountNumber,
1e dlnitialBalance)
doub
ignore 1es numros de compte ngatifs ; un numro de cornpte nu1 utiliser 1e prochain nunro disponible (nlnitialAccountNurnber (= 0)
;
nlnitialAccountNumber = **nNextAccountNunber
l
nAccountNunber
= nlnitialAccountNumber
269
// if
{
commence
avec
( \
uJ 0)
0;
j dBalance = dlnitialBalance;
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
constructeur BankAccount
dfaut 0 et 0.0
:
(int,
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
: this(0, d) {l
270
ue a(lare de
Si vous
ses
objets
On ne peut pas construire un objet sans un constructeur correspondant. dfinissezvolre propre constructeur, C# retire le sien. En combinant ces deux aspects, vous pouvez crer une classe qui ne peut tre instancie que localement. Par exemple, seule une mthode dfinie dans le mme espace de nom que BankAccount peut crer un objet BankAccount avec Ie constructeur dclar comme internal (pour en savoir plus sur les espaces de nom, reportez-vous au Chapitre l6) :
ll
{
Ba*.kAccount
public class
I
BankAccount
I Les numros de compte connencent 1000 et augnentent // sarrentieilement oartir de i static int nNextAccountNumber = 1000: ll tient jour 1e nunro ds ssmnte ot le snldp int nAccountNunber;
double dBalance: // invoque 1e constructeur spcifique en fournissant I I des valeurs par dfaut pour 1es arguments manquants
internal
{
BankAccount
()
nAccountNumber dBalance = 0:
= *tnNextAccountNumber
return String.Format
] ]
("11{Ol
{1 ;N1",
;
nAccountNunber, dBalance)
Ghapitre 12
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
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.
//
InheritanceExample
System;
using
{
nanespace InheritanceExanPle
public class
t
BaseClass
Console.!'lriteline ("SoneMethod l
l
")
BaseClass
public void
{
Some0therMethod0
:
l
l
public static
{
//
:");
=
;
2;
? 273
sc. Sonre0therMethod ( )
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
Dans un langage orient objetcomme t#, nous disons que la classe Student hrite de la classe Person. Nous disons aussi Que Person est une classe de base de Student, et que Student est une sous-classe de Person. Enfin, nous disons qu'un Student EST-UNE
rerson.
274
Bemarquez que la proprit IST-UN n'est pas rflexive:un student EST_UNE person, mais I'inverse n'est pas vrai. Une Person N'EST*PAS_UN Srudent. Un nonc comme celui-ci se rfre toujours au cas gnral. ll pourrait se trouver qu'une person particulire
soit effectivement un Student, mais beaucoup de gens qui sont membres de la classe Person ne sont pas membres de la classe Student. En outre,la classe Student possde
des proprits qu'elle ne partage pas avec la classe pe rs on. Par exemple, un studen r a une moyenne de points d'UV, mais une person ordinaire n'en a pas.
aussi une Person. Et il doit en tre ainsi : siun GraduateStudent EST_UN student et un Student EST-UNE Person, alors un GraduateStudent EST_UNE person. C0FD.
L'hritage est une proprit transitive. Par exemple, si je dfinis une nouvelle classe GraduateStudnt cOmme une sOus-classe de Student, alors un Graduatestudent St
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
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.
*\
e,
ry
Que les plus impressionnables d'entre vous ne s'affolent pas : ce listing est un peu long, mais il est divis en parties clairement distinctes.
--"-ffi
l/ ll
i
SinplesavingsAccount
inplnente un SavingsAccount
coiture
forne
d'un
System;
BankAccoTJnt
using
nanespace SimpleSavingsAccount
I I II
I
I
BankAccount
sinule un conpte bancaire possdant un numro de compte (assign La cration du conpte) et un solde
public elass
t
II //
BankAccouni
et
augnentent
public static
int
1000;
Ie solde pour
chaque objet
public deciural -l
mBalance
de compte
2 76
ob
jet
InitBankAccount (0) l
nAccountNunber
**nNextAccountNumber
nBalance = mlni"tialBalancei
//
t
Balance (so1de)
Balance nBalance;
]
public decimal
get I return
]
//
{
Deposit
- tout )
0)
dpt
positif est
autoris
public void
if
{
(mAmount
nBalance *= mAnount;
l
]
1/ I'lithdraw
I
I
if
{
- tout retrait est autoris jusqu' la valeur du solde ; retourne 1e nontant retir
mWithdrawal)
(mBalance
(=
mWithdrawal)
return
]
nrWithdrawal;
//
{
ToString
public string
//
{
SavingsAccount
compte bancaire
BankAccount
100)
? 277
InitSavingsAccount
(0,
mlnterestRate)
this.mlnterestRate : mlnterestRate /
l
Ini-tBankAccount (mInitia1 )
tO0:
// Accurnulatelnterest t
invoque une
fois par
priode
//
t
ToStri-ng
- met le
compte sous
forne de chane
{11,')
",
ToBankAccountString0, mlnterestRate
100)
ba.Deposit(100); Console.1,{riteline("Conpte {0J", ba.ToBankAccountString0 ) ; I I et naintenant un compte rmunr SavingsAccount sa = new SavingsAccount();
sa. InitSavi.ngsAccount
(100,
(
12. 5M)
sa.Accumulatelnterest
; ;
Console.i^lriteline("Compte {01", sa.ToSavingsAccountString()) // attend confirmation de 1'utilisateur Console.WriteLine("Appuyez sur Entre pour terminer. ..") ;
Console.Read0;
return
'|
0;
J 1
)
1
rrr i n'est gure cliffrente de celles qui apparaissent dans d'autres chapitres de ce livre. Elle commence par la fonction d'initialisation sLlrcharge lri'. 3:rri..,rc.roiir'- | I : une pour les comptes qui Sont Crs avec un Solde initial, une autre pour ceux qui devront se contenter de comnlencer cle zrtl.
La classe Bank,!.,.
278
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.
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.
- i'ai du mal
La relation entre SavingsAccount et BankAccount n'est rien d'autre que la relation fondamentale EST-UN. Pour commencer, je vais vous montrer pourquoi, puis je vous montrerai quoi ressemblerait une relation A_UN.
La relaton EST UN
La relation EST-UN entre -s3r.,i11gsr\ccount et BarrkAccount est mise en vidence par la modification suivante c 1a s s 1 dans le programme SirnpleSa-,'iiigsAccouni- de la section prcdente :
pub-Lrc
I
t
class Classl
ef f
/ fti rpetttennsi f -
chnrrp LrtcYu
Chapitre
2:
? 27I
rlpnim:i
ba. Deposit (mPay)
;
mPnrr)
public static
t I
{0J
I et naintenanr un compre
",
ba.ToBankAccountString0 ) ;
rnunr
DirectDeposit(sa,
Console,
{0J " , sa, TosavingsAccountString 0 ) ; // attend confirmation de 1'utilisateur tonsole.T,Iriteline("Appuyez sur Entre pour terniner...',) ;
i,lriteline ( "Conpte
Console.ReadO;
return
] ]
0;
Les effets de ce programme n'ont pas chang. La seule vritable diffrence est que tous les dpts sont maintenant effectus par la fonction locale DirectDeposit | ), Les arguments de cette fonction sont le compte bancaire et le montant dposer. Remarquez (c'est le bon ct de la chose) que l'lain ( ) peut passer DirecrDeposit O soit un compte ordinaire, soit un compte rmunr, car ur.t SavingsAccc'unt EST_UN Bank,:corrni et en reoit par consquent tous les droits et privilges.
membres de BankAccount
//
{
SavingsAccount
public class
//
InitsavingsAccount
exprin
en
28
et
100)
imal mlnterestRate)
100:
//
{
nrrbl
fois par
priode
bankAccount.mBalance
= bankAccount.mBalance
+
j
(bankAccount.nBalance
* mlnterestRate) ;
mAmount)
i
I
I t
/ I
Withdraw
- tout retrait est autoris jusqu' la valeur du solde : retourne 1e montant retir
rnWithdrawal)
;
bankAccount, Withdraw(mWi.thdra,,+a1 )
Ici, la classe Sa'ringsAccount contient un membre donne bankAccount (au lieu cl'en hriter de'laril.,."ie rri)-n:). L'objet bankAccount contient le solde et le numro clu compte, irtformations ncessaires pour la gestion du compte rmunr. Les donnes propres un compte rmunr sont contenues dans la classe Sa.,,'ing.:ticit.ult t
Dans ce cas, nous disons eue...,:^'.':',.E.----,-r'
rii:'t
A_UN BanitAccounl--.
La relation
A UN
:
La relation A_UN est fondamentalement diffrente de la relation EST_UN. Cette diffrence ne selnble pas rnauvaise dans I'exemple de code suivant
I cr.e un nouveu compte rmunr BankAccount ba = new BankAccount 0
0;
I et y
dpose
cent euros
;
sa.Deposit(100)
? 28 |
/
.
sa
ll DirectDeposit - effectue autonatiquenent 1e dpt d'un void DirectDeposit(BankAccount ba, int nPay)
{
chque
]
t
ba.Deposit (nPay)
void SoneFunction0
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.
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
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( il ) "9K \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
SavingsAccount sa = ba ba sa
netll
: sa; = (BankAccount)sa; : :
ba;
ll
SavingsAccout
1e
bas
te est
admise
1e haut
sa
(SavingsAccount)ba;
La premire ligne stocke un objet SavrngsAccoinr- dans une variable BankAccoLlnt. C# effectue pour vous cette conversion. La deuxime ligne
Savingsccounr.
? 2 83
La n'est -.of4Q est proprit EST_UNvoiturepa.s rflexive. Autrement dit, mme si Explorer jE: \ une voiture, une 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 \-,/
l'excution
En gnral, le castir-rg cl'un objet de lai,i,...CCo.1fr1- Sa,"'ingsAccount st une opration dangereuse. Consiclrez l'exentple suivant :
ProcessAmount(BankAccount bankAccount)
le
compte
]
yuurrL ^',L1.i^ a+a+.ia LoLlL "^.i,l vulu T^^+/r^^+/\ IcLUoL\l
SavingsAccount sa = nehr SavingsAccount O ProcessAnount (sa) ; BankAecount ba = new BankAccount 0 ; ProcessAmount (ba) ;
l
ProcessAnaount () excute un certain nombre d'oprations, dont I'invocation de la mthode Accr:nulateliri-ef esr i'r. Le cast de ba un SavingsAccount est ncessaire, parce eue ba est dclar comm urr BankAccount. Le programme se compile correctement, parce que toutes les conversions de type sont faites par un cast explicite.
Tout se passe bien avec le premier appel f'rocessAmount O, dans Test O. L'objet sa de la classe Sa..ingsi\ccoririt est pass la mthode ProcessAmount l). Le cast de Fai'k.\cco'.rnr- Sa-ringsAccount ne pose pas de problme parce qlle I'objet ha tait de toute faon I'origine un objet Sar.ings . .r'. .
Le deuxime appel Pr o..rss.i.11,-r,lrr i n'est toutefois pas aussi chanceux. ' Le cast Sa.,'irrg,sAr:r:crrnT- ne peut pas etre autoris. L'obiet ba rI' pas de mthode Ac cunuiat r-: i nt e r e st
1
28
ob
jet
s/\
Af\
e,
Une conversion incorrecte gnre une erreur I'excution du programme (ce qu'on appelle une erreur run-time). Une erreur I'excution est beau-
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-
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)
{
TestCast
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.
? 28 5
D'un ct, je vous recommande fortement cle protger tous vos casts vers le haut avec I'oprateur i s pour viter le risque d'une erreur I'excution, d'un autre ct, je vous conseille d'viter tout cast vers le haut, si possible.
La classe ob
j ect
Les classes suivantes sont en relation les unes avec les autres:
public class
MyBaseClass {}
I'lyBase0lass {}
La relation entre ces deux classes permet au programmeur d'effectuer le test suivant
GenericFunction(MyBase01ass
mc)
une sous-classe..,
ll ...alors
I
1e
MySubClass msc
traite
(MySubClass)mc;
suite
l
]
]
Dans ce cas, la fonction Gene r icFunc r i on O diffren cie les sous-classes de MyB a s eC l
as
en utilisant le mot-cl
is.
Commentfaire la diffrence entre ces classes, apparemmentsans lien entre elles, en utilisant 0prateur is ? C# tend toutes ces classes partir de leur classe de base commune, object. Autrement dit, toute classe qui n'hrite pas spcifiquement d'une utre classe hrite de Ia classe object. Ainsi, les deux dclarations suivantes sontidentiques:
le mme
la
Test
286
o)
if
I
r
(o
is l'IyClass1)
=
(MyClass1)o;
MyClassl mcl
tl tt
)
l
'I
GenerlcFunction ( ) peut tre invoque avec n'imporle queltype d'objet. extraira des hutres ob j ect toutes les perles de MyClass 1.
Le mot-cl :-s
L'hritage et le constructeur
Le programme Inherir-anceExampl e que nous avons vu plus haut dans ce chapitre repose sur ces horribles fonctions Inlt. . . pour initialiser les objets BatrkAccoufit et SavingsAccoLiitt en leur donnant un tat valide. quiper ces classes de constructeurs est certainement la meilleure manire de procder, mais elle introduit une petite complication.
| I
I
e.,-+^JJ D LEIT,'
autonatiquenent
,,^.:-^ urlr
namespace
t
InheritingAConstructor
C1ass1
public class
t
? 287
public static
I
int Main(string[]
args)
I
Base0lass bc = new Base0lassO; Console.Writeline("\n'laintenant, cration d'un objet Subclass") SubClass sc * nefir SubClass 0 ; l/ attend confirnation de 1'utilisateur ; Console.Writeline("Appuyez sur Entre pour terniner"'") Console.nead0;
return
l
public class
{
BaseClass
public
{
BaseClass
0
;
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
'
beaucoup aux diffrents une hirarchie de classes hrites ressemble trouve au-dessus des classes tages d'un immeuble. Chaque classe Se
288
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.
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
Console.hlriteLine("\nlnvocation de SubClass(int) "1 ; Sub0lass sc2 = new SubClass(0); // attend confirmation de 1'utilisateur Console,i.IriteLine("Appuyez sur Entre pour terminer.
Console.Read O
;
. . ")
return
]
0;
public class
{
BaseClass
public
{
BaseClass
0
r
publie BaseClass(int i)
t
COnSOle .l,/s
itoT.i
")
Base0lass
? 289
n11n | 1a
\11n1
Iteq
/\
Console.
l oublic SubClass(int i)
{
Le programme commence par crer un objet par dfaut. Comme prvu, C# invoque le constructeur par dfaut cle SiibClar;s, qui commence par passer le contrle au constructeur par dfaut de BaseClass. Le programme cre alors un objet, en passant un argument entier. nouveau comme prvu, C#
invoque SubClass (int). Ce constructeur invoque le constructeur par dfaut de BaseCjass, colTtme dans I'exemple prcdent, car il n'a pas de
donnes passer.
Un constructeur d'une sous-classe peut invoquer un constructeur particulier de la classe de base en utilisant le mot-cl base. est trs similaire ^$tf$ Ce ;( il ) un autre de la mme classe en utilisant le mot-cl this. Pour tout savoir sur les constructeurs avec this, voyez le Chapitre 11' \ry
procd
// II II
InvokeBaseConstruetor
nontre
coTnment une sous-classe peut invoquer le constructeur de 1a clagse de base de son choix en utilisant 1e mot-cl base
using
System;
nanespace TnvokeBaseConstructo r
290
public
t
BaseClass
()
l public BaseClass(int i)
{
Console.I^IriteLine("Construction de BaseClass(
l l
[0])", i) ;
BaseClass
public
{
SubClass
()
public SubClass(int
{
public class
t
C1ass1
public static
t
")
Console.WriteLine("\nlnvocation de SubClass(t, 2) ") ; SubClass sc2 = ner,r SubClass(1, 2); // attend confirmation de 1'utilisateur Console . l'/riteLine ( "Appuyez sur Entre pour terminer.
Console,Read0;
..")
return l
]
0;
l
Ce programme donne la sortie suivante Invocation de SubClass 0
:
Invocation de SubClass(1,
2)
? 2g
Construction de BaseClass ( 1 ) tonstruction de SubClass (1, 2) Appuyez sur Entre pour terminer...
La classe BankAc
ount nodife
Le programme ConstructorSavingsAccounr est une version modifie du programme SinpleBankAccount. Dans cette version, le constructeur de SavingsAccount peut repasser des informations aux constructeurs de BankAccount. Seuls Main O et les constructeurs eux-mmes apparaissent ici
*u"uli3 ,\tr
// ll ll II
t
ConstructorsavingsAccount
colnme
aucune mthode
System;
virtuelle,
mai.s implmente
using
namespace ConstructorSavingsAccount
/i | II
|
t L
BankAccount
possdant
public cLass
II /l
public static int nNextAccountNumber = 1000; ll net jour le nunro de comntp pt le solrle nour public int nAceountNumber; public decimal nBalance;
//
t
tonstrueteurs
BankAccount
public
]
-(
/\ this /^\ )
:
(0
public
Bankccount
(decinal nlnitialBalance)
292
mme
chose
ici
intrts
//
{
savingsAccount
public class
exprim en pourcenrage (valeur comprise entre 0 et 100) n'rl, 1.i^ c-,,.;-^^^. puur_r-c avr_ngsAccount(decirnal nlnterestRate) : this(0, mlnterestRate)
{
// I
tnitsavingsAccount
: base(nInitial)
this.mlnterestRate = nlnterestRate
l
I ]
tOO;
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)
{
DirectDeposit(ba,
100)
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
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.
BankAccount(0)
d'un objet
en utilisant le c0nstructe u r par dfaut.
gl
la valeur par
donne au solde
dfaut 0
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)
.
.
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
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
temps normal, le ramasse-miettes opre en silence l'arrire-plan. ll ne prend le contrle du programme qu' de brefs moments, lorsque le tas est sur le point d'tre court de mmoire,
Le destructeur de C# est non dterministe parce qu'il ne peut pas tre invoqu avant que I'objet it t rcupr par le ramasse-miettes, ce qui peut se produire longtemps aprs qu'il
a cess d'tre utilis. En fait, si le programme se termine avant que l'objet soittrouv par le ramasse-miettes et retourn au tas, le destructeur n'est pas invoqu du tout.
Au bout du compte, l'effet qui en rsulte est qu'un programm eur C# ne peut pas se reposer sur le destructeur pour oprer automatiquement comme dans un langage comme C++.
Chapitre 13
polymorphisme
Dans ce chapitre :
L'embarras du choix : masquer ou craser une mthode de la classe de base. Construire des classes abstraites : parlez-vous srieusement ? Dclarer comme abstraites une mthode et la classe qui la contient.
Faire commencer une nouvelle hirarchie au-dessus d'une hirarchie existante. Empcher qu'une classe puisse tre transforme en sous-classe.
hritug" permet une claSse "d'adopter" les membreS d'une autre. Ainsi, je peux crer une classs 5st,ingsAccount qui hrite de membres donne comme inBalance et de mthodes comme Deposit O de la classe de base BankAccount. C'est trs joli, mais cette dfinition de I'hritage ne suffit pas reprsenter convenablement ce qui se passe dans le
monde rel.
llu
jlff tl$/, Y
Si
vous avez besoin de vous rafralchir la mmoire sur I'hritage des classes, relisez le Chapitre 14.
Un four micro-oncles est un type de four, non pas parce qu'il a I'air d'un four, mais parce qu'il remplit les mmes fonctions qu'un four. Un four micro-ondes remplit aussi des fonctions supplmentaires, mais au moins, il remplit les fonctions de base d'un four - et le plus important, c'est qu'il fait chauffer mes nachos quand je dis "StartCooking". Le processus interne que doit mettre en uvre le four pour remplir sa mission ne m'intresse pas, pas plus que le type de four dont il s'agit, ni son fabricant.
296
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.
Ce n'est
PY
-u#t$ Donner le mme nom deux fonctions (ou plus) s'appelle surchorger un \ =i -^- ) no- de fonction.
Les arguments d'une fonction font partie de son nom complet, comme le montre I'exemple suivant : oublic class
I
l4vClass
nrrhlin
t
/
)
I f.aire
quelque chose
rrnirl rrnntinn/int\
srr urv.r \Jrrr/
nrrhl in cfrtin
t
chose d'autre
? 29 7
AFunetion0;
Alunction(l);
AFunction(2.0);
j
C# peut diffrencier les mthodes par leurs arguments. Chacun cles appels dans Main O accde une fonction diffrente. Le type retourn ne fait pas partie du nom complet. Vous pouvez avoir deux fonctions qui ne diffrent que par le type retourn.
#5
e,
MyClass
public
{
UrClass
public static void AFunction0; public static void AMethod0; public class C1ass1
t
publi.c static
{
vo
/\ 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
) (avec
la vtre, a marche).
29 8
ob
jer
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)
// 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
; :
nAmount)
i
I
ll
.,
mBalance
mAmountToWithdrar,v ]
mBalance
mBalance
-=
nAmountToWithdraw;
rar^r
:
return
mAnountTo}.fithd
2gg
'I
class Mytlass
{
public void
t
I
1
SomeFunction0
I le veux ne crer
un conpte rrnunr
BankAccount ba
r
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
HidinsWithdrawal - redfinit la mthode de retrait de la I .... II classe de base avec une mthode de La II sous-classe du mne non
i
t,
using Systen;
nnespace Hidi.ngl,lithdrawal
t
public
t
BankAccount
(decimal mlnitialBalance)
;
mBalance = mnitialBalance
1
public decimal
{
Balance
}
nrrhl t
t
mAmount)
decinal
if
mAmountoWithdrarar
= mBalance
l
mBalance
-= nAnountTol,lithdraw;
;
return
mAnountTol,Ii.thdraw
300
//
I
SavingsAccount
compte bancaire
BankAccount
//
SavingsAccount
- 1it
et 10)
public
SavingsAccount (decimal
dec
mInitialBalance,
irnal mlnterestRate)
:
r
I
1
base
(mInitialBalance)
100;
this.mlnterestRate = mlnterestRate /
)
// Accumulatelnterest r I
invoque une
!L!v u
fois par
priode
i
\
)
/
mBalance
mBalance
(mBalance
mTnterestRate);
//
Withdraw
- tout retrait est autoris iusou' 1a valeur du solde ; retourne le montant retir
F
// soustrait 1.50
base. 'vlj-thdraw( | | VVU I I "^"^ UVUVgL
l . 5t't) ;
maintenant EtlELLUtrl lnaLLLLgllIlL effectuer un Ull
;
retrait
!CL!1L
return
l
]
nlrhri^ PUUTfL ^t^^a Urdb
t r^^^1 ufdD
MakeAWithdrar+al(BankAccount ba,
decimal nAmount)
ba. Withdraw (mAmount)
;
retire
100
F, et
00M)
// essaie de faj-re la
sa.llithdraw(100M)
;
mme
? 30 I
Console.l,iriteLine("Quand il est invoqu directenent ;") Console.l,rlrj-teLi.ne("Le solde de BankAccount est {0:C1",
ba.Balance); Console.l,iriteLine
(
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
.
t(9, y
mesure du possible. faites en sorte que ce soit la classe de base qui manipule
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
302
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.
trtlj.thdrarv
(
(decimal)', car i1
imal
le
membre
hrit
dec
)'
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 ?
iNmoinsdepasser|afentreSortiepourvoircequiyestaffich'Dans
presque tous les cas, vous y verrez un avertissement qui vous prvient que quelque chose pourrait bien vous mordre si vous n'y mettez pas bon ordre.
Le descripteur ne" indique C# qu'une mthode est redfinie intentionnellement et que ce n'est pas le rsultat d'une ngligence :
//
{
new
I
U
:(dw
Cette utilisation du mot-cl new n'a rien voir avec I'utilisation du mme mot-cl pour crer un objet.
permettrai de faire remarquer ici que c'est I'une des choses que je 1r${Qa" Je me \ trouve agaantes chez C# (et C++ vnt lui) : faites ce que vous voulez ^r,7rt7 avec mes mthodes, mais ne surchargez pas mes mots-cls. Quand je dis \/ ) new, c'est que je veux crer un objet. Ils auraient pu utiliser un autre motcl pour indiquer une surcharge intentionnelle.
? 303
ReUenr
la ba s e
Revenons la mthode savingsAccounr .1,{ithdraw O de I'exemple que nous avons vu plus haut dans ce chapitre. L'appel BankAccount . w"-thd r aw ( ) depuis cette nouvelle mthode contient le motcl supplmentaire ba s e.
La version suivante de la fonction avec ce mot-cl supplmentaire ne marche pas :
new
{
if
t ]
(++nNunber0fl^lithdrawalsThisPeriod
dArnountl,lithdravn
1)
t=
l,lithdraw (1 . 5 ) ;
;
return
l
dAnountl{ithdrar+n
tn]; I I je m'appelle
moi-nne
l L'appel fn O depuis fn 0 aboutit s'appeler soi-mme, sans fin. De mme, un appel de Withdraw 0 elle,mme fait qu'elle s'appelle elle-mme en boucle, comme un chat qui court aprs sa queue, jusqu' ce que Ie programme finisse par se planter. D'une manire ou d'une autre, il vous faut indiquer C# que I'appel depuis SavingsAccount . Withdraw ( ) est l pour invoquer la mthode de la classe de base, BankAccount . \,/ithdraw 0 . Une solution consiste faire un cast du pointeur this dans un objet de la classe BankAccounr avant d'effectuer I'appel.
ll
{
//
tlithdraw
new
I
base en dfinissant explicitement 1e cast de 1'objet this public double Withdrar,r{double dWithdrawal) dans un
BankAccount
30 4
= (BankAccount)this; invoque Withdrar.+0 en utilisant cet objet BankAccount appelle 1a fonction BankAceount.Withdraw0 double dAmountl^lithdrawn = ba.Withdraru(dWithdrawal) I if (**nSurrber0fWithdrawalsThisPeriod ) 1)
BankAccount ba
// i/
{
dAnountWi.thdralrn
]
f=
ba , I^Iithdrar,r (1
.5
return
dAtnountWithd
rar,un :
Cette solution fonctionne : I'appel ba . l,,Jithd raw ( ) invoque maintenant la mthode BankAccor.lnt, comme on le voulait. L'inconvnient de cette approche est la rfrence explicite Bani.;Account. Une modification ultrieure du programme pourrait modifier la hirarchie d'hritage de telle manire que -sa.,'ings,.iccoLlnr n'hrite plus directement de BankAccount,. Une telle rorganisation brise cette fonction d'une faon qu'un nouveau programrneur pourra avoir du mal trouver. Pour moi, je n'arriverais jamais trouver un bogue comme celui-l. Il vous faut un moyen de dire C# d'appeler la fonction ''^Iithdrawo depuis "la classe qui est juste au-dessus" dans la hirarchie, sans la nommer explicitement. Ce serait la classe qui est tendue par savirrgsAcc'.-'illrt. C'est dans ce but que C# comporte le mot-cl base.
=t
.-!/
C'est le mme rnot-cl base qu'utilise un constructeur pour passer des arguments au constructeur de la classe de base. Le mot-cl base de C# est la mme chose que this, mais redfinit le cast la classe de base, quelle que soit cette classe :
ll II
t
/
II
With&aw
- tout retrait est autoris jusqu' la valeur du soide ; retourne l-e montant retir
new
/ sousrrait 1 .50 F
1
base. l,lithdraw(
.5M)
avec ce
qui reste
base.Withdrai,r(mWithdrawal)
invoque maintenant la mthode vitant par-l I'cueil qui consiste s'invoquer elle-mme. En outre, cette solution ne sera pas brise si la hirarchie d'hritage est modifie.
13ankccorLn: . iriit-hrir ar' O ,
L'appel base.,,iit-hdra-,
(;
? 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
decinal
{
rnAmount)
BankAccount ba;
SavingsAccount sa;
ba = new BankAccount(200M);
MakeAWithdrawal MakeAWithdrawal
(ba,
100M)
Console.WriteLine("\nvoqu par un intermdiaire,") ; Console.l,iriteLine("Le solde de BankAccount est [0:C]", ba.Balance) ; Console.1{riteLine("Le solde de SavingsAccount est {0:CJ", sa.Balance) /l attend confiriiiation de 1'utilisateur Console . l,trriteLine ( "Appuyez sur Entre pour terniner. . . " ) ;
Console.
Read
return
]
0;
306
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
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.
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
? 307
{:/rr
=qE)
signifie forme, et isme signifie peut-tre quelque chose en grec. Polymorphisme et late binding ne sont pas exactement la mme chose,
La de se
K mais la diffrence est subtile. notion polymorphisme rfre la possibilit de dcider I'excution qu'elle mthode invoquer. La notion '(dg, de late binding rfre la manire dont un langage implmente
se le
polymorphisme.
Le polymorphisme est un aspect crucial de la puissance de la programmation oriente objet. Il est si important qu'un langage qui ne le comporte pas ne peut pas tre prsent comme un langage orient objet.
'(dE,)
1t$!Qa. \ Un langage qui comporte des classes mais pas le polymorphisme est ^.v7q appel langage base objets. Le langage Ada en est un exemple.
Sans le polymorphisme, I'hritage n'aurait gure de signification. Je vais
donner encore un exemple pour vous le montrer. Imaginez que j'aie crit ce programme succs mondial qui utilisait une classe nomm Student. Aprs quelques mois de conception, de codage et de test, je publie cette application pour rcolter les avis de mes collgues et des critiques en tout genre (on a mme parl de crer une nouvelle catgorie de prix Nobel pour le logiciel, mais par modestie jai ignor ces suggestions).
Le temps passe, et mon patron me demande d'ajouter ce programme la prise en compte des tudiants diplms, qui ne sont pas tout fait identi-
ques aux tudiants ordinaires (ils revendiquent sans doute de ne pas tre identiques du tout). Supposez que la formule de calcul des frais de scolarit soit compltement diffrente de celle utilise pour un tudiant ordinaire. Mais mon patron ne sait pas et ne veut pas savoir qu' ce niveau de profondeur du programme il y a de nombreux appels la fonction calcTuition O .
void SomeFunction(Student
t
s)
s.CalcTuition0;
l
il me faudrait passer en revue le code de sorleF,;ncticn O pour vrifier si I'objet student qui lui est pass est un Gradua*r-eS*.udent ou un Student. Le programme appellerait Student. CaicTuition ( ) lorsque s est un Student, et GraduateStudent. CalcTuition O lorsque c'est un tudiant diplm.
Si C# n'admettait pas le late binding,
308
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.
MakeAWithdrawal(BankAccount ba,
decinal
mArnount)
if ba is
{
Savi-nesAccount
SavingsAccount sa
el se
(SavingsAccount)ba;
;
sa . Withdraw (urAmount )
ba . Withdraw(mAmount )
Maintenant, quand l'{aln O passe la fonction un objet SavingsAccounr, MakeA\ii thd rawaf ( ) vrifie I'excution le type de I'objet ba et invoque Sa';ingsAccount . i,r'ithdr a"' ( )
.
=[J\y \/
og$!ea^ Au passage, je vous signale que le programmeur aurait pu raliser le cast et ae/!!t\ I'appel dans une mme ligne : ( (SavingsAcr.lount )ba) .',,^/ithdraw (mAmount.) . 9/nl?Y \ - " j 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.
? 309
mauvaise ide. Elle ncessite que SomeFunction O connaisse tous les diffrents types d'tudiants et quels sont ceux qui sont reprsents par les diffrentes classes. Cela fait peser une trop lourde responsabilit sur les paules de la pauvre vieille SomeFunction O . Pour le moment, mon application ne connat que deux types de comptes bancaires, mais supposez que mon patron me demande d'en implmenter un nouveau, CheckingAccount, t que celui-ci soit associ une autre politique de Withd raw ( ) . Mon programme ne fonctionnera pas correctement si je ne trouve pas toutes les fonctions qui testent I'excution le type de cet argument.
En fait, I'approche
diffrents types de compte. Je veux que ce soit aux programmeurs qui utilisent SomeFunction O de connaltre leurs types de compte, et qu'ils me laissent tranquille avec a. Je veux que ce soit C# qui prenne les dcisions sur les mthodes invoquer en fonction du type de I'objet I'excution. Pour dire C# de faire le choix I'excution de la version de Withdrawai o utiliser, je marque la fonction de la classe de base avec le mot-cl virtual, et la fonction de chaque sous-classe avec le mot-cl overr,de.
J'ai rcrit I'exemple de programme prcdent en utilisant le polymorphisme. J'ai ajout des instructions de sortie aux mthodes L/ithdraw ( ) pour montrer que ce sont effectivement les bonnes mthodes qui sont invoques ('ai supprim ce qui faisait double emploi pour ne pas vous ennuyer avec des choses inutiles). Voici donc le programme Polymorphiclnheritance:
,,"$$Ig
,,.&v- l- lrJ,r ,\ l-..ffvn
ry
I I"\7
// II
t
folynorphiclnheritance
System;
* utilise le
e
redfinir
e Polymorphic Inheritanc
using
nmespc
/i
I
{
BankAccount
public class i
BankAccount
L ,
if
I
la
ici
mAnount)
rnAnount
(nAmountToWithdraw
mBalance)
3 |0
mAmountTol^lithdraw
mBalance
l
nBalance -= mnountToWithdraw;
return
l
mAmountToWithdraw
//
I
t
SavingsAccount
compte bancaire
BankAccount
l , la mme chose ici- aussi / tllthdraw * tout retrait est autoris jusqu' la valeur II du solde ; retourne le nontant retir
i
tl
retrait
return
)
public class
t
C1ass1
MakeAWithdrawal(BankAccount ba,
decimal
ba. Withdraw(mAmount) ]
;
mArnount)
..
i^-i ici
l
l l
thd ra',\i i. ) est marque comme .,,i rrual dans la classe de base BarrkAc.culrt. alors que la mthode i,,ithdraw O de la sous{lasse est marque avec le mot<l L).u,errlCe. Bien que la mthode MakeAl,/ithdrawal 0 soit inchange, le programme donne une sortie cliffrente parce que I'appel ba.l^rithri ra-w ( ; est rsolu sur la base du type de ba I'excution.
La fonction
-r,r'i
? 3l I
^$C .z 7X t(?, Y
Pour vous faire une ide prcise de la manire dont tout cela fonctionne, il est ncessaire que vous excutiez le programme clans le dbogueur de Visual Studio. Gnrez le programme comme d'habitude, et appuyez sur la touche Fl1 autant de fois que ncessaire pour faire avancer le programme pas pas. Il est impressionnant de voir le mme appel aboutir une mthode ou une autre selon le moment o il se produit.
La ytriode abstraite de C#
ce que je sais, un canarcl est un type cl'oiseau, de mme qu'un colibri et une hirondelle. En fait, tout oiseau est un sous-type d'oiseau. La rciproque de ce principe est qu'il n'existe pas d'oiseau qui ne soit pas un soustype d'oiseau. Cette remarque ne paralt pas trs profonde, mais en fait, elle I'est. L'quivalent logiciel de cet nonc est que tout objet oiseau est une instance d'une certaine sous-classe de Ci seau. Il n'y a pas d'instance de la classe Oisea'i.
Il y a divers types d'oiseaux qui partagent de nombreuses proprits (sinon, ils ne seraient pas des oiseaux), mais il n'y a pas deux types dont toutes les proprits sont les mmes (sinon, ce ne serait pas deux types diffrents). Pour prendre un exemple particulirement simple, tous les oiseaux n'ont pas la mme manire de Voier O. Le canard a son style, I'hirondelle aussi. Ils ont beaucoup de points communs, mais ce n'est pas exactement la mme chose. Le style du colibri est compltement diffrent. Ne me posez pas de questions sur les meus et les autruches.
Mais si tous les oiseaux n'ont pas la mme manire de voler, alors, qu'est-ce que Oiseau. Voler () ? La rponse est simple : c'est le sujet de cette section.
3 |2
Figure 13.1 Une description sous la forme UML des classes Hi ghS choo i
:
University
- numStudents
Student
+ Enroll 0
- numStudents + nAvgSAT
et Unive r
+ Enroll 0
+ GetGrant 0
sily.
Le langage UML
Le langage
dfinir clairement une grande partie des relations entre des objets dans un pr0gramme. L'un des avantages d'UML est que I'on peut en ignorer les aspects les plus spcialiss sans en perdre entirement la signification.
Les caractristiques de base d'UML sont les suivantes
:
tl tl tl
Une classe est reprsente par un rectangf e, divis verticalement en trois sections. Le nom de la classe apparat dans la premire section en partant du haut.
Les membres donne de la classe apparaissent dans la section du milieu, et les mthodes dans la section du bas. Si la classe n'a pas de membres donne ou de mthodes, vous pouvez omettre la section du milieu ou celle du bas.
Les membres prcds par un signe + sont publics, et ceux qui sont prcds par un signe - sont privs. UML ne dispose pas de symboles pour reprsenter la visibilit et la protection.
Un membre priv n'est accessible qu' d'autres membres de la mme classe. Un membre public est accessible toutes les classes.
tl
Le symbole "{abstract}" ct d'un nom indique que [a classe ou la mthode est a bstraite. UML utilise en fait un symbole diffrent pour une mthode abstraite, mais je simplifie.
? 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
High School
Figure 13.2
:
L'hritage de
- numStudents
HighSchcol
simplifie la
c
+ Enroll 0
lasse
-
Univer
sity,
mais
il
University
introduit
quelq ues
+ nAvgSAT
+ GetGrant 0
problmes.
Vous me direz : "Et alors ? I'hritage fonctionne, et a fait du travail en moins." C'est vrai, mais les rserves que je fais ne sont pas que de triviales questions de style. De fausses reprsentations de ce genre sont sources de confusion pour le programmeur, dans I'immdiat comme par la suite. Un jour, un programmeur qui ne connaltra pas mes astuces de programmation devra lire mon code et comprendre ce qu'il fait. Une reprsentation fausse est clifficile comprendre et utiliser.
En outre, de telles reprsentations fausses peuvent conduire ultrieurement des problmes. lmaginez que la high school dcide de nommer un tudiant "favori" pour son banquet annuel (usage local). Le programmeur, astucieux, ajoute alors la classe HighSchool la mthode \larneFavorit.e O, que I'appli-
cation invoque pour nommer favori I'objet Student correspondant. Mais maintenant, j'ai un problme. La plupart des universits n'ont pas pour pratique de nommer quoi que ce soit favori, mais aussi longtemps que Universi-ty hrite de uighschool, elle hrite de la mthode NameFavorite (). Une mthode supplmentaire peut sembler sans importance. Vous pourriez dire : "ll suffit de I'ignorer."
Une mthode de plus n'a pas grande importance, mais c'est une pierre de plus dans le mur de la confusion. Avec le temps, les mthodes et les proprits supplmentaires s'accumulent, jusqu' ce que la classe iJni versity se trouve bien encombre de tous ces bagages. Ayez piti du pauvre dveloppeur de logiciel qui doit comprenclre quelles mthodes sont "vritables" et lesquelles ne le sont pas.
Un tel "hritage de complaisance" conduit un autre problme. ta manire dont elle est crite, la Figure 13.2 implique qu'une Uni-rersity et une HighSchool ont la mme procdure de recrutement. Si peu waisemblable que cela paraisse, supposez que ce soit wai. Le programme est dvelopp, emball
? 3l 5
et expdi au public qui n'attend que lui (bien str, je n'ai pas oubli d'y mettre le nombre de bogues ncessaires pour que tout le monde veuille s'offrir, moyennant un prix tout fait raisonnable, la mise jour vers la version 2).
Quelques mois passent, et l'tablissement dcide de modifier sa procdure de recrutement. Il ne sera pas vident pour tout le monde qu'en modifiant la procdure de recrutement de la high school c'est aussi la procdure d'inscription au collge voisin qui a t modifie.
Comment faire pour viter un tel problme ? Une solution est de ne pas aller l'cole, mais une autre consiste corriger la source du problme : une universit n'est pas un type particulier de high school. Il existe bien une relation entre les deux, mais cette relation n'est pas EST_UN. Au contraire, universits et high schools sont deux types diffrents d'tablissement scolaire. La Figure 13.3 dcrit cette relation. La classe School nouvellement dfinie contient les proprits communes des deux types d'tablissement, y compris leurs relations avec les objets Student. School contient mme la
mthode commune Enro11O, bien qu'elle soit abstraite car Highschool et University ne I'implmentent pas de la mme manire.
School {abstract}
numStudents
E
Figure 13.3
:
+ Enroll 0
- {abstract}
o1 -
Hl ghS cho
9t
s
ity
Univer
doivent
+ nAvgSAT
School.
+ Enroll 0 + NameFavorite
+ Enroll 0
+ GetGrant 0
Les classes HighSchool et University hritent maintenant toutes deux d'une classe de base commune. Chacune contient des membres qui lui sont propres:l'TameFavorite O dans le cas de HighSchool, et GetGrant () pour University. De plus, ces deux classes substituent la mthode Enro1l ( ) une redfinition de celle-ci dcrivant le mode de recrutement de chaque type d'tablissement.
3 |6
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.
r--
L{"+,q
Suprieur
supplmen-
;r Lqi:e
Ecole suprieure
l
Universit
Classes prparatoires
Vous
Sch,:o
voir clue j'ai insr une nouvelle classe entre Universitv et -.11,,,,1i ir 1re. J'ai subdivis cette nouvelle classe en Collese et
? 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".
flasses
dpend en partie du problme
rsoudre.
i
l
I
I I
Collge
Classes prparatoires
ll ne me reste Qu'un
concept
:la
classe abstraite
Si intellectuellement satisfaisant que puisse tre le factoring, il introduit un problme qui lui est propre. Revenez encore une fois BankAccount. Pensez un instant la manire dont vous pourriez dfinir les diffrentes
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
"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.
// Abstractlnheritance - 1a classe BankAccount est vraiment abstraite parce qu'il n'existe pas II II d'implmentation unique pour Withdraw
nanesDace Abstractlnheritance
L
..^.: -^ uJrr
C,,a+^*. yLtrilI
ll II
I
t
'
AbstractBase0lass
cre une elasse abstraite de contenant rien d'autre qu'une mthode OutputQ
3 I8
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.
.)
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.
// Abstractlnheritanee - la classe BankAccount est vrainent abstraite parce qu'i1 n'existe pas II d'irnplmentation unique pour Withdraw II
nanespace Abstractlnheritance
t
cre une classe abstraite de contenant rien d'autre qu'une mthode 0utPut0
3l 6
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.
- legnra
ment des
couc hes
factoring produit
Le
Suprieur
'---3__,
1ry.":_l
supplmen-
E."b;pd;l
Classes prparatoires
Universit
Vous pouvez voir clue j' ai insr une nouvelle classe entre University et ' l r's. J'ai subdivis cette nouvelle classe en Coileee et Sch"..r:!i;.--r.
3 |4
High School
Figure 13.2
:
L'hritage de
- numStudents
simplifie la
classe
HlghSchool
+ Enroll 0
Univer
sity,
quelq
u
mais
es
il
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
Figure 13.1 : Une description sous la forme UML des classes Hi ghS chc o i
University
- numStudents
Student
+ nAvgSAT
etUrrlver
ci trr
+ Enroll 0 + GetGrant
re9!Qa.
o ^w7-7 z
Le langage UML
Le langage UML {Unified Modeling Language} est un langage de modlisation, permettant de dfinir clairement une grande partie des relations entre des objets dans un programme. L'un des avantages d'UML est que I'on peut en ignorer les aspects les plus spcialiss sans en perdre entirement la signific ation.
,/ / /
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,
3l 0
mAmountToWithdraw
= nBalance ;
l nBalance -= mAnountToWithdraur;
return
]
mAmountToWithdraw
//
t
I
SavingsAccount
compte bancaire
BankAccount
1a mrne chose ici aussiWithdra$ - tout retrait est autoris jusqu' 1a valeur du solde ; retourne 1e nontant retir override public decimal Withdraw(decimal mWithdrarual)
I // II
t
t
/1 soustrait 1.50 F
tl
retrait
avec ce
qui reste
return
1
base.Withdrarr(mWithdra,,ral)
imal
rrrAmount
ba . Withdraw (nAmount )
non Plus
]
l
l
1
La fonction \^rithdraw
( ) est marque comme virtual dans la classe de base que la mthode tii thd raw ( ) de la soustlasse est marque BankAc c oun r. alors avec le mot-cl override. Bien que la mthode MakeA\,nlithdrawal 0 soit inchange, le programme donne une sortie diffrente parce que I'appel ba. Wlthdraw O est rsolu sur la base du type de ba I'excution.
308
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.
jn cratis void
ba
MakeAWithdrawal(BankAccount ba,
deci.mal nAmount
{
if
t
is
SavinssAccount
SavingsAccount sa
e1 se
(SavingsAccount)ba;
;
sa . Withdraw (mAmount )
ba
Withdrav
(mAmount
MakeA'uJi
Maintenant, quand l.lain () passe la fonction un objet SavjngsAccorjnt, thd rawal ( I vrifie I'excution le type de I'objet ba et invoque Savirrgsc counr .'yiithdraw ( ) .
= \
7^H\ V / \/
1t$!Qa.
Au passage, je vous signale que le programmeur aurait pu raliser le cast et I'appel dans une nrme ligne I ((SavingsA.-iount)ba) .'y/ithdraw(mAmor-int). Je ne mentionne la chose que parce que vous la verrez beaucoup dans des programmes crits par des gens qui aiment faire de I'esbroufe.
Chapitre 14 : Ouand une classe n'est pas une classe : l'interface et la structure
57
EST-UN
Int32
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 - --"*b0hiets|.0'l pst (th'i c is a <trino) L-r --J--"-
0hiatc f1 I act (?) pc1 \ur4t 0biets[2] L (fl1p."1 Lal 0hipts f?l pct (4)
vvJL rlhr^+ll,r wuJLDLTJ ^^+ CL 1( \J.J \)
(trrrr^trrroFvemnle)
v
Appuyez
Comme les animaux sortant de I'Arche de No. chaque objet se pr.sente comme le seul de sa catgorie. J'ai implment une nrthocle 'i':-. S : : i :, : triviale pour Classi, rien que pour montrer qu'elle sait jouer avec toutes les autres classes.
,
'qE,
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
306
or.r
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
tqpe dclar
Dans certains cas, vous ne voudrez pas utiliser le type dclar. Ce que vous voudrez vraiment. c'est effectuer I'appel sur la base du type re[, c'esl--dire le type I'excr,rtion, par opposition au type dclar. Cette possibilit de dcider I'excution s'appelle polymorphisme, ou late binding (liaison tardive). Utiliser le type dclar s'appelle early binding (liaison prcoce).
Chapitre 14 : Ouand une classe n'est pas une classe : l'interface et la structure
35 5
'^^-1 uJdb
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];
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);
)
des types
Console.l^lriteLine("0b3'ets
] I
nCount**, o.ToString0 ) ;
// 0rrtoutFuncrion - affiChe tOUte prhnio nrri imnlAmgllg II ToString0 public static void OutputFunction(IFormattable id)
{
30 4
BankAccount ba
BankAccount
;
if
{
)
5)
;
1)
*= ba.Withdraw(1.
l
return
dAmountl{ithdrawn
Cette solution fonctionne : I'appel ba . i,Jittrd raw ( ) invoque maintenant la mthode Bank.r,rrrcur-1r,, comme on le voulait. L'inconvnient de cette approche est la rfrence explicite BankAc c ount. Une modification ultrieure du programme pourrait modifier la hirarchie d'hritage de telle manire que lJa.,,:-rrg,.:.{c. )un-, n'hrite plus directement de BankAc count. Une telle rorganisation brise cette fonction d'une faon qu'un nouveau programmeur pourra avoir clu mal trouver. Pour moi, je n'arriverais jamais trouver un bogue comme celui-l. Il vous faut un moyen de dire C# d'appeler la fonction ,r'ithdra'"ro depuis "la classe qui est juste au-dessus" dans la hirarchie, sans la nommer explicitement. Ce serait la classe qui est tendue par Sa,,,lngsAcccuril-. C'est dans ce but que C# comporte le mot-cl base. C'est le mme mot-cl base qu'utilise un constructeur pour passer des arguments au constructeur de la classe de base. Le mot-cl L,ase de C# est la ntme chose que this, mais redfinit le cast la classe de base, quelle que soit cette classe :
/l II
{
Withdraw
- tout retrait est autoris jusqu' 1a valeur du solde ; retourne 1e montant retir
mhrithdrawal)
new
II // soustrart 1.50 F
base.itlithdraw(1.5M)
avec ce
qui reste
( .r
elle-mme. En outre, cette solution ne sera pas brise si la hirarchie d'hritage est modifie.
353
L'appel suivant est I'appel la fonction ChangeRef erenceFunction | ). Cette fonction apparalt identique ChangeValueFunction O, I'exception de I'ajout du mot-cl ref la liste des arguments. Comm test est maintenant pass par rfrence, l'argument t se rfre I'objet original test et non une copie nouvellement cre.
Le dernier appel de l.1ain O est un appel la mthode ChangeMethod O. Comme un appel une mthode passe toujours I'objet courant par rfrence, les modifications effectues par cette mthode sont conserves une fois cle retour dans l"1ain ( )
.
La sortie de ce programme se prsente de la faon suivante Valeur initiale de test id = (10.00, 20.00) Valeur de test aprs 1'appel ChangeValueFunction ( 100, 200. 0) id = (10.00, 200.00) Valeur de test aprs 1'appe1 ChangeReferenceFunction (100, 200. 0)
id = (100.0,
200.00)
Valeur de test aprs I'appe1 ChangeMethod(1000, 2000,0) id = (1,000.00, 2,000.00) Appuyez sur Entre pour terminer.
!,/t-t
1t$!Qa" Cette unification des types de variables est trangre aux autres langages
'qE,
drivs de C, comme C++ et Java. En fait, Ia sparation entre les objets de type rfrence et de type valeur en Java peut tre un vritable casse-tte. Comme tout est un casse-tte en C*+, un de plus ou de moins ne se remarque mme pas.
302
.)
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.
)' , 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 ?
n'est avertissement. Vous ne le remarquerez mme pas, ^$st -a Ce message passerqu'unfentre Sortie pour voir ce qui y est affich. Dans moins de la 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 Le descripteur new indique C# qu'une mthode est redfinie intentionnellement et que ce n'est pas le rsultat d'une ngligence :
bon ordre.
/i
t
I
(
new
U
1e$\a.
Cette utilisation du mot-cl new n'a rien voir avec I'utilisation du mme
'qE,
^v7|
\ trouve
avec mes mthodes, mais ne surchargez pas mes mots-cls. Quand je dis new, c'st que je veux crer un objet. Ils auraient pu utiliser un autre motcl pour indiquer une surcharge intentionnelle.
Chapitre 14 : Ouand une classe n'est pas une classe: l'interface et la structure
35 |
I I une strrlct neut avoir une mthode public void ChangeMethod(int nNewValue, double
{
dNewValue)
n : nNevValue; d : dNewValue;
6 ^. ToString
//
tt
i-nitiale de test")
"
Console.}lriteLine("Va1eur de
r
;
'
OutputFunction (test ) ;
int
{
t.N =
l
newValue;
Test.D = dNewValue;
300
//
{
SavingsAccount
compte bancaire
BankAccount
/ I
SavingsAccount
- iit
et
100)
public
SavingsAccount (decimal
mnitialBalance,
decimal mlnterestRate)
:
{
base
(nInitialBalance)
this.mlnterestRate = mlnterestRate
l
100;
//
i
Accumultelnterest
invoque une
fois par
priode
(niBalance
mTnterestRate);
/i II
i
Withdraw
- tout retrait est autoris jusqu' 1a valeur du solde ; retourne 1e montant retir
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
l
r I | 1 , - , public class ^1 - , -1 Classl
nrrhf
a
ic gtar-ie voiC
MakeAWithdrawal(BankAccount ba,
decimal nAmount)
L
public static
{
int Main(stringIl
args)
BankAccount ba;
retire
100
F, et
// //
ba = new BankAccount(200M);
ba . Withdraw { I 00M)
essaie de
nel,,r
faire
sa =
Chapitre 14 : Ouand une classe n'est pas une classe: l'interface et ta structure
349
Vous pouvez raliser vous-mme un constructeur (qui n'est clorrc pas un constructeur par clfaut) qui fait effectivement quelque chose
:
public struct
{
Test
n)
this.n = n:
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.
ffi
et une instance, reportez-vous au Chapitre 8. Pour vous rafrachir la mmoire sur le passage par valeur et le passage par rfrence, voyez le Chapitre 7.Le Chapitre 12 traite de l'hritage. Et si vous ne savez pas ce qu'est une interface, c'est dans ce chapitre que vous trouverez la rponse.
298
Avec I'approche fonctionnelle. volls pourriez implmenter cette politique en dfinissant dans la classe un indicateur qui dise si I'objet est un Sa,,'ingsr\c co''iirt ou un simple Bankccount. La mthode de retrait devrait alors tester I'indicateur pour savoir si elle cloit ou non imputer la commission de 1.50 F :
public
t BankAccount
(int
nAccountType)
i/ indique 1e solde initial et dit si 1e compte /l que vous tes en train de crer est ou non l/ rrn eomnte rmunr
oublic
{
BankAccount
(decimal nlnitialBalance,
bool
mBalance
isSavingsAccount
mlnitialBalance;
this.isSavingsAccount l
isSavingsAccount;
l si
f
(
;t t
nBalance -= 1.50M;
mBalance
mBalance
-:
nAmountToWithdrar'r;
;
return
nrAnountToWithdraw
Chapitre 14 : Ouand une classe n'est pas une classe: l'interface et la structure
347
n=
1;
ll \a dclaration
MyStruct
ms;
la
ns.n =
3;
/i
ms,d = 3,0;
I //
I
un objet de classe
doit tre
a11ou
partir
MyClass mc
mn n = ?.
new MyClass;
mc.d = 2.0;
Un objet strL,icr- est stock en mmoire de la mme manire qu'une variable intrinsque. La variable ns D'est pas une rfrence un bloc de mmoire externe allou partir d'une zone de mmoire spare.
/ Lvat .(dl\y ^tK \/
^ \
La "zone de mmoire spciale" clont viennent les objets de classe s'appelle le /as (the heap).Ne me demandez pas pourquoi.
L'objet ms se trouve dans la mme zone de mmoire locale que la variable n, comme le montre la Figure 14.1.
-rside" dans
la mme zone de mmoire que
la variable de
SITUCtUI mS
1
I
l-n l.+ms
I
3.0
*
mc
type valeur rr, alors que la zone dans laquelle rside I'objet mc vient d'un espace mmoire
Vt't-L
2
2.0
particulier
du tas.
La distinction entre un type rfrence et un type valeur est encore plus vidente dans I'exemple qui suit. L'allocation d'un tableau de 100 objets
296
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.
Ce
S/
-rf4k\ -6: tl
Donner le mme nom deux fonctions (ou plus) s'appelle surchorger un nom de fonction.
Les arguments d'une fonction font partie de son nom complet, comme le
My0lass
chose
I t
ChOSe
d'aUtre
ll
l
{
Ghapitre 14 : 0uand une classe n'est pas une classe: l'interface et la structure
345
l'exigence d'implmenter CompareTo O. Conr,are Tir I ) y ajoute GetVaiue (.) , qui retourne la valeur des objets dans un r r.',.
"(
^59trK ,ffi \ \T /
Bien qu'elle puisse retourner la valeur cle I'objet .sous forme i nr, il'.' , . l'1rne dit rien sur ce que contient la classe. La gnration cl'une valeur r :,i peut trs bien mettre en jeu un calcul complexe. La classe BaseClass implmente l'interface ICcnpare (la n-rthocle concrte GetValue O retourne le membre donne nYalue). Toutefois, la mthocle CornpareTo (1, qui est galement exige par I'interface l'l r:r.i,.-, i,," est dclare
d.U>
t-
- *,. L ]
, dL
L.
Dclarer une class abstract signifie qu'il s'agit d'un concept incomplet, auquel manque I'implmentation d'une ou de plusieurs proprits (clans ce cas, la mthode CorrrpareTo ( )).
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.
,
petir
que ic2
rerlesvariables.Lesvariablesd'untypevaleurCommeli,letl:l.:'
sont dclares et initialises d'une certaine rnanire
:
int n=
'I
29 4
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
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++.
343
I Abstractlnterface -
namespace
t
I
Abstractlnterface
//
ICornpare
public interface
{
ICompare
ICornparable
f,tl
; -+L
implmente
f interface
ICompare en
int
i
nValue;
BaseClass
public
(int nInitialValue)
nValue = nInitialValue;
]
lt // II
public int
{
return l l
/ / (rrhfll
nValue;
//
complte
interface
jr+ rrrL
abstraite
eh<trrnf quoLq!L
nrrhl.in yuuraL
l.nmaa-^T^/^h.innruLJtrrPdrtrru\uuJuL
ee -
BaseClass
1/ nasse au constructeur de l-a classe de base I I Ia valeur oasse au constructeur nredent public SubClass(int nInitialValue) : base(nInitialValue)
{
]
/
tI
^ uomparelo
une indication disant si un objet d'une sous'classe esl plus granq qu un autre
',n ^-^^
orrprridp vv!!ru
{
nrrhl yuvarL
ic irrL int
BaseClass bc
= (Base0lass)right0bject;
)
;
292
nAccountNumber
= **nNextAccountNumber
;
nBalance = rnlniti-alBalance l
I
mme
chose
ici
//
{
nrrhlin
r*-**,
SavingsAccount - compte bancaire qui rapporte des intrts (errinncnnnrrn# . DanKAccount nlacq ,*rrgsAccounr : Ra.Ll
// tnitSauinssAccount - 1it le tarx rJ'intrt exnripf sp '^"o-"-'--'." I pourcentage (valeur comprise entre 0 et 100) public SavingsAccount(decimal mlnterestRate) : this(0, mlnterestRate)
I
I
l
^,.L1 i yuurrL
{
: base(minitial)
thi.s.mlnterestRate = mlnterestRate
)
tOO;
I
^l^FLJd
mme
l,^^,j1 Urdi
chose
ici
l
hllhlr^ PUUTfL
//
{
nrrhl i
Directltenosit - effectue automatiouement Ie dot d'rrn n <r.ati n 'oid DirectDeposit (BankAccount ba,
decimal npay)
chcrrp urrrYu
DirectDenosit (ba, 100) ; Console.l.Iriteline("Compte {0i ", ba,ToBankAccountString0 ) ; // et maintenant un compte rmunr SavingsAccount sa = new SavingsAccount(12.5M) ;
DirectDeposit(sa,
sa.
100)
Accunulatelnterest ( ) ; Console.Writeline("Compte [0J", sa.ToSavingsAccountString() ) ; l/ attend confirmation de 1'utilisateur Console.w*riteLine("Appuyez sur Entre pour terniner...") ;
Console.Read0;
return l
]
0;
Chapitre 14 : Ouand une classe n'est pas une classe: l'interface et la structure
34 |
Le tableau tri d'objets Student st alors pass la mthode localement dfinie DisplavArray O. Celle-ci effectue une itration sur un tableau d'objets qui implmentent Ge*.S*,rinq O. Elle utilise la proprit Array. Length pour savoir combien d'objets contient le tableau, puis elle appelle Get,strirq O pour chaque objet, et affiche le rsultat sur la console en utilisant ririreLine ( I . Le programme, de retour dans Main O , continue en triant et en affichant les oiseaux. Je suppose que vous conviendrez que les oiseaux n'ont rien voir avec des tudiants, mais la classe Bird implmente I'interface IComparable en comparant les noms des oiseaux, et I'interface IDi splayable n retournant le nom de chaque oiseau. Remarquez que lufain ( ) ne rcupre pas cette fois le tableau des oiseaux.
Ce n'est pas ncessaire. C'est la mme chose que ce fragment de code class BaseClass {} class Subtlass : BaseClass {l
:
class C1ass1
{
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
Tri
de
la liste
: IUU
:
des tudiants
L1SA
Marge Bart
Maggie
85
:50 :30 :0
Homer
290
public class
{
BaseClass
()
public
{
BaseClass
public BaseClass(int i)
t
Console.ItiriteLine("Construction de BaseClass([0])", l
l
i)
BaseClass
public
t
SubClass
()
Console.l^Iriteline("Construction
l
public SubClass(int i
il,
i2);
]
]
public sratic
t
Console.Writeline("Invocation de SubClass0 ") SubClass sc1 = new Sub0lass0; Console.lrlriteLine("\nlnvocation de SubClass(1, 2;"; SubClass sc2 = neu SubClass(1, 2); I I attend confirmation de 1'utilisateur Console.l.lriteline ("Appuyez sur Entre pour terniner.
Console.Read0;
..")
return
l
]
0;
l
Ce programme donne la sortie suivante Invocation de Sub0lass 0 Construction de Base0lass (default) Construction de SubClass (default) Invocation de SubClass(1, 2)
:
Ghapitre 14 : Ouand une classe n'est pas une classe: l'interface et la structure
339
return s;
]
]
--Birds - tri des oiseaux par non -l{ --I I Bird * tableau de noms d'oiseau cl ass Bi rd .: TComr:--^L1^ rn.: ^-1^..^b1e Lf,qD uaru rvvxrPdldUaE. LUL>yLajdl
{
private string
sName;
student
this.
]
sName
sNane;
ll CreateBirdlist - retourne une liste d'oiseaux 1a fonction r+^+..i ^ DLrfrr[J i1 SBirdNames = -+-.i-^ { "Tourtere11e", "Vautour", "A1ouette", "tourneau", "0rivet', "Corbeau", "Hirondel1e"] ; { rf / \ puDl1c statlc blrdL.j ureateblro],lstl/
DLALTL
appelante
birds
]
Ii]
= new Bird
(sBirdNames
Ii]
return birds;
]
//
{
-..L1.ipuDl1C
get
{
return
]
]
tt /i /l II
sName;
implmente
Cornpareo
la
mthode de comparaison
288
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
namespaee Exanple t
public class
{
C1ass1
scl =
ner,r SubClass0;
Console.l^Iriteline("\nlnvocation de SubClass (int) ") ; SubClass sc2 = ner,r SubClass (0) ; // attend confirrnation de 1'utilisateur Console.l^IriteLine("Appuyez sur Entre pour terminer. .,")
Console.
Read
return
l
]
0I
public class
{
BaseClass
public
t
BaseClass
0
;
public BaseClass(int i)
{
337
| !f,>Pr/ll) --^" - affiche un tablearr d'ohipts nrri \: ^' 1 -"4 dLIILiIE Ull LdUfc@u u vuJL yur inplmentent f interface lDisplayable I
ct:tir vnirl DicnlrrrArrev
nrrhlin
{
(iDisplayable
I displayables)
.int _ ___o ___ dr^-1-,.^L1^-. T^-^*l ___ _ lpnoth: rrsplaya0res Lengrn; for(int index = 0; index ( length; index**)
i
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
{
r^..L1^ rGrade = 0.0; --"i--^+^ UUUUlC U PLAVOLg // Constructor - initialise un nouvel objet student public Student(string sName, double dGrade)
I
sName;
this.sName
donnes de
1'objet
this.
l
dGrade;
// CreateStudentList - pour gagner de 1a p1ace, cre I une liste fixe d'tudiants static stringIJ sNames = {"Horner", "Marge", "Bart", "Lisa", "Maggie"} ; static double ll dGrades = l0, 85, 50, 100, 30] ; public static Student[] CreateStudentlist0
I
{
for (int i = 0; i (
{
sArray l
F^+1rFh tgLuLll
Ii]
lil ,
dGrades
Ii]
^A--^,,, drL4j/,
/l
286
o)
if (o is
{
MyClassl)
MyClassl mc1
1t
'T
(MyClass1)o;
l
'I
J
GenericFunction O peut tre invoque avec n'importe queltype d'objet. Le mot-cl extraira des hutres ob j ect toutes les perles de MyClass 1.
is
L'hritage et le constructeur
Le programme InheritanceEx:rmp1e que nous avons vu plus haut dans ce chapitre repose sur ces horribles fonctions Init. . . pour initialiser les objets BankAc(lounr et SavirrgsAccount en leur donnant un tat valide. quiper ces classes de constructeurs est certainement la meilleure manire de procder, mais elle introduit une petite complication.
// II
I
lnheritingA0onstructor
I
System;
using
t
namespace
InheritingAConstructor
Class1
public class
t
335
Cette rnttrocle trie trn tableau d'otrjets clui implnrentent I'interface I r-lr:rll ar :,.i:: ... [, classe de ces obf ets n'a mme pas cl'irnportance. Ils pourraient trris bien, par exemple. tre cles objets Strrtent. I-a classe r.rr a-,' pclurrait ntme trier la version suivante de S': rrrleir', l
nom
er ses points
d'UV
//
{
oublic dorrble
get
{
return
l
cjGrade;
// II ll
{
CompareTo
compare un tudiant un autre ; un tudiant est "neilleuril gu'un autre si ses points d'UV sont meilleurs
return
i
-1
if
{
Llg!!l
I.
return
]
0;
Le
un simple appel
[]
;
students)
l0omparable
284
=f
Une conversion incorrecte gnre une erreur l'excution du programme 4;#\ (." qu'on ^< \ coup plus appelle une erreur run-time). Une erreur I'excution est beauclifficile iclentifier et corriger qu'une erreur la compilation. \!!-/
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-
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
{
Accunulatelnterest ( ) ;
l
]
-..L1.: ^ LaL_LU ..^..i I PUUr.r_U ^+^+.1 ^ VUI_U
{
TestCast
()
SavingsAccount s = new SavingsAccount ProcessAnount (sa) BankAccount ba = new BankAccount 0 ; ProcessAnount (ba) l
L'instruction if supplmentaire teste I'objet b,ank.rccount pour vrifier qu'il est bien de la classe Sa,irngsAccoul,t. L'oprateur is retourne rrue lorsque ProcessArnount, ( ) est appele pour la premire fois. Toutefois, lorsqu'un objet banl.:Account lui est pass dans le deuxime appel, I'oprateur is retourne faise, vitant ainsi le cast invalide. Cette version de ce programme ne gnre pas d'erreur I'excution.
333
g1 I
L
return
]
I I
dGrade:
] I
return
]
L'appel i'adRrght ( ) garantit que le norn dans lequel sera insr le champ aura au moins neuf caractres de long. Si le nombre fait moins de neuf caractres, la diffrence est comble par des espaces. Ajuster une chalne une longueur standard permet d'aligner des objets en colonne. L'indication | 1 :N0 I dit, "afficher le grade avec des virgules ou des points (selon le paramtre de localisation) comme sparateur de milliers." L'indication {0} qui prcde arrondit la partie dcimale. Avec cette dclaration. je peux maintenant crire le fragment de programme suivant (le programme complet est donn dans la section "Assembler le tout", plus loin dans ce chapitre) :
ii DisplayArray - affiche un tableau d'objets qui II implrnentent f interface IDisplayable public static void DisplayArray (IDisplayable [] displayables)
t
int
t I
length = displayables.Length;
index =
for(int
0;
index
length; index*+)
l Cette mthode D.spia;'rAr ray i, 1 peut afficher n'importe quel type de tableau, condition que les rnembres du tableau dfinissent une mthode GetStringt). Voici un exemple de sortie de Displai'Arra'y(; :
Homer
:0
:85
Marge
282
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
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;
1e haut
(savingsAccount)ba;
La premire ligne stocke un objet Sa'.'ingsAcccLlnt dans une variable BankAccount. C# effectue pour vous cette conversion. La deuxime ligne
SavinqsAccount.
33 |
crire
]
le
PDA
l
PUUfAU LIdUU
{
Tan+as !oPLUP
. /r^-^,'+^, vVtuPULtr!,
IRecordable
sNot
e
TakeANote
string
il
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
recordObject)
I
I
I I
erer une
liste
de commissions
;
recordObject. TakeANote
]
slist)
Concrtement, ce fragment de code dit que la fonctio Recorcishc,;irr r,gL,st 0 accepte comme argument tout objet qui implmente la mthod fai.+..1t: -.e o (en termes humains, "tout objet qui peut enregistrer une note"). RecordShopplngl-s;t () ne fait aucune hypothse sur Ie type exact d'objet recorciObiect. Que I'objet soit effectivement un PDA ou un certain type de EtectrcnicDe'r'.ic est sans importance, pourvu qu'il puisse prendre une note.
280
et
100)
tOO;
// Accumulatelnterest I
t
invoque une
fois par
priode
j
i
I t
I I
Withdraw
- tout retrait est autoris jusqu' la valeur du solde ; retourne 1e montant retir
Ici, la classe Sa.,r j ngsliccount contient un membre donne bankAccount (au lieu d'en hriter de tsankAccor,Lrit). L'objet bankAccount contient le solde et le numro du compte, informations ncessaires pour la gestion du compte rmunr. Les donnes propres un compte rmunr sont contenues dans la classe SavilssAccoLlnr
Dans ce cas, nous disons que :ia-i j ngsccount A_UN BankAccounr:.
La relation
A UN
:
La relation A_UN est fondamentalement diffrente de la relation EST_UN. Cette diffrence ne serlble pas rnauvaise dans I'exemple de code suivant
I cre un nouveau compte rmunr BankAccount ba = nev BankAccount(J SavingsAccount_ sa = new SavingsAccount*0; sa. InitSavingsAccount I
(ba,
I et y
329
Toutefois, cette solution souffre de deux gros problmes. Le premier est fonclanrental : on ne peut pas prtendre que le stylo, le PDA et I'ordinateur portatrle sont lis par une relation cluelconque cle type EST_UN. Savoir cornrllent fonctionne Lln stylo et comment s'en servir pour prendre une rtote ne rne d<lnne auclrne information sur ce qu'est un ordinateur portable et la rnanire dont il enregistre les informations. I-e nom Tl,;:rj,,ri'r :,i,..i,. r,' est pltts rrne clescription qu'une classe de base.
Le second prolllrne est purement technique. Il serait mieux de dcrire L.tl,r,;p COrIlrIle une sous-classe de Cc,trp;',- e r . Bien que I'on puis.se raisonnablemerrt tenclre la class Pirr, partir de la mme classe de base ilc-,il|i.ii f i" on ne lteut pas en clire autant de !'e:r. ll fauclrait pour cela clfinir utt stvlo contme un certain type de i''ier:hariic a ii^jr itirigle-.-rce -l'clutefclis. ou cle l)t,-, . i:e i'1,::l;---r:ir i ii-q!i1r,:tirir':. une classe C# ne peut pas hriter clr-'deux classes cliffrentes en mme temp.s, elle ne peut tre qu'ul] setri et rirrle type tle chose.
En rev:rr;-urt i'r rros trois classes initiale.s, le seul point que les classes Pen, i'r .. et ,', , .;. ottt eI] conrnrun l)our ce que nous voulorts faire est qu'elles perrvenl toirtes tre lrtilises pour stocker cluelque t'lrost-'. [,a relation PLlti'T'-[i'l RIr--L.i']'ll ,lS-CONIME i..,,i,,.- r L.i:r;, t nous permet de communiquer I'usage rllri ;rerrt err etre fait cians un trtrt particulier, sans pour autant irnpli<1uer lute relation irrhrerrte entre ces trois r:lassr:s.
.)
Urte clescrilrtiorr rl'interface ressernl;le beerucoup une classe sans clonnes, dans larluelle torttes les rnthorles seraient abstraites. I-Jne tlescription cl'interface p()Lrr cles "chr-rses qui enregistrent" pourrait ressembler ceci :
interface IRecordable
i
void TakeANote(String
l
sNote)
Rentarquer le rnot-cl inr erf a.ce la place de c1ass. Entre les accolades se trouve urle liste de mthodes abstraites. Une interface ne contient aucune dfinition de nrembre donne.
La rnthode Tal.re.l'loie O est crite sans implmentation. Les mots-cls public et'.'r"-tr-ial ou a.Sstracr ne Sont pas ncessaires. Toutes les mttrocles d'une interface sont publiques, et une interface n'entre pas en jeu clans un hritage normal.
278
La proprit Balance permet de lire le solde, mais sans donner la possibilit de le modifier. La mthode Deposit O accepte tout dpt positif. La mthode r^/ithd raw ( ) vous permet de retirer tout ce que vous voulez dans la limite de ce que vous avez sur votre compte. ToBarrkAccor-intS -rir'g o
La classe SavlngsAccount hrite de toutes ces bonnes choses de BankAccount. cela, elle ajoute un taux d'intrt, et la possibilit d'accumuler des intrts intervalle rgulier. in O en fait le moins possible. Elle cre un BankAccount, affiche le compte, cre un Sar.ingsAc c ount, ajoute une priode d'intrts, et affiche le rsultat :
Ma
Compte 1001 200,00 Compte 1002 112,5A D (12,5',,) Appuyez sur Entre pour terminer...
^e.St
7X ttgrf Y
rnitBankAccount O . Cela initialise les membres donne propres au compte. La mthode Initsa..,ingsAccount ( ) aurait pu les initialiser directement, mais il est de meilleure pratique de permettre BankAccount d'initialiser ses propres membres.
- i'ai du nal
La relation entre Sar-ingsAccount et Banl<Account n'est rien d'autre que la relation fondamentale EST_UN. Pour commencer, je vais vous montrer pourquoi, puis je vous montrerai quoi ressemblerait une relation A_UN.
La relation EST UN
La relation EST_UN entre SavingsAccourlL et Bar,krrircounr est mise en vidence par la modification suivante Classi dans le programme SirnpleSa.,.ingsAccoLlni de la section prcdente
:
public class
t
L
C1ass1
// Direetltenos'i t - effectue
automat-i 0rement
Chapitre 14
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
Qu'e
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
ll
{
InitBankAccount (0)
]
// Balance (so1de)
tl 4
oublie deeimel
t
Balance
mArnount)
if
t ] ]
(mAmount
0)
nBalance *= mAnount;
// ||
t
Wittdrar,I
- tout retrait est autoris jusqu' la valeur du solde ; retourne 1e montant retir ($lJithdrawal)
I
if
t
(mBalance
nlrlithdrawal = nBalance
mBalance -- ntrli"thdrawal return mWithdrawal;
] I rvuL!rrr ttJ q^<+-:^
uet le
public string
{
return String.Format("{0i
nAccountNunber, mBalance)
]
[1:CJ",
l
I
{
SavingsAccount
compte bancaire
BankAccount
/l II
- lit
et
100)
? 32 5
public class
i
BankAccount
/l II
{
Withdrawal
* tout retrait est autoris jusqu' la valeur du solde : retourne le montant reti.r
dt/ithdraw)
)")
;
BankAccount
of"j
)")
i
]
SavingsAccount
l,lriteline
("
invoque
Spec
)"
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
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.
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.
? 323
// I
i
SaleSpecialCustomer
- compte utilis
Console. lrlriteLine
("
appelle SaleSpecialCustomer.Withdraw()");
l
]
ll
]
',,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
:
Passage
nnttr pff
_
Pncs:op d rfin sn..i--^^^^^..-! _-_*D_ - _-. _*vflrl1uLUultL norrr pffpct,ror Test (BankAccount)
appel
1e SavingsAccount
Withdraror
()
appelle SavingsAccount, Withdraw( (noialSaleACCOUnt Poccono I trrn JPI roDqc u ulr pour ef fectuer Test (Bankccount) appelle Savingsccoun!.Wi lhdraw
()
272
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.
/l
tnheritanceExample
Systern;
- offre la
p
dnionstration
using
{
nflnl4lvlenbef
nuhlic void
SomeMethod0
l
nrrhlic class SubClass
{
BaseClass
(nmo0thorMpthodfl lrlvs\/
UVI.vv
Conso1e.l,lriteline ("SoneOtherMethod
]
")
]
nrrhlin { nlecc'l'oql
l l er6e rrn ohiet de la classe de base Console.l,lriteline ("Utilisons un objet de BaseClass bc = new BaseClass0;
ll
:");
32
#"u$3 l_frrl,i
// tnheritanceTest
examine comment
narrf ycuL fra sL!c rrtiljc uLrrtrE
le
mot-c1
virtual
.,''
n^'r. yvu!
lih^ts ldIM!
using Systen;
public static
t
'Iest.t tDaj
lestt
<f
(sa,/
; ;
Test2 (sa)
;
;
lest3 (ssa)
;
;
//
return l
0;
(BankAccount account)
27 0
Par exemple, seule une mthode dfinie dans le mme espace de nom que BankAccount peut crer un objet tsank.,rccount avec Ie constructeur dclar comme internal (pour en savoir plus sur les espaces de nom, reportez-vous au Chapitre l6) :
i/
{
BankAccount
- simule un simple
compte bancaire
public class
I
BankAccount
I 7es numros de compte comnencent i000 et augmentent / squentiellement partir de 1 static int nNextAccountNumber = 1000; // tient jour le numro de compte et le solde int nAccountNunber:
i
double dBalance; i/ invoque 1e constructeur spcifique en fournissant I I des valeurs par dfaut pour 1es arguments nanquants
internal
{
BankAccount
{)
nAccountNumber dBalance 0:
= **nNextAccountNumber
?3|I
une chane
ri.. \
'
en11+hr1(+ vuLyuLULrfrr6/'
//
t
SubClassl
- inplmentation concrte
de AbstractBaseClass
ctrino
] ]
l,uurr
{
c = cSr1rcp ToTTnnpr{l'
! ' rvuytJ u! \ / '
[0]",
s)
l/
SubClass2
de AbstractBaseClass
{0}",
s)
class Classi
t
ba)
ba.0utput("Test");
l
* 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
;
/ (scrl lesr
l Console.
]
SubClass2 scT
;
= new SubClass2 0
C#
397
arabe, hincli, ni de toute autre langue. Le format de fichier Unicocle, plus souple, bnficie d'une compatibilit ascendante avec des caractres ANSI, et offre un assez grand nombre d'autres alphabets. Unicocle exi.ste en plusieurs formats, mais UTFS en est le format par dfaut pour C#. Le programme Friel,lrir-e suivant lit des lignes de donnes sur la console et les crits sur un fichier choisi par I'utilisateur :
,\
."u3
Ha;'l I ] \.Y
// Fileidrite - crit dans un fichier texte ce qui. est saisi sur 1a console II
using Systen; using System. I0; namespace Filel^lrite
t
public class
{
C1ass1
StreanWriter
srd
= nul1:
string
{
sFileName
r'rr '
vhile (true)
try
{
//
if
t
I
i
]
break:
// /l II II II II II II II I| II
ouvre le fichier pour y crire ; envoie une exception si 1e fichier existe dj : FileMode.CreateNew pour crer un fichier si i.l n'existe pas dj, ou envoie une exception si 1e fichier existe FileMode.Append pour crer un nouveau fichier ou ajouter quelque chose un fichier existant FileMode.Create pour crer un nouveau fichier ou pour tronquer un fichier existant 1es possibilits de FileAccess sont :
FileAccess.Read,
35
il111nl1tF11nat1^n vuLPu
{ll}" tvr
] // luJ L! no ruul^^- rrno c'imnlo fnnnt'inn I I tnStri -r-llt - fnrrrnit nrrorridp nrrhl in ctrino Tn(trinof) \,/ u Lt frr6 rvu Lt arr6
{
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,
C#
39 5
Les classes d'l/O sont dcrites dans I'espace de nom S1-stem. r,-. La classe de base des I/O de fichier est FileSrrean. Autrefois. le programmeur ouvrait un fichier. La commande c.pen prparait le fichier et retournait un handle. En gnral, ce handle n'tait rien de plus qu'un numro d'identification. Chaque fois qu'on voulait faire une opration de lecture ou d'criture sur ce fichier, il fallait prsenter ce numro.
C# utilise une approche plus intuitive. Il associe chaque fichier un objet
de la classe Fi it,St,rcarn. Le constructeur de FiieStream ouvre le fichier. Les mthodes de Frt eStream effectuent les oprations d I/O sur le fichier.
.s\)G / t\9, Y
Fr
leSirean n'est pas la seule classe qui peut effectuer des oprations d'l/O
Hsurdesfichiers,maisc,eStbienellequireprsentenotrebonvieuxfichier
de base, qui correspond 90 %, de nos besoins d'l/O sur les fichiers. C'est la classe racine qui est dcrite dans cette section. Si elle est satisfaisante pour C#, elle I'est aussi pour moi.
Fi leS i- re:rir, est une classe trs basique. Ouvrir un fichier, fermer un fichier, lire un bloc et crire un bloc, c'est peu prs tout ce qu'elle vous donne. Heureusement, I'espace de nom -Svsten.IO contient un ensemble de classes qui compltent FileS ti aiir pour vous donner un accs plus facile aux fichiers. et un sentiment de confort douillet
:
Fea 1r- :. , i .-L. riteEinar r' : Ce sont deux classes de flux qui contiennent des mthodes permettant de lire et d'crire tous les types valeur : FeadCrLar (), riteChar O, ReadB.,'t-e O, i^lriteBl''te O, et "i ainsi de suite. C'est utile pour crire un objet en format binaire (non lisible par un tre humain).
Te;<tPeader,'l'li:,'lr-iter : Deux classes permettant de lire et d'crire des caractres (du texte). Elles se prsentent en cleux versions: Si r:rLgF,:ader l:trirrglir-i*,er et S:reanrReadei ,
S
-.
r'e
anil i^, i. -o :
,/ t/
:l':r lng,F,e zter,/-i-rlngr'^/rirer : Une simple classe de flux qui se contente cle lire et d'crire des chalnes.
S:reanll.c:::lei-/-ctreanh''ri:er : Une classe plus sophistique de lecture et d'criture de texte pour ceux qui en veulent plus.
Cette section fournit les programmes suivants, clui montrent comment utiliser ces classes : Filetirite et Fi iel.eai.
35
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.
bool
byt
cha
e
llcolearr
l-.+,-.
S
sbyte
P,',-: e
decimal double
r Decinai
Cha
Doub I
float int
ui nt
1
Singie Int32
UInt-12
T-+ I Li I U A
ong
u1
ong
Uint64
0b-ject
ohicet .-"J---
short
usho
rt
Int 16 Uinr,6
Comment le sqstme de ttlpes est-il unif par des structures communes ? Un exemple
n'St que I'autre nom cle la structure Int32. Comme toutes les structures d'Cb,j ect, lnt doit en driver comme les autres. Cela conduit quelques rsultats fascinants, comme le montre le programme suivant :
int
drivent
i
I
I I
TypeUnification
System;
montre comnent
sont en
TypeUnification
using
{
namespace
G#
393
rraurqPaLc
nLLtr ^^^^';sControlLib
public class
{
Cl.ass2
public void
t Console
l . I
A_pub1ic
(
0
"C1ass2 . A_pub1ic " )
;
i,iriteline
nrnientcd
{
Console,
1
i{riteline
("
Clas
s2.
B_protect
ed "
Console
l,'/riteline
("
Clas
2 . C*pri-vate "
internal
{
voi-d D internal o
.
Console
void E internalorotectedo
,
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
t/
Les mthodes dclares comme protected sont accessibles depuis la classe C1ass2 et toute classe qui hrite de Classl. L'appel classl . B_pr otected () est autoris, parce que C1ass1 hrite de C1ass2. L'appel c1ass3.B_p.ctectei est illicite. Les mthodes dclares pri.,,ate ne sont accessibles qu'aux autres membres de la mme classe. Aussi, I'appel clas s- .1 p: i ,,? ) n'est pas autoris, alors que I'appel ciassi.C pri-".are O est
t/
correct.
35
// ChangeReferenceFunction - passe 1a struct par rfrence public static void ChangeReferenceFunction(ref Test t, int
{
t.N =
] / I
{
newValue;
Test. D
dNewValue;
/ 0r:tnrrtl'rrnnt j nn - aff iche toute mthode oui imolmente vsLPu LrrvuL Yur
ToString0
D
yUUalL
LdLIL
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
(),
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.
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 de cette fonction, I'objet t est une copie du test .structure. 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
C#
39 |
d'indpendance est couplage. Une classe qui -grf{c accde aux membres internes appelle niueou deest dite fortement couple. d'une autre classe 9-/ -i: \ =( ItY ) O"t classes qui 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
fornes
nnespce AccessControl
using
System;
;
using AccessControllib
Class2
public static
{
int Main(string[]
;
strings)
Class1 c1ass1 = nev Class1 0 Class2 c1ass2 = new C1ass2 0 C1ass3 c1ass3 = nelr C1ass3 0
l
;
ll II
|
les
I Llo4
travers
c1ass1 . B*protected
350
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.
// I
structureExample
System;
using using
string ToString0;
j
I I II
I
I/ ll
{
struct peut avoir des nenbres donne objet er de classe (statiques) 7es menbres statiques peuvent avoir des initiariseurs
une
utiliser
un constructeur pour
les
initialiser
nembres donne
d'une struct
publi"c Test(int n)
this.n = n;
l
I
{
ll une struct peut avoir des proprits d,objet I et des proprits de classe (statiques)
N
public int
}
D
get
i
set { d = value;
{ return d; l
l
C#
389
namespace
{
L
Paint
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
args)
I I
I cre un objet de type Sort dans 1'espace de nom I o nous nous rrouvons et invocue une fonction
Sort obj =
new Sort 0 ; obj . SoneFunct ion ( ) ; ll cre un objet dans un autre espace de nom, renarquez que ll Ie non de 1'espace de nom doit figurer explicitement dans toute I I rf.rence de classe
Dans ce cas, les deux class Sort et Test sont contenues dans le mme espace de nom, l/iat.hRcul-ines, bien qu'elles apparaissent dans des dclarations diffrentes dans le module.
Normalement. Sort et Test seraient dans des modules source C# diffrents que vous pourriez gnrer ensemble pour en faire un seul programme.
fonctior 'ie..:t . i"lair' ( r peut rfrencer la classe Sort sans spcifier I'espace de nom parce que les deux classes sont dans le mme espace de nom. Toute fois, Liain O doit spcifier I'espace de nom Pa in-, quand elle se rfre l'ai n',Co'or, comme dans I'appel Paiir'- . PalltCo Lor . StaticPalnr (1.
La
Remarquez que vous n'avez pas besoin de prendre des prcautions spciales en vous rfrant ir,ark. Paint 0 , parce que la classe et I'espace de nom de I'clbjet b iai:k sont connus.
3 48
de rfrence ncessite que le programme invoque 101 fois pour le tableau et une fois pour chaque objet) :
MyClass t
urc
ne,,v
(une fois
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
integers lOl = O; ll La dclaration d'un tableau de struct est tout aussi sinple Mrr(trrnt[1 .o = new MyStructitO0];
rvLLl
ms[0]
.n
.av
0;
Le constructeur de structure
Une structure peut tre initialise en utilisant une syntaxe semblable celle des classes, ce qui est intressant : public struct MyStruct
t
hllhl1^ thf n. I
MyStruct ms
* new MyStruct 0
En dpit des apparerlces, cela n'alloue pas un bloc de mmoire partir du tas, mais initialise seulement n et d la valeur zro.
C#
387
Un fichier de projef contient des instructiorrs sur les fichiers qui constituent le projet et la manire dont il.s se combinent.
Vous pouvez combiner des fichiers de projet pour produire des combinaisons de programlne qui dpendent des rnmes classes dfinies par I'utilisateur. Par exemple, vous pouvez vouloir associer un programme d'criture avec le programme de lecture correspondant. De cette manire, si I'un des deux change, I'autre est automatiquement rgnr. L'un des projets dcrirait le programme d'criture, et I'autre clcrirait le programme de lecture. On appelle solution un ensemble de fichiers de projet.
.a\\G -UnprogrammeurVisuaIC#utilisel'ExplorateurdeVisualStudiopour
fl?It \7
Un espace de nom vite les possibilits de conflit de nom. Par exemple, une bibliothque d'entres/sorties pour fichiers peut contenir une classe Convert qui convertit une reprsentation cl'un type de fichier en un autre type. En mme temps, une bibliothque de traduction peut contenir une classe du mme nom. L'assignation des espaces de nom Filero et
346
Mais une rfrence un objet est dclare et initialise d'une manire compltement diffrente :
public class
t MyClass
public int
l
MyClass mc;
lul{J
n;
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
:
nrrhlin
'i
nt n,
d;
public double
j
public class
{
MyClass
'i n+ rrrL
ul
l On accde un objet structure comme un objet cle classe, mais I'allocation est identique celle d'un type valeur :
Chapitre 16
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.
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
]
htthl'^ yuuIfL Laa ^l^ f'rF^l ura
public static
t
SubClass sc1 : new SubClass(10) ; SubClass sc2 = new Subtlass(20); MyFunc (sc1, sc2) ;
return
l
0;
- utilise 1es mthodes fournies par f interface ICompare pour afficher 1a valeur de deux objets, puis f indication de celui qui est 1e plus grand (se1on I 'oDJ et lu1-nemel public static void MyFunc(ICompare ic1, ICompare ic2)
l,tyFunc
lI ^ \
// II ll tt
{
ic
2 . GetValue ( )
string
{
s; CompareTo(ic2)
)
switch (ic1,
case
0l
'
break;
petit
que";
case
1:
break;
l
Console
,
l,lriteline
Abstractlnterface
simple.
L'interface IConpare dcrit une classe qui peut comparer deux objets et aller chercher leur valeur. ICompare hrite de I'interface IConparal-t:
383
Le remplacement de ces fonctions signifie que mme les fonctions conues pour attraper les exceptions de la classe gnrique Exceprion n'ot-lt qu'un accs limit aux nouveaux membres donne. En commenant par |Ialr, ( ) , le programme cre un objet |1at-lrClas s dont la valeur est 0, puis essaie astucieusement d'en prendre I'inverse. Je ne sais pas si vous avez dj essay, mais je n'ai jamais vu beaucoup d'inverses de 0, et si rna fonction tait cense retourner Lln nombre, a me
rt9{Qa. Le processeur Intel retourne effectivement une valeur pour 1.0/0.0 : Irrf iniry. plusieurs valeurs spciales en virgule flottante pour traiter de tels cas ^v7q \ Il existe :(dq9 ) plutt que d'envoyer une exception, car tous les langages n'ont pas la capacit \/ de traiter cles exceptions. Parmi ces valeurs spciales, ily a I'infini positif et ngatif, et le s),mbole iJaii Q,lot-a-Number - pas un nombre) positif et ngatif.
Dans les circonstances normales, la mthode In.,rerse i ) retourne le rsultat attendu. Quand on lui passe une valeur nulle, cette mthode aux ides larges envoie une CustomException, passant une chalne d'explications avec I'objet fautif.
Travaillant I'envers, Main () attrape I'exception, puis affiche un bref message destin expliquer o en est le message dans son traitement : "Erreur f atale inc onnue" signifie probablement que le programme est sur le point de fermer boutique et de rentrer chez lui. Main O donne ensuite I'exception la possibilit de s'expliquer en invoquant sa mthode ToStrlng O. ffoyez l'encadr "ToStringQ, la carte de visite de la classe" dans ce chapitre.)
Comme dans ce cas I'objet exception est effectivement un Cu s tornEv-c epr- I on, le contrle passe Cus t ornExc ept i on . To S t ring ( ) . Cette mthode T'oStrirrg O affiche le message de I'exception avec la mthode cible initiale et le numro de ligne correspondant. une mthode virtuelle d'Exception, dont toute classe ^tK Message O estpersonnalise doit hriter. d'exceptions =Qg,
Plutt que de faire des hypothses hasardeuses, Ves sage ( ) permet galement I'objet l,iathClass de s'afficher lui-mme en utilisant sa mthode Tcstrlng r. ). Iiat-hCiass . ToSrring () retourne une chane contenant la valeur et la description de l'objet.
Ne supposez rien de plus que ce que vous savez. C'est sur la mthode
ToSt'ring O d'un objet que vous devez compter pour crer une version st ring de celui-ci, plutt que d'essayer d'accder I'objet lui-mme pour en extraire Ies valeurs afficher.
3 42
Tri de la Alouette
Corbeau
liste
des oiseaux
tourneau
Grive
Hirond el I e
Tourterelle
Vautour Appuyez
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
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
38 |
usLng Systen;
namespace CustonException
{
Excepti.on
private MathClass mathobject; private string sMessage; public CustomException(string sMsg, MathClass mo)
{
nathobject =
sMessage
mo;
sMsg; Message
jllcbSd8e -ao-^^^ gbL ^.lInl\ \tuJ /, lrnh jat - _-L oqt []Jrl
string s =
rt1rrn c.
Message;
s *= "\nException s *= TargetSite;
l
envoye par";
// II
{
MathClass
- collection de fonctions
MathClass
mathnatiques
public class
int
nValue)
public
// II
t
int
l{essage
de
l'objet
Message
Math0lass attach
public string
get
{
l
]
// ToString -
combj.ne 1e nessage
personnalis avec
3 40
1 | i-+^-f^^^ r lrrL!lLE
return
l
]
liane;
La class Student (elle est peu prs au milieu de ce listing) implmente les
interfaces ICornparabie et IDispl ar.able que nous avons dcrites plus haut. ConpareTo O compare les tudiants par "grade", ce qui a pour consquence que les tudiants sont tris par grade. GetString O retourne le nom et le grade de l'tudiant. Parmi les autres mthodes de Stu'lent, il y a les proprits en lecture seule et Grade, un constructeur simple, et une mthode CreateStudentlist (). Cette dernire retourne simplement une liste fixe d'tudiants (au dpart, j'avais pens permettre I'utilisateur d'entrer au clavier les noms des tudiants, mais le listing devenait si gros qu'on n'y voyait plus I'essentiel).
Name
La classe Bird, en bas du listing, irnplmente galement les interfaces IComparable et IDispla;'ab1e. Elle implmente CornpareTo () en comparant des noms d'oiseaux I'aide d'une mthode similaire intgre la classe String. Ainsi, un oiseau est "plus grand" qu'un autre si son nom est plus grand. Cette mthode permet de trier les oiseaux par ordre alphabtique. La mthode GetName O retourne simplement le nom de I'oiseau.
Nous sommes maintenar-rt prts revenir dans l"lai n ( ) , au bon endroit. La mthode CreateStudentli st ( ) est utilise pour retourner une liste non trie, qui est stocke dans le tableau students.
V '
les autres chapitres (en particulier ceux du Chapitre 6). Ceux-ci sont des tableaux d'objets d'une classe particulire, par exemple un tableau d'objets Srudenr, alors que conf.,ar-abieCrbi ec+;s est un tableau d'objets qui implrnentent I'interface ICoinparable, indpendamment de la classe
laquelle ils appartiennent. Le tableau conparabief l-,-ject-c est pass la mthode Array . Sor: | ), qui en trie les lments sur la base clu grade.
37I
n'rhli^
t
"^.i'1 f1 /)
vvrs !r \/
try
t
f20;
l
I I attrape une erreur catch(MyException ne)
{
une partie de 1'erreur i'/riteLine ( "Exception MyException attrape dans f1 0 " ) ; gnre maintenant une nouvelle exception II I I pour remonter la chane de transmission throw nev Exception("Erreur envoye parf10") ;
I
..
. traite
.
Console
l
l
Envoyer un nouvel objet exception permet une classe de formuler un nouveau message d'erreur avec des informations supplmentaires, tout en clarifiant ce qu'il pouvait y avoir d'approximatif au dpart. Passer un objet Exception gnrique la place d'un objet spcialis MyException garantit que I'exception sera traite un niveau situ au-dessus de f 1 O.
Envoyer une nouvelle exception prsente I'inconvnient que I'indication de pile redmarre au point du nouvel envoi. La source de I'erreur originale est donc perdue, moins que f 1 ( ) n'ait pris des prcautions spciales pour la conserver. Une commande throw seule, sans argument, renvoie le mme objet
exception :
public void fl o
try
{
t20;
l
338
public string
{
Nane
of D''
{
rt1r rn sNemo
l
nrrhlic dorrhle
{
Grade
ger
{
return l
l
dGrade;
//
|| |
implmente
f interface
I0omparable
| vvruyeriu anraraTn - LvltrHat o ull auL! vLi _^^rqnrr rrn nrrtro nh-iet (nnc. e o
,^aa.
I II II
I
{
et dcide lequel
dans le
I conoare 1'obiet Student courant (aooelons-le - - --J I 'celui de gauche' ) 1 tautre student (appelons - 1e ll 'celui de droite'), et gnre une erreur si aucun des deux // n'est un obiet Student Student leftStudent = this; :r (\; | t-: ^1^+L..i^^t is student)) ar \!rrrLwuJcu
I I
{
Console
l,lriteLine
I I if
/ I
{
Student rightStudent = (Student)right0bject; // gnre maintenant -1, 0, ou I sur 1a base du // critre de tri (1a moyenne des points d'UV de 1'tudiant)
Oa classe Double contient une mthode CompareTo0 que nous aurions pu utiliser 1a place)
(rightStudent.Grade
rotrrrn -1.
leftStudenr.grade)
if
{
(rightStudent.Grade
leftStudent.Grade)
return
l
1;
return
0;
] l l inpTnentent
37 7
Console.Read0;
j cre un objet C l as s i et l'utilise immdiatement pour invoquer la mthode r i r r, Cette mthode appelle f2 l),qui appelle f,t ( ), qui appelle f + ) . La fonction ; -. ) effectue cles vrifications d'erreur extrmement ' sophistiqttes, clui la conduisent envoyer soit un objet i'lyExceprrcr-r, soit un objet Er,: - 1, i- , r n gnrique, selon la valeur de I'arqument bclolen. L'exception est cl'abord passe f -i I ). L, C# ne trouve aucune instruction catch, et le contrle remonte donc r2 (), qui attrape I'objet I'lvException. Comme I'objet trxcep.- r on gnrique n'a pas encore trouv d'instruction catci.r corresponclante, le contrle continue remonter. Finalement, c'est t 1 r') qui cclntient une instruction ':ai ( 1r Corrsponclant I'objet envoy.
Ma:'-n (
Le deuxime aptrtel de r{aini; pysvoque I'envoi par f+() d'un objet iuli-E>rc e 1r*- I rr r, qtri esl attrap par f :l ( ) . Cette exception n'est pa.s envoye f I i), parce qu'elle a t attrape et traite par f 2 i). Le progranrme clonne la sortie suivante
Bnvoie d'abord une exception gnri"que Dxception gnrique attrape dans f1 0 Exception gnrique envoye dans f4 0
:
f40
+arminor
3 ( ) , qui ne contient aucune instruction catch,, n'a rien d'inhabituel. Je pourrais mme dire que la plupart des fonctions ne contiennent aucune instruction catch, mais je pourrais tout aussi bien ne pas le dire. Une fonction n'a aucune raison d'attraper une exception si elle ne contient rien qui lui permette de traiter I'erreur d'une manire pertinente. lmaginez une fonction mathmatique ComputeX ( ) qui appelle Factorial () pour effectuer certains de ses calculs. En supposant que son code interne soit correct, si Fact,rrial O envoie une exception, c'est parce que la fonction appelante lui a pass une valeur incorrecte. ComputeX o peut ou non tre capable d'identifier pourquoi cette valeur tait incorrecte, mais en tous les cas, elle ne peut certainement pas rsoudre le problme.
Une fonction comme f2 ( ) n'attrape qu'un seul type d'exception. Elle recherche une certaine classe d'erreurs. Par exemple, l"fyException peut faire partie des
336
Comparez cet exemple t'algorithme de tri du Chapitre 6. L'implmentation de cet algorithme demandait pas mal cle travail et ncessitait beaucoup de code. Il est vrai que rien ne vous garantit que Ai'ra.,i. Sor:r | ) est meilleur ou plus rapide que cet algorithme. Il est seulement plus facile.
Assembler le tout
Voil le moment que vous attendiez tous : le prograrnme Sc,r.i Inte r-t'a c e complet, construit en utilisant ce que nous avons dcrit plus haut dans ce
chapitre
// Sortlnterface - montre conment l on peut utiliser 1e principe de f interface pour offrir II une meilleure souplesse pour 1e factoring /I et f implmentation des classes
l
using
{
System;
nanespace
Sortlnterface
lui-mme en
I
'i
c.1 ayable
string GetString0;
]
class C1ass1
{
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
(IComparable [] ) students;
II
IDisplayable
I
Bird[] birds = Bird.CreateBirdlist0 I I renarquez qu'il n'est pas indispensable de faire un cast
;
I trie maintenant par nom un tableau d'oiseaux en utilisant I Les mmes routines, bien que 1es classes Bird I I et Student n'aient pas de classe de base comnune Console.lfriteline("\nri de la liste des oiseaux"):
I
DisplayArray (displayable0bjects ) ;
37 5
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
:
I ltyException - nontre comment une nouvelle classe d'exceptions peut ll tre cre, et comment des fonctions peuvent ll attraper exactement ce qu'e1les sont faites nolr tr'itpr tnut en laissant passer les autres II
using
name
{
System;
// i-ntrodui"t un certain type de 'MyClass ' public class MyClassi) i I Uynxception - - ajoute la classe Exception standard
une rfrence MyClass
Exception
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
)")
33 4
fr-( llr9r,
Y
Que c'est beau ! Voyez comme les rsultats sont aligns sur les noms complts par des espaces pour faire tous la mme longueur.
Interfaces t rdfinies
De mme, vous trouverez dans la bibliothque stanclard de C# des interfaces intgres en abondance. Par exemple, I'interface IComparabie est dfinie de la faon suivante :
interface
{
IComparable
I I conpare 1'objet courant 1'objet ,o, ; retourne ll 1 s'i1 est plus grand, - s'il est plus petit,0 dans int CornpareTo(object o)
;
1es autres cs
Une classe implmente I'interface IC.-.r,parab1e en implmentant une mthode CorLpar-,oTo i). Par exemple, Sr,:ing f ) implmente cette mthode en comparant deux chalnes. Si les chalnes sont iclentiques, elle retourne 0. Si elles ne le sont pas, elle retourne soit I soit un signe -, selon la plus
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)
37 3
De retour dans l{ai n O, I'instruction catch spcifie qu'elle attend un objet llyException. Une fois I'exception attrape, Ie code de I'application peut
encore demander n'importe quelle proprit d'une Excep*.ion, comme dans I'appel toString O. Cette instruction catch peut galement invoquer des mthodes de I'objet l"iirCiass fautif stock dans I'envoi (throw).
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
+ {
rtt Lr y
SomeOtherFunction
]
catch(MyException
{
me)
i/
ici
332
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.
interface IDisplayable
t
{
I
'1
string GetString0;
J
Idisplayable
private string sName; private double dGrade = 0.0; /l accde aux mthodes en lecture seule
-..L1.i^ L! rrr yuvf,ru -+-.:-^
t
Name
get
{
return
l
]
sName:
37 I
Le reste de cette sortie est ce que I'on appelle une indication de pile (stack trace). La premire ligne de I'indication de pile indique I'endroit partir duquel I'exception a t envoye. Dans ce cas : Factorial (int ) (plus prcisment, la ligne 23 du fichier source C1ass1 . cs. Factorial ( ), ou la ligne 52 du mme fichier, a t invoque dans la fonction Main (strlng IJ ). L'indication de pile s'arrte I'tain ( ), parce que c'est le module dans lequel I'exception a t attrape.
t\\u-rr|r
If Cff I Y
L'indicationdepileestdisponibledansl'unedesfentresdudbogueur
de Visual Studio. Je la dcrirai au Chapitre
16.
.1
Il faut bien admettre que c'est plutt impressionnant. Le message dcrit le problme et identifie I'argument qui en est responsable. L'indication de pile vous dit quel endroit I'exception a t envoye, et comment le programme y est arriv. Avec ces informations, vous pouvez vous jeter sur le problme comme la foudre.
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
{
,,1::i':::.
:
base(sMsg)
{
tyob3'..a = *0,
//
public
permet aux classes extrieures d'accder une classe d'information MyClass MyCustomObject{ get {return myobject;J}
330
^$sq t_ 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
(
string
sNote)
il
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.
Une classe implmente une interface en fournissant une dfinition pour chaque mthode de celle-ci : nublic class Pen : IRecordable
{
sNote)
I ]
l
PUUJr-U UJ-dbb
{
PDA
: ElectronicDevice,
(string
sNote)
IRecordable
369
nValue);
// commence par donner 1a valeur 1 un "accumulateur" double dFactorial : 1.0; I I tait une boucle partir de nValue en descendant de 1 chaque fois
tttt 11r nrl
I I
do
{
dFactorial *=
nValue;
return dFactorial;
]
try
{
de 6 -6
double dFactorial = MyMathFuncti-ons.Factorial(i) ; // affiche 1e rsultat chaque passage Console.1^lriteLinel"i = {0}, factorielle = {l}", i, MyMathFunctions.Faetorial(i) ) ;
]
catch(Exception e)
f
Presque tout le contenu de cette version "exceptionnelle" de l"lain O est plac dans un bloc trv.
32 8
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
nh<f ]
rrnt
nrrhl
'i
n "oid
TakeANOte
(string
SNOte)
ThingsThatRecord
i
]
public class
{
PDA
ThingsThatRecord
I ,
pDA
ThingsThatRecord
]
-rl
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
36 7
t/
Il mlange le code normal et le code de traitement des erreurs, ce qui obscurcit le chemin d'excution normal, sans erreur.
Ces problmes ne paraissent pas si graves dans cet exemple simple, mais ils ne font qu'empirer avec la complexification du code de Ia fonction
appelante. Le rsultat est que le code de traitement des erreurs n'est jamais crit pour traiter autant de conditions d'erreur qu'il devrait. Heureusement, le mcanisme des exceptions rsout tous ces problmes.
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
{
\ /
/l
try
I I
I
ceci est
fait
SomeOtherFunction ( )
ll
l
t
catch(Exception
I
I te contrle passe par ici en cas // <i'erreur en un point quelconque du bioc try ou ll de toute fonction appele par celui-ci il 1'objet Exception donne 1a description de 1'erreur
;
l
l
nrrhf PueffL
in vnid Ss6sQtherFUnCtiOn0
i
|
l'erreur se produit
quelque
part
dans 1a fonction
326
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.
36 5
- tzv - rlv
:6
-L -1
i i
= 2, factorielle - 1, factorielle
1 = u, racl0r1elle -U lactoriaL 0 a reu un nombre ngatif Appuyez sur Entre nntrr torminor
L'indication d'une condition d'erreur I'aide d'une valeur retourne par une fonction n'est autre que la manire dont le traitement d'erreur a toujours t ralis depuis les premiers jours de FORTRAN. Pourquoi changer ?
/e
Quel est I'inconvnient de retourner des codes d'erreur ? C'tait trs bien pour FORTRAN ! C'est vrai, mais cette poque les tubes vide taient aussi trs bien pour les ordinateurs. Malheureusement, I'approche des codes d'erreur prsente plusieurs inconvnients.
Tout d'abord, cette solution dpend de la possibilit de retourner une valeur normalement illicite, mais il existe des fonctions pour lesquelles toutes les valeurs qu'il est possible de retourner sont licites. Toutes les fonctions n'ont pas la chance de ne retourner que des valeurs positives. On ne peut pas calculer le logarithme d'un nombre ngatif, mais un logarithme peut tre positif ou ngatif.
1t$Qa"
{cg)
Bien que I'on puisse contourner ce problme en utilisant la valeur retourne par une fonction pour une indication d'erreur et un argument de type out pour retourner une donne, cette solution est moins intuitive et fait perdre une partie de la nature expressive d'une fonction. Quoi qu'il en soit, lorsque que vous aurez vu comment fonctionnent les exceptions, vous vous dbarrasserez bien vite de cette ide.
D'autre part, un entier ne permet pas de stocker beaucoup d'information. La fonction Far:tor:iai O retourne -1 si I'argument qui lui est pass est ngatif. L,'identification du problme pourrait tre plus facile si nous savions exactement ce qu'tait cette valeur ngative, mais il n'y a pas de place pour retourner cette inforrnation.
Troisimement, le traitement des erreurs retournes est optionnel. Vous ne gagnerez pas grand<hose en faisant retourner par Factorial O un code
32 4
appelle SavingsAccount.Withdraw( ) pour effectuer Test (SpecialSaleAccount) appelle SpecialSaleAccount. Withdraw ( ) Passage d'un SaleSpecialCustomer
pour effectuer Test (BankAccount)
appelle SavingsAccount. Withdraw( ) pour effectuer Test (Savingsccount) appelle SavingsAccount .Withdraw ( ) pour effectuer Test {Specia1Saleccount)
appel 1e SaleSpecialCus toner.
appe
1
Wi
thdraw
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-'..
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.
363
public class
{
MyMathFunctions
I I ce cui suit renrspntp lps veleurs illicites public const int NEGATIVB_NUMBER = -1; nublic const int NON INTEGER VALUE = -2; // Factorial - retourne 1a factorielle d'une valeur
I
fournie
dValue)
nombres
ngatifs
return
]
NEGATIVE_NUMBER
// vrifie si 1e nombre est bien entier int nValue = (int)dValue; if (nValue l= dValue)
{
return
l
N0N_INTEGER_VAIUE
// commence Dar donner 1a valeur 1 un "accumulateur" double dFactorial = 1.0; I I tait une boucle partir de nValue en descendant de I I nol:r mrrl tinlier 1'accunulateur
I
chaque
fois
I par 1a valeur
obtenue
do
{
du nombre
;
NUMBER)
}trriteLine
32 2
Console.Writeline
account . l,lithdraw
( 1 00) ;
l
public static void Test2(SavingsAccount
{
account)
;
ir" stat'i c
vo'i
Tpsf
Console,WriteLine
account)
;
00)
// II II
{
BankAccount
possdant
publie class
i
il
{
Withdrar,'ral
- tout retrait est autoris jusqu' 1a valeur du solde ; retourne 1e montant retir
Withdraw(double dWithdraw) BankAccount.Withdraw()")
;
Console.Writeline(" appelle
l
//
{
SavingsAccount
compte bancaire
BankAccount
Console.i,{riteline(" appelle
]
SavingsAccount.Withdraw0")
nrrhf
{
Withdraw(double dWithdrarral)
36t
II
1a fonction Factorial de 6 -6
1-|
for(inti=6;i)
t
I
passage
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
Un simple coup d'il avis ces rsultats permet de voir qu'ils n'ont aucun sens. Pour commencer, le rsultat d'une factorielle ne peut pas tre
320
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
:
\---Il \Y' -
A propos du programme Abstractlnheritance, remarquez qu'il est illicite de crer un objet de la classe AbstractBaseCiass, mais que I'argument de Test O est dclar tre un objet de la classe AbstractBaseClass ou de I'une de ses sous-classes. C'est ici la clause concernant les sous-classes qui est cruciale. Les objets de SubClassi et SubClass2 peuvent tre passs, car I'une et I'autre sont des sous-classes concrtes de Ab st ractBas eClas s.
Chapitre 15
Ouelques exceptions
d'exception
Dans ce chaptre :
Traiter des erreurs avec les codes retourns. Utiliser plutt le mcanisme des exceptions.
Crer votre propre classe d'exceptions.
Renvoyer I'envoi de I'extrieur. Redfinir des mthodes critiques dans la classe des exceptions.
e sais que c'est difficile accepter, mais il arrive qu'une mthode (ou
une fonction) ne fasse pas ce qu'elle est cense faire. Mme celles que j'cris (surtout celles que j'cris) ne font pas toujours ce qu'elles doivent faire. Il est notoire que les utilisateurs aussi ne sont pas fiables. II suffit que vous demandiez un int pour qu'il y en ait un qui entre une chane de caractres. Parfois, une mthode poursuit son chemin joyeusement, ignorant voluptueusement qu'elle est en train de produire n'importe quoi. Il y a toutefois de bons programmeurs qui crivent leurs fonctions de manire anticiper sur les problmes et les signaler lorsqu'ils se produisent.
f ilfi ) qu" C# vous envoie la tte lorsque vous essayez de gnrer votre "9tt$ , 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.
C#
3I I
Le programme commence dans llain O par une boucle-"i,- re cottnant un bloc try. Ce n'est pas rare pour un programme de manipulation cle fichiers (dans la section sur Stre:rnP.eade;:, c'est une approche un peu diffrente qui aboutit au mme rsultat).
YK
e,
Placez toutes les fonctions d'l/O dans un bloc i-r-i.avec une instructiotr caT-r.r.r qui gnre un message d'erreur appropri. Il est gnralement considr de mauvaise pratique de gnrer un message d'erreur inappropri. La boucle whi 1e srt deux choses diffrentes. Tout d'abord. elle perrnet au programme de revenir en arrire et d'essayer nouveau en cas d'chec d'une llO.Par exemple, si le programme ne trouve pas un fichier que I'utilisateur peut lire, il peut demander nouveau le nom du fichier pour tre str de ce
qu'il fait avant d'envoyer promener I'utilisateur. Ensuite. I'excution d'une commande break dans le programme vous fait sortir d'un air clgag du bloc try, et vous dpose la fin de la boucle. C'est un mcanisme trs pratique pour sortir d'une fonction ou d'un programme.
Le programme Filei;r ite lit sur la console Ie nom du fichier crer. Il se termine en sortant de la boucle-"^il.: ie si I'utilisateur entre un nom de fichier nu11. L'essentiel du programme se produit dans les deux lignes suivantes.
Pour commencer, le programme cre un objet Fi l eSt: -.an qui reprsente le fichier de sortie sur le disque. Le constructeur:'r-eStrearn utilis ici accepte trois arguments :
t/
Le nom du fichier: Il est clair que c'est le nom du fichier ouvrir. Un simple nom de fichier comme f i L e:,ane . :xt est suppos tre dans le rpertoire courant. Un nom de fichier qui commence par une barre oblique inverse, comme \ur-- rlerioir,:\loncief icirier. i.x*, st suppos tre le chentin d'accs complet au fichier sur la machine locale. Un nom de fichier qui commence par deux barres obliques inverses, par exemple \ \ ..--- r re n.ach i ne \, un r nerto i r c \'lr: aLit r rpertoir e\nondef lchier. rxi. est suppos tre le chemin d'accs un fichier rsidant sur une autre machine. partir de l, le codage du nom de fichier devient rapidement compliqu. Le mode de fichier: Cet argument spcifie ce que vous voulez faire avec le fichier. Les modes d'criture de base sont la cration (CreatelJew), I'ajout (Aopeni), et la rcriture (Cr eate). \'.' ,:z '- -l{er,,' cre un nouveau fichier. Si le fichier existe dj, rlreateile.,L envoie une IOExceotion. Le mode Create simple cre le fichier s'il n'existe pas dj, mais l'crase (le remplace) s'il existe clj. r.ppeni ajoute quelque chose Ia fin d'un fichier s'il existe dj, et cre un nouveau fichier dans le cas contraire.
tz
C#
40 I
fichier que vous avez entr (lath est une classe conue pour manipuler des informations sur les chemins d'accs).
Le chemin d'ctccs (path) est le nom complet du dossier dans lequel se trouve le fichier. Dans le nom de fichier complet c:\user\tenD dlreC*rct'1.\tex',.lxt, le chemin d'accs est la partie c:\user\i,i-lr oireC*;orv.
.
1e$!Qa.
=qE/
^.v7q
La mthocle ilrnbiner i ) est capable de se rendre compte qu'un fichier comme c: \tes:.txt, PathO n'est pas dans le rpertoire courant.
En rencontrant la fin de la boucle whiie, soit en excutant tout le bloc tr.1, soit en y tant envoy par I'instruction catci'r, le programme revient tout en haut pour permettre I'utilisateur d'crire un autre fichier.
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 non de
fichier
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
C#
403
break;
ll
Console ]
I,]riteline
" {0J
\n\n" , fe.
Message)
fichier :")
try
t I
I I
r,vhile
{
I lit
T tStnnllr
=:
nul_LJ
break; l
ll crit
Console
.
sur la console ce
)
;
LIriteline ( slnput
I I
I artreoe toute erreur de lecture/criture et *- "*b..*-- la sisnale (ce aui fait aussi sortir de 1a boucle)
;
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
C#
40 5
Readline O. Le programme affiche cette ligne sur Ia console avec I'omniprsent appel Coirsole. i,JrlteLi:icr (t avant de revenir au dbut de la boucle pour lire une autre ligne cle texte. L'appel F eaC'iiie ( ; retourne un nu11 lorsque le programme atteint la fin du fichier. Quand cela se produit, le programme sort de la boucle de lecture, ferme I'objet, et se termine.
Remarquez comment I'appel cicse (, est insr dans son propre petit bloc try. Une instruction caici, sans arguments attrape tout ce qui passe sa porte. Toute erreur envoye par C,cse L, est attrape et ignore. L'instruction catch est l pour empcher I'exception de se propager vers le haut de la chalne et d'arrter I'excution du programme. L'erreur est ignore parce que le programme ne peut rien faire en cas cle 1,,,.-sil invalide, et parce qu'il va de toute faon se termir-rer la ligne suivante.
.$sC ,. Je ne donne I'exemple de catch sans arguments qu' des fins de clmonsHtration.Laprsenced,unseulappeldansSonproprebloc'iaVeCune instruction carcfi "attrape-tout" vite qu'un programme s'arrte cause tt9, Y d'une erreur sans importance. N'utilisez toutefois cette technique que
pour une erreur vraiment sans importance, ne pouvant causer aucun
dommage.
find fi1e
nom
\C#Prosrams\FileRead\TestFilex.txtr'
Entrez 1e
lire:TeslFilel.txt
Contenu du
,Te
fichier
tane
Et onnnro nn Et nrri c nr.r a\ Y* -" r-r" ""'*-Annrrrroz <rrr Fnfro nnrrr term'inpr
TestFilel . txt
lte.
=(t ^*\
Ce n'est toutefois pas le mme fichier. J'ai dt copier Ie fichier cr avec Test'rnJrite du rpertoire Trst-ll: !: \1 ii\ie5ug au rpertoire TestRead\b,r\deb.ig. Si vous voulez que ce soit le mme fichier dans les deux cas, il vous faut donner le chemin d'accs complet, comme c:\test.txt (j'aurais pu le faire pour ces deux exemples, mais je ne voulais pas mettre de dsordre dans votre rpertoire racine).
Ginquime partie
Chapitre 17
et le plumage
Dans ce chapitre :
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
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.
4l I
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.
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.
4l3
Comme je I'expliquerai dans les sections qui suivent, ces tapes ne sont pas mal faites si vous les suivez I'une aprs I'autre.
Slectionnez Fichier/Nouveau/Projet.
La fentre Nouveau projet apparalt.
Au lieu de I'icne Application console, cliquez sur I'icne Apptication Windows, et entrez comme nom SimpleEditor.
Dans la fentre Nouveau projet, le champ Emplacement spcifie le rpertoire dans lequel seront stocks les fichiers de SimpleEditor. Autrement dit, Visual Studio va mettre tous les fichiers que je vais
Ei(htr/ Edilr
.!' -L'-t
I i!. EO
::'::
l -' '!'
?i"ffi,,:li#"i:'l;|,t2,';',. -inl:J
rm8ordr5i/
5,rable
FqhtToLeft
li.
tigure 17.2:
L'affic h age initial pour toutes les
Ir--t ina,."r.
t.,:.1
FisF
l3qrri
t te
l,n:!nt.r
.
Ei iorEniPnpr' lexind6u
applications Windows.
&ffiGcdrdt
Ce curieux affichage s'appelle le Concepteur de formulaires (ou, plus simplement, le Concepteur). Le cadre de vous voyez gauche est le formulaire, qui va tre la base de notre programme SirnpleEdltor.
4l 5
.'^"i-^ urrr
C,,a+^-. eJLEur,
using System.Drawing; using Systern. Collections using System, ComponentModel using System.Windows . Forms; using System.Data;
;
nanespace SimpleEditor
{
//l
II
(sumnary)
,
I Snnnerv dpsc ri nti on for T'orml lll (lsunnary) nrhl j^ nlrcc l'61911 : SyStenr.WindOvS.FOrmS.FOrm
t
I
(sunmary)
lt / lt
i
tt
Required
for
InitializeConponent
lt
i
lt
l
I III
II t
r
lll
kunnary)
clean up any resources being used.
(lsumary)
)
if(
{
disposing
if
t
f
(conponents != nu11)
4t6
components.Dispose0; l
l
heca . D'i cnnco ( rli cnncinv4s urDyvoc \ uIDyu rrI I ,,
.
t
for Designer support - do not modify the contents of this nrethod r+ith the code editor. llsunnary) private void InitializeConponent0
Renrrirerl method
{
(sunrnary)
For:nt
this.AutoScaleBaseSize
this.ClientSize =
this.Name
= "Forml";
this.Text
l
"Sirnple Editor";
(summary)
ISTAThread]
static void
Annl 4ytl!rLo
inatinn rrvtl
'
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
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.
L'tiquette Forml contenue dans la barre de titre du formulaire devient Simple Editor.
6.
- du Text
4l 2
Le menu dition a besoin des trois grandes options d'dition : Couper, Copier et Coller. D'autre part, tous les diteurs comprennent les raccourcis clavier de ces trois options : Ctrl+X, Ctrl+C, et Ctrl+V, respectivement.
SimpleEditor aura galement besoin d'un menu Format, comportant les options Gras et ltalique pour mettre en forme le texte.
Fournir une aide vritable est une tche difficile - beaucoup trop complique pour un diteur simple comme SimpleEciitor. Le menu d'aide de cette application devra se contenter du minimum absolu : I'option propos de. Dernire exigence : il nous faut un moyen de contrler la taille de police. Voil une chose qui laisse la place un peu de fantaisie. En plus d'une simple fentre dans laquelle I'utilisateur peut entrer la taille de police souhaite, SimpleEdltor y ajoutera une sorte de barre munie d'un index que I'on peut faire glisser, que nous appellerons TrackBar. Pour obtenir 8 points, faites glisser I'index I'extrmit gauche. Faites-le glisser I'extrmit droite, et vous clbtenez 24 points. (J'ai une autre raison de procder ainsi : je veux vous montrer comment relier deux objets d'l/O de manire qu'un changement dans I'un soit rpercut dans I'autre.)
Itla soluton
Avec les paramtre.s que j'ai dcrits dans la section prcdente, je suis arriv la solution montre par la Figure 17.1. Vos propres rsultats peuvent tre diffrents selon vos gotts personnels.
W:"
-lol
Frrrt
?
t{J
Fichier Edition
r:I
p trI: t -'rn
=rl
SinpleEditor.
ralb
s-
'Je
police
jf14
)|
Dessner la soluton
Comme vous pouvez I'imaginer, j'ai dt passer par de nombreuses tapes pour arriver en partant de zro l'uvre d'art montre par la Figure 17.1.
4l 0
t/ t/
L'utilisateur peut entrer et effacer du texte (sinor-r, ce rle serait pas vraiment un diteur). L'utilisateur peut couper et coller du texte. non .seulement clans SrrpieEdi*.o1, mais aussi entre il ::;;, '.:i1,,1i., r ct d'etutres applications, par exemple Word.
t/ t/
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.
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.
"*
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
que le programme donne la sortie attendue. Une fois que le programme lu le fichier, il se termine. Si I'utilisateur veut lire un autre fichier, il lui suffit d'excuter nouveau le programme.
Le programme commence par une boucle rihr
le, coilffie son cousin Dans cette boucle, il va chercher le nom de fichier entr par I'utilisateur. Si le nom de fichier est vide, le programme envoie un message d'erreur:\Ious a\r?.z entr un nom ie rlchiel ,,'ide. Dans le cas contraire, le nom de fichier est utilis pour ouwir un objet FrleSt-reai. en mode cle lecture. L'appel Frle.LJpen ( ) est ici le mme clue celui utilis dans i -..-r',:ire :
Filel^i
rite.
/ t/
Le premier argument est le nom du fichier. Le deuxime argument est le modle du fichier. Le mode Fil-.Mode. Ctpen dit : "Ouvrir le fichier s'il existe. sinon envoyer une exception." L'autre possibilit est r-lprq1,11ew, qui cre un ficl-rier de longueur nulle si celui-ci n'existe pas dj. Personnellement, je n'ai jamais rencontr le besoin de ce mode (qui veut lire un fichier vide ?), mais chacun mne sa barque comme il I'entend.
Le dernier argument indique que je veux lire
t/
partir de ce
L'objet FileS:rean f s rsultant est alors insr dans un objet StreairP.r:ac1er sr qui offre des mthodes pratiques pour accder au fichier texte. Toute cette section d'ouverture de fichier est enchsse dans un bloc t r.,.,
lui-mme enchss dans une boucle whiie, insre dans une nigme. Ce bloc t ry est strictement rserv I'ouverture de fichier. Si une erreur se produit pendant le processus d'ouverture, I'exception est attrape, un message d'erreur est affich, et le programme reprend au dbut de la boucle pour demander nouveau un nom de fichier I'utilisateur. Toutefois, si le processus aboutit un objet nouveau-n St reanReader en bonne sant, la commande break fait sortir de la logique d'ouverture de fichier et fait passer Ie chemin d'excution du programme la section de lecture.
.ssG ./ Y
Hdesexceptionsdefichier.VouspouVeZinsrertoutleprogramrrrede traitement de fichier dans un mme bloc rr-,,, comme dans Fi le,.,r rte, ou tTg,
402
.' r .
0'
irtucy4Lg
{
yuufrL
f -Lgr\u
vaq ^ ..^ ,'.ss] tra
l/ i1
nous
= ""; "'" ii continue essayer de lire un noin de fichier jusqu' ce qu'il // trcuve un (1a seule naaire de quitter pour I'utiiisateur est I I d'arrter 1e programne en appuyant sur Ctrl + C)
t
en
while {true)
f-- r\r J
i
I I Iit le non du fichier d'entre Console,Write("Entrez le nom d'un
sFileName
= Console. Readline 0
ll 1'utilisteur n'a rien entr ; envoie une erreur ll pour 1ui dire que ce n'esr pas satisfaisant if (sFileName,Length == 0)
t
throw
]
rLew
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
,/
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
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^
t.iriteLine ( ) et Console
sw . C1o
s
.t^i
riteLine
e()
b'
Remarquez que le programme donne la rfrence sw la valeur nu11 Ia fermeture du fichier. Un objet fichier est parfaitement inutile une fois que celui-ci a t ferm. II est de bonne pratique de donner la rfrence la valeur nu11 une fois qu'elle est devenue invalide, afin de ne pas essayer de l'utiliser nouveau dans I'avenir. Le bloc catch qui suit la fermeture du fichier est un peu comme un gardien de but : il est l pour attraper toute erreur de fichier qui aurait pu se produire en un endroit quelconque du programme. Ce bloc met un message d'erreur. contenant le nom du fichier qui en est responsable. Mais il ne se contente pas d'indiquer simplement le nom du fichier : il vous donne son chemin d'accs complet, en ajoutant I'aide de la mthode Path. combirre O le nom du rpertoire courant avant le nom de
39 8
ob
jet
I |
FileAccess.Write,
FileAccess.ReadWrite
FileStream
fs = File.0pen (sFileName,
FileMode . CreateNew,
FileAccess.l.rlrite) ; // gnre un flux de fichier avec des caractres UTFS sw = new StreamWriter (fs , System. Text , Encoding. UTIB) ; I I lit une chalne 1a fois, et envoie chacune au // FileStrean ouvert pour criture Console.l'lriteLine("Entrez du texte ; ligne blanche pour arrter"); while (true)
{
I |it 1a ligne suivante sur la console i/ quitte si 1a ligne est blanche string slnput = Console,ReadLine0 if (slnput.Length :: 0)
I
;
break;
lue
l,friteLine ( slnput ) ;
que nous avons cr
l
I
I ferne 1e fj-chier
0
;
sl^r. C1ose
sw
= null;
i catch(IOException fe) i
I une erreur s'est produite quelque part pendant // 1e traitement du fichier indique 1'utilisateur // le nom eomnlet du fichier I ^: ^..-^ ^'. rrvru uu rnprto.i rc Dar dfaut | |t dJUULC 4U -om drr !cy! Lvr!c f IT / / celui du fichier strino cDi r = Di roctorv CotCrrrrpntDi rpntorv(\ /) uvrJ string s = Path. Conbine (sDir, sFileName) Console.l,/riteLine("Xrreur sur 1e fichier{01", s) // affj.che naintenant 1e message d'erreur de 1'exception
I
: : , ; ;
Console ] ] I
Writeline (fe
Message)
. . ")
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
ob
jet
Avec C#, les classes de System. r0 supportent galement les f/0 asynchrones. En les utilisant, l'appel read ( ) restitue immdiatement le contrle pour permettre au programme
de poursuivre son excution pendant que la requte d'l/0 est satisfaite I'arrire-plan. Le pr0gramme est libre de vrifier l'tat d'un indicateur pour savoir s1 la requte d'l/0 a abouti.
C'est un peu comme de faire cuire un hamburger. Avec des l/0 synchrones vous mettez la viande hache cuire sur la plaque chauffante, vous la surveillez jusqu' ce qu'elle soit cuite, et c'est seulement partir de l que v0us pouvez vous mettre couper les oignons qui vont aller dessus.
Avec des l/0 asynch rones, voLrs p0uvez couper les oignons pendant q ue la viande hache est en train de cuire. De temps en temps, vous jetez un coup d'il pour voir si elle est cuite. Le momentvenu, v0us abandonnez un instantvos oignons, etvous prenez la viande sur la plaque chauffante pour la mettre sur le pain.
l/0 asynchrones peuvent amliorer significativement les perforrnances d'un programme, mais elles ajoutent un niveaur supplmentaire de complexit.
Les
Utilser S t
i:"
r-r
ili,{r i t e r
[-es programmes gnrent cleux sortes de sortie. Cert;rins programrnes crivent cles tiiot:s tlc rlortnes clans un pur fornrat binaire. Ce type cle
sortie est trtile porrr stocker cles objets cl'urie nranire efficace.
Beattcoup cle pro{tar}}ines, sirron la plupart. liseni r:t crivent des chalnes cle texte, lisibles par ult :tre lrurrrairr. Les classes cle flux Sr: eai:l,,rrirer et StrcanP,e,:;,.r,1,-:r s()ut les plus souples cles classes arccueillantes pour I'homnte.
:(dqfl
\/
Ae/'!-t\
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
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.
:
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.
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
| la
mme
classe
;
C-private () c1ass1.C-private0 ;
I I class2. I I
I les mthodes internes ne sont accessibles I les classes du mme espace de nont
que par
sont accessibles
par
// soit par 1a hirarchie d'hritage soit I I toute classe ciu mme espace de nom
class I . E-internalprotected class3 . E-internalprotected
(
(
; ;
(,r
return
l
-r,hr-^ yuurru
0;
| --lvatel/ w_y!
,r^rd vvfu
/\
1,
Console,
!riteline ( "Class
C-private" ) ;
l j
// Classl - une classe interne est accessible aux autres classes du mme espace de nom, mais II pas aux classes externes qui utilisent cet II espace de non II internal class Class3
{
I I
I Ia dclarati-on d'une classe comme interne force toutes I les mthodes publiques tre galement internes
Console
nrntpntpd y!vLLLu
{
internal
t
voi.d D-internal o
("
Console . 1,,Iriteline
Class3 . D*internal" )
v LvL uee
l
^,rhlin
yuurJL
\ /
Console
|jritel,ine
"C1ass 3 . E-internalprotected"
39 0
p;lr son noni pleinentent qualifi plcut clevetiir utt peufasticlieux. Lernot"cli.r::,:rg_cleC#vousperntetd'vitercepetlstltlt"
,,,;ijotrte I'espace rJe norn s1rcifi ture liste d'esl)aces tlirf;iut rlrre (.# <.:rlrrsrrlte p)()ur essayer cle rscx-rclre utt noltt cle classe. L'exc.inple rle l)r()qr;unlne.suivarrt se corntrlile sitns une plzrintrt: l.a c<>rnrnncli' ,,
cle nclrn par
namespace Paint
{
,-i
public PaintCol"or(int nRed, int nGreen, int nBLue) i] public vcid Paint0 {l
publ"ic
]
i
namespac
{
llathP.out ines
I /
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].
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
;,ir
r i :',:
r-
388
Ti-ansiati onLibr.r :--. respectivernent ces deux ensembles de classes vite le problnre : FileIO. Ccn,rert ne peut pas tre confondu avec
-f,...,,
I GtLi . )
,,i
-il,
L -Ir l
etl
I r ;,.,,s
l'{ySruff.
,,s$G t
L'Assistant Application de Visual Stuclio place chaque classe qu'il cre dans
nez tous les programmes cle ce livre : ils ont tous ts crs I'aide de I'Assistant Application. Par exemple, le programnle AliqnOutput a t cr dans le dossier --t1!.ni;u-f lrL1*.. Le nom du fichier source est Class i . cs, qui corresponcl au nom de la classe par dfaut. Le nom de I'espace de nom dans lequel se trouve Class l . t-:: est le mme que celui du dossier:Ai,gnoLitpl-1...
HuneSpaCeclenomportantlemrnenOmquelerpertoirequ'ilcre.Exami-
t\7, V
votre
classe dans I'espace de norn global. C'est I'espace de nom de base pour tous les autres espaces de nom.
namespace MathRoutines t
class Sort
{
public void
SomeFunction0
{l
386
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
Le nessase est (Tmnossihle d'inverser CustomExc eption . MathClass Exception envoye parDouble Inverse0
Annrrrraz crrr Fntro nnrtr tprminor
(Value = 0)
Jetons un coup d'il cette sortie : le message lifc,,.ir faia L.r i rc.or.nue : vient de l'{a: i-' i l. La chane -,t n's'r- -.il'. ( -in:ossi l-,-i-' r,l 'in ,,.erser 0,\, I'c,l iet esi <--) vient de ilu.-"t,-.11E:tcLrti.l. Le message Value 0 vient cle I'objet l"1ai-irCiass lui-mme. La dernire ligne, Excep tion en.".ove parDouble In'rerse, vient de C,-Lsl-r,'t:Fl,ce 'rti cn.
questions d'hritage, mais le principe est le mme. Par exemple, une mthode
382
/
t
le
//
t
I
Inverse
retourne
lix
0)
if (nValue0fObiect ::
{
'I
0", this)
try
t I I prend f inverse de 0 MathClass math0bject = new MathClass("Va1eur", 0);
catch (Exception e)
t
//
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
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.
I Mytxception - ajoute la classe standard I une rfrence MyClass publi"c class Myfixception : Excepti.on
I I t
Exception
private
I
r
MyClasss myobject;
base(sMsg)
ll
]
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
affiche
1es inforrnations que nous voulons, srais dans un fornat plus agrable
37 8
types d'exception dfinie pour la brillante bibliothque de classe que je viens d'crire (c'est pour a que je I'appelle r j liiantli l-,rar'.'). Les fonctions qui composent Briillan+-Llbrarv envoient et attrapent des exceptions
l'{.,'Exc
eption.
Toutefois, lesfonctionsdelabibliothque:r--ll;:r:..r:.rar', peuventaussi appeler des fonctions cle la bibliothque gnrique S'.'s,r'.:r,. I-es prernire.s peuvent ne pas savoir comment traiter les exceptions de la bibliothque Si.sten, en particulier si elles sont causes par une entre errone.
q/\
f\
e,
vous ne savez pas quoi faire avec une exception, laissez-la passer poLlr qu'elle arrive la fonction appelante. Mais soyez honnte avec vous-mme : ne laissez pas passer une exception parce (lue vous n'avez simplernent pas le courage d'crire le code de traitement cl'erreur correspondant.
Si
Relancer un objet
Dans certains cas, une mthocle ne peut pas traiter entirement une erreur, mais ne veut pas laisser passer I'excepti<)n sans y mettre son grain de sel. C'est comme une fonction mathmatique qui appelle:a.i )rral ,) pour s'apercevoir qu'elle renvoie une exception. Mnte si la cause premire du problme peut tre une donne incorrecte. la fonction rnathmatique est peut-tre en mesure de fournir des indications supplmentaires sur ce qui s'est pass. Un bloc catch peut digrer partiellement I'exception envoye et ignorer le reste. Ce n'est pas ce qu'il y a de plus beau, mais a existe.
L'interception d'une exception d'erreur est une chose trs courante pour les mthodes qui allouent des lments. Par exemple, imaginez une mthode F O qui ouvre un fichier quand elle est invoque, et le referme quand elle se termine. Quelque part dans le cours de son excution, F () invoque G O . Une exception envoye de G ( I passerait directement travers r ( ) sans lui laisser la moindre chance cle fermer le fichier. Celui-ci resterait donc ouvert jusqu' ce que le programme lui-mme se termine. Une solution idale serait que F r ) contienne un bloc catch qui ferrne les fichiers ouverts. Bien entendu, F O est libre de passer I'exception au niveau suprieur aprs en avoir fait ce qu'il fallait pour ce qui la concerne.
Il y a deux manires de renvoyer une erreur. La premire consiste envoyer une deuxime exception, avec les mmes informations ou ventuellement des informations supplmentaires :
37 6
Console.
] I
{ t rrt
Writeline
(e. Message)
I tZ ' -
i
f? thFvnant; ^.Type)
rJ \vu.r!vyL4vrrr
catch (MvExceotion
{
me)
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
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 ( ) " ) ;
]
// 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
l
catch (Exception e)
{
l
]
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
instructions quelconques . ..
Si elle en a la possibilit, I'instruction catch lul.,,Exception attrapera objet I'lyS p e c i alExc epr i on envoy.
tout
Af\ =(t
gnrale
Faites toujours se succder les instructions catch de la plus spcifique Ia plus gnrale. Ne placez jamais en premier I'instruction catch Ia plus
:
public void
t
SomeFunction0
try
{
SomeOtherFunction{); l
catch (Exception
t me)
// tous
l
ici
catch(MyException e)
{
I I
I I
auctne exception ne parvient jamais jusqu'ici paree qu'e1le est attrape par un instruction catch plus gnrare
l
]
Dans cet exemple, I'instruction catch la plus gnrale coupe I'herbe sous le pied de Ia suivante en interceptant tous les envois.
37 2
(lrs*:rr:l,li!t'-,i''
1,
et nlet en utilisation
try
{
I
I
I I
SomeOtherFunction ( )
autres oPrations.
me)
l
catch (MyExcePtion
{
strj.ng s = me.ToString0; I I nais vous vez aussi accs toutes 1es proprits et // de votre pr0pre classe d'exceptions
MyClass mo = me.MYCustom0bject; I I par eremple, denandez 1'objet MyClass de
s'affi-cher
lui-mme
string s = mo'GetDescriPtionO;
]
I I
I I
MyClass myobject
l
Dans ce fragment de code, SoineFunction
) invoque Sc,neCthelFuncli tlll () de I'intrieur d'un bloc rr-v. SorneOtherFunction O cre et utilise un objet ( rnyobi ect. Quelqu" puit clans Sr-rneOthe r-Funct ron ) , une fonction de vrificapour signaler qu'une condition d'erreur se prpare envoyer une exception tion d'erreur vient de se produire. Plutt que de crer une simple Exi-e'i:i01,
r
se sert de la toute nouvelle classe l'l'u.E:;cepti:'tr, pour envoyer non seulement un message cl'erreur, mais aussi I'objet 11'\rrb -i ec t fautif
SomeF
unction
( ,r
37 0
Cotnme l'Iairi ir est le point cle clpart clu programme, il est bon de toujours en placer le contetru clans un bloc t r ,-. Toute exception qui ne sera pas "attrape" ailleurs renrontera finalernent jr-rsqu' I'lain ( r . C'est clonc votre
dernire opportunit cle rcuprer Llne erreur avant qu'elle aboutisse Windows, dont le rnessage d'erreur sera beaucoup plus difficile interprter.
I attrape I'objet E:,cip*r.ic,n et utilise sa mthode Jr-.!r-:rir i', pollr afficher sou.s fonne d'une simple chalne la rnajeure partie des inforrnatiotts sur I'erreur contenues dans I'obiet exi: eDlir,r.
.($-_.-
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. La sortie de ce programme apparalt comme suit
4. factorieTTe = 24
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
sur Entre pour terminer...
Appuyez
-1.
368
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-
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).
_-"-ffi
// FactorialException - cre une fonction factorielle qui indique FactorialQ 1es arguaents illicites I en utilisant un objet Exception It
I
using
t
System;
namespace
FactorialException
i/ ll
MyMathfunctions
collection de fonctions
mathmatiques
public class
i
// lactorial II
t
retourne fournie
la factorielle
d'une valeur
nValue)
//
string s = String.Forrnat(
illicite
pass
Factorial {0J",
366
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
return
l
MY_ERROR_1;
if (errRtn == SF-ERROR2)
{
return
l
I
My_ERROR,2;
appelle SomeOtherFunetions,
;
1it 1'erreur,
Console.l,lriteLine("Erreur de type
return
l
MY*ERROR-3;
if
{
(errRtn == SOF-ERROR2)
t/ t/
Il est trs rptitif. Il oblige le programmeur inventer de nombreuses indications d'erreur et en maltriser I'emploi.
36 4
if
t
(dFactorial =: l4vMathl'unctions
\eL ev ev ++ --J
NON*INTEGER*VALUE)
Console.
I^Iriteline
non
entier");
i, MyMathFunctions.Factorial(i)
1
J
//
Console.l^lriteLine("Appuyez Console.Read{);
de tests. Le passe est ngative (0 est accept parce qu'il premier regarde si la valeur
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
ngatif. Ensuite, remarquez que les valeurs ngatives ne crclissent pas de la mme manire que les valeurs positives. Manifestement, il y a quelque chose qui cloche.
=(dg,
^"ffi
Les rsultats incorrects retourns ici sont assez subtils par rapport ce qui aurait pu se produire. Si la boucle de Factc,r'iai O avait t crite sous la forme do i . . . I whiie (dValue l: 0), le programme se serait plant en passant un nombre ngatif. Bien str, je n'aurais jamais crit une condition comme,"hile (dValue !: 0), car les erreurs dues I'approximation auraient pu faire chouer de toute faon la comparaison avec zro.
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 - 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
36 0
ob
jet
dis bien jamais.' si vous n'avez pas I'intention de dboguer vos programmes et si vous ne vous souciez pas qu'ils marchent, alors seulement c'est peut-tre une bclnne ide.
Le programnle !'ar:'-or i alErrcr suivant montre ce qui arrive quand les erreurs ne sont pas cltectes. Ce programme calcule et affiche la fonction factorielle pour de nombreuses valeurs, dont certaines sont tout juste licites.
La factorielle du rtornbre N est gale N * (N-1) . (N-2) 1. Par exemple, la factorielle cle 4 est 4* 3n 2* l, soit 24.La fonction factorielle n'est valide que pour les nombres entiers naturels (positifs).
I I II
I
I
Factorial}JithError
crer et
utiliser
une fonction
aucune
using
t I
System;
nnespace FactorialWithError
// I
t
Uyttathfunctions
collection de fonctions
nathmatiques
public class
i/ I
t
Factorial
/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
do
t
return dFactorial;
l
]
t t^ddI ^t^dd ^rrhli^ PUUr.r-U \-r-d.t'U rwr-d.55.1. {
4t6
components.Dispose ( )
J
l
haee Di(b^<( --urDyvrrri \ ,lian^ain. ). ,/
'
liregion Windovs Form Designer generated code lll kunnary) /// Required method for Designer support - do not nodify I I I rhe contents of thi.s method r+ith the code editor. lll llsunnary)
//
Formt
;
this. ClientSize =
( 292
273)
(summary)
stlthread
static void
r
Main0
.
Annl i eeti on
Rrrn
(new Forml
))
doit commencer par static Main O, qui se trouve ici tout en bas du listing. Voil ce qui nourrit ma conviction que c'est ici qu'il faut commencer. La seule instruction que contient Main O cre un objet Fornl O et le passe une mthode Application.Run O. Je ne suis pas str de ce que fait Run O, mais je souponne fortement que la classe Forml correspond la fentre Forml que j'ai vue dans le Concepteur.
Je sais que le programme
^tK
'(dE,)
O lance I'objet Form sur son propre thread d'excution. Le thread initial s'arrte aussitt que le nouveau Forml est cr. Le thread Forml se poursuit jusqu' ce qu'il soit intentionnellement arrt. Le programme SimpleEditor lui-mme poursuit son excution aussi longtemps que des threads dfinis par I'utilisateur sont actifs.
En fait,
Application.
Run
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
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-
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
=($9,
4.
-K
Un formulaire est une fentre contenant une barre de titre, et optionnellement des barres de dfilement. Dans la terminologie de C#, une fentre n'est rien d'autre qu'un cadre rectangulaire dans lequel vous pouvez placer des images ou du texte. Une fentre n'a pas ncessairement des menus ou des tiquettes, ni mme ces petits boutons Fermer, Rduire et Restaurer.
Gnrez le programme que Windows vient de crer sur la base du modle. Vous pouvez me traiter de paranoiaque, mais je veux tre certain que toutes les erreurs qui pourront apparaltre par la suite seront rellement de mon fait et ne viendront pas de Visual Studio. Sans aucun doute, la solution va se gnrer sans encombre ce stade. L'excution de ce programme ne rvle rien d'autre qu'un formulaire vierge, dot de l'tiquette Forml. Il suffit de cliquer sur le bouton Fermer pour arrter le programme.
Le volet qui occupe la partie droite de I'affichage est la fentre
Proprits. a ne saute peut-tre pas aux yeux, mais son contenu est en relation directe avec le formulaire qui est dans la partie gauche de I'affichage. Par exemple, vous pouvez voir que la proprit Text est Form1. Vous pouvez la modifier pour vous rendre compte de I'effet produit.
5.
L'tiquette Forml contenue dans la barre de titre du formulaire devient Simple Editor.
6.
W
Figure 17.3 Changer la proprit 1'-^xt 0U
:
formulaire
change le nom qui a ppa rat dans sa barre de titre.
47t
t/ t/
Votre chien a mang votre manuscrit. Plus simplement, vous avez oubli cette mthode ou vous n'en connaissiez pas I'existence. Soyez plus attentif la prochaine fois.
Vous avez fait une faute de frappe dans le nom de la mthode ou vous lui avez pass de mauvais arguments.
:
l'Ie
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
'methodNatne' : tous les chemns de code ne retournent pas ncessairement une hleur
Par ce message. C# vous dit que votre mthode a t dclare non-void et que un ou plusieurs chemins d'excution ne retournent rien. Cela peut se produire de I'une des deux manires suivantes :
t/ t/
if
avec un
Plus vraisemblablement, vous avez calcul une valeur et vous ne I'avez jamais retourne.
4|2
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
,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
469
cloche entre Visual Studio et le rpertoire du programme. Fermez la solution, quittez Visual Studio, redmarrez, puis ouvrez nouveau la solution. Si a ne marche pas, je suis sincrement dsol.
Le mot-cl neut est re(us sur 'subclassName. nethodNaffil| car il masque le membre hrte
'
as e c I as s N am e. m eth o dN atn
e'
Avec ce message, C# vous dit que vous avez surcharg une mthode dans une classe de base sans la redfinir par une mthode qui la cache (voyez le Chapitre 13 pour en savoir plus ce sujet). Regardez I'exemple suivant : public class
{ BaseClass
l
nublic class Sub0lass:
{
BaseClass
l
l
public class
{
MyClass
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.
t/
Vous vouliez redfinir la mthode de la classe de base. Dans ce cas, ajoutez le mot-cl new dans la dfinition de SubCias s, comme dans I'exemple suivant :
public class Sub0lass
{
BaseClass
4l 0
.)
Il rn'a fallu une longue et clifficile rflexion (au rnoins un quart cl'heure) pour imaginer un problme qui mette en lumire la puissance de C# sans me faire prendre du poids. Le voici : crer un cliteur simple que nous appellerons Sinp-eEd ir ,r. Il aura les caractristiques suivantes:
t/ t/ t/ ,/ ,/
L'utilisateur peut entrer et effacer du texte (sinon. ce ne serait pas vraiment un diteur). L'utilisateur peut couper et coller du texte, non .serulenrent dans SlnnLeEciit-c,r, mais aussi entre 5.trr ri,r t:t d'autres applicatior-rs, par exemple Word.
I rlrLl a
Sinir,eEclit-c-r supporte les polices en gra.s, en italiqlle ou les deux. L'utili.sateur peut slectionner une taille cle police cle 8 24 points. Ces limites sont arbitraires, mais il s'agit ici de ne pas aller trop loin
en nombre de points.
SimpleEr.litcr ne doit pas vous permettre de quitter sans vous avoir demancl poliment d'enregistrer le fichier que vous venez de moclifier (mais vous restez libre cle quitter san.s enregistrer si c'est bien ce que vous voulez).
Exposer le problme
Chaque fois que vous tes devant un problme rsoudre, vous devez commencer par vous mettre devant le tableau noir et rflchir srieusement aux obstacles franchir. Dans le cas cl'une apltlication Windows, cette tche se divise en trois tapes :
l.
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.
t+67
Par dfaut, un membre d'une classe est';l'l-,,.ate, et une classe est internal. Aussi, nPrivateMember est toujours priv dans I'exemple suivant : class
t MyClass
public void
t
SomeFunction0
YourClass uc = netr YourClassu; I I ceci ne fonctionne pas correcremenr parce que MyClass I I ne peut pas accder au nernbre priv uc,nPrivateMember = 1:
l
]
public class
{
YourClass
int
l
nPrivateMenber
: 0; //
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."
MyClass
nrrhlic yqurru
{
Irt L
tl t
I ceci fonctionne parce que C# ne retourne une valeur // i1 ne psse pas une valeur dans la fonction
I
que dans
Some0therFunction
(out n) ;
int
n)
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.
465
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
i
MyClass
SomeFunctiono
char c1 = ta'; char c2 = 'b'; // ie ne sai s mme ns c nrrp npr.i oourrait vouloir dire ; c'est illicite
ll
char c3 = c1 * c2;
]
*a ra]-son
1
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
cecz fonctionne
463
cf oii
n nlhl
S)
e/
Console.Writeline("Nom de 1'tudiant
Console.WriteLine("Nunro
l
)
s.nTd);
Le problme est ici que I'l7F:rnc tion i ) fait rfrence un membre donne nlct au lieu du vritable membre donne nrD. Vous voyez la ressenlblance, mais C# ne Ia voit pas. Le programmeur a crit nId, mais il n'y a pas de rrld,
et puis c'est tout. Un peu moins populaire, mais galement dans le Top 10, vous avez aussi la possibilit que la variable ait t dclare dans une porte diffrente :
class
t
I
MyClass
int
{
nCount
0;
while (true)
I
I tit
un nonbre
string s = Console.Readline0l
break;
l
I
nSun
1
J
nCount#;
// affiche naintenant 1es rsultats total est r' * nSuur) ; tonsole.Writelinet"ta moyenne est " * nSun / nCount); I I ceci produit un rxcssage d'erreur de gnration Console.Writeline("La valeur finale tait " + s);
Console.Wri.teline("Le
t
)
La dernire liqne de cette fonction est incorrecte. Le problme est qu'une variable est limite la porte dans laquelle elle est dfinie. La variable s n'est pas dfinie en dehors de la boucle whrle ().
40 4
que le programme donne la sortie attendue. Une fois que le programme lu le fichier, il se termine. Si I'utilisateur veut lire un autre fichier, il lui suffit d'excuter nouveau le programme.
Le programme commence par une boucle-;h rle, comme son cousin File\^rrite. Dans cette boucle, il va chercher le nom de fictrier entr par I'utilisateur. Si le nom de fichier est vide, le programme envoie un nlessage d'erreur : \Ious ave-z cntr un iLom de f ici,i er -' l de. Dans le cas cclntraire. le nom de fichier est utilis pour ouwir un objet Fi-Le-S:i e a:. en mode de lecture. L'appel File . Open ( I est ici le rnme que celui utilis clans F i i:::r rte
/
t/
Filel{ode.0pen dit : "Ouvrir le fichier s'il existe, sinon envoyer une exception." L'autre possibilit est Cpenlile-,,,, qui cre un fichier de longueur nulle si celui-ci n'existe pas dj. Personnellement, je n'ai jamais rencontr le besoin de ce mode (qui veut lire r-rn fichier vide ?), mais chacun mne sa barque comme il l'entencl. t/
Le dernier argument indique que je veux lire
partir de ce
F.e'acj,'ir
it...
L'objet FileStream f s rsultant est alors insr dans un objet ST- rcair,lir:ac1er s r qui offre des mthodes pratiques pour accder au fichier texte. Toute cette section d'ouverture de fichier est enchsse dans un bloc
.. r-,,',
lui-mme enchss dans une boucle v;hi1e, insre dans une nigme. Ce bloc try est strictement rserv I'ouverture de fichier. Si une erreur se produit pendant le processus d'ouverture, I'exception est attrape, un message d'erreur est affich, et le programme reprend au dbut de la boucle pour demander nouveau un nom de fichier I'utilisateur. Toutefois, si le processus aboutit un objet nouveau-n St reamReader en bonne sant, la commande break fait sortir de la logique d'ouverture de fichier et fait passer le chemin d'excution du programme la section de lecture.
.r\!G ./ t(?, Y
FileRead et Fiiei^i
rite
Hdesexceptionsdefichier.VouspouVeZinsrertout|eprogrammecle
traitement de fichier dans un mme bloc t i'\,, comme dans Fi 1er'rr: j te, ou bien vous pouvez donner son propre bloc i- r-,. la section d'ouverture de fichier. Cette dernire solution est gnralement la plus facile, et elle permet de gnrer un message d'erreur plus prcis.
Une fois le processus d'ouverture de fichier termin, le programme FileRead lit une ligne de texte dans le fichier en utilisant I'appel
Chapitre 19
(et commentyremdier)
Dans ce chapitre :
'className' ne contient pas cle dfinitiol) pour'memberName'.
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
'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
Il
]-' : ''-.:
//
FileRead
Q"-+^-' uje!rr uy;!r,a!,
irarrg)ydL:
{
f rf Ei\cou
ct:-ie
fl args) LJ
// il
nous
faut un objet
pour
lire
1e
fichier
= ""; // 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
{
Console. Readline
ii 1'utilisateur n'a rien entr ; envoie une erreur ll pour 1ui dire que ce n'est pas satisfaisant if (sFileName,length == 0)
t
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)
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
400
tz
S
Le type d'accs: Un fichier peut tre ouvert pour la lecture, l'criture ou les deux.
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.
,tggler^ Il n'est
pas ncessaire de spcifier I'encodage pour lire un fichier. Srream'r,n/riter inscrit le type d'encodage dans les trois premiers octets Ou fichier. I'ouverture du fichier, ces trois octets sont lus pour dterminer I'encoclage.
Le programme Fi 1e',.rtrite commence alors lire sous forme de chanes les lignes saisies sur la console. Le programme arrte de lire lorsque I'utilisateur entre une ligne blanche, mais jusque-l il continue absorber tout ce qu'on lui donne pour le dverser dans l'objet Stream\,nlriter sw en utilisant la mthode i,ririt elin-. ( )
,
1t$!Qa.
^<r7q :(dw
\/
"n/riteLine
) et Console .\r/ri*reLine o
()
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
Windows
457
sender,
1r (!IsChangeOK0)
t
e.Cancel = true;
]
5. 6.
Gnrez le programme et excutez-le" Entrez du texte et cliquez sur le bouton de fermeture de la fentre.
Vous voyez apparatre le mme message d'avertissement que celui produit par la comrlande Fichier/Quitter.
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.
398
| I
| / fs
FileAccess.Write,
FileAccess.ReadWrite = File.0pen(sFileName,
FileMode. CreateNew,
FileStream
l/
gnre un
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
lerne
1ue
sw.
|Iriteline
0
slnput ) ;
que nous avons cr
l
I
le fichier
;
sw. C1ose
sw
= 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 -m drr rnerfnirp Dar dfaut ^.' rrutr! UU !Pc!LVi! I I JVULS // celui du fiehier
: l.
ctrino
string s : Path.Conbine(sDir,
cDir = flircctnrv
GptCttrrpntDirpetorvO
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
QU S-,'sten.
!i.,'sten.IO
Windows
655
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;
]
1. 2. 3.
fichier
essaie de
faire quelque
chose qui
provoq uera it
aille de p
texle
l-JNi
4.
D.
39 6
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.
:/dw
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
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,
Windows
453
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)
t
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
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. 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
Si quelque chose a t modifi, la
39 {f
,/
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/
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
La dclaration de Class.J comme
E-internalprotected ..
^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.
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.
Windows
451
4.
Faites la mme chose pour I'option de menu Fichier/ouvrir, en utilisant Ie nom de fonction FileOpen.
,tii
FileSave
la proprit
n0m
itit
Fe1ke
Help
.=!-..1
-*filffi
1,,,
:i
trEtul
Click
du
Forml.cslDesign]*
tai!/i!::11:;
sous-menu
:,,,,,
I
fonction, qui
sera invoque chaque fois que I'utilisa-
prrFilCiloql
,:!:l i r!
eFileC,ilrtrl
1fll
. Erp,op,,.trf-
5.
{
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.
39 2
//
I I
1a:nrne classe
C-private ( ) ; c1ass1.C_privateO;
I I elass2,
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 c1ass1
.
E-internalprotected
( (
;
;
class3 . I-internalprotected
return
l
nrrhl i
t
0;
r. voi d C nrivate 0
Console.
// 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
{
I Ia dclaration d'une classe comme interne force toutes I Les mthodes publiques tre galement internes
A_pub1ic
(
public void
0
"CLass3 .A-
Console. l^lriteLine
]
public" ) ;
nrntontod
{
vnid R nrntantpd{)
Console.
Writeline
"C1ass3
D-internal
")
l
-"h1 i. yuurrL .r^i rl inf orna l nrntontod vLveeus\/ ( ) vvru !_rrr
t Console ]
.
Windows
449
(
i.
aff.iche 1e message d'erreur dans 1a fentre de texte e1le-rnne lire 1e fichier\n"; richTextBoxl . Text *= e.Message;
ll
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
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
{
// ll //
succs
if (saveFileDialogl
t
ShowDialog
) == DialogResult,0K)
;
System,I0.Stream strOutput
= saveFileDialogl.0penFile0
390
nanespace Paint
t
public Paj.ntCoior(int nRed, int nGreen, public vcid Pa j,nt 0 | l publ"ic static vcid StaticPaint0 {l
l
int
nBLue)
{l
l
rrqil
T L
y4L
ll
aiorto
o nom
dnns
'l
esfi1r
s on cherche
args)
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);
car
black. Paint 0
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
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.
Windows
447
appelle des filtres. La dernire option de la liste des filtres est toujours *.*, qui signifie tous les fichiers.
iiil,..:
E{hier Edition 'firhaqe fr,ljet Q;,nret pboquer :,!nE6 E,r:ils FeDh{BelF
(i'rnloF.li+;r
- c0mp0un
sant 'lp.-rFiieJialcg
.=j:-:J.*tti&
fornrl.cs IDesign]+
I
. { tr:nrr:
E,lilrn
Frml
et un comp0sant
Sa-re-Filei!:loq
a
pp0re
l'essentiel du
dialogue
Lfrt,osirrts
E 5
q -j[J
oFBnFiletl'rE SiveFiletrl,1
,-., FntL,r'3lah]
1l !
iper,FitrDr.:1r,11 B:xr./E:rlDttrql
E rrlrro,rt4
PfE:s-F6Erers circulir
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.
Filter
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.
Filr,pr du composant
388
Transiariorilibrai,;
1,.,
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.
class Sort
{
public void
SomeFunction
Windows
445
'. J
chose ici ... //if (sText.Leneth ) 0) ll ... le convertit en entier int nFontSize = Int32.Parse(sText)
I l' / ^
ll s'il y a quelque
I / si la valeur est dans 1'tendue valide ... if (nFontSize )= trackBarl.Minimum && nFontSize (=
trackBarl.Maximun)
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,
386
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.
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.
t/
Windows
443
l. ,
3.
Dans le Concepteur de formulaires, slectionnez la TrackBar. Dans la fentre Proprits, slectionnez l'vnement
S
r oI
l.
invoque quand
copie dans
deux
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
4. 5. 6.
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.
38 4
0)
a s
JetOnS Un COup
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.
questions d'hritage, mais le principe est le mme. Par exemple, une mthode
Windows
441
4.
{
e)
nenultenli.Checked =
SetFont ( )
]
;
Chacune de ces fonctions inverse l'tat de I'indicateur correspondant, et invoque SetFcnt () pour modifier la police en consquence.
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
ll
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 :
Forml
()
public
t
//
Required
for
lT.lirL^rr,
"cornponent )
;
382
le
l/
t
Inverse
retourne 1/x
if (nValue0f0bject :=
t
f l
)
0)
0", this)
return 1.0
l l
(double)nValueOf0bject;
public class
I
Class1
try
I
t
//
prend
inverse de
catch(Exception e)
{
e.ToStrins0);
1
J
//
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.
Windows
439
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 p tp.z+- a
Fofmt
;t - . 1l a
',rn.
lt4(g
,le W: rd
v! '
Figure 18.2
Le
mise en
Tala de
poke
[---
forme.
8*24
.9tlfl =(fl,
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.
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----
FontStyle
if
t
fs *
FontStyle.Regular;
(isBolded)
38 0
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.
eption conventionnel
MyException
/i I
private
t
MyClasss myobject;
base(sMsg)
l/
l
public
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
Windows
437
//
l
et voil,
nous avons ce
o;
qu'il
nous faut
return (string)
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. 2.
Dans I'Explorateur de solutions, double-cliquez sur Form1. cs pour afficher le code source C# du projet. Dans Ie code source, insrez les mthodes l,JriteClipboar
l ( ) et
3. 4. 5.
Dans le Concepteur de formulaires, slectionnez I'option de menu aition/Coller. Dans la fentre Proprits, cliquez sur le bouton contenant un clair pour afficher les proprits actives Qes vnements). Slectionnez l'vnement Ci i ck. 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. 7.
Rptez les tapes 3 5 pour les options de menu Couper et Copier. J'ai nomm ces mthodes EditCut et EditCopy. Vous devez ajouter manuellement le contenu de ces mthodes, comme suit :
e)
37 8
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
:
Windows
435
6ena
!bDrr tgnnier
QdilJ
Fetbe
*"'i;.#..iilt'.':,;
F[hPr edtion
::
Formt
f-l L:)
a.
o, [l -; r L:--l /
.... .. el,-irr! i,frit
::iirb
.ii5rb EllsmE
E:
.,,
.r1,}inl 8:r lr:if
I
r
rrd,:I 'n:t'
L
lEenr
Figure 18.1
Mme le
Ia,lle de
p(Jrcdq
o-a-.
D
E Lrrr
F
r,r,::rre,i
Ibleu 5trinq[
tliT:Lfl
c0mposant
TextBox
a
m0deste
!i rllf:i,:: Tii
....
I
r} 11 r{rl) 7
l
l
.t
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).
37 6
Console.
] I
{
L!.I
lJriteline
(e. Message)
I tZ - ,,^;
^,,hli^ f uurrL
IJ ( bIxceptlonlype/
\
;
l
catch (MyException
{ me)
l^Iriteline
(ne. Message)
l
] I I tl - - n'essayez pas d'attraper des exceptions public void f3 (boo1 bExceptionType)
{
f4 (bBxceptionType)
]
envoie des exceptions d'un type ou d'un autre public void f4(boo1 bExceptionType)
t
ll t4'-
if
{
(bExc eptionType
I'objet est envoy avec 1'exception une erreur se produit new MyException("MyException envoye dans f40", thron mc);
I
f40");
I I envoie d'abord une exception gnrique Console.I.,lriteline("Envoie d'abord une exception gnriQue")
new C1ass1
0 . f1 (fa1se)
de mes exceptions
;
Ghapitre 18
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.
..R,:r^
uRttqFg
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.
;ar
i-l# ,
37tl
'I
catch(Exception
t
I
r
e)
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
.)
ll
objet
l"fr;-
pec
i a lExc e pt io n envoy.
=(t
9D'\
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
I I \ /
r
+ rlr LI Y
Some0therFunction
l
catch (Exception
I t me)
I
1 J
I tols
ici
catch (MyException e)
t
,-
// II
]
aucune exception ne
parvient jamais jusqu'ici parce qu'el1e est attrape par une instruction catch plus gnra1e
l Dans cet exemple, I'instruction catch la plus gnrale coupe I'herbe sous le pied de la suivante en interceptant tous les envois.
43 I
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
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.
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.
37 2
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
i
SomeFunction0
try
t
I
I
I I
exemple
SoneOtherFunction0; l
catch (MyException
{
I nais vous avez aussi accs toutes les proprits et // de votre propre classe d'exceptions
MyClass no
I
= ne.MyCustonObject;
MyClass de
s'afficher
1ui-mrie
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.
429
'n
-i
mnl oFrJit,rr
en suivent
fidlement le
redimensionnement.
Taille de p,:fice
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
(summary) Sunrnary
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
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 System.Windows,Forns.l'lenuTtem[] {
this.mainMenul.MenuTtems,AddRange(new
this,menuItenl.
37 0
:(dq9
avz-; \
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.
Le bloc cetciL situ la fin cle
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
E>:,:r1,'r.
ic
et utilise sa
tLapropritExce.:i.ltiil:.i].::.-::]i:E]retclurneunSouS-ensenrblepluslisible. J-^t :
llcf Y
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) :
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:
line
52
Appuyez
reur
fa-
_ Ghapitre
17 :
427
endroits les
composants lorsque le formulaire est
redimensionn n'est sans doute pas ce
Iarlle
'ie
p.rlice
l-_-
2.
et gauche du formulaire.
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.
redimensionnait le formulaire).
4.
r:i,,.r
5.
368
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
CI
// ll II
',^:^-
FactorialException
factorielle qui
indique Factorial
utilisant
1es arguments
lllicites
un objet Exception
usrrr8
t
(.,a+^a.
y s Lerii;
nanespace FactorialException
// II
{
l,tyltatnfunctions
"rooo MyMthFunctions
ll It
t
Tactorial
ngatifs
string s = String.Format(
ngatif
illicite
425
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.
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
3.
=qg,
^dK
4.
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. 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
Taille de
po[ce
l-*
366
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
return
]
MY_ERROR_1;
if (errRtn == SF-ERROR2)
{
return l
I
My_ERROR-2;
appelle SomeOtherFunctions,
;
1it 1'erreur,
Console.l'trriteline("Erreur de type
return l
MY_ERR0R*3;
if
{
return l
MY-ERROR-/*;
t/ t/
Il est trs rptitif. Il oblige le programmeur inventer de nombreuses indications d'erreur et en maltriser I'emploi.
Ghapitre 17
423
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.
Hdp
=!'I-fiSHffi
::
i ?:t?l l-,,' :
.
t et!'l
T
text0oxl
,-l
{n
T
:)';: i'.
: ., , jt :
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'
No lJone
en fait un
ensemble de
SOUS-
TexlA|gn Left B i.urt2rv|ur.t't AcceFtsPtlrr, Fal.e Ac.eFtsTb False llt,Dro Fils Text
Le lexl conteru dn! ce cmtr6l,
ftfltextao*t
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.
364
if (dFactorial =:
t
l'lyMathFunctions.NON-INTEGER-VALUE)
Console
}lriteLine
a.f.iche 1e
rsultat
chaque pssge
)
;
i, MyMathFunctions.Factorial(i)
l
"Appuyez
..
Console.ReadO
de tests. Le (0 est accept parce qu'il premier regarde si la valer-rr passe est ngative donne un rsultat raisonnable). 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 dcimale de I'argument est nulle.
Facto:ia1O, la recherche de I'indicaerreur. Toutefois, des valeurs Comme -I eI -2 n'ont 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 :
42 I
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'
Figure 17.6:
La proprit
permet de
Shortcut
assigner
| lment de menu.
7.
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.
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
8.
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.
362
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)
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
ncessaires :
I I II
I I
{
actatialErrorReturn
cre une fonction fctorielle qui retourne une indication d'erreur quand quelque chose ne va pas
using
System;
nanespace FactorialErrorReturn
// II
MyMathFunctions
4l I
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
Heb
.:rl'
u' e
ffi
'i'
|
'as*':
Boitewtils
Donres
q \
aompNt5
Windows
Flrm . T-"t
*l'splitlr
...
..
. . ....
..
- I endroit est
o l'utilisaeur p0urra
RlchTextBox
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.
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.
360
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
t
/
Factoriall.{ithError
crer et
utiliser
une fonction
using
System;
namespace
Factoriall,IithError
li /
I t
MyMathFunctions
public class
MyMathFunctions
// II
{
Factorial
// 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
{
chaque
fois
do
return dFactorial;
J
4 |7
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.
l.
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
47 5
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
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.
nublic class
Student
47 7
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.
Account
private double balance; private int numChecksProcessed; private CheckBook checkBook; public Account ( )
{
balance
0.0;
numChecksProcessed
0;
checkBook
= 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
Account
orivate
1
CheckBook checkBook
= 0; new CheckBook0;
479
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
i
read O
void writeO;
/i
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
int 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#.
Index
NET
description 3, 5, 7
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
fonction fonction
pas 156
154 145
ajouter des actions 433 des contr6les 422 des tiquettes 424 concevoir la prsentation 4l 1
fonction
147
Arrondir
37
connaltre les
composants 431 construire les menus 419 crer le cadre de
AccessControl 391 Accesseur 250 Addition sur des chalnes. oprateur 202 Aide en cours d'dition 196
plus
195
Assembleur 4 Assignation, oprateur de 56 Assistant Applications 8 Asynchrone (l/O) 396 Automatique, saisie 190 AverageAndDisplay 145 AverageAndDisplayOverloaded 148
AverageStudentGPA 125
formulaire 426
Argument(s) accorder dfinition et
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
AverageWith
CompilerError
146
utilisation
146
par dfaut,
implmenter
passer
149
documentation
196
BankAccount 240,291
BankAccountC ontructors-
AndFunction 266
lndex
String 201
Classif ication 234, 235,
483
classe 136
DOS, passer des arguments
proprits
Conversion
15
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
Console,
CustomException 380
quitter 452
Erreur
codes d'erreur 365
application, crer un
modle de22 classe 173 Const 116 Constante numricue dclarer 50 Decimal 42
limitations
44
vitesse de calcul 44
DecimalBankAccou nt 247
type 50 Constructeur(s)
252 262
comment se fait la
construction
dclarer 388 runir des fichiers source dans 387 utiliser avec using 390
EST-UN 278
et hritage 286
12
quand utiliser 281 tiquette clans une application Windows, ajouT.er 424 vnement 18,434 Exception
classe de. redfinir 380 crer une classe de 371
DisplayRoundedDecimal 149
DisplayXWithNestedloops 95 Distribu (dveloppement) 5 Do... while 84 Documentation commentaire de 195 balise 196 XML, gnrer 200
exemple 368
intercepter et
renvoyer 378 laisser passer 375 utiliser un mcanisme
cle 367
Excutable 4
Index
instructions if imbriques 76
485
Incrmentation, oprateurs
de 57, 58
hrite, surcharger 296 nom complet 182 proprits actives 434 redfinie, Accder 308
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
181
fonction
136
7-K
Java 5
implmenter les
options 440
Mthode 181 abstraite 318
accs 394
complet d'une mthode 182 d'une fonction 296 conventions sur 48 de classe 105 de fichier ,lire 446 de fonction,
surcharger
147
de variable 127
Label425
Langage(s)
c#3,5
d'assemblage 4 de haut niveau 4 Java 5
machine 4
Late binding 306
internal 394 private 393 protected 393 public 393 dfinir 179
diffrente selon la
classe 297
format de sortie
224
tardive 306
Lire les caractres saisis au clavier 210
Null
111
redfinir
298
lndex
Replace 221
487
utiliser 396
String 46,202
classe 201
convertir en un autre
type 212 StringReader 395 StringToCharAccess
2I0
StringWriter
395
185
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
et classe 345 exemple 350 mthodes d'une 349 types structure prdfinis 353 StructureExample 350 Surcharger Voir aussr
Tolnt32 212 Tolnt64 212 ToString 384 TrackBar 424 changer la taille de
Try 367
Type assigner un 65 bool 44 char 45
Signe (variable) 37
const
116
T t
Tableau 116 Voir cussi Array longueur
conversion de 45 conversion de, le cast 51 conversion explicite, le cast 65 conversion implicite 64 de longueur fixe 49 de rfrence, oprateurs
fichiers
diviser un programme
en plusieurs 385
sur
111
d'objets index
124
foreach 127
118
split
215, 223
proprit
251
trier
128
StreamWriter 395
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
erlt?ll .sr^E-I
raurudur.p 9^r{JV
488
entiers divers 35 tendue 42 valuation par 107 float 38 fonction avec ou sans 160
en virgule
int
33
intrinsque 49
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
Void
16i)
string 46 convertir
212
et char, comparais on 47
353
ou par valeur
rgles de
151
TypeUnification 354
UML (Unified Modeling Language) 312 Using, utiliser un espace de nom avec 390
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
WriteBinary
Writeline
395 173
X-Z
XML 196 documentation, gnrer 200 Zro, rfrence 161
format
cle
calcul du
41
Pentium
486
fichier dessus
Obiect (classe) 285 Objet(s) accder , mthodes pour 245 aux membres d'un 107 changer de classe 282
Pad 217
168
ParseSequenceWithSplit
PassByReference 153
2I5
PassByReferenceError I 54
PassByValue 151
Function
177
flottante
41
d'objet
115
vitesse de calcul 42
PEUT_TRE-UTILISCOMME 327 Police, changer de 439
Public 105,239
exemple 240
Polymorphiclnheritance 309
Polymorphisme 305,306 accder une mthode redfinie 308 Porte des variables, rgles
de 90
107
proprit(s)
115
110
g Etl
Quitter, enregistrer avant
de 452
Ramasse-miettes 294 ReadBinary 395
de stocker en mmoire
Prcision 39,41
Presse-papiers 435
Readline
231
212
abstraction
d'incrmentation 57.
logiques 6l ordre d'excution 55
58
c#
238
point
111
simples 54
231
48 4
G# pour
les Nuts
conditions mutuellement
exclusives,
74
contrler,
70
Concepteur de
formulaires
12
switch,
Fonction
97
redimensionner 426 Four micro-ondes 231 Fraction prcision 39 reprsenter 37 FunctionsWithDefaultArguments 149
FactorialWithError 360
Factorielle 360 Factoring 311 Fahrenheit 40
False 44
dfinir
177
Gnrer
11
Goto 100
Gras (mettre en) 439
partir d'une
Proprits
15
168
Rsultats 11 Fermeture (bouton de), implmenter 456 Fichier(s) Iire le nom du 446 rassembler des donnes
dans 394
RTF 418
type valeur
151
rfrence 152,154
des arguments par
valeur
151
un objet 175
crire 449
lire
448
source 4
diviser un programme
en plusieurs 385
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
utilit 274
HidingWithdrawal 299 Hirarchie de classes 321 Hongroise (notation) 49 HTML 196
Writeline
173
FileWrite 397
Final 367
I,
I/O asynchrones 396
If, 70
Formulaire
12
viter le else
75
482
G#
Caractre
non imprimable 46,211 de retour la ligne 47 saisis au clavier, lire 210 variable de type 45
Cast 5l
Bote outils 13 Bool 44 conversion 45 Bord, effets de252 Boucle(s) break et continue 85 briser 85 commandes de 79
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
110
84
quoi sert-elle ? 92
redfinir
380
d'une 210 Compare 204 concatnation 47,221 contrler manuellement la sortie d'un programme 217 convertir en un autre Iype 212
de contrle 224
et structure 345
tendre 323 factoring 3l I faiblement couple 391 fonction membre 136 statique d'une,
dfinir
177 391
fortement couple
Format 224
historique
202
108
*47
crer une application Windows avec 7
d'addition
Parl217
Replace 221
membre(s)
105
description 3, 5,
Cacher l/oir redfinir Cadre de travail d'une application Windows, crer 413
Calculatelnterest 72 CalculatelnterestTable 80, 137 CalculatelnterestTableMoreForgiving 86
Trim2IT
utiliser srvitch avec
Char 45
209
donne 136 statiques d'une 115 ne pouvant tre instancie que localement 270 nom, majuscules et minuscules 105
Classl
164
3
object
i
7
285
Classe 104
CalculatelnterestTablewithFunctions 138
abstraite
CalculatelnterestWithEmbeddedTest 76
sceller 325
sous-class e 234
objet
282
47I
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
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.
47 6
sName)
this.
]
IL
sName
sName;
public string
I
I
get
0
nom
;
rtrrrn
class {yClass
t
cirrdonf cot/<lr{g1t.get0
've\/ 1 J
+ " Kringle");
L'LbL' t
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
i
System;
public class
Student
-t\T^*^. sr\ame;
public string
t
Nane
class
t
I
MvClass
student.Nane = student.Nane
] ]
"Kringle"
47 4
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.
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. 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. Les objets du tas sont allous en fonction des ncessits. Ces objets sont prop.res un thread d'excution particulier.
t/
tz
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
public class
{
I'lyClass
n)
s
public string
{
ConvertPositiveNurnbers numbers
(int
n)
I I oy positive if(n)0) t
string s
tLulll ,
n.ToString0;
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.
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
new
t
I
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
t
BaseClass
l
]
BaseClass
l
]
alllllfa
=\d\y / \/
paS Une
468
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. 2.
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#).
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
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
Une autre approche consisterait s'assurer que toutes les constantes sont de mme type :
class
I MyClass
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.
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
t
MyClass
public void
{
SomeFunction0
;
I ceci ne fonctionne pas correctement prce // ne peut pas accder au membre priv uc.nPrivateMember = 1;
I )
que MyClass
public class
{
YourClass nPrivateMember
private int
J
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
en
"/'
Ce message indique gnralement que vous essayez d'utiliser deux types de variable ctiffrents dans la mme expression. Par exemple :
int naa = lfl
.
Certaines conversions ne sont pas aussi videntes, comme dans I'exemple suivant :
class
t
b
1
MyClass
float
FloatTimes2
(float
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
46 2
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).
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
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
Dans cette
1t V
uel livre Pour
Les nuls
partil...
serait complet sans notre partie des dix ? C# est trs dou pour traclitionnelle trouver cles erreurs dans vos programmes lorsque VouS essayez cle les gnrer. Vous l'avez SaI-lS doute remarqu. Mais les ntessages d'erreur qu'il gnre peuvent tre assez obscurs. Vous I'avez sans doute remarqu aussi. Le Chapitre 19 passe en revue les dix messages d'erreur de gnrations 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.
456
l. 2. 3.
fentre du programme.
Dans la fentre Proprits, slectionnez l'vnement Closing.
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
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
conrlor
return:
l
/
OpenAndReadFile
// \a
]
1'tat
non modifi
e)
de modificati.on
bTextChanged
= false;
l
]
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
Application.Exit 0
l
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
Figure 18.7
diverses tailles
divers styles
Taille de police
[E*
fichier
RTF
I
:
Dans la fentre Proprits, slectionnez l'vnement (liick. et entrez le nom de fonction FileExit.
pileExir i. j
que
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
if
{
(str0utput !=
Systeur.
nu11)
I0. Streaml'lriter stri'ltr = new Systern. I0. Streaml,lriter (strOutput) st r!{tr . I/Jrite ( richTextBoxl . Rtf) ; strl,Jtr. Close 0 ;
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.
Pour implmenter les options de menu Fichier/Ouvrir et Fichier/Enregistrer, suivez ces tapes :
l.
Entrez les mthodes CpeirAndReaolile O et SarreSpecif dcrites dans les sections prcdentes.
iedFile
()
,
3.
menu Fichier/Enregistrer.
Dans la fentre Proprits, slectionnez l'vnement Click, puis entrez le nom de fonction FileSave, comme le montre la Figure 18.6.
448
0penFileliaiog
ll |it
I
dans 1a RichTextBox
bool bReturnValue
false:
try
{
owre le fichier
System,I0.Stream
strlnput
nu11)
openFileDialogl.0penFile0
if (strlnput
{
!=
strRdr
(strlnput)
;
I 7it tout
1e contenu du fichier
;
string
sContents = strRdr.ReadoEnd0
/l nous avons modifi la fentre de texte bReturnValue = true; // assurons-nous de fermer 1e fichier pour que d'autres /l
l l
]
catch (Exception e)
446
4. 5. 6.
Gnrez le programme et excutez-le. Entrez du texte et slectionnez-le. 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.
SiinpleEditor ne serait
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
- dEUX CES
la
CgftineS paftieS
er, t'-,,',rnt
E,;r
,* ;"i
::r:rer
$ fA nd iS
l'irrdi{,lu
lr,t,:,1.:
lextBrx
est mise
jour en fonction de
la
r
I
lE
TrackBar.
Changer de
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. 2.
3.
/l invoque quand 1'utilisateur tape quelque chose dans la /i utilis pour dfinir la taille de la police
{
I t:-t
1e contenu de 1a TextBox
442
il
//
]
COde
la police 1e style et
;
tiet.E-ont t I
6. 7. 8.
Gnrez le programme et excutez-le. Entrez du texte, et slectionnez-le avec la souris. 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.
-lal-xl
FBrmt
?
SinpleEditor
permet maintenant de saisir du texte en gras
et en
ita liq
u
Taille de
policr
l--
e.
composants diffrents.
Changer de
440
fr l:
FontStyle.Bold;
if
]
{isTtalics)
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.
SZ' .-\
(
.j99q
v-/
,-
,, L'expression richTextBoxl . Font f ont; modifie la police de tout le texte qui se trouve dans la zone de texte.
lnplmenter
l. Dans les menus, slectionnez Format/Gras. 2. Dans la fentre Proprits, slectionnez l'vnement
entrez le nom FormatBold.
Click, et
3.
438
e)
j.teC1 ipboard
rtfText)
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. 9.
10.
I
Gnrez nouveau SimpleEditor. Vous avez maintenant un programme qui peut vraiment couper, copier et coller. Excutez le programme en slectionnant Dboguer/Dmarrer. 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.
l.
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
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)
{
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
if
t
rcupre
le
contenu du Presse-papiers
;
(data == nu11)
return nul1;
]
I
rcupres,
vrifie qu'el1es
sruv/ t ;
sont
if
{
J
(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 rurvv/
return nul1;
l
43 4
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
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.
'(dg,
^tK
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
ll II
tl It I
trackBa
( (System. Windows,
.
Forns.AnchorStyles.lottom
r \
Left
)
r h'
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 ;
l /lendregion
]
I
42) ;
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 ^s$c ^.. J'aurais quelque chose de plus parlant, dans la fentre Proprits pour mais c'tait sans importance. Pour obtenir 7X des programmes de grancle taille, la dfinition de vos propres noms peut ttg|| V rendre le code qui en rsulte beaucoup plus facile lire. La mthode
Initial.i
zoe nmnnnt I
chaque type.
428 w
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.
Ancrage
Haut, bas, gauche, droite
Bas Bas
fentre d ancrage
p0ur
(blancl
I
ancrage
dans chaque
direction.
Taille de
police
[-
SinpleEdit,:r.
426
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
g/
P\
e,
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
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.
422
Raccourci
Ctrl0
Fichier/0uvrir
Fichier/0uitter
dition/Couper
ditioni
Fichier/Enregistrer CtrlS
Ctrl0
CtrlX CtrlC CtrlV CtrlB
Copier ras
dition/Coller
Format/G
Format/ltalique
Ctrll
?F1
Ajouter
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
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
Uelp
iffi
' Forml,ci [Derig"]"
|'
tr-
eLei1
'
.-s
'-$."1'l
^l -.l
I
bite
out'l.j
oonnees connosent
Wind6$rs Forft,s
I I
Potnte'rt
ll(1a111lqL1U
VOUS
I
:J
I i di l'
l'4nMeru
r".t**..
Rdio&tton 6roupBo.
principal et celle du
s0us-menu.
oatecrid
qtrcul
Fres-ppier5 Gnrl
*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.
ici droite du menu options : Couper, Copier, et Coller. Fichier, et entrez dition et ses
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.
4|8
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),
barbare et capricieux. oubliez toute apprhension, cette nouvelle collection est rellement faite oour vous
!
vous voici confront un micro-ordinateur - prus par ncessit que par got, avouons-le -, sans savoir par quel bout prendre cet instrument
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#.
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
Au vu de ce symbole,
si
-'ii
21,90
b'
Lfr
Cette icne signale une
n ,,j p-( J
"'i
f-\
z
nJ
FU
tJ
(n
retenir ceci.
(143,6s
F)
65 3303 B tsBN-2-84427-2s9
llungryMinds-
First
www.efirst.com
,llilIxilrililill[|iltlll
Finst