Académique Documents
Professionnel Documents
Culture Documents
0
et de C 2.0
patrick smacchia
ditions OREILLY
18 rue Sguier
75006 PARIS
http://www.oreilly.fr
Toute reprsentation ou reproduction, intgrale ou partielle, faite sans le consentement de lauteur, de ses ayants droit, ou ayants cause, est illicite (loi du 11 mars 1957, alina 1er de larticle 40).
Cette reprsentation ou reproduction, par quelque procd que ce soit, constituerait une contrefaon sanctionne par les articles 425 et suivants du Code pnal. La loi du 11 mars 1957 autorise
uniquement, aux termes des alinas 2 et 3 de larticle 41, les copies ou reproductions strictement rserves lusage priv du copiste et non destines une utilisation collective dune part et, dautre
part, les analyses et les courtes citations dans un but dexemple et dillustration.
propos de ce livre . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
Lorganisation de ce livre . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
Support . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
Remerciements . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
Historique . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
10
15
15
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
17
20
25
28
34
Introduction au langage IL . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
41
49
49
50
54
Fichiers de configuration . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
58
vi
63
66
69
71
73
75
84
85
87
94
102
103
108
. . . . . . . . . . . . . . . . .
111
. . . . . . . . . . . . . . . . . . . . . . . . .
116
Facilits fournies par le CLR pour rendre votre code plus fiable . . . . . . . . . . .
125
CLI et CLS . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
129
87
133
Introduction . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
133
Les processus . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
134
Les threads . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
136
143
145
147
153
158
160
167
Timers . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
169
171
176
Contexte dexcution . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
180
La gestion de la scurit
vii
185
185
187
CAS : Accorder des permissions en fonction des preuves avec les stratgies de scurit . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
193
198
199
203
205
205
206
Support .NET pour les contrles des accs aux ressources Windows . . . . . . . . .
211
216
219
221
225
230
233
Le mcanisme de rflexion . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
233
238
Les attributs . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
248
255
265
Le mcanisme P/Invoke . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
265
272
277
278
288
Introduction COM+ . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
295
296
299
viii
309
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
309
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
312
Le prprocesseur . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
313
Le compilateur csc.exe . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
317
Les alias . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
320
323
Les identificateurs
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
326
327
La mthode Main() . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
334
10 Le systme de types
Stockage des objets en mmoire
337
. . . . . . . . . . . . . . . . . . . . . . . . . . . .
337
339
342
La classe System.Object . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
344
345
347
Boxing et UnBoxing . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
350
353
358
Les structures . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
364
Les numrations . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
366
370
378
385
392
395
395
Notions et vocabulaire . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
395
396
Les champs . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
397
Les mthodes . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
400
Les proprits . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
407
ix
Les indexeurs . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
409
Les vnements . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
411
416
417
Le mot-cl this . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
420
421
423
430
433
443
443
Lhritage dimplmentation . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
445
. . . . . . . . . . . . . . . . . . . . . . . .
448
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
452
Les interfaces . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
456
462
Les oprateurs is et as . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
464
466
Labstraction
13 La gnricit
467
471
474
477
481
484
Lhritage et la gnricit . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
486
487
491
493
499
467
501
501
503
509
511
515
517
519
522
522
524
. . . . . . . . . .
536
. . . . . . . . . .
539
. . . . . . . . . .
542
. . . . . . . . . .
Exemples avancs de lutilisation des itrateurs de C 2 . . . . . . . . . . . . . . . .
548
15 Collections
529
552
563
563
Les tableaux
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
565
Les squences . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
575
Les dictionnaires . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
582
587
591
595
16 Bibliothques de classes
Fonctions mathmatiques . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
597
597
600
607
613
Le dbogage . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
617
Les traces . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
620
625
La console . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
629
xi
633
633
636
641
649
651
654
656
657
660
Support des protocoles SSL, NTLM et Kerberos de scurisation des donnes changes . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
660
665
665
668
675
679
682
688
695
La bibliothque GDI+ . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
695
19 ADO.NET 2.0
705
705
Introduction ADO.NET . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
707
712
720
723
DataSet typs . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
731
735
736
738
xii
20 Les transactions
741
741
Le framework System.Transactions . . . . . . . . . . . . . . . . . . . . . . . . . . .
746
752
755
21 XML
759
Introduction XML . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
759
762
765
766
769
771
774
775
779
783
22 .NET Remoting
785
Introduction . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
785
787
790
La classe ObjectHandle . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
792
793
795
799
802
805
808
815
816
Proxys et messages . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
817
Canaux (channels) . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
830
Contexte .NET . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
842
Rcapitulatif . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
857
xiii
23 ASP.NET 2.0
859
Introduction . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
859
861
866
871
873
883
888
Pipeline HTTP . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
891
897
902
903
905
908
Contrles utilisateurs
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
912
918
Sources de donnes . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
929
935
Master pages . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
947
953
954
Scurit . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
957
Personnalisation . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
966
970
WebParts . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
973
987
Introduction . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
987
. . . . . . . . . . . . . . . . . . . . . . .
991
993
994
998
998
1000
1003
xiv
1008
1011
1013
1015
1021
1031
D Les outils
1033
Index
1035
Avant-propos
propos de ce livre
La documentation ocielle de Microsoft sur .NET est trs vaste et dcrit en dtail chaque
membre de chaque type du framework .NET. Elle contient aussi de nombreux articles concernant la description ou lutilisation de telle ou telle partie de .NET. En tant que dveloppeur,
je sais combien lutilisation de cette documentation est fondamentale lorsque lon dveloppe
avec les technologies Microsoft. Cependant, de part le volume de cette documentation, il est
assez dicile dacqurir une vision globale des possibilits de .NET. En outre, daprs ma
propre exprience, les nouvelles ides sacquirent mieux partir dun livre. Certes, on pourrait
imprimer les dizaines de milliers de pages des MSDN, mais vous auriez du mal les transporter
pour les lire au calme, dans un jardin ou sur votre canap.
Ce livre a donc t conu dans loptique dtre utilis conjointement avec les MSDN. Il nest pas
question ici dnumrer les dizaines de milliers de membres des milliers de types .NET mais
plutt dexpliquer et dillustrer avec des exemples concrets les multiples facettes du langage C ,
de la plateforme .NET ainsi que de son framework. Jespre quil rpondra aux problmatiques
que vous rencontrerez et quil vous accompagnera hors des sentiers battus dans votre dcouverte
de la technologie .NET.
Lorganisation de ce livre
Partie I : Larchitecture de la plateforme .NET
La premire partie dcrit larchitecture sous-jacente la plateforme .NET. Cest dans cette partie
que vous trouverez les rponses aux questions du type :
Quels sont les liens entre lexcution des applications .NET et le systme dexploitation sousjacent ?
Quelle est la structure des fichiers produits par la compilation de mon programme ?
Comment puis-je tirer parti de tout cela pour amliorer la qualit et les performances de
mes applications ?
Comment puis-je exploiter du code dj dvelopp sous Windows partir de mes applications .NET ?
Les collections.
Les classes de base classiques type calculs mathmatiques, dates et dures, rpertoires et
fichiers, traces et dbogage, expressions rgulires, console.
Remarques
Ce plan de prsentation de la technologie .NET permet davoir une vision globale. Nanmoins il
est bien vident quune technologie aussi vaste comporte de multiples facettes qui transcendent
ce dcoupage. Par exemple, nous avons choisi de placer la gestion de la synchronisation des accs
aux ressources dans larchitecture de la plateforme .NET du fait quelle se base sur les notions
sous-jacentes de threads et de processus. Cependant, en tant quensemble de classes utilisables
dans vos applications, la synchronisation fait aussi partie des classes de base du framework .NET.
En outre le langage C comporte des mots-cls spcialiss pour simplifier lutilisation de ces
classes. Enfin .NET Remoting prsente des techniques qui permettent une approche volue de
la synchronisation.
Cet ouvrage contient donc de nombreuses rfrences internes qui jespre, faciliteront vos recherches sur les dirents sujets exposs.
Support
considre comme la plus ardue mais aussi comme la plus fondamentale (et mon sens comme
la plus passionnante). Il nest pas possible de dvelopper correctement avec la technologie .NET
sans tenir compte des services de la plateforme sous-jacente.
Le lecteur dbutant pourra commencer par lapprentissage du langage C et des technologies
de dveloppement objet, tout en dcouvrant petit petit les possibilits de .NET. .NET est un
sujet trs vaste, qui a plus que doubl en volume avec la version 2.0. Aussi, nous nous sommes
eorc de rester prcis et concis.
Le lecteur expriment dans dautres technologies devrait pouvoir bnficier des explications
concernant les nombreuses possibilits rellement innovantes oertes par .NET.
Le lecteur expriment avec .NET 1.x se servira de lAnnexe B qui rfrence toutes les nouveauts apportes par .NET 2.0 prsentes dans le prsent ouvrage.
Support
Le prsent ouvrage est support sur mon site : http://www.smacchia.com
Vous pouvez y tlcharger les exemples de code fournis dans cet ouvrage. Nous croyons que bien
souvent un exemple vaut mieux quun long discours. Le livre contient 648 exemples dont 523
listings C et 65 pages ASP.NET 2.0.
Vous pouvez galement nous adresser vos remarques sur cet ouvrage en crivant aux ditions
OReilly :
18 rue Sguier, 75006 Paris
Email : info@editions-oreilly.fr
Remerciements
Je souhaite avant tout remercier mon amie Eli Ane pour son soutien qui ma tellement apport
durant la rdaction de cet ouvrage. Jai aussi beaucoup apprci le soutien de Francis, France,
Michel, Christine, Mathieu, Julien, Andre, Patrick, Marie-Laure et Philippe.
Merci Xavier Cazin des Editions OReilly pour son aide et son professionnalisme.
Mes remerciements vont aussi aux relecteurs et amis qui mont apports chacun de prcieux
conseils :
Alain Metge, 18 ans dexprience. Responsable de la cellule architecture logicielle aux autoroutes du Sud de la France
Dr. Bertrand Le Roy, 8 ans dexprience, participe depuis 3 ans la conception et au dveloppement de la technologie ASP.NET Microsoft Corporation.
Bruno Boucard, 18 ans dexprience, architecte/formateur la Socit Gnrale depuis 8 ans.
Microsoft Informed Architect.
Frdric De Lne Mirouze (alias Amthyste), spcialiste en dveloppement web, 20 ans dexprience, collabore notamment avec ELF, Glaxo, Nortel, Usinor. MCAD.NET.
Jean-Baptiste Evain, 3 ans dexprience, spcialiste du Common Language Infrastructure,
contributeur aux projets Mono et AspectDNG.
1
Aborder la plateforme .NET
Un ensemble de spcifications
La plateforme .NET se base sur de nombreuses spcifications, certaines maintenues par dautres
organismes que Microsoft. Ces spcifications dfinissent des nouveaux langages, comme le lan-
gage C et le langage IL ou des protocoles dchange de donnes, comme le format SOAP. Plusieurs autres initiatives dimplmentations de ces spcifications sont en cours de ralisation.
terme, .NET devrait donc tre disponible sur plusieurs systmes dexploitation et ne se cantonnera pas quau monde Microsoft. .NET est donc un nouvelle re dans le monde du dveloppement logiciel, comparable lre C, lre C++ et lre Java. Il est intressant de remarquer que ce
phnomne semble se reproduire priodiquement, approximativement tous les 7 ans. chaque
nouvelle re, la productivit des dveloppeurs augmente grce lintroduction de nouvelles
ides et les applications sont plus conviviales et traitent plus de donnes, notamment grce
la puissance croissante du hardware. La consquence est que lindustrie adopte ces nouvelles
technologies pour dvelopper des logiciels de meilleure qualit tout en rduisant les cots.
Un ensemble de classes de base utilisables partir de programmes dvelopps dans ces langages. On les dsigne parfois sous le terme de BCL (Base Class Library). Cest ce que nous
appellerons framework .NET tout au long de cet ouvrage.
Une couche logicielle respectant une spcification nomme CLI (Common Langage Infrastructure). Elle est responsable de lexcution des applications .NET. Cette couche logicielle
ne connat quun langage nomm langage IL (Intermediate Langage). Cette couche logicielle
est notamment responsable durant lexcution dune application, de la compilation du
code IL en langage machine. En consquence les langages supports par .NET doivent chacun disposer dun compilateur permettant de produire du code IL. Limplmentation Microsoft de la spcification CLI est nomme CLR (Common Langage Runtime).
Paralllement ces trois parties, il existe de nombreux outils facilitant le dveloppement dapplications avec .NET. On peut citer Visual Studio qui est un IDE (Integrated Development Environment, ou environnement de dveloppement intgr en franais) permettant de travailler notamment avec les langages C VB.NET et C++/CLI. La liste de ces outils est disponible dans larticle
.NET Framework Tools des MSDN. La plupart de ces outils sont dcrits dans cet ouvrage et
sont lists dans lAnnexe C.
Le dcoupage du prsent ouvrage se base principalement sur ces trois parties :
Historique
Le pass
Ds 1998, lquipe en charge du dveloppement du produit MTS (Microsoft Transaction Server)
souhaitait dvelopper un nouveau produit pour pallier les problmes de la technologie COM.
Ces problmes concernaient principalement le trop fort couplage entre COM et le systme sousjacent ainsi que la dicult dutilisation de cette technologie, notamment au niveau du dploiement et de la maintenance.
Historique
Outils
Implmentation du CLI
(Common Language Infrastructure)
CLR (Common Language Runtime)
Le prsent
Fin 2005, Microsoft publie la version 2.0 de .NET qui fait lobjet de cet ouvrage. Le nombre de
types de base a plus que doubl couvrant maintenant de nombreux aspects initialement omis
par les versions 1.x. Des amliorations, des volutions et des optimisations sont apparues tant
au niveau de la machine virtuelle en charge dexcuter les applications .NET quau niveau des
langages. Les outils de dveloppement et notamment loutil Visual Studio sont beaucoup plus
complets et conviviaux. Dailleurs, le sentiment gnral est que la qualit et lintgration des
outils est maintenant un lment prpondrant dune plateforme de dveloppement. La liste
des nouveauts couvertes dans le prsent ouvrage fait lobjet de lAnnexe B.
Paralllement, on assiste au dbut de la concrtisation de deux mthodologies dj bien intgrs
dans les autres plateformes de dveloppement : leXtreme Programming (ou XP ne pas confondre
avec Windows XP) et le dveloppement partir de modles.
LXP consiste rationaliser les mthodologies utilises pour dvelopper un systme dinformation en coordonnant au mieux les activits de tous les acteurs impliqus. Lide est de pouvoir
faire face aux direntes volutions et imprvus qui surviennent inexorablement dans le cahier
des charges. Pour cela, on parle aussi parfois de mthode agile. Lagilit dcoule dun certain
nombre de contraintes. Il faut avant tout tre lcoute du client en lui fournissant souvent
et rgulirement une version testable. Il faut aussi faciliter la communication et le partage des
connaissances entre les membres dune quipe grce des outils polyvalents disponibles en plusieurs versions, chacune adapte une fonction prcise. Le facteur humain est central dans lXP.
Dautres principes sont mis en uvre tels que la conception dune batterie de tests automatiques
excute rgulirement afin de signaler au plus tt les rgressions dues aux volutions. Cette
batterie est en gnral excute aprs une compilation complte de lapplication partir des
derniers sources. Le principe de daily build veut quune telle compilation soit eectue quotidiennement, en gnral pendant la nuit. Toutes ces ides sont maintenant faciles mettre en
uvre grce aux nouvelles extensions Team System proposes par Visual Studio 2005.
Le dveloppement partir de modles consiste gnrer automatiquement le code source
dune application directement partir dun modle. Ce modle est exprim en un langage
de haut niveau, spcialement adapt aux besoins fonctionnels de lapplication et donc trs
expressif. On parle de DSL (Domain Specific Language). Lavantage de cette approche est de
permettre lquipe de travailler sur des sources proches des spcifications, rduisant ainsi
les cycles dvolution et la complexit du code. Visual Studio 2005 propose des extensions
spcialises dans la conception et dans lexploitation des DSLs. Ces extensions proposent aussi
la visualisation de votre code source C ou VB.NET sous la forme de diagrammes comparables
ceux fournis par UML.
Le futur
Microsoft publiera Windows Vista durant lanne 2006. Cela marquera un tournant dcisif pour la
plateforme .NET puisque pour la premire fois, lenvironnement dexcution .NET sera fourni
de base avec un systme dexploitation. De nombreux nouveaux types .NET seront prsents
par Windows Vista pour permettre daccder aux fonctionnalits de ce systme dexploitation directement partir de votre code .NET. On peut citer notamment le nouveau framework de dveloppement dapplications graphiques WPF (Windows Presentation Framework) et le nouveau framework de dveloppement dapplication distribue WCF (Windows Communication Framework)
prsent brivement en page 1013.
Plus tard, en 2007 voire 2008, Microsoft publiera .NET 3.0 (nom de code Orcas). Cette version
sera surtout axe sur une intgration pousse des technologies introduites par Windows Vista
dans le framework et Visual Studio. Pour linstant, seule lquipe C commence faire part de ses
travaux de recherches en ce qui concerne la version 3.0 du langage. Ils se focalisent sur un framework dextension du langage et travaillent notamment sur une extension spcialise pour la
rdaction de requtes sur un ensemble de donnes quelconques (objets, relationnelles ou XML).
Des expressions lambda, qui sont un peu dans le mme esprit que les mthodes anonymes de
C 2.0 mais en plus pratiques, viendront sintgrer dans ces requtes. Dautres nouveauts sont
prvues telles que les types anonymes, le typage implicite des variables et des tableaux ainsi
quune syntaxe ecace dinitialisation des objets et des tableaux.
Trois quatre annes plus tard, une version 4.0 de .NET sera publie (nom de code Hawaii) mais
aucune information nest disponible pour linstant.
Le consortium W3C
Le 9 mai 2000, Microsoft et 10 autres entreprises dont IBM, Hewlett Packard, Compaq et SAP ont
propos au consortium W3C de maintenir le standard SOAP. Le standard SOAP dfinit un format de message bas sur XML. Les services web peuvent communiquer au moyen de messages
au format SOAP. Lide de la standardisation de ce format est de rendre les services web compltement indpendants dune entreprise ou dune plateforme. Le format SOAP est dcrit page
1000. Pour plus dinformations veuillez consulter la page http://www.w3.org/TR/SOAP.
Depuis, un certain nombre de spcifications visant tendre les fonctionnalits des services web
ont t soumis au W3C. Certaines sont en cours dimplmentation et certaines sont encore en
cours de validation (voir page 989 et 1011).
Le projet Mono
Le 9 juillet 2001, lentreprise Ximian, fonde par Miguel de Icaza, a annonc quelle dveloppait
une implmentation open source de .NET. La raison est que ses ingnieurs estiment que .NET
reprsente la meilleure technologie de dveloppement logiciel du moment. Le nom de ce projet
est Mono.
la mi 2003, lentreprise Novell rachte Ximian rcuprant ainsi Mono. Le 30 juin 2004, la version 1.0 du projet est publie. Le projet Mono devrait tre rapidement disponible en version
.NET 2.0 et C 2.0.
Le projet Mono comprend entre autres, un compilateur C (distribu sous licence GPL General Public Licence), limplmentation dune bonne partie des bibliothques .NET (distribues
sous licence MIT/X11) ainsi quune machine virtuelle qui implmente le CLI (distribue sous
licence LGPL Lesser GPL). Tout ceci est compilable sur Windows, sur Linux et sur plusieurs autres
systmes dexploitation type UNIX tels que Mac OS X. La home page du projet est http://www.
mono-project.com.
10
Malgr lombre que peut potentiellement faire le projet Mono la version commerciale de
.NET de Microsoft, ce dernier nest pas forcment contre cette initiative. John Montgomery, responsable Microsoft de .NET a dit : ...The fact that Ximian is doing this work is great. Its a validation
of the work weve done, and it validates our standards activities. Also, it has caused a lot of eyeballs in
the open source community to be directed to .Net, which we appreciate... . Le gant du logiciel nest
donc pas mcontent que le monde de lopen source ait une opportunit dutiliser .NET. Cela
reprsente autant de clients potentiels pour les produits dvelopps autour de .NET.
Sites franais
Voici des sites qui en plus de contenir des articles parmi les plus intressants disponible sur le
web, sont entirement en franais :
http://www.dotnetguru.org
http://www.microsoft.com/france/msdn
http://forums.microsoft.com/msdn/
11
http://fr.gotdotnet.com
http://www.sharptoolbox.com/
http://www.dotnet-fr.org
http://www.c2i.fr
http://www.csharpfr.com
http://www.dotnet-news.com/
http://www.labo-dotnet.com/
http://www.techheadbrothers.com
http://www.blabladotnet.com
http://www.programmationworld.com
http://www.essisharp.ht.st
Prcisons que le site dotnetguru (DNG) sort du rang grce ses nombreux articles de qualit et
grce aux entres pertinentes des blogs de ses auteurs.
Newsgroup en franais
Sur le serveur msnews.microsoft.com :
microsoft.public.fr.dotnet
microsoft.public.fr.dotnet.adonet
microsoft.public.fr.dotnet.aspnet
microsoft.public.fr.dotnet.csharp
Sites anglais
Voici dautres sites en anglais :
http://www.msdn.microsoft.com
http://www.gotdotnet.com
http://msdn.microsoft.com/msdnmag/
http://www.theserverside.net
http://www.dotnet247.com
http://www.15seconds.com
http://www.codeproject.com/
http://www.eggheadcafe.com/
http://www.devx.com/
http://channel9.msdn.com/
http://dotnet.sys-con.com/
http://dotnet.oreilly.com http://www.ondotnet.com/
http://www.dotmugs.ch/
12
http://www.asp.net/
http://www.ondotnet.com
http://dotnetjunkies.com/
http://www.codeguru.com
http://www.c-sharpcorner.com http://www.csharp-corner.com
http://www.devhood.com
http://www.developer.com
http://www.4guysfromrolla.com (ASP.NET)
La section download du site http://www.idesign.net
Newsgroup en anglais
Sur le serveur msnews.microsoft.com :
microsoft.public.dotnet.framework
microsoft.public.dotnet.framework.adonet
microsoft.public.dotnet.framework.aspnet
microsoft.public.dotnet.framework.clr
microsoft.public.dotnet.framework.performance
microsoft.public.dotnet.framework.remoting
microsoft.public.dotnet.framework.sdk
microsoft.public.dotnet.framework.webservices
microsoft.public.dotnet.general
microsoft.public.dotnet.languages.csharp
Blogs
Enfin, voici quelques blogs forte valeur ajoute sur le dveloppement logiciel orient sur les
technologies .NET :
BCL Team http://blogs.msdn.com/bclteam/
Bart De Smet (Divers) http://blogs.bartdesmet.net/bart/
Benjamin Mitchell (Web services, eXtreme Programming) http://benjaminm.net/
Brad Abrams (BCL, design) http://blogs.msdn.com/brada/default.aspx
Chris Brumme (CLR) http://blogs.msdn.com/cbrumme/
Chris Sells (Windows Form, divers) http://www.sellsbrothers.com/
Clemens Vaster (SOA, design, divers) http://staff.newtelligence.net/clemensv/
David M. Kean (FxCop, Windows Installer, divers) http://davidkean.net/
Dino Esposito (ASP.NET) http://weblogs.asp.net/despos/
Don Box (WCF, divers) http://www.pluralsight.com/blogs/dbox/default.aspx
13
2
Assemblages, modules, langage IL
Les assemblages sont les quivalents .NET des fichiers .exe et .dll de Windows. Ce sont donc les
composants de la plateforme .NET.
Dans le cas dun assemblage plusieurs modules, cest toujours ce module qui est charg
en premier par le CLR. Le module principal dun assemblage multi modules rfrence les
autres modules. Ainsi, lutilisateur dun assemblage na besoin de connatre que le module
principal.
Le module principal est un fichier dextension .exe ou .dll selon que son assemblage est un
excutable ou une bibliothque de types. Un module qui nest pas le module principal est un
fichier dextension .netmodule.
16
Fichiers de ressource
En plus du code .NET compil , un module peut physiquement contenir dautres types de ressources telles que des images bitmap ou des documents XML. De telles ressources peuvent aussi
tre contenues dans leur fichier dorigine (par exemple dextension .jpg ou .xml) et rfrences
par le module principal. Dans ce cas on dit que ces fichiers rfrencs sont des fichiers de ressources
de lassemblage. Dans ce chapitre, nous verrons que lutilisation de ressources constitue une
technique ecace pour globaliser une application.
1..*
1..*
Fichier
module
1..*
1..*
Assemblage
Type .NET
Ressource
Fichier
ressource
Ladage profr il y a trois dcennies qui disait quun logiciel passe 80% du temps dans 20%
du code est toujours dactualit. Si lon isole la grosse partie du code peu utilise dans des
modules spars, ces modules ne seront peut-tre jamais utiliss tous ensembles. Donc la
plupart du temps on conomisera les ressources ncessaires au chargement total de lassemblage dans le processus. Ces ressources sont la mmoire vive, les accs au disque dur, mais
aussi la bande passante des rseaux si lassemblage est stock sur une machine distante.
Un fichier de ressource ne sera rellement charg que lorsque le programme en aura rellement besoin. Si une application tourne en franais, on fait donc lconomie du chargement
des fichiers de ressources en anglais.
Si un mme assemblage est dvelopp par plusieurs dveloppeurs, il se peut que certains
prfrent le langage VB.NET tandis que dautres prfrent C . Dans ce cas chaque module
peut tre dvelopp dans un langage dirent.
17
Loutil ILMerge
Sachez qu linverse vous pouvez runir plusieurs assemblages au sein dun mme fichier dextension .exe ou .dll. Pour cela, il vous faut avoir recours loutil ILMerge distribu gratuitement
par Microsoft et tlchargeable partir du web. Les fonctionnalits de cet outil sont aussi exploitables programmatiquement grce une API documente. Loutil sait aussi tenir compte des
assemblages signs.
Structure du manifeste
Le manifeste contient les informations dauto description de lassemblage. Il y a quatre types
dinformations dauto description et le manifeste contient une table pour chacun de ces types :
18
Entte
CLR
Manifeste
Mtadonnes
Code
compil
en IL
Ressources
Autre module
Entte
PE
Entte
CLR
Mtadonnes
Code
compil
en IL
Ressources
La table FileDef : Cette table contient une entre pour chaque module et fichier de ressource
de lassemblage mis part le module principal (donc si un assemblage na quun module,
cette table est vide). Chaque entre inclut le nom du fichier (avec lextension), des drapeaux
dcrivant certaines caractristiques du fichier et une valeur de hachage du fichier.
La table ManifestResourceDef : Cette table contient une entre pour chaque type et chaque
ressource de lassemblage. Chaque entre contient un index vers la table FileDef pour indiquer dans quel fichier est le type ou la ressource. Dans le cas dun type, lentre contient aussi
un oset indiquant o se trouve physiquement le type dans le fichier. Une consquence
est que chaque compilation dun module implique la reconstruction du manifeste, donc la
recompilation du module principal.
La table ExportedTypeDef : Cette table contient une entre pour chaque type visible hors de
lassemblage. Chaque entre contient le nom du type, un index vers la table FileDef et un
oset indiquant o se trouve physiquement le type dans le fichier. Par souci dconomie
de place, les types visibles hors de lassemblage dfinis dans le module principal ne sont pas
rpts dans cette table. En eet, nous allons voir que ces types sont dj dcrits dans la
section mtadonnes.
19
La table ModuleDef : Cette table contient une seule entre qui dfinit le prsent module.
Cette entre contient notamment le nom du fichier avec son extension mais sans son chemin.
La table TypeDef : Cette table contient une entre pour chaque type dfini dans le module.
Chaque entre contient le nom du type, le type de base, les drapeaux du type (public,
internal, sealed etc), et des index rfrenant les entres des membres du type dans les
tables MethodDef, FieldDef, PropertyDef, EventDef, ... (une entre pour chaque membre).
La table MethodDef : Cette table contient une entre pour chaque mthode dfinie dans
le module. Chaque entre contient le nom de la mthode, les drapeaux de la mthode
(public, abstract, sealed etc), loset permettant de situer physiquement dans le module
le dbut du code IL de la mthode et une rfrence vers la signature de la mthode, qui est
contenue dans un format binaire dans un tas appel #blob dcrit plus loin.
Il y a aussi une table pour les champs (FieldDef), une pour les proprits (PropertyDef), une
pour les vnements (EventDef) etc. La dfinition de ces tables est standard et chaque table a un
numro cod sur un octet. Par exemple toutes les tables MethodDef de tous les modules .NET
ont le numro de table 6.
La table AssemblyRef : Cette table contient une entre pour chaque assemblage rfrenc
dans le module (i.e chaque assemblage qui contient au moins un lment rfrenc dans
le module). Chaque entre contient les quatre composantes du nom fort savoir : le nom
de lassemblage (sans chemin ni extension), le numro de version, la culture et le jeton de
cl publique (ventuellement la valeur nulle si il ny en a pas).
La table ModuleRef : Cette table contient une entre pour chaque module du mme assemblage rfrenc dans le module (i.e chaque module qui contient au moins un lment rfrenc dans le module). Chaque entre contient le nom du module avec son extension.
La table TypeRef : Cette table contient une entre pour chaque type rfrenc dans le module. Chaque entre contient le nom du type et une rfrence vers l o il est dfini. Si le
type est dfini dans ce module ou dans un autre module du mme assemblage, la rfrence
indique une entre de la table ModuleRef. Si le type est dfini dans un autre assemblage, la
rfrence indique une entre de la table AssemblyRef. Si le type est encapsul dans un autre
type, la rfrence indique une entre de la table TypeRef.
La table MemberRef : Cette table contient une entre pour chaque membre rfrenc dans
le module. Un membre peut tre par exemple une mthode, un champ ou une proprit.
Chaque entre est constitue du nom du membre, de sa signature et dune rfrence vers
la table TypeRef.
La dfinition de ces tables est aussi standard et chaque table a un numro cod sur un octet. Par
exemple toutes les tables MemberRef de tous les modules .NET ont le numro de table 10.
20
Les tas
En plus de ces tables la section des mtadonnes de type contient quatre tas (heap) nomms
#Strings, #Blob, #US et #GUID.
Le tas #Strings contient des chanes de caractres comme le nom des mthodes. Ainsi les
lments des tables MethodDef ou MemberRef ne contiennent pas de chanes de caractres
mais rfrencent les lments du tas #string.
Le tas #Blob contient des informations binaires, comme la signature des mthodes stocke sous une forme binaire. Ainsi les lments des tables MethodDef ou MemberRef ne
contiennent pas les signatures des mthodes mais rfrencent des lments du tas #blob.
Le tas #US (pour User String) contient les chanes de caractres dfinies directement au sein
du code.
Le tas #GUID contient les GUID dfinis et utiliss dans le programme. Un GUID est une
constante de 16 octets, utilise pour nommer une ressource. La particularit des GUID est
que vous pouvez les gnrer partir doutils tels que guidgen.exe, de faon tre quasicertain davoir fabriqu un GUID unique au monde. Les GUID sont particulirement utiliss dans la technologie COM.
Les mtadonnes de type sont trs importantes dans larchitecture .NET. Elles sont rfrences
par les jetons de mtadonnes du code IL qui font lobjet de la section page 46. Cette section
prsente un exemple qui souligne limportance des tables et des tas de la section mtadonnes
de type. Les mtadonnes de type sont aussi utilises par la notion dattributs .NET (dcrite page
248) et le mcanisme de rflexion (dcrit page 233).
Le tas #
Certains documents se rfrent parfois un tas nomm # . Ce tas spcial contient en fait toutes
les tables de mtadonnes, y compris celles du manifeste sil sagit dun module principal.
Un module Foo2.netmodule.
21
Placez dans le mme rpertoire les deux fichiers sources C suivant (Foo1.cs et Foo2.cs) ainsi
quun fichier au format jpeg nomm Image.jpg.
Foo2.cs
Exemple 2-1 :
namespace Foo {
public class UneClasse {
public override string ToString() {
return "Bonjour de Foo2" ;
}
}
}
Foo1.cs
Exemple 2-2 :
using System ;
using System.Reflection ;
[assembly: AssemblyCompany("Lentreprise")]
namespace Foo {
class Program {
public static void Main(string[] argv) {
Console.WriteLine("Bonjour de Foo1") ;
UneClasse a = new UneClasse() ;
Console.WriteLine( a ) ;
}
}
}
Comme nous souhaitons construire un assemblage avec plus dun module, nous navons pas
dautres choix que dutiliser le compilateur csc.exe en ligne de commande (car lenvironnement Visual Studio ne gre pas les assemblages multi modules). Le compilateur csc.exe est dcrit en dtails page 317.
Crer les fichiers Foo2.netmodule puis Foo1.exe en tapant dans lordre les lignes de commande
suivantes (le compilateur csc.exe se trouve dans le rpertoire <rpertoire-d-installation-deWINDOWS>\Microsoft.NET\Framework\v2.*) :
> csc.exe /target:module Foo2.cs
> csc.exe /Addmodule:Foo2.netmodule /LinkResource:Image.jpg
Foo1.cs
22
Vue du manifeste
En double cliquant sur le manifeste le texte suivant apparat dans une nouvelle fentre (certains
commentaires ont t enlevs pour plus de clart) :
.module extern Foo2.netmodule
.assembly extern mscorlib
{
.publickeytoken = (B7 7A 5C 56 19 34 E0 89 )
.ver 2:0:0:0
}
.assembly Foo1
{
.custom instance void
[mscorlib]System.Reflection.AssemblyCompanyAttribute::.ctor(string) =
( 01 00 0E 4C E2 80 99 65 6E 74 72 65 70 72 69 73 65 00 00 ) //
...L...entreprise..
.custom instance void [mscorlib]System.Runtime.CompilerServices.
CompilationRelaxationsAttribute::.ctor(int32) =
( 01 00 08 00 00 00 00 00 )
.hash algorithm 0x00008004
.ver 0:0:0:0
}
.file Foo2.netmodule
.hash = (80 CC 15 14 E2 AB E0 AF D6 BD 55 B9 1B 02 61 10
.file nometadata Image.JPG
.hash = (0D 84 86 DE 03 E0 05 68 9D 38 F4 B0 B6 19 66 BB
.class extern public Foo.UneClasse
{
.file Foo2.netmodule
.class 0x02000002
}
.mresource public Image.jpg
{
.file Image.JPG at 0x00000000
}
.module Foo1.exe
B4 CF AA 94 )
3D 73 76 06 )
23
// MVID: {3C680D21-A6C8-4151-A2A6-9B20B8FDDF27}
.imagebase 0x00400000
.file alignment 0x00000200
.stackreserve 0x00100000
.subsystem 0x0003
// WINDOWS_CUI
.corflags 0x00000001
// ILONLY
// Image base: 0x04110000
On voit clairement que les fichiers Foo2.netmodule et Image.jpg sont rfrencs. On remarque
aussi quun autre assemblage est rfrenc, cest lassemblage mscorlib qui contient entre
autre la classe object. Tous les assemblages .NET rfrencent lassemblage mscorlib car celuici contient les types de base. ce titre, lassemblage mscorlib joue un rle prpondrant et
particulier dans la plateforme .NET. Plus dinformations son sujet sont disponibles en page
94.
24
Options de ildasm.exe
Loutil ildasm.exe prsente des options trs intressantes comme la visualisation du code IL
binaire (option Show Byte) ou la prsentation du code source correspondant en C (option Show
Source Lines).
Avec ildasm.exe 2.0 certaines options sont disponibles par dfaut alors quen version 1.x il fallait
utiliser le commutateur /adv en ligne de commande afin de les obtenir. Ces options sont la
possibilit dobtenir des statistiques quant la taille en octets de chaque section dun assemblage
et quant lachage des informations sur les mtadonnes.
Par lintermdiaire de ildasm.exe vous pouvez donc faire du reverse engineering sur un assemblage. Vous pouvez rcuprer des informations comme le code IL des mthodes ou les noms
des lments dun assemblage (classes, mthodes...). En revanche, les commentaires du code
source dun assemblage ne sont pas conservs dans lassemblage. Ils ne peuvent donc pas tre
retrouvs avec ildasm.exe.
Loutil Reflector
Depuis plusieurs annes, lutilitaire Reflector dvelopp par Lutz Roeder a dtrn ildasm.exe
et est devenu loutil incontournable pour analyser des assemblages .NET. Cet outil est tlchargeable gratuitement http://www.aisto.com/roeder/dotnet/. Voici une copie dcran du traitement de notre exemple par Reflector :
25
De nombreux addins tel que statement graph de Jonathan de Halleux, qui permet de dcompiler une mthode sous la forme dun organigramme, Reflector Di de Sean Hederman qui
permet de mettre en vidence les dirences entre deux versions dun assemblage ou file
disassembler de Denis Bauer qui permet de rcuprer le code source dune application
partir de ses assemblages. Une consquence est que file disassembler permet de migrer une
application, par exemple de VB.NET vers C .
26
Les attributs utiliss pour la constitution du nom fort (voir un peu plus loin pour la dfinition dun assemblage nom fort) :
AssemblyKeyFile : spcifie le fichier dextension .snk (strong name key) gnr par lexcutable sn.exe.
AssemblyFlags : spcifie si lassemblage peut tre excut cte cte ou non. La notion
dutilisation cte cte dun assemblage est expose page 65. Si lexcution cte cte
est autorise, cet attribut spcifie si lutilisation cte cte peut tre faite dans un mme
domaine dapplication, dans un mme processus ou seulement sur la mme machine.
Les attributs relatifs lutilisation de lassemblage au sein dune application COM+. On peut
citer les attributs ApplicationID, Application-Name, ApplicationActivation.
Dans larticle Setting Assembly Attributes des MSDN vous pouvez avoir plus de dtails sur les
attributs standard relatifs aux assemblages.
Le numro majeur.
Le numro mineur.
Le numro de compilation.
27
Le numro de rvision.
Vous pouvez fixer le numro de version de lassemblage avec lattribut AssemblyVersion. Il est
possible dutiliser dans cet attribut un astrisque qui laisse le choix au compilateur de fixer les
numros de compilation et de rvision, par exemple 2.3.* . Dans ce cas le numro de compilation sera le nombre de jours couls depuis le 1er janvier 2000, et le numro de rvision sera
le nombre de secondes (divis par deux car 24*60*60=86400 et 86400 > 65536) coules dans
la journe. Ce processus de datation du numro de version dun assemblage est trs utile pour
obtenir des numros de version croissants mais toujours dirents.
Il existe en fait trois types de numros de version pour un mme assemblage ce qui entrane
une certaine confusion. Seul le numro de version de lassemblage est utilis par le CLR pour
permettre la possibilit de stockage dun assemblage cte cte. Les deux autres numros de
version, le numro de version du produit (fix par lattribut AssemblyInformationalVersion)
et le numro de version du fichier (fix par lattribut AssemblyFileVersion) sont purement
informatifs.
Lorsque vous estimez que plusieurs assemblages doivent constamment avoir le mme numro
de version, vous pouvez faire en sorte que leurs projets rfrencent le mme fichier qui contient
ce numro. Nanmoins, sachez que si vous avez rencontrez ce besoin, il est peut tre judicieux
de rassembler le code de vos assemblages dans un seul assemblage.
La dfinition des rgles dincrmentation des composantes des versions des assemblages est un
sujet sensible qui doit tre mrement rflchit. Il ny a pas de bonnes solutions dans labsolu
car ces rgles sont fonctions du modle commercial de chaque lentreprise. Elles varient selon
quun assemblage est utilis par un ou plusieurs produits, externes ou internes lentreprise,
selon que lentreprise vend des bibliothques de classes ou des produits excutables etc. Voici
quelques recommandations :
Le numro mineur : incrmenter lorsquune fonctionnalit a lgrement volu ou lorsquun membre visible de lextrieur (par exemple une mthode protge dune classe publique) a chang ou lorsquun bug majeur a t corrig.
28
Lutilisation de cet attribut permet de prciser zro, une ou plusieurs composantes du nom fort
dun assemblage amis selon les direntes versions dun assemblage avec lesquelles on souhaite
tre amis :
using System.Runtime.CompilerServices ;
...
[assembly:InternalsVisibleTo("AsmAmi1")]
[assembly:InternalsVisibleTo("AsmAmi2,PublicKeyToken=0123456789abcdef")]
[assembly:InternalsVisibleTo("AsmAmi3,Version=1.2.3.4")]
...
Le nom du fichier ;
le jeton de cl publique de la signature numrique (tous ces termes sont expliqus cidessous) ;
Le nom est fort dans le sens o il permet didentifier lassemblage dune manire unique. Cette
unicit est garantie par la seule composante jeton de cl publique .
Un nom fort est aussi sens rendre un assemblage infalsifiable. Richard Grimes dcrit lURL
http://www.grimes.demon.co.uk/workshops/fusionWSCrackThree.htm une mthode permettant de craquer un nom fort dun assemblage (i.e cette mthode permet de falsifier un assemblage sign). Cette mthode se base sur un bug du CLR version 1.1 corrig en version 2.0. Il ny
a plus notre connaissance de telles failles de scurit en .NET 2.0.
29
Avant de nous pencher sur la cration dun assemblage nom fort, nous vous conseillons de
vous familiariser avec les notions de cls publiques/cls prives et de signature numrique prsentes en page 223. En apposant une signature numrique leurs assemblages, un auteur ou
une entreprise peuvent lauthentifier. Microsoft et lorganisation ECMA ont chacun un couple
de cl publique/cl prive quils utilisent pour authentifier leurs assemblages.
Loutil sn.exe
La premire tape pour un dveloppeur ou une entreprise qui dsire crer des assemblages
noms forts est de crer un couple de cl prive/cl publique. Cette opration utilise des algorithmes mathmatiques complexes. Heureusement le Framework .NET met notre disposition
loutil sn.exe (sn pour strong name) qui eectue cette tche. Cet outil peut fabriquer un fichier
dextension .snk (pour strong name key en anglais) qui contient un couple de cl prive/cl publique en utilisant loption -k. Par exemple :
C:\Test>sn.exe -k Cles.snk
Microsoft (R) .NET Framework Strong Name Utility Version 2.0.XXXXX
Copyright (C) Microsoft Corporation. All rights reserved.
Key pair written to Cles.snk
C:\Test>
sn.exe permet aussi de visualiser la cl publique et la cl prive contenues dans un fichier dextension .snk avec loption -tp :
C:\Test>sn.exe -tp Cles.snk
Microsoft (R) .NET Framework Strong Name Utility Version 2.0.XXXXX
Copyright (C) Microsoft Corporation. All rights reserved.
Public key is
070200000024000052534132000400000100010051a7dadde83cf10e8b7c6cd99e4d062b
1aca430e11db76365ab29d6c31fc93a7bea6def9d7b2e8a7c568b0d5ada5e8e131cb98ea
3e9a876236b33b362e433fdd62bb4c5cc5ea23f1dfa76d35b5412d812f66d03e079009ea
76462392663bc08ab5f937524e794948532c679db5eda50210f8a8b2b8b186fcb342859c
48ea76d609d108b1957d3888f75b270cf85029ede8437c36b4ae59342c5fa7aacdb453c7
465cc7027405930627a5b153e5f48cdd0375840bf6feaa3548aa421ab5138fb095efa5d5
81ae61bd9248ac97293ee69b139ef9ae79d907c5cf2c194adf7c2723e269b5eef55157c4
095fccf436d7db1893aed8c63d57e9d5eba5c1dd88f8bda81b6c74b77899071823c85c86
2254865337d2b70d545d17de9b8471527bbd54d4e1bd6cb6b53fed9135c9c7b1b1af2b27
ab0414b423b61334c9c1adb0700145ba1354848b081e09e8a860d24fb9ea6c48ac2657f1
9ff1fab37a177744c377d9d7d09f34498901f4439bc6754b4ac0efcc4d84d4a6c22a05c2
eecaec3f7fabf8b45555d4788eaeda815cf743001477a8c31c24c04b4016f4ef3401617e
22441b95ca78265a0a6133150ca03c2886d4e3893f9d1dc6a3e2d8770a63b8fbd0db52d8
176bbda6e1f4074d9dfda916cf316294f0499eade4aa47d1b780627ab6fb7beb5aa48412
9062d3152e6b6585c865d319018727c1a34866484018f5f1c0ab0bf2b35e63a8a3bbf7a0
b6aeeb110f4b162426a977dc2034adf08ec41cc5d20f2d6beac92a1619aff0e25030e30a
02570eb9ad74eba0f2aba90b18789ae99f8da72
30
Rduire la taille des noms forts (qui incluent ces jetons la place de la cl publique elle
mme).
Rduire la taille des assemblages qui rfrencent de nombreux assemblages noms forts.
Signer un assemblage
Pour attribuer un nom fort un assemblage vous pouvez soit utiliser les options /keycontainer
et /keyfile du compilateur csc.exe soit utiliser le menu Proprit du projet Signature Signe
lassemblage de Visual Studio 2005.
Vous pouvez aussi utiliser lattribut dassemblage AssemblyKeyFile dans le code source du module principal. Cet attribut accepte un argument qui est le nom dun fichier dextension .snk.
Par exemple :
[AssemblyKeyFile("Cles.snk")]
Lorsque cet attribut est ajout, le compilateur signe lassemblage mais met un avertissement
vous mettant en garde quune des deux premires techniques cite est prfrable.
Lorsquun assemblage doit tre sign par une de ces trois techniques, le compilateur inclut une
signature numrique et la cl publique dans le corps de lassemblage. Lalgorithme utilis pour
hacher le contenu de lassemblage se nomme SHA-1 (Secure Hash Algorithm). Vous pouvez choisir un autre algorithme de hachage de lassemblage, comme le trs connu MD5 (MD pour Message Digest) au moyen de lattribut dassemblage AssemblyAlgorithmId qui prend en argument
une valeur de lnumration AssemblyHashAlgorithm. La taille dune valeur de hachage cre
par lalgorithme SHA-1 est de 20 octets. Cette taille peut varier selon lalgorithme utilis.
31
Le compilateur encrypte la valeur de hachage avec la cl prive et obtient ainsi la signature numrique RSA de lassemblage. La cl publique est insre dans la table AssemblyDef du manifeste
tandis que la signature numrique est insre dans une partie spciale de lentte CLR. Voici le
schma dcrivant le processus de signature dun assemblage :
Module principal
de foo1.exe
avant signature
Module principal
de foo1.exe
aprs signature
Signature
numrique
Entte PE
Entte CLR
Entte PE
Entte CLR
Encryption
avec la cl
prive
Manifeste
Signature
numrique
Manifeste
Fichier
cles.snk
Cl publique
Cl prive
Cl publique
Mtadonnes
Mtadonnes
Code compil
en IL
Code compil
en IL
Valeur de
hachage
Ressources
Ressources
Un exemple
Voici le code dun assemblage Foo1 :
Foo1.cs
Exemple 2-3 :
using System ;
namespace Foo {
class Program {
public static void Main( string[] argv ){
Console.WriteLine( "Bonjour de Foo1" ) ;
}
}
}
Compilons et signons cet assemblage avec la cl Cles.snk :
>csc.exe /keyfile:Cles.snk Foo1.cs
Analysons le manifeste de lassemblage Foo1.exe avec loutil ildasm.exe (pour plus de clart
nous avons enlev quelques commentaires) :
.assembly extern mscorlib
{
.publickeytoken = (B7 7A 5C 56 19 34 E0 89 )
.ver 2:0:0:0
}
.assembly Foo1
{
.custom instance void
// .z\V.4..
32
00 24 00 00
00 24 00 00
51 A7 DA DD
1A CA 43 0E
BE A6 DE F9
31 CB 98 EA
62 BB 4C 5C
2F 66 D0 3E
B5 F9 37 52
10 F8 A8 B2
.hash algorithm 0x00008004
.ver 0:0:0:0
04
52
E8
11
D7
3E
C5
07
4E
B8
80
53
3C
DB
B2
9A
EA
90
79
B1
00
41
F1
76
E8
87
23
09
49
86
00
31
0E
36
A7
62
F1
EA
48
FC
94
00
8B
5A
C5
36
DF
76
53
B3
00
04
7C
B2
68
B3
A7
46
2C
42
00
00
6C
9D
B0
3B
6D
23
67
85
00
00
D9
6C
D5
36
35
92
9D
9C
06
01
9E
31
AD
2E
B5
66
B5
48
02
00
4D
FC
A5
43
41
3B
ED
EA
00
01
06
93
E8
3F
2D
C0
A5
76
00
00
2B
A7
E1
DD
81
8A
02
D6 )
}
.module Foo1.exe
// MVID: {5DD7C72B-D1C1-49BB-AB33-AF7DA5617BD1}
.imagebase 0x00400000
.file alignment 512
.stackreserve 0x00100000
.subsystem 0x00000003
.corflags 0x00000009
// Image base: 0x03240000
La cl publique est bien la mme que celle que lon a surligne plus haut, lors de lanalyse du
fichier Cles.snk. Loutil ildasm.exe ne nous permet pas de visualiser ni le jeton de cl publique
ni la signature numrique. Loutil sn.exe nous permet de visualiser le jeton de cl publique avec
loption -T :
C:\Code\CodeDotNet\ModuleTest>sn.exe -T Foo1.exe
Microsoft (R) .NET Framework Strong Name Utility Version 2.0.XXXXX
Copyright (C) Microsoft Corporation. All rights reserved.
Public key token is c64b742bd612d74a
Lassemblage mscorlib est rfrenc et son jeton de cl publique est connu. Cest normal,
puisque ce jeton fait partie intgrante du nom fort de lassemblage. En fait le jeton de cl
publique a t cr pour le rfrencement dautres assemblages. Nous rappelons que la taille
dune cl publique tant de 128 octets, la taille des assemblages rfrenant beaucoup dautres
assemblages aurait t trop grosse sans la technique du jeton cl publique.
Un assemblage nom fort ne peut rfrencer un autre assemblage qui na pas de nom fort. En
revanche, un assemblage sans nom fort, peut rfrencer un assemblage nom fort.
Dans le cas o un assemblage est constitu de plusieurs modules, les modules autres que le
module principal nont pas de cl publique. En revanche durant la compilation du module
principal, le compilateur calcule une valeur de hachage pour chaque module. Ces valeurs sont
33
intgres dans les entres de la table FileDef du manifeste. Ces valeurs sont donc prises en compte
lors du calcul de la valeur de hachage du module principal. Grce cette astuce les modules dun
assemblage nom fort sont eux aussi nomms dune manire unique et infalsifiables (en .NET
2.0), sans intgrer une signature numrique.
Il faut dabord construire un fichier dextension .snk qui ne contient quune cl publique.
Pour cela il faut utiliser loption -p de sn.exe.
C:\Code>sn -p Cles.snk ClePublique.snk
Microsoft (R) .NET Framework Strong Name Utility Version 2.0.XXXX
Copyright (C) Microsoft Corporation. All rights reserved.
Public key written to ClesPubliques.snk
C:\Code>
Le fichier ClePublique.snk est distribu aux dveloppeurs qui lutilisent durant le dveloppement des assemblages la place du fichier Cles.snk (donc dans lattribut AssemblyKeyFile).
Lattribut dassemblage AssemblyDelaySign initialis avec largument boolen true, doit
tre utilis. Le compilateur comprend quil ne doit pas signer le module principal. Cependant le compilateur prvoit lespace ncessaire la signature, dans le module principal,
insre la cl publique dans le manifeste et calcule la valeur de hachage des modules sans
manifeste.
Lorsque les quipes de dveloppement fournissent des assemblages prts tre packags et
dploys, il sut de signer les assemblages avec loption -R de sn.exe.
C:\Code>sn.exe -R Foo1.exe Cles.snk
Microsoft (R) .NET Framework Strong Name Utility Version 2.0.XXX
Copyright (C) Microsoft Corporation. All rights reserved.
Assembly Foo1.exe successfully re-signed
C:\Code>
34
Vous pouvez aussi retarder la signature dun assemblage avec le compilateur csc.exe en utilisant
conjointement les options /delaysign, /keycontainer et /keyfile.
La technique de la signature retarde peut tre aussi utilise pour signer des assemblages modifis aprs la compilation, par exemple avec un framework de programmation oriente aspect.
Fichiers de ressources
Concrtement, pour exploiter des ressources de type chanes de caractres ou des ressources stockes dans un format binaire (images, animations, sons...) dans un assemblage, il faut procder
selon les quatre tapes suivantes :
Editer le fichier de ressources dans un format exploitable par les humains (extension du fichier .txt ou .resx).
Convertir le fichier de ressources dans un format binaire adapt la lecture par un programme (extension du fichier .resources).
Exploiter les ressources dans votre code source au moyen de la classe System.Resources.
ResourceManager. Bien souvent, on se sert dune classe gnre qui encapsule laccs cette
classe.
Nous commencerons par montrer comment faire ces manipulations la main laide doutils en ligne de commande. Ensuite, nous verrons que Visual Studio 2005 permet dautomatiser
grandement ce processus.
Un fichier de ressources donn cible une seule culture. Il existe trois formats de fichiers de ressources, utiliss lors de direntes tapes du dveloppement dune application :
35
Les fichiers de ressources dextension .txt : Ces fichiers associent des identifiants aux
chanes de caractres dune culture. Par exemple voici un tel fichier dont la culture destination est langlais :
Exemple 2-4 :
MyRes.txt
Bonjour = Hello!
AuRevoir = Bye...
Les fichiers de ressources dextension .resx : Ces fichiers sont au format XML. Ils associent
des identifiants aux chanes de caractres dune culture. la dirence des fichiers de ressources dextension .txt, les fichiers dextension .resx peuvent aussi associer des identifiants des ressources stockes dans un format binaire. Dans un fichier dextension .resx,
linformation binaire est convertie au format UNICODE en utilisant lencodage Base64.
Lutilitaire resxgen.exe sert convertir une telle ressource (par exemple une image au format bmp ou jpg) en un fichier dextension .resx. Lutilisation de Visual Studio 2005 simplifie
considrablement la visualisation, ldition et la maintenance des fichiers dextension .resx
grce un diteur ddi.
Les fichiers de ressources dextension .resources : Ces fichiers sont logiquement quivalents aux fichiers dextension .resx. la dirence de ces derniers, ces fichiers sont dans un
format binaire. Ce sont ces fichiers qui sont intgrs dans les assemblages ltape de la
compilation.
Comme nous lavons expliqu, seul un fichier de ressources au format .resources peut tre
intgr dans un assemblage. Loutil resgen.exe utilisable en ligne de commande permet de
construire un fichier de ressources dans un de ces formats, partir dun autre fichier de ressources dans un autre format. resgen.exe connat le format du fichier en entre et du fichier
en sortie grce aux extensions de leurs noms. Voici un exemple dutilisation :
>resgen.exe MyRes.txt MyRes.resources
36
Exemple :
MyRes.cs
Program.cs
class Program {
static void Main() {
System.Console.WriteLine( MyRes.Bonjour ) ;
}
}
Il est intressant de comparer ce code au code dun programme qui nutilise pas la classe gnre
MyRes :
Exemple 2-6 :
Program.cs
using System.Resources ;
class Program {
static void Main() {
ResourceManager rm = new ResourceManager( "MyRes" ,
typeof(MyRes).Assembly) ;
System.Console.WriteLine( rm.GetString("Bonjour") ) ;
37
}
}
La classe ResourceManager prsente plusieurs versions surcharges de GetString(). De plus, elle
prsente la mthode plus gnrale GetObject() qui peut tre utilise pour charger des chanes
de caractres. Cette mthode peut aussi tre utilise pour charger des ressources stockes dans
un format binaire telles que des images. Par exemple :
...
System.Drawing.Bitmap image = (Bitmap) rm.GetObject("UneImage") ;
string s = (string) rm.GetObject("Bonjour") ;
...
Dans le cas dutilisation de la classe gnre par resgen.exe, une proprit de type System.Drawing.Bitmap reprsente laccs une ressource de type image.
...
System.Drawing.Bitmap image = MyRes.UneImage ;
...
Intressons nous maintenant la compilation de ce programme. Dans le cas o lassemblage
contient du code, il faut prciser les noms des fichiers de ressources dextension .resources
la compilation de lassemblage. Le compilateur csc.exe du langage C prvoit cet eet les
options /resource et /linkresource. Par exemple :
>csc.exe /resource:MyRes.resources,MyRes.resources Program.cs MyRes.cs
La syntaxe avec une virgule, qui spare le nom physique du fichier de ressources ( gauche de la
virgule) du nom logique ( droite de la virgule) qui sera utilis dans le code de lassemblage pour
lidentifier. Les noms logique et physique sont ici identiques et gaux "MyRes.resources". Notez que loption /resource copie physiquement le contenu du fichier ressource dans le corps du
module compil. Vous pouvez aussi utiliser loption /linkresource pour rfrencer le fichier
de ressources partir du module principal.
38
Nous utilisons en entre de al.exe avec loption /embed un fichier nomm MyRes.es-ES.ressources.
Ce fichier a t fabriqu avec loutil resgen.exe. partir du fichier MyRes.es-ES.txt suivant :
MyRes.es-ES.txt
Exemple 2-7 :
Bonjour = Hola!
AuRevoir = Adios...
Loption /embed fait en sorte que le contenu du fichier MyRes.es-ES.ressources soit physiquement inclus dans le fichier Program.Ressources.dll.
Le nom du module principal dun assemblage satellite (celui cr avec loutil al.exe) doit
tre
[nom de lassemblage qui contient le code qui manipule les ressources].
Resources.dll.
Un assemblage satellite correspondant une culture xx-XX doit se trouver dans le sous rpertoire xx-XX (par rapport au rpertoire de lassemblage qui contient le code qui manipule
les ressources). On remarque donc que les assemblages satellites relatifs un mme assemblage ont tous le mme nom. Seul les noms des rpertoires qui les stockent permettent de
dterminer pour chacun la culture laquelle il se rfre.
Ces rgles sont illustres par cette organisation des fichiers de notre exemple :
\Program.exe
\es-ES\Program.Resources.dll
...
// fichier fabriqu
e par csc.exe
// fichier fabriqu
e par al.exe
Program.cs
class Program {
static void Main() {
MyRes.Culture = new System.Globalization.CultureInfo("es-ES");
System.Console.WriteLine( MyRes.Bonjour ) ;
}
}
En analysant le code de la classe MyRes gnre par resgen.exe, on saperoit que la recherche de
ressource ne se fait pas selon la culture prcise par la proprit Thread.CurrentUICulture mais
selon la valeur de la proprit MyRes.Culture (qui correspond au champ MyRes.resourceCulture).
On peut rcrire ce programme sans utiliser la classe MyRes comme ceci :
39
Program.cs
using System.Resources ;
// Pour la classe ResourceManager.
using System.Threading ;
// Pour la classe Thread.
using System.Globalization ; // Pour la classe CultureInfo.
class Program {
static void Main() {
Thread.CurrentThread.CurrentUICulture = new CultureInfo("es-ES");
ResourceManager rm = new ResourceManager( "MyRes" ,
typeof(MyRes).Assembly) ;
System.Console.WriteLine(rm.GetString("Bonjour")) ;
}
}
Les programmes prcdents fonctionnent aussi si lassemblage satellite Program.Resources.dll
de culture es-ES se trouve dans le GAC. Comme tout assemblage situ dans le GAC, un tel assemblage satellite a un nom fort. Il est direnci de ces homologues relatifs dautres cultures
grce la composante culture de ce nom fort.
Lors de la compilation de Program.exe vous navez aucunement besoin de rfrencer un des
assemblages satellites. Il est ainsi possible de rajouter des assemblages satellites aprs la compilation de votre programme. Pour cela, vous devez faire en sorte que votre application rcupre
la culture avec laquelle elle doit sexcuter. Celle-ci peut par exemple tre rcupre partir dun
fichier de configuration ou partir de prfrences utilisateurs.
Program.cs
using System ;
using System.Globalization ; // Pour la classe CultureInfo.
class Program {
static void Main() {
CultureInfo spainCulture = new CultureInfo("es-ES") ;
if( MyRes.ResourceManager.GetResourceSet(
spainCulture , true, false )!= null ) {
MyRes.Culture = spainCulture ;
Console.WriteLine(MyRes.Bonjour) ;
}
else
Console.WriteLine("Assemblage satellite es-ES non trouv
e !") ;
}
}
40
Formatage et culture
Le formatage de certains lments prsents aux utilisateurs tels que les dates ou les nombres
peut dpendre de la culture. Le framework .NET se sert de la valeur de la proprit CultureInfo
Thread.CurrentCulture{get;set} pour dterminer quelle culture utiliser lors dune opration
de formatage. Par exemple, le programme suivant...
Introduction au langage IL
41
Exemple 2-11 :
using System.Threading ;
// Pour la classe Thread.
using System.Globalization ; // Pour la classe CultureInfo.
class Program {
static void Main() {
Thread.CurrentThread.CurrentCulture = new CultureInfo("en-US");
System.Console.WriteLine(System.DateTime.Now.ToString()) ;
Thread.CurrentThread.CurrentCulture = new CultureInfo("fr-FR");
System.Console.WriteLine(System.DateTime.Now.ToString()) ;
}
}
...ache ceci :
6/20/2005 10:54:10 PM
20/06/2005 22:54:10
Introduction au langage IL
Nous avons vu que les assemblages contiennent du code crit en langage IL (Intermediate Language). Le langage IL est en fait un langage objet part entire. Il constitue le plus petit dnominateur commun des langages .NET.
Il faut savoir que certaines documentations, notamment celles de Microsoft, utilisent le terme
de langage MSIL pour nommer limplmentation Microsoft du langage IL. Dautres documentations utilisent le terme CIL (Common Intermediate Langage) pour nommer le langage IL. Le langage IL est entirement spcifi par lorganisation ECMA. Vous pouvez trouver les spcifications
du langage IL sur le site de lECMA.
Les outils ildasm.exe et Reflector prsents prcdemment permettent de visualiser le code
en langage IL contenu dans un module. Nous allons voir et commenter du code IL bien quune
prsentation complte du langage IL dpasserait le cadre de cet ouvrage. Nous pensons quil est
bon que les dveloppeurs .NET aient une ide de ce quest le langage IL. Pour les programmeurs
Java cela ne sera pas sans rappeler le bytecode.
Vous pouvez compiler vos propres sources crites en langage IL, en assemblages, laide du
compilateur ilasm.exe fournis avec le framework .NET ( ne pas confondre avec loutil ildasm.
exe).
Comprenez bien que malgr sa ressemblance avec du langage machine, le langage IL nest support par aucun processeur (pour linstant du moins). Le code IL est compil lexcution, en
un langage machine cible. Ce langage machine est fonction du processeur de la machine. Ce mcanisme de compilation du code IL durant lexcution, est dcrit page 111. Cette ide dutiliser
un langage intermdiaire entre les langages de haut niveau et le langage machine nest pas nouvelle. Cette ide est exploite depuis longtemps sous Java et sous dautres langages/compilateurs.
42
Comme beaucoup dautres langages, IL est un langage avec une pile (stack en anglais). Une pile
est un espace mmoire contigu qui a la particularit davoir seulement un point daccs, appel le sommet de la pile. Tout comme une pile dassiette, vous pouvez ajouter une assiette au
sommet de la pile ou enlever une assiette au sommet de la pile. En informatique les piles ne
contiennent pas des assiettes mais des valeurs types.
Une pile appartient un thread. Pour chaque oprande dune opration excute par le thread
(oprations arithmtiques, appel de fonction...), il faut faire une copie de sa valeur au sommet
de la pile.
En IL, la pile est gre par le CLR. Dans ce cas le CLR joue le rle dun processeur virtuel .
Cest pour cela que dans le monde Java, lquivalent du CLR est nomm machine virtuelle .
Le CLR ne fonctionne pas exactement comme un processeur classique. En eet, les processeurs
travaillent avec une pile et des registres alors que le CLR remplit les mmes fonctions seulement
avec la pile.
Le fait qu aucun moment cette mthode ne charge plus de deux valeurs sur la pile, est
sauv dans un attribut appel .maxstack. La taille de la pile est donc borne durant la compilation.
Les variables locales sont types et numrotes.
Chaque instruction prend exactement un octet (IL_XXXX gauche reprsente loset de
linstruction IL correspondante, par rapport au dbut de la mthode).
Introduction au langage IL
43
ldc.i4.5 (load constant 5/charge constante 5) est une instruction IL qui pousse la valeur
constante entire 5 sur la pile, sous la forme dun entier cod sur quatre octets (idem pour
6). Comprenez bien que lentier 5 nest pas un paramtre de cette instruction. En consquence, linstruction ldc.i4.5 ne prend quun octet pour tre stocke. linverse, si lon
avait eu pousser la valeur constante entire 12345678 stocke sur quatre octets sur la pile,
on aurait utilis linstruction IL ldc.i4. Cette instruction IL prend en paramtre un entier
cod sur quatre octets. Cette instruction avec son paramtre aurait alors pris cinq octets
pour tre stock. On saperoit donc, qu linstar de ce qui se fait pour les langages machines, certaines instructions du langage IL ont t spcialement conues pour optimiser
le nombre doctets utiliss pour stocker le code IL. De plus, avec cette pratique, la vitesse
dexcution est aussi optimise, puisquon conomise le temps de lecture du paramtre.
ldloc.N (load local/charge locale) est une instruction IL qui pousse la valeur de la variable
locale numro N au sommet de la pile.
stloc.N (store local/enregistre locale) est une instruction IL qui dpile la valeur au sommet
de la pile et lenregistre dans la variable locale numro N.
add (add/ajoute) est une instruction IL qui dpile les deux valeurs au sommet de la pile
et les ajoute. Le rsultat est alors stock au sommet de la pile. Notez que lautre instruction IL add.ovf teste le dpassement de valeur et lance, le cas chant, une exception
OverflowException. De plus, on ne peut combiner tous les types pour les valeurs dentrs.
Dans le cas o une combinaison de type nest pas prvue, une exception est lance.
ret (return/retourne) est une instruction IL qui provoque le retour au code de la mthode
appelante.
Plus gnralement toutes les instructions IL, dont le nom commence par ld, chargent une valeur au sommet de la pile. chacune de ces instructions IL est associe une instruction IL symtrique, dont le nom commence par st, qui dpile la valeur au sommet et lenregistre.
44
On voit bien que, linstar de lexemple prcdent, les deux valeurs de i1 et i2 sont charges
sur la pile, avant lappel de la mthode Program.f(), grce linstruction IL call qui :
Introduction au langage IL
45
46
Notez quen page 498 nous exposons les modifications principales du langage IL pour le support
la gnricit.
0x70000001 : rfrence lentre dans le tas #userstring (0x70) reprsentant la chane de caractres "Hello world!" (0x000001).
Introduction au langage IL
47
0x0100000F : rfrence lentre dans la table TypeRef (0x01) reprsentant la classe System.
Console (0x00000F).
3
Construction, configuration et
dploiement des applications .NET
Soit avoir recours une tierce technologie telle que loutil open-source Nant, ou mme, utiliser des fichiers batch qui applent le compilateur C csc.exe.
MSBuild vise unifier toutes ces techniques. Ceux qui connaissent Nant ne seront pas dpayss
car MSBuild reprend beaucoup de concepts de cet outil. Latout majeur de MSBuild sur Nant
est dtre exploit par Visual Studio 2005. MSBuild na aucune dpendance par rapport Visual
Studio 2005 puisque, rappelons le, MSBuild fait partie intgrante de la plateforme .NET 2.0. En
revanche, les fichiers dextension .proj , .csproj, .vbproj etc gnrs par Visual Studio 2005
pour construire les projets sont rdigs au format XML MSBuild. la compilation, Visual Studio
2005 utilise les services de MSBuild. En outre, le format XML MSBuild est pleinement support
50
et document. Le support de MSBuild est donc une volution consquente de Visual Studio qui
jusquici, utilisait des scripts de compilation non documents.
Description
Copy
MakeDir
Construit un rpertoire.
Csc
Exec
AL
ResGen
La liste complte de ces types de tches standard est disponible dans larticle MSBuild Task Reference des MSDN. Un aspect particulirement intressant de MSBuild est quun type de tche
est matrialis par une classe .NET. Il est ainsi possible dtendre MSBuild avec de nouveaux
types de tches en fournissant vos propres classes. Nous reviendrons sur ce point un peu plus
loin.
Reprenons notre exemple dassemblage multi modules de la page 20. Rappelons que pour
construire cet assemblage constitu des trois modules Foo1.exe, Foo2.netmodule et Image.jpg
nous avions excuts les deux lignes de commande suivantes :
> csc.exe /target:module Foo2.cs
> csc.exe /Addmodule:Foo2.netmodule /LinkResource:Image.jpg
Foo1.cs
En plus, nous dsirons ici qu lissue de la construction de lassemblage, les trois modules se
trouvent dans un sous rpertoire \bin du rpertoire courant. Voici le script MSBuild Foo.proj
capable de raliser tout ceci :
51
Foo.proj
<Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<Target Name="FooCompilation">
<MakeDir Directories= "bin"/>
<Copy SourceFiles="Image.jpg" DestinationFiles=".\bin\Image.jpg"/>
<Csc Sources="Foo2.cs"
TargetType="module"
OutputAssembly=".\bin\Foo2.netmodule" />
<Csc Sources="Foo1.cs"
TargetType="exe"
AddModules=".\bin\Foo2.netmodule" LinkResources="Image.jpg"
OutputAssembly=".\bin\Foo1.exe" />
</Target>
</Project>
On voit que la cible nomme FooCompilation est constitue de quatre tches :
Une tche de type Copy qui copie le fichier Image.jpg dans le rpertoire \bin ;
Les deux tches de type Csc qui invoquent chacune le compilateur csc.exe.
Pour excuter ce script de compilation, il faut constituer un rpertoire ayant le contenu suivant :
.\Foo.proj
.\Foo1.cs
.\Foo2.cs
.\Image.jpg
Allez dans ce rpertoire avec la fentre de commande SDK Command Prompt (Menu Dmarrer
Microsoft .NET Framework SDK v2.0 SDK Command Prompt) puis lancer la commande
>msbuild.exe. Chaque cible est obligatoirement nomme. Par dfaut msbuild.exe excute
seulement la premire cible. Vous pouvez spcifier une liste de noms de cibles spars par des
points virgules en ligne de commande avec loption /target (raccourcis /t). Vous pouvez aussi
spcifier une telle liste avec lattribut DefaultTarget de llment <Project>. Si plusieurs cibles
sont spcifies, lordre dexcution nest pas dfini.
Le comportement par dfaut de MSBuild est de stopper la construction ds quune tche met
une erreur. Vous pouvez souhaiter avoir un script de construction tolrant aux erreurs. Aussi,
chaque lment reprsentant une tche peut contenir un attribut ContinueOnError qui est positionn false par dfaut mais qui peut tre positionn true.
Notion de proprit
Pour vous permettre de paramtrer vos scripts, MSBuild prsente la notion de proprit. Une
proprit est un couple cl/valeur dfini dans un lment <PropertyGroup>. Les proprits
MSBuild fonctionnent comme un systme dalias. Chaque occurrence de $(cl
e) dans le script
est remplace par la valeur associe Typiquement, le nom du rpertoire /bin est utilis cinq
reprises dans notre script Foo.proj. Il constitue un bon candidat pour dfinir une proprit :
52
Exemple 3-2 :
<Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<PropertyGroup>
<OutputPath>.\bin</OutputPath>
</PropertyGroup>
<Target Name="FooCompilation">
<MakeDir Directories= "$(OutputPath)"/>
<Copy SourceFiles="Image.jpg"
DestinationFiles="$(OutputPath)\Image.jpg"/>
<Csc Sources="Foo2.cs"
TargetType="module"
OutputAssembly="$(OutputPath)\Foo2.netmodule" />
<Csc Sources="Foo1.cs"
TargetType="exe"
AddModules="$(OutputPath)\Foo2.netmodule"
LinkResources="Image.jpg"
OutputAssembly="$(OutputPath)\Foo1.exe" />
</Target>
</Project>
Vous pouvez en outre exploiter des proprits dfinies par dfaut par MSBuild telles que :
Type de tche
Description
MSBuildProjectDirectory
MSBuildProjetFile
MSBuildProjectExtension
MSBuildProjectFullPath
MSBuildProjectName
MSBuildPath
Lors de ldition des proprits avec Visual Studio 2005, vous vous apercevrez quun certains
nombre de cls vous sont proposes par lintellisense. OutputPath constitue une telle cl. Vous
pouvez utiliser ces cls mais rien ne vous empche de dfinir vos propres cls. Nous aurions
ainsi pu choisir pour cl RepDeSortie la place de OutputPath.
Notion ditem
La base de la construction dun projet par un script est la manipulation de rpertoires, de fichiers
(sources, ressources, excutables etc) et de rfrences (vers des assemblages, vers des services, vers
des classes COM, vers des fichiers ressources, vers des projets etc). On utilise le terme item pour
dsigner ces entits qui constituent les entres et les sorties de la plupart des tches. Dans notre
exemple, le fichier Image.jpg est un item consomm la fois par la tche Copy et par la seconde
tche Csc. Le fichier Foo2.netmodule est un item produit par la premire tche Csc et consomm
par la seconde tche Csc. Rcrivons notre script avec cette notion ditem :
53
Foo.proj
<Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<PropertyGroup><OutputPath>.\bin</OutputPath></PropertyGroup>
<ItemGroup>
<Fichier_Image Include="$(OutputPath)\Image.jpg"/>
<NetModule_Foo2 Include="$(OutputPath)\Foo2.netmodule"/>
</ItemGroup>
<Target Name="FooCompilation">
<MakeDir Directories= "$(OutputPath)"/>
<Copy SourceFiles="Image.jpg"
DestinationFiles="@(Fichier_Image)"/>
<Csc Sources="Foo2.cs"
TargetType="module"
OutputAssembly="@(NetModule_Foo2)" />
<Csc Sources="Foo1.cs"
TargetType="exe"
AddModules="@(NetModule_Foo2)"
LinkResources="@(Fichier_Image)"
OutputAssembly="$(OutputPath)\Foo1.exe" />
</Target>
</Project>
Nous remarquons que lon utilise la syntaxe @(nom de litem) pour se rfrer un item. En
outre un item peut dfinir un ensemble de fichier grce la syntaxe wildcard. Par exemple, litem
suivant fait rfrence tous les fichiers sources C du rpertoire courant sauf Foo1.cs :
<cs_source Include=".\*.cs" Exclude=".\Foo1.cs" />
Foo.proj
54
Lorsquelles sont dfinies, les proprits standard Optimize et DebugSymbols sont automatiquement prises en comptes par les tches de type Csc.
Avant de lancer ce script, il faut prciser comme argument en ligne de commande la valeur de
la proprit Configuration. Cela peut se faire avec loption /property (raccourcis /p) :
>msbuild /p:Configuration=Release
Avec un peu dastuce, il est possible dutiliser une condition pour dfinir la valeur par dfaut
de la proprit Condition :
Exemple 3-5 :
Foo.proj
<Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<PropertyGroup Condition="$(Configuration)==">
<Configuration>Debug</Configuration>
</PropertyGroup>
<PropertyGroup Condition="$(Configuration)==Debug">
...
55
MSBuild, par exemple avec lattribut DefaultTargets, vous ne pouvez prsumer daucun
ordre dexcution. Vous pouvez cependant dfinir un systme de dpendance entre cibles avec
lattribut DependsOnTargets. MSBuild nexcute une cible que lorsque lensemble des cibles sur
lesquelles elle dpend a t excut. Naturellement, le moteur de MSBuild dtecte et sanctionne
dune erreur les dpendances circulaires entre cibles :
Exemple 3-6 :
Foo.proj
<Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003"
DefaultTargets="FooCompilation">
...
<Target Name="CreeOutputPath" Condition="!Exists($(OutputPath))">
<MakeDir Directories= "$(OutputPath)"/>
</Target>
<Target Name="FooCompilation" DependsOnTargets="Cr
eeOutputPath"
Inputs="Foo2.cs;Foo1.cs"
Outputs="@(NetModule_Foo2);$(OutputPath)\Foo1.exe">
...
</Target>
</Project>
Transformations MSBuild
Vous avez la possibilit dtablir une correspondance biunivoque entre lensemble des items en
entre et lensemble des items en sortie dune cible. Pour cela, vous devez utiliser les transformations MSBuild dtailles dans larticle MSBuild Transforms des MSDN. Lavantage dutiliser
des transformations est que MSBuild ne dcide dexcuter la cible que si au moins un item en
entre est plus vieux que litem en sortie qui lui correspond. Logiquement, une telle cible est
moins souvent excute do un gain de temps.
Foo.proj
56
Exemple 3-8 :
Foo.target.proj
Une cible nomme CreateManifestResourceNames qui soccupe de lorganisation des fichiers ressources (transformation des fichiers .resx en .resources etc).
Une cible CoreCompile qui contient une tche Csc qui ralise eectivement la compilation.
Nous vous invitons consulter ces fichiers .targets situs dans le rpertoire $(MSBuildBinPath) qui est le rpertoire dinstallation de .NET 2.0 (i.e [Rep dinstallation de Windows]
\Microsoft.NET\Framework\v2.0.XXXXX).
En plus dimporter un tel fichier .targets, les fichiers gnrs par Visual Studio 2005 contiennent
essentiellement les dfinitions des proprits et items qui servent paramtrer les cibles gnriques.
Elle doit implmenter la mthode bool Execute() de linterface ITask. Cette mthode
contient le corps de la tche et doit retourner true si elle a t excute avec succs.
Elle peut prsenter des proprits dont les valeurs seront positionnes par MSBuild partir
des valeurs des attributs de la tche, avant lappel la mthode Execute(). Seules les proprit marques avec lattribut Required doivent tre obligatoirement initialises.
Voici en exemple le code dune tche nomme MyTouch qui met jour la date des fichiers spcifis par lattribut Files (nous prcisons quune telle tche nomme Touch est prsente par le
framework MSBuild) :
57
MyTask.cs
using System ;
using Microsoft.Build.Framework ;
using Microsoft.Build.Utilities ;
namespace MyTask {
public class MyTouch : Task {
public override bool Execute() {
DateTime now = DateTime.Now ;
Log.LogMessage(now.ToString() +
" est la nouvelle date des fichiers suivants:") ;
try {
foreach(string fileName in m_FilesNames) {
Log.LogMessage("
" + fileName) ;
System.IO.File.SetLastWriteTime(fileName, now) ;
}
}
catch (Exception ex) {
Log.LogErrorFromException(ex, true) ;
return false ;
}
return true ;
}
[Required]
public string[] Files {
get { return (m_FilesNames) ; } set { m_FilesNames = value ; }
}
private string[] m_FilesNames ;
}
}
Notez lutilisation de la proprit TaskLoggingHelper Task.Log{get;} qui permet dacher
des informations concernant le droulement de la tche.
Notre tche MyTouch doit tre enregistre auprs de tous les scripts MSBuild qui sont susceptibles dy avoir recours. Cela se fait au moyen dun lment <UsingTask>. Voici un tel script qui
met jour les dates des fichiers sources C du rpertoire courant (lassemblage MyTask.dll doit
tre situ dans le rpertoire C:\CustomTasks\) :
Exemple 3-10 :
<Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<UsingTask AssemblyFile="C:\CustomTasks\MyTask.dll"
TaskName="MyTask.MyTouch"/>
<ItemGroup>
<FichierSrcCs Include="*.cs"/>
</ItemGroup>
<Target Name="ToucheLesFichiersSrcCs" >
<MyTouch Files= "@(FichierSrcCs)"/>
</Target>
</Project>
Foo.proj
58
Il est intressant de noter que toutes les tches standard sont dclares par des lments <UsingTask> dans le fichier Microsoft.Common.Tasks. Ce fichier est automatiquement et implicitement import par msbuild.exe chaque excution. En analysant ce fichier, on voit que les
classes correspondantes aux tches standard sont dfinies dans lassemblage Microsoft.Build.
Tasks.dll. Vous pouvez ainsi avoir accs au code de ces tches en utilisant un outil tel que
Reflector.
Fichiers de configuration
Un assemblage excutable .NET a la possibilit davoir un fichier de configuration XML qui peut
tre modifi aprs linstallation. Le nom dun tel fichier de configuration doit obligatoirement
commencer par le nom du module contenant le manifeste (extension .exe comprise) auquel
on ajoute lextension .config (par exemple Foo.exe.config). Il doit tre plac imprativement
dans le mme rpertoire que lassemblage.
Visual Studio prsente des facilits pour ldition et la maintenance du fichier de configuration
dune application. Vous pouvez cliquer droit sur le projet qui dfinit un assemblage excutable
Add New Item... Application Configuration File Gardez le nom App.Config. la compilation, Visual Studio eectuera une copie du contenu de ce fichier dans un fichier nomm
[Nom de lassemblage executable avec extension].config situ dans le mme rpertoire
que lassemblage excutable produit.
Le fichier machine.config
La plupart des lments de configuration dune application .NET peuvent tre dclars dans
un fichier nomm machine.config situ dans le sous rpertoire /CONFIG du rpertoire dinstallation de .NET ([Rep_dinstallation_de_Windows]\Microsoft.NET\Framework\v2.0.XXXXX).
La valeur dun paramtre dfini dans le fichier machine.config nest prise en compte par une application .NET que si ce paramtre nest pas initialis dans son fichier de configuration. En outre,
certains paramtres tels que le modle de processus utilis par ASP.NET nont de sens quau
niveau de la machine. Ils ne peuvent ainsi qutre initialiss dans le fichier machine.config.
Il nest pas conseill de modifier le fichier de configuration de la machine. En eet, vous pouvez
altrer par inadvertance le comportement des applications installes sur votre machine. Cependant, dans le cas dune machine en production (un serveur ou une machine ddie une application) ce fichier peut constituer un moyen ecace pour dfinir une stratgie de configuration
globale la machine.
Fichiers de configuration
59
<connectionStrings> : Stocke les chanes de connexion aux bases de donnes (voir page
717).
<location> : Permet de dfinir les lments de configuration ASP.NET pour chaque page
web (voir page 890).
<protectedData> : Permet de dfinir des sections dinformation confidentielle dans un fichier de configuration (voir page 718).
<system.Data.[Founisseur de donn
ees]> : Permet de paramtrer les fournisseurs de donnes ADO.NET disponibles avec notamment des fichiers XML consomms par le framework
de schma (voir page 718).
<system.Diagnostics> : Contient des informations relatives aux traces (voir page 620).
<system.web> : Dfinit les paramtres utiliss par ASP.NET. La faon de configurer les applications ASP.NET est un vaste sujet auquel nous consacrons la section 23 Configuration
dune application ASP.NET , page 888.
MyApp.exe.config
60
Exemple 3-12 :
using System.Configuration ;
class Program {
static void Main() {
Configuration appCfg = ConfigurationManager.OpenExeConfiguration(
ConfigurationUserLevel.None) ;
AppSettingsSection appSettings = appCfg.AppSettings ;
int myEntier ;
if (int.TryParse( appSettings.Settings["MyEntier"].Value,
out myEntier)) {
System.Console.WriteLine(myEntier) ;
myEntier *= 10 ;
appSettings.Settings["MyEntier"].Value = myEntier.ToString() ;
appCfg.Save() ;
}
}
}
Si vous recompilez lapplication MyApp avec Visual Studio et que vous utilisez le fichier App.
Config, la valeur de MyEntier est rinitialise 1234 puisque le contenu du fichier de configuration MyApp.exe.config est cras par le contenu du fichier App.Config.
Il est clair que cette faon de procder prsente deux dsavantages majeurs :
La valeur dun paramtre nest pas type. Il a fallu explicitement parser une chaine de caractre pour obtenir la valeur du paramtre MyEntier dans une variable entire.
Le nom du paramtre nest pas vrifi par le compilateur puisquil est fourni sous forme
dune chane de caractres. Cela nuit la productivit des dveloppeurs qui ne bnficient
pas non plus de lintellisense.
MyApp.exe.config
Fichiers de configuration
61
PublicKeyToken=b77a5c561934e089" >
name="MySettings"
type="System.Configuration.ClientSettingsSection,
System, Version=2.0.0.0, Culture=neutral,
PublicKeyToken=b77a5c561934e089"
allowExeDefinition="MachineToLocalUser" />
</sectionGroup>
<sectionGroup name="applicationSettings"
type="System.Configuration.ApplicationSettingsGroup,
System, Version=2.0.0.0, Culture=neutral,
PublicKeyToken=b77a5c561934e089" >
<section
name="MySettings"
type="System.Configuration.ClientSettingsSection,
System, Version=2.0.0.0, Culture=neutral,
PublicKeyToken=b77a5c561934e089" />
</sectionGroup>
</configSections>
<userSettings>
<MySettings>
<setting name="MyEntierUsr" serializeAs="String">
<value>1234</value>
</setting>
</MySettings>
</userSettings>
<applicationSettings>
<MySettings>
<setting name="MyEntierApp" serializeAs="String">
<value>4321</value>
</setting>
</MySettings>
</applicationSettings>
</configuration>
<section
Voici le code de lapplication MyApp correspondante. On voit que les deux problmes de
lexemple prcdent sont rsolus grce lintroduction dune classe nomme MySettings drive de la classe System.Configuration.ApplicationSettingsBase. Cette classe prsente une
proprit pour chaque paramtre. Les proprits relatives aux paramtres utilisateurs sont marques avec lattribut UserScopedSettingAttribute tandis que les proprits relatives aux paramtres de lapplication sont marques avec lattribut ApplicationScopedSettingAttribute.
On remarque que la proprit MyEntierApp na pas daccesseur set. En eet, dans le cadre de
cette technique les paramtres de lapplication doivent tre accessible en lecture seule :
Exemple 3-14 :
using System.Configuration ;
class Program {
static void Main() {
MySettings mySettings = new MySettings() ;
int myEntierUsr = mySettings.MyEntierUsr ;
System.Console.WriteLine(myEntierUsr) ;
MyApp.cs
62
Mapper le nom des classes drives de la classe ApplicationSettingsBase avec les noms
des sections de configuration.
Mapper le nom des proprits de ces classes avec les paramtres de configuration.
63
Il est intressant danalyser votre fichier machine.config pour sapercevoir que toutes les
sections standard prsentes un peu plus haut sont dclares par des lments <section>.
chacune de ces sections correspond un type standard tel que ConnectionStringsSection,
AppSettingsSection ou ProtectedConfigurationSection. LExemple 3-12 montre comment
avoir accs une instance de AppSettingsSection afin de modifier le contenu de la section
<appSettings>. Remarquez que certains de ces types tels que SystemDiagnosticsSection ne
doivent tre utiliss que par le CLR. Aussi, ils ne vous sont pas accessibles.
Les valeurs des paramtres utilisateurs ne sont pas stockes dans le fichier de configuration de
lapplication mais dans un fichier nomm [nom de lutilisateur].config. Ce fichier est stock dans le rpertoire spcifi par la proprit statique System.Windows.Forms.Application.
LocalUserAppDataPath.
Dans le cas dune application dploye avec la technologie ClickOnce (prsente dans la suite du
prsent chapitre), les fichiers de configuration (de lapplication et des utilisateurs) sont dploys
dans le rpertoire ClickOnce de lapplication (voir page 83).
Par dfaut, les classes drives de ApplicationSettingBase utilisent en interne la classe
System.Configuration.LocalFileSettingsProvider pour avoir accs en lecture et en criture aux fichiers de configuration. Cette classe drive de la classe System.Configuration.
SettingsProvider. Vous pouvez construire vos propres classes drives de SettingsProvider
afin dimplmenter vos propres mcanisme de persistance des paramtres dune application
(dans une base de donnes, dans la base des registres, par lintermdiaire dun service web
etc). Il sut ensuite dtablir le lien entre vos classes drives de ApplicationSettingBase
et vos classes drives de SettingsProvider en marquant les premires avec des attributs
SettingsProviderAttribute.
Le contrle ToolStrip du framework Windows Form 2.0 prsente des facilits pour stocker directement son tat dans les paramtres de lapplication. En outre, vous pouvez prvoir le mme
genre de facilit pour vos propres types de contrles. Ceci fait lobjet de larticle Application
Settings for Custom Controls des MSDN.
Une autre stratgie doit tre utilise pour dployer les assemblages qui sont partags par
plusieurs applications. Cette stratgie prsente des fonctionnalits trs intressantes tel
quune gestion performante du versionning.
Dploiement XCopy
La stratgie de dploiement dassemblages XCopy est la plus simple quil puisse exister. Elle
consiste copier tous les assemblages concernant lapplication dployer, dans un mme rpertoire. En gnral les dveloppeurs crent une arborescence de rpertoires. Ni la base des registres
ni les Active Directory de Windows ne sont sollicits lors dun dploiement de type XCopy.
64
Pour ne perdre aucun fichier, il est conseill dencapsuler larborescence et ses fichiers dans un
seul fichier (par exemple une archive cab, zip ou rar, comme expliqu plus loin). La dsinstallation de lapplication consiste dtruire larborescence du disque dur. Le changement de version
consiste dtruire larborescence du disque dur et la remplacer par la nouvelle.
Ceux qui on dj eu grer un dploiement sous Windows, que ce soit en tant que dveloppeur,
quadministrateur ou en tant que simple utilisateur, mesurent le gain deort apport par les
dploiements de type XCopy.
Pour accder des fonctionnalits avances, telles que la cration de raccourcis bureaux ou
dicones dans la barre des tches, vous pouvez utiliser le service dinstallation de programmes
Windows nomms MSI, qui fait lobjet dune section un peu plus loin.
Comprenez bien que la grande majorit des assemblages ne sont pas partags entre plusieurs applications. Vous rencontrerez rarement la ncessit de stocker un assemblage dans
le rpertoire GAC.
Un assemblage doit avoir un nom fort pour pouvoir tre stock dans le GAC. La notion de nom
fort est expose page 28. Un assemblage sans nom fort ne peut pas tre stock dans le GAC. En
revanche un assemblage nom fort peut ne pas tre stock dans le GAC (i.e dployer avec la
stratgie de dploiement XCopy).
Chaque fois quun assemblage nom fort est charg dans un processus, la vrification de la
validit de sa signature numrique doit tre faite. Cependant, pour les assemblages partags du
rpertoire GAC, ce nest pas ncessaire. En eet, lorsquun assemblage partag est insr dans
le rpertoire GAC, la validit de sa signature numrique est automatiquement vrifie. Si la
signature numrique nest pas valide, lassemblage ne peut tre insr dans le rpertoire GAC.
Il y a donc un gain de performance lorsquun assemblage est charg partir du rpertoire GAC.
Il est intressant de remarquer que lorsque vous installez la plateforme dexcution .NET
sur une machine, les assemblages contenant les classes du framework .NET (System.dll,
System.Data.dll, System.Security.dll etc) sont installs dans le GAC. Une copie de chacun
de ces assemblages existe dans le rpertoire dinstallation de .NET (i.e [Rep dinstallation
de Windows]\Microsoft.NET\framework\v2.0.XXXXX\). Si vous installez Visual Studio, il aura
recours ces copies lors de lanalyse des rfrences vers les assemblages standard du framework
dun projet. En eet, Visual Studio ne sait pas rfrencer les assemblages stocks dans le GAC.
Seul le CLR sait exploiter les assemblages du GAC grce une couche de code nomme assembly
loader qui fait lobjet de la section page 103.
65
66
Seuls les administrateurs de la machine peuvent modifier le rpertoire GAC. Vous pouvez eectivement visualiser et modifier la structure interne du rpertoire GAC partir
dune fentre de commande. Cependant il est impratif de ne jamais chercher modifier
la structure du rpertoire GAC sans passer par loutil GACUtil.exe ou lextension du shell
ShFusion.dll (bien que cette manipulation soit possible avec un peu dastuce).
Le cache des images natives dcrit page 113, est inclus dans le rpertoire GAC. Ainsi lorsque
lon visualise le rpertoire GAC, que ce soit avec GacUtil.exe ou lextension ShFusion.dll, les
images natives sont aussi visualises.
67
Supposons quun diteur redistribue une nouvelle version de lassemblage partag parce que
des bugs ont ts corrigs. Cette version est compltement compatible avec la prcdente (compatibilit ascendante). Cette nouvelle version va tre installe cte cte avec lancienne version
dans le rpertoire GAC. Cependant les applications ne vont pas utiliser cette nouvelle version
car elles contiennent le numro de lancienne version cod en dur dans leurs manifestes.
Ce problme nexiste pas avec le modle de DLL classique puisque lancienne version de la DLL
est purement et simplement remplace par sa nouvelle version.
La solution
On pourrait rsoudre ce problme en recompilant et en rinstallant toutes les applications
clientes de lassemblage partag. La recompilation permettrait aux manifestes des assemblages
clients de mettre jour le numro de version de lassemblage partag. Clairement cette solution
lourde et fastidieuse est impraticable et il a fallu trouver une autre solution.
La solution propose par Microsoft est de crer un assemblage de stratgie dditeur (publisher policy
en anglais). Cet assemblage, plac dans le rpertoire GAC, nest l que pour crer une redirection
sur le numro de version dun autre assemblage partag, lui aussi stock dans le rpertoire GAC.
Lorsquune application demande la version 3.3.0.0 de lassemblage XXX elle utilisera en fait la
version 3.3.1.0 de lassemblage XXX, car lassemblage de stratgie dditeur associ XXX contient
linformation de redirection de 3.3.0.0 vers 3.3.1.0.
Lassemblage de stratgie dditeur nest quun fichier de configuration. La redirection est eectue par le CLR. En eet, cest le CLR qui est charg de localiser et de charger les assemblages
durant lexcution. Or, le CLR tient compte des assemblages de stratgie dditeur lors de cette
opration.
Un assemblage de stratgie dditeur ne redirige que les appels destins un assemblage. De plus
il peut y avoir plusieurs assemblages de stratgie dditeur pour un mme assemblage. Dans ce
cas, seule la version la plus rcente de lassemblage de stratgie dditeur sera prise en compte
par le CLR.
Le comportement par dfaut du CLR est de tenir compte des assemblages de stratgie dditeur.
Cependant pour une application donne, qui utilise un assemblage partag donn, lutilisateur
de lapplication peut dcider que le CLR ne doit pas tenir compte de lassemblage de stratgie
dditeur. Ceci est bien pratique dans le cas o lutilisateur se rend compte que la nouvelle version cre plus de problmes quelle nen rsout (et nous savons bien que ce genre de situation
arrive !). Nous exposons en page 106 comment raliser cette manipulation.
Comprenez bien que les assemblages de stratgie dditeur sont ncessaires lorsquun diteur a lgrement modifi un assemblage (pour une correction de bug en gnral) et na
pas modifi la compatibilit ascendante. Les assemblages de stratgie dditeur ne doivent
tre utiliss que dans le cadre prcis de cette problmatique.
68
loutil al.exe, qui est prsent en page 37. Le fichier de configuration XML doit ressembler
celui-ci :
Exemple 3-15 :
Foo2.config
<configuration>
<runtime>
<assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
<dependentAssembly>
<assemblyIdentity name="Foo2" culture= "neutral"
publicKeyToken="C64B742BD612D74A" />
<bindingRedirect oldVersion="1.0.0.0" newVersion="2.0.0.0"/>
</dependentAssembly>
</assemblyBinding>
</runtime>
</configuration>
Remarquez llment <assemblyIdentity> qui reprend les composantes du nom fort de lassemblage sur lequel doit se faire la redirection ( part le numro de version).
Remarquez llment <bindingRedirect> qui redirige le numro de version. Cet lment peut
rediriger un intervalle de numro de version, vers un numro de version, avec la syntaxe suivante : "0.5.0.0-1.2.23.3". Vous pouvez aussi avoir recours plusieurs lments <bindingRedirect>.
La cration du module de lassemblage de stratgie dditeur contenant le manifeste, se fait avec
loutil al.exe utilis avec la syntaxe suivante :
>al.exe /out:policy.1.0.Foo2.dll /version:1.0.0.0 /keyfile:Cles.snk
/linkresource:Foo2.config
Loption /out prcise le nom quaura le module contenant le manifeste. Ce nom est form
par des rgles bien prcises.
1.0 indiquera au CLR que cette stratgie dditeur sapplique aux demandes de lassemblage Foo2.dll avec la version 1.0. Seul les numros majeurs et mineurs sont prciss
ici ;
Foo2 indiquera au CLR que cette stratgie dditeur sapplique aux demandes de lassemblage Foo2.dll.
Loption /keyfile prcise le fichier contenant la paire de cls publique/priv qui signera
lassemblage de stratgie dditeur. Ces cls doivent tre les mmes que celles qui signent
Foo2.dll pour prouver au CLR que cest bien le mme diteur qui fournit Foo2.dll et sa
stratgie dditeur. De plus, en tant quassemblage partag qui doit tre stock dans le GAC,
lassemblage de stratgie dditeur doit avoir un nom fort et doit donc tre sign.
69
Foo2.config devient un module de lassemblage de stratgie dditeur grce cette option. Concrtement, le fichier Foo2.config nest pas inclus physiquement dans le module
policy.1.0.Foo2.dll mais est inclus logiquement dans lassemblage policy.1.0.Foo2.
Cab Projet : cre un fichier cab, qui rassemble les fichiers installer et les compresse dans
un fichier .cab (i.e une archive cab). Une section du prsent chapitre est consacre au dploiement par fichier .cab.
Smart Device Cab Projet : cre un fichier cab spcialement adapt au dploiement sur
Windows CE sur des machines type Pocket PC ou Smart Phone.
Merge Module Project : cre un module de dploiement. Un tel module peut tre intgr
dans dautres projets de dploiement. Ainsi un mme module de dploiement peut tre
commun plusieurs projets de dploiement. La notion de modules de dploiement nest
pas la mme que la notion de modules dassemblages.
Setup Project : Cre un projet de dploiement exploitant la technologie MSI. Cette technologie permet deectuer des actions en plus dinstaller des fichiers. Une section est consacre
la technologie MSI un peu plus loin.
Web Setup Project : Permet de dployer un projet dapplication web en installant les fichiers dans des rpertoires virtuels IIS. Le dploiement dapplication web ASP.NET est un
sujet particulier. Plus dinformation ce sujet sont disponibles en page 871.
Setup Wizard : Ceci est une aide pas pas au moyen dun assistant, pour construire un
projet de dploiement dun de ces types.
Depuis sa premire version la plateforme .NET prsente une technologie nomme No Touch
Deployment (NTD) spcialement conue pour le dploiement dapplication partir dinternet.
Cette technologie est toujours supporte par la version 2.0. Elle na pas volue car Microsoft a
prfr miser sur une technologie nomme ClickOnce qui est beaucoup plus adapte aux fortes
contraintes dun dploiement internet (scurit, bande passante, mises jour etc). Ces technologies font chacune lobjet dune section de ce chapitre.
Il ny a pas de type de projets de dploiement spciaux aux technologies NTD et ClickOnce. NTD
se gre partir de fichier de configuration et de dploiement XCopy sur le serveur permettant
le tlchargement de lapplication. En revanche, nous verrons que Visual Studio 2005 prsente
des facilits pour permettre le dploiement dune application avec la technologie ClickOnce.
Pour cela, il sut que lapplication soit reprsente par un projet de type application fentre
Windows Forms ou application console.
70
Les dploiements lourds qui impactent profondment le systme dexploitation en installant des assemblages dans le GAC, en enregistrant des classes COM dans la base des registres,
qui sont utilisables par plusieurs utilisateurs de la machine, qui requirent des droits levs type administrateurs pour leur excution etc. Clairement, seule la technologie MSI est
adapte au dploiement de ces applications. De part leur taille et pour des raisons de scurit, ce type de dploiement se fait parfois partir dun CD plutt que par lintermdiaire
dun rseau. Lutilisateur doit payer le prix dun tel dploiement : une gestion de scurit
primaire avec la technologie authenticode (si dploy partir dun rseau), dicult de
mises jour, exposition aux consquences de lenfer des DLLs, dlais dus la livraison dun
CD etc.
Les dploiements lgers dapplication purement .NET qui impactent peu le systme dexploitation. Les quatre techniques cab, XCopy, ClickOnce et NTD peuvent tre envisages pour
ce type de dploiement. ClickOnce est en gnral privilgier de part ces fonctionnalits
avances notamment quant la gestion de la scurit, des mises jour et de la bande passante. Mis part la compatibilit ascendante, il ny a pas de cas o la technologie NTD est
prfrable ClickOnce. Les techniques cab et XCopy prsentent lavantage dtre trs simples
tant pour le dveloppeur que pour lutilisateur en gnral habitu au copier/coller de fichiers. Aussi, ces deux techniques ne sont pas dnues dintrt quand il sagit de raliser des
dploiements trs simples. Dans ce cas, on prfrera srement la technologie cab puisquun
seul fichier est toujours plus facile acheminer de lditeur lutilisateur que plusieurs.
ClickOnce
MSI
Installation de fichiers.
X
X
Gestion de ODBC.
71
Self rparation
72
73
</IMPLEMENTATION>
</CODE>
</MSICD::NATIVECODE>
</SOFTPKG>
Les pages de proprits, accessibles par maj F4. Elles permettent essentiellement de grer les
configurations du projet setup (Debug /Release...) et dajouter une signature Authenticode au
projet.
74
La fentre de proprit du projet setup qui vous permet de configurer de nombreux attributs
associs au projet, comme son nom, sa culture ou le nom de lditeur, comme le montre la
Figure 3 -7.
75
comme sur la Figure 3 -8. Comprenez bien que la plupart des applications .NET ne devraient pas
utiliser la base des registres, aussi faites attention vos modifications.
76
Le manifeste de lapplication : Cest un fichier XML dextension .manifest qui rfrence lensemble des fichiers de lapplication et qui dfinit lensemble des permissions CAS ncessaires pour son excution. Son nom est la concatnation du nom de lassemblage contenant
le point dentre de lapplication (extension .exe comprise) avec lextension .manifest.
Ces deux fichiers manifestes doivent tre signs numriquement par la technologie authenticode
pour pouvoir tre utiliss. Cela se traduit par une prsence dun lment <signature> dans
77
chacun deux. La technologie authenticode est dcrit en page 230. Vous pouvez aussi consulter
larticle ClickOnce Deployment and Authenticode des MSDN.
Lorganisation des fichiers dans le rpertoire de dploiement est la suivante :
.\MyApp_1_0_0_0.application
// Manifeste de d
eploiement.
.\MyApp_1_0_0_0\MyApp.exe.manifest
// Manifeste de lapplication.
.\MyApp_1_0_0_0\MyApp.exe.deploy
// Fichiers de lapplication ...
.\MyApp_1_0_0_0\MyAppClassLib.dll.deploy // ... avec lextension .deploy.
.\MyApp_1_0_0_0\en-US\MyApp.resources.dll.deploy
...
On remarque quil existe un sous rpertoire par version de lapplication. Le nom de ce sous rpertoire ne contient pas ncessairement dindication sur le numro de version bien que ceci soit
une bonne pratique. Chacun de ces sous rpertoires contient le manifeste de lapplication relatif
la version ainsi que tous les fichiers dployer pour cette version. Une extension .deploy est
rajoute au nom de chacun de ces fichiers.
Le manifeste de dploiement est stock dans le rpertoire de dploiement. Puisquil rfrence
un manifeste dapplication qui est relatif une version de lapplication, il est lui-mme relatif
une version de lapplication. Aussi, il est judicieux que son nom soit la concatnation du nom
de lassemblage contenant le point dentre de lapplication (extension .exe non comprise) avec
une indication sur le numro de version suivie de lextension .application. Ainsi, plusieurs
manifestes de dploiement relatifs plusieurs versions dune mme application peuvent cohabiter dans le mme rpertoire de dploiement.
Utiliser le menu Properties Publish de Visual Studio 2005 sur un projet de type console ou
application Windows Forms.
Utiliser loutil fentr mageui.exe (mage pour Manifest Generation and Editing Tool) disponible dans le rpertoire SDK de linstallation de Visual Studio 2005.
Utiliser loutil en ligne de commande mage.exe disponible dans le mme rpertoire que
mageui.exe.
Loutil mage.exe est particulirement utile si vous souhaitez intgrer la cration des fichiers manifestes dans un script MSBuild. Loutil mageui.exe est surtout pdagogique car il permet dobtenir une vue prcise et comprhensible du contenu de chacun des fichiers manifestes. Lutilisation de ces outils est dcrite dans larticle Walkthrough : Deploying a ClickOnce Application
Manually des MSDN. Hormis ces deux raisons, nous vous conseillons davoir recours Visual
Studio 2005 qui prsente linterface suivante :
Application Files... : Permet de choisir lensemble des fichiers qui constituent lapplication.
Lassemblage excutable gnr par le projet courant fait automatiquement partie de cet
ensemble. Les assemblages rfrencs par ce projet, les assemblages satellites gnrs par ce
projet ainsi que tous autres fichiers relatifs ce projet font aussi partie de cet ensemble. Mis
78
part lassemblage excutable qui est forcment requis, chaque fichier peut tre marqu
comme prrequis, requis ou optionnel. Dans le premier cas, le fichier doit tre disponible
avant linstallation de lapplication. Dans le second cas le fichier doit tre tlcharg lors de
linstallation de lapplication. Dans le troisime cas le fichier doit faire partie dun groupe
de fichiers optionnels. Ces groupes permettent un tlchargement la demande des parties
fonctionnelles dune application. Nous revenons sur ce point un peu plus loin.
Prerequisites... : Permet de choisir les prrequis de linstallation de lapplication. Vous pouvez choisir notamment le framework .NET 2.0, SQL Server 2005 Express Edition mais aussi
nimporte quel application, fichier ou framework installer. Visual Studio 2005 vous permet
de gnrer un fichier setup.exe que la littrature anglo saxonne nomme bootstrapper. ClickOnce proposera au client de tlcharger et dexcuter le bootstrapper avant linstallation
de lapplication si sa machine na pas les prrequis. Vous pouvez spcifier do le bootstrapper doit tlcharger chaque prrequis (site de dploiement, site ociel du composant
etc). Techniquement lutilisateur na pas besoin dtre administrateur de la machine pour
excuter le bootstrapper. En pratique, il a souvent besoin de ltre puisque le bootstrapper doit
gnralement installer des composants qui requirent une installation avec la technologie
MSI. Plus dinformation concernant le bootstrapper sont disponible dans larticle Use the
Visual Studio 2005 Bootstrapper to Kick-Start Your Installation de Sean Draine consultable en ligne dans le MSDN Magazine doctobre 2004.
Updates... : Permet de positionner une politique de mise jour de lapplication. Une section est consacre aux mises jour un peu plus loin.
79
Options... : Permet de positionner les paramtres de lapplication tels que le nom de lditeur, du produit, la gnration dune page web daide au tlchargement, la culture cible
du dploiement, lutilisation de lextension .deploy etc.
Publish Wizard... et Publish Now : permet de publier lapplication, cest--dire de gnrer les deux fichiers manifestes et de construire larborescence de fichier que nous avons
prsent. Il faut publier votre application dans un rpertoire de dploiement spcifique
pour chaque culture supporte. Vous pouvez choisir si le rpertoire de dploiement est
un rpertoire virtuel IIS, un rpertoire FTP ou un rpertoire Windows classique que vous
pouvez alors dupliquer sur un CD.
Cette interface vous permet aussi de choisir si lapplication est disponible oine. Dans ce cas
un raccourci vers lapplication est install dans le menu des programmes et lutilisateur na pas
besoin dtre connect internet pour la lancer.
80
Contrairement la technologie MSI, vous ne pouvez pas personnaliser le processus de dploiement dune application ClickOnce. Concrtement, vous ne pouvez pas fournir de dialogues demandant lutilisateur des informations comme le rpertoire o installer lapplication. Cette
contrainte est essentielle pour garantir une scurit optimale.
Installation la demande
Nous avons vu quun fichier dune application peut tre marqu comme optionnel. Dans ce cas,
il fait partie dun groupe de fichiers optionnels. Tous les fichiers dun tel groupe sont tlchargs explicitement par le code de lapplication lorsque qu lexcution elle a besoin dun de ces
fichiers pour la premire fois. Cest linstallation la demande.
Le framework reprsent par lespace de nom standard System.Deployment permet au code
de votre application de tlcharger un groupe de fichier de lapplication non encore install. Ce code doit tre excut lors du dclenchement dun vnement type AppDomain.
AssemblyResolve ou AppDomain.ResourceResolve. On parle parfois dinstallation transactionnelle dun tel groupe de fichiers puisque soit ils sont tous installs soit aucun nest install.
Lexemple suivant expose un assemblage excutable MyApp qui rfrence un assemblage bibliothque MyLib. Supposons que dans un projet ClickOnce de dploiement, MyLib fasse partie dun
groupe de fichiers optionnels nomm MyGroup. Le code de la mthode AssemblyResolveHandler() est dclench lors de la premire excution de MyApp, lorsque MyLib nest pas trouv. Ce
code tlcharge les fichiers du groupe MyGroup puis rcupre et retourne lassemblage MyLib.
Notez la ncessit de la prsence de la classe Foo. Si nous ne nous servions pas dune classe
intermdiaire pour invoquer MyClass, le compilateur JIT compilerait la classe Program avant
davoir excut la mthode Main(). Lvnement AssemblyResolve serait donc dclench avant
mme que la mthode AssemblyResolveHandler() ait pu tre abonne :
Exemple 3-16 :
MyLib.cs
MyApp.cs
using System ;
using System.Reflection ;
using System.Deployment.Application ;
class Program {
public static void Main() {
AppDomain.CurrentDomain.AssemblyResolve += AssemblyResolveHandler ;
Console.WriteLine("Bonjour de MyApp.") ;
Foo.FooFct() ;
}
static Assembly AssemblyResolveHandler(object sender,
ResolveEventArgs args) {
if ( ApplicationDeployment.IsNetworkDeployed ) {
// La propriete CurrentDeployement retourne null
81
Aprs le dmarrage de lapplication pour un dmarrage rapide mais une prise en compte
des mises jour qu la prochaine excution.
Ou avant le dmarrage de lapplication pour sassurer que les utilisateurs excutent toujours
la version la plus rcente au prix dun dmarrage parfois ralenti.
Lors de la mise jour dune application la technologie ClickOnce ne tlcharge que les fichiers
qui ont chang. En outre lensemble des permissions accordes lors de linstallation dune application sera automatiquement hrit par les futures mises jour. Enfin, ds lors quune application a t au moins une fois mise jour sur une machine la technologie ClickOnce se met
stocker sur cette machine les donnes ncessaires pour ventuellement revenir la version prcdente. Ainsi, lutilisateur ne prend pas de risques en mettant jour une application puisquil a
la possibilit dans le menu de dsinstallation de programme de revenir la version prcdente.
82
83
de lapplication].exe. Cest cet assemblage excutable VSHost qui est lanc par Visual Studio
2005 lorsque vous demandez de dboguer lapplication. Le code de cet assemblage positionne
lensemble des permissions puis charge et excute lapplication.
Enfin, sachez que lintellisense de Visual Studio 2005 tient compte de lensemble des permissions
dfini dans le menu Security. Concrtement, il grise les noms des classes et mthodes du framework qui requirent des permissions non accordes.
84
Lors de la mise jour dune application, les anciens fichiers de donnes sont copis dans le nouveau rpertoire de donnes. Si ClickOnce tlcharge une nouvelle version dun fichier de donnes, celle-ci crase lancienne version. Cependant, pour viter la perte de donnes lancienne
version est alors copie dans un sous rpertoire inc.
Exemple 3-18 :
using System.Reflection ;
using System.Windows.Forms ;
class Program {
static void Main() {
Assembly asm = Assembly.GetExecutingAssembly() ;
MessageBox.Show("Mon CodeBase est:" + asm.CodeBase) ;
}
}
Louverture dinternet explorer sur cette URL est illustre par la figure suivante :
85
Le cache de tlchargement
Lorsquun assemblage est tlcharg pour la premire fois partir du web, il est automatiquement stock dans le cache de tlchargement (download cache en anglais). Le cache de tlchargement est un rpertoire entirement gr par le CLR. Le cache de tlchargement est indpendant du cache dinternet explorer ainsi que des dirents caches de la technologie ClickOnce. Les
avantages du cache de tlchargement sont les suivant :
Il vite de devoir tlcharger un assemblage accessible partir du web chaque fois quil
doit tre excut, do la notion de cache.
Il permet disoler physiquement les assemblages tlchargs du web des autres assemblages.
Le cache de tlchargement prsente les deux dirences suivantes avec le rpertoire GAC :
Le rpertoire GAC est global une machine. Il ne peut y avoir quun rpertoire GAC par
machine. En revanche, le CLR fait en sorte quil existe un cache de tlchargement par utilisateur dune machine. Ainsi, il peut y avoir plusieurs caches de tlchargement sur une
machine.
Le CLR accorde une plus grande confiance (i.e accorde plus de permissions) aux assemblages
contenus dans le rpertoire GAC quaux assemblages contenus dans le cache de tlchargement.
Enfin, sachez que loption /ldl de loutil gacutil.exe vous permet de visualiser le contenu du
cache de tlchargement :
C:>gacutil.exe /ldl
Microsoft (R) .NET Global Assembly Cache Utility. Version 2.0.XXXXX
Copyright (C) Microsoft Corporation 1998-2002. All rights reserved.
The cache of downloaded files contains the following entries:
Foo.exe, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null,
Custom=null
Number of items = 1
Plus dinformation sur la technologie No-Touch Deployment sont disponibles dans larticle NoTouch Deployment in the .NET Framework des MSDN.
86
Cela signifie que lors de linstallation de lapplication .NET, le framework .NET ne sera pas install sur la machine cible. Toutes les applications .NET ont besoin que le framework .NET soit
install sur la machine pour pouvoir tre excutes.
Lavertissement nous apprend que pour installer le framework .NET sur la machine cible, il faut
excuter le fichier dotnetfx.exe. Ce fichier peut tre redistribu librement.
Ce fichier fait une taille denviron 23 Mo. Il existe un tel fichier par version de .NET. Bien
entendu, il faut que le framework .NET 2.0 soit install sur une machine avant linstallation
dune application .NET, quelle que soit la technologie de dploiement utilise. Dans le cas dun
dploiement avec la technologie ClickOnce nous avons vu que vous pouvez prciser en prrequis
la prsence du framework .NET. Sinon, il faut soit fournir dotnetfx.exe par exemple sur le CD
distribu soit prvoir un lien pour permettre au client de le tlcharger.
4
Le CLR
(le moteur dexcution
des applications .NET)
Le CLR (Common Langage Runtime en anglais et moteur dexcution en franais) est llment central de larchitecture de la plateforme .NET. Le CLR est une couche logicielle qui gre lexcution le code des applications .NET. Le mot grer recouvre en fait une multitude dactions
ncessaires au bon droulement de lapplication. Listons en quelques-unes :
88
Les domaines dapplication hbergs dans un mme processus Windows partagent les ressources du processus telles que le CLR, les types de base de .NET, lespace dadressage ou les
threads.
Lorsquun assemblage excutable est dmarr, le CLR cre automatiquement un domaine dapplication par dfaut pour lexcuter. Chaque domaine dapplication a un nom et le nom du domaine dapplication par dfaut est le nom du module principal de lassemblage lanc (extension
.exe comprise).
Si un mme assemblage est charg par plusieurs domaines dapplication dans un mme processus, il peut y avoir deux comportements :
soit le CLR va charger plusieurs fois lassemblage, une fois pour chaque domaine dapplication du processus.
soit le CLR va charger une seule fois lassemblage hors de tous domaines dapplication du
processus. Lassemblage pourra nanmoins tre utilis par chaque domaine dapplication
du processus. On dit que lassemblage est domain neutral.
On verra un peu plus loin dans ce chapitre que le choix de ce comportement est configurable.
Le comportement par dfaut est de charger plusieurs fois lassemblage.
89
Un domaine dapplication peut tre dcharg indpendamment des autres domaines applications.
Un domaine dapplication na pas daccs direct aux assemblages et aux objets des autres
domaines application.
Un domaine dapplication peut avoir sa propre stratgie de gestion dexception. Du moment quil ne laisse pas sortir une exception hors de ses frontires, les problmes dun
domaine dapplication nont pas dincidence sur les autres domaines dapplication du mme
processus.
Chaque domaine dapplication peut dfinir sa propre stratgie de scurit pour paramtrer
le mcanisme CAS quutilise le CLR pour accorder des permissions au code dun assemblage.
Chaque domaine dapplication peut dfinir ses propres rgles pour paramtrer le mcanisme quutilise le CLR pour localiser ses assemblages avant de les charger.
La classe System.AppDomain
Une instance de la classe System.AppDomain est une rfrence vers un domaine dapplication du
processus courant. La proprit statique CurrentDomain{get;} de cette classe vous permet de
rcuprer une rfrence vers le domaine dapplication courant. Lexemple suivant illustre lutilisation de cette classe pour numrer les assemblages contenus dans le domaine dapplication
courant :
Exemple 4-1 :
using System ;
using System.Reflection ; // Pour la classe Assembly
class Program {
static void Main() {
AppDomain curAppDomain = AppDomain.CurrentDomain;
foreach ( Assembly assembly in curAppDomain.GetAssemblies() )
Console.WriteLine( assembly.FullName ) ;
}
}
90
(optionnel) Les rgles de scurit qui paramtrent le mcanisme CAS sur ce domaine dapplication (par un objet de type System.Security.Policy.Evidence).
(optionnel) Des informations qui paramtrent le mcanisme de localisation des assemblages de ce domaine dapplication utilis par le CLR (par un objet de type System.
AppDomainSetup).
ConfigurationFile : Cette proprit rfrence un ventuel fichier de configuration du domaine dapplication. Ce fichier doit tre au format XML et contient des informations sur
les rgles de versionning et/ou la localisation des assemblages.
Maintenant que vous savez crer un domaine dapplication, nous pouvons prsenter comment
charger et excuter un assemblage excutable dans un domaine en appelant la mthode System.AppDomain.ExecuteAssembly(). Lassemblage doit tre de type excutable et son excution
commence son point dentre. Cest le thread qui appelle ExecuteAssembly() qui excute le
code de lassemblage charg. Cette constatation illustre le fait quun thread peut indiremment
traverser les frontires entre domaines dapplication.
Voici un exemple de code en C . Le premier code est lassemblage qui va tre charg dans un
domaine dapplication par lassemblage produit par la compilation du second code :
Exemple 4-2 :
AssemblyACharger.exe
using System ;
using System.Threading ;
public class Program {
public static void Main() {
Console.WriteLine(
"Thread:{0} Vous avez le bonjour du domaine : {1}",
Thread.CurrentThread.Name,
AppDomain.CurrentDomain.FriendlyName) ;
}
}
Exemple 4-3 :
using System ;
using System.Threading ;
public class Program {
public static void Main() {
AssemblyChargeur.exe
91
92
NouveauDomaine
Soyez conscient que cette possibilit dinjecter du code dans un domaine dapplication peut
provoquer la leve dune exception de scurit si vous navez pas les droits susants.
Description
AssemblyLoad
AssemblyResolve
DomainUnload
ProcessExit
ReflectionOnlyPreBindAssemblyResolve
ResourceResolve
TypeResolve
UnhandledException
Certains de ces vnements peuvent tre utiliss pour remdier au problme qui la dclench.
Lexemple suivant illustre comment exploiter lvnement AssemblyResolve pour faire en sorte
de charger un assemblage partir dun emplacement qui na pas t pris en compte par le mcanisme de localisation des assemblages du CLR :
93
Exemple 4-5 :
using System ;
using System.Reflection ; // pour Assembly
public class Program {
public static void Main() {
AppDomain.CurrentDomain.AssemblyResolve += AssemblyResolve ;
Assembly.Load("AssemblyACharger.dll");
}
public static Assembly AssemblyResolve(object sender,
ResolveEventArgs e) {
Console.WriteLine("Assemblage {0} non trouv
e : ", e.Name) ;
return Assembly.LoadFrom(@"C:\AppDir\CetAssemblyACharger.dll");
}
}
Si la seconde tentative de chargement marche, aucune exception nest lance. Le nom de lassemblage charg la seconde tentative nest pas ncessairement le mme que celui que lon a
essay de charger la premire tentative.
En page 519 nous prsentons un programme exploitant lvnement UnhandledException.
En page 80 nous expliquons que les classes de lespace de nom System.Deployment peuvent avoir
recours au vnements AssemblyResolve et ResourceResolve afin de tlcharger dynamiquement un groupe de fichier lorsquune application dploye avec la technologie ClickOnce en a
besoin pour la premire fois.
94
Cet exemple prsente le cas simple car nous stockons un entier qui est un type valeur connu
de tous les domaines dapplication. La technologie .NET Remoting qui fait lobjet du chapitre 22
permet de partager des objets entre domaines dapplication dune manire plus volue mais
plus complexe.
La DLL mscorsvr.dll contient une version du CLR spcialement optimise pour les machines ayant plusieurs processeurs ( svr est employ pour serveur ).
La DLL mscorwks.dll contient une version du CLR spcialement optimise pour les machines ayant un seul processeur ( wks est employ pour workstation ou station de
travail ).
Ces deux DLLs ne sont pas des assemblages. En consquence, elles ne contiennent pas de code
IL, et elles ne peuvent tre analyses avec loutil ildasm.exe. Chaque processus excutant une
ou plusieurs applications .NET contient une de ces deux DLLs. On dit que le processus hberge
le CLR. Nous allons expliquer ici comment le chargement dans le processus dune de ces DLLs
se droule.
Lassemblage mscorlib.dll
Une autre DLL joue un rle prpondrant dans lexcution des applications .NET. Cest la DLL
mscorlib.dll qui est en fait lunique module de lassemblage du mme nom. Cet assemblage
contient les implmentations des types de base du framework .NET (comme la classe System.
String, la classe System.Object ou la classe System.Int32). Cet assemblage est rfrenc par
tous les assemblages .NET. Cette rfrence est cre automatiquement par tous les compilateurs qui produisent du code IL. Il est intressant danalyser lassemblage mscorlib avec loutil
ildasm.exe. Nous prcisons que lassemblage mscorlib rside lexcution hors de tous domaines dapplication. En outre, il ne peut tre charg/dcharg quune fois durant la vie dun
processus.
95
Une fois le CLR charg dans le processus, lhte du moteur dexcution a dautres responsabilits telles que la dcision prendre lorsquune exception nest pas rattrape. La figure suivante
illustre les direntes couches de cette architecture. On voit que le CLR et lhte schangent
des informations grce une API :
CLR
Host API
Hte du moteur dexcution
Win32 API
Systme dexploitation Windows
Lhte du moteur dexcution des applications Console et Winform : Lassemblage excutable est charg dans le domaine dapplication par dfaut. Lorsquun assemblage est charg
implicitement, il est charg dans le mme domaine dapplication que lassemblage qui le
sollicite. En gnral ce type dapplication na pas utiliser dautres domaines dapplication
que le domaine dapplication par dfaut.
Lhte du moteur dexcution Microsoft Internet Explorer : Par dfaut, il cre un domaine dapplication par site web visit. Ainsi les assemblages de dirents sites peuvent
sexcuter avec dirents niveaux de scurit. Le CLR nest charg que lorsque Internet Explorer a besoin dexcuter un assemblage pour la premire fois. Plus dinformations au sujet
de cet hte sont disponibles en page 84.
Lhte du moteur dexcution de SQL Server 2005 : Les requtes vers la base peuvent tre
crites en langage IL. Le CLR nest charg que la premire fois quune telle requte doit
tre eectue. Un domaine dapplication est cr pour chaque couple utilisateur/base de
donnes. Dans le chapitre courant, nous aurons loccasion de revenir sur les possibilits et
proprits de cet hte trs particulier introduit avec .NET 2.0.
96
.NET sont dans un sous rpertoire portant le numro de version. Comme il existe un rpertoire
par version du framework .NET installe sur la machine, il peut y avoir aussi plusieurs versions
des DLLs mscorsvr.dll et mscorwks.dll sur la mme machine. Cependant une seule version
du CLR peut tre charge et hberge par chaque processus.
Le fait davoir potentiellement plusieurs versions du CLR entrane lexistence dune petite
couche logicielle qui prend en paramtre la version dsire du CLR et la charge. Ce code est
appel cale (shim en anglais) et est stock dans la DLL mscoree.dll (MSCOREE veut dire Microsoft
Component Object Runtime Execution Engine).
Il ne peut y avoir quune seule DLL cale par machine. La cale est directement appele par
lhte du moteur dexcution par la fonction CorBindToRuntimeEx(). La DLL mscoree.dll
contient des interfaces et des classes COM. La fonction CorBindToRuntimeEx() cre un objet
COM, instance de la classe COM CorRuntimeHost. Cest cet objet qui va sinterfacer avec le
CLR. Pour manipuler cet objet la fonction CorBindToRuntimeEx() retourne linterface COM
ICLRRuntimeHost.
Lappel CorBindToRuntimeEx() pour crer lobjet COM sinterfaant avec le CLR, transgresse
des rgles fondamentales de COM : il ne faut pas appeler la fonction CoCreateInstance() pour
crer un objet COM. De plus, les appels aux mthodes AddRef() et Release() sur linterface
ICLRRuntimeHost nont pas deet.
PwszVersion : Le numro de version du CLR sous la forme dune chane de caractres commenant par v (exemple "v2.0.50727"). Si cette chane nest pas prcise (i.e si on passe
un pointeur nul), la version la plus rcente disponible du CLR sera alors choisie.
PwszBuildFlavor : Ce paramtre indique si lon souhaite charger le CLR pour workstation (mscorwks.dll) en prcisant la chane de caractres "wks" ou le CLR pour serveur
(mscorsvr.dll) en prcisant la chane de caractres "svr". Si vous navez quun seul processeur, le CLR pour workstation sera charg quelle que soit la valeur de ce paramtre. Microsoft
prvoit dautres types de CLR, donc dautres valeurs pour ce paramtre.
97
Lassemblage mscorlib a un traitement spcial puisquil est charg dune manire neutre
une seule fois, quelle que soit la valeur de ce paramtre.
rclsid : Le classe ID (CLSID) de la classe COM (coclass) qui implmente linterface que
vous cherchez. Seules les valeurs CLSID_CorRuntimeHost, CLSID_CLRRuntimeHost ou null
sont acceptes. La deuxime valeur est apparue avec la version 2.0 de .NET car de nouvelles
fonctionnalits dues lhbergement du CLR dans le processus de SQL Server 2005 ont ncessit une nouvelle interface et une nouvelle classe COM.
pwszBuildFlavor : Linterface ID (IID) de linterface COM dont vous avez besoin. Seules les
valeurs IID_CorRuntimeHost, IID_CLRRuntimeHost ou null sont acceptes
ppv : Un pointeur vers linterface COM retourne, de type ICorRuntimeHost ou IClrRuntimeHost selon ce qui a t demand.
La cale ne fait que charger le CLR dans le processus. Le cycle de vie du CLR est contrl par la partie non gre du code de lhte du moteur dexcution grce linterface COM ICLRRuntimeHost
98
renvoye par la fonction CorBindToRuntimeEx(). Cette interface prsente entre autres deux mthodes Start() et Stop() dont les noms illustrent leurs actions.
99
MyManagedLib.cs
namespace MyProgramNamespace {
public class MyClass {
public static int MyMethod(string s) {
System.Console.WriteLine(s) ;
return 0 ;
}
}
}
Vous pouvez facilement invoquer la mthode Main() partir de notre hte comme ceci :
Exemple 4-9 :
...
pClrHost->Start() ;
DWORD retVal=0 ;
hr = pClrHost->ExecuteInDefaultAppDomain(
L"C:\\test\\MyManagedLib.dll", // Chemin + Asm.
L"MyProgramNamespace.MyClass", // Nom entier du type.
L"MyMethod",
// Nom de la m
ethode elle doit avoir
//
la signature int XXX(string).
L"Hello from host!",
// Chane de caract`
eres en argument.
&retVal) ;
// Valeur OUT de retour.
pClrHost->Stop() ;
...
100
IValidator permet de valider lentte PE/COFF des assemblages (utilis notamment par
loutil peverify.exe).
IMetaDataConverter permet de convertir les mtadonnes COM (i.e tlb/tlh) en mta donnes .NET (utilis notamment par loutil tlbexp.exe).
Pour savoir quelles sont les mthodes prsentes par ces interfaces, il sut dobserver les fichiers
mscoree.h, ivalidator.h et gchost.h. Pour obtenir une de ces interfaces partir de votre interface IClrRuntimeHost, il sut dutiliser la fameuse mthode QueryInterface() comme ceci :
...
ICorThreadpool * pThreadPool
= NULL ;
hr = pClrHost->QueryInterface( IID_ICorThreadpool,
(void**)&pThreadPool);
...
101
entre threads et de minimiser le nombre de pages stockes en mmoire virtuelle sur le disque
dur pour profiter au mieux de la mmoire vive disponible.
Les context switching sont normalement grs par le mcanisme de multitche premptif du rpartiteur de Windows, dcrit en page 138. Lhte du moteur dexcution de SQL Server 2005 implmente sont propre mcanisme de multitche plutt bas sur un modle de multitche coopratif.
Dans ce modle, ce sont les threads eux mmes qui dcident du moment o le processeur peut
passer un autre thread. Un avantage est que les choix de ces moments sont plus fins, car lis
la smantique des traitements. Il en rsulte une gestion globale plus ecace des threads. Un
autre avantage est que ce modle est adapt lutilisation du mcanisme de fibre de Windows
(fiber en anglais).
Une fibre est un thread logique qualifi aussi de thread lger. Un mme thread physique Windows peut enchaner lexcution de direntes fibres. Lavantage est que le passage dune fibre
une autre est une opration beaucoup moins coteuse que le context switching. En contrepartie,
lorsque le mode fibre est utilis par lhte du moteur dexcution de SQL Server 2005, on perd
la garantie dune relation biunivoque entre les threads physiques Windows et les threads grs
.NET. Un mme thread gr nest plus forcment excut par le mme thread physique durant
son toute son existence. Il faut donc absolument saranchir de tout type danit entre ces
deux entits. Parmi les types danits possibles entre un thread gr et son thread physique
sous-jacent on peut citer les Thread Local Storage, la culture courante et les objets de synchronisation Windows qui drivent de la classe WaitHandle style mutex, smaphore ou vnement.
Sachez que vous avez la possibilit de communiquer lhte du moteur dexcution le commencement et la fin dune rgion de code qui exploite ce type danit avec les mthode BeginThreadAffinity() et EndThreadAffinity() de la classe Thread. Ce dernier saura alors dsactiver temporairement le mode fibre, auquel on attribut un facteur doptimisation de 20% des
performances. (Note : Dans la version actuelle de SQL Server 2005, le mode fibre a t retir car les
ingnieurs de Microsoft ntaient pas certains de la fiabilit de ce mode. Ce mode sera rintroduit dans
les versions ultrieures de ce produit).
Le stockage des pages mmoires est normalement gr par le mcanisme de mmoire virtuelle
de Windows dcrit en page 134. Lhte du moteur dexcution de SQL Server 2005 sintercale
entre les demandes mmoire du CLR et ce mcanisme afin de profiter au mieux de la mmoire
vive disponible. Cela permet aussi dobtenir un comportement prvisible lorsquune demande
dallocation mmoire du CLR choue. Comme nous le verrons plus tard, cette particularit est
essentielle pour assurer la fiabilit de serveurs tels que SQL Server 2005.
Toutes ces nouvelles possibilits sont accessibles grce une API qui permet au CLR et son
hte de dialoguer. Une trentaine de nouvelles interfaces ont t prvues. Elles sont listes un
peu plus bas. Il incombe lhte de fournir un objet qui implmente linterface IHostControl
au moyen de la mthode ICLRRuntimeHost.SetHostControl(IHostControl*). Cette interface
prsente la mthode GetHostManager(IID,[out]obj). Le CLR appelle cette mthode pour obtenir un objet de lhte auquel il va dlguer une responsabilit telles que la gestion des threads
ou le chargement des assemblages. Plus dinformation ce sujet sont disponibles en analysant
les interfaces suivantes dans le fichier mscoree.h.
102
Responsabilit
Chargement
blages
des
Interfaces
par lhte.
assem-
implmentes
IHostAssemblyManager
IHostAssemblyStore
ICLRAssemblyReferenceList
ICLRAssemblyIdentityManager
Scurit
IHostSecurityManager
IHostSecurityContext
ICLRHostProtectionManager
IHostPolicyManager
ICLRPolicyManager
Gestion de la mmoire
IHostMemoryManager
IHostMalloc
ICLRMemoryNotificationCallback
Ramasse-miettes
IHostGCManager
ICLRGCManager
Threading
IHostTaskManager
Task
Pool de threads
IHostThreadPoolManager
Synchronisation
IHostSyncManager IHostCriticalSection
IHostManualEvent IHostAutoEvent
IHostSemaphore
ICLRSyncManager
I/O Completion
IHostIoCompletionManager
ICLRIoCompletionManager
ICLRTaskManager ICLRTask
ICLRDebugManager
Dbogage
vnements du CLR
IHost-
IActionOnCLREvent
ICLROnEventManager
103
Intelligent dans le sens o lorsque le CLR ne trouve pas un assemblage dans un rpertoire,
il applique un algorithme qui lui permet, par exemple, daller chercher dans les sous rpertoires qui ont le mme nom que lassemblage. Intelligent aussi dans le sens o si une
application marchait mais ne marche plus cause dun assemblage qui nest plus localisable,
on puisse revenir trs simplement en arrire.
104
Lutilisation dune des surcharges de la mthode AppDomain.Load() qui charge un assemblage dans le domaine dapplication sur lequel est appele la mthode.
Le chargement implicite par le CLR dun assemblage. Ceci est dcrit dans la prochaine section lorsque nous expliquons comment le CLR rsout les types.
Remarquez que ces deux mthodes ne fonctionnent pas selon la philosophie .NET puisquelles
prennent en argument un chemin vers un fichier et non le nom dun assemblage. Il est prfrable de ne jamais utiliser LoadFrom() qui peut toujours tre remplace par Load(). En revanche
lutilisation trs simple de la mthode AppDomain.ExecuteAssembly() peut savrer tre un raccourci ecace.
Lalgorithme de localisation
Recherche dans le rpertoire GAC
Lalgorithme va dabord aller chercher lassemblage dans le rpertoire GAC, condition que le
nom de lassemblage fourni soit un nom fort. Lors de la recherche dans le rpertoire GAC, lutilisateur de lapplication peut choisir ou non (par lintermdiaire du fichier de configuration de
lapplication) dutiliser les assemblages de stratgies dditeurs relatifs lassemblage localiser.
105
...
<dependentAssembly>
<assemblyIdentity name="Foo3" publicKeyToken="C64B742BD612D74A"
culture= "fr-FR"/>
<codebase version="3.0.0.0"
href = "http://www.smacchia.com/Foo3.dll>"/>
</dependentAssembly>
...
Vous remarquez que lURL contient le nom du module de lassemblage contenant le manifeste (en loccurrence Foo3.dll). Si lassemblage a dautres modules, comprenez bien que tous
ces modules doivent tre aussi tlchargeables cette adresse (en loccurrence http://www.
smacchia.com/).
Lorsquun assemblage est tlcharg partir du web grce llment <codebase>, il est stock
dans le cache de tlchargement. Aussi, avant de tenter de charger un assemblage partir dune
URL, fusion va consulter ce cache pour vrifier sil na pas dj t tlcharg.
Le mcanisme de probing
Si le nom fort nest pas correctement fourni ou sil est fourni mais que lassemblage nest pas
trouv dans le rpertoire GAC et si le fichier de configuration ne contient pas dlment <codebase> relatif lassemblage localiser, alors lalgorithme tente de trouver lassemblage en
sondant certains rpertoires. Cest le mcanisme de probing (qui peut se traduire par sondage en
franais) qui est expos par lexemple suivant :
Supposons que lon veuille localiser lassemblage Foo (notez quon ne fournit pas lextension du fichier).
Supposons que les sous rpertoires indiqus par llment <probing> du fichier de configuration de lapplication pour lassemblage Foo soit "Path1" et "Path2\Bin".
Supposons que le rpertoire de base de lapplication soit "C:\AppDir\".
Supposons enfin quaucune information de culture nait t fournie avec le nom dassemblage, ou quil soit de culture neutre (i.e ce nest pas un assemblage satellite).
La recherche de lassemblage se fait dans cet ordre, dans les rpertoires suivants :
C:\AppDir\Foo.dll
C:\AppDir\Foo\Foo.dll
C:\AppDir\Path1\Foo.dll
C:\AppDir\Path1\Foo\Foo.dll
C:\AppDir\Path2\Bin\Foo.dll
C:\AppDir\Path2\Bin\Foo\Foo.dll
C:\AppDir\Foo.exe
C:\AppDir\Foo\Foo.exe
C:\AppDir\Path1\Foo.exe
C:\AppDir\Path1\Foo\Foo.exe
C:\AppDir\Path2\Bin\Foo.exe
C:\AppDir\Path2\Bin\Foo\Foo.exe
Si lassemblage est un assemblage satellite (i.e il na pas une culture neutre, par exemple il a la
culture "fr-FR") la recherche se fait dans cet ordre, dans les rpertoires suivants :
106
Lvnement AppDomain.AssemblyResolve
Enfin, si lassemblage na pas t trouv aprs toutes ces tapes, le CLR dclenche lvnement
AssemblyResolve de la classe AppDomain. Les mthodes abonnes cet vnement peuvent retourne un objet de type Assembly. Cela vous permet de fournir votre propre mcanisme de
localisation dassemblage. Cette possibilit est notamment exploite par la technologie de dploiement ClickOnce pour tlcharger des groupes de fichiers la demande comme illustr en
page 80.
Llment <codebase>, dcrit dans la section prcdente, dfinit une URL partir de laquelle lassemblage localiser doit tre charg.
Voici quoi peut ressembler un fichier de configuration (notez la ressemblance avec le fichier
de configuration dune stratgie dditeur) :
107
Assembly.Load() est appele par lapplication foo pour localiser lassemblage ASM
Le
nom fourni
est-il un
nom fort ?
foo est-elle
configure pour tenir
compte des stratgies
dditeur pour
ASM ?
N
Y a-t-il
un assemblage de
stratgie dditeur dans
le GAC pour
ASM ?
ASM
avec ce nom
fort, est-il dans
le GAC ?
La version de lassemblage
chercher est modifie
selon la stratgie dditeur
O
ASM est localis dans le GAC
foo.exe.config
a-t-il un lment codeBase
avec une URL
O
pour ASM ?
N
ASM est-il
localis dans un
sous-rpertoire du rpertoire de
lOO (en tenant compte des sous-rpertoires spcifis parllment
N
probing) ?
ASM est-il
localis dans le cadre
de tlchargement ?
ASM
est-il localis
lURL spcifie ?
O
ASM est localis dans le sous-rpertoire
108
Si vous ne souhaitez pas manipuler les documents XML, sachez que ces informations peuvent
tre configures partir de loutil .NET Framework Configuration accessible par : Menu Dmarrer Panneau de configuration Outils dadministration Microsoft .NET Framework 2.0 Configuration menu configured assembly.
Soit vous comptez sur votre compilateur pour crer un lien prcoce avec le type et vous
comptez sur le CLR pour charger lassemblage qui le contient au bon moment. Cest cette
technique nomme chargement implicite que nous dtaillons ici.
Soit vous chargez explicitement lassemblage lexcution et vous crer un lien tardif avec le
type que vous souhaitez exploiter.
109
Dans les deux cas la responsabilit du CLR incombe une partie du CLR nomme chargeur de
classe (class loader). Le chargement implicite dun assemblage A est dclench lors de la premire
compilation JIT dune mthode qui utilise un type de A.
La notion de chargement implicite se rapproche conceptuellement du mcanisme de chargement des DLLs. De mme, la notion de chargement explicite se rapproche conceptuellement du
mcanisme dutilisation dun objet COM par un langage de script (notamment le mcanisme
Automation et sa fameuse interface IDispatch).
Soit vous utilisez lenvironnement Visual Studio et il faut utiliser le menu Reference AddReference. Rappelons que lenvironnement Visual Studio .NET utilise dune manire implicite
le compilateur csc.exe pour produire des assemblages partir de code source C . Cette
manipulation ne fait donc que forcer Visual Studio utiliser les options de compilation
/reference /r et /lib du compilateur csc.exe.
Dans les deux cas il faut prciser lassemblage charger implicitement par son nom (fort ou
non). Les types de lassemblage B ainsi que leurs membres, qui sont rfrencs dans lassemblage A sont rfrencs dans les tables TypeRef et MemberRef de lassemblage A. Lassemblage B est
rfrenc dans la table AssemblyRef de lassemblage A. Un assemblage peut rfrencer plusieurs
autres assemblages mais il faut absolument viter les rfrencements cycliques (A rfrence B qui
rfrence C qui rfrence A). Visual Studio sait dtecter et interdit les rfrencements cycliques.
Vous pouvez aussi utiliser loutil NDepend (dcrit en page 1034) pour dtecter les rfrencements cycliques dassemblages.
Un exemple
Voici un exemple illustrant linfrastructure mise en uvre pour que le CLR puisse charger
implicitement un assemblage. Le premier code dfinit lassemblage rfrenc par lassemblage
dfini par le deuxime code.
Exemple 4-11 :
Code de lassemblage r
ef
erenc
e : AssemblageBibliotheque.cs
using System ;
namespace MesTypes{
public class UneClasse{
public static int Somme(int a,int b){return a+b;}
}
}
110
Exemple 4-12 :
Code de lassemblage r
ef
eren
cant : AssemblageExecutable.cs
using System ;
using MesTypes;
class Program{
static void Main(){
int i = UneClasse.Somme(3,4) ;
}
}
Notez lutilisation de lespace de noms MesTypes dans le code de lassemblage rfrenant. On
aurait pu aussi mettre les classes UneClasse et Program dans un mme espace de noms ou dans
lespace de noms anonyme. Dans ce cas, on aurait illustr le fait quun espace de noms peut
stendre sur plusieurs assemblages.
Il est intressant danalyser le manifeste de lassemblage rfrenant avec lutilitaire ildasm.
exe). On y voit clairement le fait que lassemblage AssemblageBibliotheque est rfrenc.
Cette rfrence est matrialise par une entre de la table AssemblyRef.
...
.assembly extern AssemblageBibliotheque{
.ver 0:0:0:0
}
...
Il est aussi intressant danalyser le code IL de la mthode Main(). On y voit clairement que
la mthode Somme() se trouve dans un autre assemblage appel AssemblageBibliotheque
(physiquement cette information est contenue dans les tables de mtadonnes MemberRef et
TypeRef) :
111
Schma rcapitulatif
Un type inconnu dans ce domaine dapplication est rencontr
par le compilateur JIT dans le code IL dune mthode
Le type est
dfini dans
le module
courant de
lassemblage
Lexception System.IO.FileNotFoundException
est lance
O
Charge le module
Charge lassemblage
112
Aucun de ces scnarios nest utilis pour la compilation du code IL en langage machine. Une
solution intermdiaire et plus performante a t mise en place. Cette solution consiste compiler le corps dune mthode en langage IL en langage machine, juste avant le premier appel
de la mthode. Cest pour cela que le mcanisme sappelle Juste temps (JIT Just In Time). La
compilation se fait juste temps pour que lexcution de la mthode en langage machine puisse
se faire.
Vrification du code IL
Avant de compiler une mthode en langage natif, le compilateur JIT eectue une srie de vrification quant la validit du corps de la mthode. Pour dcider quune mthode est valide ou
pas, le compilateur JIT vrifie lenchanement des instructions IL, value lvolution du contenu
de la pile et dtecte les accs mmoires interdits. Une exception est envoye si cette vrification
choue.
Votre code C crit en mode vrifiable est automatiquement traduit en code IL vrifiable. Cependant, en page 501 nous montrons que sous certaines conditions le compilateur C peut produire des instructions IL spciales qui ont la particularit dtre non vrifiables par le compilateur JIT.
Pour viter le cot du passage des arguments entrants et sortants, le compilateur JIT a la
possibilit dinsrer le corps dune mthode appele dans le corps de la mthode appelante.
Cette optimisation est nomme inlining. Pour que le cot de cette optimisation ne soit pas
suprieur au gain de performance, la mthode appele doit satisfaire un certains nombre
de contraintes simples vrifier. Son corps compil en IL doit avoir une taille infrieure
32 octets, elle ne doit pas rattraper dexceptions, elle ne doit pas contenir de boucles, elle ne
doit pas tre virtuelle etc.
Le compilateur JIT peut positionner null une variable locale de type rfrence aprs sa
dernire utilisation et avant la fin de la mthode. Ainsi, lobjet rfrenc aura une rfrence
de moins vers lui. Cela augmente les chances quil soit collect plus tt par le ramassemiettes. Cette optimisation peut tre localement dsactive en utilisant la mthode System.GC.KeepAlive().
113
Le compilateur JIT a la possibilit de stocker les variables locales les plus frquemment utilises directement dans les registres du processeur plutt que sur la pile. Cela constitue bien
une optimisation car laccs aux registres du processeur est significativement plus rapide.
Cette optimisation est nomme Enregistration.
Notion de pitching
La compilation des mthodes par le JIT consomme de la mmoire puisque le code natif des
mthodes est stock. Si le CLR dtecte que la mmoire devient une ressource critique pour
lexcution de lapplication, il a la possibilit de rcuprer de la mmoire en librant le code
natif de certaines mthodes. Naturellement un stub est regnr pour chacune de ces mthodes.
Cette fonctionnalit est appele pitching.
La mise en uvre du pitching est beaucoup plus complique quil ny parat. Pour tre ecace
le code des mthodes libres doit tre contigu pour viter la fragmentation de la mmoire.
Dautres problmes importants apparaissent dans les piles des threads puisquelles contiennent
des adresses mmoire rfrenant le code natif de certaines mthodes. Heureusement toutes ces
considrations sont totalement masques au dveloppeur.
Le code natif dune mthode produit par le compilateur JIT ne survit pas au dchargement du
domaine dapplication qui le contient.
114
Option de ngen.exe
Description
/show[nom assemblage
|nom repertoire]
/delete[nom assemblage
|nom repertoire]
/debug
De nombreuses autres options existent. Elles sont dcrites dans les MSDN larticle Native
Image Generator (Ngen.exe). Notamment, des nouvelles possibilits ont t ajoutes pour
supporter les assemblages exploitant la rflexion et pour automatiser la mise jour de la version
compile dun assemblage lorsquune de ses dpendances volue. Plus dinformation ce sujet
sont disponibles en ligne dans larticle NGen Revs Up Your Performance with Powerful New
Features de Reid Wilkes du numro dAvril 2005 de MSDN Magazine.
Description
115
Vous avez le choix de visualiser ces compteurs soit pour toutes les applications excutes
en mode gr jusquici depuis le boot de la machine, soit pour le processus courant. Le
choix de cette visualisation se fait avec largument de la mthode PerformanceCounterCategory.GetCounters(). Dans le premier cas il faut fournir la chane de caractres "_Global_"
en argument cette mthode. Dans le second cas il faut fournir la chane de caractres gale au
nom du processus en argument cette mthode.
Ces compteurs sont principalement utiliss pour valuer le cot de la compilation JIT. Si ce cot
vous parat trop lev, il faut prvoir lutilisation de loutil ngen.exe lors du dploiement de
lapplication. Voici un exemple dutilisation de ces compteurs (notez que le nom de lassemblage
est MonAssemblage.exe) :
Exemple 4-13 :
MonAssemblage.cs
using System.Diagnostics ;
class Program {
static void DisplayJITCounters() {
PerformanceCounterCategory perfCategory
= new PerformanceCounterCategory(".NET CLR Jit") ;
PerformanceCounter[] perfCounters ;
perfCounters = perfCategory.GetCounters("MonAssemblage") ;
foreach(PerformanceCounter perfCounter in perfCounters)
System.Console.WriteLine("{0}:{1}",
perfCounter.CounterName,
perfCounter.NextValue()) ;
}
static void f() {
System.Console.WriteLine("--> Appel `
a f().") ;
}
static void Main() {
DisplayJITCounters() ;
f() ;
DisplayJITCounters() ;
}
}
Ce programme ache :
# of Methods Jitted:2
# of IL Bytes Jitted:108
Total # of IL Bytes Jitted:108
IL Bytes Jitted / sec:0
Standard Jit Failures:0
% Time in Jit:0
116
Prcisons que les compteurs de performances sont aussi visualisables avec loutil perfmon.exe
(accessible avec Menu dmarrer Excuter... perfmon.exe).
Les fuites de mmoire subsistent malgr la prsence dun ramasse-miettes. En eet, un dveloppeur peut, par inadvertance, concevoir un programme qui garde des rfrences vers
des objets dont il na plus besoin (par exemple si une collection gonfle indfiniment).
Cependant, la cause la plus courante des fuites de mmoire dune application .NET est la
non dsallocation de ressources non gres.
Pour fixer les ides, le ramasse-miettes est une couche logicielle comprise dans le CLR, prsente
en un seul exemplaire dans chaque processus.
117
il comprendra mieux pourquoi lalgorithme dcrit dans la section suivante a t choisi par Microsoft pour son implmentation du ramasse-miettes.
On pourrait penser quil sut de librer un objet lorsque celui-ci nest plus rfrenc. Cette
approche simpliste peut facilement tre mise en dfaut. Imaginez deux objets A et B ; A maintient une rfrence vers B et B maintient une rfrence vers A ; ces deux objets nadmettent pas
dautres rfrences que leurs rfrences mutuelles. Clairement le programme na plus besoin de
ces objets et le ramasse-miettes doit les dtruire, alors quil existe encore une rfrence valide
sur chacun deux. Le ramasse-miettes utilise donc des algorithmes du type arbres de rfrences
et dtection de boucles dans un graphe. Larbre de rfrences admet pour racines certains objets
considrs comme immuables.
Un autre problme que celui de la destruction des objets incombe au ramasse-miettes. Cest la
fragmentation du tas. Aprs un certain temps, force davoir dsallou et allou des zones mmoires de tailles trs variables, le tas est fragment. Il est parsem despaces mmoire non utiliss
par le programme. Ce fait induit un norme gaspillage de mmoire. Il incombe au ramassemiettes de dfragmenter le tas, afin de limiter les consquences de ce problme. L encore plusieurs algorithmes et approches existent.
Enfin, le bon sens et lapproche empirique nous enseigne la rgle suivante : plus un objet est
ancien plus son esprance de vie est longue, plus il est rcent plus son esprance de vie est courte.
Si cette rgle est prise en compte par un algorithme, cest--dire si lon essaye plus souvent de
dsallouer les objets rcents que les objets anciens, alors le ramasse-miettes sous-jacent gagnera
ncessairement en performance.
Lalgorithme du ramasse-miettes .NET prend en compte ces problmatiques et cette rgle temporelle.
118
Gnration 2
Gnration 1
Gnration 0
C
E
119
collecte partielle de la gnration 0 par seconde, une collecte partielle de la gnration 1 pour
10 collectes partielles de la gnration 0, une collecte complte pour 10 collectes partielles de
la gnration 1.
La Figure 4 -6 montre que lobjet D nest pas marqu. Il est donc inactif et va tre dtruit.
A
Gnration 2
Gnration 1
Gnration 0
B
Gnration 2
Gnration 1
Gnration 0
Bonnes pratiques
Les bonnes pratiques suivantes dcoulent naturellement des proprits de lalgorithme que
nous venons dexposer :
Librez vos objets ds que possible pour viter la promotion dobjets dans les gnrations.
120
Identifiez quels objets sont susceptibles davoir une vie longue, analysez les causes et essayez
de rduire leurs dures de vie. Pour cela, nous vous conseillons davoir recours loutil
CLR Profiler fournit gratuitement par Microsoft ou au profiler fourni avec Visual Studio Team
System .
Dans la mesure du possible, vitez de rfrencer un objet avec une vie courte partir dun
objet avec une vie longue.
viter dimplmenter un finaliseur dans vos classes pour que vos objets ne survivent pas
une collecte.
Assign vos rfrences null ds que possible, surtout avant un long appel.
121
Lobjet peut tre potentiellement utilis plus tard mais ce nest pas certain. Si nous sommes
certains de lutiliser plus tard il faut utiliser une rfrence forte.
Lobjet peut tre intgralement reconstruit lidentique (par exemple partir dune base
de donnes). Si on a potentiellement besoin de lobjet plus tard mais quon ne peut le reconstruire lidentique il ne faut pas que le ramasse-miettes dtruise lobjet.
Lobjet est relativement volumineux en mmoire (plusieurs Ko). Si lobjet est lger on peut
le garder en mmoire. Cependant si les deux conditions prcdentes sappliquent un
grand nombre dobjets lgers il est judicieux dutiliser une rfrence faible pour chacun de
ces objets.
Tout ceci est bien thorique. Dans la pratique on dit quon utilise un cache dobjets. En eet, ces
conditions sont toutes remplies par les objets contenus dans un cache (on parle du concept de
cache en gnral et pas dune implmentation particulire).
Lutilisation de caches peut tre considre comme une rgulation naturelle et automatique
du compromis entre lespace mmoire, la puissance de calcul et la bande passante du rseau.
Lorsque le cache est trop rempli, une partie des objets cachs sont dtruits. Cependant, dans
lhypothse, vraisemblable, o lon doit accder un de ces objets plus tard, il faudra utiliser de
la puissance de calcul (pour fabriquer lobjet) ou/et de la bande passante rseaux (pour obtenir
les donnes contenues dans lobjet, partir dune base de donnes par exemple).
En rsum, si vous avez implmenter un cache nous vous conseillons dutiliser les rfrences
faibles.
122
La classe WeakReference prsente la mthode bool IsAlive() qui retourne true si lobjet rfrenc faiblement na pas encore t dtruit. En outre, il est classique et recommand de mettre
la rfrence forte null ds quune rfrence faible est cre pour sassurer que la rfrence forte
est dtruite.
123
124
2e cas : Si on appelle ReRegisterForFinalize() dans le code de Finalize() lobjet survivra aux collectes. Cela peut servir analyser le comportement du ramasse-miettes.
Cependant si on rpte indfiniment lappel Finalize() le programme ne sarrtera jamais, donc il faut prvoir une condition avant lappel de ReRegisterForFinalize() dans le code de Finalize(). De plus si le code dune telle mthode Finalize()
rfrence dautres objets il faut tre trs prudent car ils peuvent tre dtruits par le
ramasse-miettes sans que lon sen aperoive. En eet, cet objet indestructible nest
pas constamment considr comme actif, donc ses rfrences vers dautres objets ne
sont pas forcment prises en compte lors de la construction de larbre des rfrences
vers les objets actifs.
Facilits fournies par le CLR pour rendre votre code plus fiable
125
Boxing implicite.
Cration des champs statiques dune classe dont lassemblage est partag par les domaines
dapplication du processus.
Une pnurie de ressource se traduit en gnral par la leve dune exception par le CLR de type
OutOfMemoryException, StackOverflowException ou ThreadAbortException sur le thread qui
excute la demande de ressource responsable de la pnurie. On parle dexception asynchrone.
Cette notion soppose la notion dexception applicative. Lorsquune exception applicative est
leve, il incombe au code courant de la rattraper et de la traiter. Typiquement lorsque vous
souhaitez accder un fichier il faut prvoir quune exception de type FileNotFoundException
peut tre leve. En revanche, lorsquune exception asynchrone est leve par le CLR, le code en
cours dexcution ne peut en tre tenu pour responsable. En consquence, il est dconseill de
cder la psychose en mettant des blocs try/catch/finally partout dans votre code pour limiter
les eets de bord des exceptions asynchrones. Lentit responsable du traitement des exceptions
asynchrones est lhte du moteur dexcution que nous avons dj eu loccasion de prsenter
dans ce chapitre.
Dans le cas dune application console ou fentre classique, la leve dune exception asynchrone
est un vnement rare qui traduit en gnral un problme dalgorithme (fuite de mmoire,
appels rcursifs abusifs ou infinis etc). En consquence lhte du moteur dexcution de ces
applications fait en sorte de terminer le processus tout entier lorsquune exception asynchrone
est non rattrape par le code applicatif.
Il en est de mme dans le cas des applications ASP.NET. En eet, on constate que les dirents
mcanismes de dtection dun comportement anormal recyclent le processus en gnral avant
mme que des exceptions asynchrones soient lances. Ces mcanismes sont exposs en page 890.
Ainsi, jusqu lintgration du CLR dans le produit SQL Server 2005, les exceptions asynchrones
ntaient pas si problmatiques. Pour ne pas rgresser, la version 2005 de SQL Server doit fournir
un taux de fiabilit de cinq neufs. En dautres termes le service de persistance des donnes doit
126
tre disponible 99.999% du temps soit, au pire, 5 minutes et 15 secondes dindisponibilit par
an. En outre, pour tre ecace, le processus de SQL Server 2005 doit charger un maximum de
donnes en mmoire et limiter les chargements de pages mmoire partir du disque dur. Il est
donc amen flirter rgulirement avec la limite de 2 ou 3GB du processus si la mmoire vive
disponible le permet (ce qui est maintenant le cas sur la plupart des serveurs). Enfin, les mcanismes de time out sur les excutions des requtes fonctionnent base de leve de lexception
ThreadAbortException. En rsum, lorsque vous avez faire ce type de serveur qui pousse
le systme dans ses retranchements non seulement les exceptions asynchrones deviennent des
vnements banals mais en plus il faut viter absolument quelles ne provoquent le crash du
processus.
Face de tels impratifs les concepteurs du CLR ont du imaginer de nouvelles techniques. Elles
constituent le sujet de la prsente section.
Garder bien lesprit que ces techniques doivent tre utilises avec une grande sagesse,
seulement lorsque vous dveloppez un serveur de grande envergure qui ncessite son
propre moteur dexcution et qui est susceptible de faire face des exceptions asynchrones.
Facilits fournies par le CLR pour rendre votre code plus fiable
127
try/catch/finally. Tout le code atteignable partir des blocs catch et finally reprsente alors
une CER.
La mthode statique ProbeForSufficientStack() de cette classe est ventuellement appele
lors de lappel PrepareConstrainedRegion(). Cela dpend du fait que limplmentation des
CER par votre hte du moteur dexcution doit grer les cas de dpassement de la taille maximale de la pile dappels du thread courant (stack overflow). Sur un processeur x86, cette mthode
tentera de rserver 48Ko.
Malgr cette quantit de mmoire rserve, il se peut quune situation de stack overflow survienne. Aussi vous pouvez vous faire suivre votre appel PrepareConstrainedRegion() par
un appel la mthode statique ExecuteCodeWithGuaranteedCleanup() afin dindiquer une mthode contenant du code de nettoyage invoquer le cas chant. Une telle mthode doit tre
marque avec lattribut PrePrepareMethodAttribute pour prciser loutil ngen.exe sa fonction particulire.
La classe RuntimeHelpers prsente des mthodes permettant aux dveloppeurs dassister le CLR
dans la prparation du terrain avant lexcution de la CER. Vous pouvez par exemple les appeler
dans des constructeurs de classes.
128
Sil savre que la quantit de mmoire indique est indisponible, le constructeur de la classe
MemoryFailPoint lve une exception de type System.InsufficientMemoryException. Pour
cette raison, on parle parfois de portail de mmoire (memory gates en anglais) pour dsigner cette
fonctionnalit.
Description
MayCorruptProcess
La mthode marque peut au pire corrompre ltat du processus tout entier et ainsi, provoquer un crash.
MayCorruptAppDomain
La mthode marque peut au pire corrompre ltat du domaine dapplication courant et ainsi, provoquer son dchargement.
MayCorruptInstance
La mthode marque peut au pire corrompre ltat de linstance sur laquelle elle est appele et ainsi, provoquer sa destruction.
WillNotCorruptState
CLI et CLS
129
CLI et CLS
Sous ces deux acronymes se cache la magie qui permet .NET de supporter plusieurs langages.
Le CLI (Common Langage Infrastructure) est une spcification dcrivant les contraintes respec-
130
tes par le CLR et les assemblages. Une couche logicielle qui supportent les contraintes du
CLI est mme de grer lexcution des applications .NET. Cette spcification est produite par
lECMA. Elle est disponible sur le site de lECMA lURL : http://www.ecma-international.
org/publications/standards/ECMA-335.HTM
Le langage doit prvoir une syntaxe pour rsoudre le cas dune classe qui implmente deux
interfaces qui ont un conflit de dfinitions de mthodes. Il y a conflit de dfinitions de mthodes lorsque deux mthodes, une dans chaque interface implmente par la classe, ont
le mme nom et la mme signature. Le CLS impose que la classe doit implmenter deux
mthodes distinctes.
Seulement certains types primitifs sont compatibles avec le CLS. Par exemple le type ushort
de C nest pas compatible avec le CLS.
Les types des paramtres des mthodes publiques doivent tre CLS compliant. Cette notion
de CLS compliant est prsente quelques lignes plus loin.
Un objet lanc dans une exception doit tre une instance de la classe System.Exception, ou
dune classe drive de celle-ci.
La liste exhaustive de ces contraintes est disponible dans les MSDN larticle Common Language Specification .
La compatibilit dun langage avec le CLS nest pas forcment totale et on observe deux niveaux
de compatibilit :
Un langage supporte la compatibilit extenseur sil peut produire des classes drivant de
classes publiques contenues dans des assemblages compatibles avec le CLS. Cette compatibilit induit la compatibilit consommateur.
CLI et CLS
La dfinition des membres publics et des membres protgs des types publics.
131
Ce qui veut dire que le code des classes et mthodes prives na pas tre compatible avec la
CLS.
Le dveloppeur a tout intrt dvelopper des librairies compatibles avec le CLS. Il lui sera
ainsi plus ais de rutiliser ces classes dans le futur. Heureusement, le dveloppeur na pas
tre spcialiste des contraintes imposes par le CLS pour vrifier si ses classes sont compatibles
avec le CLS. Vous pouvez, grce lattribut System.CLSCompliantAttribute, faire vrifier par
le compilateur si les lments de vos applications (assemblages, classes, mthodes...) sont compatibles avec le CLS. Par exemple :
Exemple 4-15 :
using System ;
[assembly : CLSCompliantAttribute(true)]
namespace CLSCompliantTest {
public class Program {
public static void Fct(ushort i ) { ushort j = i; }
static void Main(){}
}
}
Le compilateur gnre un avertissement car le type ushort, qui nest pas compatible avec le
CLS, est utilis comme type dun paramtre dune mthode publique dune classe publique. En
revanche, lutilisation du type ushort lintrieur du corps de la mthode Fct() ne provoque
pas derreur ni davertissement de compilation.
Avec lattribut CLSCompliantAttribute, on peut aussi indiquer au compilateur de ne pas tester
la compatibilit avec le CLS pour la mthode Fct() tout en gardant la vrification de la compatibilit avec le CLS dans le reste du programme comme ceci :
Exemple 4-16 :
using System ;
[assembly : CLSCompliantAttribute(true)]
namespace CLSCompliantTest {
public class Program {
[CLSCompliantAttribute(false)]
public static void Fct(ushort i ) { ushort j = i ; }
static void Main(){}
}
}
Comprenez bien que la mthode Fct() ne peut pas tre appele partir du code dun langage
ne connaissant pas le type ushort.
5
Processus, threads
et gestion de la synchronisation
Nous exposons ici les notions fondamentales que sont les processus et les threads, dans larchitecture des systmes dexploitation de type Windows NT/2000/XP. Il faut avoir lesprit
que le CLR (ou moteur dexcution) dcrit dans le chapitre prcdent est une couche logicielle charge dans un processus par un hte du moteur dexcution lorsquun assemblage
.NET est lanc.
Introduction
Un processus (process en anglais) est concrtement une zone mmoire contenant des ressources.
Les processus permettent au systme dexploitation de rpartir son travail en plusieurs units
fonctionnelles.
Un processus possde une ou plusieurs units dexcution appele(s) threads. Un processus possde aussi un espace dadressage virtuel priv accessible en lecture et criture, seulement par ses
propres threads.
Dans le cas des programmes .NET, un processus contient aussi dans son espace mmoire la
couche logicielle appele CLR ou moteur dexcution. La description du CLR fait lobjet du
chapitre prcdent. Cette couche logicielle est charge ds la cration du processus par lhte
du moteur dexcution (ceci est dcrit page 94).
Un thread ne peut appartenir qu un processus et ne peut utiliser que les ressources de ce processus. Quand un processus est cr par le systme dexploitation, ce dernier lui alloue automatiquement un thread appel thread principal (main thread ou primary thread en anglais). Cest ce
thread qui excute lhte du moteur dexcution, le chargeur du CLR.
134
Une application est constitue dun ou plusieurs processus cooprants. Par exemple lenvironnement de dveloppement Visual Studio est une application, qui peut utiliser un processus pour
diter les fichiers sources et un processus pour la compilation.
Sous les systmes dexploitation Windows NT/2000/XP, on peut visualiser un instant donn
toutes les applications et tous les processus en lanant le gestionnaire des tches (task manager
en anglais). Il est courant davoir une trentaine de processus en mme temps, mme si vous
avez ouvert un petit nombre dapplications. En fait le systme excute un grand nombre de
processus, un pour la gestion de la session courante, un pour la barre des tches, et bien dautres
encore.
Les processus
Introduction
Dans un systme dexploitation Windows 32 bits, tournant sur un processeur 32 bits, un processus peut tre vu comme un espace linaire mmoire de 4Go (232 octets), de ladresse
0x00000000 0xFFFFFFFF. Cet espace de mmoire est dit priv, car inaccessible par les autres
processus. Cet espace se partage en 2Go pour le systme et 2Go pour lutilisateur. Windows et
certains processeurs soccupent de faire lopration de translation entre cet espace dadressage
virtuel et lespace dadressage rel.
Si N processus tournent sur une machine il nest (heureusement) pas ncessaire davoir Nx4Go
de RAM.
Windows alloue seulement la mmoire ncessaire chaque processus, 4Go tant la limite
suprieure dans un environnement 32 bits.
Un mcanisme de mmoire virtuelle du systme sauve sur le disque dur et charge en RAM
des morceaux de processus appels pages mmoire. Chaque page a une taille de 4Ko. L
encore tout ceci est transparent pour le dveloppeur et lutilisateur.
La classe System.Diagnostics.Process
Une instance de la classe System.Diagnostics.Process rfrence un processus. Les processus
qui peuvent tre rfrencs sont :
Les mthodes et champs de cette classe permettent de crer, dtruire, manipuler ou obtenir des
informations sur ces processus. Nous exposons ici quelques techniques courantes dutilisation
de cette classe.
Les processus
135
processus parent attend une seconde avant de tuer le processus fils. Le programme a donc pour
eet douvrir et de fermer le bloc note :
Exemple 5-1 :
using System.Diagnostics ;
using System.Threading ;
class Program {
static void Main() {
// Cree un processus fils qui lance notepad.exe
// avec le fichier texte hello.txt.
Process process = Process.Start("notepad.exe", "hello.txt") ;
// Endors le thread 1 seconde.
Thread.Sleep(1000) ;
// Tue le processus fils.
process.Kill();
}
}
La mthode statique Start() peut utiliser les associations qui existent sur un systme dexploitation, entre un programme et une extension de fichier. Concrtement ce programme a le mme
comportement si lon crit :
Process process = Process.Start("hello.txt") ;
Par dfaut un processus fils hrite du contexte de scurit de son processus parent. Cependant
une version surcharge de la mthode Process.Start() permet de lancer le processus fils dans
le contexte de scurit de nimporte quel utilisateur pourvu que vous ayez pu fournir le couple
login/mot de passe dans une instance de la classe System.Diagnostics.ProcessStartInfo.
Il y a le risque faible, mais potentiel, que le nom du mutex soit utilis par une autre application, auquel cas cette technique ne marche absolument plus et peut provoquer des bugs
diciles dtecter.
Cette technique ne peut rsoudre le cas gnral o lon nautorise que N instances de lapplication.
136
Les threads
Introduction
Un thread comprend :
Les threads
137
Une pile.
Un ensemble de valeurs pour les registres, dfinissant une partie de ltat du processeur
excutant le thread.
Tous ces lments sont rassembls sous le nom de contexte dexcution du thread. Lespace dadressage et par consquent toutes les ressources qui y sont stockes, sont communs tous les threads
dun mme processus.
Nous ne parlerons pas de lexcution des threads en mode noyau et en mode utilisateur. Ces deux
modes, utiliss par Windows depuis bien avant .NET, existent toujours puisquils se situent dans
une couche en dessous du CLR. Nanmoins ces modes ne sont absolument pas visibles du framework .NET.
Lutilisation en parallle de plusieurs threads constitue souvent une rponse naturelle limplmentation des algorithmes. En eet, les algorithmes utiliss dans les logiciels sont souvent
constitus de tches dont les excutions peuvent se faire en parallle. Attention, utiliser une
grande quantit de threads gnre beaucoup de context switching, et finalement nuit aux performances.
En outre, nous constatons depuis quelques annes que la loi de Moore qui prdisait un doublement de la rapidit dexcution des processeurs nest plus vrifie. Leur frquence semble
stagner autour de 3/4GHz. Cela est du des limites physiques qui prendront quelques temps
tre surmontes. Aussi, pour continuer la course aux performances, les grands fabricants de
processeurs tels que AMD et Intel sorientent vers des solutions types multi processeurs dans un
seul chip. En consquence, on peut sattendre voir prolifrer ce type darchitecture dans les
prochaines annes. La seule solution pour amliorer les performances des applications sera alors
davoir un recours massif au multithreading, do limportance des notions prsentes dans le
prsent chapitre.
Notion de thread gr
Il faut bien comprendre que les threads qui excutent les applications .NET sont bien ceux de
Windows. Cependant on dit quun thread est gr quand le CLR connat ce dernier. Concrtement, un thread est gr sil est cr par du code gr. Si le thread est cr par du code non gr,
alors il nest pas gr. Cependant un tel thread devient gr ds quil excute du code gr.
Un thread gr se distingue dun thread non gr par le fait que le CLR cre une instance de la
classe System.Threading.Thread pour le reprsenter et le manipuler. En interne, le CLR garde
une liste des threads grs nomme ThreadStore.
Le CLR fait en sorte que chaque thread gr soit excut au sein dun domaine dapplication,
un instant donn. Cependant un thread nest absolument pas cantonn un domaine dapplication, et il peut en changer au cours du temps. La notion de domaine dapplication est prsente
page 87.
Dans le domaine de la scurit, lutilisateur principal dun thread gr est indpendant de lutilisateur principal du thread non gr sous-jacent.
138
Le multitche premptif
On peut se poser la question suivante : mon ordinateur a un processeur (voire deux) et pourtant
le gestionnaire des tches indique quune centaine de threads sexcutent simultanment sur ma
machine ! Comment cela est-il possible ?
Cela est possible grce au multitche premptif qui gre lordonnancement des threads. Une partie
du noyau de Windows, appele rpartiteur (scheduler en anglais), segmente le temps en portions
appeles quantum (appeles aussi time slices). Ces intervalles de temps sont de lordre de quelques
millisecondes et ne sont pas de dure constante. Pour chaque processeur, chaque quantum est
allou un seul thread. La succession trs rapide des threads donne lillusion lutilisateur que
les threads sexcutent simultanment. On appelle context switching lintervalle entre deux
quantums conscutifs. Un avantage de cette mthode est que les threads en attente dune ressource nont pas dintervalle de temps allou jusqu la disponibilit de la ressource.
Ladjectif premptif utilis pour qualifier une telle gestion du multitche vient du fait que les
threads sont interrompus dune manire autoritaire par le systme. Pour les curieux sachez que
durant le context switching, le systme dexploitation place une instruction de saut vers le prochain context switching dans le code qui va tre excut par le prochain thread. Cette instruction
est de type interruption software. Si le thread doit sarrter avant de rencontrer cette instruction
(par exemple parce quil est en attente dune ressource) cette instruction est automatiquement
enleve et le context switching a lieu prmaturment.
Linconvnient majeur du multitche premptif est la ncessit de protger les ressources dun
accs anarchique avec des mcanismes de synchronisation. Il existe thoriquement un autre modle de la gestion du multitche, dit multitche coopratif, o la responsabilit de dcider quand
donner la main incombe au threads eux-mmes, mais ce modle est dangereux car les risques de
ne jamais rendre la main sont trop grands. Comme nous lexpliquons en page 100, ce mcanisme
est cependant utilis en interne pour optimiser les performances de certains serveurs tels que
SQL Server2005. En revanche, les systmes dexploitation Windows nimplmentent plus que le
multitche premptif.
Les threads
139
Valeur de ProcessPriorityClass
Low
BelowNormal
Normal
AboveNormal
10
High
13
RealTime
24
Le processus propritaire de la fentre en premier plan voit sa priorit incrmente dune unit
si la proprit PriorityBoostEnabled de la classe System.Diagnostics.Process est positionne
true. Cette proprit est par dfaut positionne true. Cette proprit nest accessible sur une
instance de la classe Process, que si celle-ci rfrence un processus sur la mme machine.
Vous avez la possibilit de changer la priorit dun processus en utilisant le gestionnaire des tches
avec la manipulation : Click droit sur le processus choisi Dfinir la priorit Choisir parmi les
six valeurs proposes savoir Temps rel, Haute ; Suprieure la normale ; Normale ; Infrieure la
normale ; Basse.
Les systmes dexploitation Windows ont un processus dinactivit (idle en anglais) qui a la priorit
0. Cette priorit nest accessible aucun autre processus. Par dfinition lactivit des processeurs,
note en pourcentage, est :
100% moins le pourcentage de temps pass dans le thread du processus dinactivit.
de
Lowest
BelowNormal
Normal
AboveNormal
Highest
140
Dans la plupart de vos applications, vous naurez pas modifier la priorit de vos processus et
threads, qui par dfaut, est assigne Normal.
La classe System.Threading.Thread
Le CLR associe automatiquement une instance de la classe System.Threading.Thread chaque
thread gr. Vous pouvez utiliser cet objet pour manipuler le thread partir dun autre thread
ou partir du thread lui-mme. Vous pouvez obtenir cet objet associ au thread courant avec
la proprit statique CurrentThread de la classe System.Threading.Thread :
using System.Threading ;
...
Thread threadCurrent = Thread.CurrentThread ;
Une fonctionnalit de la classe Thread, bien pratique pour dboguer une application multithread (multitches), est la possibilit de pouvoir nommer ses threads avec une chane de caractres :
threadCurrent.Name = "thread Foo" ;
Les threads
141
Terminer un thread
Un thread gr peut se terminer selon plusieurs scnarios :
Il sort de la mthode sur laquelle il avait commenc sa course (la mthode Main() pour
le thread principal, la mthode rfrence par le dlgu ThreadStart pour les autres
threads).
142
Le premier cas tant trivial, nous ne nous intressons quaux deux autres cas. Dans ces deux cas,
la mthode Abort() peut tre utilise (par le thread courant ou par un thread extrieur). Elle
provoque lenvoi dune exception de type ThreadAbortException. Cette exception a la particularit dtre relance automatiquement lorsquelle est rattrape par un gestionnaire dexception
car le thread est dans un tat spcial nomm AbortRequested. Seul lappel la mthode statique
ResetAbort() (si on dispose de la permission ncessaire) dans le gestionnaire dexception empche cette propagation.
Exemple 5-4 :
using System ;
using System.Threading ;
namespace ThreadTest{
class Program {
static void Main() {
Thread t = Thread.CurrentThread ;
try{
t.Abort() ;
}
catch( ThreadAbortException ) {
Thread.ResetAbort() ;
}
}
}
}
Lorsquun thread A appelle la mthode Abort() sur un autre thread B, il est conseill que A
attende que B soit eectivement termin en appelant la mthode Join() sur B.
Il existe aussi la mthode Interrupt() qui permet de terminer un thread lorsquil est dans un
tat dattente (i.e bloqu sur une des mthodes Wait(), Sleep() ou Join()). Cette mthode a
un comportement dirent selon que le thread terminer est dans un tat dattente ou non.
Si le thread terminer est dans un tat dattente lorsque Interrupt() est appele par un
autre thread, lexception ThreadInterruptedException est lance.
Si le thread terminer nest pas dans un tat dattente lorsque Interrupt() est appele, la
mme exception sera lance ds que ce thread rentrera dans un tat dattente. Le comportement est le mme si le thread terminer appelle Interrupt() sur lui-mme.
143
AbortRequested
Stopped
SuspendRequested
Background
StopRequested
Unstarted
La description de chacun de ces tats se trouve dans larticle ThreadState Enumeration des
MSDN. Cette numration est un indicateur binaire, cest--dire que ses instances peuvent
prendre plusieurs valeurs la fois. Par exemple un thread peut tre la fois dans ltat Running
AbortRequested et Background. La notion dindicateur binaire est prsente page 369.
Daprs ce quon a vu dans la section prcdente, on peut dfinir le diagramme dtat simplifi
suivant :
Unstarted
Start()
Wait()
Sleep()
Join()
WaitSleepJoin
Interrupt()
Suspended()
Running
Suspended
Resume()
Abort()
Stopped
Introduction la synchronisation
des accs aux ressources
En informatique, le mot synchronisation ne peut tre utilis que dans le cas des applications
multithreads (mono ou multi-processus). En eet, la particularit de ces applications est davoir
plusieurs units dexcution, do possibilit de conflits daccs aux ressources. Les objets de
synchronisation sont des objets partageables entre threads excuts sur la mme machine. Le
propre dun objet de synchronisation est de pouvoir bloquer un des threads utilisateur jusqu
la ralisation dune condition par un autre thread.
Comme nous allons le voir, il existe de nombreuses classes et mcanismes de synchronisation.
Chacun rpond un ou plusieurs besoins spcifiques et il est ncessaire davoir assimil tout ce
chapitre avant de concevoir une application professionnelle multithreads utilisant la synchronisation. Nous nous sommes eorc de souligner les dirences, surtout les plus subtiles, qui
existent entre les dirents mcanismes. Quand vous aurez compris les dirences, vous serez
capable dutiliser ces mcanismes.
Synchroniser correctement un programme est une des tches du dveloppement logiciel les plus
subtiles. Le sujet remplit de nombreux ouvrages. Avant de vous plonger dans des spcifications
144
Race conditions
Il sagit dune situation o des actions eectues par des units dexcution direntes senchanent dans un ordre illogique, entranant des tats non prvus.
Par exemple un thread T modifie une ressource R, rend les droits daccs dcriture R, reprend
les droits daccs en lecture sur R et utilise R comme si son tat tait celui dans lequel il lavait
laiss. Pendant lintervalle de temps entre la libration des droits daccs en criture et lacquisition des droits daccs en lecture, il se peut quun autre thread ait modifi ltat de R.
Un autre exemple classique de situation de comptition est le modle producteur consommateur. Le producteur utilise souvent le mme espace physique pour stocker les informations produites. En gnral on noublie pas de protger cet espace physique des accs concurrents entre
producteurs et consommateurs. On oublie plus souvent que le producteur doit sassurer quun
consommateur a eectivement lu une ancienne information avant de produire une nouvelle
information. Si lon ne prend pas cette prcaution, on sexpose au risque de produire des informations qui ne seront jamais consommes.
Les consquences de situations de comptition mal gres peuvent tre des failles dans un systme de scurit. Une autre application peut forcer un enchanement dactions non prvues
par les dveloppeurs. Typiquement il faut absolument protger laccs en criture un boolen
qui confirme ou infirme une authentification. Sinon il se peut que son tat soit modifi entre
linstant ou ce boolen est positionn par le mcanisme dauthentification et linstant ou ce
boolen est lu pour protger des accs des ressources. De clbres cas de failles de scurit
dues une mauvaise gestion des situations de comptition ont exist. Une de celles-ci concernait
notamment le noyau Unix.
Deadlocks
Il sagit dune situation de blocage cause de deux ou plusieurs units dexcution qui sattendent mutuellement. Par exemple :
Un thread T1 acquiert les droits daccs sur la ressource R1.
Un thread T2 acquiert les droits daccs sur la ressource R2.
T1 demande les droits daccs sur R2 et attend, car cest T2 qui les possde.
145
T2 demande les droits daccs sur R1 et attend, car cest T1 qui les possde.
T1 et T2 attendront donc indfiniment, la situation est bloque ! Il existe trois grandes approches
pour viter ce problme qui est plus subtil que la plupart des bugs que lon rencontre.
Nautoriser aucun thread avoir des droits daccs sur plusieurs ressources simultanment.
Dfinir une relation dordre dans lacquisition des droits daccs aux ressources. Cest--dire
quun thread ne peut acqurir les droits daccs sur R2 sil na pas dj acquis les droits daccs
sur R1. Naturellement la libration des droits daccs se fait dans lordre inverse de lacquisition.
Systmatiquement dfinir un temps maximum dattente (timeout) pour toutes les demandes daccs aux ressources et traiter les cas dchec. Pratiquement tous les mcanismes
de synchronisation .NET orent cette possibilit.
Les deux premires techniques sont plus ecaces mais aussi plus dicile implmenter. En effet, elles ncessitent chacune une contrainte trs forte et dicile maintenir durant lvolution
de lapplication. En revanche les situations dchecs sont inexistantes.
Les gros projets utilisent systmatiquement la troisime technique. En eet, si le projet est gros,
le nombre de ressources est en gnral trs grand. Dans ces projets, les conflits daccs simultans une ressource sont donc des situations marginales. La consquence est que les situations
dchec sont, elles aussi, marginales. On dit dune telle approche quelle est optimiste. Dans le
mme esprit, on dcrit en page 727 le modle de gestion optimiste des accs concurrents une
base de donnes.
Un type rfrence.
sbyte, byte, short, ushort, int, uint, char, float, bool (double, long et ulong,
la condition de travailler avec une machine 64 bits).
Une numration dont le type sous-jacent est parmi : byte, sbyte, short, ushort, int,
uint (double, long et ulong condition de travailler avec une machine 64 bits).
Comme vous laurez remarquez, seuls les types dont la valeur ou la rfrence fait au plus le
nombre doctets dun entier natif (quatre ou huit selon le processeur sous-jacent) peuvent tre
146
volatiles. Cela implique que les oprations concurrentes sur une valeur de plus de ce nombre
doctets (une grosse structure par exemple) doivent tre protges en utilisant les mcanismes
de synchronisation prsents ci-aprs.
La classe System.Threading.Interlocked
Lexprience a montr que les ressources protger dans un contexte multithreads sont souvent des variables entires. Les oprations les plus courantes ralises par les threads sur ces
variables entires partages sont lincrmentation et la dcrmentation dune unit et laddition de deux variables. Le framework .NET prvoit donc un mcanisme spcial avec la classe
System.Threading.Interlocked pour ces oprations trs spcifiques, mais aussi trs courantes.
Cette classe les mthodes statiques Increment(), Decrement() et Add() qui respectivement incrmente, dcrmente et additionne des entiers de type int ou long passs par rfrence. On dit
que lutilisation de la classe Interlocked rend ces oprations atomiques (cest--dire indivisibles,
comme ce que lon pensait il y a quelques dcennies pour les atomes de la matire).
Le programme suivant prsente laccs concurrent de deux threads la variable entire
compteur. Un thread lincrmente cinq fois tandis que lautre la dcrmente cinq fois.
Exemple 5-5 :
using System.Threading ;
class Program {
static long compteur = 1 ;
static void Main() {
Thread t1 = new Thread(f1) ;
Thread t2 = new Thread(f2) ;
t1.Start() ; t2.Start() ; t1.Join() ; t2.Join() ;
}
static void f1() {
for (int i = 0 ; i < 5 ; i++){
Interlocked.Increment(ref compteur);
System.Console.WriteLine("compteur++ {0}", compteur) ;
Thread.Sleep(10) ;
}
}
static void f2() {
for (int i = 0 ; i < 5 ; i++){
Interlocked.Decrement(ref compteur);
System.Console.WriteLine("compteur-- {0}", compteur) ;
Thread.Sleep(10) ;
}
}
}
Ce programme ache ceci (dune manire non dterministe, cest--dire que lachage pourrait
varier dune excution une autre) :
compteur++ 2
compteur-- 1
compteur++ 2
147
1
2
1
2
1
0
1
Si on nendormait pas les threads 10 millimes de seconde chaque modification, les threads
auraient le temps de raliser leurs tches en un quantum et il ny aurait pas lentrelacement des
excutions, donc pas daccs concurrent.
Un thread peut appeler Enter() plusieurs fois sur le mme objet la condition quil appelle
Exit() autant de fois sur le mme objet pour se librer des droits exclusifs.
Un thread peut possder des droits exclusifs sur plusieurs objets la fois, mais cela peut
mener une situation de deadlock.
Il ne faut jamais appeler les mthodes Enter() et Exit() sur un objet de type valeur,
comme un entier !
Il faut toujours appeler la mthode Exit() dans un bloc finally afin dtre certain de
librer les droits daccs exclusifs quoi quil arrive.
148
Si dans lExemple 5-5, un thread doit lever la variable compteur au carr tandis que lautre
thread doit la multiplier par deux, il faudrait remplacer lutilisation de la classe Interlocked
par lutilisation de la classe Monitor. Le code de f1() et f2() serait alors :
Exemple 5-6 :
using System.Threading ;
class Program {
static long compteur = 1 ;
static void Main() {
Thread t1 = new Thread(f1) ;
Thread t2 = new Thread(f2) ;
t1.Start() ; t2.Start() ; t1.Join() ; t2.Join() ;
}
static void f1() {
for (int i = 0 ; i < 5 ; i++){
try{
Monitor.Enter( typeof(Program) );
compteur *= compteur ;
}
finally{ Monitor.Exit( typeof(Program) ) ; }
System.Console.WriteLine("compteur^2 {0}", compteur) ;
Thread.Sleep(10) ;
}
}
static void f2() {
for (int i = 0 ; i < 5 ; i++){
try{
Monitor.Enter( typeof(Program) );
compteur *= 2 ;
}
finally{ Monitor.Exit( typeof(Program) ) ; }
System.Console.WriteLine("compteur*2 {0}", compteur) ;
Thread.Sleep(10) ;
}
}
}
Il est tentant dcrire compteur la place de typeof(Program) mais compteur est un membre
statique de type valeur. Remarquez que les oprations lvation au carr et multiplication
par deux ntant pas commutatives, la valeur finale de compteur est ici non dtermine.
Le mot cl lock de C
Le langage C prsente le mot-cl lock qui remplace lgamment lutilisation des mthode Enter() et Exit(). La mthode f1() pourrait donc scrire :
Exemple 5-7 :
using System.Threading ;
class Program {
149
Le pattern SyncRoot
linstar des exemples prcdents, on utilise en gnral la classe Monitor avec une instance de
la clase Type du type courant lintrieur dune mthode statique. De mme, on se synchronise
souvent sur le mot cl this lintrieur dune mthode non statique. Dans les deux cas, on
se synchronise sur un objet visible hors de la classe. Cela peut poser des problmes si dautres
parties du code se synchronisent sur ces objets. Pour viter ces problmes potentiels, nous vous
conseillons dutiliser un membre priv SyncRoot de type object, statique ou non selon vos besoins :
Exemple 5-8 :
class Foo {
private static object staticSyncRoot = new object();
private object instanceSyncRoot = new object();
public static void StaticFct() {
lock (staticSyncRoot) { /*...*/ }
150
151
La mthode Monitor.TryEnter()
public static bool TryEnter(object [,int] )
Cette mthode est similaire Enter() mais elle nest pas bloquante. Si les droits daccs exclusifs
sont dj possds par un autre thread, cette mthode retourne immdiatement et sa valeur de
retour est false. On peut aussi rendre un appel TryEnter() bloquant pour une dure limite
spcifie en millisecondes. Puisque lissue de cette mthode est incertaine, et que dans le cas
o lon acquerrait les droits daccs exclusifs il faudrait les librer dans un bloc finally, il est
conseill de sortir immdiatement de la mthode courante dans le cas o lappel TryEnter()
chouerait :
Exemple 5-11 :
using System.Threading ;
class Program {
static void Main() {
// `A commenter pour tester le cas o`u TryEnter() retourne true.
Monitor.Enter(typeof(Program)) ;
Thread t1 = new Thread(f1) ;
t1.Start() ; t1.Join() ;
}
static void f1() {
bool bOwner = false ;
try {
if( ! Monitor.TryEnter( typeof(Program) ) )
return;
bOwner = true;
// ...
}
finally {
// Ne surtout pas appeler Monitor.Exit() si on a pas lacc`
es.
// Noubliez pas que lon passe n
ecessairement par le bloc
// finally, y compris si lappel `
a TryEnter() retourne false.
if( bOwner )
Monitor.Exit( typeof(Program) );
}
}
}
152
Le thread T1 possdant laccs exclusif lobjet OBJ, appelle la mthode Wait(OBJ) afin de
senregistrer dans une liste dattente passive de OBJ.
Par cet appel T1 perd laccs exclusif OBJ. Ainsi un autre thread T2 prend laccs exclusif
OBJ en appelant la mthode Enter(OBJ).
T2 modifie ventuellement ltat de OBJ puis appelle Pulse(OBJ) pour signaler cette modification. Cet appel provoque le passage du premier thread de la liste dattente passive de OBJ
(en loccurrence T1) en haut de la liste dattente active de OBJ. Le premier thread de la liste
active de OBJ a la garantie quil sera le prochain avoir les droits daccs exclusifs OBJ ds
quils seront librs. Il pourra ainsi sortir de son attente dans la mthode Wait(OBJ).
Dans notre scnario T2 libre les droits daccs exclusifs sur OBJ en appelant Exit(OBJ) et
T1 les rcupre et sort de la mthode Wait(OBJ).
La mthode PulseAll() fait en sorte que les threads de la liste dattente passive, passent
tous dans la liste dattente active. Limportant est que les threads soient dbloqus dans le
mme ordre quils ont appels Wait().
Si Wait(OBJ) est appele par un thread ayant appel plusieurs fois Enter(OBJ), ce thread devra
appeler Exit(OBJ) le mme nombre de fois pour librer les droits daccs OBJ. Mme dans ce
cas, un seul appel Pulse(OBJ) par un autre thread sut dbloquer le premier thread.
Le programme suivant illustre cette fonctionnalit au moyen de deux threads ping et pong qui
se obtiennent tour de rle les droits daccs un objet balle :
Exemple 5-12 :
using System.Threading ;
public class Program {
static object balle = new object();
public static void Main() {
Thread threadPing = new Thread( ThreadPingProc ) ;
Thread threadPong = new Thread( ThreadPongProc ) ;
threadPing.Start() ; threadPong.Start() ;
threadPing.Join() ; threadPong.Join() ;
}
static void ThreadPongProc() {
System.Console.WriteLine("ThreadPong: Hello!") ;
lock (balle)
for (int i = 0 ; i < 5 ; i++){
153
Hello!
Ping
Hello!
Pong
Ping
Pong
Ping
Pong
Ping
Pong
Ping
Pong
Bye!
Le thread pong ne se termine pas et reste bloqu sur la mthode Wait(). Ceci rsulte du fait que
le thread pong a obtenu le droit daccs exclusif sur lobjet balle en deuxime.
La classe Mutex (le mot mutex est la concatnation de mutuelle exclusion. En franais on
parle parfois de mutant)).
154
La classe WaitHandle et ses classes drives, ont la particularit dimplmenter la mthode non
statique WaitOne() et les mthodes statiques WaitAll(), WaitAny() et SignalAndWait(). Elles
permettent respectivement dattendre quun objet soit signal, que tous les objets dans un tableau soient signals, quau moins un objet dans un tableau soit signal, de signaler un objet et
dattendre sur un autre. Contrairement la classe Monitor et Interlocked, ces classes doivent
tre instancies pour tre utilises. Il faut donc raisonner ici en terme dobjets de synchronisation et non dobjets synchroniss. Ceci implique que les objets passs en paramtre des mthodes statiques WaitAll(), WaitAny() et SignalAndWait() sont soit des mutex, soit des vnements soit des smaphores.
Les Mutex
En terme de fonctionnalits les mutex sont proches de lutilisation de la classe Monitor ces
dirences prs :
On peut utiliser un mme mutex dans plusieurs processus dune mme machine ou sexcutant sur des machines direntes.
Les mutex nont pas la fonctionnalit des mthodes Wait(), Pulse() et PulseAll() de la
classe Monitor.
Sachez que lorsque lutilisation dun mutex se cantonne un processus, il vaut mieux synchroniser vos accs avec la classe Monitor qui est plus performante.
Le programme suivant montre lutilisation dun mutex nomm pour protger laccs une ressource partage par plusieurs processus sur la mme machine. La ressource partage est un fichier dans lequel chaque instance du programme crit 10 lignes.
155
Exemple 5-13 :
using System.Threading ;
using System.IO ;
class Program {
static void Main() {
// Le mutex est nomme MutexTest.
Mutex mutexFile = new Mutex(false, "MutexTest");
for (int i = 0 ; i < 10 ; i++){
mutexFile.WaitOne();
// Ouvre le fichier, ecrit Hello i, ferme le fichier.
FileInfo fi = new FileInfo("tmp.txt") ;
StreamWriter sw = fi.AppendText() ;
sw.WriteLine("Hello {0}", i) ;
sw.Flush() ;
sw.Close() ;
// Attend 1 seconde pour rendre
evidente laction du mutex.
System.Console.WriteLine("Hello {0}", i) ;
Thread.Sleep(1000) ;
mutexFile.ReleaseMutex();
}
mutexFile.Close();
}
}
Remarquez lutilisation de la mthode WaitOne() qui bloque le thread courant jusqu lobtention du mutex et lutilisation de la mthode ReleaseMutex() qui libre le mutex.
Dans ce programme, new Mutex ne signifie pas forcment la cration du mutex mais la cration
dune rfrence vers le mutex nomm "MutexTest". Le mutex est eectivement cr par le systme dexploitation seulement sil nexiste pas dj. De mme la mthode Close() ne dtruit
pas forcment le mutex si ce dernier est encore rfrenc par dautres processus.
Pour ceux qui avaient lhabitude dutiliser la technique du mutex nomm en win32 pour viter
de lancer deux instances du mme programme sur la mme machine, sachez quil existe une
faon plus lgante de procder sous .NET, dcrite dans la section page 135.
Les vnements
Contrairement aux mcanismes de synchronisation vus jusquici, les vnements ne dfinissent
pas explicitement de notion dappartenance dune ressource un thread un instant donn.
Les vnements servent passer une notification dun thread un autre, cette notification
tant un vnement sest pass . Lvnement concern est associ une instance dune
des deux classes dvnement, System.Threading.AutoResetEvent et System.Threading.
ManualResetEvent. Ces deux classes drivent de la classe System.Threading.EventWaitHandle.
Une instance de EventWaitHandle peut tre initialise avec une des deux valeurs AutoReset ou
ManualReset de lnumration System.Threading.EventResetMode ce qui fait que vous pouvez
viter de vous servir de ses deux classes filles.
Concrtement, un thread attend quun vnement soit signal en appelant la mthode bloquante WaitOne() sur lobjet vnement associ. Puis un autre thread signale lvnement en
156
appelant la mthode Set() sur lobjet vnement associ et permet ainsi au premier thread de
reprendre son excution.
La dirence entre les vnement repositionnement automatique et les vnement repositionnement manuel est que lon a besoin dappeler la mthode Reset() pour repositionner lvnement
en position non active, sur un vnement de type repositionnement manuel aprs lavoir
signal.
157
Thread.Sleep(60) ;
}
}
}
Ce programme ache :
Thread0: 0
Thread1: 0
Thread1: 1
Thread0: 1
Thread1: 2
Thread1: 3
Thread0: 2
MainThread: Thread0 est arrive `
a 2 et Thread1 est arriv
e `
a 3
Thread1: 4
Thread0: 3
Thread0: 4
Les smaphores
Une instance de la classe System.Threading.Semaphore permet de contraindre le nombre daccs simultans une ressource. Vous pouvez vous limaginez comme la barrire dentre dun
parking automobile qui contient un nombre fix de places. Elle ne souvre plus lorsque le parking est complet. De mme, une tentative dentre dans un smaphore au moyen de la mthode
WaitOne() devient bloquante lorsque le nombre dentres courantes a atteint le nombre dentre maximal. Ce nombre dentre maximal est fix par le deuxime argument des constructeurs
de la classe Semaphore. Le premier argument dfinit le nombre dentres libres initiales. Si le
premier argument a une valeur infrieure celle du deuxime, le thread qui appelle le constructeur dtient automatiquement un nombre dentres gal la dirence des deux valeurs. Cette
dernire remarque montre quun mme thread peut dtenir simultanment plusieurs entres
dun mme smaphore.
Lexemple suivant illustre tout ceci en lanant 3 threads qui tentent rgulirement dentrer dans
un smaphore dont le nombre dentres simultanes maximal est fix cinq. Le thread principal
dtient durant toute la dure de lexcution trois de ces entres, obligeant les 3 threads fils se
partager 2 entres.
Exemple 5-15 :
using System ;
using System.Threading ;
class Program {
static Semaphore semaphore;
static void Main() {
// Nombre dentrees libres initiales
: 2.
// Nombre maximal dentrees simultan
ees : 5.
// Nombre dentrees detenues par le thread principal : 3 (5-2).
semaphore = new Semaphore(2, 5);
for (int i = 0 ; i < 3 ; ++i){
Thread t = new Thread(WorkerProc) ;
158
Voici lachage de ce programme. On voit bien quil ny a jamais plus de deux threads fils qui
travaillent simultanment :
Thread0:
Thread1:
Thread0:
Thread2:
Thread1:
Thread1:
Thread2:
Thread2:
Thread1:
Thread1:
Thread2:
Thread2:
Thread1:
Thread0:
Thread2:
Thread0:
Thread0:
Thread0:
Begin
Begin
End
Begin
End
Begin
End
Begin
End
Begin
End
Begin
End
Begin
End
End
Begin
End
159
t demand mais pas encore obtenu, les nouvelles demandes daccs en lecture sont mises en
attente.
Lorsque ce modle de synchronisation peut tre appliqu, il faut toujours le privilgier par
rapport au modle propos par les classes Monitor ou Mutex. En eet, le modle accs exclusif ne permet en aucun cas des accs simultans et est donc moins performant. De plus,
empiriquement, on se rend compte que la plupart des applications accdent beaucoup plus
souvent aux donnes en lecture quen criture.
linstar des mutex et des vnements et au contraire de la classe Monitor, la classe ReaderWriterLock doit tre instancie pour tre utilise. Il faut donc ici aussi raisonner en terme dobjets
de synchronisation et non dobjets synchroniss.
Voici un exemple de code qui montre lutilisation de cette classe, mais qui nen exploite pas
toutes les possibilits. En eet les mthodes DowngradeFromWriterLock() et UpgradeToWriterLock() permettent de demander un changement de droit daccs sans avoir librer son droit
daccs courant.
Exemple 5-16 :
using System ;
using System.Threading ;
class Program {
static int laRessource = 0 ;
static ReaderWriterLock rwl = new ReaderWriterLock();
static void Main() {
Thread tr0 = new Thread(ThreadReader) ;
Thread tr1 = new Thread(ThreadReader) ;
Thread tw = new Thread(ThreadWriter) ;
tr0.Start() ; tr1.Start() ; tw.Start() ;
tr0.Join() ; tr1.Join() ; tw.Join() ;
}
static void ThreadReader() {
for (int i = 0 ; i < 3 ; i++)
try{
// AcquireReaderLock() lance une
// ApplicationException si timeout.
rwl.AcquireReaderLock(1000);
Console.WriteLine(
"Debut Lecture laRessource = {0}", laRessource) ;
Thread.Sleep(10) ;
Console.WriteLine(
"Fin
Lecture laRessource = {0}", laRessource) ;
rwl.ReleaseReaderLock() ;
}
catch ( ApplicationException ) { /* `
a traiter */ }
}
static void ThreadWriter() {
for (int i = 0 ; i < 3 ; i++)
160
Ce programme ache :
Debut
Debut
Fin
Fin
Debut
Fin
Debut
Debut
Fin
Fin
Debut
Fin
Debut
Debut
Fin
Fin
Debut
Fin
lecture
lecture
lecture
lecture
ecriture
ecriture
lecture
lecture
lecture
lecture
ecriture
ecriture
lecture
lecture
lecture
lecture
ecriture
ecriture
laRessource
laRessource
laRessource
laRessource
laRessource
laRessource
laRessource
laRessource
laRessource
laRessource
laRessource
laRessource
laRessource
laRessource
laRessource
laRessource
laRessource
laRessource
=
=
=
=
=
=
=
=
=
=
=
=
=
=
=
=
=
=
0
0
0
0
0
1
1
1
1
1
1
2
2
2
2
2
2
3
Pour que ce comportement sapplique correctement il faut que la classe sur laquelle sapplique lattribut soit context-bound, cest--dire quelle doit driver de la classe System.
ContextBoundObject. La signification du terme context-bound est explique page 842.
161
: ManagedThreadId
ManagedThreadId
: ManagedThreadId
ManagedThreadId
: ManagedThreadId
ManagedThreadId
: ManagedThreadId
ManagedThreadId
=
=
=
=
=
=
=
=
27
27
28
28
27
27
28
28
162
Un domaine de synchronisation est une entit entirement prise en charge par le CLR. Un tel
domaine contient un ou plusieurs contextes .NET et donc, contient les objets de ces contextes.
Un contexte .NET ne peut appartenir qu au plus un seul domaine de synchronisation. En
outre, la notion de domaine de synchronisation est plus fine que la notion de domaine dapplication. La figure suivante illustre les relations dinclusions entre processus, domaines dapplication, domaines de synchronisation, contextes .NET et objets .NET :
Processus
Domaine dapplication
Contexte
Domaine de
synchronisation
Contexte
Objet
Objet
Lattribut System.Runtime.Remoting.Contexts.Synchronization et
les domaines de synchronisation
Vous avez peut-tre dj devin que lattribut System.Runtime.Remoting.Contexts.Synchronization sert en fait indiquer au CLR quand crer un domaine de synchronisation et comment en dlimiter sa frontire. Ces rglages se font en utilisant dans la dclaration dun attribut System.Runtime.Remoting.Contexts.Synchronization une des quatre valeurs suivantes
NOTSUPPORTED, SUPPORTED, REQUIRED ou REQUIRES_NEW. Notez que la valeur REQUIRED est choisie
par dfaut.
Lappartenance un domaine dapplication se communique de proche en proche lorsquun objet en cre un autre. On parle ainsi dobjet crateur, mais prenez en compte quun objet peut tre
aussi cr au sein dune mthode statique. Or, il se peut que lexcution de la mthode statique
soit le fruit dun appel dun objet situ dans un domaine de synchronisation. Il faut alors savoir
163
que dans ce cas, la mthode statique propage lappartenance au domaine dapplication et joue
ainsi le rle dun objet crateur. Voici lexplication des quatre comportements possibles :
NOT_SUPPORTED
SUPPORTED
REQUIRED
Ce paramtre assure quune instance de la classe sur laquelle sapplique lattribut Synchronization sera de toutes faons dans
un domaine de synchronisation. Si lobjet crateur appartient
dj un domaine de synchronisation, alors on se satisfera de
celui l. Sinon, un nouveau domaine de synchronisation est cr
pour accueillir ce nouvel objet.
REQUIRES_NEW
Ce paramtre assure quune instance de la classe sur laquelle sapplique lattribut Synchronization sera dans un nouveau domaine de synchronisation (que son objet crateur appartienne
un domaine de synchronisation ou non).
NOT_SUPPORTED Non
Lobjet cr rsidera...
Oui
SUPPORTED
Non
Oui
164
REQUIRED
REQUIRES_NEW
Non
Oui
Non
Oui
Soit le CLR autorise un autre thread T2 acqurir les droits daccs exclusifs de D. Dans ce cas,
il se peut que T1 doivent attendre au retour de son appel lextrieur de D que T2 libre les
droits daccs exclusifs de D. On dit quil y a rentrance puisque T1 demande rentrer dans
D.
Soit les droits daccs exclusifs de D restent allous T1. Dans ce cas, un autre thread T2
souhaitant invoquer une mthode dun objet de D devra attendre que T1 libre les droits
daccs exclusifs de D.
Un appel une mthode statique nest pas considr comme un appel lextrieur dun domaine de synchronisation.
Temps
Course du thread T1
Domaine de synchronisation D
Objet 1
Objet 2
Objet 3
Mthode
Mthode
On dit quil y a rentrance dans le domaine
dapplication D si un
autre thread T1 peut
ventuellement acqurir
les droits daccs exclusifs de D
Mthode
165
Notez quil sut que lattribut de synchronisation de la classe du premier objet rencontr par
un thread dans un domaine de synchronisation ait positionn la rentrance true pour quil y
ait eectivement rentrance.
Lexemple suivant est lillustration par le code de la figure ci-dessus :
Exemple 5-18 :
using System ;
using System.Runtime.Remoting.Contexts ;
using System.Threading ;
[Synchronization(SynchronizationAttribute.REQUIRES_NEW, true)]
public class Foo1 : ContextBoundObject {
public void AfficheThreadId() {
Console.WriteLine("Foo1 D
ebut : ManagedThreadId = " +
Thread.CurrentThread.ManagedThreadId) ;
Thread.Sleep(1000) ;
Foo2 obj2 = new Foo2() ;
obj2.AfficheThreadId() ;
Console.WriteLine("Foo1 Fin : ManagedThreadId = " +
Thread.CurrentThread.ManagedThreadId) ;
}
}
[Synchronization(SynchronizationAttribute.REQUIRED)]
public class Foo2 : ContextBoundObject {
public void AfficheThreadId() {
Console.WriteLine("Foo2 D
ebut : ManagedThreadId = " +
Thread.CurrentThread.ManagedThreadId) ;
Thread.Sleep(1000) ;
Foo3 obj3 = new Foo3() ;
obj3.AfficheThreadId() ;
Console.WriteLine("Foo2 Fin : ManagedThreadId = " +
Thread.CurrentThread.ManagedThreadId) ;
}
}
// On est certains que les instances de cette classe ne
// seront dans aucun domaine de synchronisation.
[Synchronization(SynchronizationAttribute.NOT_SUPPORTED)]
public class Foo3 : ContextBoundObject {
public void AfficheThreadId() {
Console.WriteLine("Foo3 D
ebut : ManagedThreadId = " +
Thread.CurrentThread.ManagedThreadId) ;
Thread.Sleep(1000) ;
Console.WriteLine("Foo3 Fin : ManagedThreadId = " +
Thread.CurrentThread.ManagedThreadId) ;
}
}
public class Program {
static Foo1 m_Objet = new Foo1() ;
static void Main() {
166
Debut
Debut
Debut
Debut
Debut
Debut
Fin :
Fin :
Fin :
Fin :
Fin :
Fin :
:
:
:
:
:
:
ManagedThreadId
ManagedThreadId
ManagedThreadId
ManagedThreadId
ManagedThreadId
ManagedThreadId
ManagedThreadId
ManagedThreadId
ManagedThreadId
ManagedThreadId
ManagedThreadId
ManagedThreadId
=
=
=
=
=
=
=
=
=
=
=
=
3
3
4
4
4
3
4
4
4
3
3
3
Il est clair que si lon avait dsactiv la rentrance dans Foo1, lachage de ce programme aurait
t le suivant :
Foo1
Foo2
Foo3
Foo3
Foo2
Foo1
Foo1
Foo2
Foo3
Foo3
Foo2
Foo1
Debut
Debut
Debut
Fin :
Fin :
Fin :
Debut
Debut
Debut
Fin :
Fin :
Fin :
: ManagedThreadId = 3
: ManagedThreadId = 3
: ManagedThreadId = 3
ManagedThreadId = 3
ManagedThreadId = 3
ManagedThreadId = 3
: ManagedThreadId = 4
: ManagedThreadId = 4
: ManagedThreadId = 4
ManagedThreadId = 4
ManagedThreadId = 4
ManagedThreadId = 4
La rentrance est utilise pour optimiser la gestion des ressources car elle permet de rduire
globalement les dures daccs exclusifs des threads sur les domaines de synchronisation. Cependant par dfaut, la rentrance est dsactive. En eet, lorsquil y a rentrance, notre comprhension de la notion de domaine de synchronisation est fortement perturbe. Bien pire encore,
activer tort et travers la rentrance peut rapidement amener des situations de deadlock. Pour
ces raisons, il est fortement dconseill dactiver la rentrance.
167
Le dveloppeur est donc dcharg de ces responsabilits. Malgr tous ces avantages, il est souhaitable de ne pas utiliser de pool de threads lorsque :
Vos tches doivent tre traites dans des STA (Single Apartment Thread). En eet les threads
dun pool sont de type MTA (Multiple Apartement Thread). Cette notion de thread apartment,
inhrente la technologie COM, est explique page 285.
168
est atteint dans le pool, ce dernier ne cre plus de nouveaux threads et les tches dans la file du
pool ne seront traites que lorsquun thread du pool se librera. En revanche, le thread responsable de la cration de la tche, na pas attendre quelle soit traite. Vous pouvez utiliser le pool
de threads de deux faons direntes :
En postant vos propres tches et leurs mthodes de traitement avec la mthode statique
ThreadPool.QueueUserWorkItem(). Une fois quune tche a t poste au pool, elle ne peut
plus tre annule.
En crant un timer qui poste priodiquement une tche prdfinie et sa mthode de traitement au pool. Pour cela, il faut utiliser la mthode statique ThreadPool.RegisterWaitForSingleObject().
Chacune de ces deux mthodes existe dans une version non protge (UnsafeQueueUserWorkItem() et UnsafeRegisterWaitForSingleObject()) . Ces versions non protges permettent
aux threads ouvriers du pool de ne pas tre dans le mme contexte de scurit que le thread qui
a dpos la tche. Lutilisation de ces mthodes amliore donc les performances puisque la pile
du thread qui a dpos la tche nest pas vrifie lors de la gestion des contextes de scurit. Les
mthodes de traitement sont rfrences par des dlgus.
Voici un exemple qui montre lutilisation de tches utilisateurs (paramtres par un numro et
traites par la mthode ThreadTache()) et de tches postes priodiquement (sans paramtre
et traites par la mthode ThreadTacheWait()). Les tches utilisateurs sont volontairement
longues pour forcer le pool crer de nouveaux threads.
Exemple 5-19 :
using System.Threading ;
public class Program {
public static void Main() {
// Positionnement initial de l
ev
enement : false.
ThreadPool.RegisterWaitForSingleObject(
new AutoResetEvent(false),
// Methode de traitement de la t
ache p
eriodique.
new WaitOrTimerCallback( ThreadTacheWait ),
null,
// La tache periodique na pas de param`
etres.
2000,
// La periode est de 2 secondes.
false) ; // La tache p
eriodique est ind
efiniment d
eclench
ee.
// Poste 3 taches utilisateur de param`
etres 0,1,2.
for (int count = 0 ; count < 3 ; ++count)
ThreadPool.QueueUserWorkItem(
new WaitCallback(ThreadTache), count) ;
// Attente de 12 secondes avant de finir le processus
// car les threads du pool sont des threads background.
Thread.Sleep(12000) ;
}
static void ThreadTache(object obj) {
System.Console.WriteLine("Thread#{0} T
ache#{1} D
ebut",
Thread.CurrentThread.ManagedThreadId , obj.ToString()) ;
Timers
169
Thread.Sleep(5000) ;
System.Console.WriteLine("Thread#{0} T
ache#{1} Fin",
Thread.CurrentThread.ManagedThreadId , obj.ToString()) ;
}
static void ThreadTacheWait(object obj, bool signaled) {
System.Console.WriteLine("Thread#{0} T
acheWait",
Thread.CurrentThread.ManagedThreadId ) ;
}
}
Ce programme ache ceci (dune manire non dterministe):
Thread#4
Thread#5
Thread#6
Thread#7
Thread#7
Thread#4
Thread#5
Thread#6
Thread#7
Thread#7
Tache#0 Debut
Tache#1 Debut
Tache#2 Debut
TacheWait
TacheWait
Tache#0 Fin
Tache#1 Fin
Tache#2 Fin
TacheWait
TacheWait
Timers
La plupart des applications contiennent des tches qui doivent tre excutes priodiquement.
Par exemple vous pouvez imaginez de vrifier priodiquement la disponibilit dun serveur. La
premire solution qui vient lesprit pour implmenter ce paradigme est de crer un thread ddi une tche qui appelle la mthode Thread.Sleep() entre deux excutions. Cette implmentation prsente linconvnient de consommer inutilement un thread entre deux excutions.
Nous allons prsenter plusieurs implmentations plus performantes fournies par le framework
.NET.
Plus prcisment, le framework prsente les trois classes System.Timers.Timer, System.Threading.Timer et System.Windows.Forms.Timer. La classe de lespace de noms Forms doit tre utilise pour excuter des tches priodiques graphiques, telles que le rafrachissement de donnes
sur lcran ou lachage conscutif des images dune animation. Le choix entre les classes des
espaces de noms Timers et Threading est plus dlicat et dpend de vos besoins.
La classe System.Timers.Timer
Limplmentation System.Timers.Timer utilise les threads du pool de threads pour excuter
une tche. Aussi, vous devez synchroniser les accs aux ressources consommes par une telle
tche.
Cette classe prsente la proprit double Interval{get;set;} qui permet daccder la priode
exprime en millisecondes. Les mthodes Start() et Stop() vous permettent dactiver ou de
dsactiver le timer. Vous pouvez aussi changer lactivation du timer en utilisant la proprit
bool Enabled{get;set;}.
170
Un dlgu de type ElapsedEventHandler rfrence la mthode qui reprsente la tche excuter. Cette rfrence est reprsente par lvnement ElapsedEventHandler Elapsed. La signature
propose par la dlgation ElapsedEventHandler est celle-ci :
void ElapsedEventHandler(object sender, ElapsedEventArgs e)
Le premier argument rfrence le timer qui a dclench la tche. Ainsi, plusieurs timers peuvent
dclencher une mme mthode et vous pouvez les direncier au moyen de cet argument.
De plus, puisquun dlgu peut rfrencer plusieurs mthodes, un mme timer peut appeler
conscutivement plusieurs mthodes chaque dclenchement. Le second argument contient la
date de dclenchement du timer que vous pouvez rcuprer avec la proprit DateTime ElapsedEvenArgs.SignalTime{get;}. Le programme suivant illustre lutilisation de la classe System.
Timers.Timer :
Exemple 5-20 :
using System.Timers ;
class Program {
static Timer Timer1 = new Timer() ;
static Timer Timer2 = new Timer() ;
static void Main() {
Timer1.Interval = 1000 ; // P
eriode = 1 seconde.
Timer1.Elapsed +=
new ElapsedEventHandler(PeriodicTaskHandler) ;
Timer2.Interval = 2000 ; // P
eriode = 2 secondes.
Timer2.Elapsed +=
new ElapsedEventHandler(PeriodicTaskHandler) ;
Timer2.Elapsed +=
new ElapsedEventHandler(PeriodicTaskHandler) ;
Timer1.Start() ; Timer2.Start() ;
System.Threading.Thread.Sleep(5000) ; // Dors 5 secondes.
Timer1.Stop() ; Timer2.Stop() ;
}
static void PeriodicTaskHandler(object sender,
ElapsedEventArgs e) {
string str = (sender == Timer1) ? "Timer1 " : "Timer2 " ;
str += e.SignalTime.ToLongTimeString() ;
System.Console.WriteLine(str) ;
}
}
Ce programme ache ceci :
Timer1
Timer1
Timer2
Timer2
Timer1
Timer1
Timer2
Timer2
Timer1
19:42:49
19:42:50
19:42:50
19:42:50
19:42:51
19:42:52
19:42:52
19:42:52
19:42:53
171
La classe System.Threading.Timer
La classe System.Threading.Timer est assez similaire la classe System.Timers.Timer. Cette
classe utilise aussi les threads du pool de threads pour excuter une tche mais la dirence
de la classe System.Timers.Timer, elle ne vous permet de prciser exactement un thread.
Une autre dirence est que cette classe vous permet de fournir une chance de dmarrage (due
time en anglais). Lchance de dmarrage dfinie linstant o le timer va dmarrer en prcisant une dure. Vous pouvez modifier lchance de dmarrage nimporte quel moment en
appelant la mthode Change(). En prcisant une chance de dmarrage nulle vous pouvez dmarrer le timer immdiatement. En prcisant la constante System.Threading.Timer.Infinite
vous pouvez stopper le timer. Vous pouvez aussi stopper le timer en appelant la mthode Dispose() mais alors vous ne pourrez plus le redmarrer.
172
de lappel. Lappel est automatiquement pris en charge par un thread du pool de threads du
processus. Le programme peut ultrieurement rcuprer les informations retournes par lappel asynchrone. La technique dappel asynchrone est entirement gre par le CLR.
Le mcanisme que nous dcrivons peut tre utilis dans votre propre architecture. Il est aussi
utilis par les classes du framework .NET, notamment pour grer les flots de donnes dune
manire asynchrone ou pour grer des appels asynchrones sur des objets distants, cest--dire
situs dans un autre domaine dapplication.
Dlgation asynchrone
Lors dun appel asynchrone vous navez pas crer ni vous occupez du thread qui excute le
corps de la mthode. Ce thread est gr par le pool de threads dcrit un peu plus haut.
Avant dutiliser eectivement un dlgu asynchrone, il est judicieux de remarquer que toutes les
dlgations prsentent automatiquement les deux mthodes BeginInvoke() et EndInvoke(). La
signature de ces deux mthodes est calque sur la signature de la dlgation qui les prsente. Par
exemple la dlgation suivante...
delegate int Deleg(int a,int b) ;
...expose les deux mthodes suivantes :
IAsyncResult BeginInvoke(int a,int b,AsyncCallback callback,object o) ;
int EndInvoke(IAsyncResult result) ;
Ces deux mthodes sont produites par le compilateur de C . Cependant, le mcanisme dintellisense de Visual Studio est capable de les infrer partir dune dlgation de faon vous les
prsenter.
Pour appeler une mthode dune manire asynchrone, il faut dabord la rfrencer avec un
dlgu ayant la mme signature. Il sut ensuite dappeler la mthode BeginInvoke() sur ce
dlgu. Comme vous lavez remarqu, le compilateur a fait en sorte que les premiers arguments
de BeginInvoke() soient les arguments de la mthode appeler. Les deux derniers arguments
de cette mthode de type AsyncCallback et object sont expliqus un peu plus loin.
La valeur de retour de lappel asynchrone dune mthode, peut tre rcupre en appelant la
mthode EndInvoke(). L aussi, le compilateur a fait en sorte que le type de la valeur de retour
de EndInvoke() soit le mme que le type de la valeur de retour de la dlgation (ce type est int
dans notre exemple). Lappel EndInvoke() est bloquant. Cest--dire que lappel ne retourne
que lorsque lexcution asynchrone est eectivement termine.
Le programme suivant illustre lappel asynchrone dune mthode WriteSomme(). Notez que
pour bien direncier le thread excutant la mthode Main() et le thread excutant la mthode
WriteSomme(), nous achons la valeur de hachage du thread courant (qui est dirente pour
chaque thread).
Exemple 5-21 :
using System.Threading ;
class Program {
public delegate int Deleg(int a, int b) ;
static int WriteSomme(int a, int b) {
int somme = a + b ;
173
Procdure de finalisation
Vous avez la possibilit de spcifier une mthode qui sera automatiquement appele lorsque
lappel asynchrone sera termin. Cette mthode est appele procdure de finalisation. Une procdure de finalisation est appele par le mme thread que celui qui a excut lappel asynchrone.
Pour utiliser une procdure de finalisation, il vous sut de spcifier la mthode dans une dlgation de type System.AsyncCallback comme avant-dernier paramtre de la mthode BeginInvoke(). Cette mthode doit tre conforme cette dlgation, cest--dire quelle doit retourner
le type void et prendre pour seul argument une interface IAsyncResult. Comme le montre
lexemple suivant, cette mthode doit appeler EndInvoke().
Un problme se pose, car les threads du pool utiliss pour traiter les appels asynchrones sont
des threads background. linstar de lexemple ci-dessous, il faut que vous implmentiez un
mcanisme de gestion dvnements pour vous assurer que lapplication ne se termine pas sans
avoir termin lexcution asynchrone. Linterface IAsyncResult prsente un objet de synchronisation dune classe drive de WaitHandle, mais cet objet est signal ds que le traitement
asynchrone est fini et avant que la procdure de finalisation soit appele. Cet objet ne peut donc
pas permettre dattendre la fin de lexcution de la procdure de finalisation.
Exemple 5-22 :
using
using
using
class
System ;
System.Threading ;
System.Runtime.Remoting.Messaging ;
Program {
174
:
:
:
:
175
asynchrone et dans la procdure de finalisation. Une autre rfrence vers cet objet est la proprit AsyncState de linterface IAsyncResult. Vous pouvez vous en servir pour reprsenter un
tat positionn dans la procdure de finalisation. Par exemple, lvnement de lexemple de la
section prcdente peut tre vu comme un tat. Voici lexemple prcdent rcrit pour utiliser
cette fonctionnalit :
Exemple 5-23 :
using System ;
using System.Threading ;
using System.Runtime.Remoting.Messaging ;
class Program {
public delegate int Deleg(int a, int b) ;
static int WriteSomme(int a, int b) {
Console.WriteLine(
"{0} : Somme = {1}", Thread.CurrentThread.ManagedThreadId , a+b) ;
return a + b ;
}
static void SommeFinie(IAsyncResult async) {
// Attend une seconde pour simuler un traitement long.
Thread.Sleep(1000) ;
Deleg proc =
((AsyncResult)async).AsyncDelegate as Deleg ;
int somme = proc.EndInvoke(async) ;
Console.WriteLine("{0} : Proc
edure de finalisation somme = {1}",
Thread.CurrentThread.ManagedThreadId , somme) ;
((AutoResetEvent)async.AsyncState).Set();
}
static void Main() {
Deleg proc = WriteSomme ;
AutoResetEvent ev = new AutoResetEvent(false) ;
IAsyncResult async = proc.BeginInvoke(10, 10,
new AsyncCallback(SommeFinie), ev) ;
Console.WriteLine(
"{0} : BeginInvoke() appelee ! Attend l
ex
ecution de SommeFinie()",
Thread.CurrentThread.ManagedThreadId ) ;
ev.WaitOne();
Console.WriteLine(
"{0} : Bye... ", Thread.CurrentThread.ManagedThreadId ) ;
}
}
176
Une mthode marque avec lattribut OneWay peut tre appele dune manire synchrone ou
asynchrone. Si une exception est lance et non rattrape durant lexcution dune mthode marque avec lattribut OneWay, elle est propage si lappel est synchrone. Dans le cas dun appel
asynchrone sans retour lexception nest pas propage. Dans la plupart des cas, les mthodes
marques sans retour sont appeles de manire asynchrone.
Les appels asynchrones sans retour eectuent en gnral des tches annexes dont la russite ou
lchec nont pas dincidence sur le bon droulement de lapplication. La plupart du temps, on
les utilise pour communiquer des informations sur le droulement de lapplication.
Lattribut System.ThreadStatic
Par dfaut, un champ statique est partag par tous les threads dun processus. Ce comportement
oblige le dveloppeur synchroniser les accs un tel champ. En appliquant lattribut System.
ThreadStaticAttribute sur un champ statique, vous pouvez contraindre le CLR crer une
instance de ce champ pour chaque thread du processus. Ainsi, lutilisation de ce mcanisme est
bien un moyen dimplmenter la notion danit entre threads et ressources.
Il vaut mieux viter dinitialiser directement lors de sa dclaration un champ statique qui est
marqu avec cet attribut. En eet, dans ce cas seul le thread qui charge la classe eectuera linitialisation sur sa propre version du champ. Ce comportement est illustr par le programme
suivant :
Exemple 5-24 :
using System.Threading ;
class Program {
[System.ThreadStatic]
static string str = "Valeur initiale ";
static void DisplayStr() {
System.Console.WriteLine("Thread#{0} Str={1}",
Thread.CurrentThread.ManagedThreadId , str) ;
}
static void ThreadProc() {
DisplayStr() ;
str = "Valeur ThreadProc" ;
DisplayStr() ;
}
static void Main() {
DisplayStr() ;
Thread thread = new Thread(ThreadProc) ;
thread.Start() ;
177
thread.Join() ;
DisplayStr() ;
}
}
Ce programme ache ceci :
Thread#1
Thread#2
Thread#2
Thread#1
Str=Valeur initiale
Str=
Str=Valeur ThreadProc
Str=Valeur initiale
// 3 threads `
a cr
eer.
178
179
bye
Jai appele fServer(), Compteur = 1
bye
Jai appele fServer(), Compteur = 1
bye
Je suis le thread principal, bye.
180
Linterface System.ComponentModel.ISynchronizeInvoke
Linterface System.ComponentModel.ISynchronizeInvoke est dfinie comme ceci :
public
public
public
public
public
}
object System.ComponentModel.ISynchronizeInvoke{
object
Invoke(Delegate method,object[] args) ;
IAsyncResult BeginInvoke(Delegate method,object[] args) ;
object
EndInvoke(IAsyncResult result) ;
bool
InvokeRequired{get;}
Une implmentation de cette interface peut faire en sorte que certaines mthodes soient toujours excutes par le mme thread, dune manire synchrone ou asynchrone :
Dans le scnario synchrone, un thread T1 appelle une mthode M() sur un objet OBJ. En
fait, T1 appelle la mthode ISynchronizeInvoke.Invoke() en spcifiant un dlgu qui
rfrence OBJ.M() et un tableau contenant les arguments. Un autre thread T2 excute la
mthode OBJ.M(). T1 attend la fin de lexcution puis rcupre les informations de retour
de lappel.
Le scnario asynchrone dire du scnario synchrone par le fait que T1 appelle la mthode
ISynchronizeInvoke.BeginInvoke(). T1 ne reste pas bloqu pendant que T2 excute la
mthode OBJ.M(). Lorsque T1 a besoin des informations de retour de lappel il appelle la
mthode ISynchronizeInvoke.EndInvoke() qui les lui fournira si T2 termin lexcution
de OBJ.M().
Linterface ISynchronizeInvoke est notamment utilise par le framework pour forcer la technologie Windows Form excuter les mthodes dun formulaire avec un mme thread. Cette
contrainte vient du fait que la technologie Windows Form est construite autour de la notion de
messages Windows. Le mme genre de problmatique est aussi adresse par la classe System.
ComponentModel.BackgroundWorker dcrite en page .
Vous pouvez dvelopper vos propres implmentations de linterface ISynchronizeInvoke en
vous inspirant de lexemple Implementing ISynchronizeInvoke fourni par Juval Lowy lURL
http://docs.msdnaa.net/ark_new3.0/cd3/content/Tech_System%20Programming.htm.
Contexte dexcution
Le framework .NET 2.0 prsente des nouvelles classes qui permettent de capturer et de propager
le contexte dexcution du thread courant un autre thread :
System.Security.SecurityContext
Contexte dexcution
181
Une instance de cette classe contient lidentit de lutilisateur Windows sous-jacent sous la
forme dune instance de la classe System.Security.Principal.WindowsIdentity ainsi que
ltat de la pile du thread sous la forme dune instance de la classe System.Threading.
CompressedStack. Ltat de la pile est notamment exploit par le mcanisme CAS lors du
parcours de la pile.
System.Threading.SynchronizationContext
Permet de saranchir des contraintes de compatibilit entre dirents modles de synchronisation.
System.Threading.HostExecutionContext
Permet un hte du moteur dexcution dtre pris en compte dans le contexte dexcution
du thread courant.
System.Runtime.Remoting.Messaging.LogicalCallContext
.NET Remoting permet de propager des informations au travers de contexte .NET Remoting au moyen dinstances de cette classe. Plus dinformations ce sujet sont disponibles en
page 856.
System.Threading.ExecutionContext
Une instance de cette classe contient la runion des contextes cits.
Reprenons lexemple en page 209 qui modifie le contexte de scurit en impersonifiant lutilisateur invite sur le thread courant :
Exemple 5-27 :
...
static void Main() {
System.IntPtr pJeton ;
if (LogonUser(
"invite" ,
// login
string.Empty, // domaine Windows
"invitepwd" , // mot de passe
2,
// LOGON32_LOGON_INTERACTIVE
0,
// LOGON32_PROVIDER_DEFAUT
out pJeton)) {
WindowsIdentity.Impersonate(pJeton) ;
DisplayContext("Main");
ThreadPool.QueueUserWorkItem(Callback,null) ;
CloseHandle(pJeton) ;
}
}
static void Callback(object o) {
DisplayContext("Callback");
}
static void DisplayContext(string s) {
System.Console.WriteLine(s+" Thread#{0} Current user is {1}",
Thread.CurrentThread.ManagedThreadId,
WindowsIdentity.GetCurrent().Name) ;
}
...
182
Contexte dexcution
static void Callback(object o) {
DisplayContext("Callback") ;
}
...
Sans surprise, cet exemple ache :
Main Thread#1 Current user is PSMACCHIA\invit
e
Callback Thread#3 Current user is PSMACCHIA\invit
e
183
6
La gestion de la scurit
Nous commencerons par exposer la technologie Code Access Security (CAS). La technologie CAS permet de mesurer le degr de confiance que lon peut avoir en un assemblage en
vrifiant sa provenance et en sassurant sa non falsification.
Nous verrons ensuite comment mesurer le degr de confiance que lon peut avoir en un utilisateur. La notion dutilisateur est implmente plusieurs niveaux (Windows, ASP.NET,
COM+ etc).
Enfin nous exposerons les dirents mcanismes de cryptographie que le framework met
notre disposition.
Dautres informations relatives la scurit sont disponibles dans cet ouvrage. Notamment
en page 660 nous prsentons direntes techniques pour tablir une communication scurise entre deux machines et en page 957 nous prsentons la scurisation dune application web
ASP.NET.
186
de scurit. En eet, un individu mal intentionn peut exploiter les faiblesses des dirents rseaux et les failles des systmes dexploitations pour substituer son propre code du code mobile
ou pour faire parvenir du code mobile sur votre machine. En outre, la simplicit dobtention
du code mobile nous pousse tlcharger des logiciels que lon naurait pas pris la peine de
commander. On est donc moins regardant quant lditeur qui publie le logiciel. Il est donc
ncessaire de limiter lensemble des permissions accordes du code mobile (peut il dtruire
des fichiers sur mon disque dur ? peut il avoir accs au rseau ? etc).
La technologie COM adresse ce problme dune manire grossire. Avant dexcuter un composant COM qui vient dtre tlcharg, lutilisateur est averti par une fentre popup qui lui laisse
le choix dexcuter ou non le composant. Un certain niveau de garantie quant la provenance
du composant peut tre fourni grce un mcanisme de certificat mais le problme majeur
subsiste : une fois que lutilisateur a choisi dexcuter le composant, le code de celui-ci a les
mmes droits que lutilisateur.
Le fait davoir une machine virtuelle telle que le CLR permet la plateforme .NET dadresser
cette problmatique avec un mcanisme beaucoup plus fin que celui de COM. En eet, le CLR
est mme dintercepter et peut empcher une action malicieuse telle que la destruction dun
fichier avant que celle-ci ne se produise. Ce mcanisme est nomm CAS (Code Access Security). Il
fait lobjet de la prsente section.
Le dploiement de code mobile dvelopp avec .NET 2.0 se fait de prfrence avec la technologie
ClickOnce qui fait lobjet dune section page 76. Il est absolument ncessaire de bien comprendre
CAS pour tirer partie de ClickOnce.
Lorsque du code demande deectuer une opration critique, le CLR doit vrifier au pralable que les assemblages contenant ce code en ont tous la permission.
187
finalement pas accorde. On peut aussi signaler que le mcanisme de rsolution des permissions
naccorde aucune permission par dfaut.
Lorsquun ensemble de permissions a t accord lassemblage, le code de lassemblage peut
modifier cet ensemble et le comportement de la gestion de la scurit durant lexcution. Naturellement ces modifications ne peuvent jamais accorder plus de permissions quil nen a
t accord lassemblage.
Assemblage charger
Obtention des preuves que
peut fournir lassemblage
charger
Preuves
Application des
stratgies de scurit
(configurable)
Permissions accordes au
code de lassemblage
188
void Main(...) {
Fct1(...);
}
void Fct1(...) {
Fct2(...);
} void Fct2(...) {
FileStream fs-File.openRead(...);
}
System.IO.File.OpenRead(string path) {
CodeAccessPermission perm=new
FileIOPermission(
FileIOPermissionAccess.Read,
path);
perm.Demand();
...
}
P3
P3
Assemblage
foo1.exe
Assemblage
foo2.dll
P1
P4
Assemblage
mscorlib.dll
P2
P1
P3
Lappel Demand()
provoque le parcours de la pile
On peut prouver quun assemblage est stock dans un certain rpertoire de la machine.
Ce type de preuve peut tre reprsent par une instance de la classe System.Security.Policy.
ApplicationDirectory.
On peut prouver quun assemblage est stock dans le GAC. Ce type de preuve peut tre
reprsent par une instance de la classe System.Security.Policy.Gac.
On peut prouver quun assemblage a t obtenu/tlcharg partir dun certain site (par
exemple www.smacchia.com). Ce type de preuve peut tre reprsent par une instance de la
classe System.Security.Policy.Site.
On peut prouver quun assemblage a t obtenu partir dune certaine URL (par exemple
www.smacchia.com/asm/UnAssemblage.dll). Ce type de preuve peut tre reprsent par une
instance de la classe System.Security.Policy.Url.
On peut prouver quun assemblage a t obtenu partir dune certaine zone. .NET prsente
cinq zones :
Internet.
189
Un site internet que vous avez ajout dans votre zone Sites sensibles (untrusted site) d
Internet Explorer.
Un site internet que vous avez ajout dans votre zone Sites de confiance (trusted site)
dInternet Explorer.
Intranet local.
Le systme de stockage local (My Computer).
Chacune de ces zones correspond une valeur de lnumration System.Security.SecurityZone. Ce type de preuves peut tre reprsent par une instance de la classe System.Security.
Policy.Zone.
Si lassemblage a t sign par son diteur avec la technologie Authenticode, on peut tablir
une preuve partir de ce certificat. Cette technologie est dcrite en page 230. Ce type de
preuves peut tre reprsent par une instance de la classe System.Security.Policy.Publisher.
Si lassemblage a un nom fort, on peut tablir une preuve partir de ce nom fort. Ce type de
preuves peut tre reprsent par une instance de la classe System.Security.Policy.StrongName.
La composante culture du nom fort nest pas prise en compte dans cette preuve.
Voici un programme permettant de construire et dacher les noms forts des assemblages contenus dans le domaine dapplication courant. Notez lutilisation de la classe
System.Security.Permissions.StrongNamePublicKeyBlob pour rcuprer la cl publique.
Notez quen informatique blob signifie Binary Large Objet. On rappelle quune cl publique
contient 128 octets en plus des 32 octets den-tte.
Exemple 6-1 :
using System ;
using System.Security.Permissions ;
using System.Security.Policy ;
using System.Reflection ;
[assembly: AssemblyKeyFile("Cles.snk")]
class Program {
static void DisplayStrongName(Assembly assembly) {
AssemblyName name = assembly.GetName() ;
byte[] publicKey = name.GetPublicKey() ;
StrongNamePublicKeyBlob blob =
new StrongNamePublicKeyBlob(publicKey);
StrongName sn = new StrongName(blob, name.Name, name.Version);
Console.WriteLine(sn.Name) ;
Console.WriteLine(sn.Version) ;
Console.WriteLine(sn.PublicKey) ;
}
static void Main() {
Assembly[] assemblies = AppDomain.CurrentDomain.GetAssemblies() ;
foreach (Assembly assembly in assemblies)
DisplayStrongName(assembly) ;
}
}
On peut tablir une preuve partir de la valeur de hachage dun assemblage. La valeur
de hachage dun assemblage permet didentifier le rsultat dune compilation dun assemblage, un peu comme un numro de version sauf que la valeur de hachage ne contient
190
Vous pouvez ajouter cette liste vos propres preuves. Celles-ci doivent tre imprativement
ajoutes lassemblage avant quil soit sign. Lide est de vous permettre de configurer totalement le mcanisme de scurit. Ce sujet dpasse le cadre de cet ouvrage.
Une instance de la classe System.Security.Policy.Evidence reprsente une collection de preuves.
En fait chaque instance de cette classe contient deux collections de preuves :
Une collection pour stocker les preuves prsentes par le framework .NET (un des huit types
dcrits ci-dessus).
En pratique les dveloppeurs ont peu dintrt manipuler les preuves. Les instances de la
classe Evidence sont manipules en interne par le framework .NET. Notamment, nous rappelons
quune telle collection de preuves est attribue chaque assemblage lors de son chargement.
Voici un programme qui ache les types des preuves fournies par les assemblages du domaine
dapplication courant. Pour ne pas compliquer inutilement cet exemple, nous nachons pas
directement les preuves sur la console. Nous prfrons acher le type des preuves :
Exemple 6-2 :
PreuveTest.cs
using System ;
using System.Reflection ;
[assembly: AssemblyKeyFile("Cles.snk")]
class Program {
static void DisplayEvidence(Assembly assembly) {
Console.WriteLine(assembly.FullName) ;
foreach (object obj in assembly.Evidence)
Console.WriteLine("
" + obj.GetType()) ;
}
static void Main() {
Assembly[] assemblies = AppDomain.CurrentDomain.GetAssemblies() ;
foreach (Assembly assembly in assemblies)
DisplayEvidence(assembly) ;
}
}
Ce programme ache ceci :
mscorlib, Version=2.0.XXXXX.0, Culture=neutral,
PublicKeyToken=b77a5c561934e089
System.Security.Policy.Zone
System.Security.Policy.Url
System.Security.Policy.StrongName
System.Security.Policy.Hash
PreuveTest, Version=0.0.0.0, Culture=neutral, PublicKeyToken=e0a058df80c8a007
191
System.Security.Policy.Zone
System.Security.Policy.Url
System.Security.Policy.StrongName
System.Security.Policy.Hash
Soit par lhte dun domaine dapplication juste avant le chargement du premier assemblage
du domaine dapplication. Dans ce cas lassemblage qui contient lhte du domaine dapplication doit avoir obtenu la mta-permission ControlEvidence. En gnral vous navez
pas vous soucier de cela. En eet, la plupart du temps vous utilisez un hte de domaine
dapplication dvelopp par Microsoft, en qui les stratgies de scurit font entirement
confiance. Pour linstant Microsoft en fournit quatre. Ils sont dcrits en page 95.
Soit par le chargeur de classes juste avant le chargement dun assemblage qui contient un
type demand par un assemblage dj charg dans le domaine dapplication. Puisque le
chargeur de classe fait partie intgrante du CLR, le CLR lui fait entirement confiance et
lui accorde la mta-permission ControlEvidence.
Dans tous les cas le mcanisme dobtention de preuves a imprativement besoin de la mtapermission ControlEvidence (voir SecurityPermission dans la liste des permission ci-dessous).
Les permissions
Comme son nom lindique, une permission permet du code dexcuter un ensemble dactions.
Certains ouvrages qualifient les permissions de privilges, dautorisations ou de droits. Ces termes
sont cependant trs connots par le vocabulaire Windows aussi nous utiliserons le terme de permission dans le prsent ouvrage.
On verra dans la section suivante quel est lalgorithme qui permet dobtenir lensemble des
permissions pour un assemblage en fonction des preuves apportes par lassemblage. En .NET,
il existe quatre catgories de permissions : les permissions standard, les permission didentit,
les mta-permissions et les permissions propritaires.
192
CAS : Accorder des permissions en fonction des preuves avec les stratgies de scurit
193
Les mta-permissions
Les meta-permissions ou permissions de scurit : Ce sont des permissions alloues au
gestionnaire de scurit lui-mme. La liste des meta-permissions est disponible larticle
SecurityPermissionFlag Enumeration des MSDN. On peut citer la mta-permission dexcuter du code non gr (valeur UnmanagedCode), la mta-permission de fournir des preuves
partir dun assemblage (prsente un peu plus haut, valeur ControlEvidence), la mtapermission dexcuter du code non protg (valeur SkipVerification) etc. La classe System.Security.Permissions.SecurityPermission qui drive de la classe CodeAccessPermission
permet de manipuler les mta-permissions partir du code, sans toutefois pouvoir sautoattribuer de telles permissions.
Configure par...
Sapplique...
Entreprise
un administrateur.
194
Machine
un administrateur.
Utilisateur
un administrateur ou
lutilisateur concern.
lhte du domaine
dapplication.
Domaine
tion
dapplica-
Il existe une hirarchie dans les stratgies de scurit. Concrtement elles sont appliques les
unes aprs les autres, dans lordre dans lequel elles sont numres ci-dessus (de entreprise
domaine dapplication ). Pour cette raison on parle de niveaux de stratgie de scurit. Lapplication dune stratgie de scurit peut imposer que les stratgies de scurit des niveaux suivant ne sappliquent pas. Par exemple lapplication de la stratgie de scurit machine peut
empcher lapplication des stratgies de scurit utilisateur et domaine dapplication . En
gnral, on constate que la plupart des rgles de scurits se trouvent dans la stratgie de scurit
machine (dailleurs, par dfaut les stratgies des autres niveaux accordent toutes les permissions).
Des groupes de code (code groups en anglais) stocks dans une arborescence,
Une liste dassemblages auxquels la stratgie de scurit donne son entire confiance (policy
assemblies ou fully trusted assemblies en anglais).
partir de ces lments et des preuves prsentes par un assemblage, on peut calculer un
ensemble de permissions. Cest lapplication de la stratgie de scurit. Avant dexposer lalgorithme utilis pour cela, il faut expliquer ce que sont les groupes de code.
Un groupe de code associe une preuve un ensemble de permissions de la liste densemble
de permissions de la stratgie de scurit. Les groupes de code sont stocks dans une arborescence dans une stratgie de scurit, cest--dire quun groupe de code parent peut avoir zro,
un ou plusieurs groupes de code enfants. Il ny a pas dobligation de relation entre la preuve
dun groupe enfant et la preuve de son groupe parent, il en va de mme pour les permissions
accordes. Cependant afin de faciliter ladministration de la scurit, il est recommand (dans
la mesure du possible) de positionner les relations (parent enfant) avec des liens logiques et de
dfinir les permissions accordes de faon hirarchique.
Pour comprendre pourquoi les groupes de code sont stocks dans une arborescence, il faut se
pencher sur lalgorithme utilis lors de lapplication dune stratgie de scurit.
Les documentations ocielles utilisent ce vocabulaire : si lune des preuves dun assemblage est
identique la preuve dun groupe de code, on dit que lassemblage est membre de ce groupe de
code. Cela justifie lappellation groupe de code. La preuve dun groupe de code permet de dfinir
un groupe dassemblages (de code) : ce groupe est constitu par les assemblages sui vrifient la
preuve.
CAS : Accorder des permissions en fonction des preuves avec les stratgies de scurit
195
Sachez que dans le cas o vous fabriqueriez vos propres preuves, il faudrait fabriquer vos propres
groupes de code pour les exploiter.
Sinon, lalgorithme commence parcourir larborescence des groupes de code selon les
rgles suivantes :
Les groupes de code enfants dun groupe de code sont pris en compte par lalgorithme
seulement si lassemblage est membre du groupe de code parent.
Lensemble des permissions accordes lassemblage par une stratgie de scurit est lunion
des ensembles de permissions des groupes de code dont lassemblage est membre.
Chaque groupe de code peut tre marqu de faon finaliser la stratgie de scurit laquelle il appartient. Dans ce cas, si un assemblage appartient ce groupe de code le mcanisme dvaluation nira pas valuer les niveaux de stratgies de scurit suivantes.
Chaque groupe de code peut tre marqu comme exclusif. Dans ce cas, si un assemblage
appartient ce groupe de code il ne bnficiera que des permissions associes ce groupe.
196
FullTrust
ECMA_Strong_Name
SN.PublicKey=ECMA
FullTrust
MyComputer_Zone
Zone=MyComputer
FullTrust
NetCodeGroup_1
All
LocalIntranet_Zone
Zone=Intranet local
Permission daccder au
site dorigine de lassemblage
LocalIntranet
NetCodeGroup_1
All_Code
Restricted_Zone
All
All
Nothing
Nothing
Permission daccder en
lecture seule aux fichiers
du rpertoire
Internet_Zone
NetCodeGroup_1
Zone=Internet
All
Internet
Permission daccder au
site dorigine de lassemblage
Trusted_Zone
NetCodeGroup_1
Zone=site de confiance
All
Internet
Permission daccder au
site dorigine de lassemblage
Au niveau de la scurit .NET, ces deux outils ont exactement la mme fonctionnalit : la configuration des stratgies de scurit entreprise , machine et utilisateur de la machine. La
stratgie de scurit dun domaine dapplication ne peut se faire que programmatiquement, en
utilisant les classes adquates du framework .NET.
La Figure 6 -4 prsente une vue gnrale de mscorcfg.msc qui permet de retrouver immdiatement les notions prsentes que lon vient dexposer :
Lorsque vous tes un administrateur, pour chaque stratgie de scurit vous pouvez (ou lorsquun utilisateur veut configurer la stratgie de scurit le concernant) :
CAS : Accorder des permissions en fonction des preuves avec les stratgies de scurit
197
Paramtres de configuration ne
concernant pas la scurit
Paramtres de la stratgie de
scurit entreprise
Arborescences
des groupes
de code
Paramtres
de la stratgie
de scurit
machine
Liste des
ensembles de
permissions
Assemblages en
qui la politique
fait confiance
Paramtres de la stratgie de
scurit concernant lutilisateur
logu
Paramtres de configuration ne
concernant pas la scurit
Exporter ou importer la stratgie de scurit dans un (ou partir dun) fichier de dploiement .msi. Ces fonctionnalits sont accessibles dans les menus Stratgies de scurit Crer
un fichier de dploiement... et Stratgies de scurit Ouvrir ...
Pour un assemblage donn, vous pouvez obtenir la liste des groupes de code (dune politique ou de toutes les politiques) dont il est membre, et la liste des permissions qui lui sont
accordes par la ou les stratgies de scurit concernes. Cette fonctionnalit est accessible
dans le menu Stratgies de scurit Evaluer un assemblage...
Sur une machine donne, les paramtres dune stratgie de scurit sont stocks dans des fichiers
de configuration au format XML. Voici leur localisation :
Stratgie de scurit entreprise
Windows XP/2000/NT
198
Windows 98/Me
Windows 98/Me
%USERPROFILE%\Application
config\vXXXX\Security.config
data\Microsoft\CLR
security
Windows 98/Me
Ces paramtres sont stocks pour chaque version du CLR. Ainsi, si plusieurs versions du CLR
cohabitent, chacune ses propres paramtres de scurit.
Le fait que ces paramtres de configuration soient stocks au format XML ore des possibilits
intressantes, comme limport de groupes de code ou densembles de permissions prsentes
au format XML. Les trois articles Importing a Permission Using an XML File, Importing
a Permission Set Using an XML File et Importing a Code Group Using an XML File des
MSDN traitent ce sujet en dtail.
Les directives de loutil caspol.exe accessibles en ligne de commande, sont dcrites dans les
MSDN larticle Code Access Security Policy Tool. travers les nombreuses directives de cet
outil, vous y retrouverez toutes les fonctionnalits prsentes ci-dessus.
199
attribut. Il faut vraiment tre certain que le code distribu ne peut pas tre dtourn. Dailleurs,
seuls certains assemblages standard de Microsoft sont marqus avec cet attribut.
Enfin, soyez conscient que ne pas utiliser cet attribut ne reprsente pas une garantie ultime
contre le dtournement de votre code. En eet, votre code peut toujours tre invoqu partir
dun assemblage qui a la permission FullTrust qui lui mme est invoqu partir dun assemblage qui na pas la permission FullTrust.
La mthode Demand()
La mthode Demand() vrifie que le code courant dispose des permissions reprsentes par le
jeu de permission. Une remonte de la pile des appels est alors dclenche afin de retrouver la
hirarchie de toutes les mthodes responsable de cet appel. Chaque mthode de cette pile est
galement teste pour le jeu de permissions. Si lune dentre elle ne dispose pas de toutes les
permissions requises, alors une exception de type SecurityException est leve. Lexcution du
code sarrte et la suite de la mthode lorigine de la remonte de la pile nest pas excute. Le
programme suivant sassure quil a la permission de lire un fichier avant de le faire :
Exemple 6-3 :
using System.Security ;
using System.Security.Permissions ;
class Program {
static void Main() {
string sFichier = @"C:\data.txt" ;
CodeAccessPermission cap =
new FileIOPermission(FileIOPermissionAccess.Read, sFichier) ;
200
Lintrt de demander explicitement les permissions est danticiper un ventuel refus et dadapter le comportement de lapplication en consquence. La demande explicite permet galement
de mettre en place des stratgies plus sophistiques dans lesquelles le jeu de permissions testes
est ventuellement construit en fonction du contexte comme le rle de lutilisateur.
201
dfaut sont restaurs. Il est aussi possible dannuler les limitations de droits avec le mthode
RevertDeny().
Une alternative existe au couple de mthodes Deny()/RevertDeny(). Le couple de mthode
PermitOnly()/RevertPermitOnly() permet aussi de modifier temporairement lensemble des
permissions accordes la mthode courante. La dirence entre ces deux manires est que
Deny() spcifie les permissions ne pas accorder alors que PermitOnly() spcifie les permissions
accorder.
202
une mme mthode, il faut quentre chaque appel, la mthode statique CodeAccessPermission.RevertAccess() soit appele. Pour supprimer le parcours de la pile dappel pour plusieurs
permissions dans une mme mthode il est donc obligatoire dappeler Assert() sur une instance
de PermissionSet.
Par prudence il vaut mieux ninvoquer la mthode Assert() quau moment o lon en a besoin
et pas, par exemple en dbut de mthode. Il faut dans le mme esprit invoquer la mthode RevertAssert() le plus tt possible. Le mieux est en gnral de servir dune structure try/finally
Notez enfin que contrairement aux mthodes Deny()/RevertDeny(), PermitOnly()/RevertPermitOnly() et Assert()/RevertAssert(), la mthode Demand() est la seule qui ninflue pas sur
le droulement ultrieur des oprations.
Linterface System.Security.IPermission
Linterface System.Security.IPermission permet de faire des oprations ensemblistes sur un ensemble de permission. Cette interface est implmente par la classe PermissionSet, la classe
CodeAccessPermission ainsi que ses classes drives. Voici sa dfinition :
public interface System.Security.IPermission {
IPermission Union(IPermission rhs) ;
IPermission Intersect(IPermission rhs) ;
bool
IsSubsetOf(IPermission rhs) ;
IPermission Copy() ;
void
Demand() ;
}
Avec la mthode IsSubsetOf() vous pouvez calculer des relations dinclusion entre permissions.
Par exemple la permission qui donne tous les accs au dossier "C:\MonRep" inclue la permission
qui donne tous les accs au dossier "C:\MonRep\patrick\".
Exemple 6-6 :
using System.Security ;
using System.Security.Permissions ;
class Program {
static void Main() {
string rep1 = @"C:\Monrep" ;
string rep2 = @"C:\Monrep\Patrick" ;
CodeAccessPermission cap1 = new FileIOPermission(
FileIOPermissionAccess.AllAccess, rep1) ;
CodeAccessPermission cap2 = new FileIOPermission(
FileIOPermissionAccess.AllAccess, rep2) ;
bool b = cap2.IsSubsetOf(cap1);
// Ici b vaut true.
}
}
203
Vous pouvez aussi calculer des nouvelles permissions partir dintersections ou dunions de
permissions avec les mthodes Union() et Intersect(). Vous pouvez ainsi rutiliser des permissions volues. Ces types doprations ensemblistes sur les permissions sont trs utiliss par le
gestionnaire de scurit. En pratique les dveloppeurs nont pas beaucoup doccasions de les
utiliser.
Description de laction
InheritanceDemand
Lorsquun assemblage est charg, permet dimposer que les types drivs du type sur lequel sapplique cette action aient les permissions spcifies.
LinkDemand
204
Description de laction
RequestMinimum
RequestOptional
Spcifie une ou plusieurs permissions requises pour excuter correctement lassemblage. Cependant lassemblage est charg mme si ces permissions ne lui sont pas accordes. On parle de permission optionnelle.
RequestRefuse
Par exemple, un assemblage qui ralise des accs une base de donnes besoin de la permission SqlClientPermission. Il peut avoir besoin de la permission RegistryPermission pour
rcuprer des paramtres mais ceci peut tre optionnel si il prvoit des paramtres par dfaut.
Enfin, il se peut quil nait absolument pas besoin de permissions telles que WebPermission ou
UIPermission. Il faudrait donc quil soit marqu par les attributs suivants :
Exemple 6-8 :
Program.cs
using System.Security.Permissions ;
using System.Data.SqlClient ;
[assembly: SqlClientPermission(SecurityAction.RequestMinimum)]
[assembly: RegistryPermission(SecurityAction.RequestOptional)]
[assembly: UIPermission(SecurityAction.RequestRefuse)]
[assembly: System.Net.WebPermission(SecurityAction.RequestRefuse)]
class Program { public static void Main() { } }
Loutil permview.exe vous permet de visualiser ces attributs. Loutil permcalc.exe dcrit en page
82 va plus loin et permet de calculer lensemble des permissions requis par un assemblage.
Lors de lchec dune demande ou dune assertion de permissions vous ne pouvez pas rattraper dexception lendroit o lerreur a eu lieu.
Les arguments passs aux permissions (comme le nom dun rpertoire pour grer les permissions daccs ce rpertoire) doivent tre connus la compilation. Plus gnralement,
vous ne pouvez pas mettre en place une logique de scurit dynamique (i.e base sur des
informations connues seulement qu lexcution telles que le rle de lutilisateur courant
par exemple).
205
Les avantages dutiliser des attributs pour manipuler des permissions sont :
La possibilit davoir accs ces attributs et aux paramtres de ces attributs au travers des
mtadonnes de lassemblage ou en utilisant loutil permview.exe.
206
thodes statiques telles que GetUserStoreForAssembly(), GetMachineStoreForAssembly(), GetMachineStoreForDomain() ou GetMachineStoreForApplication() permettant dobtenir le rpertoire correspondant la porte dsire.
Voici un exemple montrant comment accder au rpertoire de stockage isol :
Exemple 6-9 :
using System.IO ;
using System.IO.IsolatedStorage ;
class Program {
static void Main() {
// Obtient le repertoire en fonction de lutilisateur
// et de lassemblage courant.
IsolatedStorageFile isf =
IsolatedStorageFile.GetUserStoreForAssembly() ;
IsolatedStorageFileStream isfs = new IsolatedStorageFileStream(
"pref.txt", FileMode.Create, isf) ;
StreamWriter sw = new StreamWriter(isfs) ;
sw.WriteLine("Mettre ici vos pr
ef
erences...") ;
sw.Close() ;
}
}
Le rpertoire utilis par ce programme pour faire du stockage isol sous mon identit est celui
de la Figure 6 -5:
207
chaque client peut tre autoris consulter son compte ( partir dinternet). Une telle application ncessite de sparer les utilisateurs entre les clients, les simples caissiers, les caissiers avec des
responsabilits et les administrateurs. Chacune de ces catgories est appele rle. Pour lapplication chaque utilisateur joue zro, un ou plusieurs rles. En fonction du cahier des charges de
lapplication bancaire, les dveloppeurs doivent pouvoir vrifier directement partir du code
le rle de lutilisateur courant. Dans le code, cette vrification se fait avant toutes les excutions
dune fonctionnalit critique. Dans ce contexte, pour une application, accorder de la confiance
un utilisateur revient dterminer quels sont les rles quil joue.
208
209
210
Support .NET pour les contrles des accs aux ressources Windows
211
mthode nous retourne lidentit de lutilisateur seulement si le thread nest pas en cours demprunt didentit.
Lorsquun thread tente dobtenir des droits daccs une ressource, Windows tablit son verdict
partir du SID du thread et de la DACL de la ressource. Les ACE sont valus dans lordre dans
lequel ils sont stocks dans la DACL. Chaque ACE accorde ou retire des droits daccs lorsque
le SID du thread est inclus dans son SID. Lensemble des droits daccs demands est accord
ds que tous les droits daccs ont t accords durant lvaluation. Les droits daccs sont tous
refuss ds quun seul des droits daccs demands est refus par un ACE. Comprenez bien que
lordre de stockage des ACE dans la DACL importe et que Windows nvalue pas ncessairement
tous les ACE lors dune demande de droits daccs.
Pour certains types de ressources Windows permet lhritage de SD. Cette possibilit se rvle
essentielle par exemple pour un administrateur qui souhaiterait configurer les SD de milliers
de fichiers contenus dans un rpertoire en une seule manipulation.
Chaque SD dune ressource Windows contient une seconde liste dACE nomme System Access
Control List (SACL). Cette seconde liste est exploite par Windows pour auditer les accs une
ressource. linstar des ACE dune DACL les ACE dune SACL associent chacun un SID une
liste de droits daccs. Au contraire des ACE dune DACL les ACE dune SACL contiennent deux
informations binaires qui peuvent sinterprter comme ceci :
212
lvnement un de mes droits daccs a t accord un SID inclus dans mon SID doit-il tre
logu ?
lvnement un de mes droits daccs a t refus un SID inclus dans mon SID doit-il tre logu ?
Clairement, lordre de stockage des ACE dans une SACL nimporte pas.
Le nouvel espace de noms System.Security.AccessControl dfinit des types permettant dexploiter les SD. Aprs avoir prsent les types ddis la manipulation des SD de ressources
spcifiques Windows nous prsenterons des types prvus pour exploiter les SD dune manire
gnrique (i.e indpendante du type de ressource Windows sous jacent).
Support .NET pour les contrles des accs aux ressources Windows
213
System.Security.AccessControl.EventWaitHandleAuditRule
System.Security.AccessControl.FileSystemAuditRule
System.Security.AccessControl.MutexAuditRule
System.Security.AccessControl.ObjectAuditRule
System.DirectoryServices.ActiveDirectoryAuditRule
System.Security.AccessControl.RegistryAuditRule
System.Security.AccessControl.SemaphoreAuditRule
Voici la liste des numrations reprsentant les droits daccs. Par exemple, lnumration
FileSystemRights contient une valeur AppendData tandis que lnumration MutexRights
contient une valeur TakeOwnership.
Microsoft.Iis.Metabase.MetaKeyRights
System.Security.AccessControl.EventWaitHandleRights
System.Security.AccessControl.FileSystemRights
System.Security.AccessControl.MutexRights
System.Security.AccessControl.RegistryRights
System.Security.AccessControl.SemaphoreRights
Enfin les dirents types du framework .NET reprsentant directement les ressources Windows
concernes (System.Threading.Mutex, System.IO.File etc) ont des nouveaux constructeurs acceptant des ACL et des mthodes Set/GetAccessControl permettant de positionner et dobtenir
les ACL dune instance. Voici un exemple illustrant tout ceci lors de la cration dun fichier avec
une DACL :
Exemple 6-13 :
using System.Security.AccessControl ;
using System.Security.Principal ;
using System.IO ;
class Program {
static void Main() {
// Cree la DACL.
FileSecurity dacl = new FileSecurity() ;
// Rempli la DACL avec un ACE.
FileSystemAccessRule ace = new FileSystemAccessRule(
WindowsIdentity.GetCurrent().Name,
FileSystemRights.AppendData | FileSystemRights.ReadData,
AccessControlType.Allow) ;
dacl.AddAccessRule(ace) ;
// Cree un nouveau fichier avec cette DACL.
System.IO.FileStream fileStream = new System.IO.FileStream(
@"fichier.bin" , FileMode.Create , FileSystemRights.Write ,
FileShare.None, 4096 , FileOptions.None, dacl ) ;
fileStream.Write(new byte[] { 0, 1, 2, 3 }, 0, 4) ;
fileStream.Close() ;
}
}
Vous pouvez visualiser les droits daccs aux fichiers fichier.bin comme ceci : Proprit du fichier fichier.bin Scurit Paramtres avancs Autorisations Modifier les autorisations spciales
214
accordes au principal avec lequel vous avez excut le programme lecture de donnes et ajout de
donnes.
Si longlet Scurit ne sache pas il faut eectuer cette manipulation : Panneau de configuration
Options des dossiers Achage Utiliser le partage de fichiers simple (recommand).
Support .NET pour les contrles des accs aux ressources Windows
215
dacl.AddAccess(
AccessControlType.Allow, // Allow OU Deny.
WindowsIdentity.GetCurrent().Owner, // Utilisateur courant.
0x00180000, // Masque : TakeOwnerShip ET Synchronize
//
equivalent `
a
//(int) MutexRights.TakeOwnership | (int) MutexRights.Synchronize
InheritanceFlags.None, // D
esactive...
PropagationFlags.None); // ...lheritage dACE.
string sSDDL = csd.GetSddlForm( AccessControlSections.Owner ) ;
Console.WriteLine("Security Descriptor : " + sSDDL) ;
MutexSecurity mutexSec = new MutexSecurity() ;
mutexSec.SetSecurityDescriptorSddlForm(sSDDL);
AuthorizationRuleCollection aces = mutexSec.GetAccessRules(
true, true, typeof(NTAccount)) ;
foreach (AuthorizationRule ace in aces){
if (ace is MutexAccessRule){
MutexAccessRule mutexAce = (MutexAccessRule)ace ;
Console.WriteLine("-->SID : " +
mutexAce.IdentityReference.Value) ;
Console.WriteLine("
Type de droits dacc`
es : " +
mutexAce.AccessControlType.ToString()) ;
if (0xffffffff == (uint)mutexAce.MutexRights)
Console.WriteLine("
Tous les droits !") ;
else
Console.WriteLine("
Droits : " +
mutexAce.MutexRights.ToString()) ;
}
}
}
}
Cet exemple ache :
On remarque que par dfaut un DACL dun nouveau SD contient lACE qui donne tous
les droits tout le monde. Vous pouvez utiliser la mthode CommonAcl.Purge(SecurityIdentifier) pour supprimer les ACE relatives un SID dans un ACL.
216
Ces trois oprations ncessitent la mta-permission CAS SecurityPermissionFlag.ControlPrincipal pour tre menes bien.
217
Cette dernire alternative constitue la politique de principal prise par dfaut par tous les domaines dapplication.
218
219
S(P(M)) = P(S(M)) = M
On ne peut obtenir M si lon dtient P(M) sans connatre la paire de cls (S,P).
On ne peut obtenir M si lon dtient S(M) sans connatre la paire de cls (S,P).
On voit que les cls S et P jouent un rle symtrique do le nom de ce type dalgorithme. Pour
que Julien envoi le message M Mathieu dune manire confidentielle, il doit lui envoyer une
des deux versions cryptes du message. Mathieu pourra alors obtenir le message original en
appliquant lalgorithme symtrique sur le message crypt avec les deux cls. En pratique, Julien
et Mathieu se seront mis daccord sur la cl utilise pour lencryption. Si un tiers intercepte la
version crypte du message, il ne pourra pas obtenir le message original puisquil ne dtient pas
la paire de cl (S,P). Tout ceci est rsum dans la figure suivante :
Rseau peu sr, seuls les
messages encrypts y circulent
Mathieu
Connat le couple
de cls (S,P)
S(M1)
S(M2)
Julien
Connat le couple
de cls (S,P)
Un tiers
Ne peut retrouver un message partir de sa version
encrypte car il ne possde pas la paire de cls (S,P)
220
puisque pendant des sicles, les algorithmes ont constitu la pierre angulaire de la cryptographie.
221
sDec = utf.GetString(bDec) ;
}
System.Console.WriteLine("Message : " + sMsg) ;
System.Console.WriteLine("Encrypt
e : " + sEnc) ;
System.Console.WriteLine("Decrypt
e : " + sDec) ;
}
}
Cet exemple ache ceci :
Message : Le message a` encrypter !
Encrypte : iNnbHD1R3nci5tGElIvKIBapaTmfqHEV
Decrypte : Le message `a encrypter !
Dans lexemple prcdent, notez que les objets encryptor et decryptor implmentent tous les
deux linterface ICryptoTransform. Ceci est une consquence de laspect symtrique des algorithmes.
La classe DESCryptoServiceProvider peut aussi tre utilise pour construire une cl et un vecteur dinitialisation. Ainsi lexemple prcdent peut tre rcrit comme suit pour exploiter cette
possibilit :
Exemple 6-21 :
...
des.GenerateKey() ;
des.GenerateIV() ;
ICryptoTransform encryptor = des.CreateEncryptor() ;
ICryptoTransform decryptor = des.CreateDecryptor() ;
...
La paire de cls doit tre connue des deux parties souhaitant schanger des messages. un
moment donn, il faut que cette paire de cls circule sur un canal de communication entre
les deux parties.
Une paire de cl nest valable quentre deux parties. Si Julien souhaite changer des messages crypts avec Mathieu et avec Sbastien il doit dtenir deux paires de cls : une pour
crypter les changes avec Mathieu et une pour crypter les changes avec Sbastien. On voit
donc apparatre un problme de gestion de cls.
Les algorithmes asymtriques rsolvent ces deux problmes. Un algorithme asymtrique a les
trois proprits dun algorithme symtrique que nous rappelons en reprenant les notations de
la section prcdente :
222
En plus de ces trois proprits un algorithme asymtrique a les deux proprits suivantes :
Nous avons donc introduit une dissymtrie dans notre paire de cl do le nom de ce type dalgorithme.
On comprend maintenant comment Mathieu et Julien peuvent exploiter ce type dalgorithme
pour schanger des messages sans schanger de cls et sans avoir grer un grand nombre de
cls. Il sut quils calculent chacun une paire de cl que nous nommons (Sj,Pj) et (Sm,Pm).
Mathieu diuse la cl Sm tandis que Julien diuse la cl Sj. Toute personne souhaitant envoyer
un message M Mathieu dune manire confidentielle peut utiliser la cl Sm pour lencrypter. Si
Mathieu a pris le soin de garder prive la cl Pm, il est alors le seul pouvoir dcrypter Sm(M) en
calculant Pm(Sm(M)). Tout ceci est rsum dans la figure ci-dessous
Rseau peu sr, seuls les
messages encrypts y circulent
Mathieu
Connat le couple
de cls (Sm,Pm) ainsi
que la cl Sj
Sm(M1)
Sj(M2)
Julien
Connat le couple
de cls (Sj,Pj) ainsi
que la cl Sm
Un tiers
Connat les cles Sj et Sm. Ne peut retrouver un message partir de
sa version encrypte car il ne possde ni la cl Pj, ni la cl Pm
223
Lalgorithme RSA
Lalgorithme RSA cr en 1977, est lheure actuelle lalgorithme asymtrique le plus utilis. La
protection des cartes bancaires ou des messages militaires lutilise. La plateforme .NET lutilise
aussi. Le nom de RSA provient du nom de ses inventeurs, R.L. Rivest, A. Shamir et L.M Adelman.
Lalgorithme RSA est bas sur une proprit des grands nombres premiers. Soit deux grands
nombres premiers A et B. Il est trs facile de calculer le produit de A et de B. En revanche, lorsque
lon ne connat que le produit AB, il est trs dicile de calculer les nombres A et B. Sans rentrer
dans les dtails de lalgorithme RSA, vous pouvez considrer que la paire de nombre (A,B) dfinit la cl prive tandis que le produit de A et de B dfinit la cl publique.
Tant que lon ne saura obtenir une paire de nombres premiers A et B partir de leur produit
quen temps polynomial, lalgorithme RSA restera extrmement fiable. En fait, on ne sait mme
pas prouver quil existe un algorithme en un temps meilleur que le temps polynomial. La plupart des mathmaticiens contemporains supposent que ce problme restera inaccessible pendant encore de nombreuses dcennies. Nanmoins, lhistoire a montr quil est quasiment impossible destimer quand un problme mathmatique sera rsolu.
Sachez enfin que lon utilise des algorithmes statistiques ecaces pour le calcul de grands
nombres premiers. Ce type dalgorithme permet de dterminer si un grand nombre est premier
un degr de certitude qui peut tre aussi grand que lon veut sans jamais tre gal 100%.
Si par ces deux calculs Julien obtient la mme valeur, il peut tre sr que lauteur de FOO dtient
la cl prive Pm. On dit que Pm(x) constitue une signature numrique du fichier FOO. Si Mathieu
224
a pu convaincre Julien quil est le seul dtenteur de la cl prive Pm, Julien peut tre sr que
lauteur de FOO est Mathieu.
Une faille dans cet algorithme existe nanmoins. Nous avons dit que Le calcul de la valeur de
hachage la particularit de fournir de manire quasiment sre deux nombres dirents pour deux
fichiers dirents . Si un tiers parvient trouver une squence doctets qui produit la mme
valeur de hachage, il peut utiliser la signature numrique prcdente dans un fichier contenant
cette squence doctets. Julien naura aucun moyen de savoir que ce fichier na pas t produit
par un dtenteur de la cl prive Pm. Cependant la taille des valeurs de hachage est de lordre
de 20 octets. Il y a donc moins dune chance sur 10 puissance 48 pour quune squence doctets
prise au hasard fournisse une valeur de hachage donne.
En page 28 nous expliquons comment la plateforme .NET permet de signer numriquement les
assemblages. En page 230 nous verrons que cette technique est utilise dans les environnements
Windows depuis bien avant .NET pour authentifier des fichiers. Nous prsenterons alors une
technologie permettant de pouvoir convaincre quon est le seul dtenteur dune certaine cl
prive.
225
226
Cette API est capable de grer les modifications de mots de passe. Autrement dit, si vous stockez
des donnes en les encryptant pour un utilisateur donn, vous serez capable de les exploiter
mme lorsque le mot de passe de cet utilisateur aura t modifi. Ceci est possible grce un
systme de stockage des cls expires. Plus de dtails ce sujet sont disponibles dans larticle
Windows Data Protection des MSDN.
La classe System.Security.Cryptography.ProtectedData
Lexemple suivant montre comment utiliser la classe System.Security.Cryptography.ProtectedData pour protger des donnes au niveau dun utilisateur. Nous aurions pu utiliser la valeur DataProtectionScope.LocalMachine pour protger ces donnes au niveau de la machine
courante. Dans cet exemple, nous exploitons loption dajouter de lentropie lencryption. Cela
signifie quun processus sexcutant dans le contexte adquat (i.e sous le bon utilisateur ou sur la
bonne machine) naura pas la possibilit de dcrypter les donnes sil ne connat pas lentropie
utilise pour les encrypter. Vous pouvez donc considrer lentropie comme une sorte de cl
secondaire :
Exemple 6-23 :
using System.Security.Cryptography ;
class Program{
static void Main() {
string sMsg = "Le message `
a encrypter !" ;
string sEnc, sDec ;
System.Text.Encoding utf = new System.Text.UTF8Encoding() ;
byte[] entropy = new byte[] { 1, 2, 3, 4, 5, 6, 7, 8 };
{
byte[] bMsg = utf.GetBytes(sMsg) ;
byte[] bEnc = ProtectedData.Protect(
bMsg , entropy , DataProtectionScope.CurrentUser);
sEnc = System.Convert.ToBase64String(bEnc) ;
}
{
byte[] bEnc = System.Convert.FromBase64String(sEnc) ;
byte[] bDec = ProtectedData.Unprotect(
bEnc, entropy, DataProtectionScope.CurrentUser);
sDec = utf.GetString(bDec) ;
}
System.Console.WriteLine("Message : " + sMsg) ;
System.Console.WriteLine("Encrypt
e : " + sEnc) ;
System.Console.WriteLine("Decrypt
e : " + sDec) ;
}
}
Cet exemple ache ceci :
Message : Le message `a encrypter!
Encrypte: AQAAANCMnd8BFdERjHoAwE/Cl+sBAAAA3uy2/pifMEGRALpANT44y
QAAAAACAAAAAAADZgAAqAAAABAAAAA4UMUfUFqt5Xrz5U6hAVJ1AAAAAASAAACg
AAAAEAAAAHMxZCILz7K6JJc4Mmd/4P8YAAAAiiE+UBJdtaReGyP9vMsmw6HsqHM
227
LksdXFAAAAMRqfALSa8aaMm1mkestfBudeX91
Decrypte: Le message `a encrypter!
La classe System.Security.Cryptography.ProtectedMemory
La classe System.Security.Cryptography.ProtectedMemory permet de protger des donnes
au niveau de portes plus fines que celles prsentes par la classe ProtectedData. Les options
prsentes par lnumration MemoryProtectionScope sont les suivantes :
SameProcess : Spcifie que seul du code invoqu dans le mme processus que celui qui a
encrypt les donnes sera mme de les dcrypter.
SameLogon : Spcifie que seul du code invoqu dans un processus dans le mme contexte
utilisateur que celui qui a encrypt les donnes sera mme de les dcrypter. Cela implique
notamment quil faut que les oprations dencryptions et de dcryptions dune mme donne aient lieux durant la mme session Windows.
CrossProcess : Spcifie que les donnes peuvent tre dcryptes par nimporte quel code
excut dans nimporte quel processus la condition que le systme dexploitation nait pas
t redmarr entre lopration dencryption et lopration de dcryption.
Lexemple suivant illustre lutilisation de cette classe. Il faut que les donnes encrypter soient
stockes sur un tableau doctets dune taille multiple de 16 :
Exemple 6-24 :
using System.Security.Cryptography ;
class Program {
static void Main() {
string sMsg = "01234567890123456789012345678901" ;
System.Text.Encoding utf = new System.Text.UTF8Encoding() ;
System.Console.WriteLine("Message : " + sMsg) ;
byte[] bMsg = utf.GetBytes(sMsg) ;
ProtectedMemory.Protect(bMsg,
MemoryProtectionScope.SameProcess);
System.Console.WriteLine("Encrypt
e : " + utf.GetString(bMsg)) ;
ProtectedMemory.Unprotect(bMsg,
MemoryProtectionScope.SameProcess);
System.Console.WriteLine("Decrypt
e : " + utf.GetString(bMsg)) ;
}
}
Cet exemple ache ceci :
Message : 01234567890123456789012345678901
Encrypte : m;SH<^"?vfn6b{m.Op%L
Decrypte : 01234567890123456789012345678901
La classe System.Security.SecureString
La manipulation des chanes de caractres avec la classe String prsente plusieurs vulnrabilits
si lon considre quun individu mal intentionn peut avoir accs aux pages mmoires dun
processus Windows :
228
Pour pallier cette vulnrabilit, le framework .NET prsente la classe System.Security.SecureString. Cette classe dont limplmentation se base sur les services DPAPI, permet de stocker en
mmoire une chane de caractre sous une forme encrypte.
Vous pouvez initialiser une instance de SecureString soit partir dun tableau doctets (que
vous pouvez par la suite mettre zro) soit en la construisant caractre aprs caractre. La classe
Marshal prsente plusieurs mthodes pour rcuprer une chane de caractres encrypte dans
une instance de SecureString.
Lexemple suivant montre comment utiliser une instance de SecureString pour stocker un mot
de passe saisi par un utilisateur sur la console. Nous achons ensuite cette chane de caractres
sur la console. Pour les besoins de lexemple, nous convertissons linstance de BSTR contenant
la chane de caractre dcrypte en une instance de String. En pratique, il faut viter cette manipulation. Fatalement, pour exploiter une chane de caractres scurise il faut un moment
ou un autre la dcrypter en mmoire. Il faut alors la stocker dans une zone mmoire que
lon matrise pour pouvoir la dtruire ds que possible et pour viter les problmes poss par
la classe String vus prcdemment :
Exemple 6-25 :
using System ;
using System.Runtime.InteropServices ;
class Program {
static void Main() {
System.Security.SecureString pwd = new
System.Security.SecureString();
ConsoleKeyInfo nextKey = Console.ReadKey(true) ;
while(nextKey.Key != ConsoleKey.Enter){
pwd.AppendChar(nextKey.KeyChar);
Console.Write("*") ;
nextKey = Console.ReadKey(true) ;
}
Console.WriteLine() ;
pwd.MakeReadOnly();
IntPtr bstr = Marshal.SecureStringToBSTR(pwd) ;
// Recup`ere une instance de la classe String
// pour les besoins de lexemple.
try{
Console.WriteLine( Marshal.PtrToStringAuto(bstr) ) ; }
finally{ Marshal.ZeroFreeBSTR(bstr) ; }
}
}
229
230
La technologie Authenticode doit tre utilise pour identifier lauteur ou lentreprise qui
a fabriqu le fichier excutable. Elle permet dexcuter un programme sans crainte dune
ventuelle falsification.
La technologie du nom fort doit tre utilise pour identifier un assemblage (i.e pour nommer dune manire unique chaque assemblage et mme, chaque version dun mme assemblage). Elle permet notamment le stockage dassemblages cte cte.
La technologie Authenticode permet de signer toutes sortes de fichiers excutables, les assemblages .NET comme les fichiers excutable Windows contenant du code natif. La technologie du nom fort ne se cantonne quant elle quaux assemblages.
La vrification de la validit dun fichier excutable par la technologie Authenticode nest
eectue par Windows quune seule fois, lorsque lon tlcharge le fichier ou lorsquon linstalle (par exemple partir dun CD). La vrification du nom fort est eectue par le CLR
chaque chargement dun assemblage sign.
Cependant, la dirence principale entre ces deux technologies rside dans le concept de certificat.
231
232
Le mcanisme CAS est capable dobtenir une preuve partir dun assemblage sign avec un
certificat X.509 (voir page 189). Vous pouvez ainsi dcider que les assemblages signs avec un
certificat peuvent sexcuter avec plus (ou moins) de permissions que les autres.
Les dtails de la cration dun certificat, de la signature dun excutable par un certificat et de
la vrification dun certificat dpassent le cadre du prsent ouvrage. Tout ceci est dcrit dans
larticle Signing and Checking Code with Authenticode des MSDN.
En outre, sachez que le framework .NET prsente les espaces de noms System.Security.
Cryptography.X509Certificates et System.Security.Cryptography.Pkcs qui contiennent
des types spcialiss dans la manipulation de ces standards ainsi que dans la manipulation des
listes de certificats.
7
Rflexion, liens tardifs,
attributs
Nous avons parl page 17 des mtadonnes et de la faon dont elles sont physiquement stockes
dans les assemblages. Nous allons voir dans ce chapitre quelles constituent la base des mcanismes de rflexion et dattributs.
Le mcanisme de rflexion
Le mcanisme de rflexion dnomme lutilisation durant lexcution, des mtadonnes de type
dun assemblage. En gnral cet assemblage est charg explicitement lors de lexcution dun
autre assemblage mais il peut tre aussi construit puis charg dynamiquement.
Le mot rflexion est utilis pour montrer que lon utilise limage dun assemblage (comme une
image dans un miroir). Cette image est constitue par les mtadonnes de type de lassemblage.
Lors de la dcouverte des types dun assemblage lexcution par lanalyse dynamique de
ses mtadonnes de type. Par exemple, les outils ildasm.exe ou Reflector chargent explicitement un module dun assemblage et analyse son contenu (voir page 21).
Lors de lutilisation de liens tardifs. Cette technique consiste utiliser une classe situe dans
un assemblage qui nest pas connu la compilation. La technique du lien tardif est particulirement utilise par les langages interprts comme les langages de script.
Lorsque lon souhaite exploiter les informations contenues dans les attributs.
234
Lorsque lon souhaite accder aux membres non publics dune classe partir de lextrieur
de la classe. Bien videmment cette pratique est viter, mais il est ncessaire parfois dy
avoir recours, par exemple pour faire des tests unitaires qui ne peuvent se satisfaire des seuls
membres non publics.
Lors de la construction dynamique dun assemblage. Pour utiliser les classes dun assemblage construit dynamiquement on utilise la technique du lien tardif explicite.
Le mcanisme de rflexion est utilis par le CLR et le framework dans de multiples cas. Par
exemple, limplmentation par dfaut de la mthode Equals() sur un type valeur utilise la
rflexion pour comparer deux instances champs champs.
La rflexion est galement utilise par le CLR lors de la srialisation dun objet afin de connatre
les champs srialiser, ou encore par le ramasse-miettes, qui sen sert pour construire larbre de
rfrencement lors dune collecte dobjets.
Il est plus abstrait que le format TLB et le langage IDL. Par exemple il nutilise pas dadresses
physiques. Ainsi il peut tre utilis la fois sur des machines 32 et 64 bits.
Contrairement aux mtadonnes du format TLB, les mtadonnes .NET sont toujours physiquement contenues dans le module quelles dcrivent.
Le niveau de dtail de description des donnes est beaucoup plus pouss que dans le format
TLB. Concrtement on peut trs simplement avoir toutes les informations possibles sur
nimporte quel lment dclar dans un assemblage (par exemple le type dun argument
dune mthode dune classe).
Le niveau de dtail sans prcdent de la rflexion .NET est d de nombreuses classes de base
du Framework .NET qui permettent dextraire et dutiliser les mtadonnes de type dun assemblage contenu dans un domaine dapplication. La plupart de ses classes se trouvent dans lespace
de noms System.Reflection. Il y a une classe pour chaque type dlment dun assemblage :
une classe dont les instances reprsentent les mthodes des classes
(System.Reflection.MethodInfo) ;
une classe dont les instances reprsentent les champs des classes
(System.Reflection.FieldInfo) ;
Le mcanisme de rflexion
235
une classe dont les instances reprsentent les paramtres des mthodes
(System.Reflection.ParameterInfo).
etc.
Finalement ces classes ne reprsentent rien dautre quun moyen de visualiser logiquement lensemble des mtadonnes de type. La visualisation nest pas physique dans le sens o certains lments utiliss pour lorganisation interne dun assemblage (comme les jetons de mtadonnes)
ne sont pas reprsents.
Toutes les classes de lespace de noms System.Reflection sutilisent dune manire trs logique. Par exemple, partir dune instance de System.Reflection.Assembly, on peut obtenir un tableau dinstances de System.Type. partir dune instance de System.Type on
peut obtenir un tableau dinstances de System.Reflection.MethodInfo. partir dune instance de System.Reflection.MethodInfo, on peut obtenir un tableau dinstances de System.
Reflection.ParameterInfo. Tout ceci est illustr par la Figure 7-1.
Byte[]
GetILAsByteArray()
GetModules()
MethodBody
GetMethodBody()
Module
ConstructorInfo
GetTypes()
GetTypes()
GetConstructor()
GetParameters()
ParameterInfo
GetParameters()
GetMethodBody()
Assembly
MethodInfo
GetMethods()
Type
GetEvents()
GetProperties()
GetFields()
FieldInfo
EventInfo
PropertyInfo
ReflectedType
236
Exemple 7-1 :
using System ;
using System.Reflection ;
class Program{
public static void Main(){
Assembly assembly = Assembly.GetExecutingAssembly() ;
foreach (Type type in assembly.GetTypes()){
Console.WriteLine("Classe : " + type) ;
foreach (MethodInfo method in type.GetMethods()){
Console.WriteLine(" M
ethode : " + method) ;
foreach (ParameterInfo param in method.GetParameters())
Console.WriteLine("
Param : " +
param.GetType()) ;
}
}
}
}
Voici lachage de ce programme :
Classe : Program
Methode : Void Main()
Methode : System.Type GetType()
Methode : System.String ToString()
Methode : Boolean Equals(System.Object)
Param : System.Reflection.ParameterInfo
Methode : Int32 GetHashCode()
Le mcanisme de rflexion
237
"PublicKeyToken=b77a5c561934e089, Version=" +
typeof(System.Object).Assembly.GetName().Version.ToString() ;
// Charge explicitement lassemblage System.
// Nul besoin de charger lassemblage mscorlib car il est
// automatiquement et implicitement charg
e par le CLR.
Assembly.ReflectionOnlyLoad(systemAsmStrongName) ;
// Pour chaque assemblage du domaine dapplication courant...
foreach (Assembly a in AppDomain.CurrentDomain.GetAssemblies()){
Console.WriteLine("\nAssemblage:" + a.GetName().Name) ;
// Pour chaque type de cet assemblage...
foreach (Type t in a.GetTypes()){
// Seules les classes publiques sont retenues...
if (!t.IsClass || !t.IsPublic) continue ;
bool bDeriveDException = false ;
bool bDeriveDirectement = true ;
// System.Exception est-il un type de base de ce type ?
Type baseType = t.BaseType ;
while (baseType != null && !bDeriveDException){
// Pour trouver les classes dattributs remplacer cette
// ligne par if( baseType == typeof(System.Attribute))
if (baseType == typeof(System.Exception))
bDeriveDException = true ;
else bDeriveDirectement = false ;
baseType = baseType.BaseType ;
}// end while
//
//
//
if
238
classe Assembly prsente la proprit bool ReflectionOnly{get;} qui permet de savoir quun
assemblage a t charg par cette mthode.
239
du corps de la mthode en langage machine. Cette information est physiquement stocke dans
une zone mmoire associe la mthode, appele stub.
Cette constatation est trs importante puisque dans un langage comme C++, lorsquune mthode nest ni virtuelle ni abstraite (i.e ni virtuelle pure en terminologie C++), le compilateur
calcule lemplacement physique du corps de la mthode en langage machine. Ensuite, le compilateur insre un pointeur vers cet emplacement chaque appel de la mthode concerne.
Cette dirence confre un avantage certains .NET, puisque les compilateurs nont plus se
soucier de dtails techniques tels que la reprsentation dune adresse mmoire. Le code IL est
totalement indpendant de la couche physique qui lexcute.
En ce qui concerne les liens dynamiques, presque tout se passe comme pour les liens prcoces. Le
compilateur insre le jeton de mtadonne correspondant la mthode virtuelle (ou abstraite)
appeler, lendroit du code IL o lappel a lieu. On parle ici du jeton de mtadonne de la
mthode dfinie dans le type de la rfrence sur laquelle lappel lieu. Cest ensuite au CLR
de dterminer lexcution vers quelle mthode se brancher en fonction de limplmentation
exacte de lobjet rfrenc. Cest le polymorphisme.
Cette technique dinsertion par le compilateur du jeton de mtadonne pour les liens prcoces
et dynamiques est utilise dans les trois cas suivant :
Lorsque le code contenu dans un module appelle une mthode dfinie dans le mme module.
Lorsque le code contenu dans un module appelle une mthode dfinie dans un autre module du mme assemblage.
Lorsque le code contenu dans un module dun assemblage appelle une mthode dfinie
dans un autre assemblage rfrenc la compilation. Si ce dernier assemblage nest pas dj
charg lors de lappel de la mthode, le CLR le charge implicitement.
Liens tardifs
En .NET, le code dun assemblage A peut instancier et utiliser lexcution un type dfini dans
un assemblage B non rfrenc la compilation de A. On qualifie ce type de lien lien tardif. Dans
la premire dition du prsent ouvrage ainsi que dans dautres documents, les liens tardifs sont
parfois nomms liens tardifs explicites. Le lien est tardif dans le sens o, comme nous allons le
voir, il est cr aprs la compilation, durant lexcution. Le lien est explicite dans le sens o le
nom de la mthode appeler doit tre prcis explicitement dans une chane de caractres.
Lide de lien tardif nest pas nouvelle dans le monde du dveloppement Microsoft. Tout le mcanisme dit dAutomation dans la technologie COM, qui utilise linterface IDispatch, nest rien
dautre quune usine gaz faite pour pouvoir crer des liens tardifs partir de langages de
scripts ou peu typs, comme VB. La notion de liens tardifs existe aussi en Java.
Le concept de lien tardif fait partie des ides que les dveloppeurs habitus au langage C++ ont
du mal assimiler. En eet, en C++ seul les liens prcoces et les liens dynamiques existent. Le
problme de comprhension vient du fait suivant : on comprend bien que les informations ncessaires pour crer un lien (i.e les jetons de mtadonnes) sont dans lassemblage B contenant la
classe appeler, mais on ne comprend pas pourquoi le dveloppeur ne profite pas de la capacit
du compilateur crer des liens prcoces et dynamiques en rfrenant B la compilation de
A. Il existe plusieurs raisons direntes :
240
La raison la plus courante est que pour certains langages il ny a pas de compilateur ! Dans
un langage type script, les instructions sont interprtes une une. Dans ce cas il ne peut y
avoir que des liens tardifs. Les liens tardifs permettent donc dutiliser des classes compiles et
contenues dans des assemblages, partir de langages interprts. Le fait que la technique des
liens tardifs soit trs facile utiliser en .NET, fait que lon peut facilement crer des langages
interprts propritaires (tel que le langage IronPython http://www.ironpython.com/).
Certaines applications ont pour vocation dappeler le code de nimporte quel assemblage
qui lui est propos. Lexemple typique est loutil open-source NUnit qui permet de tester le
code de nimporte quel assemblage en invoquant ses mthodes. Nous approfondissons ce
sujet lors de la construction dun attribut personnalis un peu plus loin dans ce chapitre.
On doit utiliser des liens tardifs entre le code dun assemblage A et les classes dun assemblage B si B nexiste pas lors de la compilation de A. Cette situation est dcrite un peu plus
loin dans ce chapitre o lon y parle de construction dynamique dassemblages.
Les performances des liens tardifs explicites sont beaucoup moins bonnes que celles des
liens prcoces et des liens tardifs implicites (mme si vous utilisez les optimisations prsentes un peu plus loin).
On ne peut crer un lien tardif avec une classe obfusque. En eet, lors de lobfuscation, le
nom de la classe est chang dans lassemblage qui la contient. Or, la technique du lien tardif
retrouve la classe avec laquelle le lien tardif doit tre cr grce son nom. Comme nous
allons le voir, ce nom est stock du cot du consommateur de la classe dans une chane de
caractres. Il y a donc incompatibilit entre obfuscation et lien tardif.
Prciser un type
Intressons-nous aux direntes techniques qui permettent de prciser un type :
241
Certaines mthodes de certaines classes acceptent une chane de caractres qui contient le
nom complet du type (i.e avec son espace de noms).
En C on utilise souvent le mot-cl typeof() qui prend en paramtre un type, et retourne linstance de System.Type adquate.
Vous pouvez aussi utiliser une des surcharges de la mthode statique GetType() de la
classe System.Type.
Si un type est encapsul dans un autre type, vous pouvez utiliser une des surcharges des
mthodes non statiques GetNestedType() ou GetNestedTypes() de la classe System.
Type.
Vous pouvez aussi utiliser les mthodes non statiques GetType() GetTypes() ou GetExportedTypes() de la classe System.Reflection.Assembly.
Vous pouvez aussi utiliser les mthodes non statiques GetType() GetTypes() ou FindTypes() de la classe System.Relection.Module.
Supposons maintenant que le programme suivant soit compil dans un assemblage Foo.dll.
Nous allons exposer plusieurs manires permettant de crer une instance de la classe NMFoo.
Calc partir dassemblages qui ne rfrencent pas Foo.dll.
Exemple 7-3 :
using System ;
namespace NMFoo {
public class Calc {
public Calc() {
Console.WriteLine("Calc.Constructeur appel
e") ;
}
public int Sum(int a, int b) {
Console.WriteLine("Methode Calc.Sum() appel
ee") ;
return a + b ;
}
}
}
242
Chacune de ces mthodes se dcline en de nombreuses versions surcharges (et parfois gnriques) avec les arguments suivants :
Une classe, sous forme dune chane de caractres ou dune instance de System.Type ;
Si lassemblage qui contient la classe nest pas prsent dans le domaine dapplication, lappel CreateInstance() ou CreateInstanceFrom() dclenche le chargement de cet assemblage. Pour raliser ce chargement, une des mthodes System.AppDomain.Load() ou System.AppDomain.LoadFrom() est appele en interne, selon que lon appelle CreateInstance()
ou CreateInstanceFrom(). Un constructeur de la classe est choisi, partir des arguments passs.
Une instance de la classe ObjectHandle, renfermant un objet marshall par valeur est retourne.
Dans le chapitre 22 relatif .NET Remoting, nous prsentons une autre utilisation de ces
mthodes dans le cadre dapplications distribues.
Les surcharges de CreateInstance() o le type est prcis sous la forme dune instance de
System.Type, retournent directement une rfrence vers un objet.
La classe System.Activator prsente aussi la mthode CreateComInstanceFrom() utilise pour
crer des instances dobjets COM et la mthode GetObject() utilise pour crer des objets distants.
243
Cas particuliers
Avec les mthodes prsentes, vous pouvez crer une instance de pratiquement nimporte
quelle classe ou structure. Deux cas particuliers sont noter :
244
La mthode Type.InvokeMember()
Revenons vers la mthode Type.InvokeMember() qui nous a servi prcdemment crer une
instance dun type inconnu la compilation en invoquant un de ces constructeurs. Cette mthode accomplit en interne trois tches :
Elle cherche un membre du type sur lequel elle est appele qui correspond aux informations quon lui passe.
Elle utilise le membre (invocation pour une mthode, cration dobjet puis invocation
pour un constructeur, obtention ou dfinition de la valeur pour un champ...).
Lexemple suivant montre comment invoquer la mthode Sum() sur notre instance de la classe
NMFoo.Calc (notez que lors de la phase de dboguage, le dbogueur est capable de continuer sa
course dans le corps de la mthode invoquer grce un lien tardif) :
Exemple 7-8 :
using System ;
using System.Reflection ;
class Program {
static void Main() {
object obj = AppDomain.CurrentDomain.CreateInstanceAndUnwrap(
"Foo.dll", "NMFoo.Calc") ;
Type type = obj.GetType() ;
object[] parametres = new object[2] ;
parametres[0] = 7 ;
parametres[1] = 8 ;
int result = (int)type.InvokeMember(
"Sum",
// Nom de la m
ethode.
BindingFlags.InvokeMethod,
null,
// Pas besoin de binder.
obj,
// Lobjet cible.
parametres); // Les param`
etres.
// Ici, result vaut 15.
}
}
La surcharge la plus couramment utilise de la mthode Type.InvokeMember() est :
245
//
//
//
//
//
Le nom du membre.
Quel membre doit
etre pris en compte.
Les r`
egles de recherche du membre.
Lobjet sur lequel invoquer le membre.
Les arguments de lappel.
Le paramtre invokeAttr est un indicateur binaire qui signale sur quel type de membre la recherche va se porter. Pour chercher sur les mthodes, nous utilisons lindicateur BindingFlags.
InvokeMethod. Les dirents indicateurs sont dcrits en dtail dans les MSDN, dans larticle
BindingFlags Enumeration.
Le paramtre binder indique un objet de type Binder qui va orienter InvokeMember() sur la
faon dont elle va eectuer ses recherches. La plupart du temps vous positionnerez ce paramtre
null pour indiquer que vous souhaitez utiliser la proprit System.Type.DefaultBinder. Un
objet de type Binder fournit ce genre dinformations :
Il indique quelles conversions de types sont acceptes ou non pour les arguments. Dans
lexemple prcdent, nous aurions pu fournir deux arguments de type double. Grce au
DefaultBinder, lappel de la mthode aurait march car il supporte la conversion du type
double vers le type int.
Il indique si nous utilisons les paramtres optionnels dans notre liste de paramtres.
Tout ceci (notamment la table de conversion de type) est dcrit en dtail dans les MSDN lentre Type.DefaultBinder Property. Vous pouvez crer vos propres objets Binder en drivant
de la classe Binder. Nanmoins une instance de DefaultBinder sut dans la plupart des cas et
cette possibilit est trs rarement utilise.
Si une exception est lance durant lappel du membre, InvokeMember() intercepte lexception
et relance une exception de type System.Reflection.TargetInvocationException. Naturellement lexception lance dans la mthode est rfrence par la proprit InnerException de lexception relance.
Notez enfin que lorsque vous crez un lien tardif, vous ne pouvez pas accder, a priori,
aux membres non publics. Lexception System.Security.SecurityException est alors, en
gnral, lance. Nanmoins si le bit TypeInformation de System.Security.Permissions.
ReflectionPermissionFlags (accessible par une instance de la classe System.Security.
Permissions.ReflectionPermission) est positionn, vous avez accs aux membres non publics.
Si le bit MemberAccess est positionn, vous avez accs aux types non visibles (i.e encapsuls dans
dautres types, dune manire non publique).
246
Exemple 7-9 :
using System ;
using System.Reflection ;
class Program {
static void Main() {
object obj = AppDomain.CurrentDomain.CreateInstanceAndUnwrap(
"Foo.dll", "NMFoo.Calc") ;
Type type = obj.GetType() ;
// Creation du lien tardif.
MethodInfo methodInfo = type.GetMethod("Sum") ;
object[] parametres = new object[2] ;
parametres[0] = 7 ;
parametres[1] = 8 ;
int result ;
// 10 invocations de Sum au travers du lien tardif.
for (int i = 0 ; i < 10 ; i++)
result = (int)methodInfo.Invoke(obj, parametres) ;
}
}
247
namespace NMFoo {
public interface ICalc {
int Sum(int a, int b) ;
}
}
Exemple 7-13 :
using System ;
namespace NMFoo {
public class CalcAvecInterface : ICalc {
public CalcAvecInterface() {
Console.WriteLine("Calc.Constructeur appel
e") ;
}
public int Sum(int a, int b) {
Console.WriteLine("Methode Calc.Sum() appel
ee") ;
return a + b ;
}
}
}
Exemple 7-14 :
Code de lassemblage client de la classe cible, non connue `
a la
compilation (ProgramAsm.cs)
using
using
using
class
System ;
System.Reflection ;
NMFoo ;
Program {
248
Soyez attentif au transtypage explicite en ICalc de lobjet retourn par la mthode CreateInstanceAndUnwrap() qui nous permet dutiliser ensuite un lien dynamique sur la mthode Sum().
On aurait pu viter ce transtypage en utilisant la surcharge gnrique de la mthode Activator.CreateInstance<ICalc>().
La figure suivante reprend lorganisation ainsi que les liens entre nos trois assemblages :
Assemblage contenant la classe
Program
Assemblage contenant
linterface ICalc
Les attributs
Quest ce quun attribut ?
Un attribut est une information qui se rapporte un lment du code tel quune classe ou une
mthode. Par exemple, le framework .NET fournit lattribut System.ObsoleteAttribute qui
peut tre utilis pour marquer une mthode comme ceci (notez la syntaxe avec les crochets []) :
[System.ObsoleteAttribute()]
void Fct() { }
Linformation la mthode Fct() est marque avec lattribut System.ObsoleteAttribute est
insre dans lassemblage lors de la compilation. Cette information peut alors tre exploite
par le compilateur C . Lorsquil rencontre un appel cette mthode, il peut mettre un avertissement indiquant quil vaut mieux viter dinvoquer une mthode obsolte, qui risque de
Les attributs
249
disparatre dans les prochaines versions. Sans attribut, vous seriez oblig de commenter et de
documenter lobsolescence de la mthode Fct(). La faiblesse de cette dmarche est que vous
nauriez aucune garantie que vos clients soient au courant de cette obsolescence.
Un attribut peut tre consomm par le CLR lexcution. Par exemple, le framework .NET
prsente lattribut System.ThreadStaticAttribute. Lorsquun champ statique est marqu
avec cet attribut, le CLR fait en sorte qu lexcution il existe une version de ce champ par
thread.
Un attribut peut tre consomm par un dbuggeur lexcution. Ainsi lattribut System.
Diagnostics.DebuggerDisplayAttribute permet de personnaliser lachage dun lment du code (ltat dun objet par exemple) lors du dboguage.
Un attribut peut tre consomm par un outil. Par exemple, le framework .NET prsente
lattribut System.Runtime.InteropServices.ComVisibleAttribute. Lorsquune classe est
marque avec cet attribut, loutil tlbexp.exe gnre un fichier qui permettra ultrieurement cette classe dtre consomme comme si elle tait dfinie avec la technologie COM.
Un attribut peut tre consomm par votre propre code lexcution, en ayant recours au
mcanisme de rflexion pour accder linformation. Ainsi, il peut tre intressant de dfinir lintgrit des champs de vos classes en marquant leurs champs avec des attributs. Tel
champ entier doit tre dans telle plage de valeur. Tel champ de type rfrence ne doit jamais tre nul. Tel champ doit rfrencer une chane dau plus 100 caractres etc. Grce au
mcanisme de rflexion, il est ais dcrire du code capable de valider ltat de nimporte
quelle classe dont les champs sont marqus. Nous dtaillons un peu plus loin un exemple
de consommation dattributs lexcution par du code propritaire.
Un attribut peut tre consomm par un utilisateur qui analyse un assemblage avec un outil
tel que ildasm.exe. Ainsi, nous pouvons imaginer un attribut qui permettrait dassocier
une chane de caractres un lment du code source. Cette chane de caractres tant insre dans lassemblage, il devient alors possible de consulter des commentaires sans avoir
besoin du code source.
Un attribut est ncessairement dfinit par une classe qui drive de la classe System.
Attribute.
250
Une classe dattribut nest instancie que lorsque le mcanisme de rflexion accde un
de ses reprsentant. Selon lutilisation, une classe dattribut nest donc pas forcment instancie ( linstar de la classe System.ObsoleteAttribute qui na pas tre utilis avec le
mcanisme de rflexion).
Le Framework .NET met votre disposition de nombreux attributs. Certains sont destins
tre consomms par le CLR. Dautres sont consomms par le compilateur ou des outils
fournis par MS.
Vous avez la possibilit de crer vos propres classes dattributs. Ils seront alors ncessairement consomms par vos propres programmes ou outils puisque vous ne pouvez pas modifier ni le compilateur ni le CLR.
Par convention, le nom dune classe dattribut est sux par Attribute. Cependant, un attribut nomm XXXAttribute peut en C tre utilis la fois avec lexpression XXXAttribute
mais aussi avec lexpression XXX lorsquil marque un lment du code.
Dans le chapitre consacr la gnricit, nous prsentons en page 497 les rgles relatives au
recoupements entre les deux notions dattribut et de gnricit.
All
Assembly
Lassemblage lui-mme.
Class
Les classes.
Constructor
Les constructeurs.
Delegate
Les dlgus.
Enum
Les numrations.
Event
Les vnements.
Field
Les champs.
GenericParameter
Interface
Les interfaces.
Method
Les mthodes.
Module
Les modules.
Les attributs
251
Parameter
Property
ReturnValue
Struct
Les structures.
252
Loutil NUnit permet dexcuter et donc de tester, nimporte quelle mthode de nimporte quel
assemblage. Comme il ny a pas de sens excuter toutes les mthodes dun assemblage, NUnit
nexcute que les mthodes marques avec un attribut de type TestAttribute.
Pour implmenter une version simplifie de ce comportement, nous nous imposons les
contraintes suivantes :
Le test dune mthode est considr comme concluant si celle-ci nenvoie aucune exception
non rattrape.
Nous dfinissions un attribut TestAttribute qui peut sappliquer sur les mthodes. Lattribut peut tre paramtr par le nombre de fois que la mthode doit tre excute (proprit
int TestAttribute.nTime). Lattribut peut aussi tre paramtr pour permettre dignorer
une mthode marque (proprit bool TestAttribute.nTime).
Les attributs
foreach(Type type in assembly.GetTypes() ){
foreach(MethodInfo method in type.GetMethods() ){
// Obtient les attributs de type TestAttribute
// qui marquent la m
ethode r
ef
erenc
ee par method.
// Declenche lappel `
a TestAttribute.ctor().
object[] attributes = method.GetCustomAttributes(
typeof(TestAttribute),false) ;
if( attributes.Length == 1 ){
// Obtient une ref
erence de type TestAttribute.
TestAttribute testAttribute =
attributes[0] as TestAttribute ;
// Si la methode nest pas `
a ignorer.
if( ! testAttribute.Ignore ){
object [] parameters = new object[0] ;
object instance = Activator.CreateInstance(type) ;
// Invoque la m
ethode nTime fois.
for(int i=0;i< testAttribute.nTime ; i++){
try{
//Invocation de la m
ethode avec un lien tardif.
method.Invoke(instance,parameters) ;
} catch(TargetInvocationException ex) {
Console.WriteLine(
"La m
ethode {" + type.FullName + "." +
method.Name +
"} a lanc
e une exception de type " +
ex.InnerException.GetType() +
" lors de lex
ecution " + (i+1) + ".") ;
}// end catch(...
}// end for(...
}// end if( ! attribute.Ignore )
}// end if( attributes.Length == 1 )
}// end foreach(MethodInfo...
}// end foreach(Type...
}
}
class Foo {
[Test()]
public void Plante() {
Console.WriteLine("Plante()") ;
throw new ApplicationException() ;
}
int state = 0 ;
[Test(4)]
public void PlanteLaDeuxiemeFois() {
Console.WriteLine("PlanteLaDeuxiemeFois()") ;
state++ ;
if (state == 2) throw new ApplicationException() ;
}
253
254
Ce programme ache :
TestAttribute.ctor() par defaut.
Plante()
La methode {Foo.Plante} a lance une exception de type
System.ApplicationException lors de lex
ecution 1.
TestAttribute.ctor(int).
PlanteLaDeuxiemeFois()
PlanteLaDeuxiemeFois()
La methode {Foo.PlanteLaDeuxiemeFois} a lanc
e une exception de type
System.ApplicationException lors de lex
ecution 2.
PlanteLaDeuxiemeFois()
PlanteLaDeuxiemeFois()
TestAttribute.ctor() par defaut.
NePlantePas()
TestAttribute.ctor() par defaut.
Plusieurs remarques simposent :
255
256
Lexcution dun script dans un navigateur web : lide est quun script dans un page Web
construit dynamiquement un assemblage qui est sauv de faon persistante chez le client.
Lexcution dun script dans une page ASP.NET : lide est quun script dans un page
ASP.NET, construit dynamiquement un assemblage qui est sauv de faon persistante
dans le cache du serveur. Ainsi, seule la premire visite de la page provoque la cration de
lassemblage.
La compilation dune expression rgulire fournie lexcution. Le problme de lvaluation dune expression rgulire est reprsentatif de cette classe de problmes qui admettent
une solution globale lente, et des solutions particulires rapides. Lide est de construire
une solution particulire lexcution, lorsque lexpression rgulire est fournie, plutt que
dimplmenter au dveloppement la solution globale lente. Le framework .NET permet la
compilation des expressions rgulires et tout ceci est expliqu page 625.
Tout ceci peut paratre abstrait aussi allons-nous prsenter un exemple concret qui a la mrite
davoir une utilit potentielle dans lindustrie.
Un problme concret
Prsentation du problme
Lexemple prsent ici est bas sur lvaluation dun polynme P coecients entiers, dfini
sur les entiers. Imaginez une application o ou un tel polynme est fourni lexcution (par
exemple par un utilisateur). Supposons que lapplication doive, par la suite, valuer ce polynme pour un trs grand nombre de valeurs. Nous allons montrer que ce problme concret
admet une solution trs optimise, utilisant la fabrication lexcution dun nouvel assemblage.
Par la suite nous allons supposer que le polynme P saisi par lutilisateur durant lexcution de
lapplication est :
P(x) = 66x3 + 83x2 13 735x + 30 139
Pour la petite histoire, ce polynme dcouvert par les mathmaticiens Dress et Landreau en
1999, a la particularit de ne prendre pour valeurs que des nombres premiers pour x entre 26
et 19 (inclus). Nous aurions pu prendre nimporte quel autre polynme coecients entiers
pour illustrer notre exemple.
257
Pour nos tests de performance, nous valuerons P pour chacune de ces valeurs 10 millions de
fois. On ne travaillera quavec des entiers sur quatre octets signs (le type int en C , qui est aussi
le type System.Int32). Pour ceux qui ont oubli leurs cours de mathmatique nous rappelons
que la manire la plus conomique en terme doprations dvaluer ce polynme est de lcrire
sous cette forme :
P(x) = 30 139 + x(13 735 + x(83 + 66x))
Cette astuce sappelle la mthode de Hrner. Seulement trois multiplications et trois additions
sont ncessaires pour valuer P pour une valeur de x.
258
Problme potentiel 2 : Le compilateur JIT pourrait tre assez intelligent pour sapercevoir que ce nest pas la peine dappeler la mthode Evalue() puisquon nutilise pas ses rsultats. Nous avons pu tablir que ce nest pas le cas en vrifiant que si la mthode Evalue()
incrmente un compteur global, les rsultats sont sensiblement quivalents.
259
ldc.i4
0x75bb // coef 30139 mis sur le haut de la pile
ldarg.0
// valeur de x mise sur le haut de la pile
ldc.i4
0xffffca59// coef -13735 mis sur le haut de la pile
ldarg.0
// valeur de x mise sur le haut de la pile
ldc.i4.s
83
// coef 83 mis sur le haut de la pile
ldarg.0
// valeur de x mise sur le haut de la pile
ldc.i4.s
66
// coef 66 mis sur le haut de la pile
mul
//
add
//
mul
//
Evaluation du polyn
ome en x
add
//
avec trois additions et
mul
//
trois multiplications
add
//
stloc.0
br.s
IL_001a
ldloc.0
ret
method Program::Calc
Il sut maintenant de produire ce code IL pour nimporte quel polynme, avec les classes de
lespace de noms System.Reflection.Emit.
Dans cet exemple, linstruction IL ldarg est utilise avec le paramtre 0 pour charger le
premier argument. Si la mthode navait pas t statique ldarg.0 aurait reprsent le pointeur this et il aurait fallu utiliser ldarg.1 pour accder au premier argument. Par la suite
la mthode ne sera pas statique, aussi nous utiliserons ldarg.1 pour accder au premier
lment.
Voici le code :
Exemple 7-21 :
using
using
using
using
using
System ;
System.Reflection ;
System.Reflection.Emit ;
System.Threading ;
System.Diagnostics ;
260
261
262
Sauver lassemblage avec la ligne de code suivant, juste avant le retour de la mthode BuildCodeInternal() :
TheAsm.Save("MainMod.dll") ;
Nous pouvons comparer le code IL produit par le compilateur C et le code IL produit par notre
propre programme :
Code IL produit par le compilateur C
.method private hidebysig static
int32 Calc(int32 x) cil managed
{
// Code size 28 (0x1c)
.maxstack 7
.locals init ([0]
int32 CS$00000003$00000000)
IL_0000: ldc.i4 0x75bb
IL_0005: ldarg.0
IL_0006: ldc.i4 0xffffca59
IL_000b: ldarg.0
IL_000c: ldc.i4.s 83
IL_000e: ldarg.0
IL_000f: ldc.i4.s 66
IL_0011: mul
IL_0012: add
IL_0013: mul
IL_0014: add
IL_0015: mul
IL_0016: add
IL_0017: stloc.0
IL_0018: br.s IL_001a
IL_001a: ldloc.0
IL_001b: ret
} // end of method CCalc::Calc
Nous nutilisons pas de variables locales, contrairement au code produit par le compilateur
C.
Nous nutilisons pas linstruction optimise ldc.i4.s qui charge sur la pile une valeur dun
octet (i.e entre -128 et 127) dans quatre octets. Nous pourrions optimiser notre programme
en utilisant cette instruction.
263
Conclusion
Vous avez vu ici un exemple reprsentatif des possibilits de la cration dynamique dassemblages. On pourrait ladapter simplement dautres domaines aussi utiles que le calcul vectoriel (composition avec une matrice, valuation dune forme quadratique etc). Il arrive souvent quune mme matrice, inconnue avant lexcution, soit compose des millions de fois (par
exemple pour calculer les dplacements des points dune scne 3D).
La majorit des dveloppeurs nauront jamais construire dassemblages dynamiquement, mais
certains projets ont beaucoup gagner utiliser cette possibilit.
8
Interoprabilit .NET
code natif / COM / COM+
La plateforme .NET prsente plusieurs techniques pour faire interoprer du code gr avec du
code natif et pour faire cooprer des objets grs avec des objets COM. Ce besoin est particulirement prsent lors dun processus de migration dune application C++ ou VB6 vers .NET. En
eet, linteroprabilit permet de migrer vos projets petit petit, composant aprs composant.
Le mcanisme P/Invoke
Le CLR utilis conjointement avec certaines classes de base du framework .NET, permet du
code gr dappeler des fonctions compiles en code natif. Cette possibilit est nomme Platform
Invoke ou P/Invoke. Vous pouvez lutiliser pour appeler les fonctions de vos propres DLLs natives.
Microsoft exploite aussi le mcanisme P/Invoke pour appeler les fonctions de ses propres DLLs
natives.
Notons lexistence du mcanisme internal call qui a la mme finalit que P/Invoke. Ce mcanisme consiste implmenter les artefacts des appels aux fonctions natives directement (en dur)
dans le code du CLR. Il est donc plus performant que P/Invoke mais nest utilisable que par
Microsoft.
Lattribut DllImport
Les classes qui permettent dutiliser P/Invoke se trouvent dans lespace de noms System.
Runtime.InteropServices. Pour appeler une fonction dune DLL native partir dun programme C , il faut dabord dclarer cette fonction dans une classe C :
La dclaration de cette fonction doit tre marque avec lattribut System.Runtime.InteropServices.DllImport qui indique le nom de la DLL.
266
Un exemple
Le programme suivant appelle la fonction Beep() dfinie dans la DLL native et standard
Kernel32.dll.
Exemple 8-1 :
using System.Runtime.InteropServices ;
class Program {
[DllImport("Kernel32.dll")]
public static extern bool Beep(uint iFreq, uint iDuration);
static void Main() {
bool b = Beep(100, 100);
}
}
Dans certains cas rares, vous ne pouvez pas spcifier le nom de la fonction dfinie dans la DLL
car vous avez dj une mthode qui a ce nom. Il est possible de changer le nom dune fonction
implmente dans une DLL native comme ceci :
Exemple 8-2 :
using System.Runtime.InteropServices ;
class Program {
[DllImport("Kernel32.dll", EntryPoint = "Beep")]
public static extern bool MonBeep(uint iFreq, uint iDuration) ;
static void Main() {
bool b = MonBeep(100, 100) ;
}
}
Convention dappel
Les fonctions implmentes dans les DLLs natives supportent plusieurs conventions dappel.
Ces conventions dappel indiquent au compilateur comment doit se comporter le passage dargument. Si vous devez utiliser une fonction qui a une convention dappel particulire, il faut
utiliser lattribut DllImport comme ceci :
[DllImport("MaDLL.dll", CallingConvention=XXX)]
XXX est une valeur de lnumration System.Runtime.InteropServices.CallingConvention.
Les significations des valeurs de cette numration sont exposes dans les MSDN larticle
CallingConvention Enumeration. Par dfaut cette valeur vaut StdCall qui est la convention
dappel standard sur les systmes dexploitation Microsoft (mis part Windows CE qui admet la
convention dappel standard Cdecl).
Le mcanisme P/Invoke
267
Il faut parcourir la pile pour vrifier que tous les appelants ont la permission UnmanagedCode.
Afin damliorer les performances, vous pouvez supprimer cette tape en utilisant lattribut
System.Security.SuppressUnmanagedCodeSecurity sur une mthode P/Invoke ou sur
une classe qui contient des mthodes P/Invoke . Cependant, lutilisation de cet attribut
peut tre compromettante pour la scurit.
Il faut crer une fentre de pile (stack frame en anglais) qui soit compatible avec les fentres
de pile utilises dans le code non gr. Cette tape est en gnral trs lgre.
Puisque la seconde tape est lgre et puisque la premire tape peut tre supprime si la scurit ne fait pas partie de vos considrations, vous pouvez faire en sorte que lutilisation de
P/Invoke ait un faible impact sur les performances.
Type win32
Type .NET
System.StringSystem.
StringBuilder
string
BYTE
System.Byte
Byte
SHORT
System.Int16
Short
WORD
System.UInt16
ushort
DWORD, UINT,ULONG
System.Int32
uint
INT, LONG
System.UInt32
uint
BOOL
System.Bool
bool
CHAR
System.Char
char
FLOAT
System.Single
float
DOUBLE
System.Double
double
268
On dit quun type est blittable si la reprsentation binaire de ses instances est identique en mode
gr et en mode natif. Dans le cadre de linteroprabilit, lutilisation des types blittable est donc
plus performante. Dans le tableau prcdent, seuls les types de chanes de caractres ne sont
pas blittables. Les tableaux monodimensionnels dlments de type blittable ainsi que les types
seulement composs de champs de types blittables sont aussi des types blittable.
Le mcanisme P/Invoke
269
Exemple 8-3 :
using System.Runtime.InteropServices ;
class Program {
[DllImport("user32.dll", CharSet = CharSet.Auto)]
public static extern int MessageBox(System.IntPtr hWnd,
string text, string caption, uint type) ;
static void Main() {
MessageBox(System.IntPtr.Zero, "hello", "caption text", 0) ;
}
}
Soit le code appelant fournit une mmoire tampon et la taille de cette mmoire tampon.
Dans ce cas la fonction appele remplie la mmoire tampon avec la chane de caractres
retourner.
Soit le code appelant sattend recevoir un pointeur partir de la fonction appele. Dans
ce cas, la fonction appele alloue la zone mmoire contenant la chane de caractres.
Il faut absolument tenir compte de ces dirences lorsque lon appelle au moyen de P/Invoke
une fonction dont le code est non gr. La fonction GetCurrentDirectory() de la DLL
Kernel32.dll est une bonne candidate pour illustrer le premier cas.
DWORD GetCurrentDirectory(
DWORD
nBufferSize,
LPTSTR lpBuffer) ;
En eet, cette fonction admet un pointeur vers une zone de mmoire tampon et sa taille en
argument. Voici le code C permettant dappeler cette fonction. On ne peut utiliser la classe
string car les instances de cette classe sont immuables. Il faut donc se servir de la classe System.
Text.StringBuilder.
Exemple 8-4 :
using System.Text ;
using System.Runtime.InteropServices ;
class Program {
[DllImport("Kernel32.dll", CharSet = CharSet.Auto)]
public static extern uint GetCurrentDirectory(
uint Taille,
StringBuilder sTmp);
static void Main() {
uint Taille = 255 ;
StringBuilder sTmp = new StringBuilder( (int) Taille);
uint i = GetCurrentDirectory(Taille, sTmp) ;
System.Console.WriteLine(sTmp) ;
}
}
270
La fonction GetCommandLine() de la DLL Kernel32.dll est une bonne candidate pour illustrer
le second cas.
LPTSTR GetCommandLine() ;
Cette fonction retourne un pointeur vers la chane de caractres fournie en ligne de commande.
Si nous utilisons le type string comme type de retour, le P/Invoke marshaller copiera la chane
de caractres retourne dans une mmoire tampon alloue par ses soins. Ensuite le P/Invoke
marshaller dsallouera la chane de caractres originale. Or, cette chane de caractres originale
ne doit pas tre dsalloue, car elle existait avant lappel de la fonction GetCommandLine() et
sera srement utilise ultrieurement, dans dautres appels de fonctions. Il faut donc utiliser la
classe IntPtr, qui permet de copier la chane de caractres retourne dans une instance de la
classe string, sans dsallouer la chane de caractres originale :
Exemple 8-5 :
using System.Runtime.InteropServices ;
class Program {
[DllImport("Kernel32.dll", CharSet = CharSet.Auto)]
public static extern System.IntPtr GetCommandLine() ;
static void Main() {
System.IntPtr ptr = GetCommandLine() ;
string sTmp = Marshal.PtrToStringAuto(ptr);
System.Console.WriteLine(sTmp) ;
}
}
Le mcanisme P/Invoke
271
Attribut de direction
Lors de la dclaration dune mthode P/Invoke , vous pouvez utiliser les attributs System.
Runtime.InteropServices.In et System.Runtime.InteropServices.Out devant chaque argu-
272
273
Exemple 8-7 :
// compile with: /clr
#include "stdafx.h"
#include "windows.h"
int main(){
bool b = Beep(100, 100) ;
}
Le compilateur C++/CLI peut se passer de lattribut DllImport car il connat la dfinition de la
mthode que lon souhaite appeler grce linclusion du fichier dentte adquat. Dans notre
exemple, la mthode Beep() est dfinie dans le fichier dentte windows.h. La visualisation de
lassemblage produit par le compilateur C++/CLI avec loutil ildasm.exe nous rvle que la mthode statique suivante a t fabrique :
.method public static pinvokeimpl(lasterr stdcall)
int32 modopt([mscorlib]System.Runtime.CompilerServices.CallConvStdcall)
Beep(
uint32 modopt([Microsoft.VisualC]Microsoft.VisualC.IsLongModifier) A_0,
uint32 modopt([Microsoft.VisualC]Microsoft.VisualC.IsLongModifier) A_1)
native unmanaged preservesig {
.custom instance void
[mscorlib]System.Security.SuppressUnmanagedCodeSecurityAttribute::
.ctor() = ( 01 00 00 00 )
// Embedded native code
// Disassembly of native methods is not supported.
// Managed TargetRVA = 0x00001D92
} // end of method Global Functions::Beep
La visualisation de lassemblage produit par le compilateur C pour lExemple 8-7 expose cette
dfinition :
.method public hidebysig static pinvokeimpl("Kernel32.dll" winapi)
bool Beep(uint32 iFreq,uint32 iDuration) cil managed preservesig{}
Du point de vue du CLR, les deux versions de la mthode Beep() sont invoquer avec le mcanisme P/Invoke puisquelles ont toutes les deux le drapeau pinvokeimpl. Nanmoins, on remarque que le compilateur C++/CLI a gnr du code natif pour localiser la mthode Beep()
contenue dans la DLL kernel32.dll tandis que la version C compte sur le CLR pour raliser cet
opration. Dautres dirences sont noter comme lutilisation dans la version C++/CLI de lattribut SuppressUnmanagedCodeSecurity (dcrit page 201) et du modificateur IsLongModifier
qui rsout les problmes dus au fait que les mots-cls C++/CLI long et int se rfrrent tous deux
au type Int32.
274
La dfinition de types non grs avec des mthodes dont le corps contient du code natif.
La dfinition de types non grs avec des mthodes dont le corps contient du code IL.
La dfinition de types grs avec des mthode dont le corps contient du code IL.
Les #pragma managed et unmanaged doivent tre dfinis hors dun type pour prendre eet. Un
type ne peut donc avoir la fois des mthodes natives et gres. En outre, le langage C++/CLI ne
permet pas la dfinition de types grs avec des mthodes dont le corps contient du code natif.
Tout cela est illustr par le programme suivant :
Exemple 8-8 :
// compile with: /clr
#include "stdafx.h"
#using <mscorlib.dll>
#pragma unmanaged
class TypeNatifCodeNatif{
public:
TypeNatifCodeNatif(int state){ m_State = state ; }
int GetEtat(){ return m_State ; }
private:
int m_State ;
} ;
#pragma managed
class TypeNatifCodeIL {
public:
TypeNatifCodeIL(int state){ m_State = state ; }
int GetEtat(){ return m_State ; }
private:
int m_State ;
} ;
ref class TypeGereCodeIL {
public:
TypeGereCodeIL(int state){ m_State = state ; }
int GetEtat(){ return m_State ; }
private:
int m_State ;
} ;
int main(){
TypeNatifCodeNatif* o1 = new TypeNatifCodeNatif(1) ;
int i1 = o1->GetEtat() ;
delete o1 ;
TypeNatifCodeIL* o2 = new TypeNatifCodeIL(2) ;
int i2 = o2->GetEtat() ;
delete o2 ;
TypeGereCodeIL^ o3 = gcnew TypeGereCodeIL(3) ;
int i3 = o3->GetEtat() ;
return 0 ;
}
275
Si vous compilez ce code et que vous analysez lassemblage produit avec loutil ildasm.exe
vous vous apercevrez que le compilateur a prvu deux types valeurs grs TypeNatifCodeIL
et TypeNatifCodeNatif pour permettre dutiliser les types natifs sous-jacent partir du code
gr. Ces deux types grs ne prsentent aucun membre. Leurs tats sont stocks par les types
natifs. Les types natifs sont stocks dans des sections binaires de lassemblage qui ne sont pas
visualisables partir des outils ildasm.exe et Reflector.
On remarque la prsence des quatre mthodes gres publiques statiques TypeNatifCodeIL.GetEtat(...), TypeNatifCodeIL.{ctor}(...), TypeNatifCodeNatif.GetEtat(...) et
TypeNatifCodeNatifL.{ctor}(...). Il ny a pas despace de noms TypeNatifCodeNatif ou
TypeNatifCodeIL. Le langage IL accepte les noms de mthodes contenant un point. Il est intressant de remarquer que les deux mthodes .GetEtat() prennent un paramtre reprsentant
la rfrence this. En outre, les deux mthodes relatives TypeNatifCodeIL contiennent du code
IL alors que les deux mthodes relatives TypeNatifCodeNatif ont le drapeaux pinvokeimpl
indiquant quelle appelle une mthode native au moyen de P/Invoke.
Enfin, si vous visualisez le code IL de la mthode main(), vous verrez que le compilateur
C++/CLI utilise les types de lespace de noms System.Runtime.CompilerServices pour rendre
possible la magie du code IL qui manipule des types natifs.
Un champ _handle de type void*. Ce champ ne peut tre de type GCHandle. En eet, a
structure gcroot<T> est native et ne peut avoir de champs de type gr tels que GCHandle.
Les mthodes de gcroot<T> peuvent utiliser des instances de GCHandle de puisquelles sont
compiles en IL. La passerelle entre une instance de GCHandle et le champ _handle est assure par des oprateurs statiques de conversion de la structure GCHandle. Pour simplifier
ces oprations, le fichier gcroot<T> utilise les deux macros suivantes :
__GCHANDLE_TO_VOIDPTR(x)
((GCHandle::operator System::IntPtr(x)).ToPointer())
__VOIDPTR_TO_GCHANDLE(x)
(GCHandle::operator GCHandle(System::IntPtr(x)))
276
Dans lexemple suivant, la classe TypeNatifCodeNatif garde une rfrence vers une chane de
caractre gre. Comme vous pouvez le voir, le recours gcroot est rduit au strict ncessaire
et vous ne voyez pas apparatre la structure gre GCHandle :
Exemple 8-9 :
// compile with: /clr
#include "stdafx.h"
#include <vcclr.h>
using namespace System ;
#pragma unmanaged
class TypeNatifCodeNatif {
public:
TypeNatifCodeNatif( gcroot<String^> s ) {m_s = s;}
gcroot<String^> m_s ;
} ;
#pragma managed
int main() {
TypeNatifCodeNatif* obj = new TypeNatifCodeNatif("Bonjour") ;
Console::WriteLine( obj->m_s ) ;
delete obj ;
}
Pour exploiter un objet gr partir du code natif vous devez dvelopper vous-mme un type
natif avec du code gr qui fait la passerelle entre les deux mondes. Lexemple suivant montre
comment du code natif qui dtient une rfrence (en fait un handle) sur une instance gre de
type string peut invoquer la mthode get_Length() sur cette instance :
Exemple 8-10 :
// compile with: /clr
#include "stdafx.h"
#include <vcclr.h>
using namespace System ;
#pragma managed
class TypeNatifCodeIL {
public:
static int Lentgh(gcroot<String^> s){
return s->Length;
}
};
#pragma unmanaged
class TypeNatifCodeNatif {
public:
TypeNatifCodeNatif(gcroot<String^> s) {
m_Length = TypeNatifCodeIL::Lentgh(s) ;
}
277
int m_Length ;
} ;
#pragma managed
int main() {
TypeNatifCodeNatif* obj = new TypeNatifCodeNatif("Bonjour") ;
Console::WriteLine( obj->m_Length ) ;
delete obj ;
}
Introduction
Une application excute sous un systmes Windows a la possibilit dexploiter des ressources
systmes telles que les fichiers, le registre, les pilotes, les processus, les threads, les mutex, les
pipes nomms, les sokets, les fentres etc. Une mme ressource systme peut tre exploite
simultanment par plusieurs processus (on peut citer par exemple les mutex nomms). Une
ressource systme ne peut donc pas tre rfrence par un pointeur puisquelle nappartient
pas lespace dadressage dun processus. En consquence, les processus Windows accdent aux
ressources systmes par lintermdiaire de pointeurs logiques nomms handles.
Toutes les fonctions win32 permettant la manipulation dune ressource systme acceptent un
paramtre en entr de type HANDLE. Un handle est un numro cod sur quatre octets. Chaque
processus Windows maintient en interne une table dassociation entre les handles et les ressources systmes utilises. Deux ressources systmes ne peuvent donc pas tre rfrence par
le mme handle au sein dun processus. Deux handles rfrenant une mme ressource partir
de deux processus distincts peuvent tre deux entiers dirents.
Une ressource systme est cre lors de lappel de certaines fonctions win32 telles que CreateFile(), CreateMutex() ou CreateThread(). Ces fonctions ont la particularit de retourner un
handle. Lappel une de ces fonctions ne cre pas ncessairement une ressource. Par exemple
vous pouvez rcuprer un handle vers un mutex existant en appelant la fonction CreateMutex() paramtre avec le nom du mutex. La fonction win32 CloseHandle() permet de signifier
Windows que le processus appelant na plus besoin de la ressource systme rfrence. Windows
dtruit une ressource systme lorsque plus aucun processus ne maintient de handle vers elle.
Le compteur de performance Windows Processus/Nombre de handles vous permet de connatre
le nombre de handles couramment dtenus par un processus. La colonne Handles du gestionnaire des tches vous permet aussi de visualiser ce nombre en temps rel.
La classe HandleCollector
Une instance de la classe System.Runtime.InteropServices.HandleCollector permet de fournir au CLR une estimation du nombre de handles actuellement dtenu par le processus. Vous
278
pouvez prciser un seuil initial et un seuil maximum partir duquel le ramasse-miettes lancera
une collecte. En eet, le ramasse-miettes na aucune connaissance de la quantit de mmoire
non gre maintenue par des ressources systmes et cette classe permet de pallier cette faiblesse.
Une instance de HandleCollector peut tre nomme de faon ntre concerne que par les
handles vers un certain type de ressource.
Il ny avait pas de vrification de type la compilation. Par exemple, rien ne vous empchez
de communiquer un handle sur une fentre (de type win32 HWND) une fonction win32 qui
a besoin dun handle sur un fichier (de type win32 HFILE).
Vous naviez pas de garanties quant la libration dun handle. Une exception de type
ThreadAbortException ou OutOfMemory pouvait compromettre cette opration.
Vous tiez expos une situation de comptition (race condition) quant la fermeture du
handle. Rien nempchait un thread dutiliser un handle pendant quun autre thread tait
en train de le fermer. Cela pouvait mme mener un problme de scurit connu sous
le nom de handle-recycling attack o du code malveillant exploitait une ressource en train
dtre ferme.
Le framework .NET 2.0 ore un moyen de pallier ces problmes avec les classes abstraites
System.InteropServices.CriticalHandle et System.InteropServices.SafeHandle. Lide
est de prvoir une classe non abstraite drive dune de ces classes pour grer le cycle de
vie dun type de handle. Cette classe doit tre drive de SafeHandle pour implmenter
un type de handle qui supporte un compteur de rfrences. Sinon cette classe doit driver
de CriticalHandle. Les deux classes SafeHandle et CriticalHandle drivent de la classe
System.Runtime.ConstrainedExecution.CriticalFinalizer. Et ont donc un finaliseur critique. Les finaliseurs critiques sont dcrits en page 129. De ce fait, on obtient certaine garantie
de fiabilit sur lopration de fermeture dun handle.
Vous pouvez aussi driver des classes abstraites CriticalHandleMinusOneIsInvalid, SafeHandleMinusOneIsInvalid et SafeHandleZeroOrMinusOneIsInvalid dont les noms sont loquents
quant aux services de dure de vie oerts. Ces classes sont dans lespace de noms Microsoft.
Win32.SafeHandles.
279
Les composants COM (i.e les quivalents dans la technologie COM des assemblages, des DLLs en
gnral mais aussi des excutables) prsentent optionnellement des mtadonnes, contenues
dans ce que lon appelle une bibliothque de types (type library en anglais). Une bibliothque de
types peut tre contenue directement dans le composant COM ou dans un fichier part, dextension tlb. En plus du fait quun composant COM na pas obligatoirement une bibliothque
de types, le formatage binaire des mtadonnes au sein des bibliothques de types est totalement
dirent du formatage binaire des mtadonnes dans les assemblages .NET.
COMComposant.idl
[
object,
uuid(947469B1-61EB-4010-AE29-8380C2D577E9),
dual,
helpstring("IClasseCOM Interface"),
pointer_default(unique)
]
interface IClasseCOM : IDispatch{
HRESULT CalcSomme([in]int a, [in]int b, [out,retval] int *pResult) ;
} ;
280
DotNETClient.cs
281
Assemblage inter-oprable
CLR
IInterface1
Client
.NET
IInterface2
IInterface1
RCW
IInterface2
Objet
COM
IUnknown
282
Figure 8 -2 : Ajout dune rfrence vers un composant COM partir de Visual Studio .NET
Exemple 8-12 :
using System ;
using System.Runtime.InteropServices ;
[
ComImport,
Guid("947469B1-61EB-4010-AE29-8380C2D577E9"),
InterfaceType(ComInterfaceType.InterfaceIsDual)
]
public interface IClasseCOM {
[return : MarshalAs(UnmanagedType.I4)]
int CalcSomme(
[In,MarshalAs(UnmanagedType.I4)] int a,
[In,MarshalAs(UnmanagedType.I4)] int b,
[Out,MarshalAs(UnmanagedType.I4)]out int c) ;
}
[
ComImport,
Guid("1E3B6413-7E63-42B5-874D-E0A27A42190C")
]
public class CClasseCOM {}
class Program {
static void Main() {
IClasseCOM foo = new CClasseCOM () as IClasseCOM ;
int result ;
foo.CalcSomme(2, 3, out result) ;
System.Console.WriteLine("R
esultat :{0}", result) ;
}
}
283
284
Les classes RCW convertissent les Basic String (BSTR) de COM en des instances de la classe
System.String.
285
Les classes RCW convertissent les VARIANT de COM en des instances dune classe drive de
la classe object. La classe .NET de lobjet sous-jacent dpend naturellement du type sousjacent du VARIANT et vous pouvez transtyper cet objet .NET avec loprateur as de C .
Les classes RCW convertissent les SAFEARRAY de COM en des tableaux grs du type adquat. Par exemple un argument de type SAFEARRAY(BSTR) dans une mthode COM devient un argument de type System.String[] dans la mthode de la classe RCW.
COM gre les erreurs au moyen de la valeur de retour de chaque mthode de chaque interface COM. Cette valeur de retour est toujours de type HRESULT. Un HRESULT est une valeur
code sur quatre octets qui prcise si une erreur sest produite lors de lappel dune mthode
sur un objet COM. Le cas chant, le HRESULT contient le type de lerreur et ventuellement
des informations sur la couche logicielle qui a gnr lerreur.
Si lappel une mthode dun objet COM renvoie un HRESULT derreur, une exception de type
COMException est automatiquement leve par le CLR. La proprit ErrorCode de cette exception
contient la valeur du HRESULT.
286
Vous pouvez aussi spcifier lapartment COM dun thread en utilisant lun des attributs System.
STAThread ou System.MTAThread sur la mthode qui constitue le point dentre du thread. Par
exemple :
...
[MTAThread]
public static void Main(){
}
...
287
MyAsm.exe.manifest
Visual Studio 2005 permet dexploiter simplement cette technique. Pour cela, il faut que
vous positionniez lattribut Isolated dune rfrence COM true. Cela fonctionne aussi si
la rfrence COM est un OCX. La compilation du projet fait alors en sorte de crer le fichier
dextension .manifest dans le rpertoire de sortie. Elle copie aussi les DLLs composants
COM dans ce rpertoire. Reg free COM est particulirement utile si vous dsirez avoir recours des classes COM dans un projet dploy la XCopy, par exemple avec la technologie
ClickOnce.
Prcisons que pour exploiter cet attribut il faut que les classes COM soient enregistres dans
la base des registres de la machine qui ralise la compilation. En outre, il vaut mieux tester
ce genre dapplication sur une machine vierge. En eet, une utilisation dfectueuse de reg
free COM ne serait pas dtecte sur une machine sur laquelle les composants COM utiliss
sont enregistrs.
288
Sachez que vous pouvez vous passer de reg free COM en eectuant le travail de localisation, de
chargement et dinstanciation de la classe COM vous-mme. Cette technique est utile si votre
dploiement seectue potentiellement sur des versions antrieures Windows XP. Pour chaque
classe et composant COM il faut :
CLR
IInterface
IDispatch
Objet
.NET
CCW
IUnknown
Client .NET
289
La classe .NET doit avoir un constructeur sans arguments, appel aussi constructeur par
dfaut. En eet, COM ne supporte pas la notion de constructeur avec arguments. Parmi les
constructeurs de la classe .NET, seul ce constructeur sans argument pourra tre appel par
lintermdiaire du CCW.
Seules les classes publiques dun assemblage peuvent tre encapsules dans un CCW.
La classe .NET peut avoir des membres statiques, mais ils ne seront pas utilisables par lintermdiaire dun CCW puisque COM ne supporte pas cette notion de membres statiques.
Les surcharges dune mthode de la classe .NET seront renommes, car COM ne supporte
pas cette notion. Concrtement, un blanc soulign est mis avant le nom de chaque mthode
surcharge, et un numro est mis la fin du nom de la mthode.
dotNET2COM.cs
namespace Test {
public interface ICalc {
int CalcSomme(int a, int b) ;
}
public class CCalc : ICalc {
public int CalcSomme(int a, int b) {
return a + b ;
}
}
}
Pour produire lassemblage dotNET2COM.dll partir de ce fichier C , il sut de taper la ligne
de commande suivante :
>csc.exe -t:library dotNET2COM.cs
Pour produire une bibliothque de types COM dcrivant le CCW qui encapsule la classe CCalc
et linterface ICalc, il sut de taper la ligne de commande suivante :
>tlbexp.exe dotNET2COM.dll /out:dotNET2COM.tlb
Comprenez bien que le CCW est produit lexcution par le CLR. Durant cette opration
le CLR na pas besoin dune bibliothque de types. La bibliothque de type nest utile que
pour les clients qui souhaitent eectuer un lien prcoce COM avec le CCW.
Nous pouvons visualiser la bibliothque de types dotNET2COM.tlb au moyen de loutil OLE
Viewer (oleview.exe). Cet outil est accessible par le menu Outils de Visual Studio .NET. Il est
aussi accessible en ligne de commande. OLE Viewer comporte le menu Fichier Visualiser une
bibliothque de types... . Rappelons que les bibliothques de types sont dcrites dans un format
binaire, et quil est donc ncessaire de disposer dun tel outil pour les visualiser :
290
{
interface _CCalc;
_Object;
ICalc;
[
odl,
uuid(84181003-CCB9-3219-B373-629AF4E3B246),
hidden,
dual,
291
oleautomation,
custom(0F21F359-AB84-41E8-9A78-36D110E6D2F9, Test.CCalc)
]
interface _CCalc : IDispatch {
} ;
} ;
Plusieurs remarques peuvent tre faites :
_Object : Cette interface reprsente les mthodes de la classe System.Object dont drive toutes les classes .NET. Linterface _Object est dfinie dans la bibliothque de types
mscorlib.tlb.
_CCalc : Cette interface COM est linterface de classe (class interface en anglais) de la
classe .NET CCalc. Une interface de classe est sense prsenter tous les membres
publiques non statiques dune classe .NET (y compris les membres des types dont
la classe est drive). Cependant, dans notre exemple cette interface de classe ne
contient aucun membre. Lutilisation dinterfaces de classes tant dconseille, le
comportement par dfaut de tlbexp.exe est de ne pas insrer de membres dans
une interface de classe quil produit. Lutilisation dinterfaces de classes est dconseille car elle couple les clients non grs avec des classes gres et non avec des
interfaces. Vous pouvez nanmoins prciser tlbexp.exe que vous souhaitez que
linterface de classe soit correctement construite en utilisant dans votre code C lattribut System.Runtime.InteropServices.ClassInterface avec la valeur AutoDual. Par
exemple :
...
[ClassInterface(ClassInterfaceType.AutoDual)]
public class CCalc : ICalc{
...
}
...
Les champs publics dune classe admettant une interface de classe, sont matrialiss
dans linterface de classe sous forme de deux accesseurs. De mme, les accesseurs des
proprits des interfaces publiques, sont prsents comme des mthodes dans les interfaces COM gnres par tlbexp.exe.
Une classe ID a t produite automatiquement pour la classe COM CCalc. On aurait pu utiliser lattribut System.RunTime.InteropServices.Guid pour spcifier notre propre attribut
dans le code C :
...
[Guid("A51C81A3-4892-39EC-981A-AF77FB4CFD36")]
public class CCalc : ICalc{
...
}
...
292
Linterface COM ICalc tend linterface COM IDispatch qui permet de crer des liens tardifs explicites sur les classes COM.
Vous avez la possibilit dutiliser dans votre code .NET lattribut .NET System.RunTime.
InteropServices.COMVisible sur une interface, une classe, une mthode ou un vnement
afin dindiquer tlbexp.exe que lentit concerne ne soit pas visible dans la bibliothque de
types construite. Par exemple :
...
[ComVisible(false)]
public class CCalc : ICalc{
...
}
...
Les structures publiques sont prises en compte par tlbexp.exe. Elles pourront ainsi tre passes
comme arguments des mthodes des classes et des interfaces.
293
dotNET2COM.reg
[HKEY_CLASSES_ROOT\Test.CCalc]
@="Test.CCalc"
[HKEY_CLASSES_ROOT\Test.CCalc\CLSID]
@="{A51C81A3-4892-39EC-981A-AF77FB4CFD36}"
[HKEY_CLASSES_ROOT\CLSID\{A51C81A3-4892-39EC-981A-AF77FB4CFD36}]
@="Test.CCalc"
[HKEY_CLASSES_ROOT\CLSID\{A51C81A3-4892-39EC-981A-AF77FB4CFD36}
\InprocServer32]
@="C:\WINDOWS\System32\mscoree.dll"
"ThreadingModel"="Both"
"Class"="Test.CCalc"
"Assembly"="dotNET2COM, Version=0.0.0.0, Culture=neutral,
PublicKeyToken=null" "RuntimeVersion"="v1.0.3705"
[HKEY_CLASSES_ROOT\CLSID\{A51C81A3-4892-39EC-981A-AF77FB4CFD36}\ProgId]
@="Test.CCalc"
[HKEY_CLASSES_ROOT\CLSID\{A51C81A3-4892-39EC-981A-AF77FB4CFD36}
\Implemented Categories\{62C8FE65-4EBB-45E7-B440-6E39B2CDBF29}]
La cl InprocServer32, qui est sense contenir le chemin et le nom du fichier qui contient limplmentation de la classe COM, est gale mscoree.dll. Cette DLL, appele DLL cale, permet
le chargement du CLR dans un processus. Les quatre composantes du nom fort de lassemblage
qui contient limplmentation de la classe .NET sont spcifies dans la sous-cl Assembly. Lorsquune application aura besoin de la classe COM qui a pour ProgID Test.Calc, elle chargera
dabord le CLR (si ce nest dj fait pour ce processus) puis elle utilisera le mcanisme de localisation dassemblage, pour localiser lassemblage dotNET2COM.
La sous cl ThreadingModel vaut both, cest--dire que les instances de la classe COM CCW supportent indiremment les modes STA et MTA. Ceci est la consquence du fait que les modles
STA et MTA ne sont pas pris en compte dans .NET. Les objets .NET nont pas danit avec les
threads.
Par dfaut le ProgID de la classe COM produite est {espace de nom}.{nom de la classe}. Vous
pouvez toutefois spcifier un autre ProgID en utilisant lattribut .NET System.RunTime.
InteropServices.ProgId dans votre code .NET. Par exemple :
...
[ProgID("dotNET.CCalc")]
public class CCalc : ICalc{
...
Loutil regasm.exe prsente loption /tlb qui permet de fabriquer une bibliothque de type,
exactement comme avec loutil tlbexp.exe.
>regasm.exe dotNET2COM.dll /tlb:dotNET2COM.tlb
294
Les outils regasm.exe et tlbexp.exe peuvent tre utiliss indpendamment sur le mme assemblage. On pourrait craindre que les identifiants uniques des classes et des interfaces ne soient pas
les mmes dans les fichiers produits par regasm.exe et dans les fichiers produits par tlbexp.exe.
Heureusement il nen est rien, ce qui nous amne penser que les valeurs de ces identifiants
uniques sont calcules dune manire dterministe partir de lassemblage. Empiriquement, les
identifiants uniques ne changent pas mme si les membres des classes ou des interfaces concernes varient. En revanche, les identifiants uniques changent pour les classes ou les interfaces
dont le nom change.
Introduction COM+
295
de conversion trs prcises pour convertir les direntes classes dexception et les dirents
HRESULT. Par exemple HRESULT COR_E_DIVIDEBYZERO est converti en une exception de type
DivideByZeroException. Tout ceci fait lobjet de larticle HRESULTs and Exceptions des
MSDN.
Introduction COM+
Quest ce que COM+ ?
COM+ est le terme dsignant les services dentreprise dans le contexte des applications destines
tre excutes sous les systmes dexploitation Microsoft. COM+ est donc la technologie Microsoft
pour construire des serveurs dapplications. Un service dentreprise de COM+ est une fonctionnalit volue qui peut tre ajoute une classe COM. Ces fonctionnalits sont axes autour du
dveloppement dapplications distribues transactionnelles. On verra la liste de ces fonctionnalits dans la prochaine section mais on peut dj citer le pooling dobjets ou la passerelle avec
des milieux transactionnels non Microsoft.
COM+ 1.0 est apparu avec Windows 2000. COM+ ntait pas une nouvelle version de COM mais
une nouvelle version de la technologie MTS (Microsoft Transaction Server) qui permettait, entre
autres, de raliser des transactions distribues. Par rapport la technologie MTS, on obtenait de
bien meilleures performances avec COM+ 1.0. COM+ 1.5 est apparu avec Windows XP et ajoute
quelques services dentreprise COM+ 1.0.
296
Pooling dobjets ;
Lide du pooling dobjet est de recycler les objets pour quils puissent chacun servir plusieurs clients conscutivement. Les cots de la construction et de la destruction dun objet
sont alors diviss par le nombre de clients servis par lobjet. Le pool reprsente le conteneur
des objets qui ne sont pas en train de servir un client.
Transactions distribues ;
COM+ permet dexploiter le serveur transactionnel de Windows nomm MS DTC (Distributed
Transaction Coordinator) afin de raliser des transactions distribues.
297
Interoprabilit XA
Ce mcanisme permet dencapsuler, au sein dune transaction gre par le modle de transaction Microsoft (OLE Transaction), les accs une base de donnes qui supporte le modle
transactionnel XA (X/Open Distributed Transaction Processing (DTP).
Composants privs
Tous les composants servis dune COM+ application ne sont pas ncessairement accds par
les clients de la COM+ application. Il existe souvent des composants servis qui ne doivent
tre utiliss que par dautres composants servis, existant dans le mme processus et appartenant la mme COM+ application. Pour empcher un client dutiliser un tel composant
servi, il faut le dclarer comme un composant servi priv.
298
Scurit Role-Based
COM+ prsente son propre mcanisme de scurit, bas sur le rle que joue lutilisateur
appelant. Ce mcanisme est dirent du mcanisme de scurit .NET bas sur les rles, et
du mcanisme Windows bas sur les rles.
Services SOAP
Ce mcanisme permet de publier un composant servi comme un service web. On peut toujours continuer y accder comme un composant COM+.
299
300
301
Processus
CLR
Composant servi
1 (contexte A)
Composant servi
2 (contexte A)
Composant servi
3 (contexte B)
ContextUtil
COM : Contexte A
COM : Contexte B
servis dun mme assemblage appartiennent la mme COM+ application. Les COM+ applications reprsentent ce que lon appelle communment les serveurs dentreprises. Les clients utilisent les instances des composants servis dune COM+ application, et les composants servis utilisent les services dentreprise COM+ pour eectuer leurs tches.
Plusieurs attributs dassemblage ont t conus pour que vous puissiez configurer les paramtres
de la COM+ application qui contiendra lassemblage que vous dveloppez. Voici les principaux :
302
Le catalogue COM+
Le catalogue COM+ est une base de donnes prsente dans tous les systmes dexploitation Microsoft depuis Windows 2000. Le catalogue COM+ contient les informations de configuration des
COM+ applications qui rsident sur la machine. Le catalogue COM+ est physiquement stock
sur deux supports :
Une partie des informations du catalogue COM+ est stocke dans la base des registres, directement avec les informations des classes COM.
Lautre partie des informations du catalogue COM+ est stocke dans une srie de fichiers
bibliothques de composants (extension .clb). Ces fichiers sont stocks dans le rpertoire
<< %systemroot%\Registration >>.
Le format des fichiers .clb nest pas document. Il est fortement dconseill dessayer de modifier ou de visualiser les donnes du catalogue COM+ par lintermdiaire dun diteur tel que
regedt32.exe ou en modifiant les fichiers .clb. Nous prsenterons un peu plus loin, un outil
spcialement conu pour visualiser et modifier les donnes du catalogue COM+, de faon
pouvoir crer et paramtrer des applications COM+ sur une machine. La Figure 8 -7 expose les
direntes relations existant entre le catalogue COM+, les COM+ applications et les composants
servis, installs sur une machine.
OS Microsoft (Windows 2000 ou XP)
Systme de fichiers
Fichiers .clb
Base des
registres
COM+ Catalogue
Assemblage C
Assemblage
B
Composant
servi
Composant servi
Assemblage
A
Composant
servi
Composant servi
Composant servi
Composant servi
Composant servi
COM+ Application
Nom
ID
Paramters pour les
services dentreprise
utiliss
Liste des composants
303
Processus client
Client non gr
CLR
Client .NET
Proxy
Instance dun
composant servi
Proxy
Client .NET
304
Enregistrer les composants servis dans la base des registres, pour quils puissent tre accds
comme des objets COM. La description de cette tape fait lobjet de la section prcdente.
Construire une bibliothque de types dcrivant les composants servis contenus dans lassemblage. Cette bibliothque de types contient les informations spcifies par les attributs
COM+ contenus dans lassemblage.
Trouver la COM+ application qui doit contenir ces composants servis. Si la COM+ application nest pas trouve alors crer une nouvelle COM+ application.
Configurer la COM+ application partir des informations de la bibliothque de types, issues des attributs COM+ de lassemblage. Les paramtres non positionns par ces valeurs
prennent des valeurs par dfaut. De plus, si la COM+ application existait dj, il se peut
que les valeurs de ses paramtres changent avec linstallation de ces nouveaux composants
servis.
La faon manuelle :
Il vous sut dutiliser loutil Services Installation Utility (regsvcs.exe) en ligne de commande, avec pour argument lassemblage qui contient les composants servis. Par exemple :
>regsvcs.exe SystemeBancaire.dll
Les options de loutil regsvcs.exe sont dcrites dans les MSDN larticle .NET Services
Installation Tool (Regsvcs.exe). Si le nom de la COM+ application nest pas spcifi dans
lassemblage ou partir de loption /appname de regsvcs.exe, le nom de la COM+ application sera gal au nom de lassemblage (sans extension de fichier).
La faon programme :
Il vous sut dutiliser la classe RegistrationHelper au sein dune mthode dune classe
dun assemblage. La classe RegistrationHelper prsente la mthode InstallAssembly()
prvue cet eet. Par exemple :
Exemple 8-17 :
using System.EnterpriseServices ;
public class Program {
static void Main() {
RegistrationHelper rh = new RegistrationHelper() ;
string sCOMPlusAppName = "Serveur bancaire." ;
string sTypeLibName = "SystemeBancaire.tlb" ;
rh.InstallAssembly(
305
La faon automatique :
Quand un client a besoin quun composant servi soit instanci, le CLR vrifie si le composant servi est eectivement prsent dans une COM+ application. Si tel nest pas le cas,
le CLR installe automatiquement lassemblage dans une nouvelle COM+ application. Bien
que sduisante, cette faon de faire doit tre vite car vous pouvez dicilement tre certain
que le premier client a les droits dadministrateur qui sont ncessaires pour raliser cette
opration.
306
Si une COM+ application est en cours dutilisation, loutil services de composants vous lindique avec une petite animation. Il vous permet aussi de stopper une COM+ application utilise.
Si vous changez les paramtres dune COM+ application, il faudra stopper la COM+ application
pour que les changements soient pris en compte lors de la prochaine utilisation.
Le langage C 2.0 et la
comparaison C 2.0/C++
9
Les concepts fondamentaux
du langage
310
Enfin les possibilits de dfinir des alias de noms despace de noms, et dimbriquer des espaces
de noms, sont prsentes en C .
311
Type de ressource
Mot-cl C
espace de noms
namespace
classe
class
interface
interface
structure
struct
numration
enum
dlgu
delegate
Dans une moindre mesure on peut considrer que les commentaires constituent une sorte de
ressources dont la caractristique principale est de ne pas tre pris en compte par le compilateur.
Pour utiliser une ressource dfinie dans lespace de noms de nom B, il faut que celui-ci soit
dclar en tte du fichier source ou dans lespace de noms o lon souhaite utiliser la ressource,
avec le mot-cl using. Par exemple :
Exemple 9-1 :
using B;
namespace A{
class Program{
static void Main() {}
ClasseFoo f ; // la classe ClasseFoo peut
etre utilis
ee.
}
}
namespace B{
using A;
class ClasseFoo{Program p;} // la classe Program peut
etre utilis
ee.
}
Toutes les ressources dclares dans un mme espace de noms sont accessibles partir du code
contenu dans cet espace de noms. Y compris celles qui sont dclares dans le mme espace de
noms dans dautres fichiers sources, dautres modules ou assemblages rfrencs la compilation.
Comprenez bien que le mot-cl using nest quune commodit lusage du compilateur C 2
pour lui permettre de faire le lien entre les ressources et les noms des ressources qualifis sans
leurs espaces de noms. Lquivalent en Java et en VB.NET est le mot cl Imports/imports. Dans
aucun de ces trois langages ce mot-cl nimporte quoi que ce soit au sens o quelque chose est
dplac.
On peut utiliser le mot-cl using pour dfinir un alias despace de noms. En gnral, cela permet
dviter davoir nommer les espaces de noms imbriqus. Par exemple :
using SysWinForm = System.Windows.Forms ;
Il faut tre conscient que dans ce cas on est oblig dutiliser lalias devant les identificateurs de cet
espace. Ce nest pas le cas lorsquil ny a pas dalias et quil ny a pas de collisions didentificateurs.
Enfin, il nexiste pas en C la syntaxe Java suivante :
312
Namespace 1
Namespace 2
fichier1.cs
...
Namespace 3
Ressource D
Ressource E
fichier2.cs
Ressource C
...
Ressource A
fichier3.cs
Ressource B
Les ressources hors de tout espace de noms sont dans lespace de noms anonyme.
Comme expos par la ressource E, depuis la version 2.0 de C une classe une structure ou
une interface a la possibilit dtre dclare sur plusieurs fichiers sources dun mme projet,
mais dans le mme espace de noms.
Le prprocesseur
313
compilation est prise en charge par le compilateur csc.exe dcrit un peu plus loin. Ce compilateur peut tre appel soit directement en ligne de commande, soit par lenvironnement
de dveloppement Visual Studio. Les options de compilation sont dfinies soit dans la ligne de
commande, soit par lintermdiaire de lenvironnement de dveloppement.
Il y a principalement deux tapes la compilation :
Prprocessing des fichiers sources : cette tape gnre de nouveaux fichiers sources, modifis
selon des directives prprocessing qui peuvent tre dfinies soit dans les fichiers sources eux
mme, soit directement en ligne de commande du compilateur. Ces directives sont dcrites
un peu plus loin.
Compilation des fichiers sources rsultant du prprocessing : le produit de cette tape est soit :
un module contenu dans un fichier dextension .netmodule. Rappelons que lenvironnement Visual Studio ne sait pas grer les modules.
Le prprocesseur
C++ C En C la directive #define ne peut plus dfinir une constante remplacer. On ne
lutilise plus que pour changer simplement le code source, avec laide des directives #if #elif
#else et #endif.
Les constantes prdfinies ( __LINE__ ,__FILE__, __DATE__...) nexistent plus.
Les macros avec paramtres nexistent plus.
Lenvironnement de dveloppement Visual Studio est capable de mettre en gris les lignes qui ne
seront pas prises en compte lors de la compilation.
La trs clbre directive #include nexiste plus, puisque lorganisation des fichiers sources est
trs dirente en C .
Les oprateurs # et ## de manipulation de chanes de caractres par le prprocesseur nexistent
plus.
La directive #warning a t ajoute en C et fonctionne sur le mme principe que la directive
#error.
Les directives #line #region et #endregion apparaissent et sont spcifiques C .
C Toute compilation dun fichier source est prcde dune phase de mise en forme du
fichier. Celle-ci est eectue par un prprocesseur. Il neectue que des traitements simples de
manipulation textuelle. En aucun cas le prprocesseur nest charg de la compilation. Toutes les
directives prprocesseur sont prcdes par le caractre #.
Le prprocesseur reconnat les directives suivantes :
#define
#undef
#if
#elif
#else
#endif
#error
#warning #line
#region #endregion
#pragma warning disable
#pragma warning restore
314
Le prprocesseur
315
fonction de la dfinition dune constante symbolique. Lavantage par rapport lutilisation des
directives prprocesseur #if #elif #else et #endif, est quil nest pas ncessaire daller commenter les appels la mthode lorsque celle-ci nest plus dfinie. Linconvnient est quun certain nombre de contraintes doivent sappliquer la mthode. Par exemple la mthode doit retourner le type void. La liste exhaustive de ces contraintes se trouve larticle The Conditional
Attribute dans les MSDN. Voici un exemple dutilisation de lattribut ConditionalAttribute :
Exemple 9-4 :
//#define __TRACE__
class Program {
[System.Diagnostics.Conditional("__TRACE__")]
public static void Trace(string s) {
System.Console.WriteLine(s) ;
}
static void Main() {
Trace("Hello") ;
System.Console.WriteLine("Bye") ;
}
}
Ce code ache ceci si la constante symbolique __TRACE__ nest pas dfinie.
Bye
Ce code ache cela si la constante symbolique __TRACE__ est dfinie.
Hello
Bye
316
La directive #warning fonctionne sur le mme principe, mis part quelle narrte pas la compilation mais gnre un avertissement.
La directive #line
La directive #line permet au dveloppeur de modifier la ligne et ventuellement le fichier o
une erreur est dclare par le compilateur. Lexemple suivant ache que les erreurs de compilations trouves sont la ligne 1 dans le fichier M
ethode Main() :
Exemple 9-7 :
class Program {
public static void Main() {
#line 1 "Methode Main()"
Le compilateur csc.exe
int i == 0 ;
317
// <- erreur ici : pas le droit dutiliser ==
}
}
Attention lutilisation de cette directive car elle peut mettre en dfaut certaines directives de
lenvironnement de dveloppement. Notamment, lenvironnement nest plus capable de retrouver lerreur puisquil ne dispose plus ni du fichier, ni de la ligne valide.
Le compilateur csc.exe
Le compilateur csc.exe peut tre appel soit directement en ligne de commande, soit par lenvironnement de dveloppement Visual Studio, soit par des scripts de compilation type MSBuild
ou NAnt. Les options de compilation sont dfinies soit dans la ligne de commande, soit par
lintermdiaire de lenvironnement soit au sein des scripts.
La Figure 9 -3 illustre les entres possibles et les types de fichiers de sortie possibles, de csc.exe.
Un seul fichier est produit par compilation :
Prsentons les options de compilation les plus utilises laide de quelques exemples :
Compile fichier.cs et produit fichier.exe (notez que pour produire un excutable il faut
quau moins une mthode statique Main() existe dans au moins une classe).
>csc.exe fichier.cs
Compile fichier.cs et produit fichier.dll (une mthode statique Main() nest pas requise ici).
>csc.exe /target:library fichier.cs
318
xx
Fichier1.cs
Modules rfrencs avec loption
/addmodule :
Module.netmodule
xx
Module1.netmodule
Produit un assemblage
librairie si loption
/target:library est utilise
Librairie.dll
xx
Librairie1.dll
Excutable rfrenc avec loption
/reference : ou /r :. Au plus un
excutable rfrenc par
compilation. Si la sortie est un
xecutable, ne pas rfrencer
dexcutable
et
Executable.exe
csc.exe
ou
Produit un assemblage
excutable si loption
/target:exe est utilise
Executable.exe
Compile fichier.cs et produit prog.exe. La mthode Main() qui sert de point dentre est
celle de la classe Prog qui est dans lespace de noms Namespace001.
>csc.exe /out:prog.exe /main:Namespace001.Prog fichier.cs
Compile avec optimisation tous les fichiers dextension .cs dans le rpertoire courant, dfinit la constante symbolique MACRO1 pour chacun de ces fichiers et produit prog.exe.
>csc.exe /out:prog.exe /define:MACRO1 /optimize *.cs
Compile en mode debug tous les fichiers dextension .cs dans le rpertoire courant, ne gnre pas davertissement et produit le fichier mod.netmodule.
>csc.exe /target:module /out:mod.netmodule /warn:0 /debug *.cs
Compile en tenant compte des rfrences aux assemblages dont les modules avec le manifeste sont les fichiers lib1.dll et lib2.dll.
Le module mod.netmodule sera un module de lassemblage dont le module contenant le
manifeste est prog.exe.
Pour la compilation, les fichiers lib1.dll et lib2.dll peuvent se trouver dans le rpertoire
courant ou dans le rpertoire de chemin C:\.
>csc.exe /lib:c:\ /r:lib1.dll;lib2.dll /addmodule:mod.netmodule
/out:prog.exe fichier.cs
Le compilateur csc.exe
319
Option
Description
/target /t
/out
/main /m
Spcifie la classe qui contient la fonction Main() qui servira de point dentre. Attention, le nom de la classe doit comprendre les espaces de nom
et doit respecter la casse. Cette option ne peut tre utilise que si lon fabrique un excutable.
/define /d
/optimise /o
/warn /w
Rgle le niveau davertissement. Ce niveau varie entre 0 (pas davertissement) et 4 (tous les avertissements sont achs).
/debug
/addmodule
Rfrence les modules utiliss par le produit de la compilation (un assemblage ou un module). Rappelons qu lexcution, tous les modules dun
assemblage doivent se trouver dans le mme rpertoire que lassemblage.
/reference/r
/lib
Spcifie les rpertoires o le compilateur peut chercher les fichiers rfrencs par loption /reference ou /r. Ces fichiers sont cherchs dans
lordre suivant :
Dans le rpertoire courant ;
dans le rpertoire du Common Langage Runtime ;
dans les rpertoires spcifis par /lib ;
dans les rpertoires spcifis par la variable denvironnement LIB.
320
/resource/
linkresource
Ajoute des ressources lassemblage. Lutilisation de ces options est dtaille page 37.
/unsafe
Indique que votre code peut contenir des zones de code non vrifiables
par le CLR (voir page 501).
/doc
Cette option permet de produire un fichier XML contenant les informations prsentes dans les commentaires /// du code source. Un exemple de
lutilisation de cette option est prsent un peu plus loin dans ce chapitre.
/help /?
Ache laide.
Une trentaine doptions sont disponibles. Nous vous avons prsent ici les plus courantes. Larticle C Compiler Options Listed by Category des MSDN fournit la liste exhaustive des options.
Toutes ces options sont aussi disponibles dans les proprits dun projet dans lenvironnement
de dveloppement Visual Studio.
Les alias
Alias sur les espaces de noms et sur les types
Le mot cl using peut tre utilis pour dfinir un alias vers un espace de nom ou vers un type.
La porte dun tel alias est le fichier courant si il est dfini hors de tout espace de noms. Dans le
cas contraire, la porte dun alias est le fichier courant intersection lespace de nom dans lequel
il est dfinit.
Exemple 9-8 :
//Definition de lalias C vers le type System.Console.
using C = System.Console;
class Program{
static void Main(){
C.WriteLine("Hello 1");
}
}
Code de Asm1.dll
Code de Asm2.sll
Les alias
Exemple 9-11 :
321
Code de Program.exe qui r
ef
erence Asm1.dll et Asm2.dll
Exemple 9-12 :
namespace Custom.IO{ public class Stream{} }
namespace FooIO{ public class Stream{} }
Le qualificateur global
Dans certains projets volumineux il se peut que vous ayez un conflit entre le nom dun espace
de noms et le nom dune ressource. Le programme suivant ne compile pas :
Exemple 9-14 :
using System ;
class Program {
class System { }
322
}
C 2 introduit le qualificateur global qui, plac devant un qualificateur dalias despaces de
noms, indique au compilateur que lon souhaite utiliser un espace de noms. Lexemple prcdent doit donc tre rcrit comme suit :
Exemple 9-15 :
using System ;
class Program{
class System { }
const int Console = 691 ;
static void Main(){
global::System.Console.WriteLine("Hello 1");
global::System.Console.WriteLine("Hello 2");
}
}
Exemple 9-16 :
namespace FooIO{ public class Stream{} }
Code de Asm2.sll
Exemple 9-17 :
namespace FooIO{ public class Stream{} }
Exemple 9-18 :
323
Les commentaires
C
Le texte plac entre les balises /* suivie de */ est comment. Ces balises peuvent ventuellement se trouver sur deux lignes direntes.
Si une ligne contient la balise // alors le texte de cette ligne qui suit cette balise est comment.
Si une ligne contient la balise /// alors le texte de cette ligne qui suit cette balise est comment. De plus ce texte fera partie de la documentation automatique du code source, prsente plus loin.
Un commentaire de type /*...*/ ne peut tre imbriqu dans un autre commentaire /*...*/.
En revanche, un commentaire de type // ou /// peut tre imbriqu dans un commentaire de
type /*...*/.
En consquence, une bonne ligne de conduite est dutiliser les commentaires de type // ou
/// pour commenter le code, et dutiliser les commentaires de type /*...*/ pour dsactiver
temporairement une rgion du code.
324
La documentation automatique
C ore la possibilit de produire un document partir des commentaires dun code source
marqus avec la balise ///. Cette possibilit est trs intressante car :
Ds quun projet atteint une certaine taille, les seuls commentaires dans le code ne susent
pas donner une vue densemble du projet. On est oblig davoir de la documentation
associe au projet.
Documenter du code est une tche longue et fastidieuse. Lexprience montre quavec le
temps les dveloppeurs ngligent la maintenance de la documentation technique dun projet. Seules les entreprises qui peuvent se permettre davoir un dpartement ddi la documentation technique parviennent maintenir correctement la documentation dun projet.
Les dveloppeurs sont bien souvent les personnes les mieux places pour commenter leur
code.
Le fait que la documentation technique dun projet soit un processus parallle au dveloppement du projet, implique la dsynchronisation inluctable de la documentation. La possibilit
de gnrer automatiquement la documentation partir du code source rsout ce problme
puisque la documentation technique est intgre au dveloppement du projet. Concrtement,
ds que le dveloppeur dclare une classe ou une mthode, il cre la documentation technique
associe au mme endroit (et au mme moment).
La production automatique de la documentation technique se fait en deux tapes :
Il faut dabord extraire et hirarchiser les informations prcises par les commentaires ///
du code source. Ces informations sont alors stockes dans un document XML. Ces commentaires contiennent eux-mmes des balises XML qui se retrouveront directement dans
le document XML gnr. Notez que Visual Studio 2005 vous aide dans la production de
ces balises avec lintellisense. Prcisons que les commentaires issus de la documentation
automatique se retrouvent aussi dans les tooltips de Visual Studio concernant les entits commentes.
Appliquer une feuille de style au fichier XML afin dobtenir une prsentation adapte la
lecture. Cette feuille de style est en gnral une transformation XSLT. La prsentation finale
est en gnral une arborescence de fichiers HTML.
document
XML
Arborescence
HTML
reprsentant la
documentation
automatique
Fichier2.c
Projet 2
Fichier3.c
Extraction et
Transformation
hirarchisation des
XSLT
commentaires ///
(durant la compilation
avec loption /doc)
325
Exemple 9-19 :
namespace MonEspaceDeNoms {
/// <summary>
/// MaClass illustre la production automatique de
/// la documentation technique
/// </summary>
class MaClass {
/// <summary>
/// Le point dentree de lapplication
/// </summary>
static void Main() {}
/// <summary>
/// La fonction f(int)
/// </summary>
/// <param name="i">Un entier</param>
static void f(int i){}
}
}
...produit le fichier XML suivant :
<?xml version="1.0"?>
<doc>
<assembly>
<name>AutomaticDocTest</name>
</assembly>
<members>
<member name="T:MonEspaceDeNoms.MaClass">
<summary>
MaClass illustre la production_automatique de
la documentation technique
</summary>
</member>
<member name="M:MonEspaceDeNoms.MaClass.Main(System.String[])">
<summary>
Le point dentree de lapplication
</summary>
</member>
<member name="M:MonEspaceDeNoms.MaClass.f">
<summary>
La fonction f(int)
</summary>
<param name="i">Un entier</param>
</member>
</members>
</doc>
Notez la prsence des balises <summary> et <param> la fois dans le code source C et dans le
fichier XML. Vous avez deux manires de produire ce fichier XML :
326
Soit vous prcisez dans les proprits du projet dans Visual Studio que vous souhaitez produire un fichier XML de documentation lors de la compilation. Pour cela il faut prciser
le nom du fichier XML dans loption Fichier de documentation XML de la fentre Gnrer des
proprits du projet.
partir du fichier XML, vous pouvez appliquer une feuille de style pour produire la documentation technique au format que vous souhaitez. Visual Studio 2003 prsentait un outil de
cration de pages HTML partir de tels documents XML Avec Visual Studio 2005, nous vous recommandons dutiliser des outils spcialiss eectuer cette tche. Citons notamment lexcellent
outil NDoc qui est open source et tlchargeable gratuitement.
Les identificateurs
Les identificateurs sont des noms choisis par le dveloppeur qui nomment des ressources telles
que des espaces de noms, des classes, des mthodes de classes, des champs de classes et en fait,
tout ce qui peut tre nomm dans le code source.
Un identificateur doit obir aux rgles suivantes :
Le premier caractre est soit une lettre (A Z ou a z ou une lettre accentue UNICODE)
soit le caractre de soulignement _ soit le caractre @. Le premier caractre ne peut tre un
chire.
Les autres caractres sont dans lensemble des caractres cits ( part @), unis avec lensemble des chires (0 9).
Un identificateur ne peut contenir plus de 255 caractres.
Un identificateur ne peut tre un mot-cl C .
327
Il est aussi conseill de faire commencer le nom dun champ priv par m_ et de faire commencer
le nom dune interface par un I majuscule.
Les conditions, qui excutent (ou pas) un bloc de code qu une certaine condition, portant
gnralement sur les tats de variables et dobjets.
Les branchements ou sauts, qui permettent de rediriger directement vers une instruction
particulire lunit dexcution. Cependant ce type de structures de contrle est proscrire
car il complexifie grandement la maintenance du code. De plus il est dmontr quon peut
toujours se passer de branchements dans du code source C .
Les appels de mthodes modifient eux aussi le comportement par dfaut de lunit dexcution,
dexcuter les instructions les unes la suite des autres. Cela peut sassimiler un saut, la
dirence fondamentale qu la fin de la mthode, lunit dexcution est capable de retourner
linstruction situe juste aprs lappel de mthode. Ce comportement fait quen gnral on ne
classe pas un appel de mthode dans les structures de contrles.
Utilisation de if/else
C
328
Lensemble else et son bloc dinstructions est optionnel. Un bloc dinstructions peut tre une
seule instruction ou plusieurs instructions, auquel cas il faut placer les accolades qui dfinissent
le bloc dinstructions :
if ( expression retournant un bool
een )
i = j*5 ;
else{
// Commencement du bloc dinstructions `
a ex
ecuter
// si la condition est fausse.
i = j*2 ;
j++ ;
} // Fin du bloc dinstructions `
a ex
ecuter si la condition est fausse.
Pour les lecteurs non habitus, il faudra faire attention lors de la lecture du code, au cas o il
ny aurait quune instruction. Vous pouvez utiliser les accolades, mme dans le cas o il ny a
quune instruction. En fait, il est conseill de toujours utiliser les accolades pour amliorer la
lisibilit du code.
Une condition est considre comme une instruction. Il est donc tout fait possible dimbriquer
les conditions :
if( expression1 retournant un bool
een )
if( expression2 retournant un bool
een )
Bloc `a executer si expression1 et expression2 sont true
else // se rapportant `a lexpression2
Bloc `a executer si expression1 true et expression2 false
else // se rapportant `a lexpression1
if( expression3 retournant un bool
een ) // pas de bloc else pour ce if
Bloc dinstructions `a executer si expression1 false et expression3 true
Ceci nuit gravement la qualit du code. De plus les conditions imbriques sont, en gnral,
issues dune mauvaise conception.
i = 5 ; int j = 8 ;
// si b vaut true alors...
// si b vaut false alors...
// si b vaut true alors...
// si b vaut false alors...
// si i diff
erent de 0 alors...
// si i egal `
a 0 alors...
// si i egal 4 alors...
// si i diff
erent de 4 alors...
// si i strictement inf
erieur `
a 4 alors...
// si i inf
erieur ou
egal `
a 4 alors...
// si i strictement inf
erieur `
a 4 et j strictement
// superieur `
a 6 alors...
if( i >= 4 && i<= 6)
// si i dans lintervalle ferm
e [4,6] alors...
if( i < 4 || j > 6)
// si i strictement inf
erieur `
a 4 ou j strictement
329
` 6 alors...
// superieur a
// si i hors de lintervalle ferm
e [4,6] alors...
// si i diff
erent de 4 ou b est true alors...
La facilit dcriture ?:
Une facilit dcriture est propose :
condition ? Val retournee si condition true : Val retourn
ee si condition false ;
On parle doprateur ternaire ?:. En eet, ces le seul oprateur pour lequel trois oprandes sont
prises en compte.
Voici quelques exemples dutilisation :
bool b = true ;
int i = 5 ;
int j = 8 ;
// k1 = i si b true, sinon k = j
int k1 = b ? i : j ;
// k2 = 6 si i different de j, sinon k2 = 7
int k2 = (i!=j) ? 6 : 7 ;
// s="bonjour" si i strictement inf
erieur `
a j, sinon s="hello"
string s = i<j ? "bonjour" : "hello" ;
// k3 = i*2 si i superieur ou egal `
a j, sinon k3 = i*3
int k3 = i>=j ? i*2 : i*3 ;
Linstruction switch
C++ C Programmeur C/C++ ATTENTION ! Il y a de subtiles modifications en ce qui
concerne lutilisation du mot-cl switch :
Vous pouvez toujours switcher sur une variable de type entier, boolen, numration.
La nouveaut C est que vous pouvez switcher sur une chane de caractres.
La continuation vers le mot-cl case suivant, ne se fait pas automatiquement, donc le motcl break est obligatoire. La continuation vers le mot-cl case suivant se fait automatiquement lorsquil ny a pas dinstructions pour le cas prsent.
Vous pouvez dclarer des variables lintrieur dun bloc dinstructions, dans un bloc dinstructions case, mme si celui-ci nest pas entre accolades.
C Tout comme les mots-cls if/else, le mot-cl switch permet de modifier le cours du
programme en fonction de la valeur dune variable. Cependant, lutilisation de switch est particulirement adapte aux types valeurs discrtes (entiers, numrations, string) et sa syntaxe
permet de traiter plusieurs cas de valeurs, plus facilement que les mots-cls if/else. Voici un
exemple :
330
Exemple 9-20 :
class Program {
static void Main() {
int i = 6 ;
switch (i){
case 1:
System.Console.WriteLine("i vaut 1") ;
break ;
case 6:
System.Console.WriteLine("i vaut 6") ;
break ;
default:
System.Console.WriteLine("i ne vaut ni 1 ni 6") ;
break ;
}
}
}
Deux mots-cls apparaissent dans cet exemple, en plus des mots-cls switch et case :
break : lorsque lunit dexcution rencontre linstruction break, elle continue son cours
directement la fin du switch. Notez que si un bloc de code switch contient au moins une
instruction, il doit se terminer soit par linstruction break soit par une instruction goto soit
par une instruction return. Dans le cas contraire, le compilateur signale une erreur.
Vous avez la possibilit dexcuter le mme bloc dinstructions pour plusieurs valeurs. Par
exemple :
Exemple 9-21 :
class Program {
static void Main() {
int i = 6 ;
switch (i){
case 1:
case 3:
case 6:
System.Console.WriteLine("i vaut 1 ou 3 ou 6") ;
break ;
default:
System.Console.WriteLine("i ne vaut ni 1 ni 3 ni 6") ;
break ;
}
}
}
331
Dans ce cas, il est impratif quaucune instruction napparaisse aprs case 1: ou case 3:.
Enfin on peut utiliser aussi linstruction de branchement goto (dcrite un peu plus loin) mais
ceci est compltement proscrire. Le cas suivant montre que lon peut faire du code dicilement comprhensible en quelques lignes :
Exemple 9-22 :
class Program {
static void Main() {
int i = 6 ; int j = 7 ;
switch (i){
case 1:
System.Console.WriteLine("passage par case 1") ;
goto default ;
case 6:
System.Console.WriteLine("passage par case 6") ;
if (j > 2) goto case 1 ;
break ;
default:
System.Console.WriteLine("passage par default") ;
break ;
}
}
}
Ce programme ache :
passage par case 6
passage par case 1
passage par default
Enfin, sachez que les types possibles de la variable sur laquelle agit un switch sont :
Les types entiers sbyte byte short ushort int uint long ulong.
Les chanes de caractres, le type string. Notez que dans ce cas, si une instruction switch
a plus de 6 blocs case le compilateur C 2 utilise une table de hachage pour viter de trop
nombreuses comparaisons de chanes de caractres.
332
Boucle while
Boucle do/while
La seule dirence entre ces types de boucle est mise en vidence dans cet exemple. Les boucles
do/while excutent au moins une fois le bloc dinstructions. Si la condition est fausse en entrant
dans le while, les boucles de type while nexcutent pas le bloc dinstructions.
333
Linstruction break fait quitter la boucle. Cette instruction fait aussi quitter un bloc switch
comme on la vue prcdemment.
Dans le cas de boucles imbriques, ces deux instructions sappliquent la boucle la plus proche
deux.
Exemple 9-23 :
class Program {
static void Main() {
for (int i = 0 ; i < 10 ; i++){
System.Console.Write(i) ;
if (i == 2) continue ;
System.Console.Write("C") ;
if (i == 3) break ;
System.Console.Write("B") ;
}
}
}
Ce programme ache :
0CB1CB23C
Les instructions break et continue ont tendance compliquer la lisibilit du code. Le rsultat
de lexemple prcdent, nest vident pour personne. Il faut donc les utiliser le moins souvent
possible.
Linstruction break peut aussi tre utilise pour interrompre ce quon appelle des boucles infinies, cest--dire des boucles de type for, do/while et while, dont la condition de sortie est
toujours vraie. Voici des exemples de boucles infinies :
for(;;) {...}
for(;true;) {...}
while(true) {...}
do{...}
while(true) {...}
Boucles et optimisations
Du fait que le code contenu dans une boucle est potentiellement sujet un grand nombre
dexcutions, il peut tre ecace dessayer de loptimiser. Voici quelques conseils :
Si vous dtectez des appels des mthodes qui ncessitent beaucoup de passages darguments, il peut tre ecace de copier le corps de la mthode dans la boucle (on parle
dinlining). Remarquez quen page 112, nous expliquons que parfois le compilateur JIT du
CLR est capable deectuer une telle optimisation.
Si vous accdez une proprit dun objet dont vous savez que la valeur retourne restera
constante durant toute la boucle, il est ecace de stocker au pralable ces valeurs constantes
dans des variables locales.
Penser utiliser la classe StringBuilder plutt que la classe String pour fabriquer une
chane de caractres dans une boucle.
334
Si vous avez tester plusieurs conditions de sortie dune boucle, il est ecace de tester en
premier la condition de sortie la plus probable.
Bien quen gnral moins pratique dutilisation, les boucles for sont lgrement plus ecaces que les boucles foreach.
Lorsque vous ralisez des optimisations, essayez surtout de quantifier le gain de performance apport. En eet, dans un environnement gr par le CLR, certaines de vos optimisations peuvent
gner le CLR et son compilateur JIT pour finalement savrer contre productives.
La mthode Main()
C++ C Comme en C/C++, en C le point dentre dun assemblage excutable est une
mthode appele Main().
La mthode Main()
335
Comme en C/C++, en C , la mthode Main() peut retourner int ou void et accepte ventuellement un tableau de chanes de caractres reprsentant les arguments en ligne de commande de
lexcutable. En C , il nest plus besoin de prciser la taille du tableau.
la dirence de C/C++ le tableau de chanes de caractres ne contient pas le nom de lexcutable en premire occurrence, mais directement le premier argument.
la dirence de C/C++ le m de Main est une majuscule.
la dirence de C/C++ Main est une mthode statique dune classe et non une fonction globale.
C Chaque assemblage directement excutable (i.e dont le module principal a une extension
.exe) possde au moins une mthode statique Main() dans une de ses classes. Cette mthode
reprsente le point dentre du programme, cest--dire que juste aprs le lancement et linitialisation dune application .NET, le thread principal va commencer par excuter le code de cette
mthode. Lorsque cette mthode retourne, le processus est dtruit la condition quil ny ait pas
de threads de premier plan (thread foreground) qui soient toujours en cours dexcution. Si ces
notions de thread ou de thread foreground vous sont trangres, sachez quelles sont prsentes
au dbut du chapitre 5.
Un mme assemblage peut avoir ventuellement plusieurs mthodes Main() (chacune dans une
classe dirente). Le cas chant, il faut prciser au compilateur quelle mthode Main() constitue le point dentre du programme. Ce qui peut se faire soit avec loption /main en ligne de
commande du compilateur csc.exe, soit dans les proprits du projet sous Visual Studio, Application startup object. Cette facilit est extrmement utile pour dboguer une classe particulire.
Une mthode Main() est statique et sa signature suit les rgles suivantes :
Par exemple le programme suivant ajoute les nombres passs en arguments en ligne de commande, et ache le rsultat. Sil ny a pas dargument, le programme le signale :
Exemple 9-25 :
class Program {
static void Main(string[] args) {
if (args.Length == 0)
System.Console.WriteLine("Entrez des nombres `
a ajouter.") ;
else{
long result = 0 ;
foreach (string s in args)
result += System.Int64.Parse(s) ;
336
Les informations communiques en ligne de commande (ainsi que les valeurs des variables
denvironnement) peuvent tre aussi rcupres grce aux mthodes string[] GetCommandLineArgs() et IDictionary GetEnvironmentVariables() de la classe System.Environment.
10
Le systme de types
C++ C
C C est un langage typ, cest--dire que chaque objet a un type et un seul. Ce type est
compltement dfini au moment de la cration de lobjet, lexcution. En C chaque variable
doit tre initialise, sinon le compilateur produira une erreur lors de son utilisation.
Allocation/dsallocation
Lors de lexcution dun programme, le fait de rserver une zone mmoire pour quelle
contienne les donnes relatives un objet est nomm allocation. Lopration inverse de restitution de la zone mmoire est appele dsallocation. La taille de cette zone mmoire, spcifie
en octets, doit tre au moins gale au nombre doctets ncessaires pour stocker ltat de lobjet.
Ce nombre doctets est fonction de limplmentation de lobjet.
Un processus peut un instant donn contenir un ou plusieurs threads. En tant quespace
dadressage, le processus contient toutes les zones mmoires alloues pour tous les objets du
programme. En tant quunits dexcution, seuls les threads peuvent utiliser les objets.
La pile
Chaque thread Windows a une zone mmoire prive que lon nomme la pile (stack en anglais).
Cette zone de mmoire est prive dans le sens o elle ne devrait pas tre accessible par les autres
threads (bien que ceci soit possible sous certaines conditions spciales). Cette zone mmoire est
338
contenue dans lespace dadressage du processus du thread. Le thread se sert de sa pile principalement pour :
Chaque thread a un accs privilgi sa pile. En eet, les jeux dinstructions machine contiennent
des instructions optimises pour accder la pile. En outre le langage IL contient de nombreuses
instructions ddies la gestion de la pile. Notez quune pile est de taille variable et borne en
gnral par une grandeur de lordre du Mo. Cette limite peut tre dfinie lors de la construction
du thread.
Le tas
Un processus a gnralement un seul (mais parfois plusieurs) tas (heap en anglais). Cest une
zone mmoire contenue dans lespace dadressage du processus. Cette zone mmoire est accessible par tous les threads du processus. Donc, contrairement aux piles des threads dun processus, le tas nest pas spcifique un thread particulier. Le tas est principalement utilis pour
stocker des objets et, linstar des piles, sa taille peut varier au cours du temps. Cependant la
taille maximale du tas est beaucoup plus grosse que le Mo. En fait, il est assez singulier que le
bon droulement dune application soit limit par la taille maximale du tas.
Comparaison pile/tas
Un objet peut donc tre stock soit dans une pile dun thread soit dans un tas dun processus.
Les notions de pile et de tas doivent coexister car chacune a ses avantages :
Lavantage du tas est quil peut tre beaucoup plus gros quune pile. De plus il est accessible
par tous les threads du processus mais ceci nest pas toujours un avantage.
Lavantage de la pile est que laccs aux donnes est plus rapide quavec le tas. Ce gain est d
des instructions IL spciales. Ce gain est aussi d au fait que laccs la pile na pas tre
synchronis.
Il serait donc judicieux dutiliser le tas pour stocker les objets volumineux et dutiliser la pile
pour stocker les objets de petite taille. Nous allons voir que cest exactement ce choix qui a t
fait par les concepteurs de .NET.
En C++ le choix du mode dallocation dune variable (dun objet) est laiss au dveloppeur.
Lallocation statique est utilise lorsque lobjet est directement dclare dans le code (par
exemple int i=0;) Lallocation dynamique est utilise lorsque lobjet est alloue avec loprateur new (par exemple int * pi = new int(0);).
339
Une autre dirence importante entre C++ et C est la responsabilit de la dsallocation des
variables dynamiques. En C++ cette responsabilit incombe au dveloppeur alors quen C elle
incombe une couche logicielle fournie par lenvironnement dexcution .NET. Cette couche
est nomme ramasse-miettes et elle fait lobjet de la section 116. Dans tous les cas, cette responsabilit est lourde car si les variables alloues dynamiquement, devenues inutiles, ne sont pas
rgulirement dsalloues, la taille du tas crot en permanence et finira srement par causer
des problmes. Ce type de problme est connu sous le nom de fuite de mmoire (memory leak en
anglais).
La dsallocation des variables alloues statiquement est dans tous les cas sous la responsabilit
du thread qui possde la pile concerne. Retenez surtout quen C la responsabilit du dveloppeur est allge par rapport au C++ :
340
Exemple 10-1 :
// TypeVal est un type valeur, car cest une structure.
struct TypeVal {
public int m_i ;
public TypeVal( int i ) { m_i = i ; }
}
// TypeRef est un type reference, car cest une classe.
class TypeRef {
public int m_i ;
public TypeRef( int i ) { m_i = i ; }
}
class Program {
static void Main() {
TypeVal v1 = new TypeVal(6);
TypeVal v2 = v1; // Une nouvelle instance du type TypeVal est
// creee et le champ v2.i est aussi
egal `
a 6.
// Neanmoins v1 et v2 sont deux instances diff
erentes
// de type TypeVal.
v2.m_i = 9;
// Ici v1.i vaut 6, il y a bien deux instances du type TypeVal.
System.Diagnostics.Debug.Assert( v1.m_i == 6 && v2.m_i == 9 ) ;
TypeRef r1 = new TypeRef(6);
TypeRef r2 = r1; // Il ny a pas de nouvelle instance de la
// classe TypeRef. r2 et r1 sont deux r
ef
erences de la
// meme instance de la classe TypeRef.
r2.m_i = 9;
// Ici r1.i vaut 9, il ny a quune seule instance de la
// classe TypeRef.
System.Diagnostics.Debug.Assert( r1.m_i == 9 && r2.m_i == 9 );
}
}
On saperoit dans lexemple prcdent que loprateur new peut tre ventuellement utilis
pour les allocations dobjets de type valeur mais ne modifie en rien le caractre statique de lallocation. Dans ce cas, cet oprateur sert cependant communiquer des paramtres au constructeur.
Contrairement au C++, lors dune allocation statique (i.e dun type valeur) en C on ne
peut fournir des arguments au constructeur sans utiliser loprateur new. En C++ ceci tait
accept par le compilateur :
MyType v1(6) ;
En C il faut crire :
MyType v1 = new MyType(6) ;
Dans le cas dune allocation dynamique, donc de lallocation dun objet de type rfrence, lutilisation de loprateur new est obligatoire (y compris si le constructeur ne prend pas de paramtres). Nous allons dtailler par la suite quels sont les types valeur et quels sont les types
341
rfrence, mais nous pouvons dj prciser que les types valeur sont les types primitifs de C
(dclars avec les mots-cls int,double...) les structures (dclares avec le mot-cl struct) et
les numrations (dclares avec le mot-cl enum) alors que les types rfrence sont les classes
(dclares avec le mot-cl class) et les dlgations (qui sont des classes particulires dclares
avec le mot-cl delegate).
Les instances des types valeur ne sont pas toujours stockes sur la pile. En eet, lorsquun champ
dune instance de classe est de type valeur, il est stock au mme endroit que linstance de la
classe, cest--dire sur le tas. En revanche, les objets de types rfrence sont toujours stocks sur
le tas. Lorsquun champ dune instance de structure est de type rfrence, seule la rfrence est
stocke au mme endroit que linstance de la structure (sur la pile ou sur le tas selon les cas).
342
Une classe est dclare avec le mot-cl class. Avant tout, comprenez bien que ref1 ref2 et ref3
sont trois rfrences vers des objets, instances de la classe Personne. Au dpart ref1 est initialise
avec le mot-cl null. Cela signifie quaucun objet nest rfrenc par ref1. ce stade, aucun
membre de Personne ne peut tre utilis sur ref1.
Deux objets Personne sont alors allous (donc sont allous dynamiquement sur le tas du processus puisquils sont de type rfrence). Appelons-les Raymond et Josiane. Contrairement ce
que lon a vu pour les types valeur, dans le cas de type rfrence, il est obligatoire dutiliser loprateur new pour crer Raymond et Josiane. ce stade nous disposons des deux objets Raymond et
Josiane qui instancient la classe Personne. Nous disposons aussi de trois rfrences, ref1 qui est
nulle, ref2 qui rfrence Raymond et ref3 qui rfrence Josiane. ref1 rfrence alors Raymond.
Raymond est vieilli de 10 ans. ref1 rfrence alors Josiane. Josiane est vieillie de 10 ans.
Au final, les objets Raymond et Josiane ont tous les deux t modifis sans passer par les rfrences ref2 et ref3. De plus les objets Raymond et Josiane ne sont jamais manipuls directement. La syntaxe de C ne permet de manipuler des instances de types rfrence que par lintermdiaire de rfrences.
Laccolade de fin de la mthode Main() implique que les objets Raymond et Josiane ne sont
plus rfrencs (en eet les rfrences ref1, ref2 et ref3 nexistent plus). Raymond et Josiane ne
seront plus jamais utiliss puisquils ne sont plus rfrencs. Ainsi ces deux objets seront marqus comme non actifs par le prochain dclenchement du ramasse-miettes. Ils seront dsallous
ultrieurement.
343
Types rfrences
System.Object
Interfaces
Classes
System.ValueType
System.String
System.SByte
System.Byte
System.Int16
System.UInt16
System.Int32
System.UInt32
System.Int64
System.UInt64
System.Char
System.Boolean
System.Double
System.Single
System.MultiCastDelegate
System.Enum
System.Decimal
Dlgations
numrations
Structures
System.Array
Tableaux
System.Delegate
Pointeurs
Lgende
A drive de B
Les types primitifs (appels aussi types lmentaires) : Ces types reprsentent les entiers, les
nombres virgules, les caractres et les boolens. Ce sont des types valeur et en gnral
les langages dfinissent des alias pour faciliter leur utilisation. Ainsi le type System.Int16
correspond lalias short en C et Short en VB.NET.
Les numrations : Ces types sont de type valeur et sont utiliss pour typer des ensembles de
valeurs.
Les structures : Ces types sont de type valeur. Les structures et les classes ont des similitudes
et des dirences.
Les classes : Ces types sont de type rfrence. Notez que le type reprsentant les chanes de
caractres et le type reprsentant les tableaux sont respectivement les classes System.String
344
Les dlgations : Ces types sont des classes particulires dont les instances sont utilises pour
rfrencer des mthodes.
Les pointeurs : Ces types sont trs spciaux et utilisables seulement sous certaines conditions.
Nous dtaillons ce sujet dans la section en page 503.
La classe System.Object
C++ C En C toutes les classes et toutes les structures drivent de la classe Object. Les
mthodes de la classe Object, utilisables par toutes les classes et structures, ajoutent des fonctionnalits de hachage dobjet et de RTTI (Run Time Type Information). La fonctionnalit la plus
utilise est assurment la possibilit de redfinir la mthode Object.ToString() qui est cense
retourner une chane de caractres dcrivant lobjet, un peu comme loprateur C++ dans les
flots de donnes.
C Le mot cl object du langage C est un alias vers System.Object. La vue densemble du
CTS montre que mis part les interfaces et les types pointeurs, tous les types drivent automatiquement et implicitement, directement ou indirectement de la classe Object. En ce qui
concerne les interfaces on peut toujours convertir un objet rfrenc par une interface en une
rfrence de type Object car une interface est toujours implmente par un type rfrence ou
valeur. Ainsi, la classe Object joue un rle prpondrant dans larchitecture de la plateforme
.NET. De nombreuses mthodes standard acceptent leurs arguments sous forme de rfrences
types par la classe Object.
La classe Object prsente plusieurs mthodes. Chacune de ces mthodes peut donc sappliquer
tous les objets. Nanmoins il est logique de redfinir certaines des mthodes virtuelles pour
pouvoir les utiliser. Ces mthodes virtuelles peuvent tre redfinies dans le cadre dune classe,
mais aussi dans le cadre dune structure. Dans le cas dune numration, seule la mthode virtuelle ToString() est automatiquement redfinie par le compilateur. Les mthodes de la classe
Object sont :
345
class Program {
static void Main() {
Personne raymond = new Personne("Raymond", 50) ;
// WriteLine() appelle automatiquement Raymond.ToString()
System.Console.WriteLine(raymond) ;
}
}
Ce programme ache :
Nom:Raymond Age:50
Console.WriteLine() appelle automatiquement la mthode virtuelle ToString() des objets dont elle doit acher ltat sur la console. Si cette mthode nest pas redfinie, cest limplmentation par dfaut de la mthode ToString() de la classe Object qui est invoque.
346
Nous allons maintenant nous intresser aux possibilits oertes par le framework .NET pour
personnaliser la comparaison des instances de vos types.
347
348
349
Exemple 10-7 :
...
class Article : System.ICloneable {
...
public object Clone() {
// Copie superficielle = Copie en profondeur.
return this.MemberwiseClone() ;
}
}
class Commande : System.ICloneable {
...
public object Clone() {
// Copie en profondeur.
Commande clone = new Commande() ;
clone.Quantite = this.Quantite ;
clone.Article = this.Article.Clone() as Article;
return clone ;
}
}
...
Lachage de ce programme est maintenant ceci :
Commande : 2 x Chaussure
Commande : 2 x Chaussure
Remarquez que dans lexemple prcdent nous prcisons quen ce qui concerne la classe Article
la copie superficielle est quivalente la copie en profondeur. On pourrait tre tent darmer
que pour une classe donne, la copie superficielle est quivalente la copie en profondeur si
et seulement si tous ses membres sont de type valeur. Or la classe Article admet un champ de
type string qui est un type rfrence. Nous expliquons un peu plus loin dans ce chapitre (en
page 370) que la classe String prsente certaines proprits dont limmuabilit de ses instances,
qui font que souvent, les instances de cette classe peuvent tre considres comme des instances
dun type valeur.
Linterface ICloneable est souvent critique principalement parce quelle ne permet pas ses
implmentations de communiquer clairement leurs clients sil sagit dune copie en profondeur ou dune copie superficielle, voire dune copie en profondeur incomplte. Les concepteurs
du framework ont failli rendre cette interface obsolte lors du passage .NET 2.0. La raison principale qui a sauve cette interface de lobsolescence est quelle est implmente par de nombreuses classes standard ou propritaires. Aussi, il reste dconseill de limplmenter moins
de fournir une documentation rigoureuse. Une alternative est par exemple un constructeur de
copie qui prend en paramtre un boolen prcisant quelle genre de copie lon souhaite :
Exemple 10-8 :
...
class Commande {
public int Quantite ;
public Article Article ;
public override string ToString() {
350
Un autre argument qui plaide en faveur de linterface ICloneable est quelle permet dimplmenter naturellement le design pattern prototype qui permet de crer de nouveaux objets en
clonant un objet prototype rfrenc par une rfrence de type ICloneable.
Boxing et UnBoxing
C++ C Rien de semblable nexiste en C/C++. Tout ceci dcoule directement du fait que
tous les types drivent de la classe Object. Il nexiste pas de telle classe en C/C++.
C Les instances de type valeur, locales une mthode, sont directement stockes dans la
pile du thread. Le thread na donc pas besoin de pointeurs ni de rfrence vers les instances de
types valeur.
Beaucoup de mthodes ont besoin darguments sous forme dune rfrence de type Object.
Comme tous les types, les types valeur drivent de la classe Object. Cependant les instances de
types valeur nayant pas de rfrences, il a fallu trouver une solution pour pouvoir obtenir une
rfrence vers une instance dun type valeur lorsque lon en a besoin. Cette solution fait lobjet
de la prsente section : cest le boxing (appel compartimentation en franais. Ce terme est peu
usit, aussi nous conserverons le terme boxing dans cet ouvrage).
Opration de Boxing
Voici un exemple concret pour exposer la problmatique. La mthode f() accepte une rfrence
de type object. A priori, on ne peut donc pas lappeler avec un argument qui nadmet pas de
rfrence, par exemple un entier de type int :
Boxing et UnBoxing
351
Exemple 10-9 :
class Program {
static void f( object o ) { }
public static void Main() {
int i = 9 ;
f( i ) ;
}
}
Cependant le petit programme prcdent compile et fonctionne. La magie du boxing a permis
dobtenir une rfrence vers une instance qui nen avait pas ! Lopration de boxing seectue en
interne en trois tapes :
Une nouvelle instance du type valeur est cre et alloue sur le tas.
Cette instance est initialise avec ltat de linstance alloue sur le tas. Dans le cas de notre
entier, une copie de quatre octets est eectue. On peut dire que notre instance initiale a
t clone.
Une rfrence vers la nouvelle instance est utilise la place de linstance alloue sur le tas.
On profite de cet exemple pour souligner que le mot cl C int est un alias vers le type
System.Int32.
352
Exemple 10-10 :
class Program {
static void f(object o1, object o2) {
if (o1 == o2)
System.Console.WriteLine("M
emes r
ef
erences") ;
else
System.Console.WriteLine("R
ef
erences diff
erentes") ;
}
public static void Main() {
int i = 9 ;
f(i, i) ;
}
}
Exemple 10-11 :
class Program {
static void f(object o1, object o2) {
if (o1 == o2)
System.Console.WriteLine("M
emes r
ef
erences") ;
else
System.Console.WriteLine("R
ef
erences diff
erentes") ;
}
public static void Main() {
int i = 9 ;
object o = i;
f(o, o) ;
}
}
Opration de UnBoxing
Lopration inverse du boxing existe et sappelle le UnBoxing (le terme dcompartimentation existe
en franais mais nous continuerons utiliser le terme unboxing). Voici un exemple o le unboxing est mis en uvre :
Exemple 10-12 :
class Program {
public static void Main() {
int i = 9 ;
object o = i;
// i est box
e.
int j = (int)o; // o est unbox
e.
}
}
Le code IL de cette mthode Main() est le suivant :
.locals init ([0] int32 i,
[1] object o,
IL_0000:
IL_0002:
IL_0003:
IL_0004:
IL_0009:
IL_000a:
IL_000b:
IL_0010:
IL_0011:
IL_0012:
353
[2] int32 j)
ldc.i4.s
9
stloc.0
ldloc.0
box
[mscorlib]System.Int32
stloc.1
ldloc.1
unbox
[mscorlib]System.Int32
ldind.i4
stloc.2
ret
On voit que linstruction unbox du langage IL est prvue spcialement pour lopration de unboxing. Sans entrer dans les dtails internes, sachez que linstruction unbox place un pointeur
sur la pile vers lobjet box qui se trouve sur le tas. Cest linstruction IL ldind qui charge sur la
pile, la valeur de lobjet rfrence.
Linstance ne peut tre unboxe vers le type spcifi si elle nest pas exactement de ce type. Lexception InvalidCastException peut donc tre lance par une opration de unboxing. De plus
si la rfrence unboxer est nulle lexception NullReferenceException est lance.
En C , les oprations de boxing et dunboxing sont implicites. Cest--dire que cest le compilateur qui va gnrer les appels aux instructions IL box et unbox lorsque cest ncessaire. Il nen va
pas ncessairement de mme pour tous les langages .NET.
Les types prsents ici sont assez similaires aux types C++.
Le mot-cl unsigned nexiste pas en C . On utilise les types byte, ushort, uint et ulong pour
typer les entiers non signs.
La taille dun long/ulong est de huit octets en C .
En C , le type decimal (sur 16 octets), reprsente les rels dune manire exacte, dans la limite
de 28 chires significatifs.
En C , le type bool naccepte que les valeurs true et false. Aucune conversion avec des types
entiers nest permise.
En C le type char est maintenant cod sur deux octets et respecte la norme UNICODE. Rappelons quen C++ ce type est cod sur un octet et respecte la norme ASCII.
C Le langage C possde plusieurs types primitifs, qui sont tous de type valeur. Chaque type
primitif de C correspond exactement un type du CTS. Les types primitifs sont dfinis par
les mots-cls : bool, byte, sbyte char, short, ushort, int, uint, long, ulong, float,
double, decimal. Les correspondances entre ces mots-cls et les types du Framework .NET sont
dfinies ci-aprs.
Chaque type primitif permet lcriture de constantes. En eet le dveloppeur a souvent besoin
dinitialiser ses variables de type primitif avec des valeurs de ce type.
354
Taille
bits
en
Intervalle de valeur
byte
System.Byte
[ 0 ; 255 ]
sbyte
System.SByte
[ -128 ; 127 ]
short
System.Int16
16
[ -32768 ; 32767 ]
ushort
System.UInt16
16
[ 0 ; 65635 ]
int
System.Int32
32
[-2,1x109 ; 2.1x109 ]
uint
System.UInt32
32
[ 0 ; 4.2x109 ]
long
System.Int64
64
[-9,2x1018 ; 9,2x1018]
ulong
System.UInt64
64
[ 0 ; 1,8x1019]
Les types non signs ushort, uint et ulong ainsi que le type sign sbyte ne sont pas CLS
compliant (cest--dire quils ne sont pas conformes au CLS dfini page 129). Concrtement
les autres langages utilisant .NET ne sont pas tenus de les implmenter. Il ne faut donc pas
utiliser ces types dans les arguments de mthodes susceptibles dtre appeles par un autre
langage.
// i = 1 milliard
// j nest pas
egal `
a 10 milliards
Quelle est la valeur de j ? Elle nest pas de 10 milliards. En eet le calcul stant eectu avec des
int, (limit deux milliards et quelque en valeur absolue) le rsultat est de type int. Ensuite
le rsultat est finalement copi dans une variable de type long. Le problme principal est que
rien nest signal au dveloppeur par le compilateur (sauf sil utilise le mot-cl checked). Pour
rsoudre ces problmes, on doit signaler au compilateur que lon veut utiliser des valeurs sur
huit octets en ajoutant L la constante entire :
355
Types du CTS
correspondants
Taille
en
bits
Intervalle de valeur
Plus petite
valeur possible
Prcision
float
System.Single
32
[-3,4x1038 ;
3,4x1038 ]
1,4x10-45
7 dcimales
double
System.Double
64
[-1,8x10308 ;
1,8x10308]
4,9x10324
15 dcimales
decimal
System.
Decimal
128
[ -8x1028 ; 8x1028
]
1x10-28
28 dcimales
Description
Les types single et double prsentent aussi les champs statiques constants suivants :
356
Champs
Description
Epsilon
Plus petite valeur positive non nulle reprsentable avec le type primitif concern.
MaxValue
MinValue
Le type decimal
Avec ses 28 dcimales, le type dcimal est trs utile dans les applications financires ou chaque
dcimale compte. Il vaut mieux lviter pour des applications de calcul intensif qui ne ncessitent pas une telle prcision car il est coteux de manipuler des instances de decimal.
Dfinition de constantes
Par dfaut les constantes relles sont de type double. Voici quelques exemples :
double
double
double
double
double
d1
d2
d3
d4
d4
=
=
=
=
=
10 ;
10.0 ;
10E3 ;
10.1E3;
1.1E-1;
//
//
//
//
//
OK,
OK,
OK,
OK,
OK,
d1 vaut dix.
d2 vaut dix.
notation scientifique d3 vaut dix milles.
notation scientifique d4 vaut dix milles cent.
notation scientifique d5 vaut z
ero virgule onze.
Le type boolen
Le mot-cl C pour ce type est bool. Une variable de ce type ne peut prendre que deux valeurs :
true ou false. Le fait que la valeur true soit reprsente par un 1 ou un 0 par le CLR lexcution ne concerne en aucun cas le dveloppeur. Si vous avez un tableau de boolens, il est
prfrable dutiliser un des types prvus cet eet dcrit en page 573, qui optimise le stockage
des valeurs en mmoire en stockant 8 boolens dans un octet.
357
Le mot-cl bool est un alias pour le type du CTS System.Boolean. Cette structure prsente les
deux mthodes public static bool Parse(string s) et public string ToString()
dcrite un peu plus haut. Notons que Sytem.Boolean prsente aussi les deux champs statiques
constants de type string, FalseString (gal "False") et TrueString (gal "True"). Ces deux
chanes de caractres sont les images (respectivement les antcdents) des valeurs false et true
par la mthode ToString() (respectivement Parse()).
c1
c2
c3
c4
=
=
=
=
A ;
//
\x41 ; //
(char)65 ; //
\u0041 ; //
Lettre A
Le 65`eme
Le 65`eme
Le 65`eme
en UNICODE.
caract`
ere UNICODE A(41 est en Hexa).
caract`
ere UNICODE A.
caract`
ere UNICODE A(41 est en Hexa).
Le mot-cl char reprsente le type du CTS System.Char. Cette structure prsente notamment
des mthodes statiques pour dterminer si un caractre est une lettre majuscule, une lettre minuscule, un chire etc. En voici quelques-unes :
Mthodes statiques de la classe
System.Char
Description
bool IsLower(char ch )
bool IsUpper(char ch )
bool IsDigit(char ch )
bool IsPunctuation(char ch )
bool IsWhiteSpace(char ch )
char ToUpper(char ch )
char ToLower(char ch )
358
Oprateur
Opration
Laddition.
La soustraction.
La multiplication.
La division.
359
Ces oprations sappliquent tous les types primitifs, entiers ou rels. Chacune de ces oprations
accepte deux oprandes. Une facilit dcriture est propose avec les cinq oprateurs suivants :
Facilit dcriture
Equivalence
i += j;
i = i+j;
i -= j;
i = i-j;
i *= j;
i = i*j;
i /= j;
i = i/j;
i %= j;
i = i%j;
Sur les variables de type double et float, la variable prend la valeur particulire NaN (Not
a Number).
Sur des variables de types entiers les bits significatifs qui dpassent ne sont pas pris en
compte. On obtient donc une valeur errone. Pour pallier ce comportement par dfaut
pour le moins dangereux, vous pouvez utiliser le mot-cl checked :
Exemple 10-14 :
class Program {
static void Main() {
byte i = 255 ;
checked {
i += 1 ; // Une exception de type
// System.OverflowException est lanc
ee.
}
}
}
Le mot-cl checked peut aussi sappliquer directement une expression.
360
...
byte i = 255 ;
i = checked( (byte)(i+1) ) ; // Une exception de type
// System.OverflowException est lanc
ee.
Le mot-cl checked ne peut sappliquer une classe ou une mthode. Dans un bloc de
code vrifi par le mot-cl checked, vous pouvez utiliser le mot-cl unchecked pour ponctuellement revenir au comportement par dfaut.
Lorsquil y a mme priorit loprateur le plus gauche dans lexpression est prioritaire.
Mais cela na pas dimportance puisque les oprations de base avec la mme priorit sont
commutatives.
Par exemple :
Exemple 10-16 :
...
int
int
int
int
int
int
int
a = 3 ;
b = 10 ;
c = 4 ;
r1 = b+c %a ;
r2 = (b+c)%a ;
r3 = a*b+c ;
r4 = a*b%c ;
//
//
//
//
r1
r2
r3
r4
=
=
=
=
11
2
34
2 (30 modulo 4)
...
361
Toute la dicult vient du fait que ces deux oprateurs peuvent se placer soit avant soit aprs
la variable, ce qui change lordre dvaluation des expressions. Par exemple :
Exemple 10-17 :
...
int
int
int
int
i
j
k
l
=
=
=
=
3 ;
i++ ; // j vaut 3 et i vaut 4
3 ;
++k ; // l vaut 4 et i vaut 4
...
Lorsque lon utilise ces oprateurs avec des types entiers, il se peut que lon provoque un dpassement de capacit. Comme pour tout dpassement de capacit sur des variables de type entier
une exception nest pas lance...
Exemple 10-18 :
...
byte i = 255 ;
i++ ; // i vaut 0
...
... moins que lon utilise le mot-cl checked :
Exemple 10-19 :
...
...
byte i = 255 ;
checked{ i++ ; } // Une exception de type
// System.OverflowException est lanc
ee.
Type
retourn
int
int
int
uint
long
362
int
long
ulong
int
ERREUR de
compilation
long
long
long
ulong
ulong
ulong
ulong
long
ERREUR de
compilation
float
float
double
double
decimal
decimal
float double
decimal
ERREUR de
compilation
double
float
double
float
float
float
double
double
double
decimal
decimal
decimal
De plus les transtypages (cast en anglais) sont autoriss de nimporte quel type entier ou rel vers
nimporte quel type entier ou rel. Bien entendu ceci reste dangereux puisque les intervalles de
valeurs de tous ces types sont tous dirents. Le dveloppeur peut toujours utiliser le mot-cl
checked. Ainsi si une valeur doit tre convertie dans un type o elle nest pas reprsente, une
exception de type System.OverflowException sera lance.
Exemple 10-20 :
...
int i = -5 ;
checked {
byte b = (byte)i ; // Exception lanc
ee. Le type byte ne
}
// repr
esente pas de nombres n
egatifs.
...
363
Notez cependant, quil est possible darrondir un type rel en un type entier. Par exemple, on
peut convertir le double 3.1415 en un byte de 3. Cest la valeur entire se trouvant avant la
virgule lors de lcriture du rel qui est alors choisie.
Lorsque plusieurs oprateurs sont utiliss dans une expression, ils sont excuts selon leur ordre
de priorit (dcrit un peu plus haut). Si les types primitifs des variables sont dirents, les rsultats intermdiaires suivent les rgles nonces.
Aussi nomme
Oprateur
AND
Et logique
&
OR
Ou logique
XOR
Ou exclusif logique
NOT
Seul loprateur agit sur un seul oprande, contrairement aux autres qui agissent sur deux
oprandes. Rappelons ces oprations :
Bit A
Bit B
A AND B
A OR B
A XOR B
Des oprations de dcalages peuvent tre eectues sur des variables de type int, long, uint et
ulong grce aux oprateurs :
Rappelons quun dcalage gauche dune position en notation dcimale provoque une multiplication par 10. De mme, un dcalage gauche dune position en notation binaire provoque
364
une multiplication par deux. Un dcalage droite dune position en notation binaire provoque
une division entire par deux.
Exemple 10-21 :
...
uint a = 11 ;
uint b = a << 2 ; // a est inchang
e, b vaut 44
uint c = a >> 2 ; // a est inchang
e, c vaut 2
...
Prcisons que :
Si le dcalage se fait sur une variable dun type sign (i.e int ou long) le dcalage est arithmtique, cest--dire que le bit de signe est inchang et nest pas dcal.
Si le dcalage se fait sur une variable dun type non sign (i.e uint ou ulong) le dcalage est
binaire, et le rsultat reste positif.
Les structures
C++ C En C++, les structures et les classes sont des notions similaires. En C la dirence
de base est que les structures dfinissent des types valeur et les classes dfinissent des types rfrence.
En C toutes les structures drivent de la classe Object.
Contrairement au C++, les champs dune structure sont privs par dfaut. De plus tous les
champs que lon veut dclarer publics doivent tre prcds du mot-cl public.
Contrairement au C++, les structures ne peuvent driver daucune classe ou structure, et ne
peuvent servir de base pour aucune classe ou structure drive.
En C , une structure peut avoir un ou plusieurs constructeurs, mais le dveloppeur na pas le
droit de dfinir un constructeur par dfaut (i.e sans argument). De plus la syntaxe dinitialisation des champs dans le constructeur ( : nom_dechamp(valeur)...) nexiste plus. Enfin le constructeur par dfaut initialise 0 les champs.
Une structure peut avoir des mthodes publiques et prives, mais les mthodes doivent tre
compltement dfinies lintrieur de la dclaration de la structure (idem pour les classes). Il
ny a donc plus doprateur de rsolution de porte :: .
En C , la possibilit de mettre des champs de bits dans une structure nexiste plus.
En C , il nest pas obligatoire de mettre un ; la fin de la dclaration dune structure (idem
pour les classes).
Enfin, mais ceci est une dirence gnrale, en C , au sein dun mme espace de nom, il nest
pas ncessaire davoir dj dfini une structure pour lutiliser.
C En C , une structure est dclare avec le mot-cl struct. La notion de structure est assez
proche de celle dune classe. Les points communs sont :
Une structure peut avoir des champs, des proprits, des mthodes, des dlgus, des vnements et des types. Si lun de ces membres nest pas dclar avec le mot-cl public, il nest
pas accessible hors de la classe.
Les structures
...
struct Employee{
int salaire ;
public string nom ;
}
...
365
Les membres dune structure sont accessibles hors de la dfinition dune structure avec
loprateur point . .
...
Employee raymond ;
raymond.nom = "Raymond" ;
raymond.salaire = 3000 ;
...
lintrieur dune autre structure. Elle peut cependant tre instancie hors de la structure encapsulante, si son niveau de visibilit le permet.
lintrieur dune classe. Elle peut cependant tre instancie hors de la classe encapsulante, si son niveau de visibilit le permet.
lextrieur de toute classe et de toute structure. On peut donc instancier cette structure partout dans lespace de noms courant, et dans les zones du code utilisant cet espace de nom.
Une structure peut avoir un ou plusieurs constructeurs mais le dveloppeur na pas le droit
de dfinir un constructeur par dfaut (i.e sans arguments). De plus le compilateur sattend
ce que tous les champs de la structure soient initialiss dans tous les constructeurs.
...
struct Employee {
int salaire ;
public string nom ;
Employee (int _salaire,string _nom){
salaire = _salaire ;
nom = _nom ;
}
}
...
Le constructeur par dfaut initialise 0 les champs de type valeur et nul les champs de
type rfrence.
Une structure peut avoir des mthodes autres que les constructeurs :
...
struct Employee {
int salaire ;
366
Une structure est un type valeur alors quune classe est un type rfrence, avec toutes les
dirences que cela induit.
Les structures ne peuvent driver daucune classe ou structure, et ne peuvent servir de base
pour aucune classe ou structure drive, bien que toutes les structures drivent implicitement de la classe Object.
Contrairement aux champs dune classe, les champs dune structure ne peuvent tre explicitement initialiss dans la dclaration mme du champ.
Les instances des structures tant souvent stockes sur la pile, il vaut mieux quelles ne soient
pas trop volumineuses. Dans le cas de trs grosses structures, il vaut mieux opter pour des
classes.
Les numrations
C++ C Les numrations du C restent assez proches de celles du C++. Cependant
quelques dirences sont remarquer.
La classe System.Enum prsente des fonctions daide la manipulation des numrations.
Les numrations en C sont idales pour remplacer le mcanisme de drapeaux binaires en C++.
Rappelons quen C++ le mcanisme de drapeaux binaires utilise les macros. Une telle approche
empche le bnfice de la vrification du type par le compilateur.
C Un type numration dfinit un ensemble de valeurs. En C , un tel type est dfini avec
le mot-cl enum. Les variables dun type numration prennent leurs valeurs dans cet ensemble.
Par exemple :
Exemple 10-22 :
class Program {
enum Marque { Renault, Peugeot, Lancia }
static void Main() {
Marque marque = Marque.Renault ;
switch (marque){
case Marque.Renault: System.Console.WriteLine("Renault") ; break ;
case Marque.Peugeot: System.Console.WriteLine("Peugeot") ; break ;
case Marque.Lancia: System.Console.WriteLine("Lancia") ; break ;
}
}
}
Une numration peut tre dfinie :
Les numrations
367
lintrieur dune structure ou dune classe. Elle peut cependant tre instancie hors de la
structure ou de la classe encapsulante, si son niveau de visibilit le permet.
lextrieur de toute classe et de toute structure. On peut donc instancier cette numration partout dans lespace de noms courant, et dans les zones du code utilisant cet espace
de noms.
Associer nos propres valeurs entires dans lensemble des valeurs de lnumration :
...
enum Marque{ Renault = 100 , Peugeot= Renault+1 , Lancia = 200 }
...
Par dfaut la premire valeur est 0, puis il y a un incrment de 1 pour les suivantes. Il vaut
mieux dfinir la valeur 0 comme la plus courante, car les constructeurs initialisent 0 par
dfaut les champs de type numration.
Choisir nimporte quel autre type entier que int pour dfinir les valeurs du type numration :
...
enum MarqueFR : byte { Renault , Peugeot } ;
enum MarqueIT : long { Fiat , Ferrari } ;
...
Toute numration drive de la classe Object. Par consquent les mthodes de cette classe sont
accessibles sur une numration. Notons que la mthode Object.ToString() est redfinie pour
chaque numration, de faon que la chane de caractres retourne corresponde la chane de
caractres dsignant ltat courant dans le code. Par exemple :
368
Exemple 10-23 :
class Program {
enum Marque { Renault, Peugeot, Lancia }
static void Main() {
Marque marque = Marque.Renault ;
string s = marque.ToString();
System.Console.WriteLine(s) ;
}
}
Ce programme ache :
Renault
La classe System.Enum
La classe System.Enum drive de la classe System.ValueType. Elle prsente des mthodes statiques daide la manipulation dun type numration. Parmi ces mthodes remarquons :
Les numrations
369
Exemple 10-26 :
...
foreach( string s in System.Enum.GetNames( typeof(Marque) ) )
System.Console.WriteLine(s) ;
...
Indicateurs binaires
On peut faire en sorte quune instance dune numration puisse contenir plusieurs valeurs
de lensemble des valeurs de lnumration. Cette notion est connue sous le nom dindicateurs
binaires. Les indicateurs binaires sont notamment utiliss pour manipuler les drapeaux (flag en
anglais) pour stocker plusieurs informations binaires dans une mme variable. Par exemple :
Exemple 10-28 :
// Dans cet exemple, pas besoin de plus quun octet.
[System.Flags()]
enum Drapeaux : byte {
Drapeau1 = 0x01, // Le bit 1 est `
a 1, les autres `
a 0.
Drapeau2 = 0x04, // Le bit 3 est `
a 1, les autres `
a 0.
Drapeau3 = 0x10 // Le bit 5 est `
a 1, les autres `
a 0.
}
class Program {
public static void Main() {
// En binaire, Drap vaut 10001000.
Drapeaux drapeau = Drapeaux.Drapeau1 | Drapeaux.Drapeau3 ;
// (Si le bit 1 est positionn
e)
equivalent `
a
// (Si Drapeau1 positionne).
if ((drapeau & Drapeaux.Drapeau1) > 0) {/* */}
// (Si les bits 3 et 5 sont positionn
es)
equivalent `
a
// (Si Drapeau2 et Drapeau3 positionn
es).
if ( (drapeau & Drapeaux.Drapeau2) > 0 &&
(drapeau & Drapeaux.Drapeau3) > 0 )
{ /* */ }
System.Console.WriteLine( drapeau.ToString() ) ;
}
}
370
Remarquez que les indicateurs binaires doivent tre marqus avec lattribut System.Flags. Cet
attribut permet de prciser au CLR et aux clients quil sagit dun type dindicateur binaire et
non pas dune numration classique. Notamment, la prsence de cet attribut aecte le rsultat
de la mthode ToString() et cet exemple ache ceci :
Drapeau1, Drapeau3
Il acherait ceci si lattribut Flags tait absent :
17
Un exemple dindicateur binaire prsent par le framework .NET est lnumration System.
Threading.ThreadState dcrite page 143.
371
Les chanes de caractres constantes rgulires, similaires aux chanes de caractres constantes
de C++.
C
s = "ABCDEFG" ;
On peut introduire des caractres spciaux dans une constante chane de caractres. Comprenez
bien que ces caractres sont interprts par le compilateur C . En voici la liste :
Terme franais
Terme anglais
Reprsentation
Valeur
Nouvelle ligne
New line
\n
0x000A
Tabulation horizontale
Horizontal tab
\t
0x0009
Tabulation verticale
Vertical tab
\v
0x000B
Retour arrire
Backspace
\b
0x0008
Retour chariot
Carriage return
\r
0x000D
Saut de page
Form feed
\f
0x000C
Antislash
Backslash
\\
0x005C
Apostrophe simple
Single quote
0x0027
Apostrophe doubles
Double quote
\"
0x0022
Caractre nul
Null
\0
0x0000
372
Elle accepte tous les caractres tels quils sont, y compris le caractre antislash \ mais mis
part le caractre double apostrophes " . En eet, ce dernier dfinit la fin de la chane.
Elle accepte et prend en compte le passage la ligne, dans lcriture de la chane de caractres. Cette fonctionnalit est particulirement utile lorsque lon gnre du code.
Voici un exemple :
Exemple 10-30 :
class Program {
public static void Main() {
string sReguliere ="\\bonjour\n\\comment ca va\n\\ce matin ?" ;
string sVerbatim = @"\bonjour
\comment ca va
\ce matin ?" ;
System.Console.WriteLine("Chane de caract`
eres r
eguli`
ere :") ;
System.Console.WriteLine(sReguliere) ;
System.Console.WriteLine("Chane de caract`
eres verbatim :") ;
System.Console.WriteLine(sVerbatim) ;
}
}
Voici lachage obtenu :
Chane de caract`eres reguli`ere :
\bonjour
\comment ca va
\ce matin ?
Chane de caract`eres verbatim :
\bonjour
\comment ca va
\ce matin ?
373
Description
int Length()
int CompareTo(string s)
Semblable Compare() mais cette mthode est non statique. Retourne Poids(de linstance)-Poids(de largument).
bool EndWith(string s)
Retourne true si la chane reprsente par linstance courante se termine par la chane s.
Retourne une nouvelle chane qui est la chane reprsente par linstance courante dans laquelle on a insr la
chane s partir du pos-ime caractre.
Retourne une nouvelle chane qui est la chane reprsente par linstance courante dans laquelle on a insr n espaces au dbut.
374
Retourne une nouvelle chane qui est la chane reprsente par linstance courante dans laquelle on a enlev n caractres partir du pos-ime caractre.
string Replace(string
oldstr, string newstr)
Retourne une nouvelle chane qui est la chane reprsente par linstance courante sur laquelle on a remplac chacune des sous chanes gale oldstring par newstring.
Retourne une nouvelle chane qui est la chane reprsente par linstance courante dans laquelle on a remplac
chaque caractre oldchar par newchar.
Retourne un tableau de nouvelles chanes obtenues partir de la chane reprsente par linstance courante. Un tableau de caractres sparateurs est pass en argument.
Retourne true si la chane reprsente par linstance courante commence par la chane s.
string ToLower()
Retourne une nouvelle chane qui est la chane reprsente par linstance courante avec toutes les majuscules transformes en minuscules.
string ToUpper()
Retourne une nouvelle chane qui est la chane reprsente par linstance courante avec toutes les minuscules transformes en majuscules.
string Trim()
Retourne une nouvelle chane qui est la chane reprsente par linstance courante avec les espaces, en dbut et
en fin de chane, supprims. TrimStart() (respectivement
TrimEnd()) est identique mais ne traite que le dbut (respectivement la fin).
Retourne une nouvelle chane qui est la chane reprsente par linstance courante, avec les caractres spcifis dans le tableau en argument en dbut et en fin
de chane supprime. TrimStart(char[]) (respectivement
TrimEnd(char[])) est identique mais ne traite que le dbut
(respectivement la fin).
375
En eet cette mthode est trs utilise et prsente un trs grand nombre doptions. Le tableau
ci-dessous illustre plusieurs exemples de mise en forme avec les variables suivantes :
int i = 123 ;
double d = 3.1415 ;
string sOut = System.String.Format( Signature de Format() ) ;
Signature de
Format()
Valeur de sOut
Remarque
"abcd"
"abcd"
"ab{0}cd",i
"ab123cd"
"ab{0}cd",d
"ab3,1415cd"
"ab{0}cd",i,d
"ab123cd"
Largument d nest pas utilis, mais aucune erreur nest dclare ni provoque.
"ab{0}cd{1}ef",i,d
"ab123cd3.1415ef"
"ab{1}cd{0}ef",i,d
"ab3.1415cd123ef"
"ab{0}{0}cd ",i
"ab123123cd"
"ab{0}cd{1}ef",i
ERREUR `
a lex
ecution
"ab{0,6}cd",i
"ab 123cd"
i est reprsent par six caractres avec cadrage droite. Trois caractres despace sont
ajouts. Si le nombre de caractres tait infrieur au nombre de caractres requis pour
acher i, i aurait t ach dans sa totalit
quand mme.
"ab{0,-6}cd",i
"ab123 cd"
"ab{0:0000}cd",i
"ab0123cd"
376
"ab{0,6:0000}cd",i
"ab 0123cd"
"ab{0:####}cd",i
"ab123cd"
"ab{0:##}cd",i
"ab123cd"
"ab{0:##.##}cd",d
"ab3,14cd"
"ab{0:##.#}cd",3.14
"ab3,1cd"
Arrondi infrieur.
"ab{0:##.#}cd",3.18
"ab3,2cd"
Arrondi suprieur.
"ab{0:##%}cd",0.143
"ab14%cd"
0.143 est multipli par 10 puis arrondi infrieur. 0.147 aurait produit 15%.
"ab{0:E}cd",d
"ab3,141500E+000cd"
Reprsentation scientifique.
"ab{0:X}cd",i
"ab7Bcd"
"ab{0:x}cd",I
"ab7bcd"
La classe System.Text.StringBuilder
Comme nous lavons vu au dbut de la prsente section, les instances de la classe String sont
immuables. Ce fait nuit aux performances, car chaque modification dune chane de caractres
il faut allouer une nouvelle instance de String. Ceci est dautant plus coteux si vous manipulez
des chanes de caractres longues.
Si une de vos applications modifie souvent des chanes de caractres ou manipule des chanes
de caractres volumineuses, il est plus ecace dutiliser la classe System.Text.StringBuilder.
En gnral une instance de StringBuilder se construit partir dune instance de la classe String
avec le constructeur StringBuilder(string).
Pour bien comprendre la classe StringBuilder, il faut assimiler la notion de capacit dune instance de StringBuilder. Pour cela nous prsentons les proprits suivantes de StringBuilder
(qui sont toutes publiques et non statiques) :
int Length{get;}
Cette proprit donne le nombre de caractres de la chane.
int Capacity{get;set;}
Cette proprit reprsente le nombre de caractres allous physiquement pour stocker la
chane de caractres. Ce nombre est la capacit. La valeur de ce champ est toujours suprieure ou gale la valeur du champ Length. Il est particulirement utile de positionner
ce champ une valeur suprieure la taille relle de la chane. Vous pouvez ainsi prvenir
des oprations dallocation mmoire du ramasse-miettes (parfois coteuses), en vous crant
une marge pour manipuler la chane.
377
Notez que la valeur de ce champ peut tre automatiquement incrmente lorsquune manipulation sur la chane de caractres produit un rsultat dune taille plus grande que la capacit. La valeur de cette incrmentation dpend de limplmentation du framework. Dans
cette situation, limplmentation courante de Microsoft double la capacit.
Si vous positionnez ce champ une valeur infrieure la valeur du champ Length lexception ArgumentOutOfRangeException est lance.
int MaxCapacity{get ;}
Cette proprit donne la capacit maximale que peut avoir la chane de caractres. Dans
limplmentation courante de Microsoft ce champ vaut Int32.MaxValue soit 2.147.483.647
caractres.
StringBuilder Append(...)
Ajoute des caractres la fin de la chane de caractres. Cette mthode existe en de nombreuses versions surcharges.
StringBuilder Replace(...old,...new,...)
Remplace le ou les caractres spcifis par old, par le ou les caractres spcifis par new. La
recherche de old peut ventuellement se faire sur une sous chane de caractres en utilisant
dautres versions surcharges de cette mthode.
Voici un petit programme qui illustre tout ceci (lachage de chaque appel Program.Display()
est insr en commentaire en blanc sur fond noir) :
Exemple 10-31 :
class Program{
static void Display(System.Text.StringBuilder s) {
System.Console.WriteLine("La chane : \"{0}\"",s) ;
System.Console.WriteLine(" Length
: {0}", s.Length) ;
System.Console.WriteLine(" Capacity : {0}", s.Capacity) ;
}
public static void Main(){
System.Text.StringBuilder s = new
378
"--salut--" ) ;
"hell--salut--o"
14
16
s.Capacity = 18 ;
Display(s) ;
//La chane : "hell--salut--o"
// Length
: 14
// Capacity : 18
s.Replace("salut","SALUT `
A TOUS") ;
Display(s) ;
//La chane : "hell--SALUT `
A TOUS--o"
// Length
: 21
// Capacity : 36
s.EnsureCapacity(42) ;
Display(s) ;
//La chane : "hell--SALUT `
A TOUS--o"
// Length
: 21
// Capacity : 72
}
}
Avant davoir recours aux services de la classe StringBuilder, vous devez tre conscient quil
est possible de violer la condition dimmutabilit des instances de la classe String si vous vous
autorisez utiliser du code non vrifiable. Cette possibilit est expose en page 508.
Cependant cette notion est conceptuellement proche de celle des pointeurs sur fonction et des
pointeurs sur mthode du C++. Au mme titre que ces derniers, un dlgu est une rfrence
vers une ou plusieurs mthodes (statiques ou non).
Les dlgus sont nanmoins plus puissants que les pointeurs sur fonctions, puisquun mme
dlgu peut rfrencer plusieurs mthodes. En outre la syntaxe est beaucoup plus claire et
conviviale.
379
Certains ouvrages utilisent les termes classe dlgue ou type dlgu pour nommer
les dlgations. Dautres publications utilisent le terme dlgu pour nommer les dlgations. Dans ce cas il faut utiliser les termes instance de dlgu ou objet dlgu
pour nommer ce que nous appelons ici dlgu . Soyez vigilants, et reprez chaque
fois la dirence entre les types et les instances.
380
Ce programme ache :
Appel de f1.
Appel de f2 avec largument "hello"
Nous utilisons deux dlgations, pour bien souligner le fait quun dlgu ne peut rfrencer
seulement une mthode dont la signature (mme ordre, nombre et type des arguments et mme
type de retour) correspond celle fournie lors de la dclaration de la dlgation. Par exemple
la ligne suivante aurait provoqu une erreur de compilation car la mthode, f1() na pas la
signature adquate :
Deleg2 d2
= new Deleg2(f1) ;
381
382
La classe System.Delegate
Il faut savoir quen interne, lorsque vous placez plusieurs mthodes dans le mme dlgu,
une instance de la classe System.Deletage est cre pour chaque mthode. En fait, la classe
MulticastDelegate est une liste dinstances de la classe System.Delegate. Lexemple suivant
montre comment utiliser la classe Delegate pour invoquer une une les mthodes contenues
dans un dlgu. Notez le recours la mthode GetInvocationList() pour obtenir la liste des
dlgus.
383
Exemple 10-36 :
using System ;
public class Article {
public int m_Prix = 0 ;
public Article(int Prix) { m_Prix = Prix ; }
public int IncPrix(int i) {
m_Prix += i ;
return m_Prix ;
}
}
public class Program {
public delegate int Deleg(int i) ;
public static void Main() {
Article a = new Article(100) ;
Article b = new Article(103) ;
Article c = new Article(107) ;
Deleg delegs = a.IncPrix ;
delegs += b.IncPrix ;
delegs += c.IncPrix ;
int somme = 0 ;
// Obtient la liste des del
egu
es.
Delegate[] delegArr = delegs.GetInvocationList();
// Invoque une `a une chaque m
ethode r
ef
erenc
ee.
foreach (Deleg deleg in delegArr)
somme += deleg(20);
Console.WriteLine(
"Prix apr`es increment de 20 : a:{0} b:{1} c:{2}",
a.m_Prix , b.m_Prix , c.m_Prix) ;
Console.WriteLine("Somme des prix:{0}", somme ) ;
}
}
Cet exemple ache :
Prix apr`es increment de 20 : a:120 b:123 c:127
Somme des prix:370
384
385
Dans le premier essai de suppression, le dlgu deleg nest pas modifi. Pour infirmer ou
confirmer le bon droulement dune suppression, vous pouvez inspecter la taille de la liste des
instances de la classe Delegate obtenue avec la mthode GetInvocationList().
Une mthode qui doit retourner une rfrence vers un objet retourne une rfrence nulle
pour signifier que lobjet demand ne peut tre fabriqu ou trouv. Cela vite dimplmenter un code derreur de retour binaire.
Lorsque vous rencontrez une mthode qui accepte un argument de type rfrence qui peut
tre nulle, cela signifie en gnral que largument est optionnel.
Un champ de type rfrence nulle peut signifier que lobjet qui le prsente est en cours
dinitialisation, de mise jour ou de destruction et na donc pas un tat valide.
La notion de nullit est aussi largement exploite dans les bases de donnes relationnelles pour
signifier quune valeur dans un enregistrement na pas t assigne. La notion de nullit peut aussi
servir pour dsigner un attribut optionnel dans un lment XML.
Parmi les nombreuses dirences entre les types valeurs et les types rfrences, nous pouvons
nous pencher sur le fait quune instance dun type valeur ne peut avoir de valeur nulle. Cela
pose en gnral de nombreux problmes. Par exemple, comment interprter une valeur entire
nulle (non encore assigne) rcupre dune base de donne ? De multiples solutions existent
mais aucune dentre elles nest pleinement satisfaisante :
Si tout lintervalle de valeurs entires nest pas utilisable, on cre une convention. Par
exemple, une valeur entire nulle est reprsente par un entier gal 0 ou -1. Les nombreux
dsavantages de cette solution sont vidents : contrainte maintenir partout dans le code,
possibilit dvolution de lintervalle de valeurs pris par ce champ particulier etc.
On cre une structure wrapper contenant deux champs, un entier et un boolen qui, si
positionn false, signifie que nous avons une valeur nulle. Ici, on doit grer une structure
en plus pour chaque type valeur, et un tat en plus pour chaque valeur.
On cre une classe wrapper contenant un champ entier. Ici, le dsavantage est quen plus du
maintient dune nouvelle classe, on surcharge le ramasse-miettes en crant de nombreux
objet sur le tas.
On utilise le boxing, par exemple en transtypant notre valeur entire en une rfrence de
type object. En plus de ne pas tre type-safe, cette solution a aussi le dsavantage de surcharger le ramasse-miettes.
386
Pour pallier ce problme rcurrent, les concepteurs de C 2 ont dcid dajouter au langage la
notion de types nullables.
La structure System.Nullable<T>
Le framework .NET 2005 prsente la structure gnrique System.Nullable<T> dfinie comme
ceci :
public struct System.Nullable<T> {
public Nullable(T value) ;
public static explicit operator T(T? value) ;
public static implicit operator T?(T value) ;
public bool HasValue { get ; }
public T Value { get ; }
public
public
public
public
public
}
Cette structure rpond bien la problmatique des valeurs nulles lorsque le type paramtre T
prend la forme dun type valeur tel que int. Voici un petit exemple qui illustre lutilisation de
cette structure. La premire version de Fct() exploite la nullit dune rfrence de type string
tandis que la seconde version exploite la nullit dune instance de la structure Nullable<int> :
Exemple 10-38 :
class Foo {
static string Fct(string s) {
if (s == null)
return null ;
return s + s ;
}
static System.Nullable<int> Fct(System.Nullable<int> ni){
if (!ni.HasValue)
return ni ;
return (System.Nullable<int>) (ni.Value + ni.Value) ;
}
}
387
388
...est quivalente :
Nullable<int> i = null ;
Par exemple, les deux mthodes suivantes sont rigoureusement quivalentes :
Exemple 10-41 :
class Foo {
static System.Nullable<int> Fct1( System.Nullable<int> ni ) {
if ( !ni.HasValue )
return ni ;
return (System.Nullable<int>) ( ni.Value + ni.Value ) ;
}
static int? Fct2(int? ni){
if (ni == null)
return ni;
return ni + ni ;
}
}
En gnral, les instances de types nullables quivalentes se mlangent bien :
Exemple 10-42 :
class Program{
static void
int? ni1
int? ni2
int? ni3
int? ni4
int? ni5
ni1++ ;
ni2++ ;
}
}
Main(){
= null ;
= 9 ;
= ni1 + ni2 ; // OK,
= ni1 + 3 ; // OK,
= ni2 + 3 ; // OK,
// OK,
// OK,
ni3
ni4
ni5
ni1
ni2
vaut null.
vaut null.
vaut 12.
reste `
a null.
passe `
a 1.
Main(){
= null ;
= 9 ;
ni1 ;
int i2 = ni2 ;
389
//
type int? to int.
// KO: Cannot implicitly convert
//
type int? to int.
int i4 = ni1 + 6 ;
// KO: Cannot implicitly convert
//
type int? to int.
// OK `a la compilation mais une exception de type
// InvalidCastException est lanc
ee `
a lex
ecution,
// car ni1 est toujours nulle.
int i5 = (int)ni1 ;
int i3 = ni1 + ni2 ;
}
}
// os est une r
ef
erence nulle
// oi n
etait pas une r
ef
erence nulle
Sous la pression de la communaut, les ingnieurs de Microsoft ont dcid de remdier ceci.
Ainsi, lassertion du programme suivant est vrifie :
Exemple 10-45 :
class Program{
static void Main(){
int? ni = null ;
object o = ni ;
// boxing
System.Diagnostics.Debug.Assert( o == null ) ;
390
Du point de vue du CLR, une instance dun type valeur T boxe peut tre nulle. Si elle nest pas
nulle, le CLR ne stocke pas dinformation quant savoir si elle tait originalement issue du type
T ou Nullable<T>. Vous pouvez ainsi unboxer un tel objet dans lun de ces deux types comme
le montre lexemple suivant. Soyez nanmoins conscient que vous ne pouvez pas unboxer une
valeur nulle :
Exemple 10-46 :
class Program {
static void Main() {
int i1 = 76 ;
object o1 = i1 ;
// boxing dun int
int? ni1 = (int?)o1 ; // unboxing en un int?
System.Diagnostics.Debug.Assert( ni1 == 76 ) ;
int? ni2 = 98 ;
object o2 = ni2 ;
// boxing dun int?
int i2 = (int)o2 ;
// unboxing en un int
System.Diagnostics.Debug.Assert( i2 == 98 ) ;
int? ni3 = null ;
object o3 = ni3 ;
int i3 = (int)o3 ;
}
}
391
// OK Struct.ctor() par d
efaut
// est appel
e.
KO: System.Nullable<Struct> does not
contain a definition for m_i.
KO: System.Nullable<Struct> does not
contain a definition for Fct.
}
}
En revanche, si besoin est, le compilateur saura utiliser vos redfinitions doprateurs :
Exemple 10-48 :
struct Struct {
public Struct(int i) { m_i = i ; }
public int m_i ;
public static Struct operator +( Struct a, Struct b) {
return new Struct( a.m_i + b.m_i ) ; }
}
class Program {
static void Main() {
Struct? ns1 = new Struct(3) ;
Struct? ns2 = new Struct(2) ;
Struct? ns3 = null ;
Struct? ns4 = ns1 + ns2 ; // OK, ns4.m_i vaut 5.
Struct? ns5 = ns1 + ns3 ; // OK, ns5 vaut null.
}
}
En ce qui concerne une instance dune numration nullable, ayez conscience que vous devez
toujours obtenir la valeur sous-jacente pour lutiliser. Par exemple :
Exemple 10-49 :
class Program{
enum MyEnum { VAL1, VAL2 }
static void Main(){
MyEnum? e = null;
if( e == null )
System.Console.WriteLine("e est null") ;
else
switch(e.Value){ // Ici on est sur que e nest pas null.
case MyEnum.VAL1: System.Console.WriteLine("e vaut VAL1") ;
break ;
case MyEnum.VAL2: System.Console.WriteLine("e vaut VAL2") ;
break ;
}
}
}
392
393
Les mots-cls abstract et sealed. Prcisons quun mme type ne peut tre la fois abstract
et sealed .
Les membres. Lensemble des membres dun type dfini sur plusieurs fichiers sources est
lunion des membres de chaque dfinition partielle.
Les attributs. Lensemble des attributs sappliquant sur un type dfini sur plusieurs fichiers
sources est lunion des attributs appliqus sur chaque dfinition partielle. En particulier, le
type concern sera marqu plusieurs fois par un mme attribut si celui-ci marque plusieurs
dfinitions partielles.
Les interfaces implmentes. Lensemble des interfaces implmentes par un type dfini sur
plusieurs fichiers sources est lunion des interfaces dclares dans chaque dfinition partielle.
11
Notions de classe et dobjet
Notions et vocabulaire
C++ C En C , en franais, le mot attribut est rserv pour dautres notions que celle de
champ dune classe. Cette notion dattribut est prsente page 248. De plus on verra que C
distingue la notion de champs et de proprits.
Enfin puisque la plateforme .NET dispose dun ramasse-miettes, un destructeur C est trs diffrent dun destructeur C++.
C Une classe est un type de donnes. Les notions de variable dun type et dinstance dune
classe sont similaires, dans le sens o les traitements sorganisent autour des donnes. Un objet
est une instance dune classe. La notion de classe dfinit un concept alors que la notion dobjet
396
dfinit linstance dun concept. La notion de classe est au concept de voiture ce que la notion
dobjet est une voiture particulire. Une voiture dans la rue est une instance du concept de
voiture.
Il est intressant de souligner un paradoxe de la programmation par objets. Les objets nexistent
qu lexcution et sont cres, grs et dtruits par lenvironnement dexcution. Les dveloppeurs ne font qucrire des classes qui lors de lexcution, entraneront la cration dobjets. Aussi,
certains prfreraient utiliser la terminologie programmation par classes.
Dans le contexte dun langage objet tel que C , le terme variable est utilis pour dsigner un
objet instance dun type valeur, allou dans le corps dune mthode et dont la dure de vie ne
dpasse pas la porte de cette mthode.
Une classe est constitue de plusieurs entits de direntes natures : les champs, les proprits,
les mthodes, les vnements, les indexeurs et les types encapsuls. On appelle ces entits les
membres de la classe. Nous allons avoir loccasion dans le prsent chapitre de dtailler chacune
de ces entits.
Chaque objet renferme une quantit dinformation sous forme de valeurs stockes dans ses
champs. Lensemble de ces valeurs est appel ltat de lobjet.
La notion de mthode se rapproche de celle de fonction, ceci prs quune mthode est appele
sur une instance de la classe. Une mthode agit sur lobjet sur lequel elle est appele. Elle peut
aussi bien lire ou crire les champs de lobjet, queectuer une action sur lobjet.
Un objet a une dure de vie qui est un sous intervalle de la dure dexcution du programme
qui lhberge. Un objet est ncessairement construit un moment donn et dtruit plus tard
un autre moment. Une mthode est automatiquement appele lors de la construction. Elle
est nomme constructeur. Elle permet par exemple dinitialiser lobjet ou dallouer des ressources
consommes par lobjet. Il peut y avoir plusieurs constructeurs pour une mme classe car il peut
y avoir plusieurs faons de construire un objet.
Une mthode est automatiquement appele lors de la destruction dun objet par le ramassemiettes de la plateforme .NET. La destruction des objets est un sujet sensible en .NET/C qui
doit absolument tre bien compris.
Les champs
397
Exemple 11-1 :
class Program{
// Ici sont places les membres de la classe Program.
static void Main() {
// Ici sont placees les instructions de la m
ethode Main().
}
// Ici aussi, sont places les membres de la classe Program.
}
Les membres dune classe sont des entits dclares dans la classe. Il y a six sortes de membres :
Les champs
Les proprits
Les indexeurs
Les mthodes
Les vnements
part quelques dtails que nous soulignerons, tout ce qui va tre dit ici propos des membres
dune classe est aussi valable pour les membres dune structure.
...le membre soit un champ une proprit une mthode un vnement ou un type.
...lobjet soit de type valeur (cas dune structure) ou de type rfrence (cas dune classe).
Les champs
C++ C
Tout dabord, les champs (statiques ou non) peuvent tre initialiss directement dans leurs dclarations au sein de la classe. La valeur dun champ non statique dun objet sera aecte avant
lappel au constructeur. La valeur dun champ statique sera aecte avant la cration de toutes
les instances de la classe.
En revanche, on perd la syntaxe C++ dinitialisation de champs directement aprs le prototype
du constructeur :
ctor(int champ) :m_Champ(champ) {}
398
Les spcifications du compilateur C sont moins permissives que celles du C++. Elles obligent
que tous les champs non statiques de type valeur soient initialiss avant la fin de lappel dun
constructeur de la classe. Les champs de type rfrence non initialiss sont automatiquement
positionns null.
Enfin un champ peut tre const, cest--dire initialis directement au sein de la classe ou
readonly, cest--dire initialis au sein de la classe ou dans les constructeurs. Dans les deux
cas le champ nest plus modifiable aprs son initialisation.
C La notion de champ dune classe est la mme que la notion de champ dune structure.
Lensemble des tats des champs non statiques dune instance dune classe reprsente ltat de
cette instance. Un champ peut tre de nimporte quel type (primitif, numration, structure,
classe, interface...).
Lorsquun objet A a pour champ un autre objet B, on dit quil y a une agrgation de A sur B si B
est de type valeur. Si B est de type rfrence, on dit que A a une rfrence sur B. Cette distinction
est importante pour une approche de type UML ou OMT.
Directement lors de leur dclaration au sein de la classe (Champs1 et Champs2 dans lexemple).
Exemple 11-2 :
class Foo {
int Champ1 = 4 ;
int Champ2 = 3 ;
int Champ3 ;
public Foo() { // Un constructeur public de la clase Foo.
Champ1 = 7 ;
Champ3 = 6 ;
}
}
On voit bien avec Champ1 que les deux faons ne sont pas incompatibles, cependant linitialisation de Champ1 dans la classe est inutile ici, puisque cest son initialisation dans le constructeur
qui sera excute en dernier.
Champs constants
On peut rendre un champ constant, cest--dire quil prend sa valeur linitialisation (au sein
de la classe ou dans le constructeur) puis ne peut plus tre modifi. C propose deux faons de
rendre un champ constant :
Les champs
399
400
Les mthodes
C++ C De nombreuses dirences existent entre C et C++ en ce qui concerne les mthodes. Notez que la possibilit davoir des mthodes constantes (i.e qui ne changent pas les
champs de lobjet) du C++, nexiste pas en C .
En C++ le passage dargument une mthode ou une fonction peut se faire soit par pointeur,
soit par valeur soit par rfrence.
C permet pour toute mthode, le passage dargument par valeur et par rfrence. Sous certaines conditions C permet aussi le passage par pointeur.
Par dfaut, C fait passer les arguments de type valeur par valeur, et les arguments de type rfrence par rfrence.
La syntaxe du passage par rfrence des arguments de type valeur a chang.
C introduit la possibilit quun argument ne soit que out, i.e on ne sintresse qu sa valeur
de sortie.
Enfin les arguments par dfaut du C++ ont disparu. Ils sont remplacs par un concept beaucoup
plus puissant, proche du passage dargument en nombre indtermin du C++ (utilis notamment par la fonction printf).
C Une mthode dinstance dune classe est une fonction qui porte sur les objets de la classe.
Une mthode peut manipuler les champs et proprits de la classe afin deectuer un calcul ou
afin de modifier ltat de lobjet.
Exemple 11-5 :
public class Article {
public decimal PrixHT = 10 ;
public decimal TVA = 19.6M ;
public decimal GetPrixTTC() { // Une m
ethode de la classe Article.
return PrixHT + PrixHT * (TVA / 100);
}
}
Les mthodes font partie de la classe au mme titre que les champs et les proprits. Les mthodes jouent un rle essentiel en C et rassemblent une grande partie des points matriser
de ce langage.
Pour une bonne comprhension de ce qui suit, il est prfrable de bien avoir assimil les notions
de type valeur et de type rfrence, dcrites page 339.
Les mthodes
401
Une variable (un objet) de type valeur est passe par valeur une mthode.
Par exemple :
Exemple 11-6 :
public class Article { public int Prix = 0 ; }
class Program {
static void Main() {
int i = 10 ; // int est un type valeur.
Article article = new Article() ; // Article est un type r
ef
erence.
article.Prix = 10 ;
fct(i, article);
// Ici i vaut 10 et article.Prix vaut 100.
}
static void fct(int i, Article article) {
// Lentier i nest pas le m
eme que lentier i de Main().
// Linstance de la classe Article pass
ee est bien la m
eme que
// linstance de la classe Article de la m
ethode Main.
i = 100;
article.Prix = 100;
}
}
402
C C ore la possibilit de forcer le passage par rfrence dun argument. Cette technique
est utilisable pour les types valeur et rfrence. Cette technique permet de passer par rfrence
des arguments de type valeur. Nous allons voir juste aprs que cette technique impacte aussi le
passage des objets de type rfrence. Cette technique utilise le mot-cl ref, la fois lors de la
dclaration de la mthode, et lors de lappel, par exemple :
Exemple 11-7 :
class Program {
static void Main() {
int i = 10 ; // int est un type valeur.
fct(ref i);
// Ici i vaut 100.
}
static void fct( ref int i ) {
// Lentier i est le meme que lentier i de Main.
i = 100 ;
}
}
Les mthodes
403
linstar de cet exemple, on utilise souvent cette technique pour dlguer lallocation dun ou
plusieurs objets, dans une mthode. Dans ce cas, il est prfrable dutiliser la technique dargument out, expose un peu plus loin.
404
Exemple 11-10 :
public class Article { public int Prix = 0 ; }
class Program {
static void Main() {
int i ; // i nest pas initialis
e.
Article articleA ; // articleA nest pas initialis
e.
Article articleB = new Article() ; // articleB r
ef
erence un objet.
articleB.Prix = 100 ;
fct( out i, out articleA, out articleB ) ;
// Ici i vaut 10. articleA.Prix vaut 10 et articleB r
ef
erence
// lobjet cree dans fct(). articleB.Prix vaut 10 mais articleB
// ne reference plus le m
eme objet quavant lappel de fct().
}
static void fct(out int i, out Article a, out Article b) {
i = 10 ;
a = new Article() ;
b = new Article() ;
a.Prix = i ;
b.Prix = i ;
}
}
Les mthodes
405
406
Cet exemple utilise loprateur is expliqu dans le prochain chapitre. Comme vous le voyez,
cet oprateur permet de tester le type dun objet. De plus nous spcifions que les arguments
correspondant largument qui utilise le mot-cl params, doivent tre de type object. Cest grce
cette astuce que nous pouvons faire varier le type des arguments, car tous les types hritent du
type object.
Surcharge de mthodes
C++ C
C C permet plusieurs mthodes dune mme classe davoir le mme nom. Cette possibilit est appele surcharge (overloading en anglais) du nom dune mthode. Dans ce cas, les
listes des arguments de ces mthodes doivent tre direntes deux deux. En eet, dans le cas
contraire il y aurait ambigut lors de lappel. Largument de retour nest pas pris en compte
par le compilateur pour cette direnciation. Cest--dire que ce dernier gnre une erreur si
deux mthodes ont le mme nom, la mme liste de paramtres, mais un type de paramtre
de retour dirent. En revanche, si deux mthodes dun mme type ont le mme nom et des
listes de paramtres direntes, elles peuvent avoir un type de retour dirent. Il est nanmoins
fortement dconseill que les direntes surcharges aient des types de retour dirents car ce
dernier est en gnral fortement li la smantique associe au nom commun des surcharges.
Dautres ambiguts peuvent apparatre si certaines de ces mthodes acceptent des arguments
variables en type et en nombre. Dans ce cas le compilateur C lve lambigut en prfrant les
mthodes sans arguments variables en type et en nombre. Cependant ce type dambigut est
viter afin de garder un code lisible. Par exemple :
Exemple 11-12 :
using System ;
class Program {
static void Main() {
fct("Appel1", "bonjour") ; // Appel de la surcharge
fct("Appel2") ;
// Appel de la surcharge
fct("Appel3", 10) ;
// Appel de la surcharge
fct("Appel4", 10, 11) ;
// Appel de la surcharge
fct("Appel5", 10.1) ;
// Appel de la surcharge
}
// Surcharge 1 :
static void fct(string str) {
System.Console.WriteLine("surcharge 1") ;
}
// Surcharge 2 :
static void fct(string str, int i) {
System.Console.WriteLine("surcharge 2") ;
}
// Surcharge 3 :
static void fct(string str, params object[] Args) {
System.Console.WriteLine("surcharge 3") ;
}
// Surcharge 4 :
3.
1.
2.
4.
3.
Les proprits
407
Les proprits
C++ C
C C permet aux classes et aux structures davoir des proprits. Les proprits permettent
daccder ltat dune instance avec la mme syntaxe que laccs un champ, mais sans passer
directement par un champ. Laccs se fait par des mthodes spciales appeles les accesseurs de
la proprit. Lavantage est double :
Lutilisateur de la classe veut un accs ltat de lobjet avec la mme syntaxe que laccs
un champ. Il na pas appeler explicitement une mthode.
Le dveloppeur de la classe veut intercepter tous les accs ltat de lobjet. Pour cela il
utilise les accesseurs.
Il y a deux types daccs ltat dun objet. Aussi, il y a deux accesseurs possibles pour chaque
proprit :
chaque accs en lecture dune proprit laccesseur get de la proprit est appel.
chaque accs en criture dune proprit laccesseur set de la proprit est appel.
Par exemple :
Exemple 11-13 :
public class Foo {
private int m_ChampPrivate = 10 ;
public int Prop { // Une propri
et
e de type int.
get{ return m_ChampPrivate;}
set{ m_ChampPrivate = value;}
}
}
public class Program {
static void Main() {
Foo foo = new Foo() ;
foo.Prop = 56 ;
// Laccesseur set est appel
e.
int i = foo.Prop ; // Laccesseur get est appel
e.
}
}
Les accesseurs se codent comme des mthodes mis part que :
408
Accesseur get
Laccesseur get dune proprit a pour contrainte de retourner un objet de mme type que sa
proprit (ou une rfrence vers un objet si cest un type rfrence).
Une proprit nest pas oblige dimplmenter laccesseur set. Dans ce cas on dit quelle est accessible en lecture seule (read only en anglais, bien que lon ne parle pas ici du mot-cl readonly).
Une proprit accessible en lecture seule ne peut pas tre initialise. En revanche, on peut retourner un objet dont ltat est calcul dans laccesseur get.
Exemple 11-14 :
public class Foo {
private int m_ChampPrivate = 10 ;
public bool Prop { // Une propri
et
e de type bool en lecture seule.
get{ return (m_ChampPrivate >100);}
}
}
public class Program {
static void Main() {
Foo foo = new Foo() ;
bool b = foo.Prop ; // Laccesseur get est appel
e.
}
}
Accesseur set
Dans le corps de laccesseur set, le mot-cl value est utilisable. Il reprsente un objet du mme
type que la proprit (ou une rfrence si cest un type rfrence). Cest lobjet (ou la rfrence)
fourni lors de lassignation de la proprit.
Une proprit nest pas oblige dimplmenter laccesseur get. Dans ce cas on dit quelle est
accessible en criture seule. Une proprit accessible en criture seule est inaccessible en lecture.
En revanche, on peut calculer un nouvel tat pour lobjet qui prsente la proprit, en tenant
compte de la valeur passe.
Exemple 11-15 :
public class Foo {
private int m_ChampPrivate = 10 ;
public int Prop { // Une propri
et
e de type int en
ecriture seule.
set{ m_ChampPrivate = value*2;}
}
}
public class Program {
Les indexeurs
409
Remarques
Une proprit est oblige davoir au moins un accesseur. Elle a soit un accesseur get, soit un
accesseur set, soit les deux.
Il est conseill de ne pas modifier ltat dun objet dans les accesseurs get. Il faut considrer les
accesseurs get comme un moyen dintercepter et de contrler un accs ltat de lobjet. On
peut par exemple en profiter pour loguer ces accs.
Il est conseill de ne pas lancer dexception partir dun accesseur, moins quelle soit rattrape
dans laccesseur mme. En eet, syntaxiquement le client na pas ncessairement conscience
dappeler un accesseur lors de laccs une proprit. Le client peut donc tre surpris par une
exception.
En page 419, nous expliquons que les accesseurs dune mme proprit peuvent avoir des niveaux de visibilit dirents.
Les indexeurs
C++ C Comme le langage C++, le langage C permet de dfinir loprateur daccs un
tableau [] dans une classe. Cependant C permet une dfinition beaucoup plus complte de
cet oprateur. En eet, il peut y avoir plusieurs index, on peut utiliser nimporte quel type pour
les index (y compris vos propres classes) et il peut y avoir plusieurs dfinitions de cet oprateur
pour une mme classe.
C C permet de considrer un objet comme un tableau une ou plusieurs dimensions. En
eet, C autorise lutilisation de loprateur [] directement aprs le nom dun objet. Cet oprateur accepte une liste de un ou plusieurs paramtres, de nimporte quel type (entier, chane de
caractres, objet de toutes classes etc). Les paramtres doivent tre spars par des virgules.
Bien videmment, la classe de lobjet doit prvoir le fait que loprateur [] puisse tre utilis
directement aprs le nom dun objet. Pour cela elle dclare un ou plusieurs indexeurs. Les indexeurs des classes peuvent tre considrs comme des proprits particulires, aux dirences
suivantes prs :
Proprit
Indexeur
410
Laccesseur set a la liste des paramtres de lindexeur en plus du paramtre value implicite.
Comme pour les proprits, les accesseurs des indexeurs sont facultatifs, bien quil en faille au
moins un par indexeur. La dfinition des accesseurs autorise un accs en lecture seule, criture
seule ou lecture/criture. Les paramtres des indexeurs ne peuvent utiliser les mots-cls ref et
out.
Lexemple suivant expose la syntaxe des indexeurs. Nous prsentons laccs un tableau de personnes. Les personnes peuvent tre accdes par leurs noms (une chane de caractres) en lecture
seulement. Les personnes peuvent aussi tre accdes par leurs index dans le tableau interne
lobjet qui les contient, en lecture/criture.
Exemple 11-16 :
using System ;
public class Personnes {
// Tableau prive interne qui contient les noms des personnes.
string [] m_Noms ;
// Le constructeur qui initialise le tableau.
public Personnes(params string [] noms){
m_Noms = new string[noms.Length] ;
// Copie le tableau.
noms.CopyTo(m_Noms,0) ;
}
// Lindexeur qui retourne lindex `
a partir du nom.
public int this[string nom]{
get{ return Array.IndexOf(m_Noms,nom);}
}
// Lindexeur qui retourne le nom `
a partir de lindex.
public string this[int index]{
get{ return m_Noms[index];}
set{ m_Noms[index] = value;}
}
}
class Program {
static void Main() {
Personnes tableau = new Personnes (
"Anna" , "Ingrid" , "Maria" , "Ulrika" ) ;
Console.WriteLine(tableau [1]) ; // Affiche "Ingrid"
int index = tableau["Maria"] ;
tableau[index] = "Marie" ;
Console.WriteLine(tableau[index]) ; // Affiche "Marie"
}
}
En gnral les classes qui ont des indexeurs doivent prsenter la possibilit dtre utilises avec
la syntaxe avec la syntaxe des mots-cls foreach et in.
Les vnements
411
Les vnements
Introduction
C++ C C dfinit la notion dvnement qui est compltement absente en C++. En C++ il
faut implmenter soi-mme la plomberie du mcanisme dvnement, par exemple au moyen
du design pattern (Gof) observateur .
C
Des abonns lvnement. Ces derniers sont prvenus chaque fois que lvnement est
dclench. Ils ont la possibilit de sabonner et de se dsabonner lvnement dynamiquement (i.e durant lexcution du programme). En C , un abonn est reprsent par une
mthode.
Lvnement lui-mme, qui peut tout moment tre dclench par lobjet. En interne, lvnement a la connaissance de ses abonns. La responsabilit de lvnement est de prvenir
ses abonns lorsquil est dclench. Lvnement a la possibilit de fournir des informations
ses abonns, lorsquil est dclench avec lobjet argument de lvnement. En C , un vnement est semblable un dlgu membre dune classe ou dune structure, mis part quil
est dclar avec le mot cl event.
Le concept vnement/abonns nest pas nouveau et est connu sous le nom de publisher/subscriber
(diteur/souscripteur) en Java. Le mme comportement est obtenu avec lutilisation du design
pattern (Gof) observateur (sujet/observateur).
Les vnements sont trs utiliss dans les applications avec une interface graphique (Windows
Forms et ASP.NET). En eet, chaque action possible sur linterface (clic sur un bouton, texte
tap, ComboBox droule, etc) dclenche lappel la mthode adquate. Lors dun clic souris,
largument de lvnement peut tre par exemple la position de la souris sur lcran. Dans la
plateforme .NET, lhritage combin avec les vnements sont la base de tous les contrles
graphiques.
La syntaxe C
Supposons que nous ayons cr dans une classe un vnement qui sappelle EventName. Les
noms des entits concernes par lvnement sont fonction du nom EventName. Un argument
dvnement est un objet dune classe drive de la classe System.EventArgs. Vous avez donc
la possibilit de crer vos propres types darguments dvnement en crant vos propres classes
drives de System.EventArgs.
Pour amliorer la lisibilit du code, il est prfrable que la classe dargument de lvnement
EventName sappelle EventNameEventArgs.
Les abonns sont matrialiss par des mthodes (statiques ou non). Le fait de prvenir les abonns se traduit par un appel chacune de ces mthodes. Ces mthodes ont en gnral la signature
suivante :
Premier argument de type objet. Lorsque la mthode est appele, cet argument rfrence
lobjet qui contient lvnement.
412
Un vnement est un membre dune classe, dclare avec le mot-cl event. Un vnement est
une instance de la dlgation EventNameEventHandler. Un vnement a la possibilit dtre statique ou non. Les niveaux de visibilit sappliquent aux vnements. Comme un vnement est
un membre dune classe, la dclaration dun vnement peut tre prcde par un ou plusieurs
des mots-cls suivants :
new
virtual
public
sealed
protected
override
internal
abstract
private
extern
static
Un vnement contient :
Un dlgu qui renferme les rfrences vers les mthodes abonnes. La dlgation a la
mme signature que les mthodes abonnes et doit sappeler EventNameEventHandler.
Un accesseur add qui est invoqu lorsquune mthode est abonne lvnement.
Un accesseur remove qui est invoqu lorsquune mthode est dsabonne lvnement.
Ces trois entits sont dfinies automatiquement par le compilateur C ds que lvnement est
dclar. Vous pouvez nanmoins dfinir vos propres corps daccesseurs. Si vous changez le corps
dun accesseur, vous devez aussi coder le corps de lautre accesseur. Dans le corps dun accesseur,
le mot-cl value dsigne le dlgu ajouter ou supprimer. Par exemple les deux dfinitions
suivantes dvnement sont acceptables :
// sans accesseurs
public event ClickButtonEventHandler ClickButton ;
// avec accesseurs
private event ClickButtonEventHandler m_ClickButton ;
public event ClickButtonEventHandler ClickButton{
add
{ m_ClickButton += value ; }
remove
{ m_ClickButton -= value ; }
}
Notez quil est trs rare davoir modifier les accesseurs add et remove. Cependant, cette facilit
peut tre utile afin de restreindre le niveau de visibilit de lvnement. Comprenez que les
possibilits dexploiter le polymorphisme sur un vnement (i.e le fait de pouvoir utiliser les
mots-cls virtual, new, override et abstract dans la dclaration dun vnement) ne sapplique
qu ces deux accesseurs add et remove. Clairement, cette facilit et sduisante pour le puriste
mais extrmement peu usite.
En C lvnement est dclench par lappel son dlgu. Ce nest pas le cas dautres langages
.NET. Par exemple VB.NET dclenche un vnement au moyen du mot-cl RaiseEvent qui
nexiste pas en C . Le compilateur impose que lappel au dlgu doit tre ralis au sein de
la classe qui dclare lvnement. Aussi, cette classe a la possibilit de prsenter une mthode
publique OnEventName() qui permet de dclencher lvnement hors de la classe.
...
public void OnClickButton(System.EventArgs Arg){
if(ClickButton != null )
// Declenche levenement.
Les vnements
413
ClickButton(this,Arg);
}
...
Un exemple
Voici un exemple o un objet de la classe AgenceDePresse publie des bulletins dinformation
sur la France et le monde. La publication se fait laide des deux vnements InfoMonde
et InfoFrance. Largument dun vnement bulletin dinformation (instance de la classe
InfoEventArgs) contient la description du bulletin dinformation. Enfin, les instances de la
classe Abonne ont la possibilit de recevoir un bulletin dinformation en abonnant leur mthode
ReceptionInfo().
Exemple 11-17 :
using System ;
class Abonne{
private string m_Nom ;
public Abonne( string Nom ) { m_Nom = Nom ; }
// Methode `a appeler lorsquun
ev
enement est d
eclench
e
// (i.e lorsquun bulletin dinformation est publi
e).
public void ReceptionInfo(object sender, EventArgs e){
InfoEventArgs info = e as InfoEventArgs ;
if( info != null ){
Console.WriteLine( m_Nom + " re
coit linfo : " +
((InfoEventArgs)e).GetBulletinInfo() ) ;
}
}
}
//La classe dargument de levenement bulletin dinformation.
class InfoEventArgs: EventArgs{
private string m_Description ;
public string GetBulletinInfo() { return m_Description ; }
public InfoEventArgs (string Description) {
m_Description = Description ;
}
}
// Definition du type delegue handler dun bulletin dinformation.
public delegate void InfoEventHandler(object sender, EventArgs e) ;
// Classe contenant les evenements bulletin dinformation.
class AgenceDePresse {
// Definition des evenements bulletins dinformation.
public event InfoEventHandler InfoFrance;
public event InfoEventHandler InfoMonde;
// Methodes de declenchement des
ev
enements
414
Le programme ache :
Raymond
Olivier
Olivier
Mathieu
Raymond
recoit
recoit
recoit
recoit
recoit
linfo
linfo
linfo
linfo
linfo
:
:
:
:
:
Dans cet exemple, rien ne change si vous enlevez les deux occurrences du mot-cl event (i.e rien
ne change si nous utilisons deux dlgus au lieu dutiliser deux vnements). Il y a deux raisons
cela. La premire est que nous nutilisons pas daccesseurs dvnements. La seconde est que
nous ne dclenchons pas un de ces vnements hors de la classe AgenceDePresse. En eet, une
utilit du mot-cl event est quil empche lvnement dtre dclench lextrieur de sa classe.
Le dclenchement dun vnement est aussi interdit partir des mthodes des classes drives
Les vnements
415
vnements asynchrones
Lorsquun vnement est dclench, il faut bien tre conscient que cest le mme thread qui
excute linstruction de dclenchement et les mthodes abonnes lvnement.
Un problme potentiel est que certaines mthodes abonnes sexcutent en un temps inacceptable. Un autre problme potentiel est quune mthode abonne peut lever une exception. Cette
dernire empchera alors les excutions des autres mthodes abonnes non encore excutes
et remontera jusqu la mthode qui a dclench lvnement. Il est clair que cela constitue un
problme puisque la mthode qui dclenche un vnement doit absolument tre totalement
dcouple des mthodes abonnes.
Pour pallier ces problmes, il serait bien pratique que les mthodes abonnes soient excutes
dune manire asynchrone (la notion dappel de mthode asynchrone est dcrite page 171). En
outre, il serait aussi intressant que dirents threads excutent les direntes mthodes abonnes. Ainsi, une mthode abonne qui sexcute en un temps inacceptable ou qui lance une
exception ne gne ni lexcution du dclencheur de lvnement, ni les excutions des autres
mthodes abonnes. Le pool de threads du CLR qui est dcrit en page 167 est particulirement
adapt ces deux contraintes.
Ceux qui ont compris la syntaxe dun appel asynchrone de mthode risquent dtre tents dutiliser directement la fonction BeginInvoke() sur lvnement lui-mme. Le problme de cette
solution est que seule une mthode abonne sera excute. La bonne solution, illustre par
lexemple suivant qui est base sur lExemple 11-17 ncessite le parcours explicite de la liste des
mthodes abonnes :
Exemple 11-19 :
...
v
// Methodes de declenchement des e
enements
// (i.e de publication de bulletin dinformation).
public void OnInfoFrance(InfoEventArgs BulletinInfo){
if( InfoFrance != null ){
Delegate[] abonnes = InfoFrance.GetInvocationList() ;
416
Notez enfin quil ny a pas lieu dappeler la mthode EndInvoke() puisque les mthodes abonnes ne sont pas senses retourner de rsultat la mthode qui a dclench lvnement.
417
On peut restreindre la visibilit du type encapsul. Cest--dire quun type dfini dans une
classe ne pourra tre utilis partout dans le code, mais seulement l o vous lavez dcid
(en gnral, seulement lintrieur de la classe encapsulante).
On peut accder tous les membres de la classe encapsulante, partir des mthodes du
type encapsul. Voici un exemple pour illustrer cette possibilit qui nest pas triviale.
Les instances de TypeEncapsule ont un libre accs au champ priv m_i dune instance
de TypeEncapsulant :
Exemple 11-21 :
public class TypeEncapsulant {
private int m_i = 10 ;
public class TypeEncapsule {
public void Add(TypeEncapsulant Foo, int i) {
Foo.m_i += i ; // Acc`
es au champ priv
e TypeEncapsulant.m_i.
}
}
}
public class Program {
static void Main() {
TypeEncapsulant foo1 = new TypeEncapsulant() ;
TypeEncapsulant.TypeEncapsule foo2 = new
TypeEncapsulant.TypeEncapsule() ;
foo2.Add(foo1, 3) ;
// Ici foo1.m_i vaut 13.
}
}
418
de base dans la classe drive, nexiste pas en C . En C on ne peut que conserver le niveau de
visibilit des membres de la classe de base.
C Lencapsulation est un puissant concept de la programmation par objets qui permet de
matriser la visibilit que lon a des membres dune classe ou dune structure vus de lextrieur
de celle-ci.
Lutilisateur dune classe (cest--dire un dveloppeur qui crit du code qui instancie ou rfrence une instance de la classe), na donc accs qu certains membres. Cela rduit dautant la
complexit dutilisation des objets puisque le nombre de points daccs est rduit.
Pour bien comprendre les dirents niveaux de visibilit il faut avoir compris la notion dassemblage qui fait lobjet du chapitre 2 et la notion de classe drive, qui fait lobjet du chapitre 12.
Il y a cinq niveaux de visibilit dirents. Chaque niveau de visibilit porte sa restriction une
partie du code source extrieur de la classe. Seul le niveau de visibilit public ninduit aucune
restriction. Chaque membre dune classe a un niveau de visibilit (et un seul) parmi :
Rsumons tout ceci dans un tableau. En colonne les niveaux de visibilit dun membre dune
classe A. En ligne les zones de code. Un OK signifie que la zone de code concerne a accs ce
membre.
419
public
internal
protected
internal
protected
private
Mthodes de la classe A.
OK
OK
OK
OK
OK
OK
OK
OK
OK
OK
OK
OK
OK
OK
OK
OK
Les niveaux de visibilit interne, et interne protg, ne peuvent sappliquer aux membres dune
structure.
Un vnement ne peut tre dclench hors de son type, quelque soit son niveau de visibilit.
Mme le code dun type encapsul de son propre type ne peut dclencher un vnement. Seuls
les accesseurs de lvnement sont impacts par son niveau de visibilit.
Par dfaut (i.e si on ne spcifie pas explicitement un niveau de visibilit) les membres dune
classe et dune structure sont privs.
420
Cette possibilit nest pas exploitable sur les accesseurs add et remove dun vnement.
Le mot-cl this
C++ C Comme en C++, le mot-cl this existe en C . En C , ce mot-cl, utilisable dans
les mthodes non statiques, est une rfrence (et non un pointeur comme en C++) vers lobjet
courant.
C Dans toutes les mthodes non statiques, de toutes les classes, C permet dutiliser le motcl this. Ce mot-cl est une rfrence vers lobjet sur lequel opre la mthode courante. Le motcl this ne peut donc tre utilis dans les mthodes statiques.
En C , le mot-cl this permet des arguments dune mthode dinstance et des champs (ou
proprits) dinstances de la classe davoir des noms similaires. Ainsi, dans le corps de la mthode, il faut faire prcder le nom en question par this pour dsigner la variable globale au
lieu de la variable locale (i.e le champ au lieu de largument). Par exemple :
Exemple 11-23 :
class Foo {
string str ; // Champ non statique de la classe.
void fct( string str ){ // M
ethode non statique.
this.str = str ; // Le champ str est
egale `
a largument str.
}
}
421
Une autre utilit du mot-cl this est de communiquer une rfrence de lobjet courant
dautres mthodes, ventuellement dautres classes. Ce type dutilisation tmoigne en gnral
dune architecture objet volue. Par exemple, le design pattern (Gof) nomm communment
visiteur utilise cette facilit :
Exemple 11-24 :
class Foo {
void fct(){ // Methode non statique.
fct2(this);
}
static void fct2( Foo f ){
// Travaille avec lobjet r
ef
erenc
e par f...
}
}
En outre, le mot-cl this est utilis par la syntaxe des indexeurs dcrite page 409. Le mot-cl this
est aussi utilisable au sein des mthodes non statiques des structures. Bien quune structure soit
un type valeur, le mot-cl this reprsente bien linstance courante. Ceci est bien une facilit C
car il ny a pas dopration de boxing ralise et donc pas de rfrence.
Pour ceux qui sont intresss par le langage IL, sachez quen IL, la rfrence this est passe
comme largument zro une mthode dinstance, dcalant ainsi dune position dans la liste
les autres arguments.
422
Toutefois la surcharge des oprateurs de transtypage est possible en C , ce qui rend quand mme
les exemples possibles.
C Une mthode est automatiquement appele lorsque lobjet est construit. On appelle
ce type de mthode un constructeur (ctor en abrg). En C , syntaxiquement, une mthode
constructeur porte le nom de la classe, et ne retourne rien (mme pas le type void). Il peut y
avoir :
Un seul constructeur : dans ce cas, ce sera toujours lui qui sera appel. Le compilateur ne
fournit pas de constructeur par dfaut.
Plusieurs constructeurs : dans ce cas ils dirent selon leurs signatures, le constructeur est
alors une mthode surcharge. Le compilateur ne fournit pas de constructeur par dfaut.
// ctor 1
423
C Pour une bonne comprhension de la prsente section, il faut avoir assimil la section concernant le ramasse-miettes en page 116. Par rapport C++ nous parlerons de finaliseur plutt que de destructeur.
Dans la section concernant le ramasse-miettes, nous expliquons quil existe un thread allou et
gr par le CLR qui est ddi lappel des finaliseurs des objets. Le finaliseur dune classe est
424
425
426
Exemple 11-29 :
...
static void Main() {
System.Console.WriteLine("Thread#{0} Thread principal.",
Thread.CurrentThread.ManagedThreadId ) ;
Article a = new Article(300) ;
using(a){
// Ici, vous pouvez utiliser lobjet a
// cest sa zone de validit
e.
}// Cette accolade provoque lappel automatique de a.Dispose().
// Elle signe la fin de la zone de validit
e de a.
}
...
ATTENTION : lutilisation du mot-cl using prsente ci-dessous na rien voir avec les espaces
de noms et les alias.
Notez quune zone de validit peut tre commune plusieurs objets, de mmes classes ou de
classes direntes :
Exemple 11-30 :
...
static void Main() {
System.Console.WriteLine("Thread#{0} Thread principal.",
Thread.CurrentThread.ManagedThreadId ) ;
Article a = new Article(300) ;
Article b = new Article(400) ;
using(a) using(b){
// Ici, eventuellement utilisation des objets a et b.
}// Fin de la zone de validit
e des objets a et de b.
}
...
Les mthodes Dispose() seront appeles dans lordre inverse de la dclaration des using laccolade de la fin de la zone de validit (ici b.Dipose() puis a.Dispose() ).
Pour des raisons de smantique de nom de mthode, vous pouvez tre tent dutiliser une
mthode par exemple nomme Close() au lieu dimplmenter linterface IDisposable et sa
mthode Dispose(). Il vaut mieux viter cette pratique car le simple fait dimplmenter linterface IDispose est un moyen standard dexposer vos clients quils doivent utiliser la mthode
Dispose(). Il existe mme des analyseurs de code capables de dtecter que Dispose() nest pas
appele sur un objet dont la classe implmente IDisposable.
Si vous optez cependant pour lexistence dune mthode Close() il faut encapsuler lappel
cette mthode dans la mthode Dispose(), certaines classes .NET le font. Il est alors important
de bien documenter cela afin dviter de voir appeler successivement Close() et Dispose().
Parfois une telle pratique peut provoquer la leve dexceptions.
Lorsquune classe implmente linterface IDisposable il est conseill dinterdire lappel toutes
les autres mthodes sur un objet qui a t dispos en lanant une exception de type System.
ObjectDisposedException. Pour laisser une plus grande marge de manuvre aux clients de
427
votre classe, il est aussi conseill de permettre dappeler plusieurs fois la mthode Dispose()
sur un mme objet. Bien entendu seul le premier appel aura un eet. Avec ces rgles, la classe
Article peut tre rcrite comme ceci (notez le recours un champ priv boolen) :
Exemple 11-31 :
public class Article : System.IDisposable {
public int m_Prix ;
public Article(int prix) { this.m_Prix = prix ; }
private bool m_bDisposed = false;
public void Fct() {
if (m_bDisposed)
throw new System.ObjectDisposedException("Nom de lobjet");
// Ici le corps de Fct().
}
public void Dispose() {
if (!m_bDisposed){
m_bDisposed = true;
// Ici liberation des ressources.
}
}
}
Limplmentation prcdente de Article a une faiblesse. Dans une application multi-threaded,
il se peut que le test sur m_bDisposed dans une mthode ait lieu entre le test dans la mthode
Dispose() et laectation true dans la mthode Dispose(). Pour la mme raison, il se peut
aussi que le code de libration des ressources de la mthode Dispose() soit appele deux fois
par deux thread dirents. Pour pallier ce problme sans rendre la classe toute entire threadsafe vous pouvez par exemple synchroniser laccs m_bDisposed comme ceci :
Exemple 11-32 :
public class Article : System.IDisposable {
public int m_Prix ;
public Article(int prix) { this.m_Prix = prix ; }
private object m_SyncRootDisposed = new object();
private bool m_bDisposed = false ;
public void Fct() {
lock ( m_SyncRootDisposed )
if ( m_bDisposed )
throw new System.ObjectDisposedException("Nom de lobjet") ;
// Ici le corps de Fct().
}
public void Dispose() {
bool bOldDisposedState = true;
lock ( m_SyncRootDisposed )
if ( !m_bDisposed ) {
bOldDisposedState = false;
m_bDisposed = true ;
428
Toutes les classes qui maintiennent une ou plusieurs ressources non gres (telle quun
handle vers un objet Windows par exemple) doivent avoir un finaliseur qui libre ces ressources. On peut ajouter quil est essentiel quune telle classe doit toujours garder ses ressources non gres prives. Ces deux conditions permettent de garantir que les ressources
non gres seront libres. En outre, seules les classes doivent tre habilites maintenir
des ressources non gres puisque les structures nont pas de finaliseur.
La remarque prcdente implique quune classe qui ne maintient que des ressources gres na
pas besoin de finaliseur. En eet, il ne servirait alors rien dassigner les champs de types rfrence nul car un finaliseur est excut strictement entre deux collectes du ramasse-miettes et
son excution implique la perte des rfrences contenues dans les champs de lobjet concern.
On peut tre tent dimplmenter un finaliseur dans une classe qui maintient des rfrences vers
des objets gres qui ont une mthode Close() ou Dispose() (comme une connexion une base
de donnes par exemple). En fait, ce genre de classe maintient directement ou indirectement
par lintermdiaire dautres classes des ressources non gres. Lappel la mthode Close() ou
Dispose() ne permet donc que danticiper le moment o ces ressources non gres seront libres. Il ne sert donc rien dinvoquer ces mthodes dans un finaliseur. Il faut les invoquer
avant, partir dune mthode Dispose(). Et ceci soulve une autre question : quand doit-on
implmenter linterface IDisposable ?
Une partie de la rponse est simple : si votre classe maintient des rfrences vers des objets dont
la dure de vie est la mme que celle de ses instances et dont limplmentation prsente une
mthode telle que Dispose() ou Close(), il faut quelle implmente une mthode Dispose()
qui appelle les mthodes Dispose() et Close() de ces objets. Si votre classe ne rpond pas au critre cit, la dcision dimplmenter ou non linterface IDisposable est plus complique puisque
vous tes face un dilemme. Dun cot il est bon davoir une mthode Dispose() pour mettre
nul les rfrences que vous maintenez vers dautres objets grs de faon augmenter leurs
chances dtre librs lors de la prochaine collecte. Dun autre cot implmenter linterface
IDisposable complexifie votre code ainsi que celui de vos clients. Le choix doit donc se faire en
fonction des besoins en mmoire de vos applications et de la taille des objets grs rfrencs.
Lorsquune de vos classes implmente un finaliseur, vous pouvez tre tent dimplmenter aussi
linterface IDisposable pour permettre au client de librer les ressources (gres et non gres)
au plus tt. Dans ce cas nous vous conseillons dappliquer les rgles suivantes :
429
Implmentez une mthode protected virtual void Dispose(bool bDisposeManagedRes). Appelez cette mthode partir de la mthode Dispose() avec le paramtre bDisposeManagedRes gal true. Appelez cette mthode partir du finaliseur avec le paramtre
bDisposeManagedRes gal false.
Dans la mthode Dispose(), aprs avoir appel la surcharge Dispose(bool bDisposeManagedRes) appelez GC.SuppressFinalize(this). Vous indiquez ainsi au ramasse-miettes quil
naura pas besoin dappeler le finaliseur de cet objet.
Si vous appliquez ces conseils, vous soulagez ainsi le thread finaliseur de la libration inutile des
ressources gres, vous vous protgez des oublis de lappel `
a Dispose() de la part de vos clients
et vous optimisez lexcution des clients qui noublient pas dappeler Dispose(). Voici une classe
Foo qui suit ces conseils (sans la synchronisation des accs m_bDisposed) :
Exemple 11-33 :
public class Foo : System.IDisposable {
public Foo() {
// Ici eventuellement allocation des ressources.
}
private bool m_bDisposed = false;
public void Fct() {
if ( m_bDisposed )
throw new System.ObjectDisposedException("Nom de lobjet");
// Ici le corps de Fct().
}
public void Dispose() {
Dispose(true);
System.GC.SuppressFinalize(this);
}
~Foo() { Dispose(false) ; }
protected virtual void Dispose(bool bDisposeManagedRes) {
if ( !m_bDisposed ) {
m_bDisposed = true;
// Ici liberer les ressources non-g
er
ees.
if ( bDisposeManagedRes ) {
// Ici liberer les ressources g
er
ees.
}
}
}
}
Dans le cas o Foo est une classe de base qui admet des classes drives qui maintiennent des
ressources, il est judicieux de rcrire la mthode Dispose(bool bDisposeManagedRes) puisque
celle-ci est virtuelle et protge. Il est intressant de remarquer que le design des classes du framework suit les rgles nonces.
430
Nous accdons aux membres statiques avec cette syntaxe NomDeLaClasse.NomDuMembreStatic. Nous navons pas besoin de loprateur de rsolution de porte comme en C++.
Nous ne pouvons pas accder un membre statique au moyen dune instance de la classe.
Au niveau conceptuel rien ne change mis part quil peut exister un constructeur statique et
des classes purement statiques.
C Les champs, les proprits, les mthodes et les vnements dune classe ont la possibilit
dtre dclars avec le mot-cl static. Ce mot-cl signifie que le membre appartient la classe,
et non aux objets, comme cest le cas des membres non statiques. Les membres statiques sont
parfois appels membres partags, car ils sont partags par les instances de la classe. Dailleurs le
langage VB.NET utilise le mot-cl Shared qui veut dire partag en anglais la place du mot cl
C static. Les membres non statiques sont appels membres dinstances.
Les membres dclars comme statiques subissent les mmes rgles de niveau de visibilit que
les autres membres. De plus, parmi les direntes sortes de membres, seuls les types encapsuls
et les indexeurs ne peuvent tre dfinis avec le mot-cl static.
Ils sont accessibles dans toutes les mthodes de la classe par leurs noms, et lextrieur de la
classe (selon leurs niveaux de visibilit) par la syntaxe : NomDeLaClasse.NomDuChampStatic
Les champs peuvent tre initialiss directement durant leurs dclarations au sein de la classe.
Comme pour linitialisation des champs dinstances, linitialisation des champs statiques est un
sujet subtil. En eet, lorsque la classe est cre, les champs statiques sont dabord initialiss
leurs valeurs par dfaut. Puis ils sont initialiss avec leur valeur dinitialisation (pour ceux qui
en ont une), dans leur ordre dapparition dans la classe. Enfin le constructeur statique (voir plus
loin) est appel. Tout ceci permet de faire des rfrences croises comme ci-dessous, bien que ce
soit fortement dconseill :
431
Exemple 11-34 :
public class Program {
static int a = b + 2 ; // a r
ef
erence
static int b = a + 3 ; // b r
ef
erence
public static void Main() {
System.Console.WriteLine("a = {0}",
System.Console.WriteLine("b = {0}",
}
}
// a = 2
// b = 5
Le cas de figure suivant porte aussi confusion, dailleurs le rsultat est dirent du prcdent.
En eet, la construction de la classe CB se fait au milieu de la construction de la classe CA, puisque
CA est appele en premier mais CA utilise CB !
Exemple 11-35 :
public class CA {
static public int a = CB.b + 2 ;
}
public class CB {
static public int b = CA.a + 3 ;
}
public class Program {
public static void Main() {
System.Console.WriteLine("CA.a = {0}", CA.a) ;
System.Console.WriteLine("CB.b = {0}", CB.b) ;
}
}
// CA.a = 5
// CB.b = 3
Mthodes statiques
Les mthodes statiques se distinguent par les caractristiques suivantes :
Une mthode statique na pas accs aux mthodes, champs, proprits et vnements non
statiques de la classe.
Une mthode statique nest pas accessible partir dune rfrence vers une mthode de la
classe,
Une mthode statique est accessible dans toutes les mthodes (statiques ou non) de la classe
par son nom, et lextrieur de la classe (si son niveau de visibilit le permet) par la syntaxe :
NomDeLaClasse.NomDeLaMethodeStatique()
Une mthode statique ne peut utiliser le mot-cl this expliqu dans la section prcdente.
432
ni quel thread ralise cet appel ne sont fixs par la spcification du langage C . Cependant, les
points suivant de la spcification C peuvent vous aider valuer ce moment :
Le constructeur statique est appel par le CLR lorsque celui-ci charge les mtadonnes de
types de la classe concerne, dans le domaine dapplication courant.
Le constructeur statique doit tre appel avant toute cration dinstance de la classe.
Le constructeur statique doit tre appel avant quun membre statique de la classe soit accd.
Le constructeur statique doit tre appel aprs que les champs statiques initialiss explicitement dans le code, soient initialiss.
Le constructeur statique dune classe charge par le CLR doit tre appel au moins une fois
durant lexcution du programme.
Vous avez la possibilit de forcer lappel au constructeur dune classe avec la mthode static
void RunClassConstructor(type t) de la classe System.Runtime.CompilerServices.RuntimeHelpers. Naturellement, lexcution na lieu que si le constructeur de classe concern na pas
dj t appel.
Voici une classe qui compte le nombre dinstances couramment valides, ce nombre tant initialis dans son constructeur statique :
Exemple 11-36 :
public class Article {
static int NbArticles ;
// Un champ statique.
static Article() {
// Le constructeur statique.
NbArticles = 0;
}
int Prix = 0 ;
// Un champ priv
e non statique.
public Article(int Prix) { // ctor
this.Prix = Prix ;
NbArticles++ ;
}
~Article() { NbArticles-- ; }// finaliseur
}
Classes statiques
C permet la dfinition de classes statiques. Une classe statique ne peut avoir que des membres
statiques. Nayant pas de constructeur dinstance, une classe statique ne peut tre instancie. En
outre on ne peut driver dune classe statique. Notez quune structure ne peut tre statique.
Pour dfinir une classe statique il sut dutiliser le mot-cl static dans sa dfinition :
Exemple 11-37 :
static class Program {
static void Main() { }
}
433
loprateur daectation = ;
loprateur modulo % ;
loprateur dappel dune fonction () qui permettait les foncteurs (fonctions objets) sur
laquelle repose une grande partie de la STL. Cette notion de foncteur reste cependant implmentable dun autre faon comme nous lexpliquons en page 591.
Les oprateurs unaires, qui nagissent que sur un seul oprande. La mthode statique surchargeant un oprateur unaire naccepte quun argument du type de sa classe. De plus elle
renvoie aussi une valeur du type de sa classe.
434
Tous les oprateurs ne sont pas surchargeables, et cest heureux. En eet, le code devient vite plus
complexe lorsque des oprateurs sont surchargs, moins que la signification de loprateur soit
vraiment logique dans le contexte de la classe. En eet, chaque oprateur surchargeable a une signification bien connue des dveloppeurs. Ainsi loprateur + est utilis pour laddition ou la
concatnation. Il est logique de lutiliser dans une classe reprsentant des nombres (par exemple
des nombres fractionnaires, complexes ou les quaternions etc) dautres objets mathmatiques
contenus dans un ensemble avec une structure de groupe additive (matrice, vecteur, fonction,
forme etc) ou des chanes de caractres. Mais, que penser dune utilisation de cet oprateur dans
une classe reprsentant des personnes ? Cest pour cette raison que nous vous conseillons de ne
surcharger les oprateurs que lorsque cela est logique dans le contexte de la classe.
Voici la liste des oprateurs surchargeables :
Oprateurs
geables
unaires
surchar-
+ -!
++ --
Certains oprateurs sont la fois binaires et unaires. Par exemple loprateur - peut la fois
rendre un oprande ngatif, ou soustraire deux oprandes.
Les oprateurs unaires dincrmentation ++ et de dcrmentation -- surchargs, provoquent lappel vers la mme mthode, quils soient postfixs ou prfixs. Rappelons que
lorsque lon utilise ces oprateurs, lordre dvaluation des oprateurs est dirent, selon leur
position par rapport loprande.
Voici un exemple dutilisation des oprateurs. Nous crons une classe Distance et une classe
Surface. En interne les valeurs sont sauves dans des doubles ou une unit reprsente un mtre
(respectivement un mtre carr). Nous surchargeons les oprateurs plus + et toile * entre
deux distances, loprateur dincrmentation ++ dun mtre sur une distance, et loprateur
de division / par une distance ou par une surface pour une surface :
Exemple 11-38 :
public class Distance {
double m_Mesure = 0.0 ;
public double Mesure { get { return m_Mesure ; }
set { m_Mesure = value ; } }
public Distance(double d) { m_Mesure = d ; }
public static Distance operator +(Distance d1, Distance d2) {
return new Distance(d1.m_Mesure + d2.m_Mesure) ;
}
public static Surface operator *(Distance d1, Distance d2) {
return new Surface(d1.m_Mesure * d2.m_Mesure) ;
}
public static Distance operator ++(Distance d) {
435
436
Cet exemple montre bien la ncessit de coder les oprateurs dans la logique de leur utilisation
habituelle en C . Sinon le code devient vite compltement illisible.
Les oprateurs de transtypage implicite. Lcriture suivante sut provoquer lappel loprateur de transtypage implicite de CSrc vers CDest, dclar dans CSrc.
CDest objDest = objSrc ;
Les oprateurs de transtypage explicite. Lcriture prcdente ne sut pas provoquer lappel
loprateur de transtypage explicite de CSrc vers CDest, dclar dans CSrc. Dans ce cas,
le compilateur gnre une erreur et le dveloppeur doit alors explicitement exprimer le
transtypage comme ceci :
CDest objDest = (CDest) objSrc ;
Comme tous les oprateurs, les oprateurs de transtypage sont des mthodes statiques. Ils sont
dclars dans la classe source et ils portent le nom de la classe destination. Lutilisation de lun
des mots-cls explicit ou implicit est obligatoire.
Voici un exemple de code o un objet de classe Distance peut tre transtyp implicitement en
une variable double et explicitement en objet de classe DistanceEnti`
ere. Notez la ncessit de
tenir compte de la nullit potentielle de la rfrence en entre des oprateurs :
Exemple 11-40 :
public class Distance {
public double m_Mesure = 0.0 ;
public Distance(double d) { m_Mesure = d ; }
public static implicit operator double(Distance d) {
// Doit tenir compte du cas ou d est nulle.
if (object.ReferenceEquals(d, null))
return 0.0 ;
437
return d.m_Mesure ;
}
public static explicit operator DistanceEntiere(Distance d) {
if (object.ReferenceEquals(d, null))
// Vous pouvez aussi pr
ef
erer retourner la r
ef
erence
// nulle dans ce cas.
return new DistanceEntiere(0) ;
// Notez la necessite de construire un nouvel objet pour un
// type destination reference.
return new DistanceEntiere((int)d.m_Mesure) ;
}
}
// Distance enti`ere signifie que la distance est cod
ee sur un entier.
public class DistanceEntiere {
public int m_Mesure = 0 ;
public DistanceEntiere(int i) { m_Mesure = i ; }
}
class Program {
static void Main() {
Distance d1 = new Distance(5.3) ;
// OK le transtypage est implicite.
double dbl1 = d1 ;
// La forme explicite est aussi accept
ee.
double dbl2 = (double)d1 ;
// Erreur de compilation !! : le transtypage doit
etre explicite.
DistanceEntiere de1 = d1 ;
// OK le transtypage est explicite.
DistanceEntiere de2 = (DistanceEntiere)d1 ;
// Teste le cas o`u la ref
erence source est nulle.
Distance d2 = null ;
double dbl3 = (double)d2 ;
DistanceEntiere de3 = (DistanceEntiere)d2 ;
}
}
Il est toujours prfrable de dclarer ses oprateurs de transtypage comme explicites. Dans le cas
contraire votre code risque dtre permissif, cest--dire que le compilateur aura faire des choix.
Au mieux il dclarera une erreur car il ne saura pas quel choix faire. Au pire, il ne donnera pas
davertissements et fera le choix contraire votre attente. Par exemple imaginons quune instance de la classe Distance puisse tre convertie implicitement en une instance du type double
ou une instance de la classe string. Quel choix de transtypage le compilateur doit-il faire lorsque
lon veut acher cet objet avec la mthode Console.WriteLine(object) ?
Exemple 11-41 :
public class Distance {
public double m_Mesure = 0.0 ;
public Distance(double d) { m_Mesure = d ; }
public static implicit operator double(Distance d) {
if (object.ReferenceEquals(d, null))
438
Oprateurs de comparaison
C++ C Comme C++, C permet de redfinir le comportement des oprateurs de comparaison == et != .
Cependant les motivations sont direntes en C . Par dfaut le comportement de ces oprateurs
sur des objets de type rfrence est de comparer si les rfrences rfrencent le mme objet. Or,
assez souvent, on souhaite comparer le contenu.
De plus en C les oprateurs de comparaison ne sont pas dfinis par dfaut sur les structures,
contrairement C++.
C Les oprateurs de comparaison sont les suivants :
Oprateur
Nom
Commentaires
==
Oprateur dgalit.
!=
Oprateur dingalit.
<=
Oprateur infrieur ou
gal.
439
>=
Oprateur suprieur ou
gal.
<
Oprateur
strictement.
infrieur
>
Oprateur
suprieur
strictement.
Par dfaut, si ces oprateurs ne sont pas redfinis, les rgles suivantes sont appliques selon la
nature des types des oprandes :
Si les deux oprandes sont de mme type structure, le compilateur produit une erreur. La
comparaison par dfaut sur des structures nexiste pas en langage C .
Si les deux oprandes sont de type rfrence et que loprateur de comparaison est lgalit
ou lingalit, le compilateur va dabord vrifier que les classes des deux objets rfrencs
sont les mmes. Cette condition peut tre remplie mme si les types des rfrences sont
dirents. En eet, lhritage, qui fait lobjet du prochain chapitre, permet un objet et
une rfrence vers cet objet dtre de types dirents. Si les deux objets rfrencs ont la
mme implmentation, les oprateurs de comparaison vont vrifier si cest le mme objet
qui est rfrenc ou non.
Si les deux oprandes sont de type rfrence et que loprateur de comparaison nest pas
lgalit ou lingalit, une erreur est produite par le compilateur. La comparaison par dfaut autre que lgalit/ingalit, sur des types rfrence nexiste pas en langage C .
Lorsque vous dcidez de redfinir les oprateurs dgalit et dingalit, il est conseill que vous
redfinissiez aussi la mthode Equals() de la classe Object. Si vous suivez ce conseil, il faut que
les deux surcharges appellent la mthode Equals(), comme dans lexemple suivant : (plus dinformation concernant la mthode Object.Equals() sont disponibles en page 345, notamment
on y explique pourquoi la compilation du programme suivant met un avertissement quant
la mthode Object.GetHashCode()).
Exemple 11-42 :
public class Distance {
private double m_Mesure = 0.0 ;
public Distance(double d) { m_Mesure = d ; }
public override bool Equals(object obj) {
Distance d = obj as Distance;
// Verifie que lobjet auquel on se compare est bien une distance.
440
d2) {
sont nulles.
;
d2) {
}
class Program {
static void Main() {
Distance d1 = new Distance(5.2) ;
Distance d2 = new Distance(5.2) ;
Distance d3 = new Distance(7.3) ;
Distance d4 = null ;
// Toutes ces assertions sont vrais.
System.Diagnostics.Debug.Assert( d1 == d2 ) ;
System.Diagnostics.Debug.Assert( d1 != d3 ) ;
System.Diagnostics.Debug.Assert( d1 != null ) ;
System.Diagnostics.Debug.Assert( null != d1 ) ;
System.Diagnostics.Debug.Assert( !(d1 == null) ) ;
System.Diagnostics.Debug.Assert( !(null == d1) ) ;
System.Diagnostics.Debug.Assert( d4 == null ) ;
}
}
Comme vous pouvez le constater en analysant cet exemple, surcharger les oprateurs == et =!
nest pas une manipulation aise :
Il faut dabord envisager les cas o une voire les deux rfrences sont nulles. Par convention,
deux rfrences nulles doivent tre considres comme gales.
Il faut aussi envisager le cas o lautre rfrence laquelle on se compare ne rfrence pas
un objet de type Distance.
441
langages .NET ne la supportent pas et ne peuvent pas lutiliser. Aussi, si votre classe est destine tre utilise par dautres langages .NET, nous vous conseillons de prvoir des mthodes
publiques avec les mmes fonctionnalits que vos oprateurs redfinis. Par exemple si vous
surchargiez loprateur + dans une classe, nous vous conseillons de prvoir une mthode
statique Add() remplissant la mme fonctionnalit. Rappelons que si vous avez un doute quant
aux respect de votre code par rapport au CLS, vous pouvez utilisez lattribut CLSCompliant que
nous dcrivons en page 131.
Voici la liste des noms de substitution donns aux mthodes qui ont pour but dorir la mme
fonctionnalit quun oprateur redfini. Ces noms sont conseills, et le compilateur vous autorise utiliser vos propres identificateurs.
Oprateur
Oprateurs de transtypage
Transtypage implicite vers le type Xxx
ToXxx FromXxx
ToXxx FromXxx
Add
Subtract
Multiply
Divide
Mod
Xor
&
BitwiseAnd
BitwiseOr
<<
LeftShift
>>
RightShift
Plus
Negate
++
Increment
--
Decrement
OnesComplement
442
Not
Oprateurs de comparaison
==
Equals
!=
Compare
<=
Compare
>=
Compare
<
Compare
>
Compare
12
Hritage/drivation
polymorphisme et abstraction
La complexit dun programme en fonction de sa taille, grandit plus vite quune fonction
linaire de sa taille.
Reprsentation
de la fonction
linaire
Taille du programme
444
Un programme deux fois plus gros quun autre est donc plus que deux fois plus long crire,
tester et maintenir.
Cette loi est empirique. Le travail de du concepteur dun programme consiste ce que la complexit reste aussi proche que possible de la fonction linaire. Le mcanisme de drivation ou
dhritage est un point important pour laider dans cette tche mais nous verrons en fin du
prsent chapitre que ce nest pas le seul.
Concrtement, on part du constat que dans un programme, direntes classes ont des fonctionnalits identiques :
Dans un programme de gestion de personnel dune entreprise, il peut y avoir une classe
pour les secrtaires, une classe pour les techniciens, une classe pour les cadres. Toutes ces
classes ont ceci de commun quune instance reprsente un employ, avec les attributs Nom,
Age, Adresse, Salaire et les mthodes Evalue(), ModifieLesHoraires(), Augmente().
Dans un programme de dessin, il peut y avoir une classe pour les cercles, une classe pour
les rectangles, une classe pour les triangles. Toutes ces classes ont ceci de commun quune
instance a les attributs CouleurDuTrait, TailleDuTrait et les mthodes Dessine(), Translation(), Rotation(), Grossi().
Un Technicien est un employ, un Rectangle est une figure gomtrique, une socket est
un point de communication.
Un Technicien peut tre valu, un Rectangle peut tre grossi, une socket peut tre utilise pour envoyer un stream.
Lhritage dimplmentation
445
Lhritage dimplmentation
La syntaxe
C++ C La syntaxe pour lhritage dimplmentation est la mme en C et C++. Conceptuellement il existe une grosse dirence entre C++ et C . C ne supporte pas lhritage multiple. Une classe C ne peut driver que dune seule classe de base. Nous verrons que ce manque
est en partie compens par la formalisation, en C , du concept, plus simple dinterface et dimplmentation de plusieurs interfaces par une classe.
C En C , la syntaxe pour indiquer que la classe Technicien drive de la classe Employe est
la suivante :
// La classe Technicien herite de la classe Employe.
class Technicien : Employe { ... }
Une classe ne peut driver que dune seule classe de base. En langage objet, on dit que C
supporte lhritage dimplmentation simple mais pas lhritage dimplmentation multiple. Ce nest
pas le cas de tous les langages orients objet. Les langages C++ et EIFFEL supportent lhritage
dimplmentation multiple alors que le langage Java ne le supporte pas.
En C , si une classe C drive dune classe B, rien nempche la classe B de driver dune classe A.
On dit que C drive indirectement de A. En outre la classe B est la fois une classe drive et une
classe de base.
446
Labrador
Chien
Reptile
Caniche
Lzard
Schma de drivation
Une classe de base peut tre aussi une classe drive. Tout ceci peut tre illustr dans un schma
de drivation. Par exemple :
Il existe des langages de modlisation pour les schmas objets, le plus courant tant le langage
UML (Unified Modelling Langage). Nous avons utilis la notation du diagramme de classes de ce
langage pour reprsenter le schma de la Figure 12-2. Comme vous le constatez, en UML, une
flche blanche pointe vers une classe de base, et se divise vers les classes drives.
Visual Studio 2005 prsente la possibilit de visualiser un diagramme de classes ressemblant un
diagramme de classes UML. Vous pouvez avoir accs cette possibilit en cliquant droit sur un
projet et en slectionnant View Class Diagram. Un fichier XML dextension .cd est alors insr
au projet. Il contient la mise en forme de votre diagramme. Voici par exemple un extrait de la
hirarchie des classes de contrles de Windows Forms prsentes en page 679 :
Lhritage dimplmentation
447
ment dclar, le compilateur appelle le constructeur sans argument de la classe de base sil y en
a un, sinon il y a une erreur de compilation.
Cependant, la syntaxe du langage C est lgrement dirente de celle du langage C++. Comme
il ny a pas dhritage dimplmentation multiple en C , la classe de base est unique. Donc il nest
pas ncessaire de prciser le nom de la classe de base. Dans ce contexte, en C le nom de la classe
de base est remplac par le mot-cl base.
C En C , chaque constructeur dune classe drive appelle un constructeur de la classe de
base. Cet appel peut tre implicite, cest--dire quil ny a pas de code qui montre clairement que
cet appel doit se faire. Dans ce cas cest le constructeur sans argument de la classe de base qui est
appel. Si la classe de base na pas de constructeur sans argument, alors une erreur est produite
la compilation. Rappelons les principes suivants :
Si une classe dclare un seul constructeur : il ny a quun seul constructeur, celui-ci. Le compilateur ne fournit pas de constructeur par dfaut.
Si une classe dclare plusieurs constructeurs : ils dirent selon leurs signatures et on dit que
le constructeur est surcharg. Le compilateur ne fournit pas de constructeur par dfaut.
Lappel au constructeur de la classe de base peut tre explicite partir dun constructeur
dune classe drive. Pour cela le mot-cl base doit tre utilis avec la mme syntaxe que dans
lexemple suivant :
Exemple 12-1 :
public class Employe {
string m_Nom ;
short m_Age ;
// Constructeur acceptant comme liste darguments : string, short
public Employe(string Nom, short Age) {
m_Nom = Nom ;
m_Age = Age ;
}
// Constructeur sans arguments.
public Employe() {
m_Nom = "n/a" ;
m_Age = 0 ; // <- Pas necessaire, un short est initialis
e `
a 0.
}
}
class Technicien : Employe { // Technicien h
erite de Employe.
string m_Competences ;
// Appel au constructeur de la classe de base acceptant comme liste
// darguments : string, short
public Technicien(string nom, short age, string competences)
: base(nom,age) {
m_Competences = competences ;
}
}
448
Une classe finalise peut tre une classe drive. Dailleurs, comme tous les types, les classes finalises drivent de la classe Object. De plus, les structures peuvent tre vues comme des classes
finalises de type valeur.
En C++, pour avoir accs au polymorphisme, on utilise le plus souvent des pointeurs de
type classe de base sur lesquels on appelle des mthodes virtuelles. En C , les pointeurs
ntant pas trs populaires, on utilise la plupart du temps des rfrences types par une
classe de base, rfrenant des objets dimplmentations drives. Notez quen C++ le polymorphisme est aussi accessible par lintermdiaire de rfrences, mais cette possibilit est
moins utilise que les pointeurs.
En C++ une mthode virtuelle est dclare avec le mot-cl virtual dans la premire classe
de base qui implmente cette mthode. Les implmentations de cette mthode dans les
classes drives peuvent optionnellement rutiliser ce mot pour signaler que cette mthode
est virtuelle. la lecture du code il nest donc pas toujours vident quune mthode est
449
virtuelle. Le langage C rsout ce problme. Une mthode virtuelle est toujours dclare
avec le mot-cl virtual, dans la premire classe de base qui limplmente. Dans chaque
classe drive o la mthode virtuelle est redfinie, il faut faire prcder la mthode du
mot-cl override.
Une dirence conceptuelle :
La dirence conceptuelle au niveau des mthodes virtuelles, entre C++ et C , se situe dans
le fait quen C , le dveloppeur peut redfinir une mthode virtuelle tout en interdisant
le polymorphisme de sappliquer. Pour cela on doit utiliser le mot-cl new au lieu du motcl override. Si on ne met rien aucun de ces deux mots-cls, le compilateur produira seulement un avertissement du type : vous devriez utiliser new mais lexcution tout se passe
comme si on avait utilis new. Soyez vigilant !
La problmatique
En programmation objet, on est souvent confront au problme suivant : on cre des objets,
instances de plusieurs classes drives dune classe de base, puis on veut leur appliquer un traitement de base, cest--dire, un traitement dfini dans la classe de base. Le problme est que ce
traitement dire selon la classe drive. Par exemple :
On veut obtenir une description de tous les employs (traitement de base : obtenir une
description dun employ, quelle que soit sa catgorie).
On veut dessiner toutes les figures gomtriques (traitement de base : dessiner une figure
gomtrique, quel que soit le type de figure).
On veut envoyer des donnes par lintermdiaire dun point de communication (traitement
de base : envoyer des donnes, quel que soit le protocole de communication sous-jacent).
Toute llgance de cette mthode vient du fait que le traitement de base sapplique sur un
objet, sans connatre prcisment sa classe, on ne le connat que par une rfrence de type
sa classe de base : cest une illustration du polymorphisme.
Ce que nous avons appel traitement de base, sappelle mthode virtuelle. Cest une mthode
dfinie la fois dans la classe de base (prcde du mot cl virtual) et dans la classe drive
(prcde du mot-cl override).
Il existe plusieurs corps pour cette mthode, un dans la classe de base et ventuellement un dans
chacune des classes drives. Lors de lappel de cette mthode sur une rfrence de type classe
de base, le corps adquat est choisi lexcution par le programme. La notion de plusieurs corps
pour une mme mthode se traduit par le mot polymorphisme (plusieurs formes).
Une classe drive nest pas oblige de redfinir le corps dune mthode virtuelle de sa classe de
base. Une classe de base qui contient au moins une mthode virtuelle est une classe polymorphe.
En outre, il est possible de redfinir le corps dune mthode virtuelle avec lutilisation conjointe
des mots-cls override sealed. Dans ce cas, cela signifie que cette mthode virtuelle ne peut
plus tre redfinie dans les classes drives de la classe drive. On parle de mthode finalise.
450
Un exemple
Voici un exemple o lon a :
On peut illustrer ceci avec un diagramme de classe obtenu avec Visual Studio 2005 :
451
452
Reprenons lexemple prcdent et observons leet de lutilisation de new pour redfinir une
mthode virtuelle :
Exemple 12-3 :
public class Employe{
...
}
class Secretaire : Employe{ // Secretaire h
erite de Employe.
...
}
class Technicien : Employe{ // Technicien h
erite de Employe.
public Technicien(string nom):base(nom) {}
public new void DisplayDescription(){
base.DisplayDescription() ;
System.Console.Write( " Fonction : Technicien\n") ;
}
}
class Program {
static void Main(){
Employe[] tableau = new Employe[3] ;
tableau[0] = new Technicien("Line") ;
tableau[1] = new Secretaire("Lisanette") ;
tableau[2] = new Secretaire("Anne-Mette") ;
foreach( Employe employe in tableau )
employe.DisplayDescription();
// Appel de la methode DisplayDescription() de Technicien
((Technicien)tableau[0]).DisplayDescription();
}
}
Voici ce quache ce programme :
Nom : LineNom : Lisanette
Fonction : Secr
etaire
Nom : Anne-Mette
Fonction : Secr
etaire
Nom : Line
Fonction : Technicien
Remarquez que la mthode DisplayDescription() est appele deux fois pour lobjet dcrivant
Line. Cependant la premire fois, il sagit de la mthode DisplayDescription() de Employe
alors que la deuxime fois, cest la mthode DisplayDescription() de Technicien. Dans le
premier cas on voit bien que le polymorphisme a t dsactiv. Dans le deuxime cas, on a
explicitement transtyp notre rfrence de type Employe vers lobjet qui reprsente Line, en une
rfrence de type Technicien. Il ny a donc pas eu l aussi, dapplication de polymorphisme. Le
fait de transtyper explicitement une rfrence de type une classe de base vers une rfrence, de
type classe drive se nomme downcast . Nous reviendrons un peu plus loin sur ce sujet.
Labstraction
C++ C Le concept dabstraction est compltement similaire entre les langages C et C++.
Cependant des dirences de syntaxe apparaissent, et mme de vocabulaire.
Labstraction
453
Les mthodes virtuelles pures du langage C++ sappellent, en langage C , mthodes abstraites
et se dclarent avec le mot-cl abstract en prfixe.
De plus il ne sut plus quun classe abstraite ait au moins une mthode abstraite pour tre
abstraite. Il faut quelle soit dclare avec le mot-cl abstract en prfixe. Contrairement au C++,
en C il peut donc y avoir des classes abstraites sans mthodes abstraites.
Malgr les apparences, le concept dinterface existe en C++ : rien nempche de dclarer une
classe avec seulement des mthodes abstraites et sans aucun champ. Cependant C met en vidence ce concept avec le mot-cl interface. La dirence est quen C , le fait dutiliser le mot-cl
interface oblige les dveloppeurs respecter des contraintes (pas dajout de champ, pas dajout
de corps de mthode etc).
Les interfaces sont trs importantes en C puisquune classe peut implmenter plusieurs interfaces, ce qui permet de combler en partie le manque dhritage dimplmentation multiple.
La problmatique
Dans lexemple de la section prcdente, acher la description dun employ (i.e acher son
nom) a un sens. Il est donc utile de mettre du code dans la mthode virtuelle DisplayDescription() de la classe de base Employe. Lorsquelle est instancie, la classe Employe a quelque chose
acher.
Il arrive que lon nait pas de code mettre dans la mthode virtuelle parce quil y a un manque
dinformation ce niveau de larbre dhritage.
Par exemple pour la classe FigureGeometrique il ny a rien mettre dans la mthode virtuelle
Dessine(). En eet, ce niveau de lhritage on ne sait pas quel type de figure gomtrique on
instancie.
De mme pour la classe PointDeCommunication (aussi dfinie plus haut) il ny a rien mettre
dans la mthode EnvoieUnStream() puisque ce niveau de lhritage on ne connat pas le protocole de communication sous-jacent.
On pourrait trs bien dclarer une fonction virtuelle sans code lintrieur. Mais on sent bien
que lon a besoin de mcanismes plus performants pour dclarer proprement ces classes de base
qui ont un manque dinformation sur ce quelles reprsentent lorsquune de leurs classes drives est instancie. Une telle classe de base veut imposer des oprations ces classes drives,
alors quelle mme na pas assez dinformations pour implmenter ces oprations, mme en
partie.
454
drive dune classe abstraite nimplmente pas toutes les mthodes abstraites, elle est ellemme abstraite.
La consquence immdiate et fondamentale de tout ceci est que : Une classe abstraite nest pas
instanciable.
Il ne peut y avoir dobjets instances dune classe abstraite, mais une rfrence peut avoir pour
type une classe abstraite. Une telle rfrence rfrence alors un objet dune classe drive non
abstraite et lapplication du polymorphisme lors de lappel des mthodes virtuelles et abstraites
est alors automatiquement mise en uvre. Le polymorphisme est encore plus vident lorsquil
est utilis sur des mthodes abstraites puisquon est certain que ce ne peut tre le corps de la
mthode abstraite qui est appele, puisquil nexiste pas !
Remarquez quune mthode abstraite ne doit pas avoir une visibilit prive. En eet, les classes
drives seraient dans limpossibilit de limplmenter.
Un exemple
Voici un exemple o lon a : une classe de base abstraite FigureGeometrique, deux classes drives Cercle et Rectangle, un traitement de base qui est de dessiner la figure.
Ce traitement dire selon la classe. Il est abstrait pour la classe FigureGeometrique (i.e il nest
pas implmentable dans cette classe car il y a un manque dinformations concrtes sur la nature
de la forme gomtrique). Pour un objet de type Cercle, ce traitement excute lachage dun
cercle en fonction du centre et du rayon. Pour un objet de type Rectangle, ce traitement excute
lachage dun rectangle en fonction de trois de ses sommets.
Exemple 12-4 :
class Point{
public Point (int x,int y){this.x = x;this.y = y;}
int x ; int y ;
}
abstract class FigureGeometrique{
// On verifie quune methode abstraite na pas de corps.
public abstract void Dessine();
}
class Cercle : FigureGeometrique {
private
Point
m_Centre ;
private
double
m_Rayon ;
public Cercle(Point centre, double rayon) {
m_Centre = centre ;
m_Rayon = rayon ;
}
public override void Dessine (){
// Dessine un Cercle `a partir de son centre et de son rayon.
}
}
class Rectangle : FigureGeometrique {
private Point m_Sommet1 ;
private Point m_Sommet2 ;
private Point m_Sommet3 ;
Labstraction
455
Exemple 12-5 :
abstract class A { public abstract void Foo();}
abstract class B : A { public abstract override void Foo();}
class C : B { public override void Foo() { /*...*/ } }
Comme lillustre lexemple suivant, lassociation des mots cls abstract et override peut aussi
permettre B de forcer ses classes drives rimplmenter la mthode Foo(), dj implmente par la classe A :
456
Exemple 12-6 :
class A { public virtual void Foo() { /*...*/ } }
abstract class B : A { public abstract override void Foo();}
// La classe C est forcee de reimpl
ementer la m
ethode Foo.
class C : B { public override void Foo() { /*...*/ } }
Les interfaces
C++ C Une interface peut tre vue comme une classe abstraite sans champ avec seulement des mthodes, des proprits, des vnements ou des indexeurs abstraits. Ce concept est
tout fait implmentable en C++, mais C va plus loin en orant la possibilit dutiliser le motcl interface la place du mot-cl class. Cette distinction est importante en C . En eet une
classe peut ventuellement driver de plusieurs interfaces.
C Il existe des classes abstraites trs particulires. Ce sont celles qui nont que des mthodes,
des proprits, des vnements ou des indexeurs abstraits. La thorie de la programmation objet
les appelle interfaces ou bien abstractions. On dit quune classe implmente une interface au lieu
de dire quune classe drive dune interface. On dit aussi quune classe qui implmente une
interface est une implmentation de linterface. Les structures peuvent aussi implmenter des
interfaces mais cette possibilit est dangereuse comme nous lexpliquerons un peu plus loin.
Pour dclarer une interface en langage C , il sut de dclarer une classe avec le mot-cl
interface au lieu du mot-cl class. Une interface ne peut avoir que quatre types de membres :
des mthodes (des spcifications de mthodes), des proprits, des vnements et des indexeurs.
Les niveaux de visibilit ne peuvent sappliquer aux mthodes dune interface. Cest la classe
qui implmente linterface den dcider. En fait, on ne peut utiliser que les mots-cls virtual
et public devant la dclaration dun membre dune interface. Les consquences de ces rgles
dutilisation sont dtailles dans les sections suivantes. Voici un point souligner propos des
interfaces :
Une classe peut ventuellement implmenter plusieurs interfaces (en plus de driver de sa classe
de base). De mme une interface peut driver dune ou plusieurs autres interfaces. On dit dune
telle interface quelle tend les autres interfaces.
Cette possibilit permet de rajouter vos propres comportements une classe, mais aussi des
comportements dfinis par des interfaces standard de larchitecture .NET. Par exemple linterface IEnumerator permet un objet dtre utilis par la structure de contrle de boucle foreach,
comme sil sagissait dun tableau. Dautres interfaces .NET utilisables dans vos classes sont prsentes dans cet ouvrage. Nous vous conseillons de faire commencer le nom de vos interfaces
par un I majuscule, linstar des interfaces standard.
Une rfrence peut tre de type interface. Dans ce cas on ne peut accder lobjet quavec le
comportement spcifique de linterface, par exemple :
Exemple 12-7 :
interface
interface
class C :
public
public
IA { void f(int i) ; }
IB { void g(double d) ; }
IA, IB {
void f(int i) { System.Console.WriteLine("f de C {0}", i) ; }
void g(double d){ System.Console.WriteLine("g de C {0}", d) ; }
Les interfaces
457
}
class Program {
static void Main() {
// Une reference interface sur un objet.
IA obj1 = new C() ;
// Une reference interface sur un objet.
IB obj2 = new C() ;
// Recuperation de lobjet dapr`
es une r
eference interface.
C _obj2 = (C)obj2 ;
obj1.f(5) ;
}
}
Contrairement aux classes et aux structures, les interfaces ne drivent pas de la classe Object.
Cependant comme seules les classes et les structures sont mme dimplmenter une interface,
il est permis dappeler une mthode de la classe Object sur une interface. Lexemple suivant
montre quil est aussi permis de transtyper une rfrence de type interface en rfrence de type
Object.
Exemple 12-8 :
interface I {}
class C : I {}
class Program {
static void Main() {
I c = new C() ;
c.GetHashCode();
object o = c;
}
}
458
Les interfaces
459
f(1)
f(2)
IA.f(3)
IB.f(4)
Une mme classe peut donc avoir plusieurs implmentations pour une mme mthode. Telle
ou telle implmentation sera excute en fonction du type de la rfrence sur laquelle la mthode est appele. Cest une forme de polymorphisme.
Classe Foo
Interface IC
460
rB.fa() ;
rC.fa() ;
}
}
Ce programme ache :
Foo.fa
IA.fa
IB.fa
IC.fa
Les interfaces
461
Ce programme ache :
FooBase.f(1)
FooBase.f(2)
FooDeriv.f(3)
FooDeriv.f(4)
FooDeriv.f(5)
En rgle gnrale il vaut mieux ne pas implmenter dinterface dans les structures. Si vous
navez pas dautres possibilits, il vaut mieux appeler les mthodes de linterface partir
de limplmentation, contrairement ce qui est conseill pour les classes.
462
Pour les plus sceptiques, lanalyse avec ildasm.exe du code IL gnr pour la mthode Main()
montre clairement quil y a une opration de boxing ralise. Notez que les deux autres oprations de boxing sont ncessaires pour pouvoir acher ltat lentier retourn par la mthode
GetState() :
.method private hidebysig static void Main() cil managed {
.entrypoint
// Code size
86 (0x56)
.maxstack 2
.locals ([0] valuetype Struct s,
[1] class IInterface I)
IL_0000: ldloca.s
s
IL_0002: initobj
Struct
IL_0008: ldloc.0
IL_0009: box
Struct // <- cest ici que lop
eration de boxing
//
de la structure a implicitement lieu.
IL_000e: stloc.1
IL_000f: ldloca.s
s
IL_0011: ldc.i4.s
10
IL_0013: call
instance void Struct::SetState(int32)
IL_0018: ldloc.1
IL_0019: ldc.i4.s
20
IL_001b: callvirt
instance void IInterface::SetState(int32)
IL_0020: ldstr
"Retour de s.GetState():"
IL_0025: ldloca.s
S
IL_0027: call
instance int32 Struct::GetState()
IL_002c: box
[mscorlib]System.Int32
IL_0031: call
string
[mscorlib]System.String::Concat(object,object)
IL_0036: call
void [mscorlib]System.Console::WriteLine(string)
IL_003b: ldstr
"Retour de i.GetState():"
IL_0040: ldloc.1
IL_0041: callvirt
instance int32 I::GetState()
IL_0046: box
[mscorlib]System.Int32
IL_004b: call
string
[mscorlib]System.String::Concat(object,object)
IL_0050: call
void [mscorlib]System.Console::WriteLine(string)
IL_0055: ret
} // end of method Program::Main
463
Comme les mthodes virtuelles et abstraites les proprits, les vnements et les indexeurs virtuels et abstraits peuvent tre :
(Re)dfinies dans une classe drive avec le mot-cl override. Dans ce cas le polymorphisme
sapplique sur les accesseurs.
(Re)dfinies dans une classe drive avec les mots-cls override sealed. Dans ce cas le polymorphisme sapplique sur les accesseurs, et le membre concern ne peut tre redfini dans
les classes drives de la classe drive.
Redfinies dans une classe drive avec le mot-cl new. Dans ce cas le polymorphisme ne
sappliquera pas sur les accesseurs.
Comme pour les mthodes abstraites, pour contenir une proprit, un vnement ou un indexeur abstrait, une classe doit tre abstraite. De plus comme les mthodes abstraites, un tel
membre abstrait ne doit pas avoir une visibilit prive. Voici un exemple avec des proprits
virtuelles et abstraites :
Exemple 12-14 :
abstract class FooBase {
protected int valA = 0 ;
public virtual int Prop1 {
get { return valA ; }
set { valA = value ; }
}
public virtual int Prop2 {
get { return 43 ; }
}
public abstract int Prop3 {
get ;
set ;
}
}
class FooDeriv : FooBase {
private int valB = 0 ;
public override int Prop1 {
get { return base.Prop1 * 2 ; }
set { base.Prop1 = value * 2 ; }
}
public override sealed int Prop2 {
get { return valA > valB ? valA : valB ; }
}
public override int Prop3 {
get { return valA + valB ; }
set { valA = value - valB ; }
}
}
Il nest pas ncessaire de faire de la classe FooDeriv une classe abstraite, puisquelle na pas de
membres abstraits. Notez lutilisation du mot-cl base pour appeler les accesseurs dfinis dans
la classe de base.
464
Les oprateurs is et as
C++ C La possibilit de permettre lvaluation ou le transtypage du type dune expression lexcution (permis en C avec les oprateurs is et as) est implmente en C++ avec
loprateur typeid(expression) qui renvoie un objet de type type_info.
Cette possibilit rentre dans le cadre plus gnral du RTTI (RunTime Type Information en anglais, information de type lexcution en franais).
Le RTTI est la fois plus facile utiliser en C quen C++, et incomparablement plus complet,
grce aux mtadonnes de type et au mcanisme de rflexion, prsents en page 233.
Loprateur is
Loprateur is sert dterminer lexcution si une expression peut tre transtype (caste) dans
un type donn. Cet oprateur retourne un boolen. Son oprande de gauche est une expression
et son oprande de droite un type.
Concrtement un objet de classe A drivant dune classe B et implmentant les interfaces I1, I2,
...In, peut tre utilis par lintermdiaire :
Dune rfrence de type, une classe de base situe dans la hirarchie des classes de base de
B.
Dune rfrence de type, une interface supporte par une classe de base situe dans la hirarchie des classes de base de A.
Si la rfrence est nulle loprateur is retourne false. Voici un exemple dutilisation du motcl is :
Exemple 12-15 :
using System ;
interface IA { void f(int i) ; }
interface IB { void g(int i) ; }
abstract class FooBase { public abstract void h(int i) ; }
class FooDeriv : FooBase, IA {
public void f(int i) { /*...*/ }
public override void h(int i){ /*...*/ }
}
class Program {
static void Main() {
IA refA = new FooDeriv() ;
IB refB = null ;
FooBase refAbst = null ;
FooDeriv refC = null ;
// Ici, le transtypage peut se faire.
if ( refA is FooBase ){
refAbst = (FooBase)refA ;
// utilise refAbst...
}
Les oprateurs is et as
465
Faites attention car bien souvent loprateur is est utilis tort, dans des situations o le
polymorphisme aurait vit bien des tests.
Loprateur as
Aprs avoir dtermin lexcution si une expression peut tre transtype (caste) dans un type
donn avec loprateur is, on ralise eectivement le transtypage la plupart du temps. Loprateur as permet deectuer ces deux tapes dun seul coup. Si le transtypage ne peut avoir lieu,
la rfrence nulle est retourne. Il faut donc toujours tester la rfrence retourne.
Lavantage dutiliser loprateur as au lieu de loprateur is lorsque ceci est possible est double :
le code est plus lisible, les performances sont meilleures. Voici la mthode Main() de lexemple
de la section prcdente, rcrite en utilisant loprateur as :
Exemple 12-16 :
...
class Program {
static void Main() {
IA refA = new FooDeriv() ;
IB refB = null ;
FooBase refAbst = null ;
FooDeriv refDeriv = null ;
// Ici, le transtypage peut se faire.
refAbst = refA as FooBase ;
if ( refAbst != null ) {
// utilise refAbst...
}
// Ici, le transtypage peut se faire.
refDeriv = refA as FooDeriv ;
if ( refDeriv != null ) {
// utilise refC...
}
466
13
La gnricit
Sans conteste la gnricit constitue la fonctionnalit phare de .NET 2005 au niveau des langages.
Aprs avoir expos en quoi consiste la gnricit, nous examinerons les implications du support
de la gnricit, au niveau du langage C 2, du CLR et du framework. Sachez demble que les
types et les mthodes gnriques sont CLS compliant et que par consquent, ils sont supports
par tous les langages ciblant le CLR 2.0.
Un problme de C 1 et sa rsolution
par les types gnriques de C 2
Le problme du typage des lments dune collection en C 1
Supposons que nous ayons implmenter une classe Stack (pile en franais) qui permet dempiler et de dpiler des lments. Pour simplifier, nous considrons que la pile ne peut contenir
plus quun certain nombre dlments ce qui nous permet dutiliser en interne un tableau C .
Voici une implmentation de la classe Stack qui satisfait ces contraintes :
Exemple 13-1 :
class Stack{
private object[] m_ItemsArray ;
private int m_Index = 0 ;
public const int MAX_SIZE = 100 ;
public Stack() { m_ItemsArray = new object[MAX_SIZE] ; }
public object Pop() {
if (m_Index ==0 )
throw new System.InvalidOperationException(
"Impossible de depiler un
el
ement dune pile vide.") ;
468
Chapitre 13 : La gnricit
return m_ItemsArray[--m_Index] ;
}
public void Push( object item ) {
if(m_Index == MAX_SIZE)
throw new System.StackOverflowException(
"Impossible dempiler un
el
ement sur une pile pleine.") ;
m_ItemsArray[m_Index++] = item ;
}
}
Premirement, les clients de la classe Stack doivent transtyper explicitement tout lment
obtenu partir de la pile. Par exemple :
...
Stack stack = new Stack() ;
stack.Push(1234) ;
int number = (int)stack.Pop() ;
...
Un deuxime problme moins flagrant se situe au niveau des performances. Il faut tre
conscient que lorsque lon utilise notre classe Stack avec des lments de type valeur, nous
ralisons implicitement une opration de boxing linsertion dun lment et une opration de unboxing la rcupration dun lment. Ce phnomne est mis en vidence par
la version IL du client ci-dessus :
L_0000:
L_0005:
L_0006:
L_0007:
L_000c:
L_0011:
L_0016:
L_0017:
L_0018:
L_001d:
L_0022:
L_0023:
L_0024:
Enfin, un troisime problme vient du fait que lon peut empiler des lments de types diffrents dans une mme instance de la classe Stack. Or, en gnral nous souhaitons avoir des
piles dlments qui partagent un mme type. Cette possibilit peut facilement mener des
problmes de transtypages qui ne sont dcouverts qu lexcution comme dans lexemple
ci-dessous :
...
Stack stack = new Stack() ;
stack.Push("1234");
int number = (int)stack.Pop() ; // Provoque une exception de type
469
// InvalidCastException.
...
Lorsquun problme de transtypage nest pas dtect la compilation mais quil provoque une
exception lexcution, on dit que le code nest pas type-safe. Or, dans le dveloppement logiciel
comme dans toute discipline, plus une erreur est dtecte tt dans le processus de production
moins elle est nuisible. Il faut donc dans la mesure du possible avoir du code type-safe puisque
celui-ci permet la dtection derreurs au plus tt, lors de la compilation.
Il est possible dimplmenter notre concept de pile dune manire type-safe. Nous pourrions en
eet dcider dimplmenter une classe StackOfInt pour dcrire une pile contenant des entiers,
une classe StackOfSring pour dcrire une pile contenant des chanes de caractres etc.
Exemple 13-2 :
class StackOfInt{
private int[] m_ItemsArray ;
private int m_Index = 0 ;
public const int MAX_SIZE = 100 ;
public StackOfInt(){m_ItemsArray = new int[MAX_SIZE];}
public int Pop() { /*...*/ return -1 ; }
public void Push(int item) { /*...*/ }
}
class StackOfString{
private string[] m_ItemsArray ;
private int m_Index = 0 ;
public const int MAX_SIZE = 100 ;
public StackOfString(){m_ItemsArray = new string[MAX_SIZE];}
public string Pop() {/*...*/ return null ; }
public void Push(string item) {/*...*/}
}
Bien quelle soit type-safe et quelle rsolve la fois le problme de transtypage et le problme
de performance cette solution nest clairement pas satisfaisante. Elle implique de la duplication
de code puisque la logique dune pile est implmente par plusieurs classes. Les consquences
sont plus de code maintenir et donc une baisse de la productivit.
470
Chapitre 13 : La gnricit
public Stack(){ m_ItemsArray = new T[MAX_SIZE] ; }
public T Pop(){
if (m_Index ==0 )
throw new System.InvalidOperationException(
"Impossible de depiler un
el
ement dune pile vide.") ;
return m_ItemsArray[--m_Index] ;
}
public void Push(T item) {
if(m_Index == MAX_SIZE)
throw new System.StackOverflowException(
"Impossible dempiler un
el
ement sur une pile pleine.") ;
m_ItemsArray[m_Index++] = item ;
}
}
class Program{
static void Main(){
Stack<int> stack = new Stack<int>();
stack.Push(1234) ;
int number = stack.Pop() ; // Plus besoin de casting.
stack.Push(5678) ;
string sNumber = stack.Pop() ; // Erreur de compilation :
// Cannot implicitly convert type int to string.
}
}
Cette solution est performante car elle nentrane aucune opration de boxing/unboxing.
Le client crit du code type-safe. Il na pas la possibilit davoir lexcution une pile dlments de types dirents. Dans notre exemple, le compilateur interdit toute insertion ou rcupration dun lment dun type dirent que int ou qui nest pas implicitement convertible en int.
Comprenez bien que dans notre exemple la classe gnrique est Stack<T> alors que T est le type
qui paramtre notre classe gnrique. On dit que T est un type paramtre. On utilise parfois le
terme polymorphisme paramtr (parametric polymorphism en anglais) pour dsigner la gnricit.
En eet, notre classe Stack<T> peut prendre plusieurs formes (Stack<int>, Stack<string> etc).
Elle est donc polymorphe et paramtre par un type. Attention, il ne faut pas confondre ceci
avec le polymorphisme des langages objets qui permet de manipuler direntes formes dobjets
(i.e des objets instances de classes direntes) au travers dune mme interface.
En bref, la classe Stack<T> reprsente nimporte quelle pile alors quune pile dobjets est une
pile de nimporte quoi.
471
Les types gnriques ferms engendrs par les templates C++ sont produits par le compilateur C++ et sont contenus dans le composant produit de la compilation.
472
Chapitre 13 : La gnricit
Vue du code source
Une seule classe gnrique
Vue de lassemblage
Une seule classe gnrique
Compilateur C
class Stack<T> {
private T[] m_ItemsArray;
private int m_Index=0;
public const int MAX_SIZE=100;
public Stack() {...}
public void Push(T item) {...}
}
class Program
static void Main() {
Stack<int>
s1=
new Stack<int>();
Stack<bool>
s2=
new Stack<bool>();
Stack<double>
s3=
new Stack<double>();
Stack<string>
s4=
new Stack<string>();
Stack<object>
s5=
new Stack<object>();
Stack<IDisposable>
s6=
new Stack<IDisposable>();
...
}
}
Vue lexcution
Plusieurs classes gnriques
Stack<T>
Program
Stack<int>
Stack<bool>
Stack<double>
Les types gnriques ferms engendrs par la gnricit .NET sont produits lexcution
par le compilateur JIT et le type gnrique sous-jacent nest prsent quen une seule version
dans lassemblage produit de la compilation.
Autrement dit, la notion de type gnrique ouvert existe en C /.NET au niveau du code source,
du composant et du runtime alors quen C++, elle nexiste quau niveau du code source.
Cette remarque souligne clairement un atout la gnricit de C puisque la taille des composants .NET est dautant rduite. Cela nest pas ngligeable puisque le phnomne de gonflement de la taille des composants C++, connu sous le nom de code-bloat, peut tre parfois trs
pnalisant (sans compter lavalanche davertissements produite par certains compilateurs C++).
En outre le modle de programmation par composant propos par .NET est plus puissant avec
cette implmentation de la gnricit puisquun type gnrique ouvert peut tre ferm par un
type dun autre composant.
Il peut y avoir cependant du code-bloat en .NET dans une moindre mesure. En eet, les types
gnriques ferms crs lexcution par le CLR ne sont jamais collects ni par le ramassemiettes ni par une autre entit du CLR. Ils rsident dans leurs domaines dapplication jusqu ce
que celui-ci soit dtruit. Dans certains cas rares rsolubles en dchargeant la main un domaine
dapplication, il peut donc y avoir un encombrement de la mmoire. Un bon point pour la gnricit en .NET est quun type gnrique ferm nest eectivement cr que le plus tard possible,
lorsquil va tre utilis pour la premire fois. De plus, il faut bien tre conscient que le nombre de
classes construites lexcution est forcment born par le nombre de classes gnriques fermes
utilises dans le code source.
473
Un problme similaire plus gnant survient lorsque lon utilise loutil ngen.exe pour amliorer
les performances globales en eectuant le travail du compilateur JIT avant lexcution. Dans ce
cas, tous les types gnriques ferms mentionns dans votre code sources seront crs. Loutil
ngen.exe est dailleurs incapable de distinguer si certains types gnriques ferms mentionns
dans le code source ne seront jamais utiliss.
474
Chapitre 13 : La gnricit
Possibilit de crer des alias sur le nom dun type gnrique ferm
La directive using peut tre utilise pour crer un alias sur le nom dun type gnrique ferm.
La porte dune telle directive est le fichier courant si elle est utilise hors de tous espace de
noms, sinon la porte est lintersection entre le fichier courant et lespace de noms dans lequel
lalias est dfini. Par exemple :
using Annuaire = Dictionary<TelephoneNumber, string>;
class TelephoneNumber { }
class Dictionary<K, V>{ }
...
Annuaire annuaire = new Annuaire();
La contrainte dimplmenter une certaine interface ou (non exclusif) de driver dune certaine classe.
C C++ Le mcanisme de template de C++ na pas besoin de contraintes pour exploiter les
types paramtres puisque les types paramtres sont forcment rsolus au moment de la compilation. Dans ce cas, toute tentative dutilisation dun membre absent est donc dtecte la
compilation.
475
Contraintes de drivation
Si vous souhaitez utiliser certains membres des instances dun type paramtre au sein dun type
gnrique, vous devez lui apposer une contrainte de drivation. Voici un exemple qui illustre
la syntaxe :
Exemple 13-7 :
interface ICustomInterface { int Fct() ; }
class C<U> where U : ICustomInterface {
public int AutreFct(U u) { return u.Fct(); }
}
Vous pouvez apposer plusieurs contraintes dimplmentation dinterfaces et une contrainte de
drivation dune classe de base sur un mme type paramtre. Le cas chant, la classe de base
doit apparatre en premier dans la liste des types. Vous pouvez aussi utiliser conjointement la
contrainte du constructeur par dfaut avec une ou plusieurs contraintes de drivations. Dans ce
cas la contrainte du constructeur par dfaut doit apparatre en dernier :
Exemple 13-8 :
interface ICustomInterface1 { int Fct1() ; }
interface ICustomInterface2 { string Fct2() ; }
class BaseClass{}
class C<U>
where U : BaseClass, ICustomInterface1, ICustomInterface2, new() {
public string Fct(U u) { return u.Fct2() ; }
}
Vous ne pouvez pas utiliser une classe sealed ou une des classes System.Object, System.Array,
System.Delegate, System.Enum ou System.ValueType comme classe de base pour un type paramtre.
Vous ne pouvez pas non plus utiliser les membres statiques de T comme ceci :
Exemple 13-9 :
class BaseClass { public static void Fct(){} }
class C<T> where T : BaseClass {
void F(){
// Erreur de compilation : T is a type parameter,
// which is not valid in the given context.
T.Fct();
}
}
Un type utilis dans une contrainte de drivation peut tre un type gnrique ouvert ou ferm.
Illustrons cette possibilit avec linterface System.IComparable<T>. Rappelons que les types qui
implmentent cette interface peuvent voir leurs instances compares une instance de type T.
476
Chapitre 13 : La gnricit
Exemple 13-10 :
class C1<U> where U : System.IComparable<int> {
public bool Egaux(U u,int i) { return u.Equals(i) ; }
}
class C2<U> where U : System.IComparable<U> {
public int Compare(U u1,U u2) { return u1.CompareTo(u2) ; }
}
class C3<U,V> where U : System.IComparable<V> {
public int Compare(U u, V v) { return u.CompareTo(v) ; }
}
class C4<U, V> where U : System.IComparable<V>, System.IComparable<int>
{ public int Compare(U u, int i) { return u.CompareTo(i) ; }
}
Notez quun type utilis dans une contrainte de drivation doit avoir une visibilit gale ou
suprieure celle du type gnrique qui contient le type paramtre concern. Par exemple :
Exemple 13-11 :
internal class BaseClass{}
// Erreur de compilation : Inconsistent accessibility :
// constraint type BaseClass is less accessible than C<T>
public class C<T> where T : BaseClass{}
Pour pouvoir tre exploites dans un type gnrique, certaines fonctionnalits peuvent vous
obliger apposer certaines contraintes de drivation. Par exemple si vous souhaitez utiliser
un type paramtre T dans une clause catch, vous devez contraindre T driver de la classe
System.Exception ou dune de ses classes drives. De mme si vous souhaitez utiliser le motcl using pour disposer automatiquement une instance dun type paramtre, celui-ci doit tre
contraint dimplmenter linterface System.IDisposable. Enfin, si vous souhaitez utiliser le
mot-cl foreach pour numrer les lments dune instance dun type paramtre, celui-ci doit
tre contraint dimplmenter une des deux interfaces System.Collections.IEnumerable ou
System.Collections.Generic.IEnumerable<T>.
Notons enfin que dans le cas particulier o T est contraint dimplmenter une interface et T est
un type valeur, lappel dun membre de linterface sur une instance de T ne provoque pas de
boxing. Lexemple suivant met en vidence ce phnomne :
Exemple 13-12 :
interface ICompteur{
void Increment() ;
int Val{get;}
}
struct Compteur : ICompteur {
private int i ;
public void Increment() { i++ ; }
public int Val { get { return i ; } }
}
class C<T> where T : ICompteur, new() {
public void Fct(){
477
T t = new T() ;
t.Increment() ; // Modifie l
etat de t.
System.Console.WriteLine( t.Val.ToString() ) ;
// Modifie letat dune copie box
ee de t.
(t as ICompteur).Increment() ;
System.Console.WriteLine(t.Val.ToString()) ;
}
}
class Program {
static void Main() {
C<Compteur> c = new C<Compteur>() ;
c.Fct() ;
}
}
Ce programme ache :
1
1
478
Chapitre 13 : La gnricit
Exemple 13-14 :
interface I1<T> {}
interface I2<T> {}
class C1<U> {
public void Fct1(U u){} // Cette fct ne peut
etre appel
ee si U est int.
public void Fct1(int i){}
public void Fct2(U u1, U u2){}
// Pas dambiguit
e.
public void Fct2(int i, string s){}
public void Fct3(I1<U> a){}
// Pas dambiguit
e.
public void Fct3(I2<U> a){}
public void Fct4(U a){}
// Pas dambiguit
e.
public void Fct4(U[] a){}
}
class C2<U,V> {
public void Fct5(U u, V v){}
// Possibilit
e dambiguit
e si
public void Fct5(V v, U u){}
// le type U = le type V.
public void Fct6(U u, V v){}
// Possibilit
e dambiguit
e si
public void Fct6(V v, U u){}
// le type U = le type V != int .
public void Fct6(int u, V v){}
public void Fct7(int u, V v){}
// Possibilit
e dambiguit
e si
public void Fct7(U u, int v){}
// le type U = le type V = int.
public void Fct8(U u, I1<V> v){} // Possibilit
e dambiguit
e
public void Fct8(I1<V> v, U u){} // par exemple pour c2<I1<int>,int>.
public void Fct9(U u1, I1<V> v2){} // Pas dambiguit
e.
public void Fct9(V v1, U u2){}
public void Fct10(ref U u){}
// Pas dambiguit
e.
public void Fct10(out V v){ v = default(V) ; }
}
class Program {
static void Main(){
C1<int> a = new C1<int>() ;
a.Fct1(34) ; // Appelle
Fct1(int i)
C2<int, int> b = new C2<int, int>() ;
b.Fct5(13, 14) ; // Erreur de compilation : This call is ambiguous.
b.Fct6(13, 14) ; // Appelle Fct6(int u, V v)
b.Fct7(13, 14) ; // Erreur de compilation : This call is ambiguous.
C2<I1<int>,int> c = new C2<I1<int>,int>() ;
c.Fct8(null,null) ; //Erreur de compilation:This call is ambiguous.
}
}
479
pertinente car le fait que les types gnriques ferms ayant des types paramtres rfrences se
partagent la mme implmentation lexcution amne se poser la question. Tout ceci est
illustr par lexemple suivant :
Exemple 13-15 :
using System ;
class C<T> {
private static int m_NInst = 0;
public C() { m_NInst++ ; }
public int NInst { get { return m_NInst ; } }
}
class Program {
static void Main() {
C<int>
c1 = new C<int>() ;
C<int>
c2 = new C<int>() ;
C<int>
c3 = new C<int>() ;
C<string> c4 = new C<string>() ;
C<string> c5 = new C<string>() ;
C<object> c6 = new C<object>() ;
Console.WriteLine( "NInst C<int>
: " + c1.NInst.ToString() ) ;
Console.WriteLine( "NInst C<string> : " + c4.NInst.ToString() ) ;
Console.WriteLine( "NInst C<object> : " + c6.NInst.ToString() ) ;
}
}
Ce programme ache :
NInst C<int>
: 3
NInst C<string> : 2
NInst C<object> : 1
480
Chapitre 13 : La gnricit
Le constructeur statique
Si un type gnrique contient un constructeur statique, celui-ci est appel par le CLR chaque
cration dun de ses types gnriques ferms. Nous pouvons exploiter cette proprit pour ajouter nos propres contraintes sur les types paramtres. Par exemple, on ne peut pas strictement
contraindre un type paramtre ne pas tre le type int. On peut donc profiter du constructeur
statique pour vrifier une telle contrainte comme ceci :
Exemple 13-17 :
using System ;
class C<T> {
static C() {
int a=0;
if( ((object) default(T) != null) && a is T )
throw new ArgumentException("Ne pas utiliser le type C<int>.") ;
}
}
Notez le test de la non nullit de la valeur par dfaut de T. En eet, lexpression (a is T) est
vrai lorsque T est le type object et lorsque T est le type int. Pour liminer le premier cas, nous
comptons sur le fait que lexpression (object)defaut(object) renvoie la valeur nulle.
481
class D<U> {
public static implicit operator U(D<U> val) { return default(U) ; }
}
Dans ce cas deux rgles sont appliques par le CLR :
Si une conversion implicite prdfinie existe du type Src vers le type Dest, alors toute redfinition (implicite ou explicite) de cette conversion est ignore.
Si une conversion explicite prdfinie existe du type Src vers le type Dest, alors toute redfinition de cette conversion est ignore. En revanche, les redfinitions implicites de la
conversion du type Src vers le type Dest sont utilises.
482
Chapitre 13 : La gnricit
Si T na pas de contrainte de type valeur, alors les oprateurs dgalit et dingalit peuvent
tre utiliss entre une rfrence de type T et la rfrence null. Si T prend la forme dun type
valeur, le test dgalit sera faux et le test dingalit sera vrai.
Loprateur typeof
Loprateur typeof utilis sur un type paramtre retourne linstance du type Type correspondant
la valeur courante du type paramtre.
Loprateur typeof utilis sur un type gnrique retourne linstance du type Type correspondant
lensemble des valeurs courantes des types paramtres.
Ce comportement nest pas flagrant puisque la proprit Name des types retourns nache pas
les noms des types paramtres.
Exemple 13-21 :
class C<T>{
public static void PrintTypes(){
System.Console.WriteLine( typeof(T).Name ) ;
System.Console.WriteLine( typeof(C<T>).Name ) ;
System.Console.WriteLine( typeof(C<C<T>>).Name ) ;
if(typeof(C<T>) != typeof(C<C<T>>))
System.Console.WriteLine("Malgr
e un nom similaire ce ne sont" +
" pas les m
emes instances de Type.") ;
}
483
}
class Program {
static void Main() {
C<string>.PrintTypes() ;
C<int>.PrintTypes() ;
}
}
Ce programme ache :
String
C1
C1
Malgre un nom similaire ce ne sont pas les m
emes instances de Type.
Int32
C1
C1
Malgre un nom similaire ce ne sont pas les m
emes instances de Type.
Il nen est pas de mme si lon utilise la proprit FullName :
Exemple 13-22 :
...
public static void PrintTypes(){
System.Console.WriteLine( typeof(C<T>).FullName ) ;
System.Console.WriteLine( typeof(C<C<T>>).FullName ) ;
}
...
Ce programme ache :
C1[[System.String, mscorlib, Version=2.0.0.0, Culture=neutral,
PublicKeyToken=b77a5c561934e089]]
C1[[C1[[System.String, mscorlib, Version=2.0.0.0, Culture=neutral,
PublicKeyToken=b77a5c561934e089]], AsmTest, Version=1.0.0.0,
Culture=neutral, PublicKeyToken=null]]
C1[[System.Int32, mscorlib, Version=2.0.0.0, Culture=neutral,
PublicKeyToken=b77a5c561934e089]]
C1[[C1[[System.Int32, mscorlib, Version=2.0.0.0, Culture=neutral,
PublicKeyToken=b77a5c561934e089]], AsmTest, Version=1.0.0.0,
Culture=neutral, PublicKeyToken=null]]
484
Chapitre 13 : La gnricit
Loprateur default
Dans lexemple de la pile, nous avons considr lopration Pop() sur une pile vide comme une
erreur dutilisation de la classe Stack<T> de la part du client (i.e une violation du contrat prsent par une pile). Nous aurions pu aaiblir le contrat et considrer cette opration comme un
vnement possible. Dans le premier cas, lancer une exception est le traitement adapt. Dans le
second, il serait plus judicieux de retourner un lment vide que le client interprtera comme :
il ny a plus dlment dans ma pile. Cependant nous ne connaissons rien du type T de llment
retourner. Si T est un type rfrence nous souhaiterions retourner une rfrence nulle alors
que si T est le type int nous souhaiterions peut tre retourner 0. Le mot cl default de C 2
permet dobtenir la valeur par dfaut dun type, i.e la rfrence nulle pour un type rfrence ou
un block de mmoire de la taille adquate mis 0 pour un type valeur.
Exemple 13-23 :
class Stack<T>{
...
public T Pop(){
if (m_Index == 0)
return default(T);
return m_ItemsArray[--m_Index] ;
}
...
}
La notion de type nullable constitue une manire plus lgante de dfinir la valeur par dfaut
dun type valeur.
Transtyper implicitement une instance dun type T (si T est de type valeur sinon une rfrence de type T) vers une rfrence de type objet. Si T est de type valeur, il y a une opration
de boxing.
Transtyper explicitement une rfrence de type objet vers une instance dun type T. Si T est
de type valeur, il y a une opration de unboxing.
Transtyper explicitement une instance dun type T vers une rfrence de type une interface
quelconque. Si T est de type valeur, il y a une opration de boxing.
Transtyper explicitement une rfrence de type une interface quelconque vers une instance
dun type T. Si T est de type valeur, il y a une opration de boxing.
Dans les trois derniers cas, si le transtypage est impossible, une exception de type InvalidCastException
est lance.
485
Si T est contraint de driver de la classe C, vous pouvez transtyper implicitement une instance de T en C ou en toute sous-classe de C et vice versa. Si une conversion propritaire
implicite existe de C vers un type A alors le compilateur accepte une conversion implicite
de T vers A. Si une conversion propritaire explicite existe de A vers C alors le compilateur
accepte une conversion explicite de A vers T.
Les oprateurs is et as
Pour viter une exception de type InvalidCastException lorsque vous ntes pas certain dune
conversion de type impliquant un type paramtre T, il est conseill dutiliser loprateur is pour
tester si la conversion est possible et loprateur as pour tenter de raliser la conversion. Rappelons que loprateur as retourne la rfrence null si la conversion est impossible. Par exemple :
486
Chapitre 13 : La gnricit
Exemple 13-25 :
using System.Collections.Generic ;
class C<T> {
public void Fct(T t){
int i = t as int ; // Erreur de compilation :
// The as operator must be used with a reference type.
string s = t as string ;
if( s!= null ) { /*...*/ }
if( t is IEnumerable<int> ){
IEnumerable<int> enumerable = t as IEnumerable<int> ;
foreach( int j in enumerable) { /*...*/ }
}
}
}
Lhritage et la gnricit
Les dirents cas
Une classe non gnrique peut driver dune classe gnrique. Dans ce cas tous les types paramtres doivent tre rsolus :
class B<T> {...}
class D : B<double> {...}
Une classe gnrique peut driver dune classe gnrique. Dans ce cas il est optionnel de rsoudre tous les paramtres. En revanche il est obligatoire de rappeler toutes les contraintes sur
les types paramtres non rsolus. Par exemple :
class B<T> where T : struct { }
class D1<T> : B<T> where T : struct { }
class D2<T> : B<int> { } // Maladroit T est ici un type param
etre
// diff
erent.
class D3<U,V> : B<int> { }
Enfin, sachez quune classe gnrique peut driver dune classe non gnrique.
487
}
class D2<T> : B<T>{
public override T Fct(T t) { return default (T) ; }
}
// Erreur de compilation :
// does not implement inherited abstract member B<U>.Fct(U)
class D3<T, U> : B<U> {
// Erreur de compilation : no suitable method found to override
public override T Fct(T t) { return default(T) ; }
}
On profite de lexemple pour souligner le fait quune classe gnrique peut aussi tre abstraite.
Cet exemple montre aussi le genre derreur de compilation que lon obtient lorsque lon nomme
maladroitement les types paramtres.
Il est intressant de noter que les types paramtres dune classe gnrique drive peuvent tre
utiliss dans le corps dune mthode virtuelle rcrite, mme si la classe de base nest pas gnrique.
Exemple 13-27 :
class B {
public virtual void Fct() { }
}
class D<T> : B where T : new(){
public override void Fct() {
T t = new T();
}
}
Toutes les rgles nonces dans la prsente section restent valables pour limplmentation
dinterfaces ventuellement gnriques, par des classes ou des structures ventuellement gnriques.
488
Chapitre 13 : La gnricit
Exemple 13-28 :
class C1 {
public U Fct<U>(U u) { return u ; }
}
class C2<T> {
public U Fct<U>(U u) { return u ; }
public static U FctStatic<U>(U u) { return u ; }
}
class C3<T> {
// Avertissement de compilation : Type parameter T has same
// name as type parameter from outer type C3<T>.
public T Fct<T>(T t) { return t ; }
}
class Program {
static void Main() {
C1 c1 = new C1() ;
c1.Fct<double>(3.4);
C2<int> c2 = new C2<int>() ;
c2.Fct<double>(3.4);
c2.Fct<string>("hello");
C3<int> c3 = new C3<int>() ;
c3.Fct<double>(3.4) ;
}
}
Cette possibilit nest pas utilisable ni sur les oprateurs, ni sur les mthodes extern ni sur les
mthodes particulires que constituent les accesseurs des proprits, des indexeurs et des vnements.
489
cas dune rcriture dune mthode gnrique virtuelle ou abstraite qui a des contraintes sur
ses types paramtres, vous ne devez pas rcrire lensemble des contraintes. Dans le cas dune
implmentation dune mthode dinterface qui a des contraintes sur ses types paramtres, vous
devez rcrire lensemble des contraintes. Ces rgles sont illustres par lexemple suivant qui
compile sans erreurs ni avertissements :
Exemple 13-30 :
using System ;
abstract class B {
public virtual A Fct1<A, C>(A a, C c) { return a ; }
public abstract int Fct2<U>(U u) where U:class,IComparable<U>,new() ;
}
class D1 : B {
public override X Fct1<X, Y>(X x, Y y) { return x ; }
public override int Fct2<U>(U u) { return 0 ; }
}
interface I {
A Fct1<A, C>(A a, C b) ;
int Fct2<U>(U u) where U : class, IComparable<U>, new() ;
}
class D2 : I {
public X Fct1<X, Y>(X x, Y y) { return x ; }
public int Fct2<U>(U u) where U : class, IComparable<U>, new()
{ return 0 ; }
}
490
Chapitre 13 : La gnricit
// C.Fct1<U>() cannot be inferred from the usage.
string s = C.Fct1() ;
// Erreur de compilation : Cannot implicitly convert type
// System.IDisposable to string.
string s = C.Fct1<System.IDisposable>() ;
s = C.Fct1<string>() ; // OK
C.Fct2("hello") ; // Inf`ere : le type param`
etre U est string.
// Erreur de compilation : The type arguments for
// method C.Fct2<U>(U) cannot be inferred from the usage.
C.Fct2(null) ;
int i = C.Fct3(6) ; // Inf`
ere : le type param`
etre U est int.
double d = C.Fct3(6) ; // ATTENTION : Inf`
ere : le type param`
etre
// U est int et non pas double.
// Erreur de compilation : Cannot implicitly convert int
// to System.IDisposable.
System.IDisposable dispose = C.Fct3(6) ;
// Inf`ere le type param`etre U est string.
C.Fct4("hello", "bonjour") ;
// Erreur de compilation : The type arguments for method
// C.Fct4<U>(U,U) cannot be inferred from the usage.
C.Fct4(5, "bonjour") ;
C.Fct5(new int[6]) ; // Inf`
ere : le type param`
etre U est int.
}
}
>
: ;
491
492
Chapitre 13 : La gnricit
static T
Fct2<T>() { return default(T) ; }
static void Main() {
GenericDelegateA<string> d1 = Fct1 ; // Le compilateur inf
ere
// Fct1<string>.
GenericDelegateB d2 = Fct1 ; // Le compilateur inf
ere Fct1<int>.
GenericDelegateC<string> d3 = Fct2<string> ; // OK mais pas
// dinf
erence.
// Erreur de compilation : The type arguments for
// method Program.Fct2<T>() cannot be inferred from the usage.
GenericDelegateC<string> d4 = Fct2 ;
}
}
Comme lillustre cet exemple, il ny a jamais dinfrence sur les types paramtres dun dlgu
gnrique.
La mthode Handler(Base) a un contrat moins contraignant sur ses entres que celui propos par la dlgation DelegateType(Derived). Une instance de DelegateType peut donc
rfrencer la mthode Handler() sans risque de downcasting illgal. Cest la contravariance.
La mthode Derived Handler() a un contrat plus contraignant sur ses sorties que celui propos par la dlgation Base DelegateType(). L aussi, une instance de DelegateType peut
donc rfrencer la mthode Handler() sans risque de downcasting illgal. Cest la covariance.
493
Exemple 13-37 :
class Base { }
class Derived : Base { }
delegate B DelegateType<B,D>(D d) ;
class Program {
static Derived Handler(Base b){return b as Derived;}
static void Main() {
DelegateType<Base, Derived> delegateInstance = Handler ;
// La reference en entree est implictement cast
ee de Derived vers Base.
// La reference en sortie est implictement cast
ee de Derived vers Base.
Base b = delegateInstance( new Derived() ) ;
}
}
494
Chapitre 13 : La gnricit
Exemple 13-39 :
using System ;
using System.Collections.Generic ;
class Program {
static void Main() {
List<int> list = new List<int>() ;
Type type1 = list.GetType() ;
Type type2 = typeof(List<int>) ;
Type type3 = typeof(List<double>) ;
// type4 represente un type g
en
erique ouvert
Type type4 = type3.GetGenericTypeDefinition();
System.Diagnostics.Debug.Assert(type1 == type2) ;
System.Diagnostics.Debug.Assert(type1 != type3) ;
System.Diagnostics.Debug.Assert(type3 != type4) ;
}
}
La classe System.Type supporte de nouvelles mthodes et proprits ddies la gnricit :
public abstract class Type : System.Reflection.MemberInfo,
System.Runtime.InteropServices._Type,
System.Reflection.IReflect
{
// Utilise lors de la construction dun type avec lAPI Emit
// pour ajouter des types generics param`
etres.
public virtual System.Type MakeGenericType(
params System.Type[] typeArgs) ;
// Obtient les types param`etres quils soient ouverts ou ferm
es.
public virtual System.Type[] GetGenericArguments() ;
// Obtient la forme ouverte dun type g
en
erique
public virtual System.Type GetGenericTypeDefinition() ;
// Retourne true si contient des types param`
etres non pr
ecis
es.
public virtual bool IsGenericTypeDefinition { get ; }
// Retourne true si appelee sur un type g
en
erique ouvert.
// Contrairement `a IsGenericTypeDefinition la recherche dun type
// non precise se fait recursivement sur les tous les types
// param`etres precises.
public virtual bool ContainsGenericParameters { get ; }
// Retourne true si appelee sur un type qui est un type param`
etre
// dun type generique ou dune methode g
en
erique.
public virtual bool IsGenericParameter { get ; }
//--------------------------------------------------------------// Les membres suivants ne peuvent
etre appel
ee que sur les types
// pour lesquels IsGenericParameter est true
// Obtient la position (0 based) dun type param`
etre ouvert.
public virtual int GenericParameterPosition { get ; }
// Obtient la methode generique qui d
eclare le type param`
etre
// ouvert, null si non declaree dans une m
ethode g
en
erique.
public virtual System.Reflection.MethodBase DeclaringMethod { get ; }
495
496
Chapitre 13 : La gnricit
class C<U, V>
where U : Bar, IDisposable, new()
where V : struct {}
static void Main() {
WriteTypeConstraints( typeof(C<Foo,int>) ) ;
WriteTypeConstraints(
typeof(C<Foo,int>).GetGenericTypeDefinition() ) ;
}
}
Ce programme ache :
C2
C2
where U:Bar,IDisposable,new()
where V:struct,ValueType,new()
On saperoit que la contrainte dtre un type valeur oblige le compilateur ajouter les
contraintes de drivation de ValueType et dimplmentation dun constructeur par dfaut.
497
// generique fermee.
public virtual System.Reflection.MethodInfo
GetGenericMethodDefinition() ;
...
}
Le programme suivant montre comment se lier tardivement une mthode gnrique et comment linvoquer aprs avoir rsolu les types paramtres :
Exemple 13-41 :
using System ;
using System.Reflection ;
class Program{
public class Bar{}
public class Foo : Bar, IDisposable{ public void Dispose() { } }
public static void Fct<U, V>()
where U : Bar, IDisposable, new()
where V : struct {
Console.WriteLine(typeof(U).Name);
Console.WriteLine(typeof(V).Name);
}
static void Main() {
Type typeProgram = typeof(Program) ;
MethodInfo methodGenericOpen = typeProgram.GetMethod("Fct",
BindingFlags.Static | BindingFlags.Public) ;
// Resoud les types param`
etres.
MethodInfo methodGenericClosed =
methodGenericOpen.MakeGenericMethod (
new Type[] { typeof(Foo), typeof(int) } ) ;
System.Diagnostics.Debug.Assert (
methodGenericClosed.GetGenericMethodDefinition() ==
methodGenericOpen ) ;
methodGenericClosed.Invoke (
null, // null car m
ethode statique -> pas dinstance
new object[0]) ; // new object[0] car pas de param`
etres
}
}
Ce programme ache :
Foo
Int32
498
Chapitre 13 : La gnricit
Exemple 13-42 :
[System.AttributeUsage(System.AttributeTargets.GenericParameter)]
public class A : System.Attribute{}
class C<[A]U, V> { }
Une classe dattribut ne peut tre une classe gnrique.
Une classe gnrique ne peut driver directement ou indirectement de la classe System.
Attribute.
Une classe dattribut peut utiliser des types gnriques et dfinir des mthodes gnriques :
Exemple 13-43 :
class C<U, V> { }
public class A : System.Attribute{
void Fct1(C<int, string> c) { }
void Fct2<X>() { }
}
La gnricit et le langage IL
Le support de la gnricit implique des changements au niveau du CLR mais aussi, au niveau
du langage IL, au niveau du CTS et au niveau des mtadonnes contenues dans les assemblages.
Dans les corps des mthodes dun type gnrique ou dans le scope dune mthode gnrique, le
langage IL utilise la notation !x pour nommer un type paramtre situ en position x (0 indexe)
dans la liste des types paramtres.
De nouvelles instructions IL ont t rajoute tel que stelem.any ou ldelem.any pour laccs aux
lments dun tableau dlments dun type paramtre (elles viennent ainsi complter la famille
dinstructions stelem.i2, ldelem.i2, stelem.i4, ldelem.i4, stelem.ref, ldelem.ref...). Certaines instructions IL telles que stloc.x ou ldloc.x (qui permettent de manipuler les variables
locales) ne tenaient dj pas compte du type des valeurs manipules. Elles taient donc prtes
pour la gnricit et seule leur interprtation par le compilateur JIT a volu.
Seulement deux tables de mtadonnes sont rajoutes dans la liste des tables de mtadonnes
dun assemblage contenant des types ou des mthodes gnriques :
Les contraintes type valeur/rfrence et constructeur par dfaut ne sont pas stockes dans un
attribut ou dans une table de mtadonnes quelconque. Elles sont tout simplement contenues
dans le nom des types paramtres dans le nom du type ou de la mthode gnrique. Ainsi les
classes suivantes...
class C1<T> where T : new() {...}
class C2<T> where T : class {...}
class C3<T> where T : struct {...}
...sont nommes en IL :
... C1<(.ctor) T> {...}
... C2<(class) T> {...}
... C3<(value type, .ctor, [mscorlib]System.ValueType) T> {...}
499
500
Chapitre 13 : La gnricit
WellKnownObjectMode.SingleCall) ;
...
// cote client
RemotingConfiguration.RegisterActivatedClientType(
typeof(Serveur<int>), url) ;
RemotingConfiguration.RegisterWellKnownClientType(
typeof(Serveur<string>), url) ;
Si vous souhaitez utiliser les fichiers de configuration cot client ou cot serveur, il faut imprativement prciser les types paramtres utiliss :
// c
ote serveur
<service>
<activated
type="ServeurAssembly.Serveur[[System.Int32]],ServeurAssembly"/>
</service>
// c
ote client
<client url="...">
<activated
type="ServeurAssembly.Serveur[[System.Int32]],ServeurAssembly"/>
</client>
La syntaxe avec doubles crochets (double square brackets) permet de prciser une liste de type paramtres :
type="ServeurAssembly.Serveur[[System.Int32],[System.String]],
ServeurAssembly"
La classe System.Activator supporte aussi la gnricit. Sachez juste que lorsque vous utilisez
conjointement cette classe avec un type gnrique, vous ne pouvez pas utiliser les surcharges
des mthodes CreateInstance() et CreateInstanceFrom() dans lesquelles vous devez prciser
les noms des types dans des chanes de caractres.
14
Les mcanismes utilisables dans C
Nous allons voir que C permet de suspendre ponctuellement la gestion du code par le CLR
pour permettre aux dveloppeurs des accs mmoires directs laide de pointeurs. Ainsi avec
C , vous pouvez raliser dune manire standard certaines optimisations qui jusquici ntaient
possibles que dans les environnements non grs tels que C++. Ces optimisations concernent
par exemple le traitement de donnes volumineuses en mmoire, tel que les bitmaps.
linstar des langages C++ et Java, C propose un mcanisme de gestion dexceptions simple et
classique mais puissant.
Nous verrons enfin que le langage C 2 ajoute deux possibilits syntaxiques proches du concept
de programmation fonctionnelle qui, dans certains cas prcis, amliorent grandement la lisibilit du code.
502
Les fuites de mmoire (memory leak en anglais). (Maintenant gr pour la plupart, par le
ramasse-miettes).
Lutilisation dun pointeur invalide. Cette contrainte est rsolue dune manire radicale :
LA MANIPULATION DE POINTEURS EST INTERDITE EN MODE GR.
Cependant, lors de la prsentation du CTS en page 342 nous avons montr que le CLR sait
manipuler trois sortes de pointeurs :
Les pointeurs grs. Ces pointeurs peuvent pointer vers une donne contenue dans le tas des
objets grs par le ramasse-miettes. Ces pointeurs ne peuvent pas tre utiliss explicitement
par du code C . Ils sont cependant exploits implicitement par le compilateur C lorsque
ce dernier compile des mthodes avec des arguments out et ref.
Les pointeurs non grs de fonction. La section en page 272 expose lutilisation de ces pointeurs.
Les pointeurs non grs. Ces pointeurs peuvent pointer vers toute donne contenue dans
la zone dadressage utilisateur du processus. Le langage C permet dutiliser cette sorte de
pointeur dans un mode dexcution spcialement prvu cet eet : le mode dexcution
non vrifiable (parfois nomm mode dexcution non protg). Le code IL mis par le compilateur C correspondant aux zones o vous manipulez des pointeurs non grs contient
des instructions IL spciales. Leur eet sur la mmoire du processus ne peut tre vrifi par
le compilateur JIT du CLR (do le terme zone de code non vrifiable). En consquence, un
individu mal intentionn peut profiter des zones de code non vrifiables pour eectuer des
actions malicieuses. Pour pallier cette faiblesse, lexcution le CLR ne se permet dexcuter
du code non vrifiable que si ce code a la mta permission CAS SkipVerification.
Devant la dclaration dune classe ou dune structure. Dans ce cas le code de toutes les mthodes (statiques ou non) de la classe peut utiliser des pointeurs.
Devant la dclaration dune mthode (statique ou non). Dans ce cas les pointeurs peuvent
tre utiliss dans tout le corps de la mthode.
503
lintrieur du corps dune mthode (statique ou non). Dans ce cas les pointeurs peuvent
tre utiliss dans le bloc de code signal. Par exemple :
unsafe{
...
}
Prcisons que si une mthode accepte au moins un pointeur en argument ou en retour, il faut
que la mthode (ou sa classe) soit non vrifiable, mais aussi que toutes les zones de codes appelant cette mthode soient non vrifiables.
les numrations,
les pointeurs.
504
FooType * pointeur ;
Par exemple :
long * pUnEntier = 0 ;
Notez que la dclaration ...
int * p1,p2 ;
Loprateur sizeof
Loprateur sizeof permet dobtenir la taille, en octets, dun type valeur. Cet oprateur ne peut
tre utilis quen mode non vrifiable. Par exemple :
int i = sizeof(int) // i vaut 4
int j = sizeof(double) // j vaut 8
Loprateur - utilis entre deux pointeurs de mme type T, retourne une valeur de type
long. Cette valeur est gale la dirence doctets entre les deux pointeurs divise par sizeof(T).
Les oprateurs de comparaison peuvent tre utiliss sur deux pointeurs de mme type ou de
types dirents. Rappelons la liste des oprateurs de comparaison :
==
!=
<
>
<=
>=
505
Casting de pointeurs
Les pointeurs en C ne drivent pas de la classe Object. Les oprations de boxing et unboxing
(voir page 350) nexistent donc pas avec les pointeurs. Cependant les pointeurs supportent la
fois le transtypage (casting) explicite et implicite.
Les transtypages implicites se font de nimporte quel type pointeur vers le type de pointeur void*.
Les transtypages explicites se font de :
Nimporte quel type de pointeur vers un des types sbyte, byte, short, ushort, int, uint,
long, ulong (attention, nous ne parlons pas ici des type sbyte*, byte* ,short* etc).
Un des types sbyte, byte, short, ushort, int, uint, long, ulong vers nimporte quel type de
pointeur.
Pointeurs de pointeurs
Notons la possibilit dutiliser un pointeur vers un pointeur (bien que celle-ci soit quasi inutile
en C ). On appelle ceci un double pointeur. Par exemple :
long unLong = 98 ;
long * pUnLong = &unLong ;
long ** ppUnLong = &pUnLong ;
Il est trs important davoir une convention de nommage des pointeurs et des doubles pointeurs. En gnral, pour le nom dun pointeur on a un p minuscule comme premier caractre,
et pp pour un double pointeur.
506
}
//Ici pPrix nexiste plus et lobjet article nest plus
epingl
e.
}
}
Si nous navions pas utilis le mot-cl fixed dans cet exemple, le compilateur aurait produit une
erreur car il sait dtecter que lobjet qui sera rfrenc par la rfrence article sera susceptible
dtre dplac lexcution.
On peut pingler plusieurs objets de mme type, dans une mme clause fixed. Si lon a besoin
dpingler des objets de types dirents il faut utiliser des clauses fixed imbriques.
Il faut pingler des objets le moins souvent possible, et pour le moins longtemps possible. En
eet, lorsque des objets sont pingls, le travail du ramasse-miettes est considrablement moins
ecace.
Les variables de type valeur dclares en tant que variables locales dans une mthode (statique
ou non) nont pas besoin dtre pingls puisquelles ne sont pas prises en compte par le ramassemiettes. En consquence il ne faut pingler que les objets de types pointables , dfinis en tant
que champs (statiques ou non) dune classe (et non dune structure).
En anglais le fait dpingler un objet se dit to pin an object .
507
Voici lachage :
Affichage de 6 elements du tableau oups !!
0
1
4
9
0
2042318948
Affichage de tous les elements du tableau:
0
1
4
9
Notez quil est ncessaire dpingler seulement le tableau et non pas chacun des lments du
tableau. Ceci confirme le fait qu lexcution les lments de type valeur dun tableau sont
stocks dune manire contigu en mmoire.
508
509
Saisie dun paramtre invalide par lutilisateur (une date de naissance en lan 3000 par
exemple).
Ces situations, qui ne sont pas des bugs mais que lon peut appeler erreurs, engendrent cependant un arrt du programme, moins quelles ne soient traites. Pour les traiter on peut tester
les codes derreur retourns par les fonctions, mais ceci prsente deux inconvnients :
Le code devient lourd, puisque chaque appel une fonction est suivi de nombreux tests.
Les tests ne sont pas centraliss. Cela viole le principe de cohrence du code, trs important
en architecture logicielle.
Le programmeur doit prvoir toutes les situations possibles ds la conception du programme. Il doit aussi dfinir les ractions du programme et les traitements eectuer
pour chaque type derreur. Il ne peut pas simplement factoriser plusieurs types derreur en
un seul traitement.
En fait ces inconvnients sont majeurs, et il a fallu trouver une solution ce problme : cest la
gestion des exceptions.
Le CLR, le code du framework .NET ou notre propre code construit un objet qui contient,
ventuellement, des paramtres descriptifs de lerreur. Le dtail de limplmentation dun
tel objet sera prsent un peu plus loin.
Un gestionnaire dexception rattrape lexception. Il lanalyse et a la possibilit dexcuter du code, par exemple pour sauver des donnes ou avertir lutilisateur.
510
511
512
De plus, en tant quobjet C , ce dernier est obligatoirement allou dynamiquement (ce qui nest
pas le cas en C++).
C Une exception est toujours reprsente par un objet. Cet objet contient en gnral des
informations de description relatives au problme qui a provoqu lexception. Cet objet est
obligatoirement une instance dune classe qui drive de la classe System.Exception. Il existe
deux types de classes drivant de System.Exception :
Celles fournies par C /.NET qui sont lance par le systme, mais peuvent aussi tre lances
dans vos propres mthodes (par exemple System.DivideByZeroException vue prcdemment).
Celles que vous dfinissez vous-mme et qui ne peuvent tre lances que dans vos propres
mthodes.
La classe System.Exception
La classe System.Exception contient des proprits que vous pouvez utiliser pour vos propres
classes :
Il est conseill dutiliser ces proprits mais ce nest pas une obligation. Par exemple, dans le cas
des exceptions qui instancient la classe System.DivideByZeroException :
La proprit Source est une chane de caractres gale au nom de lassemblage do lexception est lance.
513
514
515
Dans le mme gestionnaire dexception, le compilateur interdit les clauses catch de classes
drives de B, aprs la dfinition dune clause catch(B b). En eet les clauses catch des
classes drives de B nauraient aucune chance dtre excutes.
La clause catch(System.Exception) rattrape toutes les exceptions puisque toutes les classes
dexception drivent de la classe System.Exception.
Vous avez la possibilit davoir une clause catch vide. Vous navez qu crire le mot-cl
catch suivit directement de laccolade ouvrante du bloc catch. Ce type de clause est quivalent une clause catch(System.Exception).
516
Si un gestionnaire dexception a une clause catch vide celle-ci est forcment en dernire position. Mme remarque pour une clause catch(System.Exception). Si ces deux clauses sont
prsentes, la clause catch vide doit tre en dernire position.
La clause finally
Si la clause finally est prsente, elle se trouve obligatoirement aprs toutes les clauses catch.
La clause finally est un bloc dinstructions excut dans tous les cas possibles qui sont :
La clause finally est en gnral utilise pour librer des ressources critiques (une connexion
avec une base de donnes, un fichier ouvert, etc) indpendamment du fait quune exception
ait t lance ou pas. Notez que si une exception est lance dans un bloc finally (ce qui est dconseill) alors quune exception lance na pas pu tre rattrape par le gestionnaire dexception
courant, elle remplace cette dernire. Par exemple :
Exemple 14-11 :
using System ;
public class Program {
public static void Main(){
try {
try {
throw new ArgumentOutOfRangeException();
}
catch(DivideByZeroException e){
Console.WriteLine("Gestionnaire 1:") ;
Console.WriteLine("Exception: "+e.Message) ;
}
finally {
Console.WriteLine("finally 1") ;
throw new DivideByZeroException();
}
}
catch(Exception e) {
Console.WriteLine("Gestionnaire 2:") ;
Console.WriteLine("Exception: "+e.Message) ;
}
finally {
Console.WriteLine("finally 2") ;
}
}
}
517
Ce programme ache :
finally 1
Gestionnaire 2:
Exception: Attempted to divide by zero.
finally 2
518
}
Console.WriteLine(article.i) ;
}
}
Ce programme ache :
Exception: Specified argument was out of the range of valid values.
2
Comprenez bien que lallocation dune seconde instance de Article, dans le bloc try, a chou
du fait quune exception a t lance sans tre rattrape, dans le constructeur.
519
Le CLR a parfois la responsabilit de rattraper une exception pour en relancer une autre,
considre comme plus significative. Par exemple nous avons vu dans la section prcdente
que le CLR propage une exception de type TypeInitializationException quel que soit le
type de lexception qui a t lance dans le constructeur statique. Notez que dans ce genre
de cas, lexception initialement lance est stocke dans la proprit InnerException de lexception. Cest aussi le cas des exceptions lances partir dune mthode appele avec un
lien tardif explicite.
En page 285 nous expliquons que le CLR transforme les codes derreur HRESULT retourns
par les mthodes des objets COM en exception gre. De mme, lorsquun objet gr est
considr comme un objet COM, le CLR produit un code derreur HRESULT partir dune
exception gre qui remonte dans le code natif. Nous verrons dans la prochaine section
comment le CLR se comporte avec le systme dexception natif de Windows.
520
Exemple 14-14 :
using System ;
using System.Threading ;
public class Program {
public static void Fct() {
try {
Console.WriteLine("Fct() Bloc try.") ;
throw new Exception("Hello from Fct()...") ;
} finally {
Console.WriteLine("Fct() Bloc finally.") ;
}
}
public static void Main() {
Console.WriteLine("Thread{0}: Hello world...",
Thread.CurrentThread.ManagedThreadId ) ;
AppDomain currentDomain = AppDomain.CurrentDomain;
currentDomain.UnhandledException += UnhandledExceptionHandler;
try{
Console.WriteLine("Main() Bloc try.") ;
Fct() ;
}
catch{
Console.WriteLine("Main() Bloc catch.") ;
}
throw new Exception ("Bonjour...") ;
}
static void UnhandledExceptionHandler(
object s, UnhandledExceptionEventArgs e) {
Console.WriteLine("Thread{0}: UnhandledExceptionHandler: {1}",
Thread.CurrentThread.ManagedThreadId ,
(e.ExceptionObject as Exception).Message) ;
// a) Sauvegarder un rapport derreur.
// b) Proposer `a lutilisateur de sauvegarder l
etat courant.
// c) Proposer `a lutilisateur denvoyer automatiquement le
//
rapport derreur `a l
equipe de d
eveloppement.
}
}
En page 125 nous exposons des techniques propres au CLR permettant damliorer la fiabilit dune application susceptible de faire face des exceptions asynchrones.
521
Type de lexception gr
STATUS_FLOAT_INEXACT_RESULT
System.ArithmeticException
STATUS_FLOAT_INVALID_OPERATION
STATUS_FLOAT_STACK_CHECK
STATUS_FLOAT_UNDERFLOW
STATUS_FLOAT_OVERFLOW
System.OverflowException
STATUS_INTEGER_OVERFLOW
STATUS_FLOAT_DIVIDE_BY_ZERO
System.DivideByZeroException
STATUS_INTEGER_DIVIDE_BY_ZERO
STATUS_FLOAT_DENORMAL_OPERAND
System.FormatException
STATUS_ACCESS_VIOLATION
System.NullReferenceException
STATUS_ARRAY_BOUNDS_EXCEEDED
System.IndexOutOfRangeException
STATUS_NO_MEMORY
System.OutOfMemoryException
STATUS_STACK_OVERFLOW
System.StackOverflowException
System.Runtime.InteropServices.
SEHException
Le mcanisme SEH fonctionne avec un modle de filtres dexceptions enregistrs auprs des
threads. Ces filtres permettent de prciser Windows des fonctions natives appeler lorsquune
exception native est dtecte sur un thread. Le CLR enregistre son propre filtre sur chaque
thread excutant du code gr. La fonction native enregistre a la responsabilit de dclencher
lvnement AppDomain.UnhandledException. Si un thread na pas t cr par le CLR (i.e sil
522
Common Language Runtime Exceptions : liste les exceptions gres (trier par espaces de
noms).
Managed Debugging Assistants : liste des vnements problmatiques connus du CLR qui
se traduisent parfois par une exception gre. Ainsi, si vous souhaitez confirmer quune telle
exception est la consquence dun tel vnement ou si vous souhaitez tre inform dun tel
vnement lorsquil ne se traduit pas par une exception vous devez vous servir de cette liste.
Chacun de ces vnements est expliqu dans les MSDN dans larticle Diagnosing Errors
with Managed Debugging Assistants.
Native Run-Time Checks : liste des exceptions critiques qui peuvent survenir dans un programme C/C++.
Visual Studio vous permet notamment dajouter vos propres types dexceptions dans une de ces
listes.
Celles qui surviennent cause dun problme denvironnement dexcution et qui peuvent
tre rgles par une modification de cet environnement (absence dun fichier de configuration, mauvais mot de passe, rseau indisponible, permissions restreintes etc). On parle
dexception applicative.
Celles qui surviennent cause dun problme denvironnement dexcution qui ne peut
tre rsolu. Par exemple, les applications gourmandes en mmoire telles que SQL Server
2005 peuvent tre limites par les 2 ou 3Go despace utilisateur dun processus Windows
32 bits. On parle dexception asynchrone du fait quune telle exception ne dpend pas de la
smantique de la portion de code qui la lev. Pour grer ce genre de problme vous devez
avoir recours des mcanismes complexes prsents en page 125. Cela revient considrer
523
ces situations anormales comme normales ! Soyez conscient que seuls les gros serveurs
qui poussent le systme dans ses retranchement en utilisant au mieux toute la mmoire disponible devraient rencontrer des exceptions asynchrones et donc avoir recours ce type de mcanisme.
Celles qui surviennent cause dun bug et qui ne peuvent tre rgles que par une nouvelle
version corrigeant ce bug.
Soit vous avez faire un rel problme mais vous pouvez y remdier en corrigeant les
conditions qui lont engendr. cette fin, vous pouvez avoir besoin de nouvelles informations (mot de passe invalide redemander lutilisateur...).
Soit vous avez faire un problme que vous ne pouvez rsoudre ce niveau. Dans ce cas la
seule bonne attitude est de relancer lexception. Il se peut quil ny ait plus de gestionnaire
dexception appropri et dans ce cas vous dlguez la dcision prendre lhte du moteur
dexcution. Dans les applications consoles ou fentres ce dernier dcide de faire tomber
le processus tout entier. Notez que vous pouvez avoir recours lvnement AppDomain.
UnhandledException dclench dans cette situation de faon matriser la terminaison du
processus. Vous pouvez alors profiter de cette dernire chance pour sauver des donnes
( la Word) qui sans cela seraient dfinitivement perdues. Dans une application ASP.NET
un mcanisme de traitement des erreurs dcrit en page 904 est mis en place.
En thorie un troisime scnario est envisageable. Il se peut que lexception reue reprsente une fausse alerte. En pratique, ce cas narrive jamais.
Il ne faut pas rattraper une exception pour la loguer puis la relancer. Pour loguer les exceptions
et le code quelles ont traverses, nous vous conseillons davoir recours des techniques non
intrusives telles que lutilisation des vnements spcialiss de la classe AppDomain ou lanalyse
des appels de mthodes sur la pile au moment o lexception a t lance.
Il ne faut pas nettoyer les ressources que lon a allou lorsque lon rattrape une exception.
Dailleurs, soyez conscient quen gnral, seules les ressources non gres sont susceptibles de
vous poser des problmes (fuite de mmoire etc). Ce genre de code de libration de ressources
doit tre plac dans des clauses finally ou dans des mthode Dispose(). En C , les clauses
finally sont souvent implicitement encapsules dans une clause using qui agit sur des objets
qui implmentent linterface IDisposable.
524
En gnral, plus la mthode est profonde, moins elle doit rattraper dexceptions propritaires.
La raison est que les exceptions propritaires ont souvent une signification dans le mtier de
votre application. Ainsi si vous dveloppez une bibliothque de classes, il faut laisser remonter
les exceptions significatives pour les applications clientes de la bibliothque.
Le code est trs peu lisible. En eet, pour comprendre le code il faut faire la main le travail du CLR qui consiste remonter les appels jusqu trouver un gestionnaire dexception.
Mme si vous sparez proprement votre code en couches dappel, le code reste peu lisible.
La gestion dune exception par le CLR est beaucoup plus coteuse que lanalyse dun code
de retour.
La rgle fondamentale nonce en dbut de section peut aussi vous aider dcider : une application qui fonctionne dans des conditions normales dexcution ne lance pas dexception.
Ne pas sous estimer les bugs dont les consquences sont rattrapes
Une utilisation abusive des exceptions arrive lorsque lon se dit que, puisquon rattrape toutes
les exceptions, celles provoques par dventuels bugs seront rattrapes aussi. On se dit quelles
ne provoqueront pas de plantage du programme. Ce raisonnement ne tient pas compte du fait
que les principales nuisances des bugs sont celles qui passent inaperues, comme des rsultats
indtermins, inattendus et faux.
525
}
static void MethodBody() {
System.Console.WriteLine("Hello") ;
}
static void Main() {
DelegateType delegateInstance = GetMethod() ;
delegateInstance() ;
delegateInstance() ;
}
}
Voici le mme code rcrit en C 2 laide dune mthode anonyme :
Exemple 14-16 :
class Program {
delegate void DelegateType() ;
static DelegateType GetMethod() {
return delegate() { System.Console.WriteLine("Hello") ; };
}
static void Main() {
DelegateType delegateInstance = GetMethod() ;
delegateInstance() ;
delegateInstance() ;
}
}
Plusieurs remarques simposent :
Le mot-cl delegate a une nouvelle utilisation en C 2. Il est utilis au sein du corps dune
mthode pour indiquer au compilateur quil doit sattendre trouver le corps dune mthode anonyme.
On constate que lon peut assigner une mthode anonyme un dlgu.
On comprend le nom de mthode anonyme pour cette nouvelle fonctionnalit : La mthode dfinie au sein de GetMethod() na eectivement pas de nom. On peut cependant
linvoquer car elle est rfrence par un dlgu.
Notez aussi que la syntaxe dassignement de plusieurs mthodes un dlgu avec loprateur
+= est utilisable avec les mthodes anonymes :
Exemple 14-17 :
using System ;
class Program{
delegate void DelegateType() ;
static void Main(){
DelegateType delegateInstance = delegate() {
Console.WriteLine("Hello") ; };
delegateInstance += delegate() { Console.WriteLine("Bonjour") ; };
delegateInstance() ;
}
}
526
527
concerne avec lattribut ParamArrayAttribute. Or les mthodes anonymes ne peuvent supporter dattribut.
Exemple 14-19 :
using System ;
class Program {
delegate void DelegateType( params int[] arr ) ;
static DelegateType GetMethod() {
// erreur de compilation : param is not valid in this context.
return delegate(params int[] arr){ Console.WriteLine("Hello");} ;
}
}
528
}
class Program{
static void Main(){
Foo<double> inst = new Foo <double>() ;
inst. Fct(5.5) ;
}
}
En C 2, les dlgations peuvent admettrent des arguments gnriques. Un dlgu instance
dune telle dlgation peut rfrencer une mthode anonyme. Il faut alors rsoudre les types
gnriques lors de la dfinition de la mthode anonyme, comme le montre lexemple suivant :
Exemple 14-22 :
class Program{
delegate void DelegateType<T>( T t );
static void Main() {
DelegateType<double> delegateInstance = delegate(double arg) {
System.Console.WriteLine( "Hello arg:{0}" , arg.ToString()) ;
} ;
delegateInstance(5.5) ;
}
}
529
Exemple 14-24 :
public class FooForm : System.Windows.Forms.Form{
System.Windows.Forms.Button m_Button ;
public FooForm(){
InitializeComponent() ;
m_Button.Click += delegate(object sender, System.EventArgs args){
System.Windows.Forms.MessageBox.Show("m_Button Clicked") ;
} ;
}
void InitializeComponent() {/*...*/}
}
En apparence les mthodes anonymes constituent une possibilit simple ajoute C 2. En apparence seulement : entrons dans les arcanes du compilateur C 2 pour saisir toute la puissance
de ce mcanisme.
530
531
1
2
1
2
Cela peut laisser perplexe car la variable compteur locale la mthode MakeCompteur() semble
survivre linvocation de celle-ci. Cela contredit compltement la notion de variable locale
telle que nous la connaissons. Il semble quil existe deux instances de la variable locale compteur.
Comme nous lavons mentionn, en .NET 2.0 il ny a pas de nouvelles instructions IL pour la
gestion des mthodes anonymes. Ce mystre doit donc forcment provenir du compilateur. Une
analyse de lassemblage produit par le compilateur simpose :
Contrairement la section prcdente, le compilateur ne se contente pas de crer une nouvelle mthode. Il cre une nouvelle classe nomme ici <>c__DisplayClass1.
Cette classe contient aussi un champ dinstance nomm compteur qui garde ltat de la variable locale du mme nom. On dit que la variable compteur est capture par la mthode
anonyme.
532
533
534
Exemple 14-29 :
using System ;
class Program {
delegate void DelegateTypeCompteur() ;
static DelegateTypeCompteur MakeCompteur(string compteurName) {
int compteur = 0 ;
DelegateTypeCompteur delegateInstanceCompteur = delegate{
Console.WriteLine(compteurName + (++compteur).ToString()) ;
} ;
return delegateInstanceCompteur ;
}
static void Main() {
DelegateTypeCompteur compteurA = MakeCompteur("Compteur A:") ;
DelegateTypeCompteur compteurB = MakeCompteur("Compteur B:") ;
compteurA() ;
compteurA() ;
compteurB() ;
compteurB() ;
}
}
Ce programme ache :
Compteur
Compteur
Compteur
Compteur
A:1
A:2
B:1
B:2
Deux restrictions existent cependant quant la capture dun argument par une mthode anonyme. Largument ne doit pas tre out ou ref. Cette consquence est logique puisquun tel argument ne peut plus tre considr comme une variable locale car il survit lexcution de
la mthode.
535
= new CompteurBuilder("Fabrique1") ;
= new CompteurBuilder("Fabrique2") ;
cBuilder1.BuildCompteur("Compteur A:") ;
cBuilder1.BuildCompteur("Compteur B:") ;
cBuilder2.BuildCompteur("Compteur C:") ;
Ce programme ache :
Compteur
Compteur
Compteur
Compteur
Compteur
Compteur
A:1
A:2
B:1
B:2
C:1
C:2
Compteur
Compteur
Compteur
Compteur
Compteur
Compteur
fabrique
fabrique
fabrique
fabrique
fabrique
fabrique
par:Fabrique1
par:Fabrique1
par:Fabrique1
par:Fabrique1
par:Fabrique2
par:Fabrique2
Dcompilons la mthode BuildCompteur() pour bien montrer que la rfrence this est capture :
internal DelegateTypeCompteur BuildCompteur(string compteurName){
CompteurBuilder.<>c__DisplayClass1 class1 = new
CompteurBuilder.<>c__DisplayClass1() ;
class1.<>4__this = this ;
class1.compteurName = compteurName ;
class1.compteur = 0 ;
return new DelegateTypeCompteur(class1.<BuildCompteur>b__0) ;
}
La rfrence this ne peut pas tre utilise dans une mthode anonyme dont le type encapsulant
est une structure (i.e un type valeur). Voici lerreur gnre par le compilateur :
Les methodes anonymes definies dans des structures ne peuvent
acc`eder au membre this. Neanmoins, vous pouvez copier le membre
this dans une variable locale ext
erieure `
a la m
ethode anonyme
et lutiliser `a partir de celle-ci.
536
On peut voir un objet comme un ensemble de donnes auquel des fonctions sont attaches
(par lastuce du passage de la rfrence this en premier argument).
537
On peut voir une fermeture comme une fonction auquel des donnes sont attaches (par
lastuce de la capture des valeurs des variables de lenvironnement lexical).
538
Dlgu et fermeture
En y regardant de plus prs, on saperoit que la notion de dlgu utilis sur une mthode
dinstance en .NET 1.1 est conceptuellement proche de la notion de fermeture. En eet, un
tel dlgu rfrence la fois des donnes (ltat de lobjet cible) et un comportement. Une
contrainte existe cependant : le comportement doit tre une mthode dinstance de la classe
dfinissant le type de la rfrence this.
Cette contrainte est aaiblie en .NET 2005. Grce certaines surcharges de la mthode Delegate.CreateDelegate() vous pouvez maintenant rfrencer le premier argument dune mthode statique dans un dlgu. Par exemple :
Exemple 14-33 :
class Program {
delegate void DelegateType(int writeNTime) ;
// Cette methode est declaree publique pour
eviter des
// probl`emes de reflexion sur membres non publics.
public static void WriteLineNTimes(string s, int nTime) {
for(int i=0;i<nTime;i++)
System.Console.WriteLine(s) ;
}
static void Main() {
DelegateType deleg = System.Delegate.CreateDelegate(
typeof(DelegateType),
"Bonjour",
typeof(Program).GetMethod("WriteLineNTimes")) as DelegateType ;
deleg(4) ;
}
}
Ce programme ache :
539
Bonjour
Bonjour
Bonjour
Bonjour
Notez enfin quen interne limplmentation des dlgus a compltement t revue dans la
version 2.0 du framework et du CLR. La bonne nouvelle est que linvocation de mthodes au
travers de dlgus est maintenant beaucoup plus performante.
Un exemple
Voici un exemple dimplmentation des itrateurs en C 1. La classe Personnes joue le rle
dnumrable tandis que la classe PersonnesEnumerator joue le rle de lnumrateur. La classe
540
IEnumerable
IEnumerator GetEnumerator()
IEnumerator
Client
EnumerableConcret
object Current
bool MoveNext()
EnumeratorConcret
IEnumerator GetEnumerator()
541
class Program {
static void Main() {
Personnes arrPersonnes = new Personnes(
"Michel", "Christine", "Mathieu", "Julien") ;
foreach (string s in arrPersonnes)
System.Console.WriteLine(s) ;
}
}
La syntaxe est un peu lourde au regard de lenvergure de la fonctionnalit. Il est bienvenu que
la syntaxe C 2 simplifie ce point. Ce programme ache :
Michel
Christine
Mathieu
Julien
Il est clair que le compilateur C a interprt le mot-cl foreach comme ceci :
Exemple 14-35 :
...
class Program{
static void Main(){
Personnes arrPersonnes = new Personnes(
"Michel", "Christine", "Mathieu", "Julien") ;
System.Collections.IEnumerator e = arrPersonnes.GetEnumerator();
while (e.MoveNext())
System.Console.WriteLine((string)e.Current) ;
}
}
542
543
}
}
class Program {
static void Main() {
Personnes arrPersonnes = new Personnes(
"Michel", "Christine", "Mathieu", "Julien") ;
foreach (string s in arrPersonnes)
System.Console.WriteLine(s) ;
}
}
Il est clair que la syntaxe est plus claire quen C 1. Cependant laction du mot-cl yield return
doit vous paratre trange. Comment yield return peut retourner un numrateur alors quil
semble retourner une chane de caractres ? Et puis, quelle est limplmentation dun tel numrateur puisque clairement, ce programme ne fournit explicitement aucune classe qui implmente IEnumerator ? Tout deviendra limpide lorsque lon procdera une analyse du travail du
compilateur C 2. Prsentons dabord ltendue des possibilits.
Une mthode peut parfaitement contenir plusieurs fois le mot-cl yield return. Ainsi, nous
pouvons aussi crire :
Exemple 14-38 :
public class Personnes : System.Collections.IEnumerable {
public System.Collections.IEnumerator GetEnumerator() {
yield return "Michel" ;
yield return "Christine" ;
yield return "Mathieu" ;
yield return "Julien" ;
}
}
class Program {
static void Main() {
Personnes arrPersonnes = new Personnes() ;
foreach (string s in arrPersonnes)
System.Console.WriteLine(s) ;
}
}
Daprs les deux exemples prcdents, il semble que lon peut considrer que le programme se
branche juste aprs la dernire instruction yield return chaque itration (et au dbut pour
la premire itration). Nous vrifierons que cette intuition est la bonne.
544
545
546
Christine
Michel
-->Iterateur PosPaires
Michel
Mathieu
-->Iterateur Concat
Julien
Mathieu
Christine
Michel
Michel
Mathieu
547
Les mots-cls yield break et yield return ne peuvent apparatre dans une mthode qui a des
arguments ref ou out. Concrtement, il ne faut pas quune telle mthode puisse retourner autre
chose quun numrateur.
548
D
C
Sens du parcours de
larbre par litrateur
549
550
551
}
class Program {
static void Main() {
Foo collec = new Foo() ;
foreach (int i in collec.UnIterateur())
System.Console.WriteLine(i) ;
}
}
Le compilateur C 2 produit lassemblage suivant partir du code prcdent :
552
itration et au dbut pour la premire itration. Cette magie est possible grce au champ
<>1__state. Le compilateur insre une instruction switch ds le dbut de la mthode pour
que le programme se branche au bon endroit. Le compilateur positionne <>1__state pour la
prochaine itration avant de quitter une itration.
Le champ <>2__current reprsente la valeur calcule par la dernire itration. Il est donc forcment du type de ce quon numre (int32 dans nos deux derniers exemples).
Si notre entit numrable est un objet, i.e si la mthode qui contient une instruction yield
est une mthode dinstance (comme dans le premier cas), le compilateur produit un champ
<>4__this qui rfrence lobjet numrable. Dans le cas contraire aucun champ nest cr.
Enfin, remarquez que si la mthode qui contient une instruction yield a des variables locales ou
des arguments, ceux-ci sont capturs par le compilateur qui en fait alors des champs de la classe
quil cre. Par exemple le champs <i>5__1 capture la variable locale i dans le second exemple.
Ceci est tout fait comparable la capture de lenvironnement lexical au moyen dune fermeture que lon a vu dans le cas des mthodes anonymes. La classe fabrique par le compilateur
est donc bien une fermeture et nul doute que lon va pouvoir exploiter tout ceci pour aller plus
loin que le concept ditration.
Les itrateurs de C 2 supportent le pattern lazy valuation. Cest--dire que les lments de
la collection peuvent ntre produits que si besoin est. Autrement dit, comme un itrateur
a un tat ( linstar dune mthode anonyme), les lments peuvent tre calculs un par un,
au fur et mesure des demandes du client. Ils nont pas besoin dtre rsidents en mmoire.
Les itrateurs de C 2 peuvent itrer sur une collection dlments priori infinie (i.e borne
par les limites de la machine).
Nous allons exploiter ces proprits afin dutiliser les itrateurs dans dautres contextes que litration sur les lments dun numrable.
553
move, resultatCalcul=0
move
move, resultatCalcul=1
move
move, resultatCalcul=2
554
Il faut bien comprendre le rle des champs statiques black et white. Chaque fois que lon appelle
la mthode White(), un nouvel itrateur est cr. Il faut donc nappeler cette mthode quune
seule fois et rfrencer litrateur retourn avec le champ white.
Remarquez aussi que si nous ne limitions pas artificiellement le nombre de coups dans la
mthode Main() (i.e si lon remplaait la boucle for(int i=0 : i<5 ;i++) par la boucle
while(true)) les mthodes White() et Black() sappelleraient en alternance sans fin. Si ces mthodes sappelaient dune manire plus traditionnelle , ce comportement ferait rapidement
exploser la pile du thread. Ici il nen ait rien. Cela vient du fait que la notion de continuation
peut se comprendre aussi comme une sorte de goto inter mthodes (rappelons quen C le
lutilisation du mot-cl goto doit tre confine dans le corps dune mthode).
Le pattern Pipeline
Les itrateurs sont particulirement adapts au pattern pipeline que vous connaissez tous pour
lavoir utilisez dans vos fentres de commande shell. Par exemple :
Exemple 14-46 :
using System.Collections.Generic ;
class Program{
static public IEnumerable<int> PipelineIntRange(int begin,int end) {
System.Diagnostics.Debug.Assert(begin < end) ;
for(int i=begin;i<=end ;i++)
yield return i ;
}
static public IEnumerable<int> PipelineMultiply(int factor ,
IEnumerable<int> input){
foreach (int i in input)
yield return i * factor ;
}
static public IEnumerable<int> PipelineFilterModulo(int modulo ,
IEnumerable<int> input ){
foreach (int i in input)
if( i%modulo == 0 )
yield return i ;
}
static public IEnumerable<int> PipelineJoin(IEnumerable<int> input1,
IEnumerable<int> input2){
foreach (int i in input1)
yield return i ;
foreach (int i in input2)
yield return i ;
}
static void Main(){
foreach (int i in PipelineJoin(
PipelineIntRange(-4, -2), PipelineFilterModulo( 3,
PipelineMultiply( 2,
PipelineIntRange(1, 10) ) ) ) )
System.Console.WriteLine(i) ;
555
}
}
Le programme ache ceci :
-4
-3
-2
6
12
18
On comprend bien -4,-3 et -2 mais il est un peu plus compliqu de saisir le pourquoi du 6,12 et
18. Voici une explication schmatise :
PipelineIntRange(1,10) produit
PipelineMultiply(2) produit
PipelineFilterModulo(3) produit
1
2
6
2
4
3
6
4 5 6 7 8 9 10
8 10 12 14 16 18 20
12
18
556
}
static public IEnumerable<int> PipelineJoin(IEnumerable<int> input1,
IEnumerable<int> input2){
foreach (int i in input1)
yield return i ;
foreach (int i in input2)
yield return i ;
}
static void Main(){
foreach (int i in PipelineJoin(
PipelineIntRange(-4, -2), PipelineFilterModulo( 3,
PipelineMultiply( 2,
PipelineIntRange(1, 10) ) ) ) )
System.Console.WriteLine(i) ;
}
}
...on obtient cette sortie :
Production
-4
Production
-3
Production
-2
Production
Production
Production
6
Production
Production
Production
12
Production
Production
Production
18
Production
de:-4
de:-3
de:-2
de:1
de:2
de:3
de:4
de:5
de:6
de:7
de:8
de:9
de:10
On voit bien qu aucun moment les entiers produits par un maillon du pipeline ne sont stocks
dune quelconque manire. Ds que le producteur initial (i.e PipelineIntRange) cre un entier,
ce dernier est consomm immdiatement. Chaque maillon est un producteur et un consommateur dentier (mis part PipelineIntRange qui nest que producteur dentier).
557
En fait, il est possible dimplmenter certains patterns de concurrence avec les itrateurs de C 2.
Voici un autre exemple simple o un producteur calcule les nombres de la suite de Fibonacci
tandis quun consommateur ache ces valeurs sur la console :
Exemple 14-48 :
using System.Collections ;
using System.Threading ;
public class Program{
static AutoResetEvent eventProducterDone=new AutoResetEvent(false) ;
static AutoResetEvent eventConsumerDone =new AutoResetEvent(false) ;
static int currentFibo ;
static void Fibo(){
int i1 = 1 ;
int i2 = 1 ;
currentFibo = 0 ;
// Le producteur enclanche le m
ecanisme.
eventProducterDone.Set() ;
while(true) {
// On attend que le consommateur ait fini.
eventConsumerDone.WaitOne() ;
// On produit.
currentFibo = i1 + i2 ;
i1 = i2 ;
i2 = currentFibo ;
// On signale que lon a produit.
eventProducterDone.Set() ;
}
}
static void Main() {
Thread threadProducteur = new Thread(Fibo);
threadProducteur.Start() ;
for (int i = 1 ; i < 10 ; i++) {
// On attend que le producteur ait fini.
eventProducterDone.WaitOne();
// On consomme.
System.Console.WriteLine(currentFibo);
// On signale que lon a consomm
e.
eventConsumerDone.Set();
}
}
}
Remarquez que ltat du thread producteur (i.e les valeurs de i1 et i2) est stock tous moment
sur sa pile. Voici la mme problmatique implmente laide dun itrateur :
Exemple 14-49 :
using System.Collections.Generic ;
public class Program {
static IEnumerator<int> Fibo(){
558
int i1 = 1 ;
int i2 = 1 ;
int currentFibo = 0 ;
while (true){
currentFibo = i1 + i2 ;
i1 = i2 ;
i2 = currentFibo ;
// On signale que lon a produit.
yield return currentFibo ;
}
}
static void Main() {
IEnumerator<int> e = Fibo() ;
for (int i = 1 ; i < 10 ; i++){
// On donne la main au producteur pour quil produise.
e.MoveNext() ;
// On consomme.
System.Console.WriteLine(e.Current) ;
}
}
}
Cette fois ci, les valeurs de i1 et i2 sont tous moment stockes dans lnumrateur cr par
lappel la mthode Fibo().
559
Lide est davoir autant de pipeline que de nombres premiers et de faire la cascade sur un premier
itrateur qui produit les entiers de 2 n. Pour valuer le nombre de pipeline ncessaire (i.e le
nombre de nombres premiers entre 2 et n) on se sert dun thorme conjectur par Gauss et
dmontr par de la Valle Poussin qui arme que si P(n) dsigne le nombre de nombres premiers
infrieurs n alors :
P(n)
n
ln n
Voici le programme :
Exemple 14-50 :
using System ;
using System.Collections.Generic ;
class Program {
static public IEnumerable<int> PipelineIntRange(int begin, int end){
System.Diagnostics.Debug.Assert(begin < end) ;
for (int i = begin ; i <= end ; i++)
yield return i ;
}
static public IEnumerable<int> PipelinePrime(IEnumerable<int> input){
using (IEnumerator<int> e = input.GetEnumerator()){
e.MoveNext() ;
int premier = e.Current ;
// Le premier nombre obtenu est forc
ement un premier.
Console.WriteLine(premier) ;
if (premier != 0){
while (e.MoveNext()){
// Elimine tous les multiples de premier.
if (e.Current % premier != 0)
yield return e.Current ;
}
}
}
}
const int N = 100 ;
static void Main() {
// Applique la formule de Gauss/de la Vall
ee Poussin
// pour obtenir le nombre dit
erateur.
int N_PREMIER = (int)Math.Floor( ((double)N)/Math.Log(N) ) ;
// Produit un pipeline de N_PREMIER PipelinePrime
// chaine avec un PipelineIntegerRange.
// Chaque appel `a PipelinePrime produit un it
erateur.
List<IEnumerable<int>> list = new List<IEnumerable<int>>();
list.Add(PipelinePrime( PipelineIntRange(2, N)) ) ;
for( int i=1 ; i<N_PREMIER ; i++ )
list.Add( PipelinePrime(list[i-1]) ) ;
// Traverse toutes les valeurs du dernier it
erateur dans la chaine
// afin de provoquer la cascade des calculs.
560
Le framework .NET
15
Collections
Lutilisation de collections est fondamentale dans le dveloppement. On a trs souvent manipuler des donnes structures en listes, en tableaux, en dictionnaires etc. Ce chapitre prsente
les direntes classes du framework qui rpondent aux dirents besoins des dveloppeurs.
Nous prsentons dabord les similitudes dans la manipulation des dirents types de collections.
Ensuite, nous prsentons les tableaux, leur utilisation en C ainsi que les concepts sous-jacents
de .NET. Puis nous prsentons les collections qui stockent leurs lments dans une squence tel
quune liste, une file dattente ou une pile. Enfin nous prsentons des collections particulires
appeles dictionnaires.
Durant notre expos, nous nous eorons de souligner les dirences entre chaque implmentation afin de vous aider choisir quel type de collection est le mieux adapt chacun de vos
besoins. Vous pouvez aussi consulter larticle Selecting a Collection Class des MSDN lorsque
vous hsitez entre plusieurs implmentations.
Lensemble des types de collections propos par le framework peut savrer assez limit dans le
cadre de certaines applications. Par exemple, il ny a pas dimplmentation des classiques arbres
binaires ou ensemble. En cherchant sur internet, vous trouverez srement des implmentations
directement rutilisables. Notamment, vous pouvez avoir recours au framework open-source Power Collections de lentreprise Wintellect qui a pour but de combler certaines lacunes du framework (http://www.wintellect.com/powercollections/).
564
Chapitre 15 : Collections
Il est de votre responsabilit de veiller ce que la taille dune collection ne change pas durant
le parcours des lments de celle-ci. En dautres termes, vous devez synchroniser les accs vos
collections entre les dirents threads dun programme.
Les tableaux
565
Les tableaux
Rfrences vers un tableau et cration de tableaux
C permet la cration et lutilisation de tableaux une ou plusieurs dimensions. Voici la syntaxe
qui permet de dclarer une rfrence vers un type de tableau :
int [] r1 ;
// r1 est une r
ef
erence vers un tableau dentiers de
int [,] r2 ;
// dimension 1.
// r1 est une r
ef
erence vers un tableau dentiers de
int [,,] r3 ;
// dimension 2.
// r1 est une r
ef
erence vers un tableau dentiers de
// dimension 3.
double [,,,] r4;// r4 est une ref
erence vers un tableau de doubles de
// dimension 4.
Il est essentiel de comprendre que les types int[], int[,], int[,,] et double[,,,] reprsentent
des classes (donc des types rfrence) qui sont fabriques et gres lexcution par le CLR. De
plus, chacune de ces classes drive de la classe abstraite System.Array que nous dcrivons un
peu plus loin. Cela entrane les remarques suivantes :
566
Chapitre 15 : Collections
Lallocation dun tableau ne se fait que lorsque vous utilisez loprateur new. Les lignes de
code ci-dessus nallouent pas un seul tableau. Elles dfinissent quatre rfrences, chacune
vers un type de tableau particulier. Lorsque vous crez un tableau, la taille de chacune des
dimensions doit tre prcise soit statiquement par une constante entire, soit dynamiquement par une variable dun type entier. Par exemple :
Exemple 15-4 :
public class Program {
public static void Main() {
byte i = 2 ;
long j = 3 ;
// t1 est un tableau `a une dimension d
el
ements de type int.
// Il contient 6 elements.
int [] t1 = new int [6] ;
// t2 est un tableau a` deux dimensions d
el
ements
// de type double. Il contient 12
el
ements (j=3 et 3x4=12).
double [,] t2 = new double [j,4] ;
// t3 est un tableau `a 3 dimensions d
el
ements des r
ef
erences de
// type object.Il contient 30
el
ements(j=3 et i=2 et 3x5x2=30).
object [,,] t3 = new object [j,5,i] ;
}
}
Les tableaux sont allous sur le tas et non sur la pile. Lallocation de certains tableaux sur la
pile est possible dans des situations trs particulires (voir page 508).
Chaque type de tableau est driv de la classe object puisque la classe System.Array drive
de la classe object.
On ne copie pas physiquement un tableau avec loprateur daectation = . Avec cet oprateur on ne fait quobtenir une autre rfrence vers le mme tableau.
Le passage dun tableau une mthode se fait toujours par rfrence, mme si le mot-cl
ref nest pas utilis.
C oblige tous les lments dun tableau avoir le mme type. Cependant cette contrainte peut
tre facilement contourne. Il sut de spcifier que les lments sont des rfrences vers une
classe de base (resp. vers une interface), pour quen fait, chaque lment puisse tre une rfrence vers un objet de nimporte quelle classe drive (resp. de nimporte quelle classe qui
implmente linterface).
Les tableaux
567
568
Chapitre 15 : Collections
int [][,,] t1 ;
string [,,][][] t2 ;
double [][][,,][,,,] t3 ;
Comme vous pouvez le constater, la reprsentation mentale des tableaux irrguliers est plus
complique que lide de tableau multidimensionnel. En outre, contrairement aux tableaux
multidimensionnels, les tableaux irrguliers ne sont pas CLS compliant . Les autres langages
ne les implmentent pas ncessairement. Il faut donc restreindre leur utilisation lintrieur
dun assemblage C et ne jamais les mettre en arguments de mthodes exposes par un assemblage. Cependant, lutilisation de tableaux irrguliers est plus performante car le langage IL
contient des instructions spcialement optimises pour la manipulation de tableau unidimensionnel. Lexemple suivant exhibe un facteur doptimisation suprieur 2 :
Exemple 15-5 :
using System;
using System.Diagnostics;
class Program {
const int N_ITERATION = 10000;
const int N_ELEM = 100;
static void Main() {
int tmp = 0;
int[][] arrayJagged = new int[N_ELEM][];
for (int i = 0; i < N_ELEM; i++)
arrayJagged[i] = new int[N_ELEM];
Stopwatch sw = Stopwatch.StartNew();
for (int k = 0; k < N_ITERATION; k++)
for (int i = 0; i < N_ELEM; i++)
for (int j = 0; j < N_ELEM; j++){
tmp = arrayJagged[i][j];
arrayJagged[i][j] = i * j;
}
Console.WriteLine("Tableau irr
egulier: " + sw.Elapsed );
int[,] arrayMultiDim = new int[N_ELEM, N_ELEM];
sw = Stopwatch.StartNew();
for (int k = 0; k < N_ITERATION; k++)
for (int i = 0; i < N_ELEM; i++)
for (int j = 0; j < N_ELEM; j++){
tmp = arrayMultiDim[i, j];
arrayMultiDim[i, j] = i * j;
}
Console.WriteLine("Tableau multidimensionnel:" + sw.Elapsed );
}
}
Enfin notez que le CLR dtecte les dbordements daccs sur les tableaux irrguliers. Par
exemple :
Les tableaux
569
Exemple 15-6 :
class Program{
static void
int[][]
t1[0] =
t1[1] =
t1[2] =
int i =
int j =
}
}
Main() {
t1 = new int[3][] ;
new int[12] ;
new int[5] ;
new int[9] ;
t1[0][7] ; // OK, ici il ny a pas de d
ebordement.
t1[1][7] ; // D
ebordement sur la deuxi`
eme dimension,
// une exception est lanc
ee.
570
Chapitre 15 : Collections
On ne prcise pas la taille de chacune des dimensions puisquelles sont implicitement contenues
dans les tableaux constants. En outre, le compilateur dtecte et sanctionne dune erreur, lcriture dun tableau constant irrgulier :
// Erreur de compilation.
int [][] tabval = { {3,4} , {7,8,9} } ;
La classe System.Array
Comme nous lavons vu, chaque type de tableau est une classe fabrique lexcution par le CLR
qui drive de la classe abstraite System.Array. Ceci permet dutiliser les nombreux membres de
cette classe.
Membres de System.Array
Les proprits de System.Array les plus couramment utilises sont :
Les tableaux
571
d vaut -3.
Certaines surcharges de BinarySearch<T>() acceptent un objet implmentant IComparer<T> pour spcifier lopration de comparaison.
572
Chapitre 15 : Collections
static void ConstrainedCopy(Array src, int srcIndex, Array dest, int destIndex, int length)
Cette nouvelle mthode ralise une copie dun tableau unidimensionnel ou multidimensionnel avec la garantie que le tableau destination nest pas modifi si lopration chouait.
Pour cela elle utilise le mcanisme de rgion dexcution contrainte dcrit en page 125.
Cette mthode a un cot et ne doit tre utilise qu bon escient (dans un contexte multithreads). Dans le cas dune copie dune sous squence dun tableau multidimensionnel, il
faut considrer que vous avez faire des tableaux unidimensionnels avec les lments des
direntes dimensions mis bout bout.
Un peu plus loin, nous dcrivons de nombreuses nouvelles mthodes de la classe System.Array
permettant de manipuler simplement et ecacement les lments dun tableau.
Les tableaux
573
Exemple 15-11 :
public class Program {
static void Main() {
int[] lengths = { 4, 5 } ;
int[] lowerBounds = { -2, 3 } ;
double[,] arrBiDim = System.Array.CreateInstance(
typeof(double), lengths, lowerBounds) as double[,] ;
double d1 = arrBiDim[-2, 5] ; // OK, les indexes sont valides.
double d2 = arrBiDim[0, 0] ; // Lancement de lexception
}
// IndexOutOfRangeException.
}
Le tableau arrayBiDim contient 20 lments (4x5) dont les coordonnes vont de [-2,3] [1,7]
inclus. Lexemple illustre aussi le fait que le CLR dtecte les dbordements ventuels.
Tableaux de bits
La classe System.Collection.BitArray
Vous pouvez utiliser la classe System.Array pour stocker un tableau unidimensionnel de boolens, mais le framework .NET met votre disposition la classe System.Collections.BitArray
spcialement prvue cet eet. Les avantages dutiliser cette classe la place de System.Array
sont multiples :
La classe BitArray a des indexeurs entiers. La syntaxe conviviale daccs aux lments (en
lecture et criture) avec loprateur [] reste possible sur les tableaux de type BitArray.
La classe BitArray admet plusieurs constructeurs trs pratiques pour initialiser des tableaux
de boolens.
BitArray(int)
BitArray(int,bool)
Le premier argument spcifie le nombre de bits dans le tableau de bits. Le second spcifie la valeur initiale des arguments.
BitArray(bool[])
Le tableau de type BitArray est initialis partir dun tableau de boolens de type Array.
BitArray(byte[])
Le tableau de type BitArray est initialis partir dun tableau doctets de type Array.
La classe BitArray admet plusieurs mthodes trs pratiques pour travailler avec des boolens. En voici quelques-unes :
574
Chapitre 15 : Collections
BitArray Not()
Void SetAll(bool)
BitArray And(BitArray)
BitArray Or(BitArray)
BitArray Xor(BitArray)
La structure System.Collections.Specialized.BitVector32
La structure BitVector32 permet de contenir un tableau dexactement 32 bits. Si elle est adapte
vos besoins, prfrez utiliser cette structure plus performante que la classe BitArray. Vous
pouvez construire une instance de BitVector32 partir dune autre instance de BitVector32 ou
partir dun entier. Cette structure prsente un indexeur qui vous permet daccder en lecture
ou en criture un bit en spcifiant sa position dans lintervalle ferm [0,31]. Elle prsente aussi
des oprations spcifiques la manipulation de bits avec des masques ou avec des sections.
Les squences
575
Les squences
Nous prsentons ici des interfaces puis des classes spcifiques pour la manipulation de tableaux
dont la taille peut varier au fur et mesure de lajout ou de la suppression dlments. On appelle de tels tableaux des squences.
Linterface System.Collections.Generic.ICollection<T>
public interface ICollection<T> : IEnumerable<T>
Linterface System.Collections.Generic.ICollection<T> est implmente par toutes les
classes reprsentant des collections. Voici les membres de linterface ICollection<T> :
void Clear()
Supprime tous les lments de la collection.
index spcifie lindice du tableau array partir duquel commence la copie. Si le tableau
destination nest pas assez grand (i.e si la taille de array index est infrieure au nombre
dlments de la collection) lexception System.ArgumentException est lance.
Si les lments sont de type rfrence, ce sont simplement les rfrences qui sont copies et
pas les objets. Cest ce quon appelle une copie superficielle. Voici un exemple o les lments
de type rfrence dune liste, sont copis dans un tableau :
Exemple 15-13 :
using System.Collections.Generic ;
class Article{
public decimal Prix ;
public Article(decimal Prix) { this.Prix = Prix ; }
}
public class Program {
public static void Main() {
576
Chapitre 15 : Collections
Article a1 = new Article(98.5M) ;
Article a2 = new Article(190M) ;
ICollection<Article> collection = new List<Article>() ;
collection.Add(a1) ;
collection.Add(a2) ;
Article[] tableau = new Article[2] ;
// Copie les elements de collection dans tableau.
collection.CopyTo(tableau, 0) ;
tableau[0].Prix = 80M ;
decimal d = a1.Prix ;
// Ici d vaut 80.
}
}
Linterface System.Collections.Generic.IList<T>
public interface IList<T> : ICollection<T>, IEnumerable<T>
Linterface System.Collections.Generic.IList<T> permet de considrer quune collection est
une liste. Linterface IList<T> tend linterface ICollection<T>. Limplmentation de prdilection de cette interface est la classe System.Collections.Generic.List<T>, prsente ci-aprs.
Les membres de linterface IList<T> sont :
Cette mthode insre un lment dans la liste la position reprsente par lindex,
augmentant ainsi la taille de la liste dune unit. Si lindex nest pas valide, lexception
ArgumentOutOfRangeException est lance. Si linsertion choue, en gnral parce que la
liste nest accessible quen lecture seule, lexception NotSupportedException est lance.
Indexeur qui permet daccder en lecture ou en criture llment situ la position spcifie par lindex. Les exceptions ArgumentOutOfRangeException et NotSupportedException
peuvent tre lances pour les mmes raisons nonces dans les mthodes prcdentes.
La classe System.Collections.Generic.List<T>
La classe System.Collections.ArrayList trs utilise dans les applications dveloppes avec
.NET 1.x doit maintenant tre abandonne au profit de la classe System.Collections.Generic.List<T> dfinie comme ceci :
public class List<T> : IList<T>, ICollection<T>, IEnumerable<T>,
IList,
ICollection,
IEnumerable
Les squences
577
Cette classe gnrique est trs optimise. Notamment, lutilisation de List<object> est bien
plus performante que lutilisation de ArrayList.
Contrairement une instance de la classe Array, une instance de List<T> a un nombre dlments variable. Comme dans tout compromis, cet avantage des listes sur les tableaux se paye.
En loccurrence, cet avantage se paye par une demande dallocation mmoire lorsque le nombre
dlments de la liste grandit. En revanche, linstar des tableaux, laccs aux lments dune
liste partir dun index se fait en un temps constant (i.e indpendant du nombre dlments).
Nous verrons dans la prochaine section un autre modle de liste implment par la classe LinkedList<T> o linsertion se fait un temps constant et o laccs aux lments se fait en un temps
proportionnel au nombre dlments. Vous connaissez maintenant les points cls pour dcider
si vous devez utiliser la classe Array, la classe List<T> ou la classe LinkedList<T>, selon vos
besoins spcifiques.
La classe List<T> prsente les constructeurs suivants (la notion de capacit est explique un peu
plus loin dans cette section) :
List<T>()
Dans limplmentation Microsoft, ce constructeur sans argument construit une instance de
capacit 0.
List<T>(int capacity)
Ce constructeur construit une instance de List<T> de capacit capacity.
ArrayList(IEnumerable<T> collection)
Ce constructeur construit une instance de ArrayList avec les lments de la collection numrable collection.
En plus des membres des interfaces IList<T> et ICollection<T>, la classe List<T> prsente les
membres suivants :
T[] ToArray()
Equivalent ICollection<T>.CopyTo(T[],0).
int IndexOf(T value, ...)
int LastIndexOf(T value, ...)
Mme comportement que les direntes surcharges des mthodes IndexOf() et LastIndexOf() de la classe Array., mis part quici ce sont des mthodes dinstance et non des
mthodes statiques.
int BinarySearch(T value, ...)
Mme comportement que les direntes surcharges de la mthode BinarySearch() de la
classe Array.
IList<T> AsReadOnly()
Mmes remarques que pour la mthode AsReadOnly() de la classe Array.
void AddRange(IEnumerable collection)
void InsertRange(int index, IEnumerable collection)
List<T> GetRange(int index, int count)
void RemoveRange(int index, int count)
Ces mthodes permettent dinsrer, dajouter, dobtenir ou de supprimer un ensemble
dlments contigus de la liste sur laquelle elles sont appeles. La dirence entre linsertion
et lajout est que lajout insre les lments la fin de la liste.
578
Chapitre 15 : Collections
Capacite:3
Capacite:3
Capacite:3
Capacite:6
Capacite:6
Capacite:6
Capacite:12
Capacite:12
Capacite:8
Les squences
579
Le constructeur de List<T> que nous utilisons dans lexemple prcdent accepte une valeur
entire qui reprsente la capacit initiale. En outre, il est clair que limplmentation Microsoft
double le nombre dlments chaque fois quune allocation est ncessaire. On dit de la classe
List<T> quelle a un facteur dexpansion (grow factor en anglais) gal 2.
La classe System.Collections.Generic.LinkedList<T>
La classe System.Collections.Generic.LinkedList<T> reprsente le concept de liste doublement lie. Une liste doublement lie est une collection de nuds lis les uns aux autres la faon
des maillons dune chane. Chaque nud est li au nud qui le prcde et au nud qui le suit.
Chaque nud connat la liste lie qui le contient. Chaque nud contient un lment de la liste.
La liste doublement lie ne connat que le premier et le dernier nud. Tout ceci est illustr par
la figure suivante :
Liste doublement lie
Nud 6
Nud 5
Nud 1
Nud 2
Nud 3
Nud 4
580
Chapitre 15 : Collections
public System.Collections.Generic.LinkedListNode<T> Previous { get ; }
public T Value { get ; set ; }
}
Remarquez que les proprits List, Next et Previous ne sont pas accessibles en criture, ce qui
implique que seules les oprations dune liste doublement lie permettent de lier un nud
une liste.
En plus des mthodes de linterface ICollection<T> la classe LinkedList<T> prsente les
membres suivants :
void RemoveHead()
void RemoveTail()
Supprime llment en tte de liste avec RemoveHead() ou en bout de liste avec RemoveTail(). Lexception InvalidOperationException est lance si la liste est vide.
La classe System.Collections.Generic.Queue<T>
La notion de file dattente est souvent utilise en programmation. Une file dattente sert en gnral traiter des messages dans leur ordre darriv, de la mme manire quun commerant sert
ses clients dans lordre dans lequel ils sont arrivs. Le terme classique pour dsigner ce comportement est FIFO (First In First Out que lon peut traduire par premier arriv premier servi).
Les squences
581
linstar dune file dattente chez un commerant, nous parlerons de dbut de file dattente et de
fin de file dattente.
Pour reproduire un tel comportement, il est conseill dutiliser la classe System.Collections.Generic.Queue<T> prvue cet eet et dfinie comme ceci :
public class Queue<T> : ICollection<T>, IEnumerable<T>,
ICollection,
IEnumerable
Cette classe prsente principalement les trois mthodes suivantes :
T Dequeue()
Cette mthode retire llment qui est au dbut de la file dattente et le renvoie. Si la file
dattente est vide, lexception InvalidOperationException est lance.
T Peek()
Cette mthode renvoie llment qui est au dbut de la file dattente sans le retirer. Si la file
dattente est vide, lexception InvalidOperationException est lance.
linstar de la classe List<T>, la classe Queue<T> a une capacit et implmente la mthode TrimToSize() pour minimiser les baisses de performances provoques par les allocations/dsallocations
induites par les changements de taille de la file dattente. La classe Queue<T> prsente aussi un
constructeur qui permet de fixer la capacit initiale.
La classe System.Collections.Generic.Stack<T>
La notion de pile (stack en anglais) est souvent utilise en programmation. Par exemple chaque
thread utilise une pile pour stocker ses donnes de traitement. Une pile sert en gnral traiter
des messages dans lordre de dpt sur la pile, de la mme manire quune pile de dossiers est
traite. En gnral on commence par le dossier du dessus de la pile. Le terme classique pour
dsigner ce comportement est LIFO (Last In First Out que lon peut traduire par dernier arriv
premier servi). linstar dune pile de dossier, nous parlerons du sommet de la pile.
Pour reproduire un tel comportement il est conseill dutiliser la classe System.Collections.Generic.Stack<T> prvue cet eet et dfinie comme ceci :
public class Queue<T> : ICollection<T>, IEnumerable<T>,
ICollection,
IEnumerable
Cette classe prsente principalement les trois mthodes suivantes :
T Pop()
Cette mthode retire llment qui est situ au sommet de la pile et le renvoie. Si la pile est
vide, lexception InvalidOperationException est lance.
T Peek()
Cette mthode renvoie llment qui est situ au sommet de la pile sans le retirer. Si la pile
est vide, lexception InvalidOperationException est lance.
582
Chapitre 15 : Collections
linstar de la classe List<T> et de la classe Queue<T>, la classe Stack<T> dispose dune capacit
et implmente la mthode TrimExcess() pour minimiser les baisses de performances provoques par les allocations/dsallocations induites par les changements de taille de la pile. La classe
Stack<T> prsente aussi un constructeur qui permet de fixer la capacit initiale.
Les dictionnaires
Les dictionnaires sont des collections dont les lments sont des couples cl valeur. Dans un
couple, la cl sert indexer la valeur. Par exemple dans les dictionnaires dune langue, les mots
sont les cls et les dfinitions les valeurs. Dans les classes que nous prsentons dans cette section,
les cls et les valeurs peuvent tre de types quelconques. Prcisons quun dictionnaire ne peut
contenir plusieurs couples cl/valeur ayant la mme cl.
Les deux principales caractristiques dun dictionnaire sont :
La rapidit est cruciale, car on utilise des dictionnaires avant tout pour des raisons de performance. Pour optimiser linsertion et la recherche, deux familles dalgorithmes existent. Dans le
framework .NET ils donnent lieu aux deux implmentations prsentes dans cette section.
Linterface Sytem.Collections.Generic.IDictionary<K,V>
Les deux implmentations de dictionnaires, savoir les classes System.Collections.Generic.SortedDictionary<K,V> et System.Collections.Generic.Dictionary<K,V>, implmentent linterface System.Collections.Generic.IDictionary<K,V> dfinie comme suit :
public interface IDictionary<K, V> : ICollection<KeyValuePair<K, V>>,
IEnumerable<KeyValuePair<K, V>>
Ainsi, un dictionnaire peut tre vu comme une collection dinstances de la structure System.Collections.Generic.KeyValuePair<K,V> dfinie comme suit :
public struct KeyValuePair<K, V>{
public K Key ;
public V Value ;
}
Les mthodes de linterface IDictionary<K,V> sont les suivantes :
Les dictionnaires
583
La classe System.Collections.Generic.SortedDictionary<K,V>
Limplmentation System.Collections.Generic.SortedDictionary<K,V> du concept de dictionnaire, repose sur le fait quil doit exister une relation dordre total sur les cls. Les couples
cl/valeur sont constamment ordonns selon lordre total des cls. Lalgorithme dichotomique
peut ainsi tre appliqu lors de linsertion dun couple cl/valeur et lors de la recherche dune
cl. Lavantage principal de cet algorithme et quil est performant puisquil aboutit en un temps
logarithmique. Cest cet algorithme que lon applique intuitivement lorsque lon recherche un
mot dans un dictionnaire de la langue Franaise par exemple. Dans ce cas, lordre total sur les
cls est lordre alphabtique des mots du dictionnaire. Il nous permet en quelques secondes
de trouver parmi des dizaines de milliers de dfinitions, la dfinition correspondante au mot
cherch.
Clairement, limplmentation de SortedDictionary<K,V> doit tre mme de comparer deux
instances du type K. Si les cls ne supportent pas linterface IComparable<K> ou si le comparateur
par dfaut Comparer<K>.Default ne convient pas, vous pouvez utiliser certains constructeurs de
SortedDictionary<K,V> qui acceptent un paramtre de type IComparer<K>. Cette interface est
dcrite un peu plus loin.
La classe SortedDictionary<K,V> est dfinie comme suit :
public class SortedDictionary<K, V> : IDictionary<K, V>,
ICollection<KeyValuePair<K, V>>, IEnumerable<KeyValuePair<K, V>>,
IDictionary, ICollection, IEnumerable
En plus des membres de linterface IDictionary<K,V>, la classe SortedDictionary<K,V> prsente la mthode bool TryGetValue(K key, out V value) qui retourne une valeur en fonction de sa cl. Si la cl est trouve, la valeur de retour est true. Sinon, elle vaut false.
La classe System.Collections.Generic.Dictionary<K,V>
Limplmentation de la classe System.Collections.Generic.Dictionary<K,V> est base sur le
concept de table de hachage que nous allons dcrire. Ainsi, elle corrige certains problmes potentiels inhrents limplmentation de la classe SortedDictionary<K,V> :
Nous navons pas toujours une relation dordre total sur les cls du dictionnaire.
Si lopration de comparaison de deux cls est une opration coteuse, les performances
dutilisation de SortedList<K,V> sont dgrades.
584
Chapitre 15 : Collections
Linsertion et la recherche dans une table de hachage se font en un temps en gnral
constant (i.e indpendamment du nombre dlments), ce qui est mieux que le temps logarithmique de lalgorithme dichotomique de limplmentation de SortedDictionary<K,V>.
...
...
...
Les dictionnaires
585
enfin, une fois le panier trouv, la recherche squentielle dans le panier sil y a collision.
586
Chapitre 15 : Collections
Linterface System.Collections.Generic.IEqualityComparer<T>
Dans le cas o vous ne pouvez pas (ou ne souhaitez pas) modifier la classe reprsentant les cls
pour introduire votre algorithme de hachage, vous pouvez implmenter linterface IEqualityComparer<K> dans une classe propritaire spcialement prvue cet eet. Cette interface est
dfinie comme suit :
public interface IEqualityComparer<T> {
bool Equals(T x, T y) ;
int
GetHashCode(T obj) ;
}
Certains constructeurs de la classe Dictionary<K,V> acceptent cette interface en argument.
Lalgorithme doit avoir une bonne distribution alatoire afin dviter les collisions.
Deux objets ayant le mme tat doivent avoir la mme valeur de hachage.
Le calcul de la valeur de hachage doit utiliser des champs immuables. Des champs immuables sont des champs initialiss la construction de lobjet, dont la valeur ne change
pas durant la vie de lobjet. Si ce conseil nest pas respect, la valeur de hachage dun mme
objet risque de ne pas tre constante dans le temps.
Un exemple
Voici un exemple ou nous implmentons notre propre algorithme de hachage pour les instances de la classe Personne. Notre algorithme multiplie la valeur du code de hachage de la
chane de caractres du nom de la personne par son anne de naissance. Notez que lon ne craint
pas les dbordements lors de la multiplication car par dfaut, les dpassements de capacit de
multiplication dentiers ne provoquent pas dexception.
Exemple 15-15 :
using System.Collections.Generic ;
class Personne {
public Personne(string nom, int anneeNaissance) {
m_Nom = nom ;
m_AnneeNaissance = anneeNaissance ;
}
public override int GetHashCode() {
return m_AnneeNaissance * m_Nom.GetHashCode();
}
public override bool Equals(object o) {
Personne personne = o as Personne;
if (personne != null)
return (personne.GetHashCode() == GetHashCode());
return false;
}
587
588
Chapitre 15 : Collections
System.Collections.BitArray et System.Collections.Specialized.BitVector32
Il ny a aucun sens trier des bits !
System.Collections.Generic.Queue<T> et System.Collections.Generic.Stack<T>
Ces types de collections sont utiliss car laccs leurs lments est trs particulier (accs
FIFO ou accs LIFO). Le tri dune de ces collections modifierait laccs ses lments, et donc
sa raison dtre.
System.Collections.Generic.LinkedList<T>
Si vous avez trier les lments dune liste lie, mieux vaut utiliser la classe List<T> pour
reprsenter votre liste.
System.Collections.Generic.SortedDictionary<T>
Les lments dune telle collection sont par dfinition constamment tris par rapport
leurs cls. Il nest donc pas ncessaire de prvoir une opration de tri.
System.Collections.Generic.Dictionary<K,T>
Cette implmentation utilise un algorithme bas sur les valeurs de hachages des cls pour
avoir un accs rapide aux lments. Le tri des lments perturberait irrmdiablement lorganisation interne ncessaire la rapidit de ces accs.
Soit la classe implmente une des interfaces System.IComparable ou System.IComparable<T> qui, grce la mthode int CompareTo(T other) permet dordonnancer linstance
courante avec linstance passe en argument.
Soit une autre classe implmente linterface System.Collections.IComparer ou System.Collections.Generic.IComparer<T> pour ordonnancer les instances de la classe
concerne (grce la mthode int Compare(object/T,object/T)). Dans ce cas, cette autre
classe doit tre encapsule dans la classe concerne si la comparaison doit se faire entirement ou partiellement sur des membres privs. Cette autre classe est en gnral drive
de la classe abstraite System.Collections.Generic.Comparer<T> qui, bien videmment,
implmente linterface IComparer<T>.
Signalons enfin que certaines mthodes de comparaison utilisent un dlgu de type System.Comparison<T> pour comparer les lments. Cette dlgation est dfinie comme ceci :
public delegate int System.Comparison<T>(T x, T y) ;
Lorsque vous le pouvez, il est prfrable dutiliser la premire manire. Vous tes cependant
oblig dutiliser une des deux autres manires si vous ne pouvez ou ne souhaitez pas modifier
la classe dont les instances doivent tre compares.
Tous les types primitifs du CTS reprsentant des nombres supportent les interfaces IComparable
et IComparable<T>.
589
Voici un exemple de tri sur un tableau avec des lments dune classe propritaire qui implmente linterface IComparable :
Exemple 15-19 :
using System ;
class Article : IComparable<Article>{
public decimal prix ;
public Article(decimal prix) { this.prix = prix ; }
int IComparable<Article>.CompareTo(Article other){
return prix.CompareTo(other.prix) ;
}
}
public class Program {
public static void Main() {
Article[] tab = { new Article(98M) , new Article(19M) ,
590
Chapitre 15 : Collections
new Article(9.5M) } ;
Array.Sort<Article>(tab) ;
// Ici, tab[0].prix vaut 9.5 ; tab[1].prix vaut 19 ;
// tab[2].prix vaut 98
}
}
591
}
}
void Sort()
void Sort(Comparison<T> comparison)
void Sort(IComparer<T> comparer)
void Sort(int index, int count, IComparer<T> comparer)
Trie une partie des lments dune liste. Cette partie est contenue entre les lments index
et index+count.
System {
delegate
delegate
delegate
delegate
void
bool
U
int
Action<T> ( T obj ) ;
Predicate<T> ( T obj ) ;
Converter<T,U> ( T from ) ;
Comparison<T> ( T x, T y ) ;
Dans lExemple 15-21 nous avons eu loccasion de nous servir dun dlgu instance de Comparison<T> pour trier les lments dun tableau. Lexemple suivant expose quatre dirents
traitements sur des listes (une requte, un calcul, un tri et une conversion), eectus grce
des instances de ces dlgations :
Exemple 15-22 :
using System.Collections.Generic ;
class Program {
class Article {
public Article(decimal prix,string name){Prix=prix ; Name=name;}
public readonly decimal Prix ;
public readonly string Name ;
}
static bool IsEven(int i) { return i % 2 == 0 ; }
592
Chapitre 15 : Collections
static int sum = 0 ;
static void AddToSum(int i) { sum += i ; }
static int CompareArticle(Article x, Article y){
return Comparer<decimal>.Default.Compare(x.Prix, y.Prix) ;
}
static decimal ConvertArticle(Article article){
return (decimal)article.Prix ;
}
static void Main(){
// Recherche de tous les entiers pairs.
// Utilisation implicite dun d
el
egu
e de type Predicate<T>.
List<int> integers = new List<int>() ;
for(int i=1 ; i<=10 ; i++)
integers.Add(i) ;
List<int> even = integers.FindAll( IsEven );
// Somme les elements de la liste dans le champ statique sum.
// Utilisation implicite dun d
el
egu
e de type Action<T>.
integers.ForEach( AddToSum );
// Tri dune liste delements dun type complexe.
// Utilisation implicite dun d
el
egu
e de type Comparison<T>.
List<Article> articles = new List<Article>() ;
articles.Add( new Article(5,"Tongues") ) ;
articles.Add( new Article(3,"Ballon") ) ;
articles.Sort( CompareArticle );
// Cast des elements dune liste d
el
ements dun type complexe.
// Utilisation implicite dun d
el
egu
e de type Converter<T,U>.
List<decimal> artPrix =
articles.ConvertAll<decimal>( ConvertArticle );
}
}
Les lecteurs qui ont eu loccasion dutiliser la Standard Template Library (STL) de C++ reconnaissent la notion de foncteur (fonctor en anglais) aussi nomme fonction-objet. Un foncteur est
un traitement paramtr. Les foncteurs sont particulirement utiles pour eectuer un mme
traitement sur chacun des lments dune collection. En C++ on surchargeait loprateur parenthse pour implmenter la notion de foncteurs. En .NET, un foncteur prend la forme dun
dlgu. En eet, dans le programme prcdent, les quatre dlgus crs implicitement sont
autant dexemples de foncteurs.
593
Exemple 15-23 :
using System.Collections.Generic ;
class Program {
class Article {
public Article(decimal prix,string name){Prix=prix ; Name=name;}
public readonly decimal Prix ;
public readonly string Name ;
}
static void Main(){
// Recherche de tous les entiers pairs.
// Utilisation implicite dun d
el
egu
e de type Predicate<T>.
List<int> integers = new List<int>() ;
for(int i=1 ; i<=10 ; i++)
integers.Add(i) ;
List<int> even =integers.FindAll(delegate(int i){return i%2==0;});
// Somme les elements de la liste.
// Utilisation implicite dun d
el
egu
e de type Action<T>.
int sum = 0 ;
integers.ForEach(delegate(int i) { sum += i ; });
// Tri dune liste delements dun type complexe.
// Utilisation implicite dun d
el
egu
e de type Comparison<T>.
List<Article> articles = new List<Article>() ;
articles.Add(new Article(5,"Tongues")) ;
articles.Add(new Article(3,"Ballon")) ;
articles.Sort(delegate(Article x, Article y){
return Comparer<decimal>.Default.Compare(x.Prix,y.Prix) ; });
// Cast des elements dune liste d
el
ements dun type complexe.
// Utilisation implicite dun d
el
egu
e de type Converter<T,U>.
List<decimal> artPrix = articles.ConvertAll<decimal>(
delegate(Article article) { return (decimal)article.Prix ; });
}
}
594
Chapitre 15 : Collections
public
public
public
public
public
public
public
public
public
public
...
}
public class Array {
public static int FindIndex<T>(T[] array, int startIndex,
int count, Predicate<T> match) ;
public static int FindIndex<T>(T[] array, int startIndex,
Predicate<T> match) ;
public static int FindIndex<T>(T[] array, Predicate<T> match) ;
public static int FindLastIndex<T>(T[] array, int startIndex,
int count, Predicate<T> match) ;
public static int FindLastIndex<T>(T[] array, int startIndex,
Predicate<T> match) ;
public static int FindLastIndex<T>(T[] array, Predicate<T> match) ;
public static T[] FindAll<T>(T[] array, Predicate<T> match) ;
public static T Find<T>(T[] array, Predicate<T> match) ;
public static T FindLast<T>(T[] array, Predicate<T> match) ;
public static bool Exists<T>(T[] array, Predicate<T> match) ;
public static bool TrueForAll<T>(T[] array, Predicate<T> match) ;
public static void ForEach<T>(T[] array, Action<T> action) ;
public static void Sort<T>(T[] array, System.Comparison<T> comparison) ;
public static U[] ConvertAll<T, U>( T[] array,
Converter<T, U> converter) ;
...
}
Itrateurs et collections
Il est ais dtendre ce genre de fonctionnalits tous types de collection grce aux itrateurs
comme le montre le programme suivant (bas sur le pattern pipeline vue en page 554) :
Exemple 15-24 :
using System ;
using System.Collections.Generic ;
class Program{
static public IEnumerable<T> Filter<T> (
Predicate<T> predicate, IEnumerable<T> collection) {
foreach (T item in collection)
595
if (predicate(item))
yield return item ;
}
static public IEnumerable<T> Transform<T> (
Converter<T,T> transformer, IEnumerable<T> collection) {
foreach (T item in collection)
yield return transformer(item) ;
}
static public IEnumerable<U> Converter<T, U> (
Converter<T, U> converter, IEnumerable<T> collection) {
foreach (T item in collection)
yield return converter(item) ;
}
static public IEnumerable<int> PipelineIntRange(int begin, int end) {
System.Diagnostics.Debug.Assert( begin < end ) ;
for ( int i = begin ; i <= end ; i++ )
yield return i ;
}
static void Main(){
int modulo = 3 ;
int factor = 2 ;
foreach (string s in
Converter<int,string>( delegate(int item) {
return "Hello:" + item.ToString() ; },
Filter( delegate(int item) {
return (item % modulo == 0) ; },
Transform( delegate(int item) {
return item * factor ; },
PipelineIntRange(1, 10) ) ) ) )
Console.WriteLine(s) ;
}
}
Ce programme ache :
Hello:6
Hello:12
Hello:18
596
Chapitre 15 : Collections
System.Collections.Generics
System.Collections
Comparer<T>
Comparer
Dictionary<K,T>
HashTable
List<T>LinkedList<T>
ArrayList
Queue<T>
Queue
SortedDictionary<K,T> SortedList<K,T>
SortedList
Stack<T>
Stack
ICollection<T>
ICollection
IComparable<T>
System.IComparable
IComparer<T>
IComparer
IDictionary<K,T>
IDictionary
IEnumerable<T>
IEnumerable
IEnumerator<T>
IEnumerator
IList<T>
IList
16
Bibliothques de classes
Fonctions mathmatiques
La classe System.Math
La classe System.Math ne contient que des champs et des mthodes statiques.
Il y a deux champs statiques : les deux constantes mathmatiques e (champ statique E ) et p
(champ statique PI ). Ces champs sont de type double.
Les mthodes statiques reprsentent lensemble des fonctions mathmatiques classiques, savoir les fonctions trigonomtriques, les fonctions logarithmiques et puissances, les arrondis etc.
Si une valeur dentre est hors de lensemble, la dfinition de la fonction mathmatique correspondante, la valeur double.NaN est retourne mais aucune exception nest lance ( part pour
la mthode Round()). Les comportements de ces fonctions aux limites infinies dcoulent logiquement des comportements des fonctions mathmatiques correspondantes (grce aux valeurs
double.NegativeInfinity et double.PositiveInfinity qui peuvent tre en entre ou en sortie
selon les fonctions).
Voici la liste exhaustive de ces fonctions :
Type Abs(Type a)
598
int Sign(Type a)
Les mthodes trigonomtriques. Tous les angles sont prciss en radians. Si un argument est
hors de lensemble de dfinition dune telle fonction, la valeur double.NaN est retourne.
double Cos(double d)
double Sin(double d)
double Tan(double d)
double Acos(double d)
double Asin(double d)
double Atan(double d)
Retourne larc tangente de d. Lensemble de dfinition est tous les nombres rels et lensemble
image est [ -p/2 ; p/2 ]. p/2 est atteint pour la valeur
double.PositiveInfinity alors que -p/2 est atteint pour
la valeur double.NegativeInfinity.
double Cosh(double d)
Retourne le cosinus hyperbolique de d. Lensemble de dfinition est tous les nombres rels et lensemble image est [
1 ; double.PositiveInfinity]. double.PositiveInfinity
est atteint pour les valeurs double.NegativeInfinity et
double.PositiveInfinity.
double Sinh(double d)
Fonctions mathmatiques
double Tanh(double d)
599
double Sqrt(double d)
double
y)
Retourne x puissance y.
Pow(double x,double
double Exp(double d)
double Log(double d)
Retourne le logarithme nprien du nombre d. Si une valeur ngative est passe, la valeur double.Nan est retourne.
double
b)
Retourne le logarithme en base b du nombre d. Si une valeur ngative est passe pour d ou b, la valeur double.Nan
est retourne.
Log(double d,double
double Log10(double d)
Type Round(Type d)
Type Round(Type d, int n)
Type est un type rel. La mthode Round() permet darrondir un nombre rel la prcision
n. Par exemple :
Math.Round(3.1415)
// retourne 3.0
Math.Round(3.1415,2) // retourne 3.14
double Ceiling(double d)
double Floor(double d)
Ceiling veut dire plafond en anglais, et floor veut dire sol. La mthode Floor() renvoie donc
la partie entire de d, soit le plus grand entier infrieur d. De mme, la mthode Ceiling()
renvoie la partie entire de d +1, soit le plus petit entier suprieur d.
600
La classe System.Random
Lutilisation de la classe System.Random permet de gnrer des nombres alatoires. Les mthodes
de cette classe ne sont pas statiques. Autrement dit, cette classe doit tre instancie pour tre
utilise. Les mthodes de cette classe sont :
Random()
Constructeur dun objet Random. La valeur de base de lalgorithme de gnration alatoire (aussi appele graine ou
seed en anglais) est calcule partir de lheure.
Random(int seed)
Constructeur dun objet Random. La valeur de base de lalgorithme de gnration alatoire est seed.
int Next()
int Next(int a)
double NextDouble()
601
La structure System.DateTime
Les dates reprsentes par les instances de System.DateTime sont dans lintervalle de temps compris entre les deux dates suivantes :
Dans une instance de System.DateTime, en interne, la date est reprsente par un entier de type
long (un entier sign sur huit octets). La correspondance est dune unit pour un intervalle de
temps de 100 nanosecondes. Cet entier est accessible en lecture/criture avec la proprit non
statique Ticks. Cet entier est compris entre les deux champs statiques constants MinValue et
MaxValue de type DateTime. Les deux entiers correspondants aux dates extrmales cites plus
haut sont 0 et 3.155.378.975.999.999.999.
Dans tout louvrage nous ne considrons que le calendrier grgorien, utilis dans le monde
occidental. Sachez que lespace de noms System.Globalization contient en plus de la classe
GregorianCalendar, dautres classes qui reprsentent dautres calendriers comme HebrewCalendar,
JapaneseCalendar, JulianCalendar etc.
La classe DateTime comprend de nombreux membres, statiques ou non. La liste exhaustive de
ces membres se trouve larticle DateTime Members des MSDN. Nanmoins, nous vous prsentons quelques-uns de ces membres les plus couramment utiliss :
Les constructeurs :
DateTime(int annee, int mois, int jour)
DateTime(int annee, int mois, int jour, int heure, int minute, int seconde)
DateTime(int annee, int mois, int jours, int heure, int minute, int seconde,
int millisec)
DateTime(long Ticks)
annee est entre 1 et 9999. mois est entre 1 et 12. jour est entre 1 et 28, 29, 30 ou 31 (en
fonction du mois). heure est entre 0 et 23. minute est entre 0 et 59. seconde est entre 0 et
59. millisec est entre 0 et 999.
Les reprsentations du temps courant (trs utiles, notamment pour les tests de performances) :
Cette proprit statique accessible en lecture seulement retourne une instance de DateTime qui reprsente la date et
lheure courante. La prcision est de lordre du centime de
seconde.
602
DateTime Date
int Day
DayOfWeek DayOfWeek
int DayOfYear
int Hour
int Millisecond
int Minute
int Month
int Second
TimeSpan TimeOfDay
int Year
DateTime Add(TimeSpan
dT)operator+
TimeSpan Subtract(DateTime
date)operator-
Soustrait date la date reprsente par linstance. Loprateur --- est en gnral prfr. Remarquez que la dure
retourne peut tre ngative.
603
Affichage
"d"
15/10/2002
"D"
"f"
"F"
"g"
15/10/2002 14:30
"G"
15/10/2002 14:30:00
"M"
15 octobre
"R"
"s"
"t"
14:30
"T"
14:30:00
"u"
2002-10-15 14:30:00Z
"U"
mardi 15 octobre 2002 12:30:00 Temps universel : cette date la France a deux
heures davance sur le temps universel.
"y"
octobre 2002
Affichage
604
yy
yyy
MM
MMM
Mois, reprsent par une abrviation de trois lettres dans la langue dinstallation
de Windows (Par exemple oct.).
MMMM
dd
ddd
Jour, reprsent par une abrviation de trois lettres dans la langue dinstallation
de Windows (Par exemple dim.).
dddd
hh
HH
mm
ss
Certains indicateurs comme y ou d ont donc un double sens. Si cest le seul caractre de la chane,
le framework choisira la reprsentation gnrale de la date.
605
Affichage
"y/M/d h:m:s"
2/10/15 2:30:0
"yy/MM/dd HH:mm:ss"
02/10/15 14:30:00
30 30inue0 0 0econ15e0
30 minutes 0 secondes
606
La structure System.TimeSpan
Les instances de la structure System.TimeSpan reprsentent une dure. On montre dans la section prcdente que de telles instances peuvent tre obtenues partir doprations sur des dates
(par exemple une soustraction dune date une autre pour savoir quelle est la dure entre deux
dates).
La reprsentation interne de la dure dans une instance de TimeSpan est la mme que pour une
instance de DateTime. La seule dirence est quune dure peut tre ngative.
La classe TimeSpan comprend de nombreux membres, statiques ou non. La liste exhaustive de
ces membres se trouve larticle TimeSpan Members des MSDN. Nanmoins nous vous prsentons quelques-uns de ces membres les plus couramment utiliss :
Les constructeurs :
TimeSpan(int jours, int heures, int minutes, int secondes)
TimeSpan(int heures, int minutes, int secondes)
TimeSpan (long Ticks)
Tous les arguments peuvent prendre une valeur positive ou ngative quelconque.
int Days
double TotalDays
607
int Hours
double TotalHours
Nombre dheures contenues dans la dure. La partie fractionnaire correspond la fraction de lheure non pleine.
int Minutes
double TotalMinutes
Nombre de minutes contenues dans la dure. La partie fractionnaire correspond la fraction de la minute non pleine.
int Seconds
double TotalSeconds
Nombre de secondes contenues dans la dure. La partie fractionnaire correspond la fraction de la seconde non pleine.
int Milliseconds
double TotalMilliseconds
Nombre de millisecondes contenues dans la dure. La partie fractionnaire correspond la fraction de la milliseconde non pleine.
long Ticks
Nombre dintervalles de 100 nanosecondes contenus dans la dure. Cest donc la reprsentation interne de la dure.
TimeSpan Duration()
Renvoie la dure en valeur absolue dun intervalle de temps.
Les oprateurs + - != == < <= > >= sont redfinis et permettent de manipuler simplement et
logiquement les instances de TimeSpan.
Manipulation de volumes
Le framework .NET prsente la classe System.IO.DriveInfo qui permet de reprsenter un volume (drive en anglais). Cette classe prsente la mthode statique DriveInfo[] GetDrives() qui
608
permet de rcuprer tous les volumes disponibles de la machine. Lorsque vous disposez dune
instance de DriveInfo, les proprits suivantes permettent dobtenir des informations relatives
au volume sous-jacent :
Proprits de DriveInfo
Fonctionalit
long AvailableFreeSpace{get;}
long TotalFreeSpace{get;}
string DriveFormat{get;}
DriveType DriveType{get;}
Retourne une valeur de lnumration DriveType dcrivant le type de volume (CDRom, Fixed, Removable
etc).
bool IsReady{get;}
string Name{get;}
directoryInfo
tory{get;}
RootDirec-
long TotalSize{get;}
string VolumeLabel{get;set;}
Cette classe admet aussi un constructeur acceptant une chane de caractres prcisant le label
du volume que lon souhaite reprsenter.
Manipulation de rpertoires
Le framework .NET prsente les deux classes System.IO.Directory et System.IO.DirectoryInfo
qui permettent de manipuler les rpertoires (directory en anglais) dun systme dexploitation. La
plupart des fonctionnalits de ces deux classes sont identiques. En fait, elles prsentent chacune
une faon de travailler avec les rpertoires :
Directory ne contient que des membres statiques. Elle permet de travailler avec les rpertoires sans avoir instancier dobjet.
DirectoryInfo ne contient que des membres non statiques. Pour travailler avec la classe
DirectoryInfo, il faut linstancier. Chaque instance de DirectoryInfo reprsente un rpertoire. La classe DirectoryInfo est drive de la classe System.IO.FileSystemInfo qui
reprsente un fichier au sens large, cest--dire un fichier ou un rpertoire.
Ces classes sont trs compltes. Voici quelques-unes unes des fonctionnalits quelles prsentent.
Pour la liste exhaustive des membres de ces classes, rfrez-vous aux MSDN :
609
Fonctionalit
NameAttributesLastAccessTimeLastWriteTimeCreationTime
string Directory.GetCurrentDirectory()void
Directory.SetCurrentDirectory(string)
DirectoryInfo Directory.CreateDirectory(string)DirectoryInfo
Directory.CreateSubdirectory(string)
string[] Directory.GetDirectories(string)
DirectoryInfo[] DirectoryInfo.GetDirectories()
string[] Directory.GetFiles(string)
leInfo[] DirectoryInfo.GetFiles()
Fi-
DirectoryInfo.Refresh()
Voici un petit exemple permettant de lister larborescence des sous rpertoires du rpertoire
courant de lapplication. Notez lappel rcursif de la mthode DisplayDirectory(), et la manire utilise pour construire la chane de caractres dindentation :
610
Exemple 16-6 :
using System ;
using System.IO ;
class Program {
static void DisplayDirectory(DirectoryInfo dir, string sIndent) {
Console.WriteLine(sIndent + dir.Name) ;
foreach (DirectoryInfo sousdir in dir.GetDirectories())
DisplayDirectory(sousdir, sIndent + " ") ;
}
static void Main() {
DirectoryInfo dir = new
DirectoryInfo( Directory.GetCurrentDirectory() );
DisplayDirectory(dir, string.Empty) ;
}
}
Manipulation de fichiers
Le framework .NET prsente les deux classes System.IO.File et System.IO.FileInfo qui permettent de manipuler les fichiers (files en anglais) dun systme dexploitation. La plupart des
fonctionnalits de ces deux classes sont identiques. En fait, elles prsentent chacune une faon
de travailler avec les fichiers :
File ne contient que des membres statiques. Elle permet de travailler avec les fichiers sans
avoir instancier dobjet.
FileInfo ne contient que des membres non statiques. Pour travailler avec FileInfo, il faut
linstancier. Chaque instance de FileInfo reprsente un fichier. FileInfo est drive de
la classe System.IO.FileSystemInfo qui reprsente un fichier au sens large, cest--dire un
fichier ou un rpertoire.
Ces classes sont trs compltes. Voici quelques-unes des fonctionnalits quelles prsentent. Pour
la liste exhaustive des membres des classes File et FileInfo, rfrez-vous aux MSDN :
Les oprations daccs et de modification du contenu dun fichier sont prsentes page 636.
En eet, laccs et la modification dun fichier font partie du cadre plus gnral de la gestion
dun flot de donnes partir dune source ou vers une destination.
Fonctionnalit
NameAttributesLastWriteTimeCreationTime
Obtention et modification de toutes les informations relatives un fichier, comme son nom
(Name), ses attributs (Attributes), la date du dernier accs (LastAccessTime), la date de la dernire modification (LastWriteTime) ou la date
de cration (CreationTime).
611
Obtention du rpertoire dun fichier avec la proprit accessible en lecture seule DirectoryInfo
Directory de la classe FileInfo.
string FileInfo.Extension
long FileInfo.Length
La classe Path prsente dautres proprits intressantes comme la proprit char[] InvalidPathChars qui contient tous les caractres non utilisables dans une chane de caractres dcrivant un chemin dans le systme dexploitation sous-jacent. Pour la liste exhaustive des membres
de Path, rfrez-vous aux MSDN.
612
Changed : cet vnement est dclench lorsquun changement quelconque intervient sur un
des objets (fichiers ou rpertoires) surveills.
Created : cet vnement est dclench lorsquun objet (fichier ou rpertoire) est ajout dans
le rpertoire (ou un des sous rpertoires) surveills.
Deleted : cet vnement est dclench lorsquun objet (fichier ou rpertoire) surveill est
dtruit.
Error : en interne, la classe FileSystemWatcher contient un tampon pour stocker les vnements intercepts mais dont la procdure de rappel na pas encore t appele. Si les
limites de ce tampon sont atteintes, lvnement Error est dclench. Vous pouvez accder
et modifier la taille du tampon avec la proprit InternalBufferSize.
Renamed : cet vnement est dclench lorsquun objet (fichier ou rpertoire) surveill est
renomm.
Vous avez la possibilit daner le type des vnements que vous souhaitez intercepter avec les
techniques suivantes :
En positionnant la proprit IncludeSubdirectories pour indiquer si vous souhaitez surveiller aussi les sous rpertoires.
En modifiant la proprit string Filter de la classe FileSystemWatcher vous pouvez imposer une contrainte sur les fichiers surveiller. Par dfaut cette proprit vaut "*". Par
exemple vous pouvez dcider de ne surveiller que les fichiers textes en aectant "*.txt"
cette proprit.
Valeur
de
lnumration
System.IO.NotifyFilters
Description
Attributs
CreationTime
DirectoryName
613
FileName
LastAccess
LastWrite
Security
Size
Pour la liste exhaustive des membres de FileSystemWatcher veuillez vous rfrer aux MSDN.
Le programme suivant surveille les changements de contenu, les changements de nom de sous
rpertoire et les accs tous les fichiers texte situs dans le rpertoire courant de lapplication
ou dans un de ses sous rpertoires :
Exemple 16-7 :
using System ;
using System.IO ;
public class Program {
public static void Main() {
FileSystemWatcher watcher = new FileSystemWatcher() ;
watcher.Path = Directory.GetCurrentDirectory() ;
watcher.NotifyFilter = NotifyFilters.LastAccess |
NotifyFilters.DirectoryName ;
watcher.Filter = "*.txt" ;
watcher.IncludeSubdirectories = true ;
watcher.Changed += new FileSystemEventHandler(OnChange) ;
watcher.EnableRaisingEvents = true ;
Console.WriteLine("Pressez \q\ pour stopper lapplication.") ;
while (Console.Read() != q) ;
}
public static void OnChange(object source, FileSystemEventArgs e) {
Console.WriteLine("Fichier : " + e.FullPath
+ " Changement:" + e.ChangeType) ;
}
}
614
sont trs htroclites. Cela va du type de clavier utilis jusquaux prfrences utilises pour les
logiciels installs sur la machine. Le registre a t introduit sous Windows NT 3.x. Avant, on
utilisait des fichiers textes de type .ini, ce qui avait pour principal inconvnient de perturber
lorganisation du systme. La base des registres vise liminer ces inconvnients. Vous pouvez
visualiser et modifier les informations contenues dans la base des registres avec un des utilitaires regedit.exe ou regedt32.exe. Ces diteurs donnent une reprsentation hirarchique
des informations de la base des registres. Celles-ci sont regroupes comme pour un systme de
fichiers, sauf quau lieu de rpertoires on parle de cls et de sous-cls. Sous NT on prfre utiliser
regedt32.exe.
Il est dconseill daccder la base des registres partir de vos applications .NET pour deux
raisons :
Il ne faut jamais y sauver la configuration de vos applications. En eet, les fichiers de configuration XML dcrits tout au long de cet ouvrage ont t conus cette fin. Ils ont lavantage dtre physiquement stocks dans le mme rpertoire que lapplication. On peut ainsi
raliser un dploiement type XCopy.
La base des registres nexiste principalement que sous les systmes dexploitation Microsoft.
Bien que cette technique de base des registres ait t porte sous dautres systmes dexploitation, son utilisation hors du monde Microsoft reste marginale. Si vous accdez la base
des registres, votre application ne pourra pas tre porte facilement vers un autre systme
dexploitation supportant .NET.
Structure du registre
La hirarchie des informations du registre est la suivante (en partant du plus gnral au plus
particulier) :
les cls ;
les sous-cls ;
Les cls racines contiennent des cls qui, elles-mmes, contiennent des sous-cls. On parle aussi
de ruches qui sont des regroupements de cls. Une entre est constitue de trois parties :
le nom de la valeur ;
le type de donne ;
la valeur elle-mme.
Chaque valeur a lun des types suivants. Nous prcisons le type utilis dans la base des registres.
Nous prcisons aussi le type .NET associ une valeur rcupre de la base des registres dans
du code .NET.
615
Type .NET
Description
REG_DWORD
System.Int32
Nombre cod sur quatre octets. Les paramtres de services et des pilotes de priphriques, les adresses mmoire et les interruptions sont en gnral de ce type.
REG_SZ
System.String
REG_MULTI_SZ
System.String[]
REG_EXPAND_SZ
System.String
Chane de caractres extrapolables au format Unicode : ce format est utilis pour les
chanes de caractres formes de variables
denvironnement comme %SystemRoot%.
REG_BINARY
System.Byte[]
Hirarchie du registre
Le registre est structur en cinq cls racines :
Les extensions de nom de fichier : on y trouve par exemple la cl .zip permettant dassocier les fichiers dextension .zip lapplication WinZip (ou une autre application)
par lintermdiaire des sous-cls de dfinition de classes.
Les PROGID des classes COM enregistres sur la machine : pour chaque PROGID on
dfinit son CLSID, et ventuellement des attributs comme le numro de version de la
classe.
CLSID : contient tous les CLSID de toutes les classes COM enregistres sur la machine.
Chaque CLSID contient le PROGID, le chemin et le nom du fichier qui contient limplmentation de la classe, et ventuellement des attributs, comme le mode dexcution
des objets de la classe.
HKEY_CURRENT_USER : comporte le profil du dernier utilisateur qui a ouvert une session sur la machine. Cette cl rfrence la cl HKEY_USERS qui contient les dernires donnes en cours, les paramtres du bureau et de limprimante, les connexions rseau et les
prfrences dapplication. Une application peut par exemple y stocker une grande quantit
de prfrences comme la taille de la fentre principale, la position des sous fentres ou les
outils disponibles par icone.
HKEY_LOCAL_MACHINE : renferme les informations matrielles et logicielles spcifiques de lordinateur local. Ces donnes sont indpendantes de lutilisateur courant. Cette
cl comporte les sous-cls suivantes :
616
SAM : base de donnes de scurit des domaines (dans NT Server) et des comptes dutilisateurs de groupes (dans NT Workstation). Les informations prsentes dans cette cl
sont reproduites dans la cl HKEY_LOCAL_MACHINE\SECURITY\SAM.
SOFTWARE : contient les donnes relatives aux logiciels installs sur la machine.
HKEY_USERS : contient toutes les donnes de profil de lutilisateur courant, ainsi que les
donnes de profil de tous les utilisateurs qui se sont connects la machine. On y trouve
aussi le profil par dfaut (sous-cl .DEFAULT). La cl HKEY_USERS\.DEFAULT constitue
une ruche dont les fichiers correspondants sont DEFAULT et DEFAULT.LOG. Lditeur de
stratgie systme permet de modifier et de crer des profils dutilisateurs.
Le dbogage
617
}
}
Cet exemple ache :
Valeur de la cle HKEY_LOCAL_MACHINE/SOFTWARE/Microsoft/.NETFramework/
DbgManagedDebugger
C:\Program Files\Fichiers communs\Microsoft Shared\VS7Debug\vs7jit.exe
PID %d AP PDOM %d EXTEXT "%s" EVTHDL %d
Le dbogage
Attributs pour personnaliser la visualisation des tats de vos objets
Lespace de noms System.Diagnostics prsente des attributs qui vous permettent de personnaliser compltement lachage des tats de vos objets durant le dbogage :
DebuggerDisplayAttribute
Sapplique aux classes, structures, dlgations, numrations, champs, proprits et assemblages. Cet attribut permet de personnaliser la ligne de description dun objet fourni par
le dbogueur. Cette ligne peut contenir des expressions entre accolades qui peuvent tre le
nom dun champ, dune proprit ou dune mthode. Comprenez bien quune telle ligne
doit aller lessentiel en restant concise :
Exemple 16-9 :
using System.Diagnostics ;
[DebuggerDisplay("{Description} prix:{m_Prix} euros")]
class Article {
private decimal m_Prix ;
private string m_Description ;
public string Description{
get{return m_Description;}
}
public Article(string description,decimal prix) {
m_Description = description ;
m_Prix = prix ;
}
}
class Program {
static void Main() {
Article article = new Article("Chaussure", 120) ;
}
}
DebuggerBrowsable
Sapplique aux champs et proprits. Permet de signifier au dbogueur comment il doit
acher ce champ ou cette proprit laide dune des trois valeurs de lnumration
DebuggerBrowsableState savoir : Collapsed : le dbogueur ache la valeur du champ
ou de la proprit lorsque ltat de lobjet est dpli (cest le comportement par dfaut) ;
618
DebuggerTypeProxyAttribute
Sapplique aux structures, classes et assemblages. Cet attribut appliqu un type T permet
de spcifier un type proxy spcialis dans lachage de ltat dune instance de T. En gnral,
ce type proxy est une classe prive encapsule dans le type T du fait quune telle classe a accs
tous les membres privs de la classe encapsulante :
Exemple 16-10 :
[System.Diagnostics.DebuggerTypeProxy(typeof(ArticleProxy))]
class Article {
private class ArticleProxy {
private Article m_Article;
public ArticleProxy(Article article) {m_Article = article;}
public string Prix { get{ return m_Article.m_Prix + " euros";}}
}
private decimal m_Prix ;
private string m_Description ;
public Article(string description,decimal prix) {
m_Description = description ;
m_Prix = prix ;
}
}
class Program {
static void Main() {
Article article = new Article("Chaussure", 120) ;
}
}
Notez la prsence de la ligne Raw View qui permet dobtenir la vue par dfaut de ltat de
lobjet :
DebuggerVisualizerAttribute
Le dbogage
619
DebuggableAttribute
Sapplique aux assemblages et aux modules. Cet attribut permet de spcifier ou non
lensemble des modes de dbogages que le compilateur JIT doit appliquer sur le code
IL de lassemblage ou du module cibl. Ces modes sont dcrits par lindicateur binaire
DebuggableAttribute.DebuggingModes. On peut citer le mode Edit and Continue, le mode
optimisations JIT dsactives, le mode ignorer les informations du fichier .pdb ou le mode qui
permet dactiver la production par le compilateur JIT des informations qui tablissent le
lien entre le code IL et le code machine gnr (JIT tracking).
DebuggerHiddenAttribute
Sapplique aux constructeurs, aux mthodes et aux proprits. Cet attribut permet de spcifier que le dbogueur ne doit pas entrer dans cette mthode. En consquence, vous ne
pouvez mettre de point darrt dans une mthode marque avec cet attribut. En revanche,
si une mthode marque avec cet attribut appelle une mthode non marque avec cet attribut, il est tout fait possible de dboguer le code de la seconde mthode. Dans ce cas, une
fentre de pile contenant External Code apparatra dans la pile dappels du dbogueur.
DebuggerStepThroughAttribute
620
DebuggerNonUserCodeAttribute
Sapplique aux classes, structures, constructeurs, mthodes et proprits. Cet attribut est
comparable DebuggerHiddenAttribute.
Les traces
Le framework .NET prsente les deux classes System.Diagnostics.Trace et System.Diagnostics.
Debug qui vous permettent de tracer le droulement dun programme. On parle aussi parfois
de log de lexcution dun programme. Chacune de ces classes prsente les quatre mthodes
Write(string), WriteLine(string), WriteIf(bool,string) et WriteLineIf(bool,string)
qui peuvent par exemple sutiliser comme ceci :
Exemple 16-11 :
using System.Diagnostics ;
class Program {
static void Main() {
Trace.Listeners.Add( new ConsoleTraceListener() ) ;
Trace.WriteLine("Trace hello");
for( int i=0;i<5;i++)
Debug.WriteLineIf(i > 2, "debug i=" + i.ToString() );
}
}
Voici lachage de ce programme sur la console :
Trace hello
debug i=3
debug i=4
La classe Trace est plutt ddie aux log dinformation sur le domaine fonctionnel de lapplication tandis que la classe Debug est destine tracer des informations utiles au dbogage. De ce
fait, elles sont relativement similaires bien quelles doivent tre utilises des fins direntes.
Ces deux classes ne sont oprationnelles que si lassemblage qui les utilise est compil avec respectivement la constante symbolique TRACE ou/et DEBUG.
Les traces
621
Listener
LExemple 16-11 commence par la ligne :
Trace.Listeners.Add( new ConsoleTraceListener() ) ;
Cela signifie que la liste des listeners va contenir un listener de type ConsoleTraceListener. Un
listener est une instance dune classe qui drive de la classe System.Diagnostics.TraceListener.
La liste de listener est commune aux deux classes Debug et Trace. Lorsquune trace est logue
par une de ces classes, chacun des listeners de la liste crit la trace sur son propre moyen de persistance, en loccurrence, la console pour un listener de type ConsoleTraceListener. Dautres
classes de listener sont prvues par le framework (les classes en gras ont t ajoutes par la version 2.0) :
System.Diagnostics.TraceListener
Microsoft.VisualBasic.Logging.FileLogTraceListener
System.Diagnostics.DefaultTraceListener
System.Diagnostics.EventLogTraceListener
System.Diagnostics.TextWriterTraceListener
System.Diagnostics.ConsoleTraceListener
System.Diagnostics.DelimitedListTraceListener
System.Diagnostics.XmlWriterTraceListener
System.Web.WebPageTraceListener
DelimitedListTraceListener : Trace dans un flot de donnes tel quun fichier. la dirence de TextWriterTraceListener, les traces sont spares par une chaine de caractres (tel
quun point virgule par exemple) de faon tre facilement exploitables par un programme
danalyse de trace.
XMLWriterTraceListener : Trace dans un fichier XML qui suit un certain schma XML.
Vous avez la possibilit de construire la liste de listener par lintermdiaire du fichier de configuration. Par exemple, ce fichier de configuration ajoute un listener de type ConsoleTraceListener
la liste :
Exemple 16-12 :
<?xml version="1.0" encoding="utf-8" ?>
<configuration
xmlns="http://schemas.microsoft.com/.NetConfiguration/v2.0">
<system.diagnostics>
<trace>
<listeners>
<add name="TraceOnConsole"
type="System.Diagnostics.ConsoleTraceListener"/>
622
Si lExemple 16-11 est utilis conjointement avec ce fichier, la liste de listeners contiendra deux
listeners qui tracent sur la console et le programme achera chaque trace en double :
Trace
Trace
debug
debug
debug
debug
hello
hello
i=3
i=3
i=4
i=4
Enfin, sachez que vous pouvez dvelopper vos propres classes de listener drives de la classe
TraceListener.
Les traces
623
Il naurait pas ach la trace Trace Hello si nous avions choisi SourceLevels.Warning. Le
concept de switch peut tre aussi implment avec des instances de la classe SourceSwitch :
Exemple 16-14 :
...
TraceSource trace1 = new TraceSource("Trace1") ;
SourceSwitch switch1 = new SourceSwitch("Switch1");
switch1.Level = SourceLevels.All;
trace1.Switch = switch1;
...
Une source de traage est paramtrable aprs compilation grce au fichier de configuration. Avec
le fichier de configuration suivant, nous naurions qu crer une source de traage nomme
Trace1 dans notre code :
Exemple 16-15 :
<?xml version="1.0" encoding="utf-8" ?>
<configuration
xmlns="http://schemas.microsoft.com/.NetConfiguration/v2.0">
<system.diagnostics>
<sources>
<source name="Trace1" switchName="Switch1">
<listeners>
<add name="TraceOnConsole"
type="System.Diagnostics.ConsoleTraceListener"/>
</listeners>
</source>
</sources>
<switches>
<add name="Switch1" value="All" />
</switches>
</system.diagnostics>
</configuration><labelstart id="_Toc114113400"/>
624
625
}
}
static void Main() {
trace1 = new TraceSource("Trace1") ;
trace1.Listeners.Add(new ConsoleTraceListener()) ;
trace1.Switch.Level = SourceLevels.All ;
trace1.TraceInformation( IndentLevel + "Begin Main()") ;
fct(2) ;
trace1.TraceInformation( IndentLevel + "End Main()") ;
}
static void fct(int i) {
Trace.Indent() ;
trace1.TraceInformation( IndentLevel +
"Begin fct(" + i.ToString() + ")" ) ;
if (i > 0)
fct(i-1) ;
trace1.TraceInformation( IndentLevel +
"End fct(" + i.ToString() + ")" ) ;
Trace.Unindent() ;
}
}
Les expressions rgulires permettent de vrifier quune chane de caractres vrifie une
rgle syntaxique (par exemple si une adresse mail contient bien des caractres avant et aprs
le caractre @ et si le nom du serveur a une extension valide comme .com, .fr ou .edu).
Les expressions rgulires permettent dextraire des lments dune chane de caractres.
La notion dexpression rgulire a surtout t popularise par la commande grep sous Unix qui
permet le filtrage de donnes. Le filtre est alors paramtr par une expression rgulire.
Nous allons maintenant prsenter quelques points principaux sur les expressions rgulires sans
toutefois rentrer dans un expos exhaustif qui sortirait du cadre de ce livre.
Syntaxe
Une expression rgulire se prsente comme une chane de caractres. Chaque caractre de cette
chane a une signification prcise. Voici les plus communs :
626
Caractre(s)
Signification
Dbut de ligne.
Fin de ligne.
x|y
x ou y.
()
Groupement.
[xyz]
Ensemble de caractres (x ou y ou z)
[x-z]
Intervalle de caractres (x ou y ou z)
[x]
Tous sauf x
Vous pouvez aussi spcifier le nombre doccurrences dune expression rgulire avec les expressions suivantes :
Caractre(s)
Signification
x*
x+
x?
x{n}
Exactement n occurrences de x.
x{n,}
Au moins n occurrences de x.
x{n,m}
Alias pour
Signification
\n
\r
Un retour la ligne.
\t
Une tabulation.
\s
[\f\n\r\t\v]
Un espacement.
\S
[\f\n\r\t\v]
627
\d
[0-9]
Un chire.
\D
[0-9]
\w
[a-zA-Z0-9_]
Un caractre alphanumrique.
\W
[a-zA-Z0-9_]
\067
Un caractre en octal.
\x5A
Un caractre en hexadcimal.
Exemples
Voici quelques exemples dexpressions rgulires et de leurs significations :
Lexpression rgulire ci-dessous vrifie si une chane de caractres commence par une lettre
majuscule.
[A-Z].*
Lexpression rgulire ci-dessous vrifie si une chane de caractres est une date au format
yyyy-mm-dd entre 1900 01-01 et 2099 12-31.
(19|20)\d\d[- /.](0[1-9]|1[012])[- /.](0[1-9]|[12][0-9]|3[01])
628
La console
629
using CompiledExpressions ;
using System.Diagnostics ;
public class Program {
const int NLOOP = 1000000 ;
const string str = "abcdefghijklmnopqrstuvwxyz" ;
static void Main() {
bool b ;
Regex regex1 = new Regex("^[^0-9]*$") ;
Stopwatch sw = Stopwatch.StartNew();
for (int i = 0 ; i < NLOOP ; i++)
b = regex1.IsMatch(str) ;
Console.WriteLine("Sans pr
ecompilation:{0}", sw.Elapsed );
sw = Stopwatch.StartNew();
PasDeChiffres regex2 = new PasDeChiffres() ;
for (int i = 0 ; i < NLOOP ; i++)
b = regex2.IsMatch(str) ;
Console.WriteLine("Avec pr
ecompilation:{0}", sw.Elapsed );
}
}
Nous avons eectu dirents tests avec lexpression rgulire pour vrifier quil ny a pas
de chire ("[0-9]*$") et lexpression rgulire pour vrifier une date (@"(19|20)\d\d[/.](0[1-9]|1[012])[- /.](0[1-9]|[12][0-9]|3[01])"). Les chanes testes taient lalphabet ("abcdefghijklmnopqrstuvwxyz") et un double alphabet ("abcdefghijklmnopqrstuvwxyz
abcdefghijklmnopqrstuvwxyz"). Voici les facteurs doptimisation obtenus :
Expression rgulire
Chane teste
Facteur doptimisation
Pas de chires
Alphabet
1,21
Pas de chires
double alphabet
1,12
Vrification de date
Alphabet
3,99
Vrification de date
double alphabet
4,03
La console
La classe System.Console permet de contrler lachage et la saisie de donnes sur la console.
Toutes les fonctionnalits de cette classe sont accessibles partir de membres statiques puisquune application a au plus une console.
Le curseur de la console
La console supporte un curseur qui reprsente lendroit o le prochain achage de donnes va
seectuer.
630
Par dfaut le curseur sache aprs le dernier caractre ach mais vous pouvez fixer sa
position avec la mthode SetCursorPosition(int column,int row).
Vous pouvez obtenir ou modifier la position courante du curseur avec les proprits int
CursorTop{get;set;} et int CursorLeft{get;set;}.
Vous pouvez choisir de rendre le curseur visible ou non avec la proprit int CursorVisible{get;set;}.
Vous pouvez obtenir ou positionner la taille du curseur en utilisant la proprit int CursorSize{get ;set} qui prend ses valeurs dans lintervalle ferm [1,100]. La taille du curseur dsigne le pourcentage du remplissage du rectangle du curseur.
Achage de donnes
Vous pouvez acher une chane de caractre lendroit o le curseur est positionn en
appelant une des surcharges de la mthode Write(). La mthode WriteLine() oblige le
curseur passer une nouvelle ligne aprs avoir acher les donnes.
La mthode Clear() permet deacer toutes les donnes couramment aches sur la
console et de mettre le curseur en haut gauche.
Vous pouvez obtenir le plus grand nombre de colonnes et de lignes possibles en fonction
de la rsolution de lcran au moyen des proprits int LargestWindowWidth{get;} et int
LargestWindowHeight{get;}.
Couleur
La classe Console vous permet de modifier la couleur des donnes que vous achez en prcisant
les couleurs avec lnumration System.ConsoleColor. Dans la suite nous utiliserons le terme
couleur background pour dsigner la couleur de fond et le terme couleur foreground pour dsigner la couleur des caractres.
La console
631
Les proprits ConsoleColor ForegroundColor{get;set;} et ConsoleColor BackgroundColor{get;set;} permettent dobtenir ou de positionner les couleurs foreground et background des prochaines donnes aches.
Vous pouvez repositionner les couleurs foreground et background leurs valeurs par dfaut
en appelant la mthode ResetColor().
Saisie de donnes
Vous pouvez obtenir le prochain caractre tap sur le clavier par lutilisateur en appelant
la mthode int ReadKey(). Lappel cette mthode est bloquant jusqu ce quun caractre
soit eectivement tap sur le clavier. Ensuite le caractre correspondant la touche appuye
est ach sur la console. Vous pouvez convertir lentier retourn en une instance de char
en appelant la mthode char Convert.ToChar(int). La surcharge int ReadKey(bool) permet de ne pas acher le caractre sur la console lorsquelle prend en argument la valeur
false.
La mthode string ReadLine() retourne la ligne qui vient dtre saisie par lutilisateur.
Cette mthode est bloquante jusqu ce que lutilisateur appuie sur la touche Entre.
La proprit bool TreatControlCAsInput{get;set;} permet de connatre ou dactiver/dsactiver le fait que lvnement Ctrl-C soit trait comme une entre normale ou
non. Si vous positionnez cette proprit false vous pouvez alors exploiter lvnement
CancelPressedKey.
Autres fonctionnalits
La mthode Beep() permet de faire jouer un beep. Une surcharge permet de modifier la
frquence et la dure du beep.
17
Les mcanismes dentre/sortie
Laccs alatoire aux donnes (seek en anglais). Cette possibilit autorise lutilisateur dplacer le curseur en avant et en arrire dans le flot de donnes. Le curseur dfinit lendroit dans
le flot de donnes partir duquel la prochaine lecture/criture se fera. Si cette possibilit
nest pas supporte par un flot de donnes, lutilisation des proprits Length et Position
et des mthodes SetLength() et Seek() de la classe Stream provoque lenvoi de lexception
NotSupportedException. Si cette proprit nest pas supporte par un flot de donnes, on
dit que le flot de donnes est forward-only. Ce terme anglais signifie que le curseur ne peut
tre dplac quen avant.
Vous pouvez tester quelles sont les manipulations supportes par un flot de donnes en utilisant
les proprits CanRead, CanWrite et CanSeek de la classe Stream. Voici la liste des classes drivant
de la classe Stream :
System.IO.FileStream
634
System.Net.Security.AuthenticatedStream
Les classes drives de cette classe sont des implmentations de protocoles de scurisations
de flot de donnes par authentification et encryptions (voir page 660).
System.Security.Cryptography.CryptoStream
Cette classe permet de crypter les donnes dun flot de donnes. Un flot qui utilise ce service
ne supporte jamais laccs alatoire (voir page 664).
System.IO.BufferedStream
Cette classe est utilise conjointement avec un flot de donnes pour lui fournir une mmoire tampon. Lutilisation de BufferedStream permet damliorer les performances dans
certains cas, comme ceux exposs page 658. BufferedStream supporte les types daccs du
flot de donnes sous-jacent.
System.IO.MemoryStream et System.IO.UnmanagedMemoryStream
Cette classe permet dobtenir une mmoire tampon partageable entre plusieurs flots de
donnes (voir page 659). La version non gre (unmanaged) permet dviter la copie des
donnes sur le tas par le CLR et est donc plus ecace.
System.IO.Compression.DeflateStream System.IO.Compression.GZipStream
Ces classes permettent de compresser les donnes dun flot (voir page 659).
System.IO.BinaryWriter et System.IO.BinaryReader
Ces classes permettent de lire et dcrire des donnes codes dans un des types primitifs
.NET partir dun flot de donnes. Ces classes prsentent des mthodes telles que short
ReadInt16(), void Write(short), short ReadDouble() ou void Write(double).
635
System.IO.TextWriter et System.IO.TextReader
Ces classes sont abstraites. Elles servent de classes de base aux classes permettant de lire et
dcrire des caractres encods dans un format particulier. La prsentation de ces formats
fait lobjet de la prochaine section. Ces classes prsentent des mthodes telles que string
ReadLine() ou void WriteLine(string).
System.IO.StringWriter et System.IO.StringReader
Ces classes sont drives respectivement de TextWriter et de TextReader. Elles sont utilises
pour crire et lire des caractres (ou des chanes de caractres) vers une (ou partir dune)
chane de caractres.
System.IO.StreamWriter et System.IO.StreamReader
Ces classes sont drives respectivement de TextWriter et de TextReader. Elles sont utilises pour crire et lire des caractres (ou des chanes de caractres) encods dans un format
particulier, vers un (ou partir dun) flot de donnes quelconque.
636
cods sur plus dun octet. Si un diteur de texte supporte lencodage UTF-8, il reproduit correctement les caractres accentus. Sinon, plusieurs caractres tranges remplacent chaque caractre
accentu. Vous avez srement dj rencontr ce problme, notamment dans la lecture de vos
mails.
Lors du traitement dun flot de donnes contenant des caractres avec une des classes StreamWriter ou StreamReader, on a la possibilit de prciser le format dencodage des caractres avec
une des classes suivantes :
System.Object
System.Text.Encoding
System.Text.ASCIIEncoding
System.Text.UnicodeEncoding
System.Text.UTF7Encoding
System.Text.UTF8Encoding
La classe Encoding contient des mthodes permettant de convertir une chane de caractres dun
format un autre.
Les mthodes ReadAllLines() et WriteAllLines() permettent dcrire ou de lire un tableau de chanes de caractres partir de ou dans un fichier.
Les mthodes ReadAllBytes() et WriteAllBytes() permettent dcrire ou de lire un tableau de doctets partir de ou dans un fichier.
Les mthodes dcriture crent le fichier si celui-ci nexiste pas et crase les anciennes donnes si
le fichier existe. En outre toutes ces mthodes ferment proprement le fichier utilis. Lexemple
suivant montre comment dupliquer un fichier texte tout en lachant lcran :
Exemple 17-1 :
using System.IO ;
public class Program {
public static void Main() {
string text = File.ReadAllText(@"G:\source\Test.txt") ;
System.Console.Write(text) ;
File.WriteAllText(@"G:\source\TestCopy.txt",text) ;
}
}
637
Pour faire transiter les donnes entre un flot de donnes entrant et un flot de donnes sortant,
on utilise en gnral une mmoire tampon (buer en anglais). Une mmoire tampon est une zone
de mmoire de taille fixe, qui est alternativement remplie par le flot de donnes entrant puis
vide dans le flot de donnes sortant. Cette opration de remplissage/vidage est eectue autant
de fois quil le faut.
Le programme suivant copie le fichier excutable de lassemblage courant, vers le fichier Copy.
exe, dans le rpertoire de lapplication. Naturellement lassemblage courant ne doit pas sappeler Copy.exe.
Exemple 17-2 :
using System.IO ;
public class Program {
static readonly int tailleTampon = 512 ;
public static void Main() {
// Le nom du domaine dapplication courant est
egal...
// ...au nom de lassemblage courant (avec lextension).
string sExe = System.AppDomain.CurrentDomain.FriendlyName ;
// Les deux fichiers sont dans le r
epertoire de lapplication.
FileStream inStream = File.OpenRead(sExe) ;
FileStream outStream = File.OpenWrite("Copy.exe") ;
// Construit une zone de m
emoire tampon.
byte[] tampon = new System.Byte[tailleTampon] ;
int nBytesRead = 0 ;
// Copie le fichier binaire.
while ((nBytesRead = inStream.Read(tampon, 0, tailleTampon)) > 0)
outStream.Write(tampon, 0, nBytesRead) ;
// Ferme les flots de donn
ees.
inStream.Close() ;
outStream.Close() ;
}
}
638
tampon de taille fixe. chaque copie de ligne nous utilisons une nouvelle chane de caractres
construite par la mthode ReadLine() de la classe StreamReader. Nous utilisons ensuite la mthode WriteLine() de StreamWriter pour copier cette chane dans le fichier destination.
Chaque ligne est ache sur la console au moyen de la mthode WriteLine() de la classe
Console. Une dirence entre ces deux mthodes WriteLine() est que celle de la classe Console
est statique alors que celle de la classe StreamWriter est dinstance.
Exemple 17-3 :
using System.IO ;
public class Program {
public static void Main() {
StreamReader inStream = File.OpenText(@"G:\source\Test.txt") ;
StreamWriter outStream = new
StreamWriter(@"G:\source\TestCopy.txt") ;
// Copie la source vers la destination.
string sTmp ;
do{
sTmp = inStream.ReadLine() ;
outStream.WriteLine(sTmp) ;
System.Console.WriteLine(sTmp) ;
}
while (sTmp != null) ;
// Ferme les flots de donn
ees.
inStream.Close() ;
outStream.Close() ;
}
}
La notion dencodage de caractres semble totalement absente de cet exemple, et pourtant cet
exemple marche quel que soit le format dencodage du fichier texte source. En fait, si lutilisateur
ne spcifie pas dencodage particulier la classe StreamReader, cette classe dtecte automatiquement le format utiliser en analysant les premiers caractres du fichier texte. Chaque type
dencodage prvoit de laisser une marque spciale sur les premiers octets dun fichier texte. Vous
pouvez obtenir cette marque avec la mthode byte[] GetPreamble() de la classe prsente par
chaque classe dencodage drive de la classe Encoding. Par exemple cette marque est les trois
octets 0xEF 0xBB 0xBF pour lencodage UTF8, et 0xFF 0xFE pour lencodage UNICODE et rien
pour lencodage ASCII. Cette marque nest pas forcment prsente dans tous les fichiers textes
et vous avez la possibilit de dsactiver lmission de cette marque lorsque vous crivez dans
un fichier texte au moyen de StreamWriter. Dans ce cas, limplmentation Microsoft utilise par
dfaut lencodage UTF-8.
La proprit Encoding CurrentEncoding{get;} de la classe StreamReader, vous permet de
connatre lencodage utilis. Cette proprit est automatiquement positionne la premire
lecture dun fichier texte. De mme la classe StreamWriter prsente la proprit Encoding
Encoding accessible en lecture seule.
639
de grandeur de cette taille, mais en gnral, il ne la connat pas. Le temps du traitement dun
flot de donnes est donc inconnu du dveloppeur. Si un tel traitement est eectu par le thread
principal dune application, le rsultat peut tre catastrophique puisque lapplication peut potentiellement se bloquer pendant un temps inconnu. De plus si lapplication a de nombreux
flots de donnes traiter dune manire indpendante, il serait judicieux quelles puissent effectuer ces traitements en parallle.
Le traitement asynchrone dun flot de donnes rsout ces problmes. Il permet de dlguer le
traitement sur un autre thread que celui qui dcide de lancer le traitement. Le dveloppeur na
absolument pas besoin de soccuper de cet autre thread. Ce dernier fait partie du pool de threads
dentre/sortie du CLR. Comme nous allons le voir dans le prochain exemple, il est ais de lancer
en parallle plusieurs traitements asynchrones dun flot de donnes. Une telle architecture pour
vos applications peut tre plusieurs dizaines de fois plus performante que le simple traitement
squentiel.
Lexemple suivant illustre le lancement en parallle de N traitements asynchrones. Tout dabord,
nous crons un fichier volumineux denviron 10 Mo avec la mthode CreateBlobFile(). Ensuite nTraitements lectures de ce fichier sont lances en parallle avec lappel la mthode
BeginRead() de FileStream, dans une boucle. Les threads aects ces traitements sont ceux
du pool de threads. La mthode BeginRead() permet de prciser une procdure de finalisation
qui est appele lorsque la lecture est finie. Cest le mme thread qui eectue la lecture et qui
excute la procdure de finalisation. Dans lexemple, la mthode OnLectureFinie() constitue la
procdure de finalisation. Lexemple montre comment cette mthode rcupre les donnes lues
et ventuellement des informations provenant du thread principal (comme lID du traitement)
grce la classe EtatFichier.
Contrairement aux sections prcdentes, la zone de mmoire tampon est gale la taille du
fichier lire. Nous navons donc pas nous occuper de la gestion du tampon. En fait la classe
FileStream gre son propre mcanisme de tampon en interne dune manire transparente pour
le dveloppeur. Le dveloppeur peut optionnellement fixer la taille de cette mmoire tampon
interne dans un argument du constructeur (ici nous spcifions 4096 octets).
Exemple 17-4 :
using System ;
using System.IO ;
using System.Threading ;
public class Program {
class EtatFichier {
public byte []
tampon ;
public FileStream
fs ;
public int
idTraitement ;
}
// Taille du fichier : presque 10Mo.
static readonly int nOctetsParFichier = 10000000 ;
static readonly int nTraitements = 5 ;
static string
sFileName = "blob.bin" ;
// Cree un fichier de nOctetsParFichier octets.
static void CreateBlobFile(){
byte [] Blob = new byte[nOctetsParFichier ] ;
FileStream fs = new FileStream(
640
641
Thread.CurrentThread.ManagedThreadId ,etat.idTraitement) ;
// Simule un traitement des donn
ees dune seconde.
Thread.Sleep(1000) ;
Console.WriteLine(
"Thread #{0} : Fin traitement #{1}",
Thread.CurrentThread.ManagedThreadId ,etat.idTraitement) ;
}
}
Cet exemple ache ceci sur ma machine. On voit bien que le thread principal a pour ID #8
et que les deux threads qui ont pour IDs #6 et #10, sont impliqus dans le traitement asynchrone. Ce comportement peut varier dune excution lautre et il peut y avoir entre 1 et min
(Ntraitement, Nombre de threads IO dans le pool) threads aects au traitement de donnes.
Thread
Thread
Thread
Thread
Thread
Thread
Thread
Thread
Thread
Thread
Thread
Thread
Thread
Thread
Thread
#0
#1
#2
#3
#4
642
donnes entrants et sortants. Ce sont les couches basses rseau, qui modulent et dmodulent le
flot physique de bits entrant et sortant du cble rseau. Ces couches sont conceptuellement en
dessous du protocole IP.
Une socket matrialise un point de communication sur une machine. Les sockets taient la
base, il y a 20 ans, un ensemble de fonctions bas niveau, crites en C. Elles ont t inventes
luniversit de Berkeley en Californie, pour rpondre au besoin de communication inter machines. Lensemble de ces fonctions est devenu standard et a t encapsul dans des classes. Les
sockets supportent plusieurs protocoles de communication. Notamment le protocole UDP/IP
(User Datagram Protocol) et surtout le protocole TCP/IP (Transfert Control Protocol) qui comme
son nom lindique, utilise le protocole IP et son mode dadressage.
Concrtement, un processus, nomm serveur, cre une socket et appelle une mthode bloquante (en gnral nomme Accept()) sur cet objet, pour attendre une requte. Un autre processus cre une autre socket. Ce processus est nomm client. Il existe sur la mme machine ou
sur une autre machine que celle qui contient le processus serveur. Le client fournit sa socket
le couple adresse IP/Port identifiant la socket du serveur. Grce cette information, la socket du
client se connecte la socket serveur, ce qui a pour eet de dbloquer le serveur de son attente
sur la mthode Accept() et de crer un flot de donnes entre le client et le serveur. partir de
cette tape, deux scnarios peuvent se produire sur le serveur :
Soit le serveur traite compltement les besoins du client avant de se remettre en mode dattente client en appelant Accept(). On dit que le serveur travaille dune manire synchrone,
car il sert les clients les uns aprs les autres.
Soit le serveur cre le plus rapidement possible une nouvelle socket quil passe un autre
thread pour quil traite les besoins du client. Le serveur principal rappelle donc Accept()
presque immdiatement aprs la connexion client. On dit que le serveur travaille dune
manire asynchrone.
Bien entendu, lutilisation du mode asynchrone est en gnral beaucoup plus performante,
puisquelle minimise les temps dattente globaux des clients. Nous allons prsenter ces deux
faons de travailler dans les deux prochaines sections.
Ct serveur
Notre serveur cre un point de communication sur le port 50000 avec une instance de la classe
TcpListener. Le serveur attend la connexion dun client en appelant la mthode AcceptTcpClient(). Lorsquun client se connecte, le serveur obtient une instance de la classe System.
Net.Sockets.NetworkStream qui reprsente un flot de donnes avec le client. Dans la mthode
TraiteClient(), le serveur ouvre un fichier texte et le copie, ligne aprs ligne, dans le flot de
643
donnes vers le client. Une fois le fichier copi, le serveur ferme le flot de donnes vers le client
et le flot de donnes en provenance du fichier et retourne en attente dun client.
Notez que les oprations de fermeture de flot ont lieu au sein dune clause finally. En eet, ce
sont typiquement des oprations de libration de ressources critiques. Nous aurions pu opter
plutt pour lutilisation du pattern using/Dispose() puisque toutes les classes de flots de donnes
implmentent linterface IDisposable.
Exemple 17-5 :
using System ;
using System.Net.Sockets ;
using System.Net ;
using System.IO ;
class ProgServeur {
static readonly ushort port = 50000 ;
static void Main() {
IPAddress ipAddress = new IPAddress(new byte[] { 127, 0, 0, 1 }) ;
TcpListener tcpL = new TcpListener(ipAddress,port) ;
tcpL.Start() ;
// Chaque passage dans la boucle = envoi dun fichier `
a un client.
while(true){
try{
Console.WriteLine( "Attente dun client..." ) ;
// Lappel `a cette m
ethode est bloquant, c`
ad, on ne sort
// de cette methode que lorsquun client est connect
e.
TcpClient tcpC = tcpL.AcceptTcpClient() ;
Console.WriteLine( "Client connect
e." ) ;
TraiteClient( tcpC.GetStream() ) ;
Console.WriteLine( "Client d
econnect
e." ) ;
}
catch(Exception e){
Console.WriteLine(e.Message) ;
}
}
}
static void TraiteClient (NetworkStream networkStream){
// Flot de donnees denvoi sur le r
eseau.
StreamWriter streamWriter = new StreamWriter(networkStream) ;
// Flot de donnees de lecture du fichier source.
StreamReader streamReader = new StreamReader(
@"C:/Text/Fichier.txt" ) ;
// Pour chaque ligne du fichier source : envoi sur le r
eseau.
string sTmp = streamReader.ReadLine() ;
try{
while(sTmp != null ){
Console.WriteLine( "Envoi de : {0}" , sTmp ) ;
streamWriter.WriteLine(sTmp);
streamWriter.Flush();
644
Affichage du serveur
Ct client
Le code du client est encore plus simple que celui du serveur. Le client cre une instance de
la classe TcpClient et lui fournit le couple adresse IP/Port du serveur. Lorsque le client est
connect, il obtient une instance de la classe System.Net.Sockets.NetworkStream qui reprsente un flot de donnes avec le serveur. Le client na plus qu lire les donnes partir de ce
flot. Nous savons que le serveur envoie des lignes de caractres. Aussi, le client lit les lignes les
unes aprs les autres, et les ache sur sa console.
Exemple 17-6 :
using System ;
using System.Net.Sockets ;
using System.IO ;
class ProgClient {
static readonly string host = "localhost" ;
static readonly ushort port = 50000 ;
static void Main() {
TcpClient tcpC ;
try{
// Lappel au constructeur de TcpClient envoie une
// exception si la connexion avec le serveur
echoue.
tcpC = new TcpClient(host,port);
Console.WriteLine(
"Connexion etablie avec {0}:{1}" ,host , port) ;
645
Affichage du client
646
avec un fichier, les mthodes BeginRead() et BeginWrite() permettent de fournir une procdure
de finalisation. Cette procdure est automatiquement appele la fin de la lecture ou de lcriture des donnes. Cette procdure est appele par le mme thread qui a eectu la lecture ou
lcriture. Vous navez pas vous occupez de ce thread. Ce dernier fait partie du pool de threads
dentre/sortie du CLR.
Contrairement lexemple du traitement asynchrone dun flot de donnes avec un fichier
(Exemple 17-4), nous nutilisons pas la possibilit de partager un objet entre le thread principal
et le thread du pool eectuant les entre/sorties. En gnral, larchitecture qui permet le
traitement asynchrone des connexions clientes distantes contient une classe dont les instances
traitent les clients (une instance de cette classe par client). Nous avons nomm cette classe
TraitementClient. Une instance de cette classe na besoin que du flot de donnes vers le client.
Ce flot de donnes est obtenu de la mme manire que dans la section prcdente, en appelant
successivement les mthodes AcceptTcpClient() puis GetStream().
Notre exemple est construit de manire ce que le serveur commence par lire les donnes du
client. Une fois rceptionnes, le serveur renvoie ces donnes au client. Le serveur eectue ces
deux oprations jusqu ce que le client ne lui envoie plus de donnes. ce moment, le serveur
arrte de traiter ce client. Bien entendu, dans un cas rel, le serveur ne renverrait pas les donnes directement au client sans traitements, mais nous avons tent de simplifier au maximum
lexemple.
Exemple 17-7 :
using
using
using
using
using
System ;
System.Net.Sockets ;
System.Net ;
System.IO ;
System.Text ;
class ProgServeur {
static readonly ushort port = 50000 ;
static void Main() {
IPAddress ipAddress = new IPAddress(new byte[] { 127, 0, 0, 1 }) ;
TcpListener tcpL = new TcpListener(ipAddress,port);
tcpL.Start() ;
while(true){
try{
Console.WriteLine( "Main:Attente dun client..." ) ;
TcpClient tcpC = tcpL.AcceptTcpClient();
Console.WriteLine( "Main:Client connect
e." ) ;
TraitementClient tc =new TraitementClient(tcpC.GetStream()) ;
tc.Traite() ;
}
catch(Exception e){
Console.WriteLine(e.Message) ;
}
}
}
}
// Une instance de cette classe est cr
e
ee pour chaque connexion client.
647
648
Voici lachage du serveur lorsquil est connect un client dont le code est prsent ci-aprs.
Notez que le serveur se remet en attente dun autre client avant mme de recevoir des donnes
du premier client.
Affichage du serveur
Exemple :
Ct client
Le fait que le serveur travaille en mode asynchrone na aucun impact sur le code du client.
Le client est ainsi trs similaire au client prsent dans la section prcdente. La dirence est
quici le client ne se contente pas dattendre des donnes en provenance du serveur. Il envoie
des donnes et reoit des donnes trois fois successivement. Voici le code :
Code du Client : Client.cs
Exemple 17-8 :
using System ;
using System.Net.Sockets ;
using System.IO ;
class ProgClient {
static readonly string host = "localhost" ;
static readonly ushort port = 50000 ;
static void Main() {
TcpClient tcpC ;
try{
// Lappel au constructeur de TcpClient envoie une
// exception si la connexion avec le serveur
echoue.
tcpC = new TcpClient(host,port);
Console.WriteLine(
"Connexion etablie avec {0}:{1}",host,port) ;
// Initialise le flot de donn
ees vers le serveur
649
Exemple :
Connexion etablie avec localhost:50000
Client -> Serveur:Vous avez le bonjour
Serveur -> Client :Vous avez le bonjour
Client -> Serveur:Vous avez le bonjour
Serveur -> Client :Vous avez le bonjour
Client -> Serveur:Vous avez le bonjour
Serveur -> Client :Vous avez le bonjour
du
du
du
du
du
du
client
client
client
client
client
client
!
!
!
!
!
!
650
Exemple 17-9 :
using System ;
using System.Net.NetworkInformation ;
public class Program {
public static void Main() {
NetworkInterface[] nis=NetworkInterface.GetAllNetworkInterfaces();
foreach (NetworkInterface ni in nis)
{
Console.WriteLine("Nom : " + ni.Name) ;
Console.WriteLine("Description : " + ni.Description) ;
Console.WriteLine("Etat : " + ni.OperationalStatus.ToString()) ;
Console.WriteLine("Vitesse : {0} Kb", ni.Speed / 1024) ;
Console.WriteLine("Media Acess Control (MAC):" +
ni.GetPhysicalAddress().ToString()) ;
Console.WriteLine("---------------------------------") ;
}
}
}
Ping
Vous pouvez utiliser la classe System.Net.NetworkInformation.Ping pour dterminer si une
machine distante est accessible par le rseau. En fait cette classe est lquivalente de la commande Ping.
Exemple 17-10 :
using System ;
using System.Net.NetworkInformation ;
public class Program {
public static void Main() {
using (Ping ping = new Ping()) {
PingReply pingReply = ping.Send("www.smacchia.com", 10000) ;
System.Console.WriteLine("IP:{0} Etat:{1}",
pingReply.Address , pingReply.Status) ;
}
}
}
Cette classe implmente linterface IDispose. En outre la mthode Ping.SendAsync() vous permet de dmarrer un ping dune manire asynchrone. Il faut alors se brancher lvnement
Ping.PingCompleted pour obtenir le rsultat du ping.
651
Statistiques rseau
Vous pouvez programmatiquement avoir accs aux statistiques des protocoles IP, ICMP, TCP,
et UDP. Ces statistiques sont les mmes que celles obtenues avec les direntes options de la
commande netstat. Par exemple :
Exemple 17-11 :
using System ;
using System.Net.NetworkInformation ;
public class Program {
public static void Main() {
IPGlobalProperties ipProp =
IPGlobalProperties.GetIPGlobalProperties() ;
IPGlobalStatistics ipStat = ipProp.GetIPv4GlobalStatistics();
Console.WriteLine("Host name:" + ipProp.HostName) ;
Console.WriteLine("Domain name:" + ipProp.DomainName) ;
Console.WriteLine("IPv4 # packets recus:" +
ipStat.ReceivedPackets) ;
Console.WriteLine("IPv4 # packets envoy
es:" +
ipStat.OutputPacketRequests) ;
TcpConnectionInformation[] tcpConns =
ipProp.GetActiveTcpConnections() ;
foreach (TcpConnectionInformation tcpConn in tcpConns) {
Console.WriteLine("localhost:{0} <-> {1}:{2} state:{3}",
tcpConn.LocalEndPoint.Port,
tcpConn.RemoteEndPoint.Address.ToString(),
tcpConn.RemoteEndPoint.Port, tcpConn.State) ;
}
}
}
Vous pouvez nobtenir que les statistiques relatives une interface rseau en utilisant la mthode NetworkInterface.GetIPInterfaceProperties().
Les premiers caractres dun URI reprsentent le mode daccs de lURI (scheme en anglais).
Voici les modes daccs les plus courants :
Le mode daccs file indique que la ressource est un fichier en local ou sur un intranet.
Le mode daccs http indique que la ressource est gre par un serveur web. Le mode
daccs https indique quil faut utilis le protocole HTTP scuris (i.e HTTP au dessus
du protocole SSL dcrit un peu plus bas page 661).
Le mode daccs mailto indique que la ressource est une adresse e-mail.
652
Le mode daccs ftp indique que la ressource est un fichier gr par un serveur FTP
(File Transfert Protocol) (mme remarque pour ftps que pour https).
Le nom du serveur.
Le nom de la ressource.
La classe WebClient
La classe System.Net.WebClient permet dcrire ou de rcuprer des donnes partir dun URI.
Ces donnes peuvent tre stockes sur le systme de fichier local, sur un intranet ou sur internet.
La classe WebClient est trs pratique puisque son implmentation dcide automatique du protocole de transfert de donnes utiliser en fonction de lURI (file, http, https, ftp ou ftps).
Lexemple suivant tlcharge une page HTML partir de son URI puis lache au format texte
sur la console :
Exemple 17-12 :
using System.Net ;
class Program {
static void Main() {
WebClient webClient = new WebClient() ;
string s =
webClient.DownloadString("http://www.microsoft.com/france");
System.Console.Write(s) ;
}
}
La classe WebClient prsente des mthodes telles que void DownloadFile(string uri,string
fileName) ou byte[] DownloadData(string uri) pour faciliter le tlchargement de donnes
stockes dans des fichiers ou au format binaire. Chacune de ces mthodes a une homologue
nomme UploadXXX(string uri, data) permettant dcrire des donnes un emplacement
indiqu par un URI. Notez aussi la prsence du couple de mthodes Stream OpenWrite(string
uri) et Stream OpenRead(string uri) permettant de manipuler une ressource accde par
son URI au moyen dun flot de donnes. Enfin, toutes ces mthodes (DownloadXXX() et
UpLoadXXX()) sont disponibles en une version asynchrone dont le nom est sux par Async.
Dans ce cas, lopration est ralise par un des threads du pool de threads du CLR. Vous pouvez
tre alors prvenu de la terminaison dune opration asynchrone en vous abonnant un
vnement tel que UpdloadDataCompleted de votre instance de la classe WebClient.
653
654
La classe System.Net.HttpListener
Le framework .NET 2.0 prsente la classe System.Net.HttpListener qui permet dexploiter la
couche HTTP.SYS pour dvelopper un serveur HTTP. Bien entendu, vous ne pouvez pas utiliser
cette classe lorsque votre application sexcute sur un OS qui ne supporte pas HTTP.SYS. Le
programme suivant montre comment utiliser cette classe afin de retourner une page HTML
qui contient la date et lheure courante ainsi que lURL demande. Une telle page HTML est retourne pour chaque requte HTTP entrante par le port 8008 destination du chemin /hello :
Exemple 17-14 :
using System.Net ;
using System.IO ;
class Program {
static void Main() {
HttpListener httpListener = new HttpListener() ;
string uri = string.Format("http://localhost:8008/hello/") ;
httpListener.Prefixes.Add(uri);
httpListener.Start();
while (true) {
// Ne pas effectuer la requ
ete si le client
HttpListenerContext ctx = httpListener.GetContext() ;
ctx.Response.ContentType = "text/html" ;
TextWriter writer = new StreamWriter(
ctx.Response.OutputStream, System.Text.Encoding.Unicode) ;
writer.WriteLine(
"<html><body>Page demandee `a {0}<br/>URL:{1}</body></html>",
System.DateTime.Now, ctx.Request.Url) ;
writer.Flush() ;
writer.Close() ;
}
655
}
}
Le dmarrage du serveur HTTP se fait lappel de la mthode Start(). Il faut au pralable
enregistrer toutes les URLs que votre instance de HttpListener va traiter laide de la proprit
Prefixes. HTTP.SYS supporte le protocole HTTPS. En consquence, vous pouvez prciser des
URLs ayant le mode daccs https://. Si une URL est dj traite par HTTP.SYS, une exception
est leve lors de lajout. Une erreur classique est de ne pas prciser de numro de port dans vos
URLs. Le port 80 est alors pris par dfaut mais en gnral ce port est dj utilis par le serveur
IIS si celui-ci est prsent sur la machine en production. Une exception est alors leve.
Une fois le serveur web dmarr, on entre dans une boucle sans fin. Chaque passage dans la
boucle reprsente le traitement dune requte HTTP. La mthode HttpListener.GetContext()
est bloquante jusqu la rception dune requte. Le traitement de la requte est alors matrialis
par une instance de System.Web.HttpListenerContext. On construit alors notre page HTML
que lon inclut dans la rponse HTTP laide du flot de donnes Stream HttpListenerContext.
Response.OutputStream.
En plus des proprits Request et Response, la classe HttpListenerContext prsente la proprit
IPrincipal User{get;} qui prcise lutilisateur Windows responsable de la requte si celui-ci
a t authentifi. HTTP.SYS supporte les quatre modes dauthentification : Connexion Anonyme, Authentification Digest, Authentification de base et Authentification intgre Windows.
Ces modes sont dcrits en page 957.
656
657
client.Credentials = new
NetworkCredential("patrick.smacchia@myserver.fr", "mypwd") ;
MailMessage msg = new MailMessage("patrick@smacchia.com",
"destinataire@xyz.com", "my subject", "my body");
msg.Attachments.Add(new Attachment(@"C:\file.txt"));
client.Send(msg) ;
}
}
Lespace de noms System.Net.Mime contient des classes pour le support de la norme Multipurpose Internet Mail Exchange (MIME). Cette norme dcrit le formatage denttes dans le corps dun
mail. Ces enttes permettent de dcrire la manire dont le corps du mail ainsi que ses attachements doivent tres achs. Par exemple, certains mails sont au format HTML et contiennent
des images.
658
Dans cet exemple, si le code de cration du flot et de ses trois services est isol, le fait que les donnes du flot soient compresses/dcompresses, encryptes/dcryptes puis mises en mmoire
tampon est compltement transparent du point de vue du code qui appelle les mthodes Read()
et Write(). Voici un diagramme de classe UML pour formaliser cette architecture :
Stream
Read([out]buffer)
Write([in]buffer)
NetworkStream
Read([out]buffer)
stream
stream
stream
GZipStream
BufferedStream
CryptoStream
Read([out]buffer)
Write([in]buffer)
Read([out]buffer)
Write([in]buffer)
Read([out]buffer)
Write([in]buffer)
stream.Read(bufferTmp)
//dcompresse les donnes
//de bufferTmp et crit le
//rsultat dans buffer
stream.Read(bufferTmp)
//dcrypte les donnes
//de bufferTmp et crit le
//rsultat dans buffer
Figure 17 -1 : Le design pattern dcorateur et les services sur les flots de donnes
659
pour obtenir les meilleures performances possibles. Empiriquement, la taille de 4Ko constitue
un bon choix, mais nous vous conseillons de faire plusieurs tests avec plusieurs valeurs.
La classe BufferedStream peut tre aussi utilise pour lire les donnes. Notez quil est conseill
de ne pas utiliser une instance de BufferedStream la fois pour lire et crire des donnes. Si
vous utilisez une instance de BufferedStream avec des donnes toujours plus grandes que la
taille interne de la zone de mmoire tampon, il se peut que la zone de mmoire tampon ne
soit jamais alloue.
Une instance de la classe System.IO.MemoryStream peut tre utilise dans le mme esprit que
BufferedStream pour amliorer les performances dun flot de donnes rseau. La dirence
dutilisation est que vous avez une marge de manuvre plus large avec MemoryStream. Une
instance de MemoryStream nest pas explicitement lie avec une instance de NetworkStream. Vous
ne prcisez le flot de donnes sous-jacent que lors de lappel la mthode WriteTo(). Ainsi, vous
pouvez par exemple envoyer les mmes donnes contenues dans la mme zone de mmoire
tampon, plusieurs clients.
La version non gre System.IO.UnmanagedMemoryStream de la classe MemoryStream permet
dviter la copie des donnes sur le tas des objets du CLR. Elle est donc plus ecace.
Notez enfin quil nest pas ncessaire dutiliser la classe BufferedStream pour utiliser une zone
de mmoire tampon avec une instance de FileStream. En eet, comme nous lavons vu, la classe
FileStream gre nativement en interne une zone de mmoire tampon.
660
Il est regrettable que lon ne puisse pas utiliser simplement la classe GZipStream pour manipuler les fichiers dextension .zip. Aussi, voici un lien vers la bibliothque gratuite et opensource SharpZipLib qui permet deectuer ceci : http://www.icsharpcode.net/OpenSource/
SharpZipLib/Default.aspx
Support des protocoles SSL, NTLM et Kerberos de scurisation des donnes changes
661
La classe SslStream
La classe SslStream est utilise pour exploiter le protocole SSL. Reprenons le code du client/serveur
TCP de la page 642 afin dillustrer la scurisation dun flot de donnes TCP avec le protocole
SSL :
Exemple 17-19 :
Serveur.cs
...
private static X509Certificate getServerCert() {
X509Store store = new X509Store(StoreName.My,
StoreLocation.CurrentUser) ;
store.Open(OpenFlags.ReadOnly) ;
X509CertificateCollection certs = store.Certificates.Find(
X509FindType.FindBySubjectName, "CN=SslSvrCertif", true) ;
return certs[0] ;
}
static void TraiteClient(NetworkStream networkStream) {
SslStream streamSsl = new SslStream( networkStream );
streamSsl.AuthenticateAsServer( getServerCert() );
StreamWriter streamWriter = new StreamWriter( streamSsl ) ;
StreamReader streamReader = new StreamReader(
@"C:/Text/Fichier.txt") ;
string sTmp = streamReader.ReadLine() ;
try {
while (sTmp != null) {
Console.WriteLine("Envoi de : {0}", sTmp) ;
streamWriter.WriteLine(sTmp) ;
streamWriter.Flush() ;
sTmp = streamReader.ReadLine() ;
}
} finally {
streamReader.Close() ;
streamWriter.Close() ;
streamSsl.Close();
networkStream.Close() ;
}
}
...
On voit que du cot serveur, un nouveau flot de donnes de type SslStream se place entre le
flot de donnes rseau networkStream et le flot de donnes streamWriter utilis pour envoyer
des donnes.
Pour crer ce flot de donnes SSL, nous rcuprons un certificat X509 nomm SslSvrCertif
stock dans le rpertoire Personnel de lutilisateur actuel. Le fait de passer ce certificat la
mthode AuthenticateAsServer() permet lauthentification SSL du serveur avec ce certificat.
Voici le code du client :
662
Exemple 17-20 :
Client.cs
...
NetworkStream networkStream = tcpC.GetStream() ;
SslStream streamSsl = new SslStream (
networkStream,false, ValidateSvrCertificateCallback );
streamSsl.AuthenticateAsClient( "SslSvrCertif" );
StreamReader streamReader = new StreamReader( streamSsl );
try {
string sTmp = streamReader.ReadLine() ;
while (sTmp != null) {
Console.WriteLine("Reception de : {0}", sTmp) ;
sTmp = streamReader.ReadLine() ;
}
} finally {
streamReader.Close() ;
streamSsl.Close();
networkStream.Close() ;
Console.WriteLine("Deconnexion de {0}:{1}", host, port) ;
}
...
static bool ValidateSvrCertificateCallback(object sender,
X509Certificate certificate,
X509Chain chain,
SslPolicyErrors sslPolicyErrors) {
if ( sslPolicyErrors != SslPolicyErrors.None ) {
Console.WriteLine(
"Erreur dans la validation du SSL Certificate !") ;
Console.WriteLine( sslPolicyErrors.ToString() ) ;
return false ;
} else return true ;
}
...
Ici aussi un flot de donnes de type SslStream se place entre le flot de donnes rseau
networkStream et le flot de donnes streamWriter utilis pour recevoir des donnes du serveur.
La mthode ValidateSvrCertificateCallback() est appele lors de lauthentification du certificat du serveur. Si cette mthode retourne true, le serveur est considr comme authentifi.
Cette mthode est connue de notre instance de SslStream car elle est rfrence par un dlgu
pass son constructeur.
On passe le nom du certificat du serveur la mthode AuthenticateAsClient(). Une autre
surcharge de cette mthode permet de passer un certificat spcifique au client pour son authentification. Lauthentification du client est optionnelle avec le protocole SSL et comprenez bien
quici, nous ny avons pas recours.
La classe NegociateStream
La classe NegociateStream est utilise pour exploiter les protocoles Windows NTLM et Kerberos.
Reprenons nos exemples afin dillustrer lutilisation de ces protocoles sur un flot de donnes
TCP. Le niveau de protection le plus lev, savoir EncryptAndSign, est choisi :
Support des protocoles SSL, NTLM et Kerberos de scurisation des donnes changes
Exemple 17-21 :
663
Serveur.cs
...
static void TraiteClient(NetworkStream networkStream) {
NegotiateStream streamAuth = new NegotiateStream(networkStream);
streamAuth.AuthenticateAsServer(
CredentialCache.DefaultNetworkCredentials,
ProtectionLevel.EncryptAndSign,
TokenImpersonationLevel.Impersonation);
WindowsPrincipal principal = new WindowsPrincipal(
streamAuth.RemoteIdentity as WindowsIdentity);
// Ne pas effectuer la requ
ete si le client
// nest pas un administrateur.
if( !principal.IsInRole( @"BUILTIN\Administrators" ) ){
networkStream.Close();
return;
}
StreamWriter streamWriter = new StreamWriter(streamAuth) ;
StreamReader streamReader = new StreamReader(
@"C:/Text/Fichier.txt") ;
string sTmp = streamReader.ReadLine() ;
try {
while (sTmp != null) {
Console.WriteLine("Envoi de : {0}", sTmp) ;
streamWriter.WriteLine(sTmp) ;
streamWriter.Flush() ;
sTmp = streamReader.ReadLine() ;
}
} finally {
streamReader.Close() ;
streamWriter.Close() ;
streamAuth.Close();
networkStream.Close() ;
}
}
...
On observe quun flot de donnes de type NegociateStream vient sintercaler entre le flot de
donnes rseau networkStream et le flot de donnes streamWriter utilis pour envoyer les donnes. Nous rcuprons lutilisateur Windows utilis par le client avec la proprit IIdentity
NegociateStream.RemoteIdentity{get;}. Lidentit de cet utilisateur a t achemine automatiquement par le protocole sous-jacent (NTLM ou Kerberos). Si cet utilisateur nest pas un
administrateur, nous choisissons de ne pas satisfaire sa requte. Une autre alternative aurait t
par exemple daecter cet utilisateur au thread courant durant la dure de la requte (impersonification) afin que les vrifications des permissions soient ralises implicitement. Ici, seule la
permission de lire le fichier C:/Text/Fichier.txt aurait t requise.
Voici le code du client. On remarque que cest lidentit de lutilisateur sous lequel ce code sexcute qui est envoye au serveur par limplmentation de NegociateStream :
664
Exemple 17-22 :
Client.cs
...
NetworkStream networkStream = tcpC.GetStream() ;
NegotiateStream streamAuth=new NegotiateStream(networkStream);
streamAuth.AuthenticateAsClient(
CredentialCache.DefaultNetworkCredentials,
WindowsIdentity.GetCurrent().Name,
ProtectionLevel.EncryptAndSign,
TokenImpersonationLevel.Impersonation);
StreamReader streamReader = new StreamReader( streamAuth ) ;
try {
// Chaque passage dans la boucle = une ligne r
ecup
er
ee.
string sTmp = streamReader.ReadLine() ;
while (sTmp != null) {
Console.WriteLine("Reception de : {0}", sTmp) ;
sTmp = streamReader.ReadLine() ;
}
} finally {
// Ferme les flots de donn
ees.
streamReader.Close() ;
streamAuth.Close();
networkStream.Close() ;
Console.WriteLine("Deconnexion de {0}:{1}", host, port) ;
}
...
18
Les applications fentres
(Windows Forms)
Les applications en mode console : on les excute dans une fentre de commande, style
DOS.
Les applications avec fentres : on peut les excuter partir dune ligne de commande ou
partir dune icone, dans un explorateur.
Cette dualit applications en mode console/applications avec fentre existe encore sous .NET.
Le compilateur C csc.exe a besoin de savoir si lexcutable quil produit est une application en mode console (option /target:exe) ou une application avec fentre (option /target:winexe).
666
Un identifiant message qui indique le type dvnement (clic droit souris, touche clavier
enfonce, etc).
Des paramtres dont les types et le nombre varient en fonction de lentre (position souris
sur lcran, code touche clavier etc).
chaque type dvnement correspond une procdure de rappel (callback procedure). Pour une
fentre donne, le dveloppeur a la possibilit dcrire, ou de rcrire, une procdure de rappel
pour un vnement particulier (par exemple un clic gauche sur un bouton).
Dans les applications avec fentres, chaque fentre a un thread et un seul qui attend la rception de messages dans une file dattente propre au thread. Un tel thread peut grer plusieurs
fentres. Lorsquun message arrive, le thread excute la procdure de rappel adquate. Le code
principal dun tel thread est donc constitu dune boucle qui est excute chaque rception
dun message. Cette boucle contient entre autres, un gigantesque switch (plusieurs centaines de
cas) qui associe les procdures de rappel (les callbacks) aux vnements.
// Tool Tips
ON_WM_SHOWWINDOW()
//
//
//
Evenement fen
etre vue ou cach
ee.
Correspond `
a la proc
edure de rappel
standard OnShowWindow().
ON_WM_MOUSEMOVE()
//
//
//
ON_WM_PAINT()
//
//
//
Evenement La r
egion visible de la fen
etre est
modifi
ee. Correspond `
a la proc
edure de rappel
standard OnPaint().
667
ON_BN_CLICKED(IDC_BN_VALIDATE, OnBnClickedValidate)
// Evenement Le bouton dID IDC_BN_VALIDATE est cliqu
e.
// Correspond `a la proc
edure de rappel propri
etaire
// OnBnClickedValidate().
END_MESSAGE_MAP()
LIDE Visual Studio simplifiait cette gestion en permettant de placer les contrles de la fentre
laide dun diteur WYSIWYG (What You See Is What You Get). Cet diteur ajoutait automatiquement les associations vnements/procdures de rappel dans la table. Cet diteur avait cependant lnorme dfaut de ne pas pouvoir modifier ou supprimer les associations quil produisait. Il fallait une certaine exprience pour raliser ces oprations la main. De plus lditeur
se servait dun fichier unique pour dcrire toutes les fentres dune application. De nombreux
conflits daccs en criture ce fichier survenaient entre les dveloppeurs de lapplication.
Cette section est crite au pass, car pour le dveloppeur .NET, tout ceci est presque du pass.
Le travail du dveloppeur dapplications avec fentres est grandement simplifi sous .NET grce
la technologie Windows Forms. Comme nous allons lexposer dans la prochaine section, la
description du contenu des fentres ainsi que les associations vnements/procdures de rappel
sont trs intuitives, et ne font pas appels des fichiers ou du code superflu. Ceci implique
notamment quune application avec fentres peut tout fait tre dveloppe sans diteur particulier. Un simple diteur de texte comme le bloc-notes sut.
Les formulaires :
Ce sont les classes qui contiennent les comportements de base des formulaires. Il vous sut
de construire une classe qui hrite dune de ces classes formulaires pour crer votre propre
formulaire. On peut citer la classe System.Windows.Forms.Form, qui reprsente la classe de
base pour tous les formulaires.
Les contrles :
Un contrle est un lment dun formulaire. Une trentaine de types de contrles classiques
sont fournis par le framework .NET, comme le bouton ou la zone ddition de texte. Ils ont
tous la particularit de driver de la classe System.Windows.Forms.Control qui dcrit le
comportement de base dun contrle. La section suivante liste toutes les classes de contrles
standard et explique comment vous pouvez aussi crer vos propres classes de contrles.
Les composants :
Dans le domaine des applications graphiques, les composants permettent de rajouter des
fonctionnalits vos formulaires Par exemple la classe System.Windows.Forms.ToolTip
permet dajouter la fonctionnalit de description par tooltip des contrles, un formulaire.
On peut aussi citer la fonctionnalit de menu et daide la demande de lutilisateur.
La classe de base de tous les composants est la classe System.ComponentModel.Component.
Les classes Control et Form drivent de la classe Component. La notion de composant est donc
plus globale que les notions de contrle et de formulaire. De plus, la notion de composant
nest pas cantonne au domaine des applications graphiques. Plus de dtails sur la notion
de composant sont disponibles dans larticle Class vs. Component vs. Control des MSDN.
668
669
private System.Windows.Forms.TextBox
...
textBoxFrancs ;
Avec Visual Studio 2003, un formulaire Windows Forms tait compltement dfini sur un mme
fichier source C . Le code gnr par cet IDE tait spar de votre propre code au moyen de
rgions. Visual Studio .2005 exploite la notion de classe partielle de C 2.0 au niveau de la gestion
des formulaires. Par dfaut, chaque formulaire nomm FormXXX correspond deux fichiers :
FormXXX.cs qui contient votre code et FormXXX.Designer.cs qui contient le code gnr par
Visual Studio 2005. Par souci de simplicit, les exemples de code des formulaires du prsent ouvrage tiennent chacun sur un seul fichier source C .
670
Les noms des champs du formulaire rfrenant les contrles, ont t modifis aussi dans la
fentre des proprits de la Figure 18-2. Dans la mthode InitializeComponent() de la classe
du formulaire, lditeur a ajout automatiquement du code qui permet de crer les contrles,
de positionner les contrles et de configurer la taille et les champs des contrles. Par exemple
le code ajout pour un bouton est :
...
//
// Euros2Francs
//
this.Euros2Francs.Location
this.Euros2Francs.Name
this.Euros2Francs.Size
this.Euros2Francs.TabIndex
this.Euros2Francs.Text
=
=
=
=
=
...
Comme vous le voyez, contrairement la gestion des fentres avec les MFC, il ny a aucune ligne
de code superflue et tout ce qui concerne un formulaire se trouve dans la classe de ce formulaire.
Ajout dvnements
Chaque type de contrle prsente un ensemble dvnements. On parle bien du mme concept
dvnement que celui prsent par C car on parle dvnements de la classe du contrle.
chaque vnement dun contrle, vous pouvez associer une mthode de la classe du formulaire
contenant le contrle. Cette mthode constitue alors la procdure de rappel de lvnement.
Par exemple le type de contrle Button prsente lvnement Click qui dclenche lappel la
procdure de rappel associe, lorsque le bouton est cliqu. En double-cliquant sur le bouton de
conversion Euros vers Francs dans lditeur, Visual Studio ajoute automatiquement la mthode
suivante la classe formulaire :
...
private void Euros2Francs_Click(object sender, System.EventArgs e) {
// `a remplir avec votre propre code.
}
...
Lditeur associe cette mthode lvnement Click avec le code suivant plac dans la mthode
InitializeComponent() aprs linitialisation des champs du contrle Euros2Francs :
...
this.Euros2Francs.Click += new
System.EventHandler(this.Euros2Francs_Click) ;
...
Plutt que de double-cliquer le bouton dans lditeur, on peut facilement associer les vnements dun contrle aux mthodes dun formulaire, partir des proprits du contrle comme
le montre la Figure 18-3. Notez que pour accder ce sous-menu des proprits, il faut cliquer
licone qui ressemble un clair.
Notez enfin que le prototype des mthodes qui servent de procdures de rappel dun vnement
est immuable. Il doit tre gal au prototype de la dlgation System.EventHandler.
671
672
Si lon avait choisi /target:exe, le compilateur naurait pas produit derreur. La dirence est
que lexcutable gnr aurait besoin dune console pour sexcuter. Si nous le lancions partir
dun explorateur, cet excutable se crerait sa propre console.
Il est vident que lditeur de formulaire de Visual Studio est surtout utile pour pr visualiser et
rgler laspect du formulaire. La majorit du code suivant est constitu par linitialisation des
contrles. Seul le code en gras a eectivement t crit la main.
Convertisseur.cs
Exemple 18-1 :
using System.Drawing ;
using System.ComponentModel ;
using System.Windows.Forms ;
namespace WindowsFrancEuro {
public class MyForm : Form {
private Button Francs2Euros ;
private Button Euros2Francs ;
private TextBox textBoxEuros ;
private Label label1 ;
private Label label2 ;
private TextBox textBoxFrancs ;
public MyForm() { InitializeComponent() ; }
private void InitializeComponent() {
this.Francs2Euros = new Button() ;
this.Euros2Francs = new Button() ;
this.label1 = new Label() ;
this.label2 = new Label() ;
this.textBoxEuros = new TextBox() ;
this.textBoxFrancs = new TextBox() ;
this.SuspendLayout() ;
// Francs2Euros
this.Francs2Euros.Location = new Point(160, 16) ;
this.Francs2Euros.Name = "Francs2Euros" ;
this.Francs2Euros.Size = new System.Drawing.Size(96, 32) ;
this.Francs2Euros.TabIndex = 0 ;
this.Francs2Euros.Text = "Francs -> Euros" ;
this.Francs2Euros.Click += this.Francs2Euros_Click ;
// Euros2Francs
this.Euros2Francs.Location = new Point(160, 56) ;
this.Euros2Francs.Name = "Euros2Francs" ;
this.Euros2Francs.Size = new Size(96, 32) ;
this.Euros2Francs.TabIndex = 1 ;
this.Euros2Francs.Text = "Euros -> Francs" ;
this.Euros2Francs.Click += this.Euros2Francs_Click ;
// label1
673
674
Soit la fentre mre reste en arrire-plan et est gele, jusqu ce que la fentre fille disparaisse. Dans ce cas on dit que la fentre fille est modale. Pour crer un formulaire modal il
faut utiliser la mthode ShowDialog() de la classe Form :
...
MaClasseDeFormulaire UnFormulaire = new MaClasseDeFormulaire()
// La methode ShowDialog() ne retourne que lorsque le formulaire
// UnFormulaire disparat.
UnFormulaire.ShowDialog() ;
...
Soit on peut continuer utiliser la fentre mre, mme si la fentre fille est encore prsente.
Dans ce cas on dit que la fentre fille est non modale (modeless en anglais). Pour crer un
formulaire non modal il faut utiliser la mthode Show() de la classe Form :
...
MaClasseDeFormulaire UnFormulaire = new MaClasseDeFormulaire()
// La methode Show() retourne imm
ediatement apr`
es la cr
eation du
// formulaire.
UnFormulaire.Show() ;
...
Il est plus courant dutiliser des fentres modales que des fentres non modales. En eet, les
fentres modales sont trs adaptes la saisie de donnes de faon squentielle. Cependant, dans
certains scnarios o plusieurs fentres sont ncessaires lexploitation de lapplication, on ne
peut quutiliser des fentres non modales. Par exemple Visual Studio utilise de trs nombreuses
fentres non modales, une pour la vue des fichiers, une pour la vue du projet, une pour acher
les messages du compilateurs etc. Mais comprenez bien quune application avec trop de fentres
non modales est souvent trs confuse pour lutilisateur (rappelez-vous votre raction lors de la
premire ouverture dun IDE).
675
Soit parce que lutilisateur fait dfiler le focus dun contrle lautre en appuyant sur la
touche TAB ou MAJ+TAB. Vous pouvez modifier lordre de dfilement des contrles en
aectant des valeurs croissantes aux proprits TabIndex des contrles.
Description
MouseDown
MouseUp
Click
DoubleClick
Un bouton de la souris est double-cliqu (il vaut mieux ne pas implmenter cet vnement en mme temps que lvnement Click. Dans
ce cas Windows appelle les deux procdures de rappel lors dun doubleclic).
MouseMove
MouseEnter
MouseLeave
MouseHover
676
MouseWheel
Lvnement Paint
Lvnement Paint, avec sa procdure de rappel OnPaint(), est trs important dans les applications graphiques sous les systmes dexploitation Windows. En eet, les achages des fentres
ne sont pas persistants. Cela veut dire que si une fentre (ou une partie dune fentre) devient
visible alors quelle ne ltait pas, il est ncessaire que le thread responsable de cette fentre
reconstruise cette partie. Lorsquune fentre (ou une partie dune fentre) devient visible, les
systmes dexploitation Windows envoient le message WM_PAINT. Sous .NET cela se traduit par le
dclenchement de lvnement Paint du formulaire concern et par lappel de la procdure de
rappel OnPaint(). Parmi les arguments de cet vnement, il y a notamment les coordonnes
du rectangle dessiner. La plupart du temps, vous ne vous occuperez pas de cet vnement
car par dfaut, lvnement est automatiquement transmis aux contrles qui doivent se racher. Nanmoins, lorsque vous utilisez la bibliothque GDI+ ou lorsque vous faites vos propres
contrles, il faut rcrire la mthode OnPaint() avec votre propre code.
Oprations asynchrones
Dans une application Windows Forms, lorsque le thread qui traite les messages Windows est bloqu par lexcution dune opration longue, lutilisateur est en gnral exaspr de voir sa fentre gele. Pour pallier ce problme, on dlgue en gnral lopration longue soit un thread
du pool soit un thread cr spcialement pour loccasion.
Windows Forms 2.0 prsente la classe BackgroundWorker qui permet de standardiser le dveloppement dopration asynchrone au sein dun formulaire. Cette classe prsente notamment des facilits pour signaler priodiquement lavancement et pour annuler lopration. Son utilisation est
illustre par lexemple suivant qui dlgue le calcul de nombres premiers au moyen dune instance de la classe System.ComponentModel.BackgroundWorker. Lavancement du traitement est
signal par une barre de progression et vous avez la possibilit dannuler lopration au moyen
dun bouton. Dans cet exemple, seules les mthodes DoWork() et IsPrime() sont excute par
un thread background du pool :
677
678
La classe System.Windows.Forms.MessageBox permet dacher trs simplement une fentre avec du texte et les combinaisons de boutons classiques :
OK ;
OK/Cancel ;
Abort/Retry/Ignore ;
Retry/Cancel ;
Yes/No ;
Yes/No/Cancel.
Lachage se fait grce la mthode statique Show() de cette classe qui est surcharges en
de nombreuse versions.
679
La classe System.Windows.Forms.ClipBoard permet de placer ou de rcuprer des informations sur le clipboard. Nutilisez jamais cette technique pour communiquer des informations entre processus. Le clipboard doit tre strictement rserv aux donnes utilisateurs.
680
681
System.Windows.Forms.ToolStripDropDown
System.Windows.Forms.ToolStripDropDownMenu
System.Windows.Forms.ContextMenuStrip
System.Windows.Forms.ScrollBar
System.Windows.Forms.HScrollBar
System.Windows.Forms.VScrollBar
System.Windows.Forms.Splitter
System.Windows.Forms.StatusBar
System.Windows.Forms.TabControl
System.Windows.Forms.TextBoxBase
System.Windows.Forms.RichTextBox
System.Windows.Forms.TextBox
System.Windows.Forms.MaskedTextBox
System.Windows.Forms.ToolBar
System.Windows.Forms.TrackBar
System.Windows.Forms.TreeView
System.Windows.Forms.WebBrowserBase
System.Windows.Forms.WebBrowser
Nous vous invitons consulter les MSDN et faire vos propres essais pour avoir plus de dtails
sur chacune de ces classes.
682
Le contrle WebBrowser :
Ce contrle permet dinsrer un navigateur web directement dans vos fentres Windows
Forms.
Le contrle MaskedTextBox :
Ce contrle ache une TextBox dans laquelle le format du texte insrer est contraint.
Plusieurs types de masques sont prsents par dfaut tel que la contrainte dentrer une date
ou un numro de tlphone au format US. Naturellement, vous pouvez fournir vos propres
masques.
Des tracs graphiques grce lutilisation de la bibliothque GDI+, qui fait lobjet de la
dernire section du prsent chapitre.
683
684
685
Utilisation du contrle
Notre contrle sutilise exactement de la mme manire quun contrle standard. Il faut juste
rfrencer lassemblage dans lequel est dfini notre contrle (dans notre exemple, le contrle
est dans le mme assemblage que le code qui lutilise mais ce nest pas le cas en gnral).
Le formulaire client de notre contrle est montr la Figure 18-7. Un clic sur le bouton Nouveau tirage produit un pourcentage alatoire qui est aect aux trois instances du contrle.
686
Figure 18 -6 : Configuration des proprits dun contrle propritaire avec Visual Studio .NET
Nous avons utilis trois instances pour illustrer le fait que vous pouvez choisir de dessiner la
grille et/ou le contour.
{
controlPourCent1 ;
controlPourCent2 ;
controlPourCent3 ;
687
688
689
Un contrle non visuel de type BindingSource qui reprsente la liaison entre notre source
de donnes (i.e le DataSet typ) et les contrles visuels de prsentation et ddition des
donnes.
Un contrle visuel de type DataGridView qui prsente la liste des employs et qui permet
aussi dditer chaque cellule.
Un contrle visuel de type BindingNavigator qui contient des boutons style VCR pour
naviguer dans la liste des employs. Ce contrle prsente aussi un bouton pour ajouter
un nouvel employ, un bouton pour dtruire lemploy couramment slectionn et un
bouton pour sauver les changements raliss sur la liste des employs.
690
ddition Visual Studio 2005 a ajout les dix contrles. On obtient ainsi une vue matresse
dtaille trs pratique pour ldition et linsertion des donnes des lignes dune table. Notez
quen page 942 nous exposons comment obtenir ce type de vue dans le cadre dune page web.
Il est intressant danalyser les lignes de code ddies la cration des liaisons. Elles se trouvent
toutes dans la mthode gnr InitializeComponent() du fichier FormEmployes.Designer.cs.
Nous avons ajout des commentaires explicatifs :
private void InitializeComponent() {
...
// Cree le BindingSource.
this.eMPLOYESBindingSource =
new System.Windows.Forms.BindingSource(this.components) ;
...
((System.ComponentModel.ISupportInitialize)
(this.eMPLOYESBindingSource)).BeginInit() ;
...
// Lie le BindingSource avec la table EMPLOYES du DataSet typ
e.
this.eMPLOYESBindingSource.DataMember = "EMPLOYES" ;
this.eMPLOYESBindingSource.DataSource = this.oRGANISATIONDataSet ;
...
// Lie le BindingNavigator avec le BindingSource.
this.eMPLOYESBindingNavigator.BindingSource =
this.eMPLOYESBindingSource ;
...
// Lie la DataGridView avec le BindingSource.
this.eMPLOYESDataGridView.DataSource = this.eMPLOYESBindingSource ;
...
// Pour chaque colonne de la DataGridView, indique
// la colonne associee dans la table des employ
es.
this.dataGridViewTextBoxColumn1.DataPropertyName =
...
this.dataGridViewTextBoxColumn2.DataPropertyName =
...
this.dataGridViewTextBoxColumn3.DataPropertyName =
...
this.dataGridViewTextBoxColumn4.DataPropertyName =
...
this.dataGridViewTextBoxColumn5.DataPropertyName =
...
le nom de
"EmployeID" ;
"DepID" ;
"Nom" ;
"Pr
enom" ;
"T
el" ;
691
...
this.nomTextBox.DataBindings.Add(
new System.Windows.Forms.Binding("Text",
this.eMPLOYESBindingSource, "Nom", true)) ;
...
this.prenomTextBox.DataBindings.Add(
new System.Windows.Forms.Binding("Text",
this.eMPLOYESBindingSource, "Pr
enom", true)) ;
...
this.telTextBox.DataBindings.Add(
new System.Windows.Forms.Binding("Text",
this.eMPLOYESBindingSource, "T
el", true)) ;
...
((System.ComponentModel.ISupportInitialize)
(this.eMPLOYESBindingSource)).EndInit() ;
...
}
692
Comme nous lavons vu, on peut se servir dun contrle BindingSource pour lier des donnes aux contrles de prsentation au moment du design de la fentre avec Visual Studio
2005.
Les contrles de type BindingSource vous permettent de fournir votre propre logique de
cration dun nouvel lment. Plus de dtails ce sujet sont disponibles dans larticle How
to: Customize Item Addition with the Windows Forms BindingSource des MSDN.
Grce aux proprits Filter et Sort de la classe BindingSource, vous pouvez filtrer ou trier
les donnes dune source avant de les prsenter.
Les contrles de type BindingSource permettent dexploiter simplement une relation one
to many . Considrons la relation nomme Est_Employ
e_Par entre les tables EMPLOYES et
DEPARTEMENTS de notre DataSet typ (cette relation est dfinie en page 710). Crons une
fentre avec deux DataGridView et deux BindingSource. La premire BindingSource est
lie avec la table DEPARTEMENT. La seconde est lie avec la premire avec pour valeur de la
proprit DataMember le nom de la relation. La seconde BindingSource est alors capable de
dtecter les changements de slection de dpartement sur la premire. Le cas chant, elle
applique la relation et ne retourne que les employs appartenant au dpartement slectionn :
Exemple 18-6 :
...
private void Form1_Load(System.Object sender, System.EventArgs e) {
depDataGridView.DataSource = depBindingSource;
empDataGridView.DataSource = empBindingSource;
depTableAdapter.Fill(dSet.DEPARTEMENTS) ;
empTableAdapter.Fill(dSet.EMPLOYES) ;
depBindingSource.DataSource = dSet ;
depBindingSource.DataMember = "DEPARTEMENTS" ;
empBindingSource.DataSource = depBindingSource;
empBindingSource.DataMember = "Est_Employ
e_Par";
}
...
693
Nimporte quel objet qui reprsente un tableau (i.e dont la classe drive de la classe System.
Array).
Une instance de la clases System.Type pour prciser le type des objets qui peuvent tre
ajouts.
Nimporte quel objet. Le type dun tel objet constituera alors le type des objets qui peuvent
tre ajouts.
694
695
La bibliothque GDI+
La bibliothque GDI+ (GDI pour Graphical Device Interface) contient de nombreuses classes qui
permettent deectuer toutes sortes de tracs : des tracs de lignes, des tracs de courbes, des
dgrads, acher des images etc. GDI+ remplace lancienne bibliothque GDI utilises par les
dveloppeurs sous Windows. En plus de bnficier de toute la convivialit des langages .NET,
cette bibliothque prsente de nouvelles fonctionnalits, notamment en ce qui concerne les
formats des fichiers images et lachage de dgrads. La bibliothque GDI+ supporte les formats
JPG, PNG, GIF et BMP alors que la bibliothque GDI ne supportait que le format BMP ( moins
dutiliser dautres librairies non standard).
La classe System.Drawing.Graphics
La bibliothque GDI+ permet de dessiner, cest--dire de faire toutes sortes de formes gomtriques tel que des rectangles, des lignes et mme des courbes de Bzier. Dans les mthodes de
chaque contrle ou formulaire, on peut se procurer une instance de la classe System.Drawing.
Graphics en appelant la mthode CreateGraphics() de la classe Control ou Form. Les instances
de cette classe constituent le support sur lequel on peut dessiner, de la mme faon quune
feuille de papier est le support du dessinateur. Pour ceux qui ont dj travaill avec GDI, les
instances de cette classe sont les quivalentes des instances Device Context (DC).
Il est important dappeler la mthode Dispose() ds que possible, sur toutes les instances
de la classe Graphics obtenues par lappel une mthode CreateGraphics().
696
La classe System.Drawing.Pen
De la mme manire quun dessinateur a besoin dun crayon pour tracer une courbe, les mthodes de la classe Graphics pour le trac de lignes, de courbes ou de contour de figures, ont
besoin dune instance de la classe System.Drawing.Pen.
Les constructeurs de la classe Pen acceptent soit une instance de la structure System.
Drawing.Color pour spcifier la couleur du trait, soit une instance de la classe System.
Drawing.Brush pour spcifier le type de remplissage dun trait pais.
Vous pouvez indiquer lpaisseur, en pixel, du trait avec la proprit Width de la classe Pen.
Vous pouvez indiquer si vous souhaitez un trac plein, en tir ou en pointill avec la proprit DashStyle de la classe Pen.
Vous pouvez indiquer quel type de dessin doit tre plac aux extrmits du trait avec la
proprit StartCap et EndCap de la classe Pen.
La classe System.Drawing.Brush
De la mme manire quun dessinateur a besoin dun pinceau pour remplir une surface, les
mthodes de la classe Graphics pour le remplissage dune surface, ont besoin dune instance
dune classe drive de la classe System.Drawing.Brush. Ces classes drives sont les suivantes
(elles sont toutes dans lespace de noms System.Drawing.Drawing2D) :
La bibliothque GDI+
697
Par exemple le code suivant ache le pentagone de la Figure 18-11, rempli dune manire alterne par un croisement de diagonales :
Exemple 18-9 :
...
using (Graphics g = CreateGraphics()) {
Brush brush = new HatchBrush( HatchStyle.DiagonalCross,
Color.White, Color.Black) ;
Point[] pts = new Point[5] ;
pts[0] = new Point(50, 3) ;
pts[1] = new Point(30, 100) ;
pts[2] = new Point(80, 30) ;
pts[3] = new Point(4, 35) ;
pts[4] = new Point(70, 100) ;
g.FillClosedCurve(brush, pts, FillMode.Alternate) ;
}
...
698
Format associ
Bmp
Gif
Jpeg
Png
Tiff
La proprit PixelFormat de la classe Image dtermine le nombre de bits par pixel (bpp) dans
limage et prend ses valeurs dans lnumration System.Drawing.Imaging.PixelFormat. Dans
le cas des formats de pixel o la valeur attribue chaque pixel dtermine un index dans un
tableau de couleur (appel palette de limage), vous devez utiliser la proprit Palette de la
classe Image.
Pour acher une image dans un formulaire ou dans un contrle, il sut dutiliser une des
versions surcharges de la mthode DrawImage() de la classe Graphics (prsente un peu plus
haut).
Les transformations que vous pouvez appliquer une image sont limites aux flip (i.e retournements verticaux et horizontaux) et aux rotations dangle droit. On verra dans la prochaine
section comment traiter plus srieusement une image.
Il est conseill dintgrer les images dans des fichiers de ressources intgrs lassemblage courant ou un assemblage satellite.
La bibliothque GDI+
699
700
Nous allons montrer le traitement dimage qui consiste inverser les couleurs. Supposons que
le nombre de bits par pixel est de 24. Linversion des couleurs consiste aecter le complment
255 pour chacune des trois composantes, pour chacun des pixels de limage. La Figure 18-13
montre lapplication de ce traitement une image. Pour lanecdote sachez que cette image, souvent utilise pour tester les traitements dimage, est la photographie de la playmate sudoise
Lena Soderberg, extraite du magazine Playboy en 1972. Par la suite, elle fut invite certaines
confrences sur le traitement dimage.
Soit nous utilisons les mthodes SetPixel() et GetPixel() de la classe Bitmap. Voici lextrait pertinent du code source :
Exemple 18-12 :
...
using (Graphics g = CreateGraphics()) {
Bitmap m_Bmp = new Bitmap("Lena.jpg") ;
g.DrawImage(m_Bmp, new Point(5, 5)) ;
// Attend 1 seconde...
System.Threading.Thread.Sleep(1000) ;
int width = m_Bmp.Width ;
int height = m_Bmp.Height ;
Color cSrc, cDest ;
for (int y = 0 ; y < height ; y++)
for (int x = 0 ; x < width ; x++) {
cSrc = m_Bmp.GetPixel(x, y) ;
cDest = Color.FromArgb(255 - cSrc.R, 255 - cSrc.G,
255 - cSrc.B) ;
m_Bmp.SetPixel(x, y, cDest) ;
}
g.DrawImage(m_Bmp, new Point(5, 5)) ;
}
...
La bibliothque GDI+
701
Soit nous passons directement par des pointeurs pour accder aux pixels de limage. Cette
technique optimise le code par rapport la technique prcdente dun facteur 20
100 selon le traitement appliquer ! Cette technique est un peu plus dlicate implmenter, mais un tel facteur doptimisation vaut bien ces eorts. Il faut notamment tenir
compte des points suivants :
La mthode (ou la partie du code) qui ralise le traitement doit tre qualifie avec le
mot-cl unsafe pour pouvoir manipuler des pointeurs (voir page 501).
Il faut verrouiller les accs la zone de mmoire du bitmap en utilisant les mthodes
LockBits()/UnlockBits() de la classe Bitmap.
702
La bibliothque GDI+
703
contient la dernire image cre tandis que lautre contient limage en cours de construction.
Ds quune image est produite, le rle des buers est invers. Il est particulirement ais dutiliser cette technique sur vos propres formulaires. Il sut dappeler la mthode SetStyle() avec
les bons arguments dans le constructeur de votre formulaire aprs linitialisation des composants. Ainsi pour ne plus avoir ce problme lors de la rotation de notre carr il sut de rcrire
notre exemple comme ceci :
Exemple 18-15 :
...
timer.Start() ;
SetStyle(
ControlStyles.AllPaintingInWmPaint |
ControlStyles.UserPaint |
ControlStyles.OptimizedDoubleBuffer,true) ;
}
private void OnTimer(object sender, System.EventArgs e) {
...
Il se peut que ce mcanisme de double buering ne soit pas adapt vos animations. Dans ce
cas, vous pouvez avoir recours aux classes BufferedGraphicsContext et BufferedGraphics afin
grer vous-mme les buers. Une instance de BufferedGraphics sobtient partir de la mthode
BufferedGraphicsContext.Allocate(). Une telle instance gre un buer en interne. Vous pouvez avoir accs ce buer au moyen de la proprit BufferedGraphics.Graphics{get;}. Une
fois que vous avez dessin sur ce buer, il faut appeler la mthode BufferedGraphics.Render()
afin dacher son contenu lcran. Voici notre exemple rcrit avec ces classes :
Exemple 18-16 :
using System.Drawing ;
using System.Windows.Forms ;
using System.Drawing.Drawing2D ;
public partial class AnimForm : Form {
private float angle ;
private Timer timer = new Timer() ;
private BufferedGraphics bufferedGraphics;
public AnimForm() {
BufferedGraphicsContext context = BufferedGraphicsManager.Current;
context.MaximumBuffer = new Size(this.Width + 1, this.Height + 1);
bufferedGraphics = context.Allocate(this.CreateGraphics(),
new Rectangle(0, 0, this.Width, this.Height));
timer.Enabled = true ;
timer.Tick += OnTimer ;
timer.Interval = 20 ; // 50 d
eclenchements par seconde.
timer.Start() ;
}
private void OnTimer(object sender, System.EventArgs e) {
angle ++ ;
if (angle > 359)
angle = 0 ;
704
19
ADO.NET 2.0
Le modle relationnel
Les premiers SGBD ont fait leur apparition la fin des annes 60, dans le cadre des programmes
spatiaux amricains. Un progrs dcisif a t ralis dans les annes 70 avec linvention du modle
relationnel. Le modle relationnel est bas sur un modle mathmatique qui permet de prsenter les donnes dune manire simple dans des tables.
La notion de table est assez proche de celle de relation. Une colonne dune table sappelle un attribut de la relation et est dfinie par un nom. Les lments dun attribut prennent leurs valeurs
dans un domaine (en gnral un type). Il faut prciser pour chaque colonne sil est obligatoire
que toutes les lignes contiennent un lment valide (i.e dans le domaine). Lensemble de la
description des colonnes dune table (i.e des attributs dune relation) est appel schma de la
relation. Les lignes dune table sont aussi appeles tuples ou enregistrements en franais ou rows
706
ou records en anglais. Une cl primaire (primary key en anglais) est lensemble des colonnes dont
la connaissance de valeurs permet didentifier une ligne unique de la table considre. Souvent,
la cl primaire nest constitue que dune colonne.
Le modle relationnel permet dviter les redondances dinformations qui menacent lintgrit
de la table et qui consomment des ressources. Cette fonctionnalit importante est possible grce
aux cls trangres (foreign key en anglais). Au lieu de disperser la mme donne dans plusieurs
endroits de la base, on utilise des cls trangres qui permettent de rfrencer la donne. La
donne nest donc pas duplique et reste accessible. Grce ce systme, on peut stocker des structures complexes dans une base de donnes relationnelle, comme une arborescence de donnes.
Un autre avantage du modle relationnel est de pouvoir assurer lintgrit des donnes de la
base, grce la possibilit de dcrire des contraintes dintgrit. Par exemple si vous avez une table
dont les lignes dcrivent des voitures et une autre table dont les lignes dcrivent des marques de
voitures, vous pouvez garantir la contrainte suivante : chaque voiture correspond une marque.
Lalgbre relationnelle contient six oprations de base, qui agissent sur des relations et produisent
des relations. Ces oprations sont lunion, la dirence, le produit cartsien, la projection, la
restriction et la jointure. Au moyen de ces oprations, lutilisateur dune base de donnes relationnelle peut accder aux donnes dune base et les modifier.
Le langage SQL
Le langage non procdural et standardis nomm SQL (Structured Query Langage) a t dvelopp
pour accder aux donnes dune base relationnelle grce des requtes. Ces requtes modlisent
des oprations de lalgbre relationnelle. Le langage SQL permet de raliser des requtes trs
complexes en quelques mots. Cet ensemble doprations sur les donnes du langage SQL est
nomm DML pour Data Manipulation Langage. On se sert aussi de requtes SQL pour construire
et modifier la structure dune base de donnes (insertion de table, assignation de droits aux
utilisateurs etc). Cette partie du langage est nomme DDL pour Data Definition Langage.
La plupart des SGBD lheure actuelle sont bass sur le modle relationnel et supportent un
langage driv de SQL. Par exemple, la gamme de SGBD SQL Server dit par Microsoft supporte
le langage T-SQL (Transact SQL) qui en plus des possibilits de base de SQL permet de dclarer
des variables, de contrler des transactions, de grer des exceptions, de grer le format XML etc.
La partie du langage relative ces possibilits est nomme DCL pour Data Control Langage.
Les applications distribues, dont seule la partie serveur est habilite accder la base. Le
serveur peut tre pris en charge par ASP.NET, utiliser les facilits de COM+ ou tre crit
compltement. De nombreux types de middleware peuvent tre utiliss entre les clients et
le serveur (HTTP/SOAP, .NET Remoting etc). Les clients peuvent tre lgers (navigateur
web) ou riches (excutables Windows Forms par exemple).
Le choix dune de ces deux architectures pour vos applications est fondamental. Le premier
choix nest judicieux que dans le cas de petites applications qui volueront peu.
Introduction ADO.NET
707
Introduction ADO.NET
Lappellation ADO.NET englobe la fois lensemble des classes du framework .NET utilises pour
manipuler les donnes contenues dans des SGBD relationnels, et la philosophie dutilisation de
ces classes.
Avant ADO.NET, la technologie ADO (ActiveX Data Object) constituait lensemble des classes
quil fallait utiliser pour accder aux bases de donnes dans les environnements Microsoft. Malgr son nom, ADO.NET est beaucoup plus quun successeur de ADO. Bien que ces deux technologies aient une finalit commune, de profondes dirences les sparent, notamment parce
quADO.NET est beaucoup plus complet.
Une application travaille en mode connect si elle charge des donnes de la base au fur et
mesure de ses besoins. Lapplication reste alors connecte la base. Lapplication envoie
ses modifications sur les donnes la base au fur et mesure quelles surviennent.
Si lapplication rcupre des donnes partir dune autre source que la base de donnes
(par exemple partir dune saisie manuelle dun utilisateur ou dun document XML)
puis quelle se connecte la base pour y stocker les nouvelles donnes.
Si lapplication nutilise pas la base de donnes. Prciser ce cas un sens, car une application peut travailler avec des classes de ADO.NET sans pour autant utiliser une base
de donnes. Par exemple, on verra que les classes de ADO.NET sont particulirement
adaptes pour la prsentation de donnes au format XML.
708
La philosophie dADO tait de travailler en mode connect. Il est trs dicile, voire impossible,
de crer des serveurs performants en terme de monte en charge (scalable), lorsque lon travaille
en mode connect. Notez quavec ADO, on peut travailler en mode dconnect au prix de beaucoup deorts.
Prcisons la signification du mot scalable. On dit dune application quelle est scalable, si, lorsque
vous ajoutez du hardware pour lexcuter (processeur, RAM, PCs etc) vous obtenez un gain de
performance commensurable la quantit de hardware ajoute. En pratique, on observe que
lajout de hardware pour excuter une application ne provoque pas forcment un gain de performance. En eet, de nombreux goulets dtranglement subsistent toujours dirents niveaux
(locking des donnes, middleware...). Seule larchitecture de lapplication peut minimiser les effets nfastes de ces goulets dtranglement.
Avec ADO.NET, on peut travailler soit en mode dconnect soit en mode connect. De nombreuses fonctionnalits intressantes, que nous dtaillons plus loin, sont disponibles pour chacun des deux modes.
La faiblesse du mode connect est quil gnre de trs nombreux accs la base de donnes et
plus gnralement, il gnre de nombreux accs rseau si la base de donnes est spare physiquement du reste de lapplication. La faiblesse du mode dconnect est quil amne consommer beaucoup de mmoire, puisquune partie de la base est rcupre en mmoire vive pour
chaque appel client. Cette faiblesse peut tre prohibitive pour un serveur devant grer un grand
nombre de clients, chaque client obligeant le serveur manipuler beaucoup de donnes en
mmoire.
Bien quune pr-analyse soit toujours ncessaire, il est en gnral plus ecace de travailler en
mode dconnect avec ADO.NET.
Le SGBD SQL Server a son propre fournisseur de donnes. Les classes de ce fournisseur
de donnes se trouvent dans lespace de noms System.Data.SqlClient. Ce fournisseur de
donnes fonctionne avec les versions 7.0 2000 et 2005 de SQL Server. Bien videmment, les
fonctionalits spcifiques une version de SQL Server ne peuvent pas tre exploites partir
de ce fournisseur de donnes si vous travaillez avec une version antrieure de ce produit.
Un autre fournisseur de donnes permet de communiquer avec les fournisseurs de donnes qui supportent lAPI OleDB. OleDB est une API permettant daccder aux donnes
dun SGBD, avec la technologie COM. Les classes de ce fournisseur de donnes se trouvent
dans lespace de noms System.Data.OleDbClient. Notez quil faut utiliser ce fournisseur de
donnes si vous travaillez avec des versions antrieures 7.0 de SQL Server.
Il existe un fournisseur de donnes .NET qui se place au dessus du protocole ODBC (Open
DataBase Connectivity). Ce fournisseur de donnes gr permet dexploiter les fournisseurs
de donnes non grs qui supportent lAPI ODBC (mais pas la totalit, voir le site de Microsoft pour plus de dtails). Les classes de ce fournisseur daccs sont disponibles dans lespace
de noms System.Data.Odbc.
Introduction ADO.NET
709
Il existe un fournisseur de donnes .NET qui est spcialis pour lutilisation de bases de
donnes Oracle. Les classes de ce fournisseur de donnes sont disponibles dans lespace de
noms System.Data.OracleClient.
DataSet
DataTableCollection
DataTable
DataRowCollection
Travail
en mode
dconnect
DataColumnCollection
ConstraintCollection
DataRelationCollection
DataRelation
Travail
en mode
connect
Base de
donnes
(SGBD)
Tous les accs au SGBD se font par lintermdiaire dune connexion dont limplmentation
fait partie du fournisseur de donnes.
710
Introduction ADO.NET
711
La cl primaire de la table DEPARTEMENTS est constitue par la colonne DepID. Elle doit tre positionne par lutilisateur chaque ajout dune ligne dans la table. Voici le diagramme de notre
base de donnes :
Departement
DEV
Dveloppement
FIN
Financier
MKT
Marketing
Table EMPLOYES
EmployeID
DepID
Nom
Prnom
Tl
MKT
Lafleur
Lon
0497112233
DEV
Dupont
Anne
0497112235
DEV
Schmol
Jean
FIN
Gripsou
Nol
0497112237
Voici le script T-SQL permettant de remplir la base. Notez que lon dlgue au SGBD le calcul
des valeurs de la colonne EmployeID :
Exemple 19-2 :
INSERT INTO DEPARTEMENTS VALUES (DEV,D
eveloppement)
INSERT INTO DEPARTEMENTS VALUES (FIN,Financier)
INSERT INTO DEPARTEMENTS VALUES (MKT,Marketing)
GO
SET IDENTITY_INSERT EMPLOYES OFF
712
INTO
INTO
INTO
INTO
EMPLOYES
EMPLOYES
EMPLOYES
EMPLOYES
VALUES (MKT,Lafleur,L
eon,0497112233)
VALUES (DEV,Dupont,Anne,0497112235)
(DepID,Nom,Pr
enom) VALUES (DEV,Schmol,Jean)
VALUES (FIN,Gripsou,No
el,0497112237)
La chane de connexion que nous utilisons dans nos exemples pour accder cette base est la
suivante :
"server = localhost ; uid=sa ; pwd = ; database = ORGANISATION"
Pensez configurer le mode dauthentification SQL Server and Windows pour excuter les
exemples avec cette chane de connexion.
713
class Program {
static void Main() {
DbConnection cnx ;
DbCommand
cmd ;
if( /*test si on travaille avec SQL Server*/ true ){
cnx = new SqlConnection();// Ici on couple notre application...
cmd = new SqlCommand() ; // ...avec le provider SqlClient.
}
// Ici on exploite cnx et cmd ind
ependamment du SGBD sous-jacent.
}
}
Pour limiter les eets de ce problme chaque fournisseur de donnes ADO.NET2 prsente une
classe drive de la classe abstraite System.Data.Common.DbProviderFactory. Cette classe prsente les mthodes suivantes :
DbConnection CreateConnection() ;
DbCommand CreateCommand() ;
DbCommandBuilder CreateCommandBuilder() ;
DbConnection CreateConnection() ;
DbConnectionStringBuilder CreateConnectionStringBuilder() ;
DbDataAdapter CreateDataAdapter() ;
DbDataSourceEnumerator CreateDataSourceEnumerator() ;
DbParameter CreateParameter() ;
DbPermission CreatePermission() ;
premire vue, il faudrait rcrire lexemple comme ceci :
Exemple 19-4 :
using System.Data.Common ;
using System.Data.SqlClient ;
class Program {
static void Main() {
DbProviderFactory fabrique ;
// Tester si lon travaille avec un SGBD type SQL Server.
if (true)
fabrique= new SqlClientFactory();
// `A partir dici le code est ind
ependent du provider sous-jacent.
DbConnection cnx = fabrique.CreateConnection() ;
DbCommand
cmd = fabrique.CreateCommand() ;
}
}
Cependant cet exemple ne compile pas car la classe SqlClientFactory na pas de constructeur
public. La seule faon de construire une instance dune classe drive de DbProviderFactory est
dutiliser la mthode statique CreateFactory() de la classe System.Data.Common.DbProviderFactories. Cette dernire classe est en fait une fabrique de fabrique. La mthode CreateFactory() retourne une fabrique dobjets ADO.NET pour un fournisseur de donnes si vous lui
passez en argument lespace de nom du fournisseur de donnes sous forme dune chane de
caractres. Notre exemple se rcrit donc comme ceci :
714
Exemple 19-5 :
using System.Data.Common ;
// Nous navons plus besoin du namespace System.Data.SqlClient !!
class Program {
static void Main() {
DbProviderFactory fabrique =
DbProviderFactories.GetFactory("System.Data.SqlClient");
// `A partir dici le code est ind
ependent du provider sous-jacent.
DbConnection cnx = fabrique.CreateConnection() ;
DbCommand
cmd = fabrique.CreateCommand() ;
}
}
Pour que notre exemple soit 100% indpendant dun fournisseur de donnes il ne nous reste
plus qu obtenir la chane de caractres contenant lespace de noms partir dun fichier de
configuration. Ceci est prvu par lattribut XML providerName dun lment XML add de llment XML connectionStrings du fichier de configuration de votre application. Par exemple le
fichier de configuration de votre application peut ressembler ceci :
Exemple 19-6 :
app.exe.config
app.cs
715
Larchitecture prsente par cet ensemble de classes est plus connue sous la forme du design
pattern nomm fabrique abstraite (abstract factory en anglais). Cette architecture ne vous empche pas de ponctuellement tenter dutiliser les services propres un fournisseur de donnes
particulier. Par exemple le programme suivant montre comment obtenir les statistiques dune
connexion condition que lon utilise le fournisseur de donnes de SQL Server (seul ce fournisseur de donnes expose cette possibilit) :
app.cs
Exemple 19-8 :
using System.Data.Common ;
using System.Configuration ;
using System.Collections ; // Pour IDictionary.
using System.Data.SqlClient ;
class Program {
static void Main() {
ConnectionStringSettings cfg =
ConfigurationManager.ConnectionStrings["Ma DB"] ;
DbProviderFactory fabrique =
DbProviderFactories.GetFactory(cfg.ProviderName) ;
DbConnection cnx = fabrique.CreateConnection() ;
IDictionary cnxStats = EventuallyGetStats(cnx) ;
}
public static IDictionary EventuallyGetStats(DbConnection cnx) {
if ( cnx is SqlConnection )
return ( cnx as SqlConnection ).RetrieveStatistics();
return null ;
}
}
La liste des fournisseurs de donnes est extensible. Aussi, chaque fournisseur de donnes disponible sur une machine doit se dclarer dans le fichier machine.config comme ceci :
Exemple 19-9 :
<?xml version="1.0" encoding="utf-8" ?>
<configuration>
...
<system.data>
<DbProviderFactories>
...
<add name="OleDb Data Provider"
invariant="System.Data.OleDb"
support="BF"
description=".Net Framework Data Provider for OleDb"
type="System.Data.OleDb.OleDbFactory, System.Data,
Version=2.0.3600.0, Culture=neutral,
PublicKeyToken=b77a5c561934e089" />
...
</DbProviderFactories>
</system.data>
</configuration >
app.exe.config
716
Notez quun fournisseur de donnes spcifique une application web peut tre dclar dans le
fichier web.config correspondant.
La chane de caractres spcifie par lattribut invariant est la mme que celle spcifie en argument de la mthode DbProviderFactory.GetFactory(). Lattribut support dfinit un masque
de 8 bits relatif lindicateur binaire System.Data.Common.DbProviderSupportedClasses spcifiant les possibilits prsentes par le fournisseur de donnes. Enfin lattribut type indique
le nom de la classe fabrique drivant de DbProviderFactory ainsi que lassemblage qui la
contient. Cet assemblage doit tre soit dans le rpertoire de lapplication soit dans le rpertoire
GAC.
Chanes de connexion
Une chane de connexion est une chane de caractres qui contient les informations ncessaires
pour localiser et pour se connecter une base de donnes. Une chane de connexion est constitue de plusieurs couples cl/valeur, spars par des points virgules. Par exemple :
string s1 ="Data Source=Server;Database=MyDB;User ID=FOOID;Password=FOOPWD;" ;
string s2 ="Data Source=Server ; " +
"Initial Catalog=pubs;Integrated Security=SSPI;" ;
string s3 ="SERVER = localhost ; UID=sa ; PWD = ; database = ORGANISATION" ;
Certaines donnes sont ncessairement reprsentes dans une chane de connexion. On peut
citer le nom du serveur hbergeant la base de donnes ou le login de lutilisateur DB sous lequel on souhaite se connecter. Les cls associes ces donnes dirent selon le fournisseur de
donnes. Par exemple, le fournisseur de donnes OleDb se sert des cls "Data Source" et "User
ID" tandis que le fournisseur de donnes Odbc se sert des cls "Server" et "UID". Le fournisseur de donnes SqlClient supporte ces deux syntaxes. Certains fournisseurs de donnes prsentent la possibilit de construire une chane de connexion au moyen dune classe drive de
System.Data.Common.DbConnectionStringBuilder. Cette possibilit est illustre par lexemple
suivant. Cet exemple exploite aussi le fait que le fournisseur de donnes SqlClient permet dnumrer les sources de donnes auxquelles il peut accder :
Exemple 19-10 :
using System.Data ;
using System.Data.Common ;
using System.Configuration ;
class Program {
static void Main() {
Configuration cfg = ConfigurationManager.OpenExeConfiguration(
ConfigurationUserLevel.None) ;
ConnectionStringSettings cfg_cs =
cfg.ConnectionStrings.ConnectionStrings["Ma DB"] ;
DbProviderFactory fabrique =
DbProviderFactories.GetFactory(cfg_cs.ProviderName) ;
DbDataSourceEnumerator e=fabrique.CreateDataSourceEnumerator();
DataTable tbl = e.GetDataSources();
if (tbl.Rows.Count > 0) {
// Laisser lutilisateur choisir un serveur.
app.cs
717
int choixUtilisateur = 0 ;
DataRow row = tbl.Rows[choixUtilisateur] ;
string dataSource = row[ "ServerName" ].ToString() ;
string instanceName = row[ "InstanceName" ].ToString() ;
string isClustered = row[ "IsClustered" ].ToString() ;
string version = row[ "Version" ].ToString();
// Met `a jour la chane de cnx dans le fichier de config
// independament du fournisseur de donn
ees sous-jacent.
DbConnectionStringBuilder csBuilder =
fabrique.CreateConnectionStringBuilder();
csBuilder.ConnectionString = cfg_cs.ConnectionString;
if( csBuilder.ContainsKey("Server"))
csBuilder.Remove("Server");
csBuilder.Add("Server", dataSource) ;
cfg_cs.ConnectionString = csBuilder.ConnectionString ;
cfg.Save() ;
} // end choix de la ligne.
} // end methode Main()
} // end class Program.
Cet exemple est particulirement pertinent si vous utilisez la cl "Data Source" dans votre
chane de connexion dans votre fichier de configuration. En eet, la valeur associe sera correctement mise jour bien que nous utilisions la chane de caractres "Server" pour la rfrencer
dans notre programme.
Si vous ne souhaitez pas utiliser cette possibilit de construction dynamique de chanes de
connexion nous vous conseillons de vous rfrer au site http://www.connectionstrings.com/.
Il runit un grand nombre dexemples de chanes de connexion prsents en fonction des
sources de donnes disponibles sur le march des SGBD.
Lorsque vous utilisez le fournisseur de donnes ODBC il est conseill dexploiter la notion de
DSN (Data Source Name) qui vite aux dveloppeurs de manipuler les donnes contenues dans la
chane de connexion. Un DSN est une information stocke dans le systme dexploitation de la
machine qui excute lapplication. Cette information associe une base de donnes une chane
de caractres. Par exemple une chane de connexion utilisant un DSN peut ressembler ceci :
string s1 = "DSN=MonAppliDSN" ;
En plus de sa simplicit dutilisation, un DSN prsente lavantage de constituer une indirection
vers une base de donnes paramtrable aprs compilation de lapplication. Cette indirection se
fait indpendamment du SGBD sous-jacent et indpendamment de lemplacement physique
de la base, puisque ces donnes sont des paramtres du DSN. En gnral on configure les DSN
prsents dans un systme dexploitation Microsoft avec le menu Panneau de configuration Outil
dadministration Sources de donnes (ODBC).
718
LExemple 19-6 illustre un fichier de configuration qui dfinit une chaine de connexion dans son
lment <connexionStrings>. LExemple 19-7 montre comment du code C dune application
peut avoir accs aux chanes de connexion dfinies dans un fichier de configuration.
Les chanes de connexion sont parfois considres comme des donnes confidentielles du fait
quelles contiennent souvent un mot de passe. Le framework .NET 2.0 vous permet de sauver une
version encrypte dune chaine de connexion dans un fichier de configuration. Ceci est expliqu
en page .
Pool de connexions
Ceux qui ont dj crit des serveurs accdant des bases de donnes savent que lutilisation dun
pool de connexions peut augmenter dune manire significative les performances. Un pool de
connexions est une collection de connexions quivalentes. Cest--dire que les connexions sont
connectes la mme base et que chaque demande dun client se sert dune de ces connexions
pour accder aux donnes. Les connexions sont des objets complexes, coteux initialiser et
dtruire. Lide sous-jacente la notion de pool de connexions est de recycler les connexions
dj ouvertes de faon diminuer globalement les cots de crations, dinitialisation et de destruction des connexions.
La bonne nouvelle est que ADO.NET fait en sorte que ce mcanisme de pooling de connexions
soit compltement transparent pour lutilisateur. Un pool de connexions est automatiquement
cr en interne pour chaque chane de connexion utilise. Par exemple :
...
string sCnx = "server=localhost ;uid=sa ;pwd =;database = ORGANISATION";
SqlConnection cnx1 = new SqlConnection(sCnx) ;
cnx1.Open() ;
// Ici le pool de connexion associ
e `
a la chaine sCnx ne contient pas de
// connexion mais le thread courant en utilise une.
cnx1.Close() ;
// La connexion nest pas detruite mais est plac
ee dans le pool associ
e
// `a la chaine sCnx.
...
La gestion en interne du pool de connexions dpend du fournisseur de donnes sous-jacent.
Aussi nous vous conseillons de vous rfrez la documentation ocielle du fournisseur si vous
souhaitez paramtrer cette gestion. Sachez que le fournisseur de donnes SqlClient prsente
des possibilits pour permettre programmatiquement un certain contrle sur la faon dont le
pooling de connexions deectue. Plus dinformations ce sujet sont disponibles dans larticle
Connection Pooling for the .NET Framework Data Provider for SQL Server des MSDN.
719
Toute la problmatique dun tel framework est quil est gnrique dans le sens o il est commun
tous les types de SGBD et donc tous les fournisseurs de donnes. Or, les dirents types de
SGBD nont pas exactement la mme structure. Bien entendu, on y retrouve dans chacun les
concepts classiques tels que les notions de tables ou dindex. Mais les dtails sont en gnral diffrents. Par exemple, il existe trois types de filtre (aussi nomm restriction) qui peuvent sappliquer une requte sur lensemble des tables dun SGBD type Oracle (OWNER, TABLE_NAME, et TYPE)
tandis quil en existe quatre pour les SGBD de type SQL Server (table_catalog, table_schema,
table_name et table_type).
La rponse cette problmatique est forcment un aaiblissement du typage. Les requtes sur la
structure dun SGBD se font dune manire non types laide dune seule mthode, la mthode
DataTable GetSchema() dfinie dans la classe de base DbConnection. Lensemble des lments
structuraux du SGBD satisfaisant une requte est contenu dans une DataTable. Si vous utilisez
cette mthode sans paramtre vous obtiendrez ce que lon nomme lensemble des collections
de mtadonnes. Pour un SGBD de type SQL Server cet ensemble est : MetaDataCollections,
DataSourceInformation, DataTypes, Restrictions, ReservedWords, Users, Databases, Tables,
Columns, Views, ViewColumns, ProcedureParameters, Procedures, ForeignKeys, IndexColumns,
Indexes, et UserDefinedTypes. Si vous utilisez cette mthode avec un paramtre de type chane
de caractres gale une de ces collections, vous obtiendrez lensemble des lments de cette
collection. Lexemple suivant illustre ceci en numrant lensemble des tables dune SGBD :
Exemple 19-11 :
using System.Data.Common ;
using System.Data.SqlClient ;
using System.Data ;
class Program {
static void Main() {
string sCnx =
"server = localhost ; uid=sa ; pwd= ; database = ORGANISATION" ;
using (DbConnection cnx = new SqlConnection(sCnx)) {
cnx.Open() ;
DataTable tbl = cnx.GetSchema("tables");
foreach (DataRow row in tbl.Rows) {
foreach (DataColumn col in tbl.Columns)
System.Console.WriteLine(col.ToString() + " = " +
row[col].ToString()) ;
System.Console.WriteLine() ;
}
} // end using cnx.
}
}
Voici un extrait de lachage de cet exemple :
...
TABLE_CATALOG = ORGANISATION
TABLE_SCHEMA = dbo
TABLE_NAME = EMPLOYES
TABLE_TYPE = BASE TABLE
720
Enfin, une troisime surcharge de la mthode GetSchema() existe. Elle vous permet de filtrer le
rsultat de la requte.
Plus dinformations ce sujet sont disponibles dans larticle Schemas in ADO.NET 2.0 des
MSDN rdig par Bob Beauchemin. La notion de restriction y est dtaille ainsi que les impacts
de ce framework au niveau de la conception dun fournisseur de donnes.
721
}
}
Cet exemple ache sur la console :
Lafleur Leon
Dupont Anne
Schmol Jean
Gripsou Noel
La plupart des classes de ADO.NET implmentent linterface IDispose et doivent donc soit tre
instancies au sein dune clause using soit tre disposes manuellement.
La classe DbProviderFactory ne prsente pas la mthode CreateDbDataReader() bien que
chaque fournisseur de donnes ait une classe drive de DbDataReader. La raison est que
les nouvelles instances de ces classes sont ncessairement obtenues en retour de lappel la
mthode ExecuteReader() sur une commande.
722
Une procdure stocke qui associe un nom et des paramtres une requte SQL stocke directement par le SGBD. Comprenez bien quarchitecturalement parlant, utiliser des procdures stockes permet de dplacer de la complexit de votre code source vers le SGBD.
La proximit dun traitement dans une procdure stocke avec les donnes elles-mmes
est un avantage. Cependant, lutilisation de procdures stockes complexifie en gnral la
maintenance globale dune application, notamment parce que plusieurs langages de programmation sont alors utiliss (T-SQL, C etc).
Une commande ne prenant en compte que le nom dune ou de plusieurs tables. Dans ce
cas, le contenu entier des tables est rcupr par la commande.
Ces types de commandes peuvent tre dfinis par la proprit CommandType de linterface
IDbCommand. La premire solution est choisie par dfaut. Il existe principalement quatre types
de commandes/requtes SQL :
SELECT : permet dobtenir des donnes. Ce type de requte est gnralement utilis pour
remplir un DataSet partir dune commande et dun DataAdapter, comme vu prcdemment.
INSERT : permet dinsrer une ligne dans une table, par exemple :
INSERT INTO DEPARTEMENTS VALUES (COM,Communication)
UPDATE : permet de modifier les valeurs dune ou de plusieurs lignes dans une table, par
exemple :
UPDATE DEPARTEMENTS SET Departement=Comm WHERE DepID = COM
DELETE : permet de supprimer une ou plusieurs lignes dans une table, par exemple :
DELETE FROM DEPARTEMENTS WHERE DepID = COM
Pour excuter une telle commande il sut dutiliser la mthode ExecuteNonQuery() de linterface IDbCommand (notez que non query signifie que lon excute pas une requte qui va nous
retourner des informations) :
Exemple 19-14 :
using System.Data.Common ;
using System.Data.SqlClient ;
class Program {
static void Main() {
// Requete SQL pour inserer un enregistrement.
string sCmd1 =
"INSERT INTO DEPARTEMENTS VALUES (COM,Communication)" ;
// Requete SQL pour modifier un enregistrement.
string sCmd2 =
723
724
725
Exemple 19-16 :
using System ;
using System.Data ;
using System.Data.SqlClient ;
class Program {
static void Main() {
DataSet dSet = new DataSet() ;
using ( SqlConnection cnx = new SqlConnection(
"server = localhost ; uid=sa ; pwd = ; database = ORGANISATION")){
using (SqlDataAdapter dAdapter = new SqlDataAdapter()) {
dAdapter.SelectCommand = new SqlCommand(
"SELECT * FROM EMPLOYES", cnx) ;
dAdapter.Fill(dSet, "EMPLOYES") ;
dAdapter.SelectCommand = new SqlCommand(
"SELECT * FROM DEPARTEMENTS", cnx) ;
dAdapter.Fill(dSet, "DEPARTEMENTS") ;
} // end using cnx.
} // end using dAdapter.
// Cree une relation dans le DataSet.
// La relation se fait entre la colonne DepID de la table
// DEPARTEMENTS et la colonne DepID de la table EMPLOYES.
DataColumn dCol1 = dSet.Tables["DEPARTEMENTS"].Columns["DepID"] ;
DataColumn dCol2 = dSet.Tables["EMPLOYES"].Columns["DepID"] ;
DataRelation dRelation = dSet.Relations.Add(
"Employes du departement", dCol1, dCol2);
// Navigue logiquement dans la relation one to many
// dun departement vers ses employ
es.
foreach (DataRow dRow1 in dSet.Tables["DEPARTEMENTS"].Rows){
Console.WriteLine("Departement : {0}", dRow1["Departement"]) ;
foreach (DataRow dRow2 in dRow1.GetChildRows(dRelation) )
Console.WriteLine(" {0} {1}",dRow2["Nom"],dRow2["Pr
enom"]) ;
}
}
}
Ce petit programme ache :
Departement : Developpement
Dupont Anne
Schmol Jean
Departement : Financier
Gripsou Noel
Departement : Marketing
Lafleur Leon
726
nes courantes et les donnes originales. En tant que client dune DataTable vous navez accs
quaux donnes courantes. Cependant chaque ligne dune table (i.e chaque instance de la classe
DataRow) prsente la proprit DataRowState RowState{get;} qui indique si ses donnes ont
subi des changements :
Valeur de lnumration
DataRowState
Description
Unchanged
Added
Deleted
Modified
Detached
La ligne vient dtre cre mais ne fait pas encore partie dune
table.
Il serait plus juste de rajouter pour chacune de ces descriptions, depuis le dernier appel la mthode AcceptChanges() ou RejectChanges() sur la DataTable contenant la ligne . En eet, si vous
appelez lune ou lautre de ces mthodes au nom loquent, ltat de chaque ligne est positionn
Unchanged. De plus, si lon appelle AcceptChanges(), les lignes qui taient dans ltat Deleted
sont eectivement dtruites. Si lon appelle RejectChanges(), les lignes qui taient dans ltat
Added sont dtruites et les donnes qui ont t modifies sont remises leurs tats initiaux.
Lappel la mthode AcceptChanges() sur un DataSet ou une DataTable ne signifie aucunement quil y a un accs la base de donnes. Noubliez pas quun DataSet ou une DataTable est
un cache de donnes dconnect. Cependant, si un cache contient des donnes extraites dune
base, avant daccepter les changements, il faut en gnral mettre jour les donnes dans la base.
Nous allons voir que les oprations de mise jour de la base de donnes et dacceptation des
changements dans un cache sont deux oprations distinctes, qui ncessitent chacune du code.
Pour mettre jour dans une base les donnes modifies dans un cache il faut se reconnecter la
base puis eectuer une requte SQL pour chaque modification. Pour vous vitez davoir coder
cette opration fastidieuse le fournisseur de donne sous-jacent a la possibilit de prsenter une
classe qui est capable de fabriquer ces requtes SQL en les dduisant dune requte SELECT.
Cette classe doit driver de System.Data.Common.DbCommandBuilder. Tout ceci est illustr par
lexemple suivant :
Exemple 19-17 :
using System.Data ;
using System.Data.SqlClient ;
class Program {
static string sCnx =
"server = localhost ; uid=sa ; pwd = ; database = ORGANISATION" ;
static string sCmd = "SELECT * FROM DEPARTEMENTS" ;
static void Main() {
DataTable dTable = new DataTable() ;
727
FillTableFromDB(dTable);
ChangeDataInTable(dTable);
SynchronizeChangesWithDB(dTable);
}
static void ChangeDataInTable(DataTable dTable) {
// Ajoute un nouveau departement.
dTable.Rows.Add("COM","Communication");
}
static void FillTableFromDB(DataTable dTable) {
using ( SqlDataAdapter dAdapter = new SqlDataAdapter(sCmd, sCnx)){
dAdapter.Fill(dTable) ;
} // end using dAdapter.
}
static void SynchronizeChangesWithDB(DataTable dTable) {
using ( SqlDataAdapter dAdapter = new SqlDataAdapter(sCmd, sCnx)){
// Construit les commandes de mise `
a jour dans dAdapter...
// ...`a partir de la commande Select.
SqlCommandBuilder cmdBuilder = new SqlCommandBuilder(dAdapter);
try {
// Met `a jour les donn
ees dans la base.
dAdapter.Update(dTable);
}
catch {
// Ici, traiter lerreur de maj.
return ;
}
// Accepte les changements dans le cache.
dTable.AcceptChanges();
} // end using dAdapter.
}
}
Sachez que ce principe de conservation en interne des donnes originales et des donnes courantes afin de faciliter la mise jour se retrouve dans un design pattern de Martin Fowler nomm
Unit Of Work.
Tel que ce programme est crit, lappel la mthode Update() provoque un accs la base pour
chaque requte SQL. Cette technique est peu ecace si vous avez sauver un grand nombre de
changements aussi, depuis ADO.NET2, les classes drives de DbDataAdapter ont la possibilit
de mettre jour en un seul accs la base tout un lot de changements. Pour cela, il sut de positionner la proprit int DbDataAdapter.UpdateBatchSize{get;set;} qui spcifie le nombre
de requtes SQL contenues dans un lot.
Enfin, notez que lopration de mise jour est susceptible de lancer une exception. La raison est
quADO.NET utilise une gestion optimiste des accs concurrents aux donnes dune base.
728
supposons que lutilisateur A rcupre le numro de tlphone de Anne Dupont 8h00. Supposons que lutilisateur B modifie le numro de tlphone de Anne Dupont 8h05. Enfin,
supposons que lutilisateur A dcide de changer aussi le numro de tlphone de Anne Dupont
8h10, partir du numro quil a rcupr 10 minutes plus tt. Il y a clairement un conflit de
mise jour du numro de tlphone de Anne Dupont. Quel est finalement le numro de Anne
Dupont : celui que lutilisateur B a sauv 8h05 ou celui que lutilisateur A a sauv 8h10 ?
ADO.NET gre ces conflits dune manire optimiste. Cela signifie quaucune mesure nest mise
en uvre pour viter ces conflits, mais si un conflit survient, il est dtect et par dfaut la donne
nest pas modifie. Dans notre exemple, le numro de tlphone garderait donc sa valeur aecte 8h05 et lutilisateur A serait averti du conflit 8h10. La gestion est optimiste dans le sens o
on espre que les conflits ne surviennent que trs rarement. Lorsquun conflit est dtect, une
exception de type System.Data.DBConcurrenyException est leve lors de lappel la mthode
Update() sur ladaptateur.
Thoriquement il existe une technique pour prvenir les conflits. Cette technique est appele
gestion pessimiste des conflits. Cette technique utilise des systmes de verrous sur les lignes (voire
sur les tables) afin de synchroniser les accs. La gestion est pessimiste dans le sens o lon prvoit
que les conflits arrivent susamment souvent pour nuire au bon droulement de lapplication.
Ce systme de verrous implique des temps dattentes pour les clients qui souhaitent accder aux
donnes, et donc une baisse significative des performances gnrales de lapplication.
Il est conseill aux dveloppeurs utilisant ADO.NET de faire en sorte que les architectures de
leurs bases de donnes et de leurs applications soient adaptes une gestion optimiste. Cette
contrainte est assez faible dans la mesure o il sut de minimiser les cas o plusieurs utilisateurs accdent en criture la mme donne. Dans la plupart des systmes distribus rels, cette
contrainte est satisfaite de facto. En eet, en gnral le volume des donnes traiter est immense
par rapport aux nombres dutilisateurs simultans. Concrtement, si des dizaines dutilisateurs
utilisent simultanment des dizaines de millions de lignes, les conflits ne surviennent que trs
rarement.
System.Data.UniqueConstraint
Une contrainte de ce type impose que la table que lon obtiendrait en projetant la table
courante sur certaines de ses colonnes nait que des lignes direntes deux deux. Les colonnes sont prcises dans la contrainte. Ce type de contraintes est particulirement utile
pour vrifier que lon nutilise pas une valeur dj utilise pour cl primaire, lorsque lon
ajoute une nouvelle ligne dans une table.
System.Data.ForeignKeyConstraint
Cette contrainte sutilise sur une table parent lorsquune relation one to many sur
des cls primaires/cls trangres existe entre une table parent et une table fille. Une telle
729
Cascade : les lignes concernes de la table fille sont soit mises jour avec la nouvelle
valeur de la cl primaire de la table parent, soit supprimes si la ligne de la table parent
est supprime. Cest le comportement adopt par dfaut.
None : rien nest fait sur les lignes concernes de la table fille.
SetDefault : chaque valeur dune ligne concerne de la table fille prend la valeur par
dfaut prcise dans sa colonne (proprit DefaultValue de la classe Column).
SetNull : les valeurs correspondantes des lignes concernes de la table fille sont positionnes nulle.
Vous pouvez dcider de ne visualiser que les lignes qui sont dans un certain tat en positionnant la proprit DataRowViewState RowStateFilter{get;set;}. Par exemple la valeur DataRowViewState.ModifiedCurrent vous permet de visualiser le contenu courant de
toutes les lignes qui ont t modifies tandis que la valeur DataRowViewState.Deleted vous
permet de visualiser le contenu original de toutes les lignes qui ont t supprimes.
Vous pouvez dcider dappliquer un filtre sur le contenu de certaines colonnes en positionnant la proprit string RowFilter{get;set;}. Le formatage de la chane de caractres
reprsentant le filtre est similaire au formatage dune clause WHERE en SQL. Plus de dtails
sur ce formatage sont disponibles dans lentre La proprit DataColumn.Expression des
MSDN.
Vous pouvez dcider de trier les lignes filtres selon lordre croissant du contenu des
colonnes reprsentant la cl primaire en positionnant la proprit bool ApplyDefaultSort{get;set;}.
Vous pouvez dcider de trier les lignes filtres selon le contenu de une ou plusieurs colonnes
en utilisant la proprit string Sort{get;set;}. Le tri se fait par ordre croissant par dfaut. Vous devez sparer le nom des colonnes avec une virgule et utiliser les expressions ASC
ou DESC pour spcifier un tri par ordre croissant ou dcroissant.
Le contenu dune DataView est dynamique. Autrement dit, il reflte en temps rel les changements apports sur les donnes de la DataTable sous-jacente. Cette particularit est illustre par
le programme suivant :
Exemple 19-18 :
using System ;
using System.Data ;
using System.Data.SqlClient ;
class Program {
static string sCnx =
730
Data Table:
Lafleur
Dupont
Schmol
Gripsou
Data View avant mutation de Gripsou:
Dupont
Schmol
Data View apres mutation de Gripsou:
Dupont
Schmol
Gripsou
la dirence du systme de vues fourni par les dirents SGBD, la classe DataView ne vous
permet pas de crer de vue sur une jointure de tables. Elle ne permet pas non plus dexclure ou
dajouter des colonnes.
DataSet typs
731
DataSet typs
Maintenant que nous avons prsent la notion de DataSet il est temps de montrer que lon
utilise en fait que rarement la classe DataSet. En eet, on prfre utiliser ce que lon nomme
des DataSet typs. Loutil xsd.exe fourni avec le framework permet de construire une classe C
ou VB.NET drive de la classe DataSet. Cette construction se fait partir dun schma XSD
ou partir des tables dune base de donnes. Cette classe gnre prsente des sous classes qui
permettent daccder aux donnes du cache dune manire fortement type. Pour bien illustrer
cette notion de typage fort, voici lExemple 19-16 rcrit en exploitant la classe de Dataset typs
ConsoleApplication1.ORGANISATIONDataSet :
Exemple 19-19 :
using
using
using
using
using
System ;
System.Data ;
System.Data.Common ;
System.Data.SqlClient ;
ConsoleApplication1 ; // Espace de noms contenant la classe
// ORGANISATIONDataSet de DataSet typ
e.
class Program {
static void Main() {
ORGANISATIONDataSet dSet = new ORGANISATIONDataSet();
using (SqlConnection cnx = new SqlConnection(
"server = localhost ; uid=sa ; pwd = ; database = ORGANISATION")){
using (SqlDataAdapter dAdapter = new SqlDataAdapter()) {
dAdapter.SelectCommand = new SqlCommand(
"SELECT * FROM EMPLOYES", cnx) ;
dAdapter.Fill(dSet, "EMPLOYES") ;
dAdapter.SelectCommand = new SqlCommand(
"SELECT * FROM DEPARTEMENTS", cnx) ;
732
Soit vous faite : Menu Data Add New Data Source Database puis vous slectionnez
une base de donnes ainsi que ses tables qui vont tre prises en compte dans votre classe de
DataSet typ.
Soit vous faite : Click droit sur votre projet Add New Item... DataSet puis vous slectionnez une base de donnes ainsi que ses tables qui vont tre prises en compte dans votre
classe de DataSet typ au moyen de la fentre Server Explorer.
Dans les deux cas, votre projet contient un nouveau fichier dextension .xsd (en loccurrence
ORGANISATIONDataSet.xsd). Un fichier de code source (en loccurrence ORGANISATIONDataSet.
Designer.cs) est associ ce dernier. Cest ce fichier qui contient le code gnr de la classe
ORGANISATIONDataSet et de ses classes encapsules :
DataSet typs
733
734
On remarque lutilisation des mthodes Fill(EMPLOYESDataTable) et Fill(DEPARTEMENTSDataTable). Ces mthodes nous vitent davoir crire les requtes SQL de slection globale des
lignes dune table. Nous aurions pu aussi utiliser les mthodes EMPLOYESDataTable GetData()
et DEPARTEMENTSDataTable GetData() qui jouent le mme rle mis part quelles vous vitent
davoir crer la table.
En fait, lintrt majeur des TableAdapter rside dans le fait quils nous vitent davoir crire
toutes sortes de requtes SQL (i.e des requtes de type SELECT, INSERT, UPDATE et DELETE). Lide
est de remplacer chaque requte SQL par une mthode dun TableAdapter. Lavantage est que
les paramtres entrant et sortant de ces mthodes sont typs. Un wizard de cration de telle
mthode existe. Voici comment sen servir pour crer une mthode EMPLOYESDataTable GetEmployesByDepID(string DepID) qui retourne une table contenant les employs dun dpartement :
Click droit de ORGANISATIONDataSet.xsd View designer Click droit sur la zone EMPLOYESTableAdapter Add Query Use SQL Statements Next SELECT which returns Rows ( ce stade
vous pouvez aussi choisir de crer une requte de type INSERT, UPDATE, DELETE) Next
Query Builder Crer la requte : SELECT EmployeID, DepID, Nom, Pr
enom, T
el FROM EMPLOYES WHERE DepID = @DepID OK Next Return a DataTable avec pour nom de mthode
GetEmployesByDepID() Finish
Voici un exemple dun programme qui utilise cette mthode :
Exemple 19-21 :
using System.Data.SqlClient ;
using ConsoleApplication1 ;
using ConsoleApplication1.ORGANISATIONDataSetTableAdapters ;
class Program {
static void Main() {
ORGANISATIONDataSet.EMPLOYESDataTable table ;
using (SqlConnection cnx = new SqlConnection(
"server = localhost ; uid=sa ; pwd = ; database = ORGANISATION")){
using (EMPLOYESTableAdapter eAdapter = new
EMPLOYESTableAdapter()) {
table = eAdapter.GetEmployesByDepID("DEV");
} // end using eAdapter.
} // end using cnx.
foreach (ORGANISATIONDataSet.EMPLOYESRow eRow in table)
System.Console.WriteLine("
" + eRow.Nom) ;
735
}
}
736
PreserveChanges : (valeur par dfaut) Ecrase seulement les donnes originales du DataSet
avec les donnes en provenance du DataReader.
OverwriteChanges : Ecrase les donnes originales et courantes du DataSet. avec les donnes
en provenance du DataReader.
Upsert : Ecrase seulement les donnes courantes du DataSet avec les donnes en provenance du DataReader.
La problmatique structurelle
Lorsque lon planifie dutiliser un modle de persistance relationnel partir dun langage objet,
le premier problme que lon se pose est celui-ci : il nexiste pas de faon simple pour stocker
ltat dun objet dont la classe est drive dune autre classe :
Soit on cre une seule table pour toute une hirarchie de classes (design pattern de Martin
Fowler nomm Single Table Inheritance). Bien que pour une petite hirarchie de classes
cette solution soit ecace, elle devient rapidement impraticable lorsque le nombre de
classes et de champs augmente.
Soit on cre une table par classe non abstraite (design pattern de Martin Fowler nomm
Concrete Table Inheritance). Cette solution a le gros dsavantage dtre dicilement maintenable. Si une classe de base change, il faut changer galement la structure de toutes les
tables reprsentant les classes drives.
Soit on cre une table pour chaque classe en utilisant un systme de cl trangre (design
patterns de Martin Fowler nomm Class Table Inheritance). Une table ne contient que les
champs dfinis dans la classe correspondante. Cette solution rsout le problme de la maintenance, mais introduit de la complexit dans laccs aux donnes.
Un autre problme dordre structurel est d la faon de stocker les relations one to many et
many to many entre objets. Dans les langages objets, les collections sont physiquement gres
soit par un systme de positionnement dobjet (types valeur) soit par un systme de rfrencement dobjet (types rfrence). Aucun de ces deux systmes nest applicable dans une base de
donnes relationnelle. Pour rsoudre les cas de relations one to many , on utilise des cls trangres. Pour rsoudre les cas de relations many to many on utilise une table dassociation. Il
faut bien comprendre que toute la dicult du stockage des relations vient de la gestion du passage du systme cl trangre/table dassociation au systme rfrence/positionnement et
inversement.
La problmatique comportementale
Un autre problme rencontr, lorsque lon utilise un modle de persistance relationnel partir
dun langage objet, est comportemental. Le problme comportemental est relatif la faon
dont les objets sont chargs et sauvegards. En gnral, vous chargez plusieurs tats dans des objets, vous faites des modifications puis vous sauvez les nouveaux tats de ces objets. Il est souvent
737
ecace de ne charger que partiellement ltat dun objet bien que cela ncessite plus danalyse et
de conception (design pattern de Martin Fowler nomm Lazy Load). De plus, durant le traitement
des donnes par lapplication, il est assez dicile de garder la trace de chaque changement. Pour
cette tche prcise, la technologie ADO.NET propose une solution ecace qui fait lobjet de la
section page 725.
Une nouvelle gnration de bases de donnes, permettant de stocker simplement les tats
des objets, a vu le jour il y a quelques annes. Lide est simplement de stocker ltat dun
objet soit en le srialisant, soit en tablissant une correspondance avec les donnes relationnelles pour bnficier de lecacit du modle de requte relationnel. Lutilisation de ces
bases de donnes dans lindustrie est jusquici reste marginale. Un tournant vient dtre
pris par Microsoft dans SQL Serveur 2005. En eet, lexcution, le processus de ce SGBD
hberge le CLR de faon permettre une certaine intgration entre les types grs .NET
et les donnes des bases. Par exemple, sous certaines conditions, un type .NET peut typer
directement les donnes dune colonne dune table (cette fonctionnalit est nomme User
Defined Type ou UDT). En outre, les procdures stockes peuvent tre rdiges en code gr
.NET.
Si vous souhaitez rester dans une approche plus traditionnelle, vous pouvez minimiser les
consquences des problmes de maintenances et de complexits inhrentes au paradigme
objet/relationnel avec la gnration automatique de code (design pattern de Martin Fowler
nomm Metadata Mapping avec gnration de code). Dans cette mthode, le code SQL
ncessaire pour construire et exploiter une base de donnes et la couche logicielle qui attaque la base de donnes sont construits partir dune mme description. Il y a moins de
problmes de maintenance, et une grande partie de la complexit peut tre encapsule dans
le code gnr.
Une autre faon que la gnration de code pour tablir un lien entre les objets et les donnes
relationnelles est de prvoir une table de correspondance (i.e de mapping) entre les champs
des classes et les colonnes de tables. lexcution, on se sert dun moteur de mapping pour
parcourir une telle table et tablir la correspondance entre les tats des objets et les donnes
relationnelles. Cest le design pattern de Fowler nomm Metadata Mapping rflexif.
Un dbat sur les pros et cons des deux variantes du design pattern Metadata Mapping est disponible lURL http://www.theserverside.net/news/thread.tss?thread_id=29071.
Enfin, prcisons quune des priorit des concepteurs de la version 3.0 de C , est dunifier au
niveau langage le monde des donnes (XML et SQL) et le monde des objets. A priori, leurs travaux se basent sur le langage exprimental Cw (prononcez C omega) prsent lURL http:
//research.microsoft.com/Comega/.
738
NHibernate : http://wiki.nhibernate.org/display/NH/Home
Une liste plus complte est disponible sur le site http://sharptoolbox.com dans la catgorie
Object-Relational mapping.
Microsoft souhaitait fournir un outil de mapping OR avec .NET 2.0 nomm Object Space. La
livraison de cet outil a t repousse, priori parce que le projet a gagn en envergure et sera
notamment intgr au futur moteur de gestion de donnes nomm WinFS.
Vous pouvez consulter cet article de Fabrice Marguerie qui prsente un certain nombre de
critres prendre en compte lors du choix dun outil de mapping OR http://madgeek.com/
Articles/ORMapping/FR/mapping.htm.
EndExecuteNonQuery()
EndExecuteReader()
EndExecuteXmlReader()
Bien quen interne le fonctionnement soit dirent, ces mthodes sutilisent pareillement quun
dlgu asynchrone, savoir, en utilisant un objet accessible par linterface IAsyncResult pour
matrialiser lappel asynchrone. Lexemple suivant obtient deux DataReader sur notre base
ORGANISATION dune manire asynchrone. Notez quune connexion ne peut supporter simultanment plusieurs appels asynchrones aussi nous sommes obligs dutiliser deux connexions
quivalentes. En outre, notez que vous devez spcifier async=true dans la chane de connexion
des connexions susceptibles dtre utilises pour eectuer des appels asynchrones. En eet, le
support de cette possibilit un lger impact ngatif sur lexcution synchrone des commandes
do lintrt du drapeau async.
739
Exemple 19-23 :
using System.Data ;
using System.Data.SqlClient ;
class Program {
static string sCnx = "server = localhost ; uid=sa ; pwd = ; " +
"database = ORGANISATION ; async=true" ;
static string sCmd1 = "SELECT * FROM EMPLOYES" ;
static string sCmd2 = "SELECT * FROM DEPARTEMENTS" ;
static void Main() {
using (SqlConnection cnx1 = new SqlConnection(sCnx))
using (SqlConnection cnx2 = new SqlConnection(sCnx)) {
cnx1.Open() ;
SqlCommand cmd1 = new SqlCommand(sCmd1, cnx1) ;
System.IAsyncResult ar1 = cmd1.BeginExecuteReader() ;
cnx2.Open() ;
SqlCommand cmd2 = new SqlCommand(sCmd2, cnx2) ;
System.IAsyncResult ar2 = cmd2.BeginExecuteReader() ;
// Ici vous pouvez faire du travail avec le thread courant.
SqlDataReader rdr1 = cmd1.EndExecuteReader(ar1);
SqlDataReader rdr2 = cmd2.EndExecuteReader(ar2);
// Ici utilisation de rdr1 et rdr2
} // end using cnx1 et cnx2.
}
}
Notez enfin que des vrifications pouvant entraner une exception lors de lappel dune mthode BeginXxx() sont eectues sur les paramtres entrants. En outre lexcution peut aussi
connatre des checs qui provoqueront lenvoi dun exception lors de lappel dune mthode
EndXxx().
740
La classe SqlBulkCopy prsente aussi la possibilit dtre notifie sur ltat courant de la copie,
la possibilit de rgler un time out sur lopration de copie et la possibilit de dfinir une correspondance entre les colonnes sources et destinations au cas o les schmas des tables impliques
ne seraient pas strictement identiques.
Une intgration pousse avec le CLR. SQL Server Express est une version simplifie du SGBD
phare (mais payant) de Microsoft, SQL Server 2005.
Dploiement dapplication XCopy possible. Une base de donne peut se rsumer un fichier .mdf prsent dans larborescence de rpertoires dune application .NET dploye.
Linstallation de SQL Server 2005 Express Edition peut faire partie des prrequis du dploiement ClickOnce dune application .NET. Pas besoin de linstallation de MDAC.
Des outils graphiques facilitent lutilisation du produit.
Une intgration pousse avec Visual Studio 2005. En outre, SQL Server Express Edition est
automatiquement install lors de linstallation de Visual Studio 2005.
Support pouss du XML. Possibilit de typer une colonne dune table en XML.
Meilleure performance et meilleure gestion de la scurit.
Plus dinformation sur ce produit peuvent tre trouves sur le site de Microsoft.
20
Les transactions
Si une des deux commandes choue, lautre commande doit absolument chouer. Les deux
commandes doivent donc tre excutes au sein dune mme transaction.
Les transactions servent garantir lintgrit dun ensemble de donnes aprs une phase
de mises jour.
Cet exemple illustre aussi le fait que les commandes excutes au sein dune transaction peuvent
se faire sur une mme base de donnes ou sur des bases de donnes direntes. En eet, on peut
supposer que les deux comptes appartiennent la mme banque, auquel cas les commandes se
font srement sur la mme base de donnes. Si les deux comptes appartiennent des banques
direntes, les commandes se font sur des bases de donnes distinctes. On dit que la transaction
est distribue.
La thorie des transactions est assez complexe. Un milieu transactionnel supporte idalement
quatre proprits, dites proprits ACID :
742
Atomicity
Consistency
Isolation
Les transactions seectuent dune manire isole les unes des autres.
Durable
Nous allons voir dans cette section quelles sont les possibilits prsentes par le framework pour
eectuer des transactions locales ou distribues. Nous verrons que lensemble des proprits
ACID peut dans certains cas tre amoindri pour ne pas trop pnaliser les performances.
Lui communiquer quelles sont les sources de donnes impliques dans la transaction. Durant cette tape, le RM dune source lenregistre auprs du moteur. On parle alors denrlement dune source de donnes au sein dune transaction (enlistement en anglais).
Eectuer les mises jour des donnes de chaque source durant la transaction.
Le moteur transactionnel est responsable de la coordination des travaux des RM. Cette coordination a pour but de garantir lintgrit des donnes : soit elles sont toutes t mises jour
correctement, soit le droulement de la transaction a rencontr un problme et aucune modification de donnes na eu lieu. Pour grer une telle coordination, du point de vue dun moteur
transactionnel les RM supportent tous une mme API qui permet de les faire participer au sein
dune transaction. Toute cette architecture est expose par la figure suivante :
743
ResourceManager
firmware
(RM)
Source de donnes
Requtes et
mises jour
des donnes
durant la
transaction
Enrlement
des sources et
coordination
des travaux
des RM par
le moteur
ResourceManager
firmware
(RM)
Source de donnes
Dans la premire phase lapplication enrle chaque source de donnes et eectue les requtes et mises jour de donnes. Le moteur informe chacun des RM quil participe une
transaction distribue. Chaque RM eectue alors une transaction locale sur ses donnes.
Il contacte le moteur transactionnel juste avant de valider sa transaction locale si celle-ci a
russi ou ds quun chec est constat. On parle de vote de la part des RM. Chaque RM vote
Succs ou Echec.
Si au moins un RM a chou dans sa transaction locale, le moteur informe tous les RM
participants que la transaction distribue a chou. Chaque RM doit alors annuler sa transaction locale en remettant ses donnes dans leur tat initial. Si le succs a t vot lunanimit, la transaction distribue est en passe dtre russie et le moteur informe chaque RM
quil doit valider sa transaction locale.
Une variante de cet algorithme existe. Dans la premire phase, lorsquun RM constate que sa
transaction locale a russi, il peut la terminer eectivement. Si la transaction distribue russit
aussi, le RM na alors rien faire durant la seconde phase. Dans le cas contraire le RM doit refaire
une transaction locale pour annuler les eets de la premire transaction. On parle dalgorithme
de recouvrement (recovery en anglais) de mise jour des donnes.
Tel quel, lalgorithme 2PC nest pas fiable 100%. Une panne rseau entre le moteur et un RM
qui survient au milieu de la deuxime phase laisse ce RM dans un tat inconsistant. Une telle
situation est qualifie de douteuse (in-doubt en anglais). Les consquences ngatives de ces cas
rares mais invitables sont rpares grce un service de recouvrement qui vrifie priodiquement
si une transaction distribue a potentiellement corrompu les donnes en se terminant dune
manire douteuse. Ce service peut tenter de rparer automatiquement les donnes corrompues
grce des informations sauves par la source de donnes lors de la premire phase. Sil choue,
le service de recouvrement peut, en dernier recours, informer un administrateur.
Il existe des protocoles permettant un moteur de coordonner une transaction distribue entre
les RM. On peut citer les protocoles OleDB, XA ou WS-AtomicTransaction.
Au vu de la description de lalgorithme 2PC, il est clair que les transactions distribues sont
des oprations coteuses qui ncessitent de nombreuses communications entre les dirents
participants. Il est donc toujours prfrable dutiliser une transaction locale si possible.
744
Ces commandes doivent tre excutes au sein dune transaction. En eet, si lajout du dpartement choue et si lajout de lemploy russit, la base de donnes se retrouve dans un tat incohrent car un employ appartient un dpartement qui nexiste pas (notez que dans ce cas prcis
la condition dintgrit rfrentielle FOREIGN KEY (DepID) REFERENCES DEPARTEMENTS(DepID)
empche ce type de corruption de donnes).
Exemple 20-1 :
using System.Data.Common ;
using System.Data.SqlClient ;
class Program {
static void Main() {
string sCnx =
"server = localhost ; uid=sa ; pwd = ; database = ORGANISATION" ;
string sCmd1 =
"INSERT INTO DEPARTEMENTS VALUES (COM,Communication)" ;
string sCmd2 =
"INSERT INTO EMPLOYES VALUES (COM,Smith,Adam,0497112239)" ;
try {
using (SqlConnection cnx = new SqlConnection(sCnx)) {
cnx.Open() ;
DbTransaction tx = cnx.BeginTransaction();
DbCommand cmd = new SqlCommand(sCmd1, cnx) ;
cmd.Transaction = tx ;
cmd.ExecuteNonQuery() ;
cmd.CommandText = sCmd2 ;
cmd.ExecuteNonQuery() ;
tx.Commit();
// Ici les deux commandes se sont correctement ex
ecut
ees
// et la transaction a
et
e effectu
ee.
} // end using cnx.
} catch {
// Si une exception a
et
e lanc
ee avant ou pendant lappel `
a
// tx.Commit() les donn
ees de la base nont pas
et
e modifi
ees.
}
745
}
}
Dans le cas particulier du SGBD SQL Server, le langage T-SQL ore la possibilit deectuer des
transactions (do le T pour Transact). Par exemple le code T-SQL suivant permet de raliser
nos deux oprations dune manire transactionnelle. Notez la ncessit de tester aprs chaque
opration si une erreur sest produite :
BEGIN TRANSACTION
INSERT INTO DEPARTEMENTS VALUES (COM,Communication)
If @@error <> 0 GOTO ERR_HANDLER
INSERT INTO EMPLOYES VALUES (COM,Smith,Adam,0497112239)
If @@error <> 0 GOTO ERR_HANDLER
COMMIT TRANSACTION
RETURN
ERR_HANDLER:
ROLLBACK TRANSACTION
RETURN
Dans notre programme C le fournisseur de donnes SqlClient exploite T-SQL pour reproduire ce comportement transactionnel. Lutilisation directe de T-SQL est donc ncessairement
plus performante pour raliser des transactions locales. En revanche, lutilisation du fournisseur de donnes se prte mieux la rdaction de transactions complexes impliquant un grand
nombre de lignes. Prcisons quil vaut mieux ne pas mettre jour une trop grosse quantit
de donnes lors dune mme transaction locale. Un ordre de grandeur ne pas dpasser est le
millier de lignes. En eet, dans le cas dune transaction massive qui se termine en chec ou en
situation douteuse, le temps pris pour finaliser la transaction devient prohibitif. Si vos besoins
dpassent cette limite, il faut trononner vos mises jour en transactions de taille raisonnable.
746
ou Ajouter/Supprimer des composants Windows Serveur dapplication. Installez alors COM+ puis
MSDTC.
Si le DTC est en cours dexcution, il est possible de lister les transactions courantes avec longlet
Liste des transactions et dobtenir des statistiques sur les transactions eectues par le DTC avec
longlet Statistiques de transactions.
Depuis plusieurs annes le moteur DTC est exploitable partir de la technologie COM+. Depuis
les versions 1.x de .NET les dveloppeurs peuvent raliser des transactions distribues avec le
DTC. En eet la partie du framework .NET nomme Service dEntreprise (qui fait lobjet de la
section page 295) permet dencapsuler les services de la technologie COM+ au moyen dune
API .NET. Ainsi, avec les versions 1.x du framework il faut utiliser les services dentreprises pour
raliser des transactions distribues et les fournisseurs de donnes ADO.NET pour raliser des
transactions locales.
Le framework System.Transactions
La version 2 du framework .NET prsente le nouvel espace de noms System.Transactions qui
propose un modle de programmation transactionnelle unifi. Tout assemblage qui souhaite
utiliser les services de ce framework doit rfrencer lassemblage Systems.Transactions.dll.
Le framework System.Transactions
747
La transaction est srialise pour tre utilise dans un autre domaine dapplication.
Toutes les connexions un SGBD sont des RM durables. Cependant, seules les connexions vers
le SGBD SQL Server 2005 sont des RM durables PSPE (pour linstant).
La documentation anglo saxonne dit quune transaction est escalated lorsquelle est dlgue du
LTM vers le DTC (on pourrait traduire le terme escalated par promue ou alourdie en franais).
Cette escalation dune transaction se fait dune manire compltement transparente du point de
vue du code client de System.Transactions. Ce sont les mmes classes et mthodes de System.
Transactions qui sont utilises indpendamment du fait qu lexcution le LTM ou le DTC est
impliqu. Loptimisation est implicite mais il est prfrable que vous ayez conscience de tout
ceci si les performances constituent pour vous un enjeu important. Prcisons quen interne, la
dlgation dune transaction vers le DTC nlimine pas compltement le LTM de la partie. Ce
dernier collabore en fait avec le DTC pour continuer grer les RM volatiles.
Nous mentionnons un malentendu classique. Comprenez bien que le LTM et le DTC sont
tous deux des moteurs transactionnels distribus. Leur dirence ne se situe pas au niveau du
nombre de RM quils peuvent grer. Seule la qualit durable/durable PSPE/volatile des RM participant une transaction permet au framework System.Transactions de dcider dimpliquer
ou non le DTC.
748
Cet exemple illustre le fait que toute connexion ouverte pendant lexistence dun objet de type
System.Transactions.TransactionScope est automatiquement et implicitement enrle dans
la transaction sous-jacente. Si vous excutez lexemple prcdent sur un autre SGBD que SQL
Server 2005 (par exemple avec SQL Server 7 ou 2000) vous constaterez quune transaction DTC
est cre dans longlet Liste des transactions du DTC. Dans ce cas prcis de transaction locale
un SGBD de type autre que SQL Server 2005 il est plus ecace dutiliser le modle de programmation transactionnel ADO.NET 1.x propos par le fournisseur de donnes. Jim Johnson
prsente dans une entre de son blog disponible lURL http://pluralsight.com/blogs/
jimjohn/archive/2005/09/13/14795.aspx une possibilit de contourner cette limitation en
rendant PSPE un RM durable non PSPE.
Le programme suivant illustre une transaction distribue qui porte sur deux bases de donnes.
Lide est ici de muter un employ de la base ORGANISATION vers la base ORGANISATIOSN2. Grce
au modle de programmation unifi de System.Transactions, le code de cet exemple est similaire celui de lexemple prcdent:
Exemple 20-3 :
using System.Data.Common ;
using System.Data.SqlClient ;
using System.Transactions ;
class Program {
static void Main() {
string sCnx1 =
"server = localhost ; uid=sa ; pwd = ; database = ORGANISATION" ;
string sCnx2 =
"server = localhost ; uid=sa ; pwd = ; database = ORGANISATION2" ;
string sCmd1 =
"DELETE FROM EMPLOYES WHERE Nom=Smith AND Pr
enom=Adam" ;
string sCmd2 =
"INSERT INTO EMPLOYES VALUES (DEV,Smith,Adam,0497112239)" ;
try {
using ( TransactionScope txScope = new TransactionScope() ) {
using ( SqlConnection cnx1 = new SqlConnection(sCnx1) ) {
cnx1.Open();
DbCommand cmd = new SqlCommand(sCmd1, cnx1) ;
cmd.ExecuteNonQuery() ;
} // end using cnx1.
Le framework System.Transactions
749
750
Lecture sale (Dirty read en anglais) : Une transaction T1 en cours peut avoir accs en lecture
aux donnes modifies par une autre transaction T2 en cours. Cela pose un problme si T2
ne valide pas ses changements.
Lecture non rptable (Non repeatable read en anglais) : Lorsquune transaction en cours eectue une seconde lecture dune certaine donne, il se peut que la valeur obtenue ait chang
du fait dune transaction qui sest termine durant le laps de temps entre les deux lectures.
Lecture fantme (Phantom read en anglais) : Lorsquune transaction en cours eectue une
seconde fois une requte quelle a dj eectue, il se peut que lensemble des lignes retournes la seconde fois soit dirent de lensemble des lignes retournes la premire fois. Cela
est alors du des modifications eectues et valides entre temps par une autre transaction.
Voici les niveaux disolations proposs par lnumration IsolationLevel : du plus permissif
au plus strict et donc, du moins coteux en terme de performance au plus pnalisant :
TIL
Lecture sale
Lecture fantme
ReadUncommited
possible
possible
Possible
ReadCommited
impossible
possible
Possible
RepeatableRead
impossible
impossible
Possible
Snapshot
impossible
impossible
impossible
Serialize
impossible
impossible
impossible
Le framework System.Transactions
751
La dirence entre les TIL Snapshot et Serialize tient dans le fait que lors de lutilisation du
TIL Snapshot, une exception peut tre leve (et par consquent une transaction peut choue)
si un des trois cas problmatiques est constat. Si vous choisissez le TIL Serialize, sachez quen
interne un systme de verrouillage des donnes est mis en place. Ce systme est coteux car il
gne lexcution des autres transactions puisquelles doivent attendre leur tour pour avoir accs
aux donnes verrouilles. On qualifie lapproche Snaphot doptimiste (dans le sens o lon est
confiant que lchec des transactions survient rarement) tandis que lapproche Serialize est
qualifie de pessimiste.
vous de choisir le niveau disolation de vos transactions en soupesant limportance de leur
succs face votre besoin de performance. Une boutade dit que seules les oprations manipulant de largent ont besoin du niveau disolation Serialize.
Mandatory : Une transaction doit exister au moment o le nouveau scope est cr. Dans
le cas contraire une exception est leve.
RequiresNew : Ce nouveau scope entrane la cration dune nouvelle transaction indpendamment du fait quune transaction courante existe ou non. Le cas chant, la transaction
cache redeviendra la transaction courante la fermeture de ce nouveau scope.
Supported : Ce nouveau scope nentrane pas de cration dune nouvelle transaction mais
prend en compte une ventuelle transaction courante au moment ou il est cr.
752
Nous allons voir dans la prochaine section quil est parfois ncessaire davoir recours une cette
syntaxe explicite dans certains contextes asynchrones. Mis part ce cas, cette pratique est en
gnral viter puisque nous voyons quelle ncessite plus de code (puisquil faut explicitement
enrler les sources de donnes) sans apporter davantages.
753
transaction mre et toutes ses transactions dpendantes chouent. Voici un exemple exposant
la syntaxe dutilisation des transactions dpendantes :
Exemple 20-6 :
using System.Data.Common ;
using System.Data.SqlClient ;
using System.Transactions ;
using System.Threading ;
class Program {
static string sCnx =
"server = localhost ; uid=sa ; pwd = ; database = ORGANISATION" ;
static string sCmd1 =
"INSERT INTO DEPARTEMENTS VALUES (COM,Communication)" ;
static string sCmd2 =
"INSERT INTO EMPLOYES VALUES (COM,Smith,Adam,0497112239)" ;
static void Main() {
try {
using (TransactionScope txScope = new TransactionScope()) {
using (SqlConnection cnx = new SqlConnection(sCnx)) {
cnx.Open() ;
DbCommand cmd = new SqlCommand(sCmd1, cnx) ;
cmd.ExecuteNonQuery() ;
DependentTransaction depTx =
Transaction.Current.DependentClone (
DependentCloneOption.BlockCommitUntilComplete );
ThreadPool.QueueUserWorkItem(AsyncProc, depTx);
txScope.Complete();
} // end using cnx.
} // end using txScope, lappel `
a ITransaction.Commit()
// seffectue ici.
} catch { }
}
static void AsyncProc(object state) {
DependentTransaction depTx = state as DependentTransaction;
try {
using (SqlConnection cnx = new SqlConnection(sCnx)) {
cnx.Open() ;
cnx.EnlistTransaction(depTx) ;
DbCommand cmd = new SqlCommand(sCmd2, cnx) ;
cmd.ExecuteNonQuery() ;
depTx.Complete();
} // end using cnx.
} catch {
depTx.Rollback() ;
}
}
}
754
System.Transactions et CAS
Il est intressant de remarquer que le code qui implique le recours au DTC pour grer
une transaction doit avoir la permission CAS System.Transactions.DistributedTransactionPermission. Dans le cas de la promotion dune RM lors de la dlgation dune transaction du LTM vers le DTC, cest le code qui a dclench la promotion qui doit avoir la permission
et non le code responsable de lenrlement.
755
756
757
singlePhaseEnlistment.Committed();
}
#endregion
}
Voici un petit programme qui exploite une seule instance de txList<string> dans une transaction :
Exemple 20-9 :
using System.Transactions ;
class Program {
static void Main() {
TxList<string> txList = new TxList<string>("List") ;
using ( TransactionScope txScope = new TransactionScope() ) {
txList.TxAdd("A") ; txList.TxAdd("B") ;
txScope.Complete();
txList.DisplayContent() ;
}
txList.DisplayContent() ;
}
}
On peut facilement suivre le droulement des oprations grce lachage du programme :
List.TxAdd(A)
List.EnlistVolatile()
List.TxAdd(B)
--> Contenu de List :
dataToCommit: A;B;
List.SinglePhaseCommit()
List.OnCommit()
--> Contenu de List : A;B ;
dataToCommit:
Si lon ne valide pas la transaction en mettant en commentaire lappel txScope.Complete(),
la transaction est automatiquement avorte et le programme ache ceci :
List.TxAdd(A)
List.EnlistVolatile()
List.TxAdd(B)
--> Contenu de List :
dataToCommit: A;B;
List.Rollback()
--> Contenu de List :
dataToCommit:
Rcrivons notre programme de faon ce quil gre deux listes transactionnelles au sein dune
mme transaction :
Exemple 20-10 :
using System.Transactions ;
class Program {
static void Main() {
TxList<string> txList1 = new TxList<string>("List1") ;
758
21
XML
Introduction XML
Les problmes rsolus par XML
XML est lacronyme de eXtensible Markup Langage. XML est la rponse au besoin de standardisation du formatage des donnes changes entre systmes htrognes. Avant XML, les donnes
taient changes dans des formats propritaires. Les applications devaient connatre ce format
propritaire pour exploiter les donnes et cela posait les trois problmes suivants.
760
Chapitre 21 : XML
une donne spcifique lintrieur du document XML peut tre connu par lapplication
en mme temps que linstance du document. On peut voir ces schmas comme des types
de documents XML.
books.xml
Introduction XML
761
<last-name>Franklin</last-name>
</author>
<price>8.99</price>
</book>
<book genre="novel" publicationdate="1967" ISBN="0-201-63361-2">
<title>The Confidence Man</title>
<author>
<first-name>Herman</first-name>
<last-name>Melville</last-name>
</author>
<price>11.99</price>
</book>
<book genre="philosophy" publicationdate="1991" ISBN="1-861001-57-6">
<title>The Gorgias</title>
<author>
<name>Plato</name>
</author>
<price>9.99</price>
</book>
</bookstore>
Plusieurs remarques simposent :
Il y a autant de balises que de types de donnes. Il y a une balise pour la notion de bibliothque <bookstore>, une balise pour la notion de livre <book> une balise pour la notion
de titre de livre <title> etc. Un groupe <balise>donn
ees</balise> est appel lment
XML. On voit quun lment XML peut contenir soit une donne soit (exclusif) dautres
lments XML.
Un lment qui contient dautres lments peut galement contenir des donnes grce
la notion dattribut XML (attention, cela na rien voir avec la notion dattribut .NET). Par
exemple, le genre dun livre est un attribut nomm genre de llment <book>.
Le fait quun lment XML puisse en contenir dautres permet de prsenter les donnes
sous une forme hirarchique. Une application qui veut obtenir les titres des livres parus en
1967 doit dabord trouver les lments <book> ayant un attribut publicationdate gal
1967 puis numrer les lments <title> correspondant. Peu importe lordre dans lequel
les livres sont stocks dans le document XML, lapplication saura toujours retrouver linformation dont elle a besoin.
On peut insrer des commentaires dans un fichier XML avec la syntaxe <!-- commentaire
-->.
Lentte dun document XML peut spcifier le type dencodage du texte avec lattribut
encoding. On voit que le fichier texte books.xml est cod au format UTF-8.
Bien que cet exemple ne le montre pas, certains attributs particuliers peuvent dfinir
des espaces de noms XML dans un lment XML. On exploite les espaces de nom XML
pour viter quil y ait collision de noms des lments ou des attributs lorsque plusieurs
langages XML sont utiliss simultanment. On associe en gnral une URI (tel que
http://www.smacchia.com) un espace de noms XML pour tre certain de lunicit du nom
de lespace de noms. Modifions notre exemple pour illustrer la syntaxe :
762
Chapitre 21 : XML
...
<bookstore xmlns:SMA="http://www.smacchia.com" >
<SMA:book genre="autobiography" publicationdate="1981"
ISBN="1-861003-11-0">
...
</SMA:book>
...
books.xsd
763
XPath
XPath est un langage permettant de rdiger des requtes pour slectionner des nuds dans un
document XML. Certains lont surnomm SQL pour XML car la finalit de XPath vis vis
des donnes reprsentes au format XML est similaire la finalit de SQL vis vis des donnes
reprsentes dans une base de donnes relationnelle.
XPath permet de slectionner des noeuds dans un document XML avec une syntaxe comparable
lcriture du chemin dun fichier. Voici des exemples dexpressions XPath (en gras) appliques
sur le document books.xml suivies par la liste des nuds slectionns :
/bookstore/book/author/first-name
<first-name>Benjamin</first-name>
<first-name>Herman</first-name>
/bookstore/book/author/*
<first-name>Benjamin</first-name>
<last-name>Franklin</last-name>
<first-name>Herman</first-name>
<last-name>Melville</last-name>
<name>Plato</name>
/bookstore/book[@publicationdate>1980]/title
<title>The Autobiography of Benjamin Franklin</title>
<title>The Gorgias</title>
XSLT
Lacronyme XSLT veut dire eXtensible Stylesheet Language Transformation. XSLT est un langage
permettant dexploiter les donnes contenues dans un document XML afin dobtenir un autre
document. Le document produit peut tre, par exemple, au format HTML, dans un autre format XML ou simplement au format texte.
764
Chapitre 21 : XML
Un programme crit en XSLT (i.e un stylesheet) est un document XML. XSLT est construit sur
un systme de template. lexcution, XSLT slectionne les nuds du document source qui
correspondent aux nuds du stylesheet puis excute le corps du template pour chaque noeud
slectionn. La slection se fait par lintermdiaire dune expression XPath.
Le stylesheet suivant appliqu au document books.xml illustre ceci. Notez la prsence de lespace
de noms caractristique xsl. Les expressions en gras sont rdiges avec XPath.
Exemple 21-3 :
books.xslt
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:template match= "/bookstore" >
<xsl:for-each select= "book[@publicationdate>1980]" >
Titre : <xsl:value-of select= "title" />
Publie il y a <xsl:value-of select= "2005 - (@publicationdate)" /> ans
</xsl:for-each>
</xsl:template>
</xsl:stylesheet>
Le document produit est celui-ci :
Titre : The Autobiography of Benjamin Franklin
Publie il y a 24 ans
Titre : The Gorgias
Publie il y a 14 ans
Pour une prsentation complte des technologies XPath et XSLT nous vous conseillons de
consulter louvrage suivant :
Comprendre XSLT
OReilly 2002
Bernd Amann, Philippe Rigaud
ISBN : 2-84177-148-2
XQuery
Le langage XQuery a la mme finalit que XSLT : transformer un document XML en un autre
document texte (qui nest pas ncessairement un document XML). Remarquez que cette finalit
peut tre aussi apprhende comme une faon de faire des requtes sur un document XML, do
le nom de XQuery.
XQuery est considr comme plus convivial que XSLT notamment parce quil nest pas luimme un langage XML. La raison principale de cet engouement pour XQuery rside cependant
dans le fait que le flot dexcution des instructions est comparable celui des langages impratifs
bien connus tels que C ou Java.
linstar dXSLT, XQuery exploite le langage XPath pour la slection de nud. Voici un programme XQuery qui transforme notre document books.xml en un fichier texte qui numre
dans lordre de leurs dates de publication les titres des livres autobiographiques :
765
Lapproche type curseur : Le document XML est parcouru laide dun curseur. Le curseur
peut tre vu comme la position dans le document XML de linformation en cours de lecture. Cette position peut dsigner un lment XML ou un attribut XML. Le programme
est responsable de dplacer le curseur partir de la position courante vers la position des
prochaines informations consommer (aller vers mon prochain lment frre , aller
vers mon premier lment enfant etc).
Lapproche type arbre : Lors dune phase de prchargement du document XML un arbre
dobjets est cr. Chaque objet dans larbre reprsente une entit du document XML. On
peut alors exploiter les informations contenues dans le document en parcourant larbre des
objets.
Lapproche type curseur a plusieurs avantages sur lapproche type arbre. Elle ne ncessite pas une
phase de prchargement et la mmoire ncessaire pour exploiter le document XML ne dpend
pas de la taille du document. Il est clair que lapproche type curseur est plus ecace en termes
de performance. Cependant, la programmation objet fait quil est parfois plus ais dexploiter
des informations contenues dans un arbre dobjets. Les deux modles ont donc chacun leurs
raisons dtre implmenter par le framework .NET.
Les oprations de gnration et de modification dun document XML peuvent aussi se faire
selon ces deux approches. Les avantages et les inconvnients sont les mmes mis part que la
phase de prchargement du modle arbre est remplac par une phase de post gnration lorsque
lon souhaite finaliser les changements.
Enfin, nous allons expliquer que les deux approches ne sont pas incompatibles. Par exemple, il
est possible de parcourir un arbre charg en mmoire avec un curseur. Notamment, nous verrons que lon peut se servir de requtes XPath sur larbre afin dobtenir un curseur sur lensemble
des nuds slectionn. Tout lavantage de ces pratiques rside dans le fait que lon peut crire du
code de manipulation de donnes indpendant du mode de stockage XML. Les donnes XML
sont consommes par une approche type curseur que le document soit pr charg en mmoire
ou charg au fur et mesure partir dune source telle quun fichier.
Si le framework standard System.Xml que nous allons prsenter ne vous satisfait pas sur certains
points, vous pouvez envisager davoir recours au framework gratuit et open-source Mvp.Xml. Plus
dinformation sont disponibles lURL http://sourceforge.net/projects/mvp-xml/.
766
Chapitre 21 : XML
" ;
767
do
Console.WriteLine("{0}{1} Attr:{2}" ,
sIndent , sElem , xtr.Value ) ;
while ( xtr.MoveToNextAttribute() ) ;
}
else if (xtr.NodeType == XmlNodeType.Text)
Console.WriteLine("{0}{1} Val:{2}",sIndent,sElem,xtr.Value) ;
}
}
}
Cet exemple ache ceci :
bookstore Attr:http://www.contoso.com/books
book Attr:autobiography
book Attr:1981
book Attr:1-861003-11-0
title Val:The Autobiography of Benjamin Franklin
first-name Val:Benjamin
last-name Val:Franklin
price Val:8.99
book Attr:novel
book Attr:1967
book Attr:0-201-63361-2
title Val:The Confidence Man
...
La classe XmlReader prsente de nombreuses facilits pour la lecture des donnes telles que le
typage des valeurs rcupres, la prise en compte des espaces de noms, viter les espaces et les
commentaires etc. Ceci est possible grce de multiples mthodes telles que MoveToContent(),
ReadContentAsBoolean(), ReadStartElement() etc.
768
Chapitre 21 : XML
settings.Schemas.Compile();
settings.ValidationType = ValidationType.Schema;
settings.ValidationEventHandler += ValidatingProblemHandler;
settings.ValidationFlags =
XmlSchemaValidationFlags.ReportValidationWarnings;
XmlReader xtr = XmlReader.Create(@"C:\books.xml",settings) ;
...
}
static void ValidatingProblemHandler(object sender,
ValidationEventArgs e) {
if ( e.Severity == XmlSeverityType.Warning ) {
Console.Write("WARNING: ") ; Console.WriteLine(e.Message) ;
} else if ( e.Severity == XmlSeverityType.Error ) {
Console.Write("ERROR: ") ;
Console.WriteLine(e.Message) ;
}
}
}
769
books2.xml
770
Chapitre 21 : XML
XmlDocument xDoc = new XmlDocument() ;
try { xDoc.Load(@"C:\books.xml") ; }
catch { }
foreach (XmlNode xNode in xDoc.ChildNodes)
DisplayNode(xNode, string.Empty) ;
}
}
771
event
event
event
event
event
event
XmlNodeChangedEventHandler
XmlNodeChangedEventHandler
XmlNodeChangedEventHandler
XmlNodeChangedEventHandler
XmlNodeChangedEventHandler
XmlNodeChangedEventHandler
NodeChanged ;
NodeChanging ;
NodeInserted ;
NodeInserting ;
NodeRemoved ;
NodeRemoving ;
772
Chapitre 21 : XML
@"/bookstore/book/author/first-name");
foreach (XmlNode book in books)
System.Console.WriteLine(book.OuterXml) ;
}
}
773
DisplayRecursive(navigator, indent) ;
}
}
static private void DisplayNode(XPathNavigator navigator,
string indent) {
if (navigator.NodeType == XPathNodeType.Text)
Console.WriteLine(indent+navigator.Value) ;
else if (navigator.Name != String.Empty)
Console.WriteLine(indent + "<" + navigator.Name + ">") ;
}
}
Cet exemple ache ceci :
<bookstore>
<book>
<title>
The Autobiography of Benjamin Franklin
<author>
...
774
Chapitre 21 : XML
Books.xml
775
Exemple 21-13 :
using System.Xml.Xsl ;
class Program {
static void Main() {
System.Xml.XmlDocument xDoc = new System.Xml.XmlDocument() ;
xDoc.Load(@"C:\books.xml") ;
XslCompiledTransform xTrans = new XslCompiledTransform();
xTrans.Load(@"C:\books.xslt");
xTrans.Transform(xDoc, null, System.Console.Out);
}
}
Dans la dernire section du prsent chapitre, nous exposons les facilits proposes par Visual
Studio pour ldition et le dbogage dun programme XSLT.
776
Chapitre 21 : XML
777
DataFile.xml
La classe System.Xml.XmlDataDocument
Nous avons vu dans les deux sections prcdentes comment transfrer des donnes au format
XML dans les tables dun DataSet et vice-versa. La classe System.Xml.XmlDataDocument (qui
drive de la classe XmlDocument) a t spcialement conue cet eet. Concrtement, on lie
une instance de XmlDataDocument avec un DataSet, puis on manipule les donnes par lintermdiaire du XmlDataDocument ou par lintermdiaire du DataSet. Il faut bien comprendre quen
interne, ce sont les mmes donnes qui sont manipules. Les changements faits par lintermdiaire du DataSet sont immdiatement visibles par lintermdiaire du XmlDataDocument et vice
versa.
Il est lgitime de se demander pourquoi utiliser la classe XmlDataDocument alors que les deux
sections prcdentes ont montr que la classe DataSet pouvait grer le format XML. Nous pouvons identifier les trois raisons suivantes :
778
Chapitre 21 : XML
Lutilisation de la classe XmlDataDocument permet de faire des requtes XPath sur les donnes.
Lutilisation de la classe XmlDataDocument permet de rester fidle un document XML
source. Si vous chargez un document XML dans un DataSet puis que vous le sauvez, il peut
y avoir des dirences de formatage entre les documents XML sources et destinations. Avec
lutilisation de la classe XmlDataDocument le document XML destination sera strictement
identique au document XML source (par exemple les espaces, lordre des lments ou les
commentaires nauront pas t modifis).
Lutilisation de la classe XmlDataDocument permet de tenir compte des relations entre les
tables du DataSet lors du formatage XML. Concrtement, dans une relation one to many ,
les lments enfants dun lment parent seront physiquement inclus dans llment parent dans le document XML. La notion de relation entre tables dun DataSet est prsente
page 728. Avec cette technique, une relation est prise en compte seulement si sa proprit
Nested est positionne true, ce qui nest pas le cas par dfaut.
779
}
}
Cet exemple ache ceci :
<EMPLOYES EmployeID="1" DepID="MKT" Nom="Lafleur" Pr
enom="L
eon"
Tel="0497112233"/>
<EMPLOYES EmployeID="3" DepID="DEV" Nom="Schmol" Pr
enom="Jean" />
...
La version 2005 de SQL Server apporte des nouvelles facilits quant lintgration de donnes
XML dans une base de donnes relationnelle. Notamment, une colonne dune table peut tre
type en XML. Chaque lment dune telle colonne est un document XML. Le type du framework .NET correspondant une telle colonne est System.Data.SqlTypes.SqlXml. Il y a aussi
possibilit de typer fortement une colonne XML par un schma XSD et vous pouvez eectuer
des requtes XQuery sur les lments dune telle colonne. Une prsentation dtaille de ces fonctionnalits de SQL Server 2005 dpasse le cadre du prsent ouvrage.
XmlSerializer ne srialise que les champs et attributs publics contrairement la srialisation dobjets binaire dcrite page 790.
XmlSerializer ne peut srialiser un graphe dobjets qui admet au moins une rfrence circulaire.
Voici un programme qui illustre lutilisation de la classe XmlSerializer en sauvant ltat dune
instance de la classe Entreprise dans un fichier XML, et en chargeant cet tat dans une autre
instance de la classe Entreprise.
Exemple 21-18 :
using System.Xml ;
using System.Xml.Serialization ;
using System.IO ;
public class book {
public string genre { get { return m_genre ; }
set { m_genre = value ; } }
private string m_genre ;
public string title { get { return m_title ; }
set { m_title = value ; } }
private string m_title ;
public decimal price { get { return m_price ; }
780
Chapitre 21 : XML
set { m_price = value ; } }
private decimal m_price ;
}
public class Program {
static public void Main() {
book b1 = new book() ;
b1.genre = "autobiography" ;
b1.title = "The Autobiography of Benjamin Franklin" ;
b1.price = 8.99M ;
//Sauve une instance de la classe book dans le fichier book.xml.
FileStream fs1 = File.OpenWrite("book.xml");
XmlSerializer xmls = new XmlSerializer(typeof(book));
xmls.Serialize(fs1, b1);
fs1.Close() ;
// Cree une instance de la classe book...
// ...`a partir du fichier book.xml.
FileStream fs2 = File.OpenRead("book.xml");
book b2 = (book)xmls.Deserialize(fs2);
fs2.Close() ;
}
}
book.xml
781
lon srialise un objet au format XML. Par exemple vous pourriez souhaiter ne pas srialiser la
proprit price et faire en sorte que la proprit genre soit un attribut XML et non un lment.
Voici les attributs .NET que vous pouvez utiliser :
XmlRoot : Permet didentifier une classe ou une structure comme le nud racine. En gnral on utilise cet attribut pour assigner un nom dirent du nom de la classe llment
XML correspondant.
XmlElement : Spcifie quun champ ou une proprit publique doit tre srialis comme
un lment. Comme il sagit du comportement par dfaut, on utilise en gnral cet attribut
pour assigner un nom dirent du nom du champ ou de la proprit, llment XML
correspondant.
XmlAttribute : Spcifie quun champ ou une proprit publique doit tre srialis comme
un attribut XML et non comme un lment, ce qui est le comportement par dfaut. On
peut en profiter pour assigner un nom dirent du nom du champ ou de la proprit,
llment XML correspondant.
XmlArray : Spcifie quun champ ou une proprit publique doit tre srialis comme un
tableau. En gnral on utilise cet attribut pour faire en sorte quun tableau dobjets soit
srialis.
XmlArrayItem : Spcifie que les instances dun type peuvent tre srialises dans un tableau.
XmlIgnore : Indique quil ne faut pas srialiser un champ ou une proprit publique.
782
Chapitre 21 : XML
Exemple :
book.xml
Loutil sgen.exe
Lorsque vous srialisez au format XML un objet dont le type na pas encore eu dinstances srialises, la classe XmlSerializer cre en interne une nouvelle classe spcialise dans la srialisation des instances de ce type. Vous pouvez amliorer les performances en pr-gnrant ces
classes spcialises dans la srialisation XML grce loutil sgen.exe (XML Serializer Generation
Tool). Cet outil accepte un assemblage XXX en entre (avec option /a) et cre un nouvel assemblage XXX.XmlSerializers.dll qui contient ces classes spcialises dans la srialisation XML.
Dans le cas de notre class book de lExemple 21-19, une classe Microsoft.Xml.Serialization.
GeneratedAssembly.bookSerializer est cre et vous pouvez lutiliser comme ceci :
Exemple 21-20 :
using Microsoft.Xml.Serialization.GeneratedAssembly;
...
public class Program {
static public void Main() {
...
FileStream fs1 = File.OpenWrite("book.xml") ;
bookSerializer bookS = new bookSerializer();
bookS.Serialize(fs1, b1);
...
book b2 = (book)bookS.Deserialize(fs2) ;
fs2.Close() ;
}
}
...
Loutil xsd.exe
Nous avons montr que lutilisation de certains attributs .NET permet de se plier un schma
XSD lors de la srialisation dun objet au format XML. Loutil xsd.exe fourni avec le framework
.NET va plus loin et permet les oprations suivantes :
xsd.exe peut gnrer le code dune classe C (drivant ventuellement de la classe DataSet)
partir dun schma XSD. Notamment, en page 731 nous expliquons comment cet outil
permet de crer des DataSet typs.
xsd.exe permet de gnrer un schma XSD partir dun fichier XML ou partir de types
contenus dans un assemblage.
Lutilisation de loutil xsd.exe est dcrite dans larticle XML Schema Definition Tool (Xsd.exe)
des MSDN.
783
books.xml
books.xsd
784
Chapitre 21 : XML
elementFormDefault="qualified"
targetNamespace="http://www.contoso.com/books"
xmlns:xs="http://www.w3.org/2001/XMLSchema">
<xs:element name="bookstore">
...
Vous pouvez aussi associer un schma XSD un document XML par lintermdiaire du menu
proprit Schemas du document XML. Ce menu propose une fentre daide au choix du
schma XSD parmi tous ceux connus de Visual Studio.
Lorsquun schma XSD est associ un document XML, Visual Studio ralise non seulement la
validation en dtectant les erreurs et en les mettant en vidence, mais il active aussi le mcanisme trs pratique dintellisense pour faciliter ldition du document XML.
Durant le dbogage dun programme .NET lors de lappel la mthode XslCompiledTransform.Transform() le dbogueur va directement au point dentre du programme
XSLT. Ceci est possible seulement si vous avez utiliser le constructeur XslCompiledTransform(bool enableDebug) avec la valeur true pour construire votre instance.
Directement partir de Visual Studio en utilisant le menu XML Debug XSLT qui est disponible lors de ldition dun programme XSLT. Pour exploiter cette possibilit, il faut au
pralable avoir fourni un fichier XML en entre et un fichier texte en sortie (qui accueillera
le produit de la transformation XSLT) dans les sous menu input et output du menu proprit
du document XSLT.
22
.NET Remoting
Introduction
Quest ce que .NET Remoting ?
Nous avons dfini la notion de domaine dapplication page 87 comme tant un conteneur
assemblage durant lexcution. Nous rappelons quun processus contient un ou plusieurs domaines dapplications, isols les uns des autres par le CLR. Lassemblage mscorlib contenant les
types de bases de la plateforme .NET est physiquement charg dans le processus hors de tout
domaine dapplication. Lisolation entre les domaines dapplication se fait principalement au
niveau des types, de la scurit et de la gestion des exceptions. Cette isolation ne se fait pas au
niveau des threads.
Si vous avez assimil cette notion de domaine dapplication, vous pouvez comprendre la dfinition de .NET Remoting qui est :
.NET Remoting est linfrastructure de la plateforme .NET qui permet des objets situs
dans des domaines dapplications dirents, de pouvoir se connatre et de pouvoir communiquer entre eux. Lobjet appelant est nomm client, lobjet appel est nomm serveur
ou objet serveur.
Deux domaines dapplications dirents peuvent se trouver :
786
.NET Remoting masque au dveloppeur ces trois aspects du problme. La localisation dun objet
se fait en gnral aprs la compilation des sources, pendant linstallation de lapplication. Plus
prcisment, linformation tel objet est accessible sur telle machine peut ne pas faire partie
du code source. Dans ce cas cette information se situe dans un fichier de configuration accessible
par le client de lobjet.
Nous prcisons, lattention des dveloppeurs ayant dj utilis les technologies Microsoft que
.NET Remoting peut tre vu comme le successeur de DCOM.
FAQ
Q : Peut-on choisir le protocole de communication sous-jacent la communication entre
deux objets ?
R : Oui. Avec .NET Remoting chaque protocole de communication est encapsul dans un objet
appel canal (channel en anglais). Les canaux encapsulant les protocoles HTTP, TCP ainsi quun
protocole de communication entre processus dune mme machine (IPC pour Inter Processus
Communication) sont implments nativement. Vous pouvez nanmoins dvelopper vos propres
canaux pour dautres protocoles.
Q : Sous quelle forme les donnes ncessaires un appel de mthode transitent-elles sur
le rseau ?
R : Les donnes ncessaires un appel de mthode sont principalement les valeurs des paramtres entrants, un identificateur de la mthode, un identificateur de lobjet avant lappel et les
valeurs des paramtres sortants au retour de lappel. En .NET Remoting les objets responsables
de lemballage de ces donnes sont appels formateurs (formaters en anglais). Les formateurs
emballant les donnes dans un format binaire ou dans un format XML nomm SOAP sont
implments nativement. Vous pouvez nanmoins dvelopper vos propres formateurs pour vos
besoins spcifiques (cryptage des donnes, limination de redondances etc).
Q : Larchitecture utilisant ces objets formateurs et canaux est-elle aussi utilise lorsque
le client et lobjet serveur sont dans le mme domaine dapplication ou dans le mme
processus ?
Dans ces deux cas, il nest videmment pas ncessaire dutiliser un canal qui utilise le rseau, et
.NET Remoting adopte automatiquement ce comportement. Il existe donc un canal spcifique
aux appels au sein du mme espace dadressage.
Q : Comment le code source du client, qui nest pas dans le mme assemblage que la classe
reprsentant lobjet serveur, peut-il connatre la classe de cet objet ?
R : Avec .NET Remoting, au moins trois techniques sont envisageables. Soit lassemblage client
rfrence lassemblage serveur la compilation. Dans ce cas lassemblage serveur doit tre install avec lassemblage client. Cette technique est donc trs contraignante. On lui prfre lencapsulation dinterfaces dans un assemblage tierce, qui est rfrenc la fois par le serveur et le
client. Une 3me technique permet de construire automatiquement cet assemblage tierce partir de lassemblage du serveur, mme si les classes de celui-ci nimplmentent pas dinterfaces.
Cette 3me technique ncessite lutilisation dun outil du framework .NET.
Q : Ne serait-il pas plus simple de rcuprer une copie de lobjet serveur dans le domaine
dapplication du client, et de travailler sur cette copie, plutt que dutiliser le rseau pour
chaque appel de mthode ?
787
R : .NET Remoting ore cette possibilit et la rponse la question est : a dpend. Ceci implique que lassemblage contenant la classe de lobjet serveur est accessible par le client et ce nest
en gnral pas souhaitable. En outre, beaucoup dobjets serveurs ne peuvent pas tre dplacs
dans un autre domaine dapplication. La raison classique est que ces objets serveurs contiennent
des rfrences vers dautres objets du domaine, qui sont inamovibles. Par exemple une instance
de la classe Thread peut tre considre comme un objet inamovible de son processus. Enfin,
dans le cas de la rcupration de lobjet par le client, on se prive de la possibilit de pouvoir
utiliser un objet dune manire concurrente entre plusieurs clients.
Q : Qui est responsable de la cration dun objet serveur ? Le client ou le serveur luimme ?
R : Les deux cas de figure sont envisageables avec .NET Remoting. Dans le cas o le serveur est
responsable de la cration dun objet, cet objet est identifi par un URI. Le client contacte cet
objet en utilisant cet URI, un peu comme vous contactez un proche en utilisant son numro de
tlphone. Dans ce cas, lobjet est eectivement cr lors du premier appel du client.
Q : Qui est responsable de la destruction dun objet serveur ? Le client ou le serveur luimme ?
R : Malgr un mcanisme de ping volu, DCOM a montr quil ne faut pas faire confiance au
client quant la destruction dun objet serveur. DCOM a aussi montr quun tel mcanisme
de ping nuit gravement aux performances. Avec .NET Remoting, les objets serveurs sont automatiquement dtruits par le serveur aprs une certaine dure durant laquelle lobjet na pas t
utilis. Un client qui essaye de contacter un objet qui nexiste plus rcupre une exception.
Q : .NET Remoting supporte-t-il les appels asynchrones ? (Reformul : Y a t-il un mcanisme pour quun client ne soit pas oblig dattendre que lappel dune mthode par .NET
Remoting se termine avant de continuer ?)
R : Oui. .NET Remoting supporte le mcanisme dappel asynchrone dcrit page 171. Lors dun
appel asynchrone, vous pouvez choisir ou non dtre averti par le serveur lorsque celui-ci a excut votre appel. Vous pouvez ainsi rcuprer des informations relatives votre traitement par
le serveur, dune manire asynchrone.
788
Comment la classe proxy fait-elle pour faire transiter les donnes entrantes et sortantes des
appels sur un rseau ?
Toutes les rponses ces questions se trouvent dans ce chapitre. Lanalyse du programme suivant
va apporter quelques lments de rponse. Nous nous intressons au cas o le client et la classe
de lobjet distant sont dans le mme assemblage et o les domaines dapplication du client et
de lobjet distant sont dans le mme processus.
Exemple 22-1 :
using
using
using
using
MBRTest.cs
System ;
System.Runtime.Remoting.Contexts ;
System.Runtime.Remoting ;
System.Threading ;
789
}
}
Ce programme ache :
obj1:
Nom du domaine : MBRTest.exe
ThreadID
: 6116
IsObjectOutOfAppDomain(obj1)=False
IsTransparentProxy(obj1)=False
obj2:
Nom du domaine : Autre domaine.
ThreadID
: 6116
IsObjectOutOfAppDomain(obj2)=True
IsTransparentProxy(obj2)=True
Notez lutilisation des mthodes statiques IsObjectOutOfAppDomain() et IsTransparentProxy() de la classe RemotingServices.
La Figure 22 -1 illustre larchitecture mise en place par le CLR pour excuter ce programme. La
notion de contexte est explique un peu plus loin dans le prsent chapitre :
Processus
Domaine dapplication par dfaut
FriendlyName = "MBRTest.exe"
Assemblage MBRTest
Contexte
Client (mthode statique Main())
...
Obj1.AfficheInfo(...)
Obj1
...
Obj2.AfficheInfo(...)
Contexte
Obj2
Thread
Assemblage MBRTest
ThreadID=6116
790
que lobjet rfrenc nest pas distant, ce qui peut tre mis en dfaut. Cela revient dire que
faire driver une classe de la classe MarshalByRefObject permet dindiquer au compilateur JIT
quune instance de cette classe peut potentiellement tre utilise dune manire distante grce
un proxy transparent. Dans le cas o cette instance et son client sont dans le mme domaine
dapplication, le client travaille avec une rfrence vers linstance et non avec un proxy transparent.
791
Domaine dapplication
contenant lobjet distant
original
...
obj1.Champs1 = 200
...
Champs 1 : 200
Champs 2 : 456
Champs 1 : 123
Champs 2 : 456
Exemple 22-2 :
...
[Serializable]
public class Foo{
...
// : MarshalByRefObject
<- en commentaire
792
la srialisation/dsrialisation. Vous pouvez ainsi profiter de telles mthodes pour fournir une
valeur aux nouveaux champs :
OnSerializingAttribute : La mthode est appele avant que ltat de lobjet ait t srialis.
OnSerializedAttribute : La mthode est appele aprs que ltat de lobjet ait t srialis.
La classe ObjectHandle
Dans le code de lexemple de la section de prsentation MBR, la place dutiliser la mthode
CreateInstanceAndUnwrap() on aurait pu crire :
...
ObjectHandle HObj2 = appDomain.CreateInstance("Remoting1","Foo") ;
Foo obj2 = (Foo)HObj2.Unwrap() ;
...
Une instance de la classe System.Runtime.Remoting.ObjectHandle contient les informations
ncessaires pour pouvoir utiliser un objet distant. En appelant la mthode UnWrap() vous rcuprez un proxy transparent si lobjet distant est MBR ou un clone si lobjet distant est MBV.
Vous pouvez optimiser vos applications en utilisant le fait que .NET scinde lopration de
rcupration dun objet distant en deux tapes : rcupration dune instance de la classe
ObjectHandle puis unwrappage de cette instance. En eet, le chargement des mtadonnes
de type dcrivant la classe de lobjet distant ne se fait qu la seconde tape, lors du unwrappage . Si le client courant nutilise pas lobjet distant et ne fait que le transmettre une autre
partie de lapplication, il na pas besoin de raliser le unwrappage . On conomise ainsi le
chargement des mtadonnes et donc, le chargement de lassemblage. Voici un programme qui
illustre ceci. Notez que ce programme exploite le fait que le constructeur statique dune classe est
appel par le CLR, lorsque celui-ci charge les mtadonnes de type de la classe concerne dans
un domaine dapplication.
Exemple 22-3 :
using System ;
using System.Runtime.Remoting ;
[Serializable]
public class Foo {
// Constructeur de la classe.
static Foo() {
Console.WriteLine(
"Chargement des metadonn
ees de Foo dans le domaine:" +
AppDomain.CurrentDomain.FriendlyName) ;
}
// Constructeur des instances de la classe.
public Foo() {
WrapTest.cs
793
Console.WriteLine(
"Appel au constructeur de Foo dans le domaine:" +
AppDomain.CurrentDomain.FriendlyName) ;
}
}
public class Program {
static void Main() {
Console.WriteLine("-->Avant CreateDomain()") ;
AppDomain appDomain = AppDomain.CreateDomain("Autre domaine.");
Console.WriteLine("-->Avant CreateInstance()") ;
ObjectHandle hObj = appDomain.CreateInstance("WrapTest", "Foo");
Console.WriteLine("-->Apr`es CreateInstance() et avant UnWrap()") ;
Foo obj = (Foo) hObj.Unwrap();
Console.WriteLine("-->Apr`es UnWrap()") ;
}
}
Voici ce que ce programme ache :
-->Avant CreateDomain()
-->Avant CreateInstance()
Chargement des metadonnees de Foo dans le domaine:Autre domaine.
Appel au constructeur de Foo dans le domaine:Autre domaine.
-->Apr`es CreateInstance() et avant UnWrap()
Chargement des metadonnees de Foo dans le domaine:WrapTest.exe
-->Apr`es UnWrap()
Ce programme illustre aussi le fait que le constructeur nest pas appel lors de la construction
dun clone.
Lorsque lon excute ce programme en faisant de Foo une classe MBR, lavant dernire ligne de
lachage napparat pas. Ceci souligne clairement que lobjet proxy nest pas du mme type que
lobjet distant.
794
En rgle gnrale, on isole chacune de ces entits dans un ou plusieurs assemblages. Cette rgle
nest absolument pas une contrainte. Physiquement vous pouvez mlanger toutes sortes dentits dans un assemblage. Cependant, utiliser cette rgle prsente des avantages que vous dcouvrirez dans les pages suivantes. En outre, nous expliquerons pourquoi et comment sparer les
mtadonnes de type des objets serveurs des implmentations de ceux-ci. La Figure 22-3 illustre
la rpartition des assemblages dans une architecture distribue classique.
Domaine dapplication du client
Assemblage contenant
le code du client
Assemblage(s) contenant
seulement les mtadonnes
des types des objets serveurs
Il peut tre surprenant quun hte puisse exposer des objets mais aussi des classes. Cela rsulte
du fait quun objet serveur peut tre soit activ par le serveur, soit activ par le client. Dans le
premier cas un client doit connatre un objet pour pouvoir lutiliser alors que dans le second
cas, le client doit connatre une classe pour pouvoir activer une instance de cette classe.
795
Interface.cs
namespace NommageInterface {
public interface IAdditionneur {
double Add(double d1, double d2) ;
}
}
Intressons nous maintenant lhte et aux classes des objets serveurs. Nous allons placer ces
deux types dentits dans le mme assemblage. Cependant nous verrons quil existe des htes
standard, et que pour les utiliser, il faut que les classes des objets serveurs soient isoles dans
des assemblages. Voici le code dun assemblage contenant la fois la classe dobjet serveur et un
hte propritaire. Notez bien lordonnancement des trois tches dans le code de lhte : cration
dun canal, activation dun objet serveur, maintient du processus.
Exemple 22-5 :
using System ;
using System.Runtime.Remoting ;
using System.Runtime.Remoting.Channels ;
Serveur.cs
796
Notez lutilisation du point terminal (end point en anglais) "ServiceAjout" associ lobjet activ. En concatnant les informations de protocole, de machine, de port et de point terminal
on obtient un URI qui localise compltement lobjet activ par le serveur. En loccurrence cet
URI est http://localhost:65100/ServiceAjout. Pour cette raison, on qualifie dobjet bien connu (wellknown object en anglais) un objet activ par le serveur et qui a un point terminal. Dans la suite
on utilisera lacronyme WKO (Well-Known Object) pour parler dun objet activ par le serveur.
Lorsque lon dcrira plus en dtail le mcanisme interne de .NET Remoting, on verra que le
serveur peut publier un objet quil active sans utiliser de point terminal.
Il ne nous reste plus qu coder lassemblage qui contient le code du client. Il est ncessaire
de crer un canal, puis de rcuprer un proxy transparent vers lobjet distant associ lURI
797
Exemple 22-6 :
using
using
using
using
System ;
System.Runtime.Remoting ;
System.Runtime.Remoting.Channels ;
System.Runtime.Remoting.Channels.Http ;
using NommageInterface ;
namespace NommageClient {
class Program {
static void Main() {
// Cree un canal HTTP puis enregistre
// ce canal dans le domaine dapplication courant.
// (la valeur 0 pour le num
ero de port c
ot
e client signifie
// que ce numero de port est choisi automatiquement
// par le CLR).
HttpChannel canal = new HttpChannel(0);
ChannelServices.RegisterChannel(canal, false);
` partir de
// Obtient un proxy transparent sur lobjet distant a
// son URI, puis transtype le proxy distant en IAdditionneur.
MarshalByRefObject objRef = (MarshalByRefObject)
RemotingServices.Connect(
typeof(IAdditionneur),
"http://localhost:65100/ServiceAjout");
IAdditionneur obj = objRef as IAdditionneur;
// Appel dune methode sur lobjet distant.
double d = obj.Add(3.0, 4.0);
Console.WriteLine("Valeur retourn
ee:" + d) ;
}
}
}
Nous disposons maintenant de trois fichiers sources C , qui vont chacun permettre de produire
un assemblage avec les trois instructions en ligne de commande suivantes :
>csc.exe /target:library Interface.cs
>csc.exe Serveur.cs /r:Interface.dll
>csc.exe Client.cs /r:Interface.dll
Nous aurions pu aussi utiliser Visual Studio pour placer ces trois projets dans un mme espace
de travail.
Il vous sut maintenant de lancer Serveur.exe puis Client.exe. Voici les achages de ces
programmes :
798
Achage de Serveur.exe
Pressez une touche pour stopper le serveur.
CAdditionneur ctor
CAdditionneur Add( 3 + 4 )
Achage de Client.exe
Valeur retournee:7
799
Le serveur attend un premier appel dun client avant dactiver lobjet dans les deux modes dappel, singleton et simple appel.
Nous prfrons parler de service dactivation dun objet par le serveur que de parler dactivation
dobjet par le serveur. En eet, le client na pas vraiment une rfrence vers un objet distant mais
bien une rfrence vers un service dactivation dobjets distants. Concrtement si lobjet utilis
par un client est dtruit, au prochain appel le client utilisera un autre objet, activ automatiquement par le serveur.
ObjServeur.cs
using System ;
namespace NommageObjServeur {
public interface IAdditionneur {
double Add(double d1, double d2) ;
}
// Implementation de la classe des objets serveurs.
public class CAdditionneur : MarshalByRefObject, IAdditionneur {
public CAdditionneur() {
Console.WriteLine("CAdditionneur ctor") ;
}
public double Add(double d1, double d2) {
Console.WriteLine("CAdditionneur Add( {0} + {1} )", d1, d2) ;
return d1 + d2 ;
}
}
}
Intressons-nous maintenant au code source de lassemblage contenant lhte. Cet hte est trs
similaire celui que nous avons utilis pour activer un objet par le serveur. La seule dirence
vient du fait que lon utilise la mthode statique RegisterActivatedServiceType() pour indiquer que la classe est activable partir dun autre domaine dapplication au lieu dutiliser
800
Exemple 22-8 :
using
using
using
using
System ;
System.Runtime.Remoting ;
System.Runtime.Remoting.Channels ;
System.Runtime.Remoting.Channels.Http ;
using NommageObjServeur ;
namespace NommageServer {
class Program {
static void Main() {
HttpChannel canal = new HttpChannel(65100) ;
ChannelServices.RegisterChannel(canal, false) ;
// Fait en sorte que ce domaine dapplication pr
esente
// la classe CAdditionneur, qui peut ainsi
etre instanci
ee
// par des clients distants.
RemotingConfiguration.RegisterActivatedServiceType(
typeof(CAdditionneur));
Console.WriteLine(
"Pressez une touche pour stopper le serveur.") ;
Console.Read() ;
}
}
}
Le client est lui aussi, trs similaire un client qui utilise un objet activ par le serveur. La seule
dirence vient du fait que lon utilise la mthode statique Activator.CreateInstance() pour
rcuprer un proxy transparent sur lobjet que lon active.
Exemple 22-9 :
using
using
using
using
using
using
System ;
System.Runtime.Remoting ;
System.Runtime.Remoting.Channels ;
System.Runtime.Remoting.Channels.Http ;
System.Runtime.Remoting.Activation ;
NommageObjServeur ;
namespace NommageClient {
class Program {
static void Main() {
HttpChannel canal = new HttpChannel(0) ;
ChannelServices.RegisterChannel(canal, false) ;
Client.cs
801
Exemple 22-10 :
...
static void Main() {
HttpChannel canal = new HttpChannel(0) ;
ChannelServices.RegisterChannel(canal, false) ;
RemotingConfiguration.RegisterActivatedClientType(
typeof(CAdditionneur),
"http://localhost:65100");
IAdditionneur obj = (IAdditionneur)new CAdditionneur();
802
Interface.cs
803
IAdditionneur FabriqueNouvelAdditionneur();
}
}
Ensuite il faut prvoir la classe CFabrique qui implmente IFabrique, et exposer un objet
CFabrique en mode dappel singleton (il vaut mieux ne pas utiliser le mode simple appel dans
ce cas) :
Exemple 22-12 :
Serveur.cs
...
public class CAdditionneur : MarshalByRefObject, IAdditionneur {
public CAdditionneur() {
Console.WriteLine("CAdditionneur ctor") ;
}
public double Add(double d1, double d2) {
Console.WriteLine("CAdditionneur Add( {0} + {1} )", d1, d2) ;
return d1 + d2 ;
}
}
public class CFabrique : MarshalByRefObject, IFabrique {
public IAdditionneur FabriqueNouvelAdditionneur() {
return (IAdditionneur)new CAdditionneur();
}
}
class Program {
static void Main() {
HttpChannel canal = new HttpChannel(65100) ;
ChannelServices.RegisterChannel(canal, false) ;
RemotingConfiguration.RegisterWellKnownServiceType(
typeof(CFabrique),
"ServiceFabrique",
WellKnownObjectMode.Singleton);
Console.WriteLine(
"Pressez une touche pour stopper le serveur.") ;
Console.Read() ;
}
}
...
Enfin, on peut activer une instance de CAdditionneur partir du client, sans connatre les classes
CAdditionneur et CFabrique. Cest exactement le but de cette manipulation :
Exemple 22-13 :
...
class Program {
static void Main() {
HttpChannel canal = new HttpChannel(0) ;
ChannelServices.RegisterChannel(canal, false) ;
Client.cs
804
Ce design pattern marche car la classe CAdditionneur et la classe CFabrique hritent toutes les
deux de la classe MarshalByRefObject.
Loutil Soapsuds.exe
Les sources et les excutables de cette section sont dans le rpertoire :
Chap22 .NET Remoting/CAO utilisation de soapsuds.exe
Loutil Soapsuds.exe, fourni avec le framework .NET est capable dextraire les mtadonnes de
la classe de lobjet serveur partir de lassemblage contenant cette classe. partir de ces mtadonnes, Soapsuds.exe peut fabriquer soit un fichier C prt tre compil, soit un assemblage
qui ne contient que ces mtadonnes. Voici la ligne de commande saisir pour fabriquer lassemblage ObjServeurPourClient.dll partir de lassemblage ObjServeur.dll.
>soapsuds /ia:ObjServeur /oa:ObjServeurPourClient.dll
Voici la ligne de commande saisir pour fabriquer le fichier source C ObjServeur.cs partir
de lassemblage ObjServeur.dll.
>soapsuds /ia:ObjServeur /gc
Voici un extrait du fichier source C ObjServeur.cs :
Exemple :
ObjServeur.cs
...
public class CAdditionneur :
System.Runtime.Remoting.Services.RemotingClientProxy, IAdditionneur {
// Constructor
public CAdditionneur() { }
public Object RemotingReference { get { return (_tp) ; } }
[SoapMethod(SoapAction =
@"http://schemas.microsoft.com/clr/nsassem/
NommageInterface.CAdditionneur/Interface#Add")]
public virtual Double Add(Double d1, Double d2) {
return ((CAdditionneur)_tp).Add(d1, d2) ;
}
}
...
805
On a donc bien une nouvelle classe CAdditionneur prsentant les mmes mthodes publiques que loriginale et drivant de la classe System.Runtime.Remoting.Services.RemotingClientProxy. Nous vous conseillons de consulter la documentation relative cette classe
dans les MSDN, car vous pouvez lutiliser pour avoir un mcanisme dauthentification du
client ou pour spcifier quel serveur proxy utiliser si le client est derrire un pare feu (firewall
en anglais). Vous pouvez nanmoins indiquer Soapsuds.exe que vous ne souhaitez pas que
les classes drivent de RemotingClientProxy en prcisant loption /nowp. Dans ce cas elles
driveront directement de MarshalByRefObject.
Loutil Soapsuds.exe permet aussi de fabriquer lassemblage contenant les mtadonnes des
classes serveurs partir dun serveur distant qui expose une instance de cette classe, via un canal
HTTP. Voici le code du serveur :
...
class Program {
static void Main() {
HttpChannel canal = new HttpChannel(65100) ;
ChannelServices.RegisterChannel(canal, false) ;
RemotingConfiguration.RegisterWellKnownServiceType(
typeof(CAdditionneur),
"MonRep/ServiceAjout.soap",
WellKnownObjectMode.SingleCall);
...
}
}
...
Voici la ligne de commande utiliser du ct client :
>Soapsuds /url:http://localhost:65100/MonRep/ServiceAjout.soap?wsdl
/oa:ObjServeurPourClient.dll
Notez lextension soap associe au point terminal et lutilisation du suxe ?wsdl sur lURL qui
permet dindiquer au serveur ce quon attend de lui, savoir obtenir les mtadonnes de type.
Prcisons enfin que loutil soapsuds.exe ne prend pas en compte les constructeurs avec arguments. Lutilisation du design pattern factory est donc plus adapte aux classes qui doivent absolument tre utilises avec des constructeurs avec arguments.
806
Dans un domaine dapplication, il nexiste pas de rfrences fortes vers un objet qui est accd
par une entit situe hors de son domaine dapplication. Pour empcher le ramasse-miettes
de collecter un tel objet, le CLR fait en sorte que chaque domaine dapplication contienne un
administrateur de baux (lease manager en anglais). Un administrateur de baux alloue une dure
de bail, au moment de lactivation de chaque objet MBR utilis dune manire distante. Ladministrateur de baux vrifie priodiquement le bail de chacun de ces objets. Ceux dont le bail nest
plus valide seront automatiquement dtruits lors de la prochaine collecte du ramasse-miettes.
Cependant, .NET Remoting rend ce mcanisme de bail assez souple. Concrtement, la dure du
bail dun objet peut tre prolonge de trois faons direntes :
807
Register(ISponsor) ;
Register(ISponsor,TimeSpan) ;
UnRegister(ISponsor) ;
808
Vous avez la possibilit de spcifier une dure de bail infinie en aectant la valeur TimeSpan.
Zero la proprit InitialLeaseTime dun bail.
Vous pouvez dfinir votre propre classe de sponsors. Nous rappelons quune telle classe doit
driver de MarshalByRefObject et implmenter linterface ISponsor. Lutilit dune classe de
sponsor apparat durant la spcification de larchitecture distribue, lorsque la vie dun objet
dpend logiquement dune condition. La condition la plus courante est que certains clients
soient toujours actifs. Il sut alors davoir un sponsor pour chaque client, qui prolonge le bail
de lobjet tant que le client associ au sponsor le dsire. Mfiez-vous cependant de ce type de
mcanisme, dit de ping. La technologie DCOM utilise ce type de mcanisme. Lexprience a
prouv que son cot nest pas acceptable ds que le nombre de clients dpasse un certain seuil
fonction de larchitecture.
ObjServeur.cs
using System ;
namespace NommageObjServeur {
public interface IAdditionneur {
double Add(double d1, double d2) ;
}
public class CAdditionneur : MarshalByRefObject, IAdditionneur {
public CAdditionneur() {
Console.WriteLine("CAdditionneur ctor") ;
}
public double Add(double d1, double d2) {
Console.WriteLine("CAdditionneur Add( {0} + {1} )", d1, d2) ;
return d1 + d2 ;
}
}
public interface IMultiplicateur {
double Mult(double d1, double d2) ;
}
public class CMultiplicateur : MarshalByRefObject, IMultiplicateur {
public CMultiplicateur() {
809
Console.WriteLine("CMultiplicateur ctor") ;
}
public double Mult(double d1, double d2) {
Console.WriteLine("CMultiplicateur Mult( {0} + {1} )", d1, d2) ;
return d1 * d2 ;
}
}
public interface IDiviseur {
double Div(double d1, double d2) ;
}
public class CDiviseur : MarshalByRefObject, IDiviseur {
public CDiviseur() {
Console.WriteLine("CDiviseur ctor") ;
}
public double Div(double d1, double d2) {
Console.WriteLine("CDiviseur Div( {0} + {1} )", d1, d2) ;
return d1 + d2 ;
}
}
}
Hote.config
<configuration>
<system.runtime.remoting>
<application name = "Serveur">
<service>
<wellknown type="NommageObjServeur.CAdditionneur,ObjServeur"
mode ="Singleton" objectUri="Service1.rem" />
<wellknown type="NommageObjServeur.CMultiplicateur,ObjServeur"
mode ="SingleCall" objectUri="Service2.rem" />
<activated type="NommageObjServeur.CDiviseur,ObjServeur" />
</service>
<channels>
<channel port="65100" ref="http" />
</channels>
810
Serveur.cs
...
class Program {
static void Main() {
RemotingConfiguration.Configure("Hote.config", false) ;
Console.WriteLine("Pressez une touche pour stopper le serveur.") ;
Console.Read() ;
}
}
...
Configuration du client
Le document XML suivant permet dutiliser partir dun client les deux services WKO et la
classe expose par le serveur prcdent :
Exemple 22-18 :
Client.config
<configuration>
<system.runtime.remoting>
<application name = "Client">
<client>
<wellknown
type="NommageObjServeur.CAdditionneur,ObjServeur"
url="http://localhost:65100/Service1.rem" />
<wellknown
type="NommageObjServeur.CMultiplicateur,ObjServeur"
url="http://localhost:65100/Service2.rem" />
</client>
<client url="http://localhost:65100/">
<activated type ="NommageObjServeur.CDiviseur,ObjServeur"/>
</client>
</application>
</system.runtime.remoting>
</configuration>
Les URLs spcifies commencent par le mode daccs http:// mais elles auraient pu commencer
par un des modes daccs tcp:// ou ipc:// si notre canal rcepteur tait de type TCP ou IPC.
Dans le cas IPC, il aurait fallu remplacer la paire nom de la machine hte/port (en loccurrence
localhost:65100) par le nom du pipe nomm sous jacent. Ce nom est fourni par lattribut
portName dans la dclaration du canal dans le fichier de configuration du serveur. Pour charger
les paramtres de configuration de ce fichier XML dans le domaine dapplication du client, on
utilise galement la mthode statique void RemotingConfiguration.Configure(string) :
811
Exemple 22-19 :
...
using NommageObjServeur ;
...
class Program {
static void Main() {
RemotingConfiguration.Configure("Client.config", false);
CAdditionneur objA = new CAdditionneur() ;
double dA = objA.Add(3.0, 4.0) ;
CMultiplicateur objM = new CMultiplicateur() ;
double dM = objM.Mult(3.0, 4.0) ;
CDiviseur objD = new CDiviseur() ;
double dD = objD.Div(3.0, 4.0) ;
}
}
...
Les appels aux constructeurs des objets WKO (objA et objM) ne sont l que pour rcuprer une
rfrence (en fait un proxy transparent) vers le type adquat. Concrtement ils ne provoquent
aucun accs au rseau.
partir dun fichier de configuration serveur, vous pouvez configurer les paramtres de ladministrateur de baux. Pour plus dinformations ce sujet nous vous conseillons de consulter
larticle <lifetime> Element des MSDN.
A priori, on ne peut pas utiliser dinterfaces puisquon est oblig dappeler le constructeur. Le
design pattern factory ne peut sappliquer puisque mme dans le cas de lactivation du serveur, le
client ne se contente pas dune interface. Il faut alors utiliser loutil Soapsuds.exe pour viter de
fournir les assemblages contenant les implmentations des objets serveurs aux clients. Cependant nous allons montrer une astuce permettant lutilisation dinterfaces en mode WKO, et
donc, en mode CAO aussi en utilisant le design pattern factory.
Interface.cs
812
Ensuite, toute lastuce repose sur lutilisation dune table, qui associe les services WKO configurs par le serveur des interfaces configures dans le client. Voici donc le code du client, avec la
gestion de la table dicoTypes :
Exemple 22-21 :
using
using
using
using
using
using
Client.cs
System ;
System.Runtime.Remoting ;
System.Runtime.Remoting.Channels ;
System.Runtime.Remoting.Channels.Http ;
System.Runtime.Remoting.Activation ;
System.Collections ;
using NommageInterface ;
namespace NommageClient {
class NotreActivateur {
private static bool bInit ;
// Table dassociation : interfaces/services distants WKO.
private static IDictionary dicoTypes ;
public static Object GetObject(Type type) {
if (!bInit)
InitdicoTypes() ;
WellKnownClientTypeEntry entry = (WellKnownClientTypeEntry)
dicoTypes[type] ;
return Activator.GetObject(entry.ObjectType, entry.ObjectUrl) ;
}
private static void InitdicoTypes() {
bInit = true ;
dicoTypes = new Hashtable() ;
foreach ( WellKnownClientTypeEntry entry in
RemotingConfiguration.GetRegisteredWellKnownClientTypes() )
dicoTypes.Add(entry.ObjectType, entry) ;
}
}
class Program {
static void Main() {
813
RemotingConfiguration.Configure("Client.config", false) ;
IAdditionneur objA = (IAdditionneur)
NotreActivateur.GetObject(typeof(IAdditionneur));
double dA = objA.Add(3.0, 4.0) ;
IMultiplicateur objM = (IMultiplicateur)
NotreActivateur.GetObject(typeof(IMultiplicateur));
double dM = objM.Mult(3.0, 4.0) ;
// Utilisation du design pattern factory pour un objet CAO.
IFabrique objFabrique = (IFabrique)
NotreActivateur.GetObject(typeof(IFabrique));
IDiviseur objD = objFabrique.FabriqueNouvelDiviseur();
double dD = objD.Div(3.0, 4.0) ;
}
}
}
Le fichier de configuration du client ressemble alors :
Exemple 22-22 :
Client.config
<configuration>
<system.runtime.remoting>
<application name = "Client">
<client>
<wellknown type="NommageInterface.IAdditionneur,Interface"
url="http://localhost:65100/Service1.rem" />
<wellknown type="NommageInterface.IMultiplicateur,Interface"
url="http://localhost:65100/Service2.rem" />
<wellknown type="NommageInterface.IFabrique,Interface"
url="http://localhost:65100/Service3.rem" />
</client>
</application>
</system.runtime.remoting>
</configuration>
Comprenez bien que le contenu de la table dassociations interfaces/services WKO distants est
crit explicitement dans ce fichier de configuration. Ce contenu est rcupr dans le code du
client par la mthode RemotingConfiguration.GetRegisteredWellKnownClientTypes().
Le fichier de configuration du serveur prsente trois services WKO. Nous rappelons que le troisime service sert utiliser des objets CAO par lintermdiaire du design pattern factory :
Exemple 22-23 :
<configuration>
<system.runtime.remoting>
<application name = "Serveur">
<service>
Hote.config
814
Serveur.cs
System ;
System.Runtime.Remoting ;
System.Runtime.Remoting.Channels ;
System.Runtime.Remoting.Channels.Http ;
using NommageInterface ;
namespace NommageServer{
public class CAdditionneur : MarshalByRefObject, IAdditionneur {
...
public class CMultiplicateur : MarshalByRefObject, IMultiplicateur {
...
public class CDiviseur : MarshalByRefObject, IDiviseur {
...
public class CFabrique : MarshalByRefObject, IFabrique {
public IDiviseur FabriqueNouvelDiviseur() {
return new CDiviseur() ;
}
}
class Program {
static void Main() {
HttpChannel canal = new HttpChannel(65100) ;
ChannelServices.RegisterChannel(canal, false) ;
RemotingConfiguration.RegisterWellKnownServiceType(
typeof(CFabrique),
"ServiceFabrique",
WellKnownObjectMode.Singleton) ;
Console.WriteLine("Pressez une touche pour stopper le serveur.") ;
Console.Read() ;
}
815
}
}
Services Windows
Lavantage principal dun service Windows par rapport une application sexcutant en mode
console, rside dans le fait quil nest pas ncessaire quun utilisateur soit logu sur la machine
pour excuter lapplication hberge par le service Windows. Un autre avantage est que la manipulation dun service en excution peut se faire directement partir dune interface graphique
qui prvoit les commandes dmarrer / arrter / suspendre / reprendre / redmarrer . Un autre
argument en faveur de cette pratique est le fait quun service peut tre excut automatiquement aprs un reboot de la machine. Nous ne dtaillons pas dans cet ouvrage la conception dun
service. Sachez quavec les classes contenues dans lespace de noms System.ServiceProcess,
cette tche a t grandement simplifie par rapport lutilisation des fonctions spcialises
win32. Tout ceci est dcrit dans les MSDN larticle System.ServiceProcess Namespace.
IIS
Lutilisation de IIS constitue une autre alternative au mode console et lutilisation dun service Windows, pour hberger vos objets serveurs. Les avantages principaux de ce choix sont les
suivants :
Vous avez accs la gestion de la scurit de IIS. Notamment, si votre serveur IIS supporte
les certificats SSL, vous pouvez avoir accs au service dencryption de vos donnes. Vous
pouvez aussi avoir accs au mcanisme dauthentification de Windows.
Les problmes de versioning sont aussi pris en charge par IIS. Celui-ci dtecte quand vous
installez une nouvelle version et soccupe entirement de la transition vers cette nouvelle
version. Installer une nouvelle version signifie remplacer un assemblage par un autre, et
remplacer le fichier de configuration par un autre, sans avoir stopper IIS. IIS ne bloque
816
Cependant toutes ces options ont un cot, et le principal problme de IIS est quil a un impact
non ngligeable sur les performances. Sachez aussi que vous ne pouvez pas utiliser de canaux
TCP ou IPC avec IIS. Pour utiliser IIS deux tapes sont ncessaires :
Sous mmc (la console gnrique dadministration de Windows), avec le snap-in IIS, crer un
nouveau rpertoire virtuel IIS. Nous prcisons quun rpertoire virtuel IIS permet de faire en
sorte quun rpertoire de la machine soit accessible partir dune URL. Dans ce rpertoire,
vous devez placer votre fichier de configuration. Ce fichier ne peut pas prendre nimporte
quel nom. Son nom doit tre web.config.
Crer un sous rpertoire bin dans le rpertoire de la premire tape. Placez les assemblages
contenant les classes de vos objets serveur dans cet assemblage. Une autre option consiste
placer ces assemblages dans le rpertoire GAC. Ils doivent alors avoir un nom fort.
Lorsque vous utilisez ce type de dploiement, llment <application> du fichier de configuration ne doit pas avoir dattribut Name. De plus vous ne devez pas spcifier de port dans le fichier
de configuration, pour ne pas interfrer avec la gestion des numros de port dIIS.
Client.cs
Proxys et messages
817
properties.Add("secure", true);
TcpChannel canal = new TcpChannel(properties,null,null) ;
ChannelServices.RegisterChannel(canal, true) ;
...
Vous pouvez galement positionner du cot serveur lattribut tokenImpersonationLevel dun
canal TCP la valeur Impersonation si vous souhaitez que la requte du cliente sexcute du
cot serveur avec le compte de lutilisateur client (autrement dit la requte est impersonifie).
Hberger le serveur avec IIS et utiliser le protocole SSL (donc HTTPS) au niveau dIIS.
Crer votre propre canal scuris HTTP.
Proxys et messages
Le but de cette section est danalyser de prs les proxys transparents et de prsenter les notions
de proxy rel et dintercepteur de messages. Rsumons tout ce que nous avons dj expos
propos des proxys transparents :
Un proxy transparent est une instance dune classe interne lassemblage mscorlib.dll.
Cette classe ne nous est donc pas accessible.
Un proxy transparent est cr automatiquement par le CLR pour rfrencer un objet distant dont la classe drive de MarshalByRefObjectet.
Dans un assemblage, la gestion interne dune rfrence vers un objet varie selon que lassemblage est charg dans le mme domaine dapplication que lobjet ou non. Dans le cas o
lassemblage et lobjet sont distants, on a une rfrence vers un proxy transparent. Dans le
cas o ils sont dans le mme domaine dapplication, on a une rfrence directe vers lobjet.
On a vu que la mthode statique bool RemotingServices. IsTransparentProxy(object)
permet de savoir si une rfrence vers un objet est un proxy transparent ou est une rfrence
directe.
Les mtadonnes du type par lequel on manipule lobjet distant doivent tre charges dans
le domaine dapplication, lorsque le CLR construit le proxy transparent vers cet objet distant. On a vu plusieurs faons dobtenir ces mtadonnes.
818
Excution de f
argVal
addr
this
result
int argRef
Client
Proxy transparent
Objet serveur
Message dappel
Constructeur de pile
Message de retour
Proxys et messages
819
using System.Runtime.Remoting.Messaging ;
using System.Collections ;
public interface IMessage {
IDictionary Properties { get;}
}
public interface IMethodMessage : IMessage {
object
GetArg(int index) ;
string
GetArgName(int index) ;
int
ArgCount { get;}
object[]
Args { get;}
bool
HasVarArgs { get;}
string
MethodName { get;}
object
MethodSignature { get;}
string
TypeName { get;}
string
Uri { get;}
LogicalCallContext
LogicalCallContext { get;}
MethodBase
MethodBase {get;}
}
Linterface IMethodCallMessage permet essentiellement de parcourir les donnes contenues
dans un message fabriqu par un proxy transparent. Linterface IMethodReturnMes\-sage
permet essentiellement de parcourir les donnes contenues dans un message fabriqu par
un constructeur de pile.
820
Proxys et messages
821
Serveur.cs
Exemple 22-26 :
...
static void Main() {
HttpChannel canal = new HttpChannel(65100) ;
ChannelServices.RegisterChannel(canal, false) ;
CAdditionneur obj = new CAdditionneur() ;
ObjRef objRef = RemotingServices.Marshal(obj) ;
FileStream fStream = new
FileStream("Additionneur.txt", FileMode.Create);
SoapFormatter soapFormatter = new SoapFormatter();
soapFormatter.Serialize(fStream, objRef);
fStream.Close();
Exemple 22-27 :
using System.IO ;
using System.Runtime.Serialization.Formatters.Soap ;
...
static void Main() {
HttpChannel canal = new HttpChannel(0) ;
ChannelServices.RegisterChannel(canal, false) ;
FileStream fStream = new FileStream("Additionneur.txt",
FileMode.Open);
SoapFormatter soapFormatter = new SoapFormatter();
IAdditionneur obj =
soapFormatter.Deserialize(fStream) as IAdditionneur;
double d = obj.Add(3.0, 4.0) ;
}
...
Cette application nous permet de vrifier que .NET Remoting assigne une identit propre
chaque instance dune classe drivant de MarshalByRefObject. Cest--dire que chaque objet a
un URI unique au monde, bas sur un GUID (Global Unique Identity). Un tel URI ressemble
ceci :
/6b03659f_0164_43e2_99cf_f36eda31adae/367459709_1.rem
822
Lorsquun objet publi est dtruit, le fichier qui lui servait de rfrence nest plus daucune utilit. Cet URI nest communiqu au client que si lobjet est activ par le client ou publi par le
serveur. Autrement dit, cet URI nest pas communiqu au client dans le cas dun objet WKO.
La technique de publication dun objet est donc une solution intermdiaire entre la technique
de service dactivation dun objet distant par le serveur et la technique dactivation dun objet
distant par le client. Lobjet est eectivement activ par le serveur mais le client tient compte
de lidentit de lobjet. Nous rcapitulons les dirences de ces quatre modes dactivation dun
objet la fin de ce chapitre.
Le fichier de publication dun objet est constitu dune trentaine de lignes au format XML, fastidieuses lire. En voici quelques extraits pertinents :
Exemple :
Additonneur.txt
...
<uri id="ref-2">/6b03659f_0164_43e2_99cf_f36eda31adae/367459709_1.rem</uri>
...
<serverType id="ref-5">NommageServer.CAdditionneur, Server,
Version=1.0.1140.28752, Culture=neutral, PublicKeyToken=null</serverType>
...
<item id="ref-8">NommageInterface.IAdditionneur, Interface,
Version=1.0.1138.27103, Culture=neutral, PublicKeyToken=null</item>
...
<a3:CrossAppDomainData id="ref-9" xmlns:a3=
"http://schemas.microsoft.com/clr/ns/System.Runtime.Remoting.Channels">
<_ContextID>1362648</_ContextID>
<_DomainID>1</_DomainID>
<_processGuid id="ref-11">faf88595_9b2e_4f23_99ee_1d0046915a98</_processGuid>
</a3:CrossAppDomainData>
...
<item id="ref-13">http://213.36.58.1:65100</item>
...
Proxys et messages
823
IMessageSink
replySink) ;
}
Grce la proprit NextSink, les intercepteurs de messages peuvent tre chans. Pour cette
raison, on les appelle parfois des chaneurs de messages. Comme vous vous en doutez, le traitement dun message reprsentant un appel synchrone se fait dans la mthode SyncProcessMessage() et le traitement dun message reprsentant un appel asynchrone se fait dans la mthode
AsyncProcessMessage(). Voici une implmentation o nous indiquons o vous pouvez placer
le code de vos traitements :
Il faut toujours marquer une classe dintercepteurs de messages avec lattribut .NET
Serializable.
[Serializable]
public class MonMsgSink : IMessageSink{
private IMessageSink m_NextSink ;
public IMessageSink NextSink{ get { return m_NextSink;} }
public MonMsgSink(IMessageSink NextSink){m_NextSink = NextSink;}
IMessage SyncProcessMessage(IMessage MsgIn){
// Ici vous pouvez faire un traitement sur MsgIn.
IMessage MsgOut = m_NextSink.SyncProcessMessage(MsgIn) ;
// Ici vous pouvez faire un traitement sur MsgOut.
return MsgOut ;
}
IMessageCtrl AsyncProcessMessage(
IMessage MsgIn,
IMessageSink replySink){
// Ici vous pouvez faire un traitement sur MsgIn.
// Vous pouvez aussi ajouter un intercepteur de msgs pour la
// chaine dintercepteurs de msgs qui sera utilis
ee pour
// traiter le msg representant le retour de lappel.
IMessageCtrl MsgCtrl =
m_NextSink.AsyncProcessMessage(MsgIn,replySink) ;
// Ici vous pouvez indiquer au CLR de ne plus attendre de retour
// pour cet appel asynchrone apr
es une dur
ee de
// 1000 millisecondes en
ecrivant : MsgCtrl.Cancel(1000);
return MsgCtrl ;
}
}
Remarquez que la mthode SyncProcessMessage() peut traiter le message entrant et le message
sortant alors que la mthode AsyncProcessMessage() ne traite que le message entrant. En revanche, la mthode AsyncProcessMessage() a la possibilit de participer la construction de la
chane dintercepteurs de messages qui sera utilise pour traiter le message contenant les informations retournes par lappel de la mthode. Naturellement, les traitements senchaneront
dans lordre inverse dans lesquels ils ont t chans.
Vous savez maintenant comment concevoir des intercepteurs de messages. Nous allons vous
expliquer, dans la suite de ce chapitre, comment injecter vos propres intercepteurs de message
824
dans la chane dun appel de mthode, et surtout, quels types de bnfices vous pouvez tirer de
cette pratique. Auparavant, nous prsentons une implmentation dun proxy rel propritaire.
Vous allez vous apercevoir que nous allons pour cela avoir accs au premier intercepteur de
messages de la chane dun appel synchrone de mthode.
Proxys et messages
825
avoir dautres intercepteurs de messages intercals entre le proxy rel et le canal. Dans la mthode Invoke() on ne fait quacher les arguments en entre de la mthode, puis la valeur de
retour. Notez que lon place lURI du service dactivation dobjets par le serveur, dans le message
dentre. Cette tape pourrait tre trs facilement modifie pour faire de la rpartition de charge
entre plusieurs serveurs.
Exemple 22-28 :
Client.cs
using System ;
using System.Runtime.Remoting ;
using System.Runtime.Remoting.Channels ;
using System.Runtime.Remoting.Channels.Http ;
using System.Runtime.Remoting.Proxies ;
using System.Runtime.Remoting.Messaging ;
using System.Collections ;
using NommageInterface ;
// Il faut utiliser soapsuds.exe
// pour que le client connaisse la classe CAdditionneur.
using NommageServer;
public class MonProxyReel : RealProxy {
// URI du service dactivation dobjet par le serveur.
String m_Uri ;
// Correspond au premier intercepteur de msg du canal HttpSender.
IMessageSink m_MsgSink ;
public MonProxyReel(Type type, String uri,
IChannelSender CanalEnvoi) : base(type) {
m_Uri = uri ;
string unused ;
// Obtient du canal, une chaine dintercepteurs de messages.
m_MsgSink = CanalEnvoi.CreateMessageSink(m_Uri, null, out unused);
}
// Methode appelee par le CLR avant chaque appel du client.
public override IMessage Invoke(IMessage msgIn) {
// Place lURI de lobjet distant dans MsgIn.
IDictionary d = msgIn.Properties;
d["__Uri"] = m_Uri;
// Affiche les arguments en entr
ee contenus dans MsgIn.
IMethodCallMessage msgAppel = (IMethodCallMessage)msgIn ;
Console.Write("MonProxyReel : Avant lappel de:{0}(",
msgAppel.MethodName) ;
for (int i = 0 ; i < msgAppel.InArgCount ; i++)
Console.Write(" {0}={1} ",
msgAppel.GetArgName(i), msgAppel.GetArg(i)) ;
Console.WriteLine(")") ;
// Realise lappel distant.
826
Proxys et messages
827
Pour cela, il faut dabord que la classe de nos instances, la classe CAdditionneur en loccurrence,
drive de la classe System.ContextBoundObject. Nous donnons la signification prcise de la
classe ContextBoundObject un peu plus loin dans ce chapitre. Ensuite il faut dfinir une classe
dattribut .NET qui drive de la classe System.Runtime.Remoting.Proxies.ProxyAttribute.
Dans cette classe, vous rcrivez la mthode virtuelle MarshalByRefObject ProxyAttribute.CreateInstance(Type t) de faon quelle intercale un proxy rel entre lobjet cr et sa
rfrence. Il faut marquer la classe CAdditionneur avec cet attribut .NET. Voici un programme
illustrant tout ceci :
Exemple 22-29 :
using
using
using
using
using
using
System ;
System.Runtime.Remoting ;
System.Runtime.Remoting.Services ;
System.Runtime.Remoting.Messaging ;
System.Runtime.Remoting.Proxies ;
System.Runtime.Remoting.Activation ;
828
if (m_bAffiche)
Console.WriteLine("MonProxyReel : Avant lappel de:{0}",
appel.MethodName) ;
msgOut = RemotingServices.ExecuteMessage(m_ObjCible, appel) ;
if (m_bAffiche)
Console.WriteLine("MonProxyReel : Apr
es lappel de:{0}",
appel.MethodName) ;
}
return msgOut ;
}
}
[AttributeUsage(AttributeTargets.Class)]
public class MonProxyAttribute : ProxyAttribute {
bool m_bAffiche ;
public MonProxyAttribute(bool bAffiche) {
m_bAffiche = bAffiche ;
}
public override MarshalByRefObject CreateInstance(Type T) {
// Creation dune nouvelle instance.
MarshalByRefObject objCible = base.CreateInstance(T);
// Intercale un proxy reel entre la nouvelle instance
// et le client.
RealProxy realProxy = new MonProxyReel(objCible, T, m_bAffiche);
return (MarshalByRefObject)realProxy.GetTransparentProxy();
}
}
// Le param`etre true indique que lon souhaite que le proxy r
eel
// signale sa presence en affichant des messages sur la console.
[MonProxyAttribute(true)]
public class CAdditionneur : ContextBoundObject {
public int Add(int a, int b) { return a + b ; }
}
public class Program {
static void Main() {
CAdditionneur obj = new CAdditionneur() ;
obj.Add(5, 6) ;
}
}
Proxys et messages
829
830
Canaux (channels)
Introduction
Les canaux sont les entits qui transmettent les messages reprsentant les appels de mthodes
inter domaines dapplication. De ce fait, il existe au moins un canal dans le domaine dapplication du client et un canal dans le domaine dapplication du serveur. Cependant, un domaine
dapplication peut contenir plusieurs canaux, et une implmentation dun canal peut tre utilise par un client ou par un serveur.
Un canal est une instance dune classe implmentant linterface System.Runtime.Remoting.
Channels.IChannel. Un canal qui peut tre utilis par un client est appel canal metteur. Par
dfinition, un canal metteur implmente linterface System.Runtime.Remoting.Channels.
IChannelSender. Un canal qui peut tre utilis par un serveur est appel canal rcepteur. Par
dfinition, un canal rcepteur implmente linterface System.Runtime.Remoting.Channels.
IChannelReceiver.
Le framework .NET expose trois implmentations pour les canaux : la classe System.Runtime.
Remoting.Channels.Http.HttpChannel, la classe System.Runtime.Remoting.Channels.Tcp.
TcpChannel et la classe System.Runtime.Remoting.Channels.Ipc.Ipc\-Chan\-nel. Chacune
de ces implmentations peut la fois servir de canal metteur et de canal rcepteur.
Les protocoles HTTP et TCP sont bien videmment supports respectivement par les classes
HttpChannel et TcpChannel. La classe IpcChannel supporte quant elle la notion de pipe nomm.
Un pipe nomm est un objet Windows permettant deux processus Windows hbergs sur la
mme machine de communiquer. Vous pouvez aussi faire communiquer deux processus Windows hbergs sur la mme machine avec un protocole rseau tel que HTTP ou TCP. Lavantage
des pipes nomms rside dans le fait quils sont implments au niveau du systme dexploitation et quils nutilisent donc pas dAPI rseau. En consquence ils sont plus performants lorsquil sagit de faire communiquer deux processus hbergs sur la mme machine. Lacronyme
IPC signifie Inter Processus Communication. La documentation anglo saxonne parle aussi parfois
de same-box communication.
Pour enregistrer un canal dans un domaine dapplication, vous pouvez soit utiliser la mthode
statique ChannelServices.RegisterChannel() soit charger un fichier de configuration .NET
Remoting avec la mthode statique RemotingConfiguration.Configure(). Ces deux techniques
ont dj t prsentes dans les pages prcdentes.
Chaque canal a un nom. Deux canaux dans le mme domaine dapplication ne peuvent pas
avoir le mme nom. Par dfaut le nom dun canal instance de HttpChannel est "http", le nom
dun canal instance de TcpChannel est "tcp" et le nom dun canal instance de IpcChannel est
"ipc". Voici comment aecter un nom particulier un canal :
...
static void Main() {
IDictionary prop = new Hashtable() ;
prop["name"] = "tcp2";
prop["port"] = "65101" ;
ChannelServices.RegisterChannel(new TcpChannel(prop, null, null)) ;
...
Chaque canal de type HTTP ou TCP a besoin dun numro de port de la machine. Un numro
de port ne peut tre utilis par plusieurs canaux existant sur une mme machine. Pour viter
Canaux (channels)
831
toute collision dans le choix dun numro de port, vous pouvez aecter le numro de port 0
durant la cration dun tel canal. Le CLR fera en sorte de trouver un numro de port libre
et daecter ce numro au nouveau canal. Comme on peut sen douter, les implmentations
Microsoft des canaux HTTP et TCP se basent sur une socket en interne.
Lorsque deux domaines dapplications se trouvent dans le mme processus, il serait dommage
dutiliser ces mcanismes lourds prvus pour la communication interprocessus. Pour cette raison, lassemblage mscorlib.dll contient la classe interne CrossAppDomain qui est prvue spcialement pour le cas o le domaine dapplication contenant le client et le domaine dapplication contenant les objets serveurs sont dans le mme processus. Cette classe est utilise automatiquement par le CLR. Vous navez donc rien faire de particulier pour bnficier de cette
optimisation.
832
une classe partir du serveur, ne prend de canal rcepteur en argument. La raison est simple :
chaque canal rcepteur dun domaine dapplication peut recevoir des appels pour chaque objet
de ce domaine utilisable dune manire distante.
Lappel la mthode RemotingConfiguration.RegisterWellKnownServiceType() cre une association interne et globale au domaine dapplication entre lURI du service WKO et le service
WKO lui-mme. Ce service est paramtr par le mode dappel et une classe. Lappel la mthode RemotingConfiguration.RegisterActivatedServiceType() enregistre dans le domaine
dapplication le fait quune certaine classe drivant de la classe MarshalByRefObject peut tre
instancie par un client distant. Nous rappelons que chaque fois quune instance de la classe
ObjRef est cre pour rfrencer un objet du domaine dapplication, un URI unique bas sur
un GUID est cr pour cet objet. Une association interne et globale au domaine dapplication,
entre cet URI et lobjet rel, est alors cre.
Lorsquun canal rcepteur reoit un message reprsentant un appel, trois cas peuvent se prsenter :
Lappel se fait sur un service WKO. Dans ce cas un objet est activ si le mode dappel est
simple appel. Si le mode dappel est singleton, soit un objet existe dj pour lURI spcifie,
soit un nouvel objet est activ.
Lappel se fait sur un objet du domaine dapplication, associ avec un URI fourni. Si un objet
est eectivement associ avec cet URI, lappel est eectu sur cet objet, sinon un message
est retourn au client indiquant que lobjet nexiste pas.
Lappel est un appel un constructeur sur une classe CAO. Dans ce cas, un nouvel objet est
cr. Cet objet est associ avec un nouvel URI bas sur un nouveau GUID. Ce nouvel URI
est retourn au client laide dune instance de la classe ObjRef.
Les intercepteurs de messages qui srialisent/dsrialisent le message, pour quil puisse transiter sur le rseau. On les appelle formateurs (formater sink en anglais). Il y a toujours un
formateur qui srialise dans une chane dintercepteurs de messages ct client, et un formateur qui dsrialise dans une chane dintercepteurs de messages ct serveur. Le framework .NET fourni deux types de formateurs : les formateurs qui srialisent/dsrialisent un
message dans un stream binaire et les formateurs qui srialisent/dsrialisent un message
dans un document SOAP. Vous avez aussi la possibilit de crer vos propres formateurs.
Canaux (channels)
833
Domaine dapplication ct client
Proxy transparent vers OBJ1
Msg Sink
Canal metteur
Msg Sink
Msg Sink
Msg Sink
transport
Msg Sink
Msg Sink
Canal
rcepteur
Msg
Sink
Constructeur de pile
OBJ1
OBJ2
834
rcepteur) est une instance dune classe qui implmente linterface System.Runtime.Remoting.
Channels.IClientChannelSinkProvider (respectivement IServerChannelSinkProvider).
Rappelons que dans un canal metteur, cette production est sollicite pour chaque proxy rel
alors que dans un canal rcepteur, cette production est sollicite une seule fois, la cration du
canal.
Chacune de ces deux interfaces prsente naturellement une mthode CreateSink(). Cette mthode est appele par limplmentation dun canal pour obtenir un nouvel intercepteur de messages du fournisseur :
IServerChannelSink IServerChannelSinkProvider.CreateSink(
IChannelReceiver canal) ;
IClientChannelSink IClientChannelSinkProvider.CreateSink(
IChannelSender
canal,
String
url,
Object
remoteChannelData) ;
Pour que lon puisse crer une chane de tels fournisseurs, chacune de ces deux interfaces prsente la proprit Next, du type de linterface concerne. Vous pouvez donc facilement crer une
telle chane dans votre code et la communiquer un canal lors de sa construction, en utilisant les
constructeurs des classes de canaux prvus cet eet. Nanmoins on prfre toujours configurer
la chane de fournisseurs dun canal par lintermdiaire du fichier de configuration Remoting
de lapplication. Ceci est illustr dans la section suivante.
Une classe nomme CustomClientSink, dont les instances sont des intercepteurs de messages. Les instances de cette classe doivent tre places dans le canal metteur, aprs le formateur.
Une classe nomme CustomServerSink, dont les instances sont des intercepteurs de messages. Les instances de cette classe doivent tre places dans le canal rcepteur, avant le formateur.
Une classe nomme CustomClientSinkProvider, dont les instances sont des fournisseurs
dinstances de CustomClientSink. Les instances de cette classe doivent tre chanes aprs
le fournisseur de formateurs dans le canal metteur.
Une classe nomme CustomServerSinkProvider, dont les instances sont des fournisseurs
dinstances de CustomServerSink. Les instances de cette classe doivent tre chanes avant
le fournisseur de formateurs dans le canal rcepteur.
Nous dveloppons aussi une classe Helper, qui fournit la mthode statique GetStreamLength()
qui retourne la taille dun stream. Cette mthode est capable de retourner cette taille, que le
Canaux (channels)
835
stream ait la possibilit daccs alatoire (seek) ou non. Voici le code du nouvel assemblage qui
contient toutes ces classes :
Exemple 22-30 :
using
using
using
using
using
CustomChannelSink.cs
System ;
System.Runtime.Remoting.Channels ;
System.Runtime.Remoting.Messaging ;
System.Collections ;
System.IO ;
namespace CustomChannelSink {
internal class Helper {
static public Stream GetStreamLength(Stream inStream,
out long length) {
// Les acc`es aleatoires sont-ils autoris
es sur InStream ?
if (inStream.CanSeek) {
length = inStream.Length ;
return inStream ;
}
// Les acc`es aleatoires ne sont pas autoris
es sur InStream.
// Copie de InStream dans OutStream pour obtenir la taille.
Stream outStream = new MemoryStream() ;
byte[] tampon = new Byte[1024] ;
int tmp, nBytesRead = 0 ;
while ((tmp = inStream.Read(tampon, 0, 1024)) > 0) {
outStream.Write(tampon, nBytesRead, tmp) ;
nBytesRead += tmp ;
}
outStream.Seek(0, SeekOrigin.Begin) ;
length = nBytesRead ;
return outStream ;
}
}
//
// Intercepteur proprietaire de messages, pour un canal
emetteur.
//
public class CustomClientSink : BaseChannelSinkWithProperties,
IClientChannelSink {
private IClientChannelSink m_NextSink ;
public CustomClientSink(IClientChannelSink nextSink) {
m_NextSink = nextSink ; }
public IClientChannelSink NextChannelSink {
get { return m_NextSink ; }
}
public void AsyncProcessRequest(
836
Canaux (channels)
Console.WriteLine(
"CustomClientSink:Sync, taille du stream de retour {0}",
length);
}
}
//
// Intercepteur proprietaire de messages, pour un canal r
ecepteur.
//
public class CustomServerSink : BaseChannelSinkWithProperties,
IServerChannelSink {
private IServerChannelSink m_NextSink ;
public CustomServerSink(IServerChannelSink nextSink) {
m_NextSink = nextSink ;
}
public IServerChannelSink NextChannelSink {
get { return m_NextSink ; }
}
public void AsyncProcessResponse(
IServerResponseChannelSinkStack sinkStack,
object state,
IMessage msg,
ITransportHeaders headers,
Stream msgStream) {
long length ;
msgStream = Helper.GetStreamLength(msgStream, out length) ;
Console.WriteLine(
"CustomServerSink:Async, taille du stream de retour {0}",
length);
m_NextSink.AsyncProcessResponse(
sinkStack, state, msg, headers, msgStream) ;
}
public Stream GetResponseStream(
IServerResponseChannelSinkStack sinkStack,
object state,
IMessage msg,
ITransportHeaders headers) {
return null ;
}
public ServerProcessing ProcessMessage(
IServerChannelSinkStack sinkStack,
IMessage msgIn,
ITransportHeaders headersIn,
Stream msgInStream,
out IMessage msgOut,
out ITransportHeaders headersOut,
837
838
Canaux (channels)
839
Dans les constructeurs des fournisseurs, le dictionnaire pass en argument correspond aux
proprits que vous souhaitez aecter aux fournisseurs. Ici nous nutilisons pas cette possibilit. Il surait dcrire dans le fichier de configuration :
<provider type = "CustomChannelSink.CustomClientSinkProvider,ChannelSink"
Prop1="hello"/>
Vous pouvez obtenir la valeur de la proprit comme ceci :
public CustomClientSinkProvider(IDictionary Prop,
ICollection ProviderData) {
string s = (string) Prop["Prop1"];
...
Vous pouvez ainsi configurer votre fournisseur. Par exemple vous pouvez spcifier le nom
dun algorithme de compression ou de cryptage des streams.
Le type IServerChannelSinkStack utilis dans les mthodes dans lintercepteur de messages ct serveur, concerne le retour des appels asynchrone. Cette pile correspond en fait
la chane dintercepteurs de messages qui sera utilise du ct serveur, pour faire transiter
le message de retour.
Le type ITransportHeaders utilis dans les mthodes dans les intercepteurs de messages
ct client et ct serveur, permet de passer des informations concernant le message. Par
exemple, si la fonction des intercepteurs de message est de crypter/dcrypter le stream, vous
pouvez spcifier dans cet en-tte si le message est eectivement crypt :
class CustomClientSink{ ...
public void ProcessMessage(
IMessage
Msg,
840
Ct serveur
Du ct serveur, toutes les informations relatives aux canaux se trouvent dans le fichier de configuration :
Serveur.cs
...
static void Main() {
RemotingConfiguration.Configure("Serveur.config") ;
Console.WriteLine("Pressez une touche pour stopper le serveur...") ;
Console.Read() ;
}
...
Nous aectons ici un formateur binaire avec un canal rcepteur HTTP.
Exemple 22-31 :
Serveur.config
<configuration>
<system.runtime.remoting>
<application name = "Serveur">
<service>
<wellknown type="NommageInterface.CAdditionneur,Interface"
mode ="Singleton" objectUri="Service1.rem" />
</service>
<channels>
<channel port="65100" ref ="http">
<serverProviders>
<provider type=
"CustomChannelSink.CustomServerSinkProvider,ChannelSink" />
<formatter ref="binary"/>
</serverProviders>
</channel>
</channels>
Canaux (channels)
841
</application>
</system.runtime.remoting>
</configuration>
Ct client
Du ct client, toutes les informations pertinentes sont galement dans le fichier de configuration :
Client.cs
...
static void Main() {
RemotingConfiguration.Configure("Client.config",false) ;
CAdditionneur objA = new CAdditionneur() ;
double dA = objA.Add( 3.0 , 4.0 ) ;
Console.WriteLine("Valeur retourn
ee:"+dA) ;
CAdditionneur objB = new CAdditionneur() ;
double dB = objB.Add( 3.0 , 4.0 ) ;
Console.WriteLine("Valeur retourn
ee:"+dB) ;
}
...
Nous aectons ici un formateur binaire avec un canal metteur HTTP.
Exemple 22-32 :
Client.config
<configuration>
<system.runtime.remoting>
<application name = "Client">
<client>
<wellknown type="NommageInterface.CAdditionneur,Interface"
url="http://localhost:65100/Service1.rem" />
</client>
<channels>
<channel ref ="http">
<clientProviders>
<formatter ref="binary"/>
<provider type =
"CustomChannelSink.CustomClientSinkProvider,ChannelSink"/>
</clientProviders>
</channel>
</channels>
</application>
</system.runtime.remoting>
</configuration>
Excution du projet
Voici ce quache le serveur sur sa console :
842
Contexte .NET
La place de la notion de contexte dans larchitecture .NET
Nous avons vu que les domaines dapplication permettent lisolation lexcution au niveau
des types, de la scurit et de la gestion des exceptions. En revanche, il existe une entit plus
fine que le domaine dapplication pour stocker les objets. Un domaine dapplication .NET peut
contenir plusieurs de ces entits, appeles contexte .NET. Pour simplifier la lecture, on les appellera contextes tant quil ny a pas de confusion avec la notion de contexte COM+, qui est
dirente (cette notion est prsente page 300). Certains auteurs utilisent les termes contexte gr
ou contexte dexcution pour un contexte .NET et le terme de contexte non gr pour les contextes
COM+. Tous les objets .NET vivent dans un contexte, et il existe au moins un contexte par
domaine dapplication. Ce contexte, appel contexte par dfaut du domaine dapplication, est cr
en mme temps que son domaine. La Figure 22-7 rsume ces relations dinclusions :
La notion de contexte ore aux dveloppeurs la fonctionnalit trs pratique dinterception des
appels un objet. Nous rappelons quintercepter un appel signifie que lon peut eectuer un
ou plusieurs traitements chaque appel entrant ou sortant vers un objet dun contexte. Ces
traitements sont matrialiss par des intercepteurs de messages. Lide centrale est que lorsquun
client appelle une mthode dun objet, il na pas conscience que son appel est intercept et que
des traitements sont eectus avant et aprs lexcution de lappel.
Contexte .NET
843
Processus
CLR
Domaine dapplication
Rgles pour
la scurit
Rgles pour les
exceptions non gres
Assemblage
Contexte
Objet
Objet
Objet
Type
Context-bound et context-agile
Daprs la section prcdente, un contexte peut tre vu comme une zone dun domaine
dapplication, contenant des objets et des intercepteurs de messages. Les appels vers les objets
dun contexte sont transforms en messages qui sont intercepts et traits par les intercepteurs
du message. Nous savons maintenant que pour transformer un appel en un message, il faut
passer par lintermdiaire dun proxy transparent. Or, nous savons aussi que le CLR ne fabrique
un proxy transparent vers un objet que si ce dernier est une instance dune classe drive de
MarshalByRefObject appele par une entit situe hors de son domaine dapplication. Ici, nous
souhaiterions bnficier du mcanisme dinterception de messages pour tous les appels, mme
ceux eectus entre entits situes dans le mme domaine dapplication. Cest exactement
pour cela quexiste la classe System.ContextBoundObject. Une instance dune classe qui drive
de ContextBoundObject est accessible seulement partir de proxys transparents. Dans ce cas,
mme la rfrence this utilise dans les mthodes de la classe est un proxy transparent et
non une rfrence directe. Il est logique que la classe ContextBoundObject drive de la classe
MarshalByRefObject, puisquelle ne fait que renforcer le comportement de cette classe qui est
dindiquer au CLR quune classe est potentiellement utilise au travers dun proxy transparent.
Une instance dune classe drivant de ContextBoundObject est qualifie de context-bound (lie au
contexte en franais). Une instance dune classe ne drivant pas de ContextBoundObject est qualifie de context-agile (dsolidariss du contexte en franais). Un objet context-bound est toujours
excut au sein de son contexte. Dans le cas dun appel non distant, un objet context-agile est
toujours excut au sein du contexte de celui qui lappelle. La Figure 22-8 illustre ceci.
Domaine dapplication
Contexte B
Contexte A
Objet context-agile
Proxy transparent
Objet context-bound
Proxy transparent
844
System ;
System.Runtime.Remoting.Contexts ;
System.Runtime.Remoting ;
System.Threading ;
[Synchronization(SynchronizationAttribute.REQUIRED)]
public class Foo1 : ContextBoundObject {
public void AfficheContexte() {
Console.WriteLine("Foo1: ContextID:" +
Thread.CurrentContext.ContextID) ;
}
public Foo2 GetFoo2() {
Foo2 obj = new Foo2() ;
obj.AfficheContexte();
return obj ;
}
}
public class Foo2 {
public void AfficheContexte() {
Console.WriteLine("Foo2: ContextID:" +
Thread.CurrentContext.ContextID) ;
}
}
public class Program {
static void Main() {
Console.WriteLine("Main: ContextID:" +
Thread.CurrentContext.ContextID) ;
Foo1 obj1 = new Foo1() ;
Contexte .NET
845
obj1.AfficheContexte();
Console.WriteLine("IsObjectOutOfContext(obj1):" +
RemotingServices.IsObjectOutOfContext(obj1)) ;
Foo2 obj2 = obj1.GetFoo2() ;
obj2.AfficheContexte();
Console.WriteLine("IsObjectOutOfContext(obj2):" +
RemotingServices.IsObjectOutOfContext(obj2)) ;
}
}
Ce programme ache :
Main: ContextID:0
Foo1: ContextID:1
IsObjectOutOfContext(obj1):True
Foo2: ContextID:1
Foo2: ContextID:0
IsObjectOutOfContext(obj2):False
On voit bien que lobjet obj2 context-agile peut tre excut hors du contexte dans lequel il
a t cr. On observe aussi que lobjet obj1 context-bound sexcute au sein de son contexte
lorsquon lappelle partir dun autre contexte.
Dans un mme domaine dapplication, les ContextID sont tous dirents. Cependant il est possible que deux ContextID de deux contextes appartenant deux domaines dapplication dirents soient gaux.
Daprs nos propres tests, dans une mthode statique, la proprit Thread.CurrentContext.
ContextID est gale au ContextID du contexte par dfaut du domaine dapplication. Ce fait est
logique tant donn que le comportement du CLR est de crer un objet dans le contexte du
client, si cela est possible. Or, une mthode statique peut constituer un client dun objet.
Attribut de contexte
Un attribut de contexte est un attribut .NET qui agit sur une classe context-bound. Une classe attribut de contexte implmente linterface System.Runtime.Remoting.Contexts.IContextAttribute.
Une classe context-bound peut avoir plusieurs attributs de contexte. Lors de la cration dun
objet de cette classe, chaque attribut de contexte de la classe vrifie si le contexte de celui qui
construit lobjet lui convient. Cette opration seectue dans la mthode :
public bool IContextAttribute.IsContextOK(
Context
IConstructionCallMessage
ctxDuClient,
ctorMsg)
846
Si au moins un attribut de contexte retourne false, le CLR doit crer un nouveau contexte pour
accueillir le nouvel objet. Dans ce cas chaque attribut de contexte peut injecter une ou plusieurs
proprits de contexte dans le nouveau contexte. Ces injections ont lieu dans la mthode suivante :
public void IContextAttribute.GetPropertiesForNewContext(
IConstructionCallMessage
ctorMsg)
Proprit de contexte
Une proprit de contexte est une instance dune classe qui implmente linterface System.
Runtime.Remoting.Contexts.IContextProperty. Chaque contexte peut avoir plusieurs proprits. Les proprits dun contexte sont injectes par les attributs de contexte dans le contexte,
lorsque le contexte est cr. Une fois que chaque attribut de contexte a inject ses proprits,
la mthode suivante est appele sur chaque proprit. Il nest alors plus possible dinjecter une
proprit dans ce contexte :
public void IContextProperty.Freeze(Context ctx)
Le CLR demande ensuite chaque proprit si elle est satisfaite par ce nouveau contexte en
appelant la mthode :
public bool IContextProperty.IsNewContextOK(Context ctx)
Chaque proprit dun contexte a un nom dfini par la proprit Name :
public string IContextProperty.Name{ get }
Les mthodes des objets hbergs dans le contexte peuvent avoir accs aux proprits du
contexte en appelant la mthode :
IContextProperty Context.GetProperty(string sPropertyName)
Cette possibilit peut tre intressante, puisque les objets du contexte peuvent ainsi partager des
informations et accder des services grce aux proprits de leur contexte. Cependant le rle
principal des proprits dun contexte nest pas de fournir cette possibilit :
Le rle principal des proprits dun contexte est dinjecter des intercepteurs de messages,
dans les rgions dinterception de messages des contextes concerns.
La description de ces rgions dinterceptions fait lobjet de la prochaine section. Familiarisonsnous dabord avec ces concepts dattributs et de proprits dun contexte laide dun exemple.
Pour ceux qui ont assimil la section sur les canaux, un attribut de contexte joue un peu le
rle dun fichier de configuration qui injecte des fournisseurs dans les canaux. De mme une
proprit de contexte joue un peu le rle du fournisseur dintercepteur de message.
Contexte .NET
847
proprit. Ces services sont en loccurrence la possibilit dcrire une chane de caractres dans
un fichier en appelant la mthode LogContextProperty.Log(string). Le nom de ce fichier est
un paramtre de lattribut LogContextAttribute. On peut ainsi avoir un fichier pour chaque
classe. Lorsquune nouvelle instance dune classe ayant pour attribut LogContextAttribute est
cre, la mthode bool LogContextAttribute.IsContextOK(Context) permet de vrifier si le
contexte dans lequel rside lentit qui appelle le constructeur, contient dj une instance de
LogContextAttribute avec le mme nom de fichier. Si ce nest pas le cas, un nouveau contexte
doit alors tre cr. La mthode LogContextAttribute.GetPropertiesForNewContext(IConstructionCallMessage ctor) cre une instance de LogContextProperty. Au retour de cette
mthode, la nouvelle proprit est automatiquement injecte dans le nouveau contexte par le
CLR. Voici le programme :
Exemple 22-34 :
using
using
using
using
System ;
System.Runtime.Remoting.Contexts ;
System.Runtime.Remoting.Activation ;
System.Threading ;
848
Ce programme ache :
ContextID=1 Ecrire Ajout de 4+5 dans le fichier LogPourFoo.txt
ContextID=2 Ecrire Ajout de 6+7 dans le fichier LogPourFoo.txt
ContextID=1 Ecrire Ajout de 8+9 dans le fichier LogPourFoo.txt
Les objets obj1 et obj3 sont hbergs dans le mme contexte puisque obj3 a t construit partir
du contexte dobj1.
Contexte .NET
849
le contexte cible. Chacune des proprits de contexte du contexte cible a la possibilit dinjecter
des intercepteurs de messages dans chacune de ces rgions.
Les intercepteurs de messages injects dans la rgion serveur interceptent tous les messages dappels venant dun autre contexte vers tous les objets du contexte cible. Il existe donc
une rgion serveur par contexte cible.
Les intercepteurs de messages injects dans la rgion objet interceptent tous les messages
dappels venant dun autre contexte vers un objet particulier du contexte cible. Il y a donc
une rgion objet pour chaque objet dun contexte. Les rgions objet se situent donc
dans le contexte cible.
Les intercepteurs de messages injects dans la rgion envoi interceptent tous les messages dappels venant dun autre contexte vers un objet particulier du contexte cible. Il y
a donc une rgion envoi pour chaque objet du contexte cible. La dirence entre une
rgion envoi et une rgion objet est quune rgion envoi se situe dans le contexte
appelant lobjet et non pas dans le contexte cible contenant lobjet. On utilise les rgions
envoi pour transmettre aux intercepteurs de messages du contexte cible des informations sur le contexte appelant.
Les intercepteurs de messages injects dans la rgion client interceptent tous les messages dappels du contexte cible vers des objets situs dans dautres contextes. Il y a donc
une rgion client pour chaque contexte cible.
La Figure 22 -9 illustre ces notions de rgions. Le contexte cible contient deux objets OBJ1 et
OBJ2. Nous avons choisi de placer deux objets dans le contexte cible et pas un, pour bien montrer que les types de rgion objet et envoi sont concernes par linterception des messages
au niveau dun objet alors que les types de rgion serveur et client sont concernes par
linterception des messages au niveau dun contexte.
Nous avons plac deux intercepteurs propritaires de messages (Msg sink) par rgion pour bien
montrer que chaque rgion peut avoir zro, un ou plusieurs intercepteurs de messages. Concrtement, tous ces intercepteurs propritaires de messages sont injects dans les rgions par les
proprits du contexte cible, mme lorsque la rgion nappartient pas au contexte cible. Comme
vous pouvez dfinir vos propres classes de proprits de contexte vous pouvez choisir quels sont
les intercepteurs de messages quil faut injecter.
Vous remarquez que chaque rgion contient un intercepteur systme de messages dit de terminaison, qui permet dindiquer au CLR la sortie dune rgion. Concrtement, vous naurez pas
vous proccuper de ces intercepteurs systmes de messages de terminaison.
Sachez que lorsque le contexte appelant et le contexte cible sont dans le mme domaine dapplication, le CLR utilise une instance de la classe CrossContextChannel interne mscorlib.dll.
Cette instance fait basculer la proprit Context du thread courant. Notre figure reprsente ces
instances.
Le code dun attribut de contexte propritaire (classe MyAfficheContextAttribute) injectant une proprit de contexte propritaire (classe MyAfficheContextProperty) dans
850
Msg sink
Msg sink de terminaison
Msg sink
CrossContextChannel
Msg sink
Msg sink
Msg sink de terminaison
Proprit du
contexte cible
(injecte des msg sink
dans les rgions)
Rgion objet pour OBJ2
Msg sink
Msg sink
OBJ2
Msg sink
Msg sink
CrossContextChannel
Contexte contenant lobjet OBJ3 qui est appel par les objets OBJ1 et OBJ2
La modification du comportement des intercepteurs de messages en fonction dun paramtre pass dans lattribut du contexte. En loccurrence, ce paramtre est un boolen qui
indique aux intercepteurs de messages sils doivent ou non acher quelque chose sur la
console.
Linjection dintercepteurs de messages dans chacune des quatre rgions par une proprit
de contexte. Lattribut de contexte MyAfficheContextAttribute fait en sorte de crer un
contexte pour chaque objet de la classe Foo. Nous crons une premire instance de Foo et
nous appelons une mthode sur cette instance pour passer par les intercepteurs de messages
des rgions envoi , serveur et objet . Pour passer par les intercepteurs de messages
Contexte .NET
851
de la rgion client , nous crons une seconde instance de Foo et nous lappelons partir
de la premire instance. Il y a donc trois contextes en jeu : le contexte dans lequel sexcute la mthode Main() (ContextID=0), le contexte qui contient la premire instance de Foo
(ContextID=1) et le contexte qui contient la seconde instance de Foo (ContextID=2).
Le fait quun appel intra contexte ne dclenche pas lappel de tous ces intercepteurs de
messages. Ce comportement est expos lorsque la premire instance de Foo sauto appelle.
Linjection de plusieurs intercepteurs de messages dans une rgion (en loccurrence la rgion client ).
Le moment o le CLR injecte eectivement des intercepteurs de messages dans les rgions.
On voit clairement que ce moment dpend du type de la rgion.
Voici le programme :
Exemple 22-35 :
using
using
using
using
using
using
System ;
System.Runtime.Remoting.Contexts ;
System.Runtime.Remoting.Messaging ;
System.Runtime.Remoting.Activation ;
System.Threading ;
System.Collections ;
//
// Les instances de cette classe de propri
et
e de contexte injectent
// des intercepteurs de messages dans les quatre r
egions dinterception.
//
public class MyAfficheContextProperty :
IContextProperty,
IContributeEnvoySink,
IContributeObjectSink,
IContributeServerContextSink,
IContributeClientContextSink {
public MyAfficheContextProperty(bool bAffiche) {
m_bAffiche = bAffiche ;
}
bool m_bAffiche ;
public bool bAffiche { get { return m_bAffiche ; } }
// IContextProperty
public string Name { get { return "PropAffiche" ; } }
public bool IsNewContextOK(Context ctx) { return true ; }
public void Freeze(Context ctx) {
Console.WriteLine("
Freeze ContextID={0}", ctx.ContextID) ;
}
// Injection de deux intercepteurs de messages
// dans la region client.
public IMessageSink GetClientContextSink( IMessageSink nextSink) {
Console.WriteLine("
GetClientContextSink()") ;
852
Contexte .NET
return false ;
}
// Injection dune nouvelle instance de MyAfficheContextProperty
// dans chaque contexte cree.
public void GetPropertiesForNewContext(
IConstructionCallMessage ctor){
IContextProperty prop = new MyAfficheContextProperty(m_bAffiche) ;
ctor.ContextProperties.Add(prop) ;
}
}
//---------------------------------------------------------------------//
// Les instances de MyAfficheMessageSink sont des intercepteurs de
// messages qui ne font que signaler leur pr
esence en affichant deux
// lignes sur la console (ils naffichent rien si (m_bAffiche==false) ).
//
[Serializable]
public class MyAfficheMessageSink : IMessageSink {
// Prochain intercepteur de messages `
a qui transmettre le message.
IMessageSink m_NextSink ;
// Message `a afficher.
string m_sAffiche ;
// Doit-on afficher un message sur la console ?
bool m_bAffiche ;
public IMessageSink NextSink { get { return m_NextSink ; } }
public MyAfficheMessageSink(IMessageSink nextSink,
string sAffiche,
bool bAffiche) {
m_NextSink = nextSink ;
m_sAffiche = sAffiche ;
m_bAffiche = bAffiche ;
}
public IMessage SyncProcessMessage(IMessage msg) {
if (m_bAffiche)
Console.WriteLine("
Deb MsgSink:{0} ContextID={1}",
m_sAffiche, Thread.CurrentContext.ContextID);
// Transmet le msg au prochain intercepteur de message...
IMessage retMsg = m_NextSink.SyncProcessMessage(msg) ;
if (m_bAffiche)
Console.WriteLine("
Fin MsgSink:{0} ContextID={1}",
m_sAffiche, Thread.CurrentContext.ContextID);
return retMsg ;
}
public IMessageCtrl AsyncProcessMessage(IMessage msg,
IMessageSink replySink) {
return m_NextSink.AsyncProcessMessage(msg, replySink) ;
853
854
Contexte .NET
GetEnvoySink()
Fin MsgSink:Region Serveur
ContextID=1
...avant lappel `a obj1...
Deb MsgSink:Region Envoi
ContextID=0
Deb MsgSink:Region Serveur
ContextID=1
GetObjectSink()
Deb MsgSink:Region Objet
ContextID=1
Ajout de 4+5
Fin MsgSink:Region Objet
ContextID=1
Fin MsgSink:Region Serveur
ContextID=1
Fin MsgSink:Region Envoi
ContextID=0
...avant la construction de obj2...
Freeze ContextID=2
GetServerContextSink()
Deb MsgSink:Region Serveur
ContextID=2
Constructeur de Foo
GetEnvoySink()
Fin MsgSink:Region Serveur
ContextID=2
...avant quobj1 appelle obj2...
Deb MsgSink:Region Envoi
ContextID=0
Deb MsgSink:Region Serveur
ContextID=1
Deb MsgSink:Region Objet
ContextID=1
Ajout cross de 6+7
Deb MsgSink:Region Envoi
ContextID=1
GetClientContextSink()
Deb MsgSink:Region Client2
ContextID=1
Deb MsgSink:Region Client1
ContextID=1
Deb MsgSink:Region Serveur
ContextID=2
GetObjectSink()
Deb MsgSink:Region Objet
ContextID=2
Ajout de 6+7
Fin MsgSink:Region Objet
ContextID=2
Fin MsgSink:Region Serveur
ContextID=2
Fin MsgSink:Region Client1
ContextID=1
Fin MsgSink:Region Client2
ContextID=1
Fin MsgSink:Region Envoi
ContextID=1
Fin MsgSink:Region Objet
ContextID=1
Fin MsgSink:Region Serveur
ContextID=1
Fin MsgSink:Region Envoi
ContextID=0
...avant quobj1 appelle obj1...
Deb MsgSink:Region Envoi
ContextID=0
Deb MsgSink:Region Serveur
ContextID=1
Deb MsgSink:Region Objet
ContextID=1
Ajout cross de 8+9
Ajout de 8+9
Fin MsgSink:Region Objet
ContextID=1
Fin MsgSink:Region Serveur
ContextID=1
Fin MsgSink:Region Envoi
ContextID=0
855
856
Rcapitulatif
857
On a vu dans la section prcdente que linjection dintercepteurs de messages dans une rgion
client ou envoi se fait aprs lappel au constructeur dun objet. Un intercepteur de messages dans une rgion client ou envoi ne peut donc pas accrocher dinformation
un message reprsentant lappel au constructeur. Or, un intercepteur de messages dans une rgion serveur peut potentiellement chercher dcrocher une information dun message
reprsentant lappel au constructeur. Dans ce cas vous pouvez accrocher linformation dans la
mthode GetPropertiesForNewContext() de lattribut du contexte :
Exemple 22-37 :
...
public class MyAfficheContextAttribute : Attribute, IContextAttribute{
public void GetPropertiesForNewContext(IConstructionCallMessage ctor){
DonneDeContexte dc = new DonneDeContexte(10);
ctor.LogicalCallContext.SetData("UneDC",dc);
IContextProperty prop = new MyAfficheContextProperty(m_bAffiche) ;
ctor.ContextProperties.Add(prop) ;
}...}
Rcapitulatif
Les modes dactivations dun objet
Nous avons vu quatre modes dactivation dun objet, WKO mode dappel simple, WKO mode
dappel singleton, CAO et publication dun objet. Voici un tableau rcapitulatif des principales
dirences de ces modes :
WKO mode
simple appel
WKO mode
singleton
CAO
Publication
chaque
appel.
Au premier
appel dun
client.
Lors de lappel
du
constructeur
par le client.
Lors de lappel
du
constructeur
par le serveur.
Quelle information
dtient le client
pour accder
lobjet ?
Un URI
contenant le
point
terminal.
Un URI
contenant le
point
terminal.
Un ObjRef
obtenu
implicitement
lors de lappel
au
constructeur.
Un ObjRef
obtenu
explicitement.
Un objet est-il
partageable entre
plusieurs clients ?
Non
Oui
Non
Oui
Non
Non
Oui
Oui
858
Non
Non
Oui, il ny a
pas de
reconstruction
automatique
dun objet.
Oui, il ny a
pas de
reconstruction
automatique
dun objet.
Non, le client
peut se
satisfaire des
mtadonnes
dune
interface
Non, le client
peut se
satisfaire des
mtadonnes
dune
interface
Oui, moins
dutiliser le
design pattern
factory.
Oui
Peut-on configurer
ce mode dans un
fichier de
configuration ?
Oui
Oui
Oui
Non
Peut-on utiliser un
constructeur avec
arguments pour
lactivation de
lobjet ?
Non
Non
Ca dpend.
Oui si on
utilise le design
pattern factory.
Oui
Proxy transparent
Proxy transparent
Proxy rel
Proxy rel
Rgion envoi
Rgion envoi
Rgion client
Rgion client
Avant srialisation
Avant srialisation
Formateur (srialisation)
Formateur (srialisation)
Aprs srialisation
Aprs srialisation
Transport
Transport
23
ASP.NET 2.0
Introduction
Une application web est une application qui renvoie des pages rdiges avec le langage HTML,
en rponse des requtes HTTP. Ces pages HTML sont exploitables par le client partir dune
application appele navigateur (browser, tel que Internet Explorer). Les navigateurs sont spcialiss dans la visualisation des pages HTML. En 1995, la socit Netscape fournit le premier navigateur exploitable par le grand public. Ce type de logiciel a rapidement boulevers le quotidien
de centaines de millions de personnes.
Les avantages des applications web sur les applications graphiques encapsules dans un excutable (les clients riches) sont nombreux :
Le dploiement de nouvelles versions de lapplication est automatique puisque tout est centralis du ct serveur.
Les navigateurs prennent en compte une grande partie de la complexit de la gestion des
contrles.
La principale dirence est quune application graphique produit des bitmaps achs sur
lcran raison de plusieurs dizaines de rafrachissements par seconde alors quune application
web produit des pages HTML renvoyes aux clients la demande. Grce larchitecture .NET
qui permet et encourage une programmation de haut niveau bas sur les composants et les
objets, le dveloppement dapplications graphiques web et le dveloppement dapplications
graphiques riches nont jamais taient aussi proche. On utilise parfois le terme de modle de
programmation unifi .
860
Historique
Au cours des annes 90, le dveloppement dapplications web sest simplifi. Durant la mme priode les applications web taient de plus en plus interactives grce lintroduction de contrles.
Les informations saisies par lutilisateur sont incluses dans les requtes HTTP. HTTP 1.1 prsente
sept mthodes pour invoquer une ressource. Les deux mthodes les plus usites sont HTTPGET et HTTP-POST. La mthode HTTP-GET permet de passer les paramtres dune requte en
ajoutant des informations lURL. La mthode HTTP-POST permet de passer les paramtres
dune requte dans le corps de cette requte. Dans ce cas, les paramtres ne sont pas visibles
dans lURL.
Les pages HTML renvoyes en rponses aux requtes HTTP sont de moins en moins statiques.
Elles sont fabriques dynamiquement en fonction des informations saisies par lutilisateur.
Cette fabrication est ralise par du code appel par le serveur HTTP.
Au dbut des annes 90, on utilisait des programmes CGI (Common Gateway Interface) pour
construire dynamiquement des pages HTML. Avec de tels programmes, vous pouviez accder
des bases de donnes et raliser des traitements. Cependant le dveloppement de programmes
CGI tait fastidieux et souvent les performances ntaient pas au rendez-vous. Dj, des techniques dutilisation de scripts rdigs en langages interprts tels que Perl mergeaient.
En 1996, Microsoft intgre les filtres ISAPI son serveur dapplication IIS (Internet Information
Server) pour faciliter la redirection des flux HTTP. Mais surtout, la mme anne, lentreprise
de Redmond cre les ASP (Active Serveur Page). Une nouveaut apporte par ASP tait que le
code tait insr dans lHTML alors quen CGI, cest lHTML qui tait insr dans le code. De
nombreuses amliorations sont apportes cette technologie de la version 1 en 1996 la version
3 en 2000. Malgr son succs les principaux concurrents, JSP et PHP, continuaient gagner des
parts de march.
En ASP.NET le code HTML et le code de fabrication dynamique des pages HTML ont la
possibilit dtre stocks dans des fichiers dirents pour permettre une meilleure collaboration entre les designers du site web et les dveloppeurs.
Les sessions dASP.NET peuvent tre partages entre plusieurs machines, par exemple, en
les stockant dans une base de donnes.
ASP.NET prsente plusieurs mthodes pour garder jour ltat des contrles dune page,
dun chargement lautre. Cela fait autant de code fastidieux en moins crire et maintenir.
Les paramtres de configuration dune application ASP.NET sont stocks dans un fichier au
format XML. Pour rpliquer une configuration, il sut de copier un tel fichier. Cela rsout
la dicult inhrente lutilisation des mta bases IIS.
861
Le processus INETINFO.EXE reprsente le processus IIS. Toutes les requtes HTTP destines
aux applications web et aux services web sont rceptionnes dans ce processus par le code
du filtre ISAPI aspnet_isapi.dll. Les rgles de scurit IIS sont appliques puis les requtes sont transmises au processus aspnet_wp.exe par lintermdiaire dun pipe nomm.
Si la rception dune requte le processus aspnet_wp.exe nexiste pas, le filtre ISAPI aspnet_isapi.dll soccupe de le dmarrer et de crer un pipe nomm ddi.
Une autre architecture est mise en uvre avec IIS 6.0 sous Windows Server 2003. Dans ce cas IIS
et ASP.NET sexcute au sein dun mme processus nomm w3wp.exe.
862
Rponse HTTP
Machine hbergeant le serveur Web
INETINFO.EXE
(IIS 5.0)
aspnet_wp.exe
CLR
Domaine dapplication
Assemblage de lapplication Web
aspnet_isapi.dll
Pipe
Pipeline
HTTP
Instance
dune classe
ASP.NET a t conu pour tre indpendant du serveur web sous-jacent. Concrtement, cela
implique quASP.NET peut sinterfacer avec dautres serveurs web quIIS. Par exemple, Visual
Studio .NET 2005 est fourni avec le serveur web WebDev.WebServer.EXE permettant de tester
et de dboguer vos applications web en cours de dveloppement. Ce serveur web est bas sur
lancien serveur web nomm Cassini qui tait fourni gratuitement par Microsoft. Ceci est trs
pratique puisque IIS nest disponible que sur les versions professionnelles de Windows. Il est
donc maintenant possible de dvelopper une application web sur une machine nayant quune
dition familiale de Windows. Notez que vous pouvez configurer Visual Studio 2005 pour quil
sinterface avec IIS en modifiant loption Proprit du projets web Options de dmarrage Utiliser un serveur de votre choix.
ASP.NET eectue un certain nombre de tches pour traiter les requtes et les rponses HTTP
dans ce que nous appelons pipeline HTTP dans la Figure 23 -1. Parmi ces tches nous pouvons
citer lauthentification ASP.NET de lutilisateur qui a initi une requte ou la gestion des informations concernant la session courante pour cet utilisateur. Tout au long de ce chapitre, nous
aurons loccasion de dcrire ces tches. Nous expliquerons aussi comment vous pouvez tendre
ce pipeline en injectant vos propres traitements certains points prcis.
Lexcution dune requte au sein du processus aspnet_wp.exe se fait sur le mme thread de
bout en bout. Pour cela, ASP.NET exploite les threads I/O du pool de threads du CLR et peut
donc excuter plusieurs requtes simultanment. En plus dviter by-design un certains nombre
de problmes de concurrence cette faon de faire est ecace car la rception dune requte
partir du pipe nomm dbute directement sur un thread I/O. On vite donc une transition
dinformation entre threads. Puisquil ny a pas de pipe nomm avec IIS 6.0 les choses se passent
diremment et dans cette version, ASP.NET utilise les threads ouvriers du CLR.
Il est trs important de remarquer que le processus aspnet_wp.exe sexcute par dfaut sous
un compte utilisateur Windows nomm ASPNET (ou Network Service sous IIS 6.0) aux privilges
restreints. En eet, durant le dveloppement il se peut que vous ne vous rendiez pas compte de
certains problmes du fait que WebDev.WebServer.EXE sexcute par dfaut sous votre compte
utilisateur. Nous verrons comment ventuellement configurer ASP.NET pour que ce processus
sexcute sous un autre utilisateur. Notez enfin que si lutilisateur initiateur dune requte a pu
863
tre authentifi comme un utilisateur Windows, il est possible que le thread qui traite la requte
sexcute sous le compte de cet utilisateur.
Vous pouvez hberger simultanment plusieurs applications web avec ASP.NET. Par exemple
un serveur IIS avec les adresses IP mappes suivantes et les sites logiques suivants, peut exposer
les applications web suivantes :
Adresses IP mappees :
www.xyz.com
www.smacchia.com
Sites logiques :
http://www.xyz.com
http://www.smacchia.com
Applications web :
http://www.xyz.com/holidays
http://www.xyz.com/holidays/cadre
http://www.smacchia.com/Documents
http://www.smacchia.com
Les rpertoires physiques contenant les applications web nont pas de contraintes quant leur
localisation. Ils peuvent tre sur le systme de gestion de fichiers local ou sur un autre systme
distant. Par exemple le rpertoire de lapplication web http://www.xyz.com/holidays/cadre
nest pas tenu dtre physiquement imbriqu dans le rpertoire de lapplication web http://
www.xyz.com/holidays.
Enfin, il est possible de faire cohabiter une version 1.x et une version 2.0 dASP.NET sur la mme
machine. Dans ce cas, vous devez prciser au filtre ISAPI aspnet_isapi.dll quelle version utiliser en vous servant de loutil aspnet_regiis.exe. Il sut de taper la ligne de commande suivante avec la version souhaite de cet outil :
aspnet_regiis.exe -r
AspnetHosting.cs
using System ;
using System.Web ;
using System.Web.Hosting ;
class Program {
static void Main() {
Console.WriteLine("Main Appdomain:" +
AppDomain.CurrentDomain.FriendlyName) ;
CustomSimpleHost host = (CustomSimpleHost)
ApplicationHost.CreateApplicationHost(
typeof(CustomSimpleHost), @"/",
System.IO.Directory.GetCurrentDirectory());
864
On dclare dabord vouloir hberger ASP.NET dans le processus courant grce lappel de la
mthode statique ApplicationHost.CreateApplicationHost(). Cette mthode cre un nouveau domaine dapplication et y charge ASP.NET. ASP.NET y charge alors lassemblage contenant la classe CustomSimpleHost. En loccurrence cet assemblage est celui de notre application,
savoir AspnetHosting.exe. Une instance de cette classe est ensuite cre dans ce nouveau domaine dapplication. La mthode CreateApplicationHost() retourne une rfrence vers cette
instance dans le domaine dapplication initial. Ceci est possible car la classe CustomSimpleHost
drive de la classe MarshalByRefObject. Ainsi grce la technologie .NET Remoting, une instance
de CustomSimpleHost peut tre rfrence et utilise partir dun domaine dapplication dirent de celui dans lequel elle rside.
Le code de la mthode ProcessRequest se sert dune instance de la classe System.Web.Hosting.
SimpleWorkerProcess pour traiter une requte HTTP GET. Ici, nous crons artificiellement
cette requte en fournissant le nom de la web form demande (Default.aspx), les paramtres de
la requte GET (ici il ny en a pas) et un flot de donnes vers lequel ASP.NET va pouvoir diriger
la rponse HTTP (en loccurrence ce flot est la console). La page HTML produite par ASP.NET
partir de la web form Default.aspx est ache sur la console. Ce programme ache donc
ceci :
Main Appdomain:ConsoleApplication6.exe
ASP.NET AppDomain:c6ed2272-1-127654065004140000
<html xmlns="http://www.w3.org/1999/xhtml" >
<body>
<center>
<form name="Form1" method="post" action="Default.aspx" id="Form1">
...
Pour excuter cet exemple il faut que vous prvoyiez de stocker une web form nomme
Default.aspx dans le mme rpertoire que lassemblage AspnetHosting.exe. Vous pouvez par
exemple vous servir de la page Default.apsx prsente un peu plus loin dans lExemple 23-3. En
outre il faut prvoir de dupliquer lassemblage AspnetHosting.exe dans le sous rpertoire /bin
du rpertoire contenant la version originale de AspnetHosting.exe. En eet, ASP.NET va automatiquement rechercher dans ce rpertoire lassemblage contenant la classe CustomSimpleHost
pour le charger dans le nouveau domaine dapplication.
865
866
Pour tester ce serveur web, il sut de demander lURL suivante dans un navigateur sexcutant
sur la mme machine :
http://localhost:8008/Default.aspx
Default.aspx
867
Les autres balises non HTML contiennent des descriptions de contrles ASP.NET. Pour linstant, retenez quun contrle ASP.NET est un champ dinstance de la classe de la web form qui
lexcution, remplace sa description dans le fichier .aspx par du code HTML.
Notons que pour une mme page .apsx Visual Studio fournit deux vues. La vue design plutt
utilise par les graphistes et la vue qui prsente le contenu du fichier .aspx. Comprenez bien
quil ne sagit que de vues des mmes donnes et que tous changements partir dune vue est
immdiatement rpercuts sur lautre vue :
Default.aspx
868
Si vous excutez cet exemple, vous vous apercevrez quil y a du code HTML en plus l o nous
avons plac des points de suspension. Il est pour linstant trop tt dans ce chapitre pour sintresser ce code. Retenez juste pour linstant que les blocs de restitutions de code ont t excuts
et ont produits les quatre lignes en gras dans la page HTML.
Il est intressant de noter que le code compris dans les blocs de restitutions de code a t compil
en une mthode de la classe qui reprsente la page :
...
private void __RenderForm1( HtmlTextWriter __w,
Control parameterContainer) {
__w.Write("\r\n
");
for (int num1 = 4 ; num1 < 8 ; num1++) {
__w.Write("\r\n
<font size=\"");
__w.Write(num1);
__w.Write("\"> Bonjour </font> \r\n
");
}
}
...
Cette mthode est automatiquement appele par ASP.NET au moment o la page est cre.
Code-Behind
Si vous crez un nouveau site web C avec Visual Studio (avec Fichier Nouveau Site Web...) vous
vous apercevrez que lorsque vous crez une nouvelle page, vous pouvez cocher loption Placez le
code dans un fichier spar. Dans ce cas, un fichier source C nomm [page].aspx.cs sera associ
votre nouvelle page [page].aspx. Vous avez la possibilit de dfinir une classe dans ce fichier
qui contiendra des membres exploitables directement lexcution dans la classe reprsentant
la page. On nomme ce code code-behind de la page. Clairement, les avantages du code-behind sur
le code-inline et les blocs de restitution de code sont dallger les fichiers .aspx et de permettre
aux designers web et aux dveloppeurs de collaborer plus ecacement puisque ces premiers
travaillent avec des fichiers .aspx tandis que ces derniers travaillent avec des fichiers .aspx.cs.
Pour utilisez cette technique avec notre mthode Btn_Click(), il faut dabord prciser dans
le fichier Default.aspx quelle classe contient le code-behind avec la directive Inherits et quel
fichier source contient cette classe avec la directive CodeFile :
Exemple 23-5 :
Default.aspx
869
<asp:listitem>noir</asp:listitem>
</asp:dropdownlist>
<br/>
<asp:button id="Button1" text="Soumettre" OnClick="Btn_Click"
runat="server"/>
<br/>
<asp:label id="Msg" runat="server"/>
</form>
</center>
</body>
</html>
Ensuite il faut dclarer la classe MyDefaultPage comme partielle avec le mot cl C 2 partial :
Default.aspx.cs
Exemple 23-6 :
using System ;
public partial class MyDefaultPage : System.Web.UI.Page {
protected void Btn_Click(Object sender, EventArgs e) {
Msg.Text = "Vous avez selectionn
e : "+Couleur.SelectedItem.Value ;
}
}
En eet, ASP.NET 2.0 prsente un modle de construction du code-behind dirent de celui
dASP.NET 1.x. Ces deux modles sont illustrs par la figure suivante :
Modle ASP.NET 1.x
System.Web.UI.Page
Classe
Default_aspx
gnre partir
du fichier .aspx
Classe
contenant le
code-behind
Prcompil par
Visual Studio
Compil
lexcution
par ASP.NET
MaClasse.dll
Classe
partielle
contenant le
code-behind
Classe
partielle
gnre partir
du fichier .aspx
Temp.dll
Notez que
MaClasse.dll
Compil lexcution
par ASP.NET
870
Il diminue le risque derreur de synchronisation puisque les dtails gnrs ne sont plus
accessibles aux dveloppeurs.
Il facilite la migration des projets ASP.NET 1.x en prservant le fait que la classe [pageName]_aspx drive de la classe contenant le code-behind qui elle-mme drive de la classe
Page.
Une mme classe de base partielle peut tre exploite peut tre rutilise par plusieurs
pages.
Par curiosit, voici quoi ressemblent la classe partielle et la classe Default_aspx gnres dans
notre exemple par ASP.NET :
public partial class MyDefaultPage : System.Web.UI.Page,
System.Web.SessionState.IRequiresSessionState {
protected System.Web.HttpApplication ApplicationInstance {
get{ return return this.Context.ApplicationInstance ; }
}
protected System.Web.Profile.DefaultProfile Profile {
get { return (DefaultProfile)this.Context.Profile ; }
}
protected System.Web.UI.WebControls.Button Button1 ;
protected System.Web.UI.WebControls.DropDownList Couleur ;
protected System.Web.UI.HtmlControls.HtmlForm Form1 ;
protected System.Web.UI.WebControls.Label Msg ;
}
namespace ASP {
public class Default_aspx : MyDefaultPage {
public Default_aspx() {
base.AppRelativeVirtualPath = "~/Default.aspx" ;
if (!Default_aspx.__initialized) {
string[] textArray1 = new string[2]
{ "~/Default.aspx", "~/Default.aspx.cs" } ;
Default_aspx.__fileDependencies =
base.GetWrappedFileDependencies(textArray1) ;
Default_aspx.__initialized = true ;
}
}
private void __BuildControl__control2(ListItemCollection __ctrl) {
ListItem item1 = this.__BuildControl__control3() ;
__ctrl.Add(item1) ;
ListItem item2 = this.__BuildControl__control4() ;
__ctrl.Add(item2) ;
}
private ListItem __BuildControl__control3() { ... }
private ListItem __BuildControl__control4() { ... }
private Button __BuildControlButton1() { ... }
871
Compilation dynamique
Dans ce modle, vous dployez les fichiers sources sur le serveur et ASP.NET soccupe de les
compiler lexcution. Chaque fichier source est compil lors de sa premire utilisation. Si un
fichier source dj compil est modifi, ASP.NET le dtecte et le recompile. Lavantage principal de ce modle est de faciliter le travail des dveloppeurs puisquils nont pas se soucier des
mises jours, du dploiement et de ltape de la compilation. En revanche, ce modle dgrade
lgrement les performances de votre serveur puisque chaque compilation a un cot.
La notion de compilation dynamique existait dj en ASP.NET 1.x puisque comme le montre
la Figure 23 -3, chaque page aspx tait compile lexcution lors de la premire demande.
En revanche, en ASP.NET 1.x vous deviez compiler manuellement (ou avec Visual Studio) les
classes contenant le code-behind ainsi que toutes les autres classes utilises par votre application.
Si vous souhaitiez rutiliser des classes dj compiles dans des assemblages, il fallait placer ces
assemblages dans le rpertoire [AppRootDir]/bin de votre application (ou dans le GAC) et les
rfrencer la compilation de votre application.
La compilation dynamique en ASP.NET 2.0 a considrablement volu. Vous pouvez dployer
vos fichiers sources dans le rpertoire [AppRootDir]/App_Code de votre application (ou dans un
de ses sous rpertoires). Cette arborescence peut contenir des fichiers sources rdigs ventuellement avec dans plusieurs langages tels que C , VB.NET, XSD (pour les datasets typs) etc. Les
classes contenues dans ces fichiers peuvent tre exploites dans le code de nimporte quelle page
872
de votre application. Pour amliorer les performances de la compilation, llment <configuration>/<system.web>/<compilation> du fichier web.config prsente plusieurs sous lments
permettant de paramtrer finement la quantit de code compiler chaque compilation (batch
compilation en anglais).
Les assemblages contenus dans le rpertoire /bin nont pas besoin dtre rfrenc daucune
sorte. Les classes et autres ressources contenues dans ces assemblages peuvent tre exploites
dans le code de nimporte quelle page de votre application. ASP.NET 2.0 se charge de les trouver
lexcution.
En plus des rpertoires /App_Code et /bin, ASP.NET sait aller chercher dautres types de ressources que des classes dans les rpertoires suivants (tous directement placs dans le rpertoire
racine de votre application) :
/App_GlobalResources et /App_LocalResources pour les fichiers ressources globaux ou locaux lapplication web (voir page 953).
/App_Browsers pour les fichiers .browser qui dclarent quelles possibilits doit supporter
un navigateur (ce rpertoire replace llment <browserCaps> in machine.config).
Toute mise jour de nimporte quel fichier contenu dans un de ces rpertoires sera automatiquement dtecte et prise en compte par ASP.NET 2.0 lexcution. La mise jour dun fichier
source provoque sa recompilation tandis que la mise jour dun assemblage dans le rpertoire
/bin provoque son rechargement par le CLR. Ce mcanisme de rechargement dassemblage exploite la possibilit nomme shadow copy du CLR qui est dcrite en page 108. Enfin, sachez que le
filtre ISAPI aspnet_filer.dll fait en sorte quaucun des fichiers contenus dans ces rpertoires
nest accessible par une requte HTTP.
873
lobfuscation des assemblages. Bien videmment, ceci se paye par une souplesse amoindrie dans
le processus de mise jour.
Loutil aspnet_compiler.exe sutilise trs simplement. Vous prcisez en entre le rpertoire
virtuel contenant le code source de votre application web avec loption /m (ou le rpertoire
Windows racine avec loption /p), le nom de votre application avec loption /v et le rpertoire
de sortie contenant le rsultat de la compilation.
aspnet_compiler.exe /m /LM/W3SVC/1/ROOT/MonSiteWeb D:/TestDeploy
aspnet_compiler.exe /v WebSite /p D:/Site/MonSiteWeb D:/TestDeploy
Il vous sut ensuite de copier/coller le contenu de ce rpertoire de sortie dans le rpertoire
virtuel adquat sur le serveur. Il est intressant de remarquer que ce rpertoire de sortie prsente
toujours vos fichiers dextensions .aspx mais vids de leur contenu, votre fichier web.config
ainsi que des assemblages aux noms gnrs dans le rpertoire /bin. Vous remarquez aussi la
prsence dun fichier PrecompiledApp.config. Les lments XML contenus dans ce fichier indique ASP.NET si il est autoris ou pas compiler des pages .aspx. Ainsi, les paramtres de
ce fichier peuvent empcher la prise en compte de nouvelles pages .aspx ajoutes un site.
874
Lorsque ASP.NET construit la classe qui reprsente cette page, il y insre du code qui construira
une arborescence de contrle serveur. Cette arborescence est illustre par la figure suivante :
Page
.Controls
LiteralControl
.Text =
"<html>
<body>
<center>"
HtmlFormControl
.Controls
LiteralControl
.Text =
" </center>
</body>
</html>"
LiteralControl
.Text =
"
Couleur : "
DropDownList .ID = "Couleur"
Button .Text = "Soumettre"
LiteralControl
.Text =
"
<br>"
Label .ID = "Msg"
.Text = "Vous avez
slectionn : blanc"
LiteralControl .Text = ""
875
pouvons maintenant prciser quASP.NET gnre aussi du code responsable de linitialisation de ces champs. Ceci souligne limportance de la comprhension de lenchanement
des actions lors de la vie dune page. En eet, si vous essayez dutiliser les champs Msg ou
Couleur avant quils naient t initialiss par ce code, vous obtiendrez une exception de type
NullReferenceException. Nous dtaillons un peu plus tard cet enchanement crucial mais
avant, intressons nous la logique de notre page Default.aspx.
Interaction client/serveur
La Figure 23 -5 illustre la logique de notre page Default.aspx en prcisant les principales tapes
de linteraction client/serveur web. Le client initie linteraction en demandant au serveur la
page /WebSite1/Default.aspx au moyen dune requte HTTP GET. Le serveur lui renvoie la
page HTML demande. Cette dernire est fabrique par une nouvelle instance de la classe Default_aspx. Lors de la premire demande dun client, les contrles serveur ont leurs tats initiaux. Pour le DropDownList ltat initial est blanc et pour le Label ltat initial est une chane de
caractres vide. Une fois la page reue, le client slectionne noir sur le contrle HTML correspondant notre DropDownList et clique sur le bouton. Cette dernire action entrane lenvoi
dune requte HTTP POST au serveur. Cette requte contient notamment ltat du contrle
HTML correspondant notre DropDownList. Le traitement interne des pages .aspx de ASP.NET
(dans le pipeline HTTP) est capable de dtecter que la requte POST est due un click du bouton
Button1. Aussi, il invoque la mthode Btn_Click() sur une nouvelle instance de la classe Default_aspx. En eet, cette mthode a t associe lvnement click sur Button1 dans notre
page Default.aspx grce la ligne <...id="Button1"...OnClick="Btn_Click"...>. Lexcution de cette mthode positionne ltat de notre contrle Label "Vous avez s
electionn
e :
noir". Le fragment HTML ajout par ce contrle est alors "<span id="Msg">Vous avez s
electionne : noir</span>".
Dans linteraction client/serveur de la section prcdente, vous avez peut tre remarqu que
la slection noir du contrle Couleur a t retenue lorsque le client charge la page pour la seconde fois. Cela rsulte du fait quASP.NET initialise automatiquement et implicitement les valeurs des contrles avec les valeurs trouves dans une requte POST. Certains types de contrles
HTML comme le contrle <span> (correspondant un contrle serveur de type Label) nont pas
leurs valeurs sauves explicitement dans la requte POST. Cela se voit en analysant la requte
POST. Seules les contrles nomms Couleur, Button1 et __VIEWSTATE (dont nous allons parler)
ont leurs valeurs sauves dans le corps de la requte POST :
POST /WebSite1/Default.aspx HTTP/1.1
...
Content-Length: 104
__VIEWSTATE=%2FwEPDwUJOTE3ODUwMjE3ZGS%2ByRcRG2v6m0v5xTATxgcXe0GIOA%
3D%3D&Couleur=blanc&Button1=Soumettre
Et pourtant, en dboguant la mthode Btn_Click() on saperoit lors dun deuxime click du
bouton que le serveur connat le contenu du contrle Msg de type Label :
Il est trs important que vous soyez convaincu que dans cet exemple le serveur ne retient aucun
tat. La valeur du contrle Label Msg est donc forcment encode dune manire implicite dans
la requte POST.
En eet, lorsque le navigateur client fabrique une requte HTTP, il rassemble dans une chane
de caractres toutes les valeurs de tous les contrles dont les valeurs nont pas t places dune
876
webSite/Default.aspx
Ct serveur
ASP.NET
Cration
Render()
Instance de
Default_aspx
HTTP/1.1 200 OK
<html>
<body>
...
<span id="Msg">
</span>
...
POST
HTTP/1.1
...
/WebSite1/Default.aspx
ASP.NET
Cration
Btn_Click()
Render()
Instance de
Default_aspx
HTTP/1.1 200 OK
<html>
<body>
...
<span id="Msg">
Vous avez
slectionn :
noir</span> ...
877
<div>
...
<input type="hidden" name="__VIEWSTATE" id="__VIEWSTATE"
value="/wEPDwUJOTE3ODUwMjE3D2QWAgIBD2QWAgIFDw8WAh4EVGV4dAUfVm91cyBhdm
V6IHPDqWxlY3Rpb25uw6kgOiBibGFuY2RkZGsF3jbEtiYP6owub0heigPWF2Fq" />
</div>
...
Lorsque le navigateur fabrique une requte HTTP, il aecte la chane de caractres base64 la
valeur du contrle HTML __VIEWSTATE. On retrouve notamment cette valeur dans notre extrait
prcdent dune requte POST (en gras). Ainsi, en dcodant ces informations le traitement interne des pages .aspx de ASP.NET est capable de retrouver tous les tats de tous les contrles.
La premire fois que lon rencontre cette astuce elle ne nous parait pas bien propre . Comprenez bien que cette technique est l pour pallier labsence dtat dans un environnement web.
Un tel comportement se rvle fort utile puisquen naviguant dune page une autre, les utilisateurs sattendent ce que les valeurs des contrles ne changent pas. Il ny avait pas de technique
quivalente dans la technologie ASP et les dveloppeurs taient obligs de prvoir du code pour
chaque contrle sens se souvenir de son tat entre les requtes.
878
Dans ce contexte, nous pouvons maintenant prciser que llment <form> dfinit un contrle
serveur de type System.Web.UI.HtmlControls.HtmlForm. Tous les contrles serveur qui peuvent
provoquer un vnement postback doivent tre dclars entre les balises dun lment <form>.
Nous avons dj mentionn quASP.NET sait associer la mthode Btn_Click() de notre classe
MyDefaultPage lvnement postback click sur Button1 du fait que lon aecte la valeur
Btn_Click lattribut OnClick de Button1 dans notre page Default.aspx :
...
<asp:button id="Button1" text="Soumettre" OnClick="Btn_Click" runat="server"/>
...
En fait, la classe System.Web.UI.WebControls.Button prsente un vnement Click de type la
dlgation EventHandler. Lors de la compilation dune page aspx, ASP.NET sait que la valeur
dun attribut OnXXX correspond au nom dune mthode quil faut associer lvnement XXX
du contrle serveur sous-jacent. On peut se passer de cette facilit du moment que lon eectue
cette association nous-mme lors de linitialisation de notre instance de Default_aspx dclenche par une requte POST. Ainsi la page suivante est quivalente notre page (nous dtaillons
un peu plus tard lvnement Page_Load() dclench par ASP.NET lors du chargement dune
page).
Exemple 23-7 :
Default.aspx
...
<asp:button id="Button1" text="Soumettre" runat="server"/>
...
Exemple 23-8 :
Default.aspx.cs
using System ;
public partial class MyDefaultPage : System.Web.UI.Page {
protected void Page_Load(object sender, EventArgs e) {
if (IsPostBack)
Button1.Click += Btn_Click;
}
protected void Btn_Click(Object sender, EventArgs e) {
Msg.Text = "Vous avez selectionn
e : "+Couleur.SelectedItem.Value ;
}
}
Notez laccs la proprit bool Page.IsPostBack{get;}. Cette proprit vaut true si la requte courante est une requte POST. Nous lutilisons car il ny a pas lieu de sabonner lvnement Button1.Click si lon nest pas dans le cas dune requte postback.
Une question se pose : Comment la plomberie ASP.NET dtermine quel vnement de quel
contrle serveur il faut dclencher lors de la rception dune requte POST dun client ?
linstar de ce que lon a vu pour le viewstate ASP.NET ajoute deux contrles cachs nomms
__EVENTTARGET et __EVENTARGUMENT chaque page HTML gnre, ainsi que du code javascript
pour les initialiser. Par exemple voici un extrait dune telle page HTML produite par une instance de notre classe Default_aspx :
<html xmlns="http://www.w3.org/1999/xhtml" >
<body>
879
<center>
<form method="post" action="Default.aspx" id="Form1">
<div>
<input type="hidden" name="__EVENTTARGET" id="__EVENTTARGET" value="" />
<input type="hidden" name="__EVENTARGUMENT" id="__EVENTARGUMENT"
value="" />
<input type="hidden" name="__VIEWSTATE" id="__VIEWSTATE" value="..." />
</div>
<script type="text/javascript">
<!-var theForm = document.forms[Form1];
if (!theForm) {
theForm = document.Form1;
}
function __doPostBack(eventTarget, eventArgument) {
if (!theForm.onsubmit || (theForm.onsubmit() != false)) {
theForm.__EVENTTARGET.value = eventTarget;
theForm.__EVENTARGUMENT.value = eventArgument;
theForm.submit();
}
}
// -->
</script>
...
Ainsi, toute requte POST contient dans les paramtres __EVENTTARGET et __EVENTARGUMENT linformation permettant la plomberie ASP.NET didentifier le contrle HTML responsable du
postback.
ContolState
Un problme courant rencontr en ASP.NET 1.x provient du fait que la chane base64 du viewstate devient norme (>10.000 caractres) ds que lon essaye dy stocker des informations telles
que le contenu dune table par exemple pour un contrle serveur de type DataGrid. Lorsque ce
problme est rencontr, on peut choisir de dsactiver le viewstate pour ce contrle et de grer son
tat autrement, par exemple en stockant le contenu de la table dans un cache du ct serveur ou
en rechargeant le contenu chaque requte. Il se pose alors un second problme : pour certains
contrles tels que le DataGrid les navigateurs utilisent le contenu du viewstate pour des raisons
fonctionnelles telles que le dclenchement dun vnement postback lors du changement des
donnes. Ainsi, en ASP.NET 1.x on peut tre tent de dsactiver le viewstate dun contrle pour
des raisons de bande passante et de lactiver pour des raisons fonctionnelles.
En ASP.NET 2.0 le problme du viewstate norme est amoindri du fait que les informations
sont stockes dune manire plus ecace dans la chane base64. Mais surtout, ASP.NET 2.0
introduit la notion de controlstate. Un contrle serveur peut profiter du controlstate pour stocker
les informations ncessaires son bon fonctionnement. Les contrles serveur suivants peuvent
ainsi profiter du controlstate pour des raisons fonctionnelles et dsactiver le viewstate pour des
raisons de bande passante : CheckBoxList, DetailsView, FormView, GridView, ListControl et
ses classes drives, LoginView, MultiView et Table.
880
Sachez que vous ne verrez pas de contrles cachs spciaux dans vos pages HTML pour stocker
les controlstate. Les controlstate sont stocks comme une sous-section du viewstate.
Exemple 23-9 :
DisplayColor.aspx
881
DisplayColor.aspx
Default.aspx
882
Cette technique prsente un dsavantage par rapport lutilisation dun vnement PostBackUrl :
lURL ne change pas dans le navigateur.
Enfin, signalons la prsence dune nouvelle proprit bool IsCrossPagePostBack{get;}. Voici
un tableau qui illustre la valeur de cette proprit selon le contexte :
PageSource realise un postback sur elle-m
eme :
PageSource.IsPostBack
true
PageSource.IsCrossPagePostBack
false
PageSource.PreviousPage
null
PageSource realise un postBackUrl sur PageCible :
PageSource.IsPostBack
true
PageSource.IsCrossPagePostBack
true
PageSource.PreviousPage
null
PageCible.IsPostBack
false
PageCible.IsCrossPagePostBack
false
PageCible.PreviousPage
r
ef
erence vers PageSource
PageSource realise un transfert sur PageCible :
PageSource.IsPostBack
false
PageSource.IsCrossPagePostBack
false
PageSource.PreviousPage
null
PageCible.IsPostBack
false
PageCible.IsCrossPagePostBack
false
PageCible.PreviousPage
r
ef
erence vers PageSource
883
Default.aspx
884
vnements
thodes
Construction
Tous
Initialisation
InitializeCulture
Page
DeterminePostBackMode
Page
OnPreInit
Page
OnInit
Tous
OnInitComplete
Page
LoadPageStateFromPersistenceMedium
Page
LoadViewState
Tous
LoadControlState
Tous
ProcessPostData
Page
Restaure
tats
les
et
m-
ContrlesCommentaires
Restaure les tats des contrles partir des valeurs spcifies dans les paramtres postback.
Chargement de
la page
Avant la fabrication
Rendu
885
OnPreLoad
Page
OnLoad
Tous
OnDataBinding
Tous
ProcessPostData
Page
Validate
Page
RaiseChangedEvent
Page
RaisePostBackEvent
Page
OnLoadComplete
Page
OnPreRender
Tous
OnPreRenderComplete
Page
Page.SaveViewState
Tous
SaveControlState
Tous
SavePageState\
-To-Persistence\
-Medium
Page
OnSaveStateComplete
Page
Render
Tous
Dernire chance dagir sur larborescence des contrles ainsi que leur
tats.
886
Finalisation
UnLoad
Tous
Dispose
Tous
Default.aspx
Default.aspx.cs
887
888
Un dlgu vers la mthode damorcage du traitement qui est excute par le thread qui a
initi le traitement de la requte (i.e celui qui excute Page_Load()). En loccurrence, notre
dlgu rfrence la mthode BeginInvoke(). Elle est excute juste aprs lvnement PreRender().
Un dlgu vers la mthode de finalisation du traitement qui est excute par un thread
du pool lorsque le traitement a t signal comme fini. En loccurrence, notre dlgu rfrence la mthode BeginInvoke. Elle est excute juste avant lvnement PreRenderComplete.
Un dlgu vers une mthode excute par un thread du pool lorsque le traitement ne sest
pas eectu dans les temps impartis. En loccurrence, notre dlgu rfrence la mthode
EndTimeOutInvoke().
889
Pour une page stocke dans un sous rpertoire [racine]\Foo de votre application web, les
valeurs des paramtres dfinis dans le fichier [racine]\Foo\Web.Config masquent les valeurs
des paramtres dfinis dans le fichier [racine]\Web.Config qui elles-mmes masquent les valeurs des paramtres dfinis dans le fichier C:\textbackslashInetpub\textbackslashwwwroot\
textbackslashWeb.Config qui elles-mmes masquent les valeurs des paramtres dfinis dans le
fichier Machine.Config. Avec ce modle, il est par exemple ais de dfinir une stratgie de scurit par dfaut commune toutes les applications web hberges sur la machine dans le fichier
Machine.Config tout en se laissant la libert dappliquer une stratgie de scurit exceptionnelle
pour certaines pages confidentielles stockes dans un sous rpertoire dune application. En plus
de cette souplesse, ce modle permet aussi le dploiement dune application ainsi que de ses
fichiers de configuration par simple copie dune arborescence de rpertoires et de fichiers (dploiement xcopy). Notez enfin quASP.NET rend inaccessible de lextrieur les accs aux fichiers
de configuration.
890
Pour plus de dtails sur la configuration dune application web, nous vous conseillons de consulter larticle ASP.NET Configuration des MSDN.
Enfin, ajoutons quun lment <location>, enfant direct de llment <configuration>, permet de redfinir pour chaque page les paramtres de configuration.
Vous pouvez indiquer ASP.NET quand redmarrer un processus qui a un comportement anormal. Lattribut requestQueueLimit prcise le nombre maximal de demandes en
attentes. Lorsque ce nombre est atteint, ASP.NET considre que le processus a un comportement anormal. Lattribut memoryLimit prcise le pourcentage maximal de mmoire
systme pouvant tre utilise. Au-del de cette limite, ASP.NET considre que le processus a un comportement anormal. Dans ce cas, il lui donne la dure spcifie par lattribut
shutdownTimeout pour sarrter automatiquement. Passe cette limite, ASP.NET arrte ce
processus si ce nest dj fait et lance un nouveau processus. Notez que les demandes en
attentes sont automatiquement rassignes ce nouveau processus.
Les attributs webGarden et cpuMask indique ASP.NET comment se comporter sur un serveur multi processeurs. En eet, si un serveur possde plusieurs processeurs, il est trs ecace de pouvoir faire en sorte que chaque processeur gre seulement certains processus. On
dit que lon cre des anits entre processus et processeurs. Cette technique est nomme
web garden.
Les attributs userName et Password indiquent lidentit de lutilisateur Windows sous lequel
doit sexcuter le processus aspnet_wp.exe.
Pipeline HTTP
891
contenu dune manire non type. ASP.NET 2.0 prsente plusieurs facilits pour raliser cette
opration :
Visual Studio 2005 vous assiste avec lintellisense lors de ldition manuelle dun fichier de
configuration.
Une nouvelle interface graphique web vous permet de mettre jour la configuration directement partir dun navigateur excut en local (pour des raisons de scurit). Vous pouvez
y avoir accs partir de Visual Studio avec longlet Site Web ASP.NET Configuration.
Une nouvelle interface graphique de mise jour de la configuration vient sinsrer dans la
console de configuration dIIS.
Des nouvelles classes de base sont fournies pour manipuler programmatiquement et surtout, dune manire fortement type, le XML contenu (voir page 58).
Pipeline HTTP
Introduction
Jusquici, nous avons expliqu comment ASP.NET traite les demandes de pages .aspx. Lors de
lacheminement dune requte HTTP entre la sortie du pipeline nomm (provenant en gnral
du processus dIIS) et le traitement de la page, nous avons eu loccasion de mentionner le pipeline http ainsi que du code pour le traitement en interne des pages .aspx. Le pipeline HTTP est
un mcanisme dASP.NET permettant principalement :
De fournir des traitements qui agissent sur les requtes ou (non exclusif) les rponses HTTP.
Un tel traitement se nomme module HTTP. ASP.NET a ses propres modules HTTP qui
traitent par exemple les identifiants de sessions ou lauthentification des utilisateurs. Nous
allons voir comment crer nos propres modules HTTP.
De fournir un traitement qui sert les requtes HTTP (i.e qui fabrique une rponse HTTP en
fonction dune requte). Un tel traitement se nomme handler HTTP. ASP.NET a ses propres
handlers HTTP, dont notamment celui qui soccupe du traitement des pages .aspx en interne. Nous allons voir comment crer nos propres handlers HTTP.
La Figure 23 -7 exhibe la place que tient le pipeline HTTP ainsi que les modules et handlers
HTTP dans ASP.NET. Cette figure se base sur le modle de processus de IIS 5.0 qui, rappelons
le, est dirent de celui de IIS 6.0 :
892
Rponse HTTP
as
aspnet_isapi.dll
IIS
Machine
hbergeant
le serveur
Web
aspnet_wp.exe
Pipe nomm
CLR
httpModule
httpModule
Pipeline HTTP
httpModule
httpHandler
Global.asax
Pipeline HTTP
893
/*...*/
</script>
Nous vous invitons consulter les MSDN pour obtenir la liste des vnements. Il est intressant
de remarquer quASP.NET utilise la rflexion pour associer les mthodes dfinies dans le fichier
Global.asax aux vnements de HttpApplication. Le nom dune telle mthode commence par
Application_ ou Session_ suivit du nom de lvnement. En consquence, vous devez porter
une attention particulire au nom de ces mthodes puisque lintellisense nest pas l pour dtecter les erreurs de syntaxe. Notez que certains vnements tel que Application_Start() ne sont
exploitables que par lintermdiaire du fichier global.asax tandis que dautres sont en plus de
cette technique, exploitables comme tout vnement dune classe .NET.
Contexte HTTP
Une instance de la classe System.Web.HttpContext est automatiquement cre par ASP.NET
pour servir chaque requte HTTP. Lors du traitement dune requte, cet objet contexte est accessible tous les niveaux du pipeline HTTP grce la proprit statique Current{get;} de la
classe HttpContext. Lors du traitement dune requte, on a souvent besoin des informations
accessibles au travers des proprits de lobjet contexte, telles que le principal de lutilisateur
initiateur de la requte (si il est authentifi), la requte HTTP elle-mme ou la rponse en cous
de fabrication.
Module HTTP
Nous pouvons maintenant nous intresser au dveloppement de nos propres modules HTTP.
Un module HTTP est une instance dune classe qui implmente linterface System.Web.
IHttpModule :
public interface IHttpModule {
void Dispose() ;
void Init( HttpApplication app ) ;
}
Un objet module est cr au lancement dune application ASP.NET. Aprs lappel au constructeur de sa classe, ASP.NET invoque la mthode Init(). Vous devez profiter de cette mthode
pour abonner vos traitements sur les requtes et les rponses HTTP. En gnral, on sabonne aux
vnements BeginRequest() et EndRequest() de HttpApplication mais vous pouvez choisir de
vous abonner nimporte quel autre vnement. Voici un exemple de module HTTP :
Exemple 23-17 :
using System ;
using System.Web ;
public class MyHttpModule : IHttpModule {
public void Dispose() { }
public void Init( HttpApplication app ) {
app.BeginRequest += OnBeginRequest;
app.EndRequest += OnEndRequest;
}
public void OnBeginRequest( object source, EventArgs args ) {
HttpApplication app = source as HttpApplication ;
894
Web.Config
<?xml version="1.0"?>
<configuration
xmlns="http://schemas.microsoft.com/.NetConfiguration/v2.0">
<system.web>
<httpModules>
<add name="MyHttpModuleName" type="MyHttpModule,MyAsm"/>
</httpModules>
...
Lattribut type prcise le nom complet (i.e avec les espaces de noms) de la classe implmentant
linterface IHttpModule, suivi dune virgule puis suivi du nom fort de lassemblage contenant
cette classe. Si le code de la classe est contenu dans le rpertoire /App_Code, vous navez pas
Pipeline HTTP
895
besoin de prciser lassemblage. Sinon, lassemblage doit tre contenu dans le rpertoire /bin.
Notez que la dclaration dun module HTTP peut se faire dans la balise <httpModules> du
fichier Machine.config afin quil soit pris en compte par toutes les applications ASP.NET de
la machine. Dans ce cas, lassemblage contenant la classe du module doit imprativement tre
prsent dans le GAC.
Vous devez absolument concevoir des modules aussi indpendants que possible du fait que
lon ne matrise pas lordre de lenchanement des appels aux traitements des modules dans le
pipeline HTTP. Cet ordre denchanement est le mme que lordre des appels aux mthodes
Init() des modules. Donc, la Figure 23 -7 nest pas tout fait exacte puisque les traitements
EndRequest() sont appels dans le mme ordre que les traitements BeginRequest() et non dans
lordre inverse. Empiriquement, on saperoit que lordre des appels aux mthodes Init() des
modules est le mme que celui de la dclaration des modules dans la balise <httpModules> mais
cet ordre nest pas garanti par Microsoft.
Handler HTTP
Vous pouvez dfinir vos propres handlers HTTP pour traiter vos propres types de ressources.
Pour cela il faut dfinir une classe implmentant linterface System.Web.IHttpHandler :
public interface IHttpHandler {
bool IsReusable { get ; }
void ProcessRequest(HttpContext context) ;
}
Une telle classe est en gnral dclare dans un fichier dextension .ashx. Par exemple, le handler HTTP suivant est une calculatrice qui prend une opration ainsi que deux oprandes dans
les paramtres dune requte GET et ache le rsultat dans la rponse. Notez que depuis Visual
Studio 2005, nous avons lintellisense sur les fichiers .ashx. Pour garder lexemple simple, la
rponse est un simple document texte sans balise <html> mais IE sait lacher :
Exemple 23-18 :
MyCalc.ashx
896
Vous pouvez alors vous servir de cette calculatrice en tapant la requte GET suivante dans votre
navigateur :
http://localhost:1232/WebSite1/MyCalc.ashx?a=11&b=3&op=mul
Notez quune instance de MyClassHttpHandler est cre pour servir chaque requte. En outre,
la ressource MyCalc.ashx est relle car elle est matrialise par un fichier la racine de lapplication. ASP.NET a su router la requte vers cette ressource et donc, vers le bon handler.
Vous pouvez aussi vous servir de handlers HTTP pour traiter des requtes vers des ressources
virtuelles i.e non matrialises par un fichier la racine de lapplication. linstar de ce que
lon a vu pour les modules HTTP, il faut prciser un tel handler HTTP en indiquant la classe qui
implmente IHttpHandler dans la balise <httpHandlers> du fichier Web.Config de lapplication
comme ceci :
Exemple :
Web.Config
<?xml version="1.0"?>
<configuration
xmlns="http://schemas.microsoft.com/.NetConfiguration/v2.0">
<system.web>
<httpHandlers>
<add verb="GET" path="*.calc" type="MyCalcHttpHandler"/>
</httpHandlers>
...
Ce que lon a nonc concernant lassemblage contenant la classe, concernant le formatage de
lattribut type et concernant la dclaration dans le fichier Machine.config reste valable pour
les handlers HTTP. Cependant cette technique est potentiellement plus puissante puisque vous
pouvez par exemple router vers votre handler toutes les requtes vers une ressource dont le nom
se termine par lextension .calc. Bien entendu, avec cette seconde technique il ny a pas besoin
dun fichier .ashx et vous pouvez dfinir la classe MyCalcHttpHandler dans un fichier C du
rpertoire /App_Code. Voici le genre de requte GET que peut alors servir notre handler :
http://localhost:1232/WebSite1/XYZ.calc?a=11&b=3&op=mul
Cette technique marche parfaitement avec le serveur web de Visual Studio. Il nen est pas de
mme en production avec IIS. En eet, il faut indiquer ce dernier que dans le cadre dun
rpertoire virtuel, les requtes dextension .calc doivent tre traites par ASP.NET. Pour cela
il faut eectuer la manipulation suivante sur loutil de configuration dIIS :
[click droits sur le rpertoire virtuel concern] Proprits Rpertoire virtuel Configuration... Ajouter Extension=.calc Chemin de lexcutable=C:\WINDOWS\Microsoft.NET\Framework\v2.0.XXXX\aspnet_isapi.dll verbe=GET
Dans le cas o vous utilisez un fichier .ashx, cette manipulation est inutile car IIS est configur
par dfaut pour router les requtes concernes vers ASP.NET.
Dans le modle de dveloppement de handlers HTTP qui vient dtre expos, un objet handler est cr par ASP.NET la premire requte et est alors exploit pour servir toutes les
897
requtes suivantes. Vous pouvez agir sur ce comportement en ayant recours linterface
IHttpHandlerFactory. Par exemple vous pouvez faire en sorte quun objet handler soit cr
pour servir chaque requte ou mme grer un pool dobjets handler recyclable.
public interface IHttpHandlerFactory {
IHttpHandler GetHandler( HttpContext context,
string
requestType,
string
url,
string
pathTranslated) ;
void ReleaseHandler(IHttpHandler handler) ;
}
Pour cela, il sut de prciser dans le fichier Web.Config votre classe qui implmente linterface
IHttpHandlerFactory la place de celle qui implmente IHttpHandler. La mthode GetHandler() est invoque par ASP.NET chaque traitement dune nouvelle requte et la mthode
ReleaseHandler() chaque terminaison de requte. Cela vous laisse la libert de dcider quand
vos handler sont crs, quand ils doivent tre recycls et quand ils doivent tre dtruits.
La taille de vos donnes : Il est maladroit de faire transiter des donnes volumineuses (>
4Ko) sur le rseau. Il est aussi maladroit de stocker des donnes volumineuses dans la mmoire dun processus. Au del dune certaine quantit de donnes, le mcanisme de mmoire virtuelle de Windows grve les performances dune manire inacceptable cause des
nombreux accs au disque dur induits. On prfre stocker ce genre de donnes dans une
base de donnes.
Le niveau de scurit requis : Il est dangereux de stocker chez le client ou de faire transiter
chaque requte des donnes confidentielles telles quun numro de carte bancaire. Ces
donnes transitent en gnral une fois sur le rseau sous une forme crypte, lorsque le client
les communique au serveur, puis sont stockes du ct serveur.
Les performances souhaites : Faire transiter des donnes sur le rseau, stocker des donnes
en mmoire ou accder une base de donnes sont des oprations directement ou indirectement coteuses en termes de performance. Il faut donc trouver un compromis selon vos
besoins.
898
technique prsente linconvnient majeur de ne pas tre applicable tous les clients puisque de
nombreux utilisateurs dcident de dsactiver les cookies sur leurs navigateurs.
Linstance de HttpApplication globale lapplication maintient une instance de la classe
System.Web.HttpApplicationState. Cet objet, lui aussi global lapplication, est accessible
au travers de la proprit Application{get;} prsente la fois par la classe HttpApplication
et par la classe Page. Cet objet peut tre vu comme un dictionnaire qui permet de stocker des
donnes globales lapplication. Voici un exemple dutilisation conjointe de ce dictionnaire et
de cookies pour assigner un identifiant unique chaque client qui se connecte (notez les appels
aux mthodes Lock() et UnLock() pour synchroniser les accs au dictionnaire) :
Exemple 23-19 :
using System ;
using System.Web ;
public partial class MyDefaultPage : System.Web.UI.Page {
protected void Btn_Click(Object sender, EventArgs e) {
Msg.Text = "Vous avez selectionn
e : "+Couleur.SelectedItem.Value ;
}
protected void Page_Load(object src, EventArgs args) {
if (Application["ClientCounter"] == null) {
Application["ClientCounter"] = 0 ;
}
HttpCookie cookie = Request.Cookies["ClientCounterCookie"] ;
int clientNumber = -1 ;
if (cookie == null) {
Application.Lock() ;
clientNumber = (int) Application["ClientCounter"] + 1 ;
Application["ClientCounter"] = clientNumber ;
Application.UnLock() ;
cookie = new HttpCookie( "ClientCounterCookie" ) ;
cookie.Value = clientNumber.ToString() ;
Response.Cookies.Add(cookie) ;
}
else {
clientNumber = Int32.Parse( cookie.Value ) ;
}
Response.Write("Client Number : " + clientNumber ) ;
}
}
En fait, on pourrait sinspirer de ce dernier exemple pour dvelopper notre scnario de panier
pour stocker les achats de chaque client. Les informations sur les achats courant seraient stockes du ct serveur et indexes par lidentifiant client. Cependant cette faon de faire a plusieurs points faibles :
899
Il faudrait aussi maintenir une logique permettant dinvalider un identifiant dun client
aprs une certaine dure sans requte de sa part.
Pour toutes ces raisons, on prfre utiliser en gnral la notion de session fournie par ASP.NET.
900
Pour que vous puissiez y sauver vos donnes, chaque session maintient en interne un dictionnaire. Pour que notre exemple fonctionne, il faut qu un moment donn nous initialisons lentre "ItemsSelected" de ce dictionnaire avec une nouvelle liste dItem. Pour cela nous pouvons profiter de lvnement Session_Start auquel nous nous abonnons au sein du fichier
Global.asax :
Exemple 23-22 :
Global.asax
Web.Config
<?xml version="1.0"?>
<configuration
xmlns="http://schemas.microsoft.com/.NetConfiguration/v2.0">
<system.web>
<sessionState mode="InProc"/>
...
Note que vous pouvez spcifier au moyen de lattribut timeout la dure en minutes durant
laquelle une session peut rester inactive avant quASP.NET la dtruise (la valeur par dfaut est
de 20 minutes) :
<sessionState mode="InProc" timeout="10"/>
901
pouvez dcider de configurer ASP.NET pour que les identifiants de sessions soient stocks dans
lURI. Pour cela il faut aecter la valeur "UseUri" lattribut cookieless :
<sessionState mode="InProc" cookieless="UseUri"/>
Une URI contenant un identifiant de session ressemble alors ceci :
http://localhost/WebSite1/(S(e53uti455tgobvi2czsh4q45))/default.aspx
En plus de la valeur par dfaut "UseCookies" et de la valeur "UseUri", lattribut cookieless
peut prendre la valeur "AutoDetect". Ainsi ASP.NET utilise un cookie pour passer lidentifiant
de la session si le navigateur du client le permet. Sinon il se met en mode URI. Avec la valeur
"UseDeviceProfile" vous pouvez spcifier ASP.NET de choisir ou non le mode cookie selon
les paramtres de Device Profile dans le fichier Machine.Config.
902
Bien videmment, il faut avoir install au pralable les bases de donnes adquates sur le serveur SQL Server concern. Pour cela, il faut vous servir des scripts SQL InstallSqlState.sql ou
InstallPersistSqlState.sql selon que vous souhaitez garder ou non vos sessions aprs leurs
expirations. Ces scripts se trouvent dans le rpertoire dinstallation de .NET.
Web.Config
<?xml version="1.0"?>
<configuration
xmlns="http://schemas.microsoft.com/.NetConfiguration/v2.0">
<system.web>
<httpModules>
<remove name="Session" />
<add
name="Session"
type="MyNamespace.MyStateModuleType,MyAsm"/>
</httpModules>
<sessionState mode="Custom"
sessionIDManagerType="MyNamespace.MySessionIDType,MyAsm"
customProvider="MySessionStateProvider" >
<providers>
<add name="MySessionStateProvider"
type="MyNamespace.MySessionStateProviderType,MyAsm" />
</providers>
</sessionState>
...
La description plus avant de limplmentation de votre propre mcanisme de gestion de session
dpasse le cadre de cet ouvrage. Nous vous invitons consulter les articles Implementing a
Session-State Store Provider et Sample Session State Store Provider des MSDN.
903
aurons loccasion de le retrouver dans dautres domaines de fonctionnalits dASP.NET tels que
la possibilit de fournir son propre mcanisme de gestion dutilisateurs ou de rles. Les caractristiques du design pattern provider sont :
Possibilit de fournir plusieurs fournisseurs avec des lments <add> dans un lment <providers> qui est lui-mme contenu dans llment reprsentant le domaine de fonctionnalit (en loccurrence <sessionState>).
Chaque fournisseur est implment au moyen dune classe qui drive dune certaine classe
de base ou qui implmente une certaine interface.
Cette classe ainsi que lassemblage qui la contient son prcis au moyen dun attribut type.
Le domaine de fonctionnalit prsente un attribut qui prend le nom du fournisseur exploiter lexcution.
Signalons que linterface graphique web dASP.NET 2.0 prsente un onglet provider qui vous
permet dadministrer les fournisseurs par dfauts pour chaque domaine de fonctionnalits.
Un client demande une page qui nexiste pas ou quil nest pas autoris voir.
Un traitement applicatif du ct serveur lance une exception non rattrape cause dun
bug ou cause de donnes corrompues.
Dans tous les cas, les consquences de lerreur seront une indisponibilit du service pour le
client. En tant que dveloppeur, seul les deux derniers types derreurs vous incombent. Dans
le cas dune demande dune page qui ne peut tre satisfaite, vous avez la possibilit de rediriger
lutilisateur vers une page derreur spciale. Dans le cas dune exception, ASP.NET produit par
dfaut une page HTML contenant le fragment de code responsable de lexception ainsi que ltat
de la pile ce moment. Bien que ce comportement soit utile pour aider les dveloppeurs
rsoudre le problme, il est plutt gnant quun utilisateur visualise une telle page. Cela peut
aussi induire des problmes de scurit. Aussi, ASP.NET prsente plusieurs techniques pour
personnaliser la gestion des erreurs.
904
La balise <system.web>/<customErrors>
Vous pouvez indiquer ASP.NET la page qui doit tre retourne lutilisateur en cas dexception non rattrape. La valeur de lattribut defaultRedirect de la balise <customError> du
fichier de configuration doit tre le nom de cette page. En positionnant lattribut mode "RemoteOnly" vous pouvez faire en sorte que la page indique par defaultRedirect ne soit retourne
quaux clients distants. Ainsi, lors de vos tests en local vous ne vous priverez pas des informations
prcieuses de la page derreur retourne par dfaut par ASP.NET. Lattribut mode peut aussi
prendre la valeur "On" pour toujours rediriger le client vers la page prcise mme si ce dernier
est en local, ou "Off" pour dsactiver ce service. Enfin, vous pouvez aussi indiquer une page
retourner pour chaque erreur HTTP au moyen de sous balises <error> :
Web.Config
Exemple :
<?xml version="1.0"?>
<configuration
xmlns="http://schemas.microsoft.com/.NetConfiguration/v2.0">
<system.web>
<customErrors mode="On" defaultRedirect="WebFormError.aspx">
<error statusCode="403" redirect="MyError403.htm"/>
<error statusCode="404" redirect="MyError404.aspx"/>
</customErrors>
...
Lvnement Application_Error
Lvnement Error de la classe HttpApplication est dclench par ASP.NET lorsquune exception est non rattrape. Vous pouvez vous abonner cette vnement en fournissant une mthode Application_Error() dans le fichier Global.asax. Vous pouvez alors rcuprer lexception et construire une page rponse comme ceci :
Exemple 23-23 :
Global.asax
905
La proprit ErrorPage
Durant le traitement dune page vous pouvez tout moment positionner la proprit string
Page.ErrorPage{get;set;}. Vous prcisez ainsi programmatiquement la page vers laquelle
ASP.NET redirigera le client dans le cas dune exception non rattrape.
<%@ Page Language="C#" %>
<html xmlns="http://www.w3.org/1999/xhtml" >
<script language=C# runat="server">
void Btn_Click(Object sender, EventArgs e) {
this.ErrorPage = "WebFormError.aspx";
Msg.InnerText = "Vous avez s
electionn
e : " + Couleur.Value ;
throw new ApplicationException("Une erreur survient !") ;
}
</script>
...
Pour quASP.NET applique eectivement la redirection, il faut que lattribut mode soit positionn "On" dans la balise <customErrors> du fichier de configuration.
Cette technique est trs pratique puisquelle permet de prciser la page derreur en fonction du
contexte courant dexcution. Ainsi, si une page a plusieurs boutons, vous pouvez prciser une
page derreur pour chaque traitement de chaque bouton. En outre, lors dune exception non
rattrape lors du traitement dune page, ASP.NET ne dclenche lvnement HttpApplication.
Error que si la valeur de cette proprit est nulle.
Web.Config
<?xml version="1.0"?>
<configuration
xmlns="http://schemas.microsoft.com/.NetConfiguration/v2.0">
<system.web>
<trace enabled="true" localOnly="true" />
...
Vous avez alors accs aux traces en tapant une URL du type http://[machine]/[Racine]
/Trace.axd dans un navigateur. La page retourne contient un tableau de traces, une par
requte HTTP traite par le serveur. Ce tableau nest pas mis jour automatiquement et
vous devez recharger la page (par exemple en cliquant F5) pour avoir accs la liste des
dernires requtes. Vous avez la possibilit dacher une page dinformation pour chaque
trace en cliquant lURL View Details dune trace. Cette page est trs complte. Elle contient
des informations telles que larborescence des contrles serveurs impliqus lors du traitement,
ltat de la session et de lapplication, les informations contenues dans la requte POST (si lon
906
a aaire une requte POST) ltat des variables internes au serveur etc. Naturellement, il
nest pas souhaitable que de telles informations soient visibles par les clients. Aussi en gnral
vous positionnerez lattribut localOnly "true" pour empcher que le handler Trace.axd soit
exploitable distance. Nous vous invitons consulter les MSDN pour en savoir plus sur les
attributs de la balise <trace>.
Vous pouvez vous servir de ce systme pour inclure vos propres traces. Pour cela, il suffit de vous servir de lobjet de type System.Web.TraceContext accessible par la proprit
Page.Trace{get;} comme ceci :
<%@ Page Language="C#" %>
<html xmlns="http://www.w3.org/1999/xhtml" >
<script language=C# runat="server">
void Btn_Click(Object sender, EventArgs e) {
Msg.InnerText = "Vous avez s
electionn
e : " + Couleur.Value ;
Trace.Write("Ma trace write.");
Trace.Warn("Ma trace warn.");
}
</script>
...
Dans une page de trace spcifique une requte, les traces provoques par la mthode Warn()
sont aches en rouge, alors que les traces provoques par la mthode Write() sont aches
en noir.
907
En outre, la classe abstraite BufferedWebEventProvider reprsente une classe de base permettant de dvelopper des fournisseurs qui stockent en mmoire des vnements. Vous pouvez crer vos propres fournisseurs de traitement dvnements avec des classes drives de
WebEventProvider.
Vous pouvez vous servir de la sous section <eventMappings> de la section <healthMonitoring>
du fichier Web.Config pour prciser quels types dvnements ASP.NET doit grer. La sous section <providers> permet de dfinir les fournisseurs de traitement dvnements exploitables.
Enfin, la sous section <rules> contient les associations entre vnements et fournisseurs. Par
exemple, le fichier Web.Config suivant contraint linfrastructure ASP.NET loguer toutes les
exceptions non rattrapes dans les traces :
Exemple :
<?xml version="1.0"?>
<configuration
xmlns="http://schemas.microsoft.com/.NetConfiguration/v2.0">
<system.web>
<trace enabled="true"/>
<healthMonitoring enabled="true">
<providers>
<add name="TraceLogProvider"
type="System.Web.Management.TraceWebEventProvider,
System.Web,Version=2.0.3600.0,Culture=neutral,
PublicKeyToken=b03f5f7f11d50a3a"/>
</providers>
<eventMappings>
<add name="Erreurs"
type="System.Web.Management.WebErrorEvent,System.Web,
Version=2.0.3600.0,Culture=neutral,
PublicKeyToken=b03f5f7f11d50a3a" />
</eventMappings>
<rules>
<add name="Tracage des erreurs."
eventName="Erreurs"
provider="TraceLogProvider"/>
</rules>
</healthMonitoring>
...
Web.Config
908
909
Chaque type de contrle de validation implmente linterface IValidator qui expose notamment la proprit bool IsValid{get;set;}. La classe Page prsente aussi une proprit
bool IsValid{get;}. La validation ct serveur se fait lorsquASP.NET appelle la mthode
Page.Validate(). Cette tape positionne les proprits IsValid de la page et des contrles
de validation. Un peu plus loin dans ce chapitre, nous expliquerons que cet appel se fait
notamment aprs lappel Page_Load(). En consquence, soit vous appelez vous-mme la
mthode Page.Validate() durant lexcution de Page_Load(), soit vous ne testez aucune des
proprits IsValid durant cette excution.
Dans lexemple prcdent nous avons positionn la proprit Display du contrle de validation MyRequiredFieldValidator la valeur Dynamic. Cela signifie que lorsque ce contrle
a eectu sa validation avec succs, il noccupe pas despace sur la page. Cela permet de
traiter proprement le cas o un autre contrle de validation plac physiquement juste aprs
MyRequiredFieldValidator et se rapportant aussi MyTextBox choue dans sa validation. En
eet, le texte de ce deuxime contrle de validation sera alors physiquement plac directement
cot de MyTextBox.
En plus des contrles RequiredFieldValidator et CompareValidator que nous avons illustr, ASP.NET prsente les contrles de validation RangeValidator et RegularExpressionValidator. Ces quatre classes de contrles drivent de la classe BaseValidator.
Grce la proprit string ValidationExpression{get;set;} de la classe RegularExpressionValidator vous pouvez fournir une expression rgulire pour la validation dune
donne saisie. Les expressions rgulires sont dcrites en page 625.
En ce qui concerne la classe CompareValidator, nous attirons votre attention sur le fait que
les types disponibles sont aussi dans lnumration ValidationDataType. En outre, plutt
que de comparer la donne saisie une valeur fixe comme dans notre exemple, vous pouvez
la comparer celle dun autre contrle en prcisant ce dernier dans la proprit string
ControlToCompare{get;set;}.
Il est courant dutiliser un contrle de type RequiredFieldValidator en plus dun de ces trois
contrles de validation puisque aucun deux ne dtecte un problme si la donne saisie est vide.
ASP.NET 2.0 introduit la proprit bool SetFocusOnError{get;set;} prsente par chacun
des contrles de validation. Elle permet de prciser sil faut mettre le focus sur le contrle
valider lorsque sa donne est invalide. Laction de cette proprit seectue indpendamment
du fait que la vrification choue du ct client ou serveur.
910
Groupe de validation
ASP.NET 2.0 introduit la notion de groupe de validation. Cette possibilit rsout un problme
courant rencontr en ASP.NET 1.x. Par dfaut, chaque vnement tous les contrles de validation sont excuts. Or, il est courant que sur une mme page on puisse dclencher un vnement sans ncessairement devoir valider toutes les donnes saisies. Par exemple, une page de
formulaire peut contenir un bouton Chercher sur le web dont laction na rien voir avec les
donnes saisies du formulaire.
911
La classe ValidationSummary
Dans un formulaire avec plusieurs donnes saisir vous pouvez souhaiter runir tous les
messages derreurs des contrles de validation un seul endroit. Ceci est possible grce
la classe de contrle de validation ValidationSummary. Un tel contrle ache la liste des
messages derreur des contrles de validation de son groupe. Vous pouvez choisir la faon
dont les messages derreur sont lists grce la proprit ValidationSummaryDisplayMode
DisplayMode{get;set;}. En outre, plutt que dacher cette liste dans un lment <span>
de la page vous pouvez choisir de lacher dans un message box en positionnant les proprits
bool ShowMessageBox{get;set;} et bool ShowSummary{get;set;}.
912
Contrles utilisateurs
ASP.NET vous ore la possibilit de dfinir vos propres contrles serveur. Un tel contrle
est nomm contrle utilisateur. Un contrle utilisateur est en gnral dfini au moyen dun
fichier dextension .ascx dbutant par une directive <%@ Control>. Ce fichier sera compil par
ASP.NET en une classe drive de la classe Control. Le nom de cette classe est prcis par la
sous directive ClassName. Voici un exemple de contrle utilisateur trs simple :
MyUserCtrl.ascx
Exemple 23-27 :
<%@ Control Language=C# ClassName="MyUserCtrl" %>
<% Response.Write("HTML du contr
ole.") ; %>
Voici un exemple dune page cliente de ce contrle. On voit quune directive <%@ Register%>
est ncessaire pour importer le nom de la classe, le nom du fichier .ascx qui la dfinit ainsi
quun prfixe qui joue le rle dun espace de noms :
Exemple 23-28 :
MyUserCtrlClient.aspx
MyUserCtrl.ascx
MyUserCtrlClient.aspx
Contrles utilisateurs
913
Vous pouvez dfinir vous-mme la classe reprsentant un contrle utilisateur sans vous servir
dun fichier .ascx. Dans ce cas, chaque page cliente dun tel contrle doit inclure une sous directive namespace de la directive <@ Register> pour prciser lespace de noms contenant la classe :
Exemple 23-31 :
MyUserCtrl.cs
using System.Web.UI ;
namespace MyUserCtrls {
public class MyUserCtrl : Control {
private string m_Color ;
public string Color{ get{return m_Color;} set{m_Color = value;}}
private string m_Text ;
public string Text{ get{return m_Text ; } set{m_Text = value;}}
protected override void Render(HtmlTextWriter writer) {
writer.Write("<p><font color=\"" + m_Color +
"\">" + m_Text + "</font></p>") ;
}
}
}
Exemple 23-32 :
MyUserCtrlClient.aspx
MyUserCtrl.ascx
914
Exemple 23-34 :
MyUserCtrlClient.aspx
Exemple 23-35 :
MyUserCtrlClient.aspx
Contrles utilisateurs
915
Exemple 23-37 :
using System.Web.UI ;
namespace MyUserCtrls {
public class MyUserCtrl : Control {
public MyUserCtrl() {
ViewState["Color"] = string.Empty;
ViewState["Text"] = string.Empty;
}
public string Color {
get { return ViewState["Color"] as string ; }
set { ViewState["Color"] = value ; }
}
public string Text {
get { return ViewState["Text"] as string ; }
set { ViewState["Text"] = value ; }
}
protected override void Render(HtmlTextWriter writer) {
writer.Write("<p><font color=\"" + ViewState["Color"] +
"\">" + ViewState["Text"] + "</font></p>") ;
}
}
}
Exemple 23-38 :
MyUserCtrlClient.aspx
916
Notez la ncessit dinitialiser les entres du viewstate requises dans le constructeur de notre
contrle utilisateur. Nous pourrions rcrire nos accesseurs get comme ceci pour nous aranchir de cette contrainte :
Exemple 23-39 :
MyUserCtrl.cs
...
public string Color {
get {
string s = ViewState["Color"] as string;
return (s == null) ? string.Empty : s;
}
set { ViewState["Color"] = value ; }
}
public string Text {
get {
string s = ViewState["Text"] as string;
return (s == null) ? string.Empty : s;
}
set { ViewState["Text"] = value ; }
}
...
On peut aussi surcharger les mthodes LoadViewState() et SaveViewState() de la classe
Control pour maintenir ltat dun contrle utilisateur dans le viewstate. Voici notre contrle
MyUserCtrl rcrit avec cette technique :
Exemple 23-40 :
MyUserCtrl.cs
using System.Web.UI ;
namespace MyUserCtrls {
public class MyUserCtrl : Control {
private string m_Color ;
public string Color { get{return m_Color;} set{ m_Color=value;} }
private string m_Text ;
Contrles utilisateurs
917
Sachez que vous pouvez aussi vous servir du controlstate pour sauver vos tats entre les diffrentes requtes. Pour cela il faut rcrire les mthodes Control.LoadControlState() et
Control.SaveControlState() et en plus dclarer la page que ce contrle supporte le controlstate en appelant la mthode RegisterRequiresControlState() :
Exemple 23-41 :
using System.Web.UI ;
namespace MyUserCtrls {
public class MyUserCtrl : Control {
...
protected override void OnInit(System.EventArgs e) {
Page.RegisterRequiresControlState(this);
base.OnInit(e);
}
protected override object SaveControlState() {
...
}
protected override void LoadControlState(object _state) {
...
}
...
}
}
MyUserCtrl.cs
918
919
La prsence de la sous directive VaryByParam est obligatoire. Si vous ne souhaitez pas exploiter cette possibilit, il faut lui fournir la valeur "none".
Si vous souhaitez utiliser plusieurs paramtres il faut sparer leurs noms avec des virgules.
Dans ce cas, ASP.NET cachera une page pour chaque valeur du produit cartsien des paramtres spcifis.
Si vous souhaitez utiliser tous les paramtres, vous pouvez utiliser la valeur "*".
Lexemple suivant se base sur lExemple 23-3. Il montre comment mettre en cache deux versions
de cette page : une lorsque le client slectionne la valeur blanc, lautre lorsquil slectionne la
valeur noir.
920
Exemple 23-44 :
<%@ Page Language=C# %>
<%@ OutputCache Duration=60 VaryByParam="Couleur" %>
<html xmlns="http://www.w3.org/1999/xhtml" >
...
<body>
<% Response.Write("Page gen
er
ee `
a : " + DateTime.Now) ; %> <br/>
...
Couleur : <asp:dropdownlist id=Couleur runat="server">
<asp:listitem>blanc</asp:listitem>
<asp:listitem>noir</asp:listitem>
</asp:dropdownlist>
...
Attention : Comprenez bien quil ne faut pas exploiter cette possibilit avec des paramtres susceptibles de varier pour chaque client, tels que les noms des clients. Cela serait inecace puisque
le cache serait encombr avec des pages rarement demandes.
Il existe aussi une sous directive VaryByControl dont le comportement est assez proche de celui
de VaryByParam. En eet, en spcifiant lidentifiant dun contrle dans VaryByControl, plusieurs
versions dune page peuvent tre caches selon la valeur fournie par le client pour ce contrle.
Ainsi, lexemple suivant a un comportement similaire celui de lexemple prcdent (notez
que la prsence dune sous directive VaryByControl rend optionnelle la prsence dune sous
directive VaryByParam) :
Exemple 23-45 :
<%@ Page Language=C# %>
<%@ OutputCache Duration=60 VaryByControl="Couleur" %>
<script language=C# runat="server">
void Btn_Click(Object sender, EventArgs e) {
Msg.Text = "Vous avez selectionn
e : "+Couleur.SelectedItem.Value ;
}
</script>
<html xmlns="http://www.w3.org/1999/xhtml" >
<body>
<% Response.Write("Page gen
er
ee `
a : " + DateTime.Now) ; %> <br/>
...
Couleur : <asp:dropdownlist id=Couleur runat="server">
<asp:listitem>blanc</asp:listitem>
<asp:listitem>noir</asp:listitem>
</asp:dropdownlist>
...
<asp:label id=Msg runat="server"/>
...
Comprenez bien quASP.NET se base sur ltat du contrle fourni par le client et non ltat du
contrle quil assigne lors de la fabrication de la page. Une consquence est que cet exemple ne
fonctionnerait plus si nous avions crit VaryByControl="Msg".
Vous pouvez aussi utiliser la sous directive VaryByHeader pour signifier ASP.NET quil peut
mettre en cache plusieurs versions dune mme page selon le contenu des enttes des requtes
921
des clients. Par exemple, la langue dsire par le client est mis dans lentte Accept-Language.
Aussi, plusieurs versions de la page suivante peuvent tre caches, une pour chaque langue spcifie par un client :
Exemple 23-46 :
<%@ Page Language="C#" %>
<%@ OutputCache Duration="60"
VaryByParam="none" VaryByHeader="Accept-Language" %>
<html xmlns="http://www.w3.org/1999/xhtml" >
<script language=C# runat="server" >
void Page_Load(Object sender, EventArgs e) {
if (!IsPostBack) {
switch ( Request.UserLanguages[0] ) {
case "fr": Response.Write("Bonjour !") ; break ;
case "de": Response.Write("Guten Tag!") ; break ;
default: Response.Write("Hello!") ; break ;
}
}
}
</script>
<body>
<% Response.Write("Page gen
er
ee `
a : " + DateTime.Now) ; %>
</body>
</html>
Enfin, vous pouvez utiliser la sous directive VaryByCustom pour signifier ASP.NET quil peut
mettre en cache plusieurs versions dune mme page selon vos propres critres. Ces critres sont
en gnral relatifs au type de navigateur quutilise le client. Si vous dsirez cacher une version de
page par type/version de navigateur, vous pouvez aecter la valeur "Browser" cette sous directive. Sinon, il faut rcrire la mthode GetVaryByCustomString() de la classe HttpApplication
pour fabriquer vous-mme vos paramtres tester.
Lexemple suivant expose une page qui est gnre diremment selon que le navigateur client
supporte ou nom les chanes de caractres en gras. Nous fournissons donc notre propre paramtre SupportsBold. Pour chaque requte, ASP.NET peut connatre la valeur de ce paramtre
en le demandant notre code dans la mthode GetVaryByCustomString().
Exemple 23-47 :
<%@ Page Language="C#" %>
<%@ OutputCache Duration="60" VaryByParam="none"
VaryByCustom="SupportsBold" %>
<html xmlns="http://www.w3.org/1999/xhtml" >
<script language=C# runat="server" >
void Page_Load(Object sender, EventArgs e) {
if ( Request.Browser.SupportsBold )
Response.Write("<B> Bonjour ! </B>") ;
else
Response.Write("Bonjour ! ") ;
}
922
Exemple 23-48 :
Exemple 23-49 :
<%@ Control Language=C# ClassName="MyUserCtrl" %>
<%@ OutputCache Duration=60 VaryByParam=none %>
<br/>
<% Response.Write("Fragment gener
e `
a : " + DateTime.Now) ; %>
Par exemple la page suivante exploite le contrle MyUserCtrl. En excutant cet exemple, vous
voyez que la page est gnre chaque requte tandis que le fragment HTML gnr par le
contrle est cach et rutilis.
Exemple 23-50 :
MyUserCtrlClient.aspx
923
Dans ce contexte, vous pouvez stocker plusieurs fragments HTML gnrs par un mme
contrle utilisateur. En eet, ici aussi les sous directives VaryByParam, VaryByControl et
VaryByCustom sont utilisables au sein de la directive OutputCache. En revanche les sous directives VaryByHeader et Location ne sont pas utilisables dans ce contexte.
Si votre contrle prsente des proprits publiques, ASP.NET stockera automatiquement dans
le cache un fragment HTML gnr pour chaque valeur du produit cartsien de ces proprits.
Pour cela, il faut que ces valeurs soient fournies statiquement la cration du contrle. Par
exemple :
MyUserCtrl.ascx
Exemple 23-51 :
<%@ Control Language=C# ClassName=MyUserControl %>
<%@ OutputCache Duration=60 VaryByParam=none %>
<script runat="server">
private string m_Couleur ;
public string Couleur { get { return m_Couleur ; }
set { m_Couleur = value ; } }
</script>
<br/>
<% Response.Write("Couleur : " + Couleur) ; %>
<br/>
<% Response.Write("Fragment gener
e `
a : " + DateTime.Now) ; %>
MyUserCtrlClient.aspx
Exemple 23-52 :
...
Substitution post-cache
ASP.NET 2.0 vous permet de ne rgnrer que certaines parties dune page cache. Cette technique est connue sous le nom substitution post-cache. Elle permet dadresser la problmatique du
cache des pages semi variables dune manire complmentaire lutilisation de fragments
cachs. En revanche, la substitution post-cache nest pas exploitable partir de contrles utilisateurs ni partir de master page caches.
Pour exploiter la substitution post-cache, il vous sut davoir recours au contrle <asp:Substitution>. Ce dernier prend en paramtre nomm MethodName le nom dune mthode statique.
Cette dernire renvoie le fragment HTML variable sous forme dune chane de caractres. Par
exemple :
924
Exemple 23-53 :
<%@ Page Language=C# %>
<%@ OutputCache Duration=60 VaryByParam=none %>
<html xmlns="http://www.w3.org/1999/xhtml" >
<script runat="server">
public static string Fct( HttpContext ctx ) {
return "Substitution effectu
ee `
a : " + DateTime.Now;
}
</script>
<body>
<% Response.Write("Page g
en
er
ee `
a : " + DateTime.Now) ; %> <br/>
<asp:Substitution Id=Substitution1 MethodName=Fct runat="server" />
</body>
</html>
Vous pouvez aussi exploiter la substitution post-cache au moyen de la mthode HttpResponse.WriteSubstitution().
925
MyList.DataTextField = "Nom" ;
DataBind() ;
}
</script>
<html xmlns="http://www.w3.org/1999/xhtml" >
<body>
<form id="Form1" runat="server">
<asp:ListBox ID="MyList" runat="server"/>
</form>
</body>
</html>
La mise en cache de donnes se fait dune manire globale lapplication web. En consquence,
cette technique de mise en mmoire de donnes sapparente avec lutilisation du dictionnaire
global, instance de HttpApplicationState. Il faut noter cependant deux dirences :
Lors de la rcupration dune donne dans le cache, il nest pas certains quelle y soit encore.
Aussi, linstar de notre exemple, il faut toujours prvoir un mcanisme pour la rcuprer
autrement que par le cache pour prvoir le cas o elle ny est plus.
La philosophie de mise jour des donnes caches est dirente de celle des donnes globales. La copie stocke dans le cache dune donne ne doit pas tre modifie. Si la donne
vient changer alors quune copie est toujours dans le cache, il faut prvoir un mcanisme
qui dtruit la copie prime pour la remplacer avec une copie jour. Sachez quen interne,
les accs aux donnes stockes dans le cache sont automatiquement synchroniss avec lutilisation dune instance de ReaderWriterLock.
926
La classe CacheDependency prsente de nombreux constructeur vous permettant de communiquer les entits associes avec une dpendance.
En outre, la mthode Cache.Insert() prsente une surcharge qui prend en paramtre une valeur de lnumration CacheItemPriority. Cette valeur vous permet de donner plus ou moins
dimportance vos donnes caches. Bien entendu, lalgorithme de purge du cache dASP.NET
tient compte de cette priorit.
Enfin, cette surcharge de la mthode Cache.Insert() prend aussi en paramtre un dlgu de
type CacheItemRemovedCallback. Cela vous donne un moyen dtre averti par un appel de mthode lorsquune donne du cache est dtruite.
Web.config
<?xml version="1.0"?>
<configuration
xmlns="http://schemas.microsoft.com/.NetConfiguration/v2.0">
<connectionStrings>
<add name="CnxStrORGANISATION"
connectionString="Data Source=localhost;
Integrated Security=SSPI;Initial Catalog=ORGANISATION"
providerName="System.Data.SqlClient"/>
927
</connectionStrings>
<system.web>
<caching>
<sqlCacheDependency enabled="true">
<databases>
<add name="DbORGANISATION"
connectionStringName="CnxStrORGANISATION"
pollTime="500"/>
</databases>
</sqlCacheDependency>
</caching>
...
Si vous utilisez SQL Server 2005 comme SGBD sous-jacent, vous pouvez mme ne dpendre
que sur la mise jour de certaines lignes dune table. Vous prcisez ces lignes au moyen dune
commande SELECT comme ceci :
Exemple 23-57 :
...
dv.AllowNew = false ;
SqlConnection cnx = new SqlConnection(
"server = localhost ; uid=sa ; pwd = ; database = ORGANISATION");
SqlCommand cmd = new SqlCommand(
"SELECT * FROM EMPLOYES WHERE EmployeID=6 OR EmployeID=7", cnx);
Cache.Insert("Employes", dv,
new SqlCacheDependency(cmd));
} // end using dAdapter.
...
Cette possibilit de dpendance sur les donnes dune base est aussi exploitable lors de la mise
en cache dune page ou dun fragment de page au travers de la sous directive SqlDependency de
la directive <%@ OutputCache> :
<%@ OutputCache Duration=60 VaryByParam="none"
SqlDependency="DbORGANISATION:EMPLOYES" %>
En interne, le mcanisme mis en uvre pour dtecter les changements est compltement diffrent selon la version de votre SGBD SQL Server.
Dans le cas de SQL Server 2005, le mcanisme de Query Notification de ce SGBD est exploit.
En interne, une dpendance ADO.NET 2.0 est cre sur la table ou les lignes concernes.
Elle est dclenche par ce mcanisme ds quune mise jour a eu lieu.
Dans le cas dune version antrieure SQL Server 2005 (7 ou 2000), un mcanisme de polling
est mis en uvre du ct serveur ASP.NET. Un thread background est utilis rgulirement
pour dtecter si les donnes dune table ont t modifies. Lintervalle utilis est spcifi en
milli secondes par lattribut pollTime de llment caching\sqlCacheDependency\databases.
Sa valeur est de 5 secondes par dfaut. Une certaine prparation avec loutil en ligne
de commande aspnet_regsql.exe est ncessaire pour activer ce mcanisme. Il faut tout
dabord indiquer votre SQBD quelle base de donnes supporte ce mcanisme (ed pour
enable database) :
928
Sources de donnes
929
Sources de donnes
Lier programmatiquement contrles et sources de donnes
Dans la section prcdente, lExemple 23-54, page 924 montre comment exploiter un contrles
ASP.NET de type ListBox pour prsenter les donnes dune colonne contenue dans une
DataView. Pour cela, il sut de fournir la DataView au travers de la proprit object ListBox.DataSource{set;}, de fournir le nom de la colonne acher au travers de la proprit
string ListBox.DataTextField{set;}, puis dappeler la mthode DataBind() sur lobjet reprsentant la page courante. Cette mthode est prsente par la classe Control. Par consquent,
elle est aussi prsente par la classe Page et donc par toutes les classes reprsentant des pages,
ainsi que tous les contrles serveurs. Limplmentation par dfaut de cette mthode consiste
appeler la mthode DataBind sur chacun des contrles enfants.
Seuls certains types de contrles serveur tels que ListBox implmentent eectivement cette mthode. Cette implmentation consiste rcuprer dans une copie en interne toutes les donnes
de lobjet fourni laide de la proprit DataSource. Ces donnes sont alors exploites par la
mthode Render() du contrle qui les ache dans la page HTML.
La classe reprsentant une source de donnes doit implmenter une des interfaces :
IDataReader (implmente par la classe DbDataReader ainsi que ses classes drives).
Dans lExemple 23-54, les donnes sont dj contenues dans le DataSet sous-jacent lors de lappel la mthode DataBind. Cela implique quil existe trois versions de ces donnes : dans le
DataSet, dans la ListBox aprs lappel la mthode DataBind() puis dans la section __VIEWSTATE de la page HTML rendue. Si vous pouvez vous le permettre, vous pouvez dsactiver le stockage de donnes dun contrle dans la section __VIEWSTATE. En outre, certaines sources de donnes telles quun DataReader ne contiennent pas en interne les donnes. Elles ne reprsentent
quun moyen daccder aux donnes. Ainsi dans lexemple suivant il ny a plus quune seule
copie des donnes, celle cre dans lobjet ListBox lors de lappel la mthode DataBind().
Exemple 23-59 :
<%@ Page Language=C# %>
<%@ Import Namespace ="System.Data" %>
<%@ Import Namespace ="System.Data.Common" %>
<%@ Import Namespace ="System.Data.SqlClient" %>
<script runat="server">
protected void Page_Load(object sender, EventArgs e) {
string sCnx =
"server = localhost ; uid=sa ; pwd = ; database = ORGANISATION" ;
string sCmd = "SELECT * FROM EMPLOYES" ;
using (DbConnection cnx =new SqlConnection(sCnx)) {
using (DbCommand cmd =new SqlCommand(sCmd, cnx as SqlConnection)){
cnx.Open() ;
using ( DbDataReader rdr = cmd.ExecuteReader() ) {
930
Ainsi nous voyons apparatre trois pratiques permettant lamlioration des performances
lorsque lon travaille avec des sources de donnes.
Soit on limite le nombre de copies des donnes au strict minimum chaque chargement
de page linstar de lexemple prcdent.
Soit on ne charge les donnes qu la premire utilisation dune page par un utilisateur puis
on se sert du viewstate pour les stocker.
Soit on se permet de maintenir une copie en mmoire (au niveau du cache, de la session
ou de lapplication) linstar de lExemple 23-54.
Selon le contexte, lune ou lautre de ces pratiques peut donner les meilleures performances.
Sources de donnes
931
932
La classe SqlDataSource que nous venons de prsenter. Elle peut servir lexploitation de
tous types de SGBD puisquelle supporte les fournisseurs de donnes SQL Server, ODBC et
OleDB.
Les sources de donnes hirarchiques sont utilises pour exploiter les donnes des documents
XML. Les contrles serveur qui permettent lexploitation de telles sources sont XmlDataSource
et SiteMapDataSource. Ces classes drivent de la classe HierarchicalDataSourceControl. La
classe SiteMapDataSource permet dexploiter des documents XML spciaux reprsentant larborescence dun site. Elle est dcrite page 956. En outre, nous revenons sur la notion de sources
de donnes hirarchiques un peu plus loin en page 945.
Sources de donnes
933
Exemple 23-62 :
using System.Collections ;
using System.Collections.Generic ;
public class Employe {
private int m_EmployeID = -1 ;
private string m_Nom ;
private string m_Prenom ;
public int EmployeID{get{return m_EmployeID;}set{m_EmployeID=value;}}
public string Nom{
get{return m_Nom;}
set{m_Nom = value;} }
public string Prenom{get{return m_Prenom;}
set{m_Prenom = value;} }
}
public class Helper {
private static List<Employe> list = new List<Employe>() ;
static Helper() {
Employe emp = new Employe() ;
emp.EmployeID = 1 ; emp.Nom = "Lafleur" ; emp.Prenom = "L
eon" ;
list.Add(emp) ;
emp = new Employe() ;
emp.EmployeID = 2 ; emp.Nom = "Dupont" ; emp.Prenom = "Anne" ;
list.Add(emp) ;
}
static public ICollection GetData() { return list ; }
}
Exemple 23-63 :
<%@ Page Language=C# %>
<html xmlns="http://www.w3.org/1999/xhtml" >
<body>
<form id="Form2" runat="server">
<asp:ObjectDataSource ID="ObjDataSrc"
runat="server"
SelectMethod="GetData" TypeName="Helper"/>
<asp:ListBox ID="MyList"
DataSourceID="ObjDataSrc"
DataTextField="Nom" runat="server" />
</form>
</body>
</html>
934
935
</form>
</body>
</html>
Notez la faon dont les paramtres en entre de la mthode UpdateName() sont lis avec les
contrles OldName et NewName au sein dune section <UpdateParameters>. Naturellement, pour
la slection, linsertion et la destruction vous pouvez pareillement exploiter des sections <SelectParameters>, <InsertParameters> et <DeleteParameters>. Ces sections contiennent les
dclarations de contrles serveurs permettant de rcuprer les paramtres en entre du traitement. Par exemple le contrle ControlParameter permet de rcuprer une valeur partir dune
proprit dun autre contrle tandis quun contrle FormParameter permet de rcuprer une
valeur partir du contenu dune TextBox. Tous ces contrles drivent du contrle Parameter.
Aussi vous pouvez trouver la liste exhaustive en consultant larticle Parameter Hierarchy des
MSDN.
DataBoundControl
HierarchicalDataBoundControl
Menu
ListControl
TreeView
BulletedList
CompositeDataBoundControl
AdRotator
CheckBoxList
DropDownList
DetailsView
ListBox
FormView
RadioButtonList
GridView
Classe abstraite
Classe non abstraite
936
Nous allons surtout nous intresser aux contrles GridView, DetailsView et FormView, particulirement adapts la prsentation et ldition de donnes dune source de donnes plate.
Le contrle GridView
linstar de son prdcesseur le contrle DataGrid, le contrle serveur GridView permet de
prsenter et dditer des donnes dans un tableau. En plus de multiples amliorations fonctionnelles, la puissance de ce contrle tient surtout dans son ecacit et sa simplicit travailler
avec des sources de donnes. Voici pour preuve, le code dune page permettant de remplir une
GridView avec les donnes dune table dune base de donnes :
Exemple 23-66 :
<%@ Page Language=C# %>
<html xmlns="http://www.w3.org/1999/xhtml" >
<body>
<form id="Form1" runat="server">
<asp:SqlDataSource ID="DataSrc" runat="server" ConnectionString=
"server = localhost ; uid=sa ; pwd = ; database = ORGANISATION"
SelectCommand="SELECT * FROM EMPLOYES" />
<asp:GridView ID="Grid" DataSourceID="DataSrc" runat="server" />
</form>
</body>
</html>
937
La proprit bool AllowSorting permet de dcider si vous souhaitez que lutilisateur puisse
trier les donnes en fonction du contenu dune colonne. Lactivation de cette possibilit fait
en sorte que les enttes des colonnes soient des hyperliens. Le trie se fait du ct serveur et est
retourn au client lorsquune telle entte est clique.
Vous pouvez trs simplement diter les donnes dune ligne, voire dtruire une ligne, en vous
servant dune GridView. Pour cela, il sut que la source de donne sous-jacente supporte
ces oprations et que vous positionniez true les proprits AutoGenerateEditButton et
AutoGenerateDeleteButton. Lexemple suivant illustre ceci :
Exemple 23-67 :
<%@ Page Language=C# %>
<html xmlns="http://www.w3.org/1999/xhtml" >
<body>
<form id="Form1" runat="server">
<asp:SqlDataSource ID="DataSrc" runat="server" ConnectionString=
"server = localhost ; uid=sa ; pwd = ; database = ORGANISATION"
SelectCommand="SELECT * FROM EMPLOYES"
UpdateCommand= "UPDATE EMPLOYES SET
DepID=@DepID,Nom=@Nom,Pr
enom=@Pr
enom,T
el=@T
el
WHERE EmployeId=@Original_EmployeId"
DeleteCommand="DELETE EMPLOYES WHERE EmployeId=@Original_EmployeId"/>
<asp:GridView ID="MyGrid" DataSourceID="DataSrc" runat="server"
AutoGenerateEditButton="true"
AutoGenerateDeleteButton="true"
DataKeyNames="EmployeId"/>
</form>
</body>
</html>
938
Le contrle GridView prsente des vnements tels que RowDeleting ou RowDeleted permettant
dappeler une mthode ct serveur juste avant ou juste aprs une modification. Linsertion de
lignes ncessite le recours un contrle DetailsView aussi nous dcrivons cette opration dans
la prochaine section.
Vous pouvez agir sur la faon dont les donnes sont aches dans la grille en modifiant les
colonnes aches. ASP.NET 2.0 fournit plusieurs styles de colonnes prdfinies grce aux types
drivant de la classe DataControlField tels que ButtonField ou BoundField. Cependant, en
utilisant le type TemplateField vous pouvez dfinir exactement le code HTML contenu dans
une cellule. Ceci est illustr par lexemple suivant qui ache une grille contenant :
Une colonne de type ButtonField contenant des boutons ayant le nom de lemploy.
Lorsquun tel bouton est cliqu, la mthode Grid_RowCommand() et invoque et change le
contenu du label Msg.
Une colonne de type TemplateField qui contient des CheckBox. Une CheckBox est ticke
seulement si le nom de lemploy correspondant contient la lettre o .
Exemple 23-68 :
<%@ Page Language=C# %>
<script language=C# runat="server">
void Grid_RowCommand(Object sender, GridViewCommandEventArgs e) {
if (e.CommandName == "Hello") {
int index = Convert.ToInt32(e.CommandArgument);
GridViewRow selectedRow = Grid.Rows[index];
TableCell cell = selectedRow.Cells[2];
string nom = cell.Text;
Msg.Text = "Vous aves s
electionn
e " + nom + ".";
}
}
</script>
<html xmlns="http://www.w3.org/1999/xhtml" >
<body>
<form id="Form1" runat="server">
<asp:SqlDataSource ID="DataSrc" runat="server" ConnectionString=
"server = localhost ; uid=sa ; pwd ; database = ORGANISATION"
SelectCommand="SELECT * FROM EMPLOYES" />
<asp:GridView ID="Grid" DataSourceID="DataSrc" runat="server"
AutoGenerateColumns=False OnRowCommand="Grid_RowCommand" >
<Columns>
<asp:ButtonField DataTextField="Nom" ButtonType=Button
HeaderText="Cliquez svp..." CommandName="Hello"/>
<asp:TemplateField HeaderText="Le nom contient la lettre o">
<ItemTemplate>
--<asp:CheckBox runat="server" Enabled=False
Checked=<%# ((string)Eval("Nom")).Contains("o") %> />-</ItemTemplate>
</asp:TemplateField>
939
Les templates
Dans lexemple prcdent, peut tre avez-vous remarqu ce bloc qui ressemble un bloc de
restitution de code :
<%# ((string)Eval("Nom")).Contains("o") %>
On nomme un tel bloc un template. Ce template se remplace par une valeur false ou true selon
que le nom de lemploy de la ligne courante contient un o ou non. Les templates constituent un mcanisme permettant de dcider comment un contenu est ach. Les contrles
serveurs de prsentation de donnes qui utilisent les templates sont les contrles GridView,
DetailsView, FormView, LoginView, TreeView, Repeater, DataList et DataGrid. Contrairement
aux blocs de restitution de code, les templates sont valus au moment o la liaison de donnes
se fait (i.e lors de lappel DataBind() sur le contrle). Chaque template est valu une fois par
ligne.
En interne, chaque template induit un contrle enfant du contrle de prsentation dans lequel
il est dfinit. Ces contrles enfants sont de type DataBoundLiteralControl. Lors de la compilation, ASP.NET ajoute la classe reprsentant la page courante une mthode qui est abonne
lvnement DataBinding dun tel contrle. Cet vnement est dclench pour chaque ligne
rcupre durant lappel la mthode DataBind() du contrle de prsentation parent. Voici la
mthode gnre pour notre exemple :
public void __DataBinding__control6(object sender, EventArgs e) {
CheckBox box1 = (CheckBox)sender ;
IDataItemContainer container1 =
(IDataItemContainer) box1.BindingContainer ;
box1.Checked = ( (string) DataBinder.Eval(
940
941
Le contrle DetailsView
Le contrle DetailsView permet de prsenter une vue dtaille de chaque ligne. Comme illustr
par lexemple suivant, il est ais dutiliser une DetailsView pour naviguer dans les direntes
lignes dune source de donnes plate :
Exemple 23-70 :
<%@ Page Language=C# %>
<html xmlns="http://www.w3.org/1999/xhtml" >
<body>
<form id="Form1" runat="server">
<asp:SqlDataSource ID="DataSrc" runat="server" ConnectionString=
"server = localhost ; uid=sa ; pwd = ; database = ORGANISATION"
SelectCommand="SELECT * FROM EMPLOYES" />
<asp:DetailsView ID="Details" DataSourceID="DataSrc" runat="server"
AllowPaging="True" >
<PagerSettings Mode="NextPreviousFirstLast" />
</asp:DetailsView>
</form>
</body>
</html>
Le type de vue du contrle DetailsView est complmentaire celui de contrle GridView. Aussi,
il est courant dutiliser une symbiose de ces deux contrles pour obtenir ce que lon appelle une
vue matresse dtaille dune source de donnes plate (master details view en anglais). Pour obtenir
une telle vue, il est ncessaire dactiver la slection dune ligne sur la GridView au moyen de la
proprit AutoGenerateSelectButton. En outre, il faut dfinir deux sources de donnes : une
942
exploite par la GridView qui prsente lensemble des lignes et une exploite par la DetailsView
qui prsente la ligne couramment slectionne. Cette deuxime source de donnes utilise la
proprit FilterExpression pour dterminer quelle est la ligne couramment slectionne :
Exemple 23-71 :
<%@ Page Language=C# %>
<html xmlns="http://www.w3.org/1999/xhtml" >
<body>
<form id="Form1" runat="server">
<asp:SqlDataSource ID="DataSrc1" runat="server" ConnectionString=
"server = localhost ; uid=sa ; pwd = ; database = ORGANISATION"
SelectCommand="SELECT * FROM EMPLOYES" />
<asp:GridView ID="MyGrid" DataSourceID="DataSrc1" runat="server"
DataKeyNames="EmployeId" SelectedIndex="0"
AutoGenerateSelectButton=true />
<asp:SqlDataSource ID="DataSrc2" runat="server" ConnectionString=
"server = localhost ; uid=sa ; pwd = ; database = ORGANISATION"
SelectCommand="SELECT * FROM EMPLOYES"
FilterExpression="EmployeId={0}">
<FilterParameters>
<asp:ControlParameter Name="EmployeId" ControlID="MyGrid"
PropertyName="SelectedValue" />
</FilterParameters>
</asp:SqlDataSource>
<asp:DetailsView ID="MyDetails" DataSourceID="DataSrc2"
runat="server" DataKeyNames="EmployeId" />
</form>
</body>
</html>
Une vue matresse dtaille reprsente lalternative optimale pour ldition et linsertion des
donnes de chaque ligne. Comme le montre lexemple suivant, pour obtenir une telle vue il
sut dactiver ces possibilits dinsertion et ddition sur la source de donnes de la DetailsView
ainsi que sur la DetailsView elle-mme :
943
944
Le contrle FormView
Le contrle serveur FormView est similaire au contrle DetailsView ceci prs quil est particulirement adapt la personnalisation de la prsentation et de ldition des donnes dune ligne.
Contrairement au contrle DetailsView, le contrle FormView ne fournit pas de code par dfaut pour prsenter les donnes. Il est ncessaire de fournir votre propre code dans des sections
<ItemTemplate>, <EditItemTemplate> et <InsertItemTemplate>. Comme le montre lexemple
suivant, lutilisation des templates devient particulirement pratique au sein de ces sections :
945
Exemple 23-73 :
<%@ Page Language=C# %>
<html xmlns="http://www.w3.org/1999/xhtml" >
<body>
<form id="Form1" runat="server">
<asp:SqlDataSource ID="DataSrc" runat="server" ConnectionString=
"server = localhost ; uid=sa ; pwd = ; database = ORGANISATION"
SelectCommand="SELECT EmployeId, Nom, T
el FROM EMPLOYES"
UpdateCommand=
"UPDATE EMPLOYES SET Tel=@T
el WHERE EmployeId=@Original_EmployeId"/>
<asp:FormView ID="Details" DataSourceID="DataSrc" runat="server"
DataKeyNames="EmployeId" AllowPaging="True" >
<ItemTemplate>
Nom : <%# Eval("Nom") %> <br/>
Telephone : <%# Eval("T
el") %> <br/>
<asp:Button ID="BtnEdit" runat="server" CommandName="Edit"
Text="Edition du numero de t
el
ephone"/>
</ItemTemplate>
<EditItemTemplate>
Nom : <%# Eval("Nom") %> <br/>
Telephone : <asp:TextBox ID="EditPhone" runat="server"
Text=<%# Bind("T
el") %> /> <br/>
<asp:Button ID="BtnUpdate" runat="server" CommandName="Update"
Text="Mise `a jour du numero de t
el
ephone."/>
<asp:Button ID="BtnCancel" runat="server" CommandName="Cancel"
Text="Annuler la mise `
a jour."/>
</EditItemTemplate>
</asp:FormView>
</form>
</body>
</html>
946
Nous portons plutt notre attention sur le fait quune source de donnes hirarchique peut
tre exploite par des contrles de prsentation originalement destins tre lis des sources
de donnes plates. Dans ce cas, un tel contrle prsente le premier niveau de hirarchie des
donnes. Lavantage est que vous pouvez utiliser un autre contrle de prsentation de donnes
imbriqu dans le premier afin daccder au second niveau de hirarchie. Par exemple considrez
le document XML suivant qui prsente des livres, et pour chaque livre, la liste des chapitres :
Exemple 23-74 :
books.xml
Master pages
947
Master pages
Master page et page de contenu
Pour garantir la cohrence visuelle dun site, il est courant que toutes ses pages prsentent des
parties similaires telles que lentte ou le bas de page. En ASP.NET 1.x, il faut avoir recours aux
contrles utilisateurs pour pouvoir rutiliser des parties similaires. Cette approche a cependant
linconvnient dobliger le dveloppeur crer plusieurs contrles utilisateur, par exemple un
pour lentte et un pour le bas de page. En outre, il faut copier la dclaration de chacun de ces
contrles dans chaque page du site, ce qui entrane des problmes de mise jour.
ASP.NET 2.0 prsente la notion de master page qui permet de rutiliser simplement un modle
de design de page sur les pages dun site. Lapplication dun tel modle est illustre par les deux
pages de la figure suivante :
Un modle de design de page (i.e une master page) se prsente sous la forme dun fichier dextension .master qui commence par une directive <%@ master%>. Le contenu dun tel fichier
est similaire celui dune page .aspx au dtail prs quil peut contenir des contrles de type
<asp:ContentPlaceHolder>. Un tel contrle dfinit un emplacement o les pages du site pourront placer leur propre contenu. Voici le code de la master page de lexemple de notre figure :
948
Exemple 23-76 :
MyMasterPage.master
Master pages
Exemple 23-77 :
949
Default.aspx
950
MyMasterPageNested.master
Default.aspx
%>
Master pages
951
Font-Size="XX-Large">Contenu de Default.aspx</asp:label>
</asp:content>
Notez que Visual Studio 2005 ne sait pas passer en mode design de page ds lors que vous utilisez
des master pages encapsules.
Web.Config
<?xml version="1.0"?>
<configuration
xmlns="http://schemas.microsoft.com/.NetConfiguration/v2.0">
<system.web>
<pages masterPageFile="~/MyMasterPage.master"/>
...
En plus de ces deux techniques dassociation statique (i.e la compilation) de master page
des pages de contenu vous pouvez dynamiquement (i.e lexcution) choisir avec quelle master
page une page de contenu doit tre rendue. Pour cela il sut de positionner la proprit string
MasterPageFile{get;set;} dune page de contenu dans lvnement OnPreInit().
Exemple 23-80 :
Default.aspx
952
MyMasterPage.master
Default.aspx
953
Dans le cas o la master page est dfinie statiquement dans une sous directive MasterPageFile
de la page de contenu nous navons pas besoin de la souplesse des liens tardifs. En eet, dans
ce cas nous connaissons ds la compilation le type de master page utilis. Aussi, vous pouvez
utiliser une directive <%@ MasterType%> dans la page de contenu afin de spcifier au compilateur quel type de master page vous souhaitez utiliser. Cela peut se faire au moyen dune sous
directive VirtualPath qui spcifie le nom du fichier contenant la master page ou au moyen
dune sous directive TypeName qui nomme directement la classe reprsentant la master page.
Le compilateur fera alors en sorte que le type de la proprit Master{get;set;} de la classe
reprsentant la page de contenu soit le bon. Vous pourrez alors utiliser des liens prcoces sur
cette master page.
Pour exploiter au mieux cette possibilit, il est conseill de fournir des proprits publiques sur
la classe de master page donnant accs aux contrles susceptibles dtre exploits partir de la
page de contenu ( linstar de ce que nous avons fait avec la proprit CellHeader dfinie dans
notre master page) :
Exemple 23-83 :
Default.aspx
954
nalisables des contrles serveurs ne sont plus prsentes puisquelles sont contenues dans
les fichiers ressources associs la page. En outre, chaque contrle contient un attribut
meta:resourcekey="XXX" o XXX dsigne une chane de caractres utilise pour identifier le
contrle au niveau des fichiers ressources.
La valeur de la proprit CurrentUICulture du thread qui traite une requte web est dtermine
durant lappel la mthode virtuelle Page.InitializeCulture() au dbut du cycle de vie de la
requte. Par dfaut, la culture du client est envoye par le navigateur chaque requte et elle
adopte par ASP.NET 2.0 pour cette requte. Vous pouvez changer ce mode de fonctionnement
en rcrivant cette mthode InitializeCulture().
Pour tester une page selon direntes cultures soit :
Vous modifiez la culture par dfaut de votre navigateur. Sous Internet Explorer il sut daller
dans le menu Outils Options internet Gnral Langues fournir une liste de culture, cellesci seront traites par ordre de priorit par ASP.NET.
Vous pouvez fournir une culture par dfaut toutes les pages situes (rcursivement) dans un
rpertoire au moyen des attributs culture et uiCulture de la section <globalization> du fichier
Web.Config. Cette culture sera choisie pour traiter une requte qui ne spcifie pas de culture ou
pour traiter une requte dont la culture nest pas supporte.
Enfin, notez que vous pouvez placer des fichiers de ressources globaux votre application web
dans le rpertoire /App_GlobalResources.
955
App.SiteMap
956
Exemple :
<?xml version="1.0"?>
<configuration
xmlns="http://schemas.microsoft.com/.NetConfiguration/v2.0">
<system.web>
<pages masterPageFile="~/MyMasterPage.master"/>
<siteMap defaultProvider="MySiteMapProvider" enabled="true"
<providers>
<add type="System.Web.XmlSiteMapProvider"
name="MySiteMapProvider"
siteMapFile= "App.SiteMap"/>
</providers>
</siteMap>
...
>
On voit apparatre la notion de fournisseur dorganisation de site (SiteMap provider en anglais). Vous configurez un tel fournisseur au moyen dune classe drivant de System.Web.
SiteMapProvider et dune source de donne contenant lorganisation du site. Ici, nous nous
servons de la classe System.Web.XmlSiteMapProvider et de notre fichier App.SiteMap. Il sagit
bien dune utilisation du design pattern provider prsent en page 902.
Un mme site peut contenir plusieurs fournisseurs dorganisation de site. De plus, lorganisation
dun site peut tre stocke dans plusieurs fichiers .SiteMap. Ces deux possibilits font lobjet de
larticle How to: Configure Multiple Site Maps and Site-Map Provider des MSDN.
Vous pouvez crer votre propre type de fournisseur dorganisation de site au moyen dune classe
propritaire drive de SiteMapProvider. Par exemple, vous pouvez souhaiter stocker lorganisation de votre site dans une base de donnes ou bien intervenir dynamiquement sur larborescence prsente au client. La fabrication dune telle classe est expose dans larticle SiteMapProvider Class (System.Web) des MSDN.
Une fois que vous avez configur un fournisseur dorganisation de site, vous devez ajouter
chaque page susceptible de contenir un contrle daide la navigation, un contrle de type
<asp:SiteMapDataSource> lie ce fournisseur. Vous pouvez alors vous servir de contrles de
type <asp:Menu> <asp:TreeView> et <asp:SiteMapPath> pour aider la navigation. En plaant
de tels contrles dans des master pages, on peut facilement dvelopper un modle puissant de
design de page dun site. Bien entendu, chacun de ces contrles prsente de nombreuses proprits permettant de modifier leur rendu graphique.
Lexemple suivant illustre lutilisation dun contrle <asp:Menu> au sein dune master page :
Exemple 23-85 :
MyMasterPage.master
Scurit
957
<tr>
<td width="30%" bgcolor="yellow">
<asp:SiteMapDataSource ID="MySiteMapDataSource"
runat="server" />
<asp:Menu ID="Menu1" runat="server" Orientation="Horizontal"
DataSourceID="MySiteMapDataSource" >
</asp:Menu>
</td>
<td align="center">
<asp:contentplaceholder id="MyContentPH" runat="Server">
</asp:contentplaceholder>
</td>
</tr>
</table>
</form></body></html>
Scurit
Il est conseill davoir assimil les concepts prsents dans le chapitre 6 consacr la scurit
avant daborder cette section.
La gestion de la scurit est particulirement importante dans le cas dapplications web. En eet,
lorsque lapplication est accessible partir dinternet, il faut absolument quelle interdise laccs
aux ressources critiques du serveur aux utilisateurs non autoriss. Il faut donc un mcanisme
dauthentification, permettant didentifier lentit lorigine dune requte. Il faut aussi un
mcanisme dautorisation permettant de spcifier quelles sont les ressources accessibles durant
lexcution dune requte, en fonction de lidentit lorigine de la requte.
Le mode dauthentification Connexion anonyme nore aucun contrle. Il implique que tous
les clients peuvent accder lapplication. Dans le cas o ce mode est activ avec dautres
modes dauthentification IIS, tout se passera comme si seul le mode dauthentification anonyme tait activ. Vous avez la possibilit de fournir un compte Windows qui reprsente les
utilisateurs anonymes. Ce compte doit obligatoirement avoir un mot de passe non vide.
Le mode dAuthentification Digest envoie une valeur de hachage du mot de passe dun utilisateur Windows plutt que le mot de passe lui-mme. Cette technique a donc lavantage
958
Le mode dAuthentification de base nest pas vraiment scuris. En eet, le nom de lutilisateur ainsi que son mot de passe sont envoys au serveur sous une forme encode et non pas
chire. La technique dencodage base-64 est utilise. Vous pouvez cependant avoir recours
au mode HTTPS afin de crypter ces donnes sensibles.
Le mode dAuthentification intgre Windows utilise un systme dchange de donnes chiffres avec le client. Ce mode nest support que par les clients .NET et le navigateur Internet
Explorer.
Web.Config
<?xml version="1.0"?>
<configuration
xmlns="http://schemas.microsoft.com/.NetConfiguration/v2.0">
<system.web>
<identity impersonate = "true" />
...
En page 209, nous expliquons que le mcanisme demprunt didentit permet daecter un utilisateur Windows authentifi au contexte de scurit dun thread non gr. En loccurrence il sagit
de lutilisateur Windows authentifi par IIS et du thread non gr sous-jacent au thread gr qui
Scurit
959
sert la requte dans le processus dASP.NET. Rappelons alors que Windows vrifie la validit de
tout accs aux ressources Windows (fichiers, rpertoires etc) de la part dun thread. Pour cela, il
se base sur le compte utilisateur associ au thread et au descripteur de scurit de la ressource
(voir page 211). Rappelons que par dfaut, le processus dASP.NET et chacun de ses threads
sexcutent dans le contexte de lutilisateur Windows spcifi dans la section <processModel> du
fichier de configuration Machine.Config (voir page 890).
Vous pouvez aussi contrler programmatiquement lappartenance un rle Windows de lutilisateur Windows associ au thread courant comme ceci :
...
IPrincipal p = Thread.CurrentPrincipal ;
if( p.IsInRole(@"BUILTIN\Administrators") ){
// Effectuer ici les operations r
eserv
ees aux administrateurs
}
...
ASP.NET permet de simplifier considrablement cette opration de vrification. En eet,
dans le fichier de configuration de lapplication, vous pouvez dfinir une liste de rgles daccs
quASP.NET doit appliquer chaque requte. Concrtement une rgle est une permission
(allow) ou une interdiction (deny) deectuer la requte, en fonction du principal ou du rle
jou par le principal. Par exemple pour nautoriser que les utilisateurs Mathieu et Julien et les
administrateurs locaux eectuer des requtes, il faut crire ceci dans le fichier de configuration :
Web.Config
Exemple :
<?xml version="1.0"?>
<configuration
xmlns="http://schemas.microsoft.com/.NetConfiguration/v2.0">
<system.web>
<authentication mode="Windows" />
<authorization>
<allow users = "Mathieu,Julien" />
<allow roles = "BUILTIN\Administrators" />
<deny users = "*" />
</authorization>
...
Le nom dutilisateur "*" reprsente tous les utilisateurs. Le nom dutilisateur "?" reprsente
lutilisateur anonyme.
Pour chaque rgle, vous pouvez aussi spcifier un verbe qui prcise la mthode HTTP autorise
ou interdite. Par exemple, pour autoriser tous les utilisateurs utiliser la mthode HTTP-GET,
et seulement les administrateurs locaux utiliser la mthode HTTP-POST il faut crire :
Web.Config
Exemple :
...
<authorization>
<allow users = "*" verb = "GET" />
<allow roles = "BUILTIN\Administrators" verb = "POST" />
<deny users = "*" />
960
Comme vous le voyez, lalgorithme dapplication de ces rgles est trs simple : la liste des rgles
est parcourue linairement et ds quune rgle est satisfaite par le principal courant, elle est
applique. Notez que la liste des rgles est en fait la concatnation des listes de rgles spcifies
dans chaque fichier de configuration. Donc, cette liste commence par les rgles du fichier de
configuration du sous rpertoire courant de lapplication et se finit par les rgles du fichier de
configuration de la machine.
Scurit
Exemple 23-86 :
961
Login.aspx
Web.Config
<?xml version="1.0"?>
<?xml version="1.0"?>
<configuration
xmlns="http://schemas.microsoft.com/.NetConfiguration/v2.0">
<system.web>
<authorization>
<deny users="?"/>
</authorization>
<authentication mode="Forms">
<forms loginUrl="Login.aspx" />
</authentication>
...
Le simple appel la mthode RedirectFromLoginPage() sut ASP.NET pour crer un cookie,
le communiquer au client et le rediriger vers la page dorigine. On saperoit quASP.NET stocke
la page vers laquelle le client doit tre redirig dans lURL utilise lors de la premire redirection
vers la page dauthentification :
http://localhost:1968/MyWebSite/...
... Login.aspx?ReturnUrl=%2fMyWebSite%2fdefault.aspx
962
La section <form> prsente plusieurs attributs pour nommer le cookie, protger les informations qui y sont contenues et lui assigner une dure de validit.
linstar de ce que nous avons vu en page 901 concernant la gestion de session sans cookies, la
gestion de lauthentification peut se faire aussi sans cookies. Tout comme llment <sessionState> llment <forms> prsente un attribut nomm cookieless. Vous pouvez vous servir de
cet attribut pour contraindre ou laisser le choix ASP.NET dutiliser lURI pour stocker la cl
reprsentant la preuve que lutilisateur est authentifi. Voici quoi ressemble une telle URI :
http://localhost:1968/MyWebSite/(F(Xd6tEmGNyNKdZ-Df9B69q4F2JUXxLd7fk8QzQt1qhqWk1FLyUX2_gw31a5XePl8YdR-YzYxEm6kuareBQqgjw2))/default.aspx
Soyez conscient que dans cet exemple, le mot de passe nest pas crypt dans la requte dauthentification. Pour viter ce problme majeur de confidentialit, il faut se servir de HTTPS pour
encrypter cette requte. Comme nous venons de le voir, les autres requtes nont pas besoin
dtre cryptes puisquelles ne contiennent pas le mot de passe.
Nous allons maintenant nous intresser aux possibilits ajoutes par ASP.NET 2.0 au niveau de
la scurit. Vous pouvez maintenant grer les utilisateurs et leur appartenance des rles dune
manire standard au moyen dune base de donnes. En outre, plusieurs nouveaux contrles
serveurs viennent grandement simplifier le dveloppement dapplication ASP.NET supportant
lauthentification.
Login.aspx
Web.Config
Scurit
963
<connectionStrings>
<add name="MyLocalSqlServer"
connectionString="Data Source=localhost;Integrated
Security=SSPI ;Initial Catalog=MyWebSiteUsers;" />
</connectionStrings>
<system.web>
<membership defaultProvider="MyMembershipSqlProvider" >
<providers>
<add
name="MyMembershipSqlProvider"
type="System.Web.Security.SqlMembershipProvider,
System.Web, Version=2.0.0.0, Culture=neutral,
PublicKeyToken=b03f5f7f11d50a3a"
connectionStringName="MyLocalSqlServer" />
</providers>
</membership>
...
Les donnes relatives aux utilisateurs de ce site sont stockes dans une base de donnes nomme
MyWebSiteUsers. Cette base se trouve dans un SGBD local de type SQL Server. Pour construire
une telle base de donnes, il sut dutiliser loutil aspnet_regsql.exe dcrit en page :
>aspnet_regsql.exe -S <host> -U <usr> -P <pwd> -d <database> -ed
Puisque nos donnes utilisateurs sont stockes dans une base de donnes relationnelle,
nous avons prcis ASP.NET dutiliser le fournisseur dutilisateurs System.Web.Security.
SqlMembershipProvider. Un fournisseur dutilisateurs est une classe qui drive de la classe
System.Web.Security.MembershipProvider. Vous pouvez crer votre propre fournisseur en
dveloppant une telle classe. Notez quASP.NET 2.0 fournit aussi par dfaut le fournisseur
System.Web.Security.ActiveDirectoryMembershipProvider. Il sagit bien dune utilisation
du design pattern provider prsent en page 902.
Dans notre Exemple 23-87, la mthode statique bool ValidateUser(string username, string
password) de la classe System.Web.Security.Membership est capable daller vrifier si un utilisateur existe en exploitant le fournisseur dutilisateurs de lapplication. Cette classe prsente
dautres mthodes statiques dont la signification et lutilisation sont particulirement intuitives :
MembershipUser CreateUser(string username, string password) ;
MembershipUser CreateUser(string username, string password, string email) ;
bool DeleteUser(string username) ;
bool DeleteUser(string username, bool deleteAllRelatedData) ;
MembershipUserCollection FindUsersByEmail(string emailToMatch) ;
MembershipUserCollection FindUsersByName(string usernameToMatch) ;
string GeneratePassword(int length, int numOfNonAlphanumericCharacters) ;
MembershipUserCollection GetAllUsers() ;
int GetNumberOfUsersOnline() ;
MembershipUser GetUser() ; // retourne lutilisateur courant
MembershipUser GetUser(string username) ;
string GetUserNameByEmail(string emailToMatch) ;
void UpdateUser(MembershipUser user) ;
bool ValidateUser(string username, string password) ;
964
Les instances de la classe MembershipUser reprsentent lexcution les utilisateurs. Cette classe
contient quelques proprits permettant dobtenir le nom, le email, la date de cration, la date
de dernier login, la date de dernire demande de page, la question demander lutilisateur
en cas de perte de mot de passe et quelques mthodes permettant de changer le mot de passe
ou la question associe. Comme vous pouvez le constater, seules les informations relatives
lauthentification sont stockes dans une instance de MembershipUser. Nous verrons dans la prochaine section consacre la personnalisation comment tendre ce mcanisme pour associer
un utilisateur des informations personnelles comme son adresse, son numro de tlphone
voire lhistorique de ses achats.
Sachez quun mcanisme de verrouillage est prvu en cas de trop nombreuses tentatives de
login rates. Dans ce cas, la mthode Membership.ValidateUser() ne peut retourner true
tant que le verrouillage na pas t dsactiv pour lutilisateur concern par lappel la mthode MembershipUser.UnlockOut(). Vous pouvez configurer ce mcanisme avec les attributs
entiers passwordAttemptThreshold et passwordAttemptWindows qui se placent dans llment
de configuration <membership>. Le verrouillage est automatiquement mis en place si plus de
passwordAttemptThreshold logins infructueux ont lieu en moins de passwordAttemptWindows
minutes.
Web.Config
<?xml version="1.0"?>
<configuration
xmlns="http://schemas.microsoft.com/.NetConfiguration/v2.0">
<connectionStrings>
<add name="MyLocalSqlServer"
connectionString="Data Source=localhost;Integrated
Security=SSPI ;Initial Catalog=MyWebSiteUsers;" />
</connectionStrings>
<system.web>
<roleManager defaultProvider="MyRoleSqlProvider" enabled="true" >
<providers>
<add
name="MyRoleSqlProvider"
type="System.Web.Security.SqlRoleProvider,
System.Web, Version=2.0.0.0, Culture=neutral,
PublicKeyToken=b03f5f7f11d50a3a"
connectionStringName="MyLocalSqlServer" />
</providers>
</roleManager>
...
Comme vous pouvez le constater, la notion de fournisseur de rles se dfinit de la mme
manire que la notion de fournisseur dutilisateurs puisque ici aussi le design pattern provider
Scurit
965
prsent en page 902 est utilis. Un fournisseur de rles est une classe qui drive de la classe
System.Web.Security.RoleProvider. ASP.NET 2.0 prsente les classes SqlRoleProvider,
AuthorizationStoreRoleProvider et WindowsTokenRoleProvider. Vous pouvez crer votre
propre fournisseur de rles au moyen dune classe drivant de RoleProvider.
De la mme manire que lon utilise les mthodes statiques de la classe MembershipUser pour
manipuler programmatiquement les comptes utilisateurs, on utilise les mthodes statiques de
la classe System.Web.Security.Roles pour manipuler programmatiquement les rles. Ici aussi
leurs signatures sont particulirement loquentes :
void AddUsersToRole(string[] usernames,string roleName) ;
void AddUsersToRoles(string[] usernames,string[] roleNames) ;
void AddUserToRole(string username,string roleName) ;
void AddUserToRoles(string username,string[] roleNames) ;
void CreateRole(string roleName) ;
bool DeleteRole(string roleName) ;
bool DeleteRole(string roleName,bool throwOnPopulatedRole) ;
string[] FindUsersInRole(string roleName,string usernameToMatch) ;
string[] GetAllRoles() ;
string[] GetRolesForUser() ;
string[] GetRolesForUser(string username) ;
string[] GetUsersInRole(string roleName) ;
bool IsUserInRole(string roleName) ;
bool IsUserInRole(string username,string roleName) ;
void RemoveUserFromRole(string username,string roleName) ;
void RemoveUserFromRoles(string username,string[] roleNames) ;
void RemoveUsersFromRole(string[] usernames,string roleName) ;
void RemoveUsersFromRoles(string[] usernames,string[] roleNames) ;
bool RoleExists(string roleName) ;
Enfin, remarquez que lon peut dfinir des rgles daccs en se basant sur ce type de rles dans
llment de configuration <authorization> :
Exemple :
Web.Config
<?xml version="1.0"?>
<configuration
xmlns="http://schemas.microsoft.com/.NetConfiguration/v2.0">
<system.web>
<authorization>
<allow roles="admin"/>
<deny roles="simpleUser"/>
</authorization>
...
966
Le contrle CreateUserWizard prend en charge la cration dutilisateurs avec la vrification du mot de passe et la possibilit de fournir un email et un couple question/rponse
utiliss en cas de perte de mot de passe. Ce contrle est hautement paramtrable avec une
expression rgulire pour le mot de passe, une autre pour le email, des messages derreurs
personnalisables pour tous types derreurs (utilisateur dj existant, champs non remplis
etc.).
Le contrle LoginView est similaire au contrle LoginStatus ceci prs quil permet dacher un ensemble de contrles dirents selon que lutilisateur courant est authentifi ou
non.
Le contrle LoginName permet dinsrer dans la page HTML en cours de construction une
chane de caractres contenant le nom de lutilisateur couramment authentifi.
Les contrles tels que CreateUserWizard, ChangePassword ou Login dont les implmentations
exploitent directement le fournisseur dutilisateurs prsentent un attribut MembershipProvider
pour le dfinir.
Signalons que linterface graphique web de ASP.NET 2.0 prsente un onglet scurit qui vous
permet dadministrer lensemble des utilisateurs, des rles et des rgles daccs dune application
web.
Personnalisation
Fournisseur de personnalisation et gestion
des donnes personnelles
ASP.NET 2.0 prsente un framework permettant de stocker et de manipuler dune manire
standard les donnes personnelles de chaque utilisateur. Ici encore, pour prciser le mode de
stockage de ces donnes ASP.NET exploite le design pattern provider. ASP.NET 2.0 ne prsente
quune classe de fournisseur de personnalisation, la classe SqlProfileProvider. Cette classe exploite une base de donnes qui a t prpare au pralable par loutil aspnet_regsql.exe . Bien
entendu, vous pouvez construire votre propre fournisseur de personnalisation au moyen dune
classe drive de System.Web.Profile.ProfileProvider.
Personnalisation
Exemple :
967
Web.Config
<?xml version="1.0"?>
<configuration
xmlns="http://schemas.microsoft.com/.NetConfiguration/v2.0">
<connectionStrings>
<add name="MyLocalSqlServer"
connectionString="Data Source=localhost;Integrated
Security=SSPI ;Initial Catalog=MyWebSiteUsers;" />
</connectionStrings>
<system.web>
<profile enabled="true" defaultProvider="MyProfileSqlProvider">
<providers>
<add
name="MyProfileSqlProvider"
type="System.Web.Profile.SqlProfileProvider,
System.Web, Version=2.0.0.0, Culture=neutral,
PublicKeyToken=b03f5f7f11d50a3a"
connectionStringName="MyLocalSqlServer" />
</providers>
<properties>
<add name="CompanyName" type="System.String" />
<add name="Birthday" type="System.DateTime" />
<add name="RequestCount" type="System.Int32"
defaultValue="0" />
</properties>
</profile>
...
La section <profile> est consacre la personnalisation. La sous-section <properties> permet
de dfinir les paramtres personnels. Chacun de ces paramtres est nomm, typ et peut avoir
une valeur par dfaut.
Ces paramtres peuvent tre exploits programmatiquement au moyen de la classe Profile.
Le compilateur fait en sorte que la classe ProfileCommon ait une proprit publique dinstance
accessible en criture et en lecture pour chacun des paramtres prciss. Pour accder cette
proprit, il sut dcrire Profile.NomDeLaPropri
et
e:
...
void Page_Load(Object sender, EventArgs e) {
Profile.RequestCount += 1 ;
LabelRequestCount.Text = Profile.RequestCount.ToString() ;
}
...
Le compilateur ASP.NET interprte ceci comme un accs une instance cache de la classe
ProfileCommon. Cette instance cache contient les donnes de lutilisateur couramment logu.
Sil ny a pas dutilisateur couramment authentifi, une exception est leve.
Les donnes personnelles dun utilisateur ne sont charges que lors du premier accs une donne durant le traitement dune requte. Le cas chant, toutes les donnes sont charges. Elles
seront sauvegardes la fin du traitement. Cela signifie que vous navez crire absolument
aucune ligne de code pour prvoir le chargement ou la sauvegarde des donnes.
968
Cependant, il peut tre ecace de ne pas charger toutes les donnes personnelles chaque requte par exemple parce quelles reprsentent un gros volume et que lon ne fait quincrmenter
un compteur RequestCount chaque requte. Aussi, ASP.NET 2.0 vous permet de partitionner
vos donnes dans des groupes. Lorsque lon accde une donne dun groupe, seules les donnes
de celui-ci sont charges en mmoire :
Web.Config
Exemple :
...
<properties>
<add name="RequestCount" type="System.Int32" defaultValue="0"/>
<group name="ProfessionalInfo">
<add name="CompanyName" type="System.String"/>
<add name="ProfessionalEmail" type="System.String"/>
</group>
</properties>
...
Un groupe ne peut contenir dautres groupes. Laccs une donne dun groupe se fait par lintermdiaire dune proprit portant le nom du groupe :
...
void Page_Load(Object sender, EventArgs e) {
LblCompanyName.Text = Profile.ProfessionalInfo.CompanyName ;
}
...
Vous pouvez typer une donne utilisateur avec nimporte quel type srialisable en XML
ou en binaire, y compris des types collections tels que System.Collections.Specialized.
StringCollection. Il est intressant daller examiner le contenu des tables cres par aspnet_regsql.exe pour se rendre compte que les donnes personnelles sont sauves sous une
forme srialise. Cependant, vous ne pouvez pas utiliser de types gnriques.
Personnalisation
Exemple :
969
Web.Config
<?xml version="1.0"?>
<configuration
xmlns="http://schemas.microsoft.com/.NetConfiguration/v2.0">
<connectionStrings>...</connectionStrings>
<anonymousIdentification enabled="true" cookieless="AutoDetect"/>
<system.web>
<profile enabled="true" defaultProvider="MyProfileSqlProvider">
<providers>...</providers>
<properties>
<add name="RequestCount" type="System.Int32"
defaultValue="0" allowAnonymous="true" />
...
</properties>
</profile>
...
Pour implmenter correctement le scnario de lutilisateur anonyme qui va potentiellement
finir par sauthentifier, il faut prvoir la migration des donnes du compte utilisateur anonyme vers le compte utilisateur rel. Cette migration doit tre ralise au moment de lauthentification. Pour cela, il sut dinsrer votre code de migration dans la mthode Profile_MigrationAnonymous() dans le fichier Global.asax :
Exemple 23-88 :
Global.asax
Le mcanisme de session est fait pour stocker des donnes temporaires relatives un
utilisateur telles que la liste de ses achats courants.
970
En outre, le mcanisme de personnalisation est mieux pens. Il est donc plus simple exploiter
et plus ecace. Notamment :
Les noms et types des paramtres sont vrifis la compilation et non lexcution.
Les donnes personnelles un utilisateur sont charges la demande contrairement aux
donnes de sessions qui sont charges puis sauves chaque requte.
971
appliquer (tel que inputstyle). Elle prsente aussi quelques attributs fortement typs commun tous les contrles serveurs web tels que BackColor ou BorderWidth. Lespace de noms
System.Web.UI.Controls ainsi que ses sous espaces de noms contiennent plusieurs classes de
dfinition de style qui drivent de la classe System.Web.UI.Controls.Style. Ces classes telles
que TableStyle, PanelStyle, TitleStyle ou TreeNodeStyle sont spcifiques au style de chaque
contrle web ou de chaque partie dun contrle web. Les proprits de ces styles peuvent tre
initialises dans la dfinition du contrle comme ceci :
<asp:Calendar ...>
<TitleStyle BorderColor="green" BorderWidth="3" ... />
</asp:Calendar>
...ou comme cela :
<asp:Calendar ...
TitleStyle-BorderColor="green"
TitleStyle-BorderWidth="3" ... />
Notion de thme
Malgr la possibilit de factoriser les styles dans un lment <header>, la ncessit dun mcanisme puissant permettant de configurer simplement lapparence de tout un site pousser les
concepteurs dASP.NET 2.0 crer la notion de thme. Un thme est la dfinition dun ensemble
de styles qui peuvent tre appliqus statiquement ou dynamiquement aux contrles des pages
dun site. La modification des styles dun thme entrane automatiquement la modification de
lapparence des pages qui lexploitent.
Un thme est matrialis par le contenu dun rpertoire. Le nom du thme est le nom du rpertoire. Un thme peut tre local une application ou partag par toutes les applications dune
machine. Dans le premier cas, le rpertoire du thme doit tre un sous rpertoire du rpertoire
/App_Theme de lapplication. Dans le second cas le rpertoire du thme est soit un sous rpertoire
du rpertoire dinstallation dASP.NET ( savoir %WINDIR%\Microsoft.NET\Framework\<version>\ASP.NETClientFiles\Themes) soit un sous rpertoire du rpertoire Inetpub\wwwroot\
aspnet_client\system_web\<version>\Themes dans le cas dapplication web hberges par IIS.
Il y a plusieurs faons dappliquer un thme aux pages dune application :
Web.Config
<?xml version="1.0"?>
<configuration
xmlns="http://schemas.microsoft.com/.NetConfiguration/v2.0">
<system.web>
<pages theme="NomDuTheme" >
...
En utilisant la sous directive Theme de la directive <@ Page> des pages .aspx concernes.
Cette possibilit permet de rcrire pour une page un thme global spcifi dans la section
de configuration <page> :
972
Exemple :
<%@ Page Theme="NomDuTheme"
...
%>
En utilisant la sous directive StyleSheetTheme de la directive <@ Page> des pages .aspx
concernes. Il faut savoir que pour les contrles dune page, les styles sont choisis dans
cet ordre croissant de priorit : styles dfinis par le thme spcifi avec la sous directive
StyleSheetTheme ; styles dfinis directement dans les dclarations des contrles ; styles dfinis dans llment <page> du fichier de configuration ; styles dfinis par le thme spcifi
avec la sous directive Theme.
PagesXXX.aspx
Exemple :
<%@ Page StyleSheetTheme="NomDuTheme"
...
%>
PagesXXX.aspx
Notion de Skin
Un rpertoire dun thme contient un ou plusieurs fichiers dextension .skin, zro, un ou plusieurs fichiers dextension .css et ventuellement des sous rpertoires contenant des ressources
rfrences par les styles, telles que des images. Les stylesheets dun thme, dfinis dans ses fichiers dextension .css seront appliqus aux pages concernes.
Les fichiers dextension .skin contiennent les dfinitions des skins des types de contrles (que lon
pourrait traduire par peau/apparence dun type de contrle). La dfinition dun skin ressemble
la dfinition dun contrle mis par que seules certaines proprits peuvent tre positionnes. Ce sont les proprits qui se rapportent au style du type de contrle. Vous pouvez les
reconnatre en regardant la dfinition dun contrle car elles sont marques avec lattribut System.Web.UI.ThemeableAttribute(true). Voici par exemple la dfinition dun fichier .skin :
Exemple 23-89 :
<asp:Label Font-Bold="true" Font-Size="20" runat="server" />
<asp:TextBox Font-Bold="true" Font-Size="20" runat="server" />
BigText.skin
WebParts
973
Voici une page sur laquelle nous appliquons le thme ThemeBig qui ne contient que le fichier
BigText.skin :
Default.aspx
Exemple 23-90 :
<%@ Page Language="C#" Theme="ThemeBig" %>
<html xmlns="http://www.w3.org/1999/xhtml">
<body>
<form id="form1" runat="server">
<asp:Label ID="Lbl1" runat="server" Text="Label 1" /><br/>
<asp:Label ID="Lbl2" runat="server" Text="Label 2"
Font-Size="10"/><br/>
<asp:Label ID="Lbl3" runat="server" Text="Label 3"
EnableTheming=false /><br/>
<asp:TextBox ID="TextBox1" runat="server" Text="TextBox"/>
</form>
</body>
</html>
Enfin, voici une copie dcran de cette page. On voit bien que seul le Label numro 3 na pas t
impact par le thme du fait que lon a positionn la proprit EnableTheming false :
Skin nomm
Lors de la dfinition dun skin, vous avez la possibilit de le nommer avec lattribut SkinId.
On peut ainsi dfinir plusieurs skins relatifs un mme type de contrle dans un thme. Bien
entendu, un thme ne peut contenir des skins homonymes ni plus dun skin anonyme.
Comme lon peut sans douter, on peut aecter un skin nomm dans la dfinition dun contrle
avec lattribut SkinId. Si lexcution un skin nomm est utilis par un contrle mais nexiste
pas dans le thme courant, ASP.NET napplique aucun skin au contrle concern. Aussi, il est
prfrable de redfinir la totalit de vos skins nomms dans la totalit de vos thmes.
WebParts
ASP.NET 2.0 prsente un framework ddi la cration de webParts. Cette notion de webParts
permet un utilisateur de personnaliser lensemble des services que peut lui rendre une page.
974
Par exemple, on pourrait imaginer que lutilisateur ait le choix entre les services : dernire
nouvelles concernant le milieu des tlcommunications ; les valeurs du jour de la bourse ; la
mto locale ; le programme TV du soir etc. Chacun de ces services est matrialis sur la page
par un contrle particulier que lon nomme une webPart. Une application web qui supporte
un tel niveau de personnalisation est nomm portail web. Jusquici, dans le monde Microsoft
seule la technologie SharePoint tait ddie la cration de portails. Les webParts dASP.NET
2.0 permettent essentiellement :
De prsenter une ou plusieurs zones sur une mme page qui contiennent chacune une
ou plusieurs webParts. Chacune de ces zones est en fait un contrle serveurs de type
WebPartZone. Les webParts contenues dans une zone peuvent tre alignes verticalement
ou horizontalement.
De dfinir lordre dans lequel sont aches les webParts dune zone.
WebParts
975
De choisir les webParts qui sont contenues dans une zone au moyen dun catalogue de webParts. Un catalogue par dfaut peut tre fourni par le site mais lutilisateur peut importer
ses propres webParts stockes dans des fichiers XML sur son disque dur.
Le contenu dune webPart est ach dans une fentre spciale que lon nomme chrome.
Pour chaque webPart, lutilisateur peut dfinir lapparence du chrome, les actions prsentes dans le chrome (minimise, close etc.), le titre crit dans le chrome etc. Notez que les
actions prsentes par un chrome sont nommes verbes.
De connecter deux webParts. Dans ce cas une webPart joue le rle du producteur dinformation un peu comme une source de donnes tandis que lautre joue le rle de consommateur.
976
Un utilisateur ne peut modifier les webParts dune page que sil est couramment authentifi. En
eet, chaque modification entrane une requte POST, qui son tour entrane du ct serveur
la sauvegarde de ltat courant des webParts pour lutilisateur couramment authentifi.
WebParts
977
Web.Config
<?xml version="1.0"?>
<configuration
xmlns="http://schemas.microsoft.com/.NetConfiguration/v2.0">
<connectionStrings>
<add name="MyLocalSqlServer"
connectionString="Data Source=localhost;Integrated
Security=SSPI ;Initial Catalog=MyWebSiteUsers;" />
</connectionStrings>
<system.web>
<webParts>
<personalization defaultProvider="MyWebPartsSqlProvider">
<providers>
<add
name=" MyWebPartsSqlProvider "
type="System.Web.UI.WebControls.WebParts.SqlPersonalizationProvider,
System.Web, Version=2.0.0.0, Culture=neutral,
PublicKeyToken=b03f5f7f11d50a3a"
connectionStringName="MyLocalSqlServer" />
</providers>
</personalization>
</webParts>
...
Puisque le design pattern provider est utilis vous pouvez bien entendu prvoir vos propres
fournisseurs de webParts avec des classes qui drivent de la classe System.Web.UI.WebControls.
WebParts.PersonalizationProvider. Ainsi les donnes relatives aux tats des webParts des
pages accessibles un utilisateur donn peuvent tre sauves un autre endroit que les donnes
personnelles de cet utilisateur.
Pour quune page puisse prsenter des webParts, il faut quelle contienne un contrle serveur de
type WebPartManager avant la dclaration de tous contrles serveurs relatifs aux webParts. Vient
alors la notion de webPartsZone. Une webPartsZone est un contrle serveur qui la particularit de pouvoir contenir des webParts. Prcisons quune webParts est forcment contenu dans
une webPartsZone. La page suivante contient une webPartsZone qui elle-mme contient trois
webParts : une webPart qui contient un contrle utilisateur (celui dfini par lExemple 23-33),
978
une webPart qui contient un contrle serveur de type GridView et une webPart qui contient un
contrle serveur de type Calendar :
Exemple 23-91 :
<%@ Page Language="C#" %>
<%@ Register TagPrefix="PRATIQUE" Src="~/Securized/MyUserCtrl.ascx"
TagName=UserCtrl %>
<html xmlns="http://www.w3.org/1999/xhtml" >
<body>
<form id="Form1" runat="server">
<asp:SqlDataSource ID="DataSrc" runat="server" ConnectionString=
"server = localhost ; uid=sa ; pwd= ; database = ORGANISATION"
SelectCommand="SELECT * FROM EMPLOYES" />
<asp:WebPartManager ID="WebPartManager1" runat="server"/>
<asp:WebPartZone ID="WebPartZone1" runat="server"
LayoutOrientation="Horizontal">
<ZoneTemplate>
<PRATIQUE:UserCtrl ID="UserCtrl1" runat="server" />
<asp:GridView ID="Grid" DataSourceID="DataSrc" runat="server"/>
<asp:Calendar ID="Calendar1" runat="server"></asp:Calendar>
</ZoneTemplate>
</asp:WebPartZone>
</form>
</body>
</html>
La Figure 23 -30 reprsente une copie dcran de cette page. Notez que nous avons choisi un
style professionnel pour acher les webParts. Voici les changements quun tel style implique
au code de notre page. Pour plus de clart, nous omettrons ce code ncessaire aux styles dans
nos prochains listings. En outre, vous aurez rarement le besoin daccder au code des pages qui
contiennent des webParts puisque le mode design de Visual Studio est particulirement ecace :
Exemple 23-92 :
<%@ Page Language="C#" %>
...
<body>
...
<asp:WebPartZone ID="WebPartZone1" runat="server"
LayoutOrientation="Horizontal" BorderColor="#CCCCCC"
Font-Names="Verdana" Padding="6">
<ZoneTemplate> ... </ZoneTemplate>
<PartChromeStyle BackColor="#F7F6F3" BorderColor="#E2DED6"
Font-Names="Verdana" ForeColor="White" />
<MenuLabelHoverStyle ForeColor="#E2DED6" />
<EmptyZoneTextStyle Font-Size="0.8em" />
<MenuLabelStyle ForeColor="White" />
<MenuVerbHoverStyle BackColor="#F7F6F3" BorderColor="#CCCCCC"
BorderStyle="Solid" ForeColor="#333333" BorderWidth="1px" />
<HeaderStyle Font-Size="0.7em" ForeColor="#CCCCCC"
WebParts
979
HorizontalAlign="Center" />
<MenuVerbStyle BorderColor="#5D7B9D" BorderStyle="Solid"
BorderWidth="1px" ForeColor="White" />
<PartStyle Font-Size="0.8em" ForeColor="#333333" />
<TitleBarVerbStyle Font-Size="0.6em" Font-Underline="False"
ForeColor="White" />
<MenuPopupStyle BackColor="#5D7B9D" BorderColor="#CCCCCC"
BorderWidth="1px" Font-Names="Verdana"
Font-Size="0.6em" />
<PartTitleStyle BackColor="#5D7B9D" Font-Bold="True"
Font-Size="0.8em" ForeColor="White" />
</asp:WebPartZone>
...
</body>
La copie dcran de la Figure 23 -33 montre qu ce stade on peut dj minimiser/restaurer ou
fermer une webPart. Ltat du chrome de chaque webPart (minimis/restaur, ouvert/ferm)
est sauv pour lutilisateur authentifi dans la base de donnes spcifie par le fournisseur de
webParts. son retour sur cette page, lutilisateur retrouvera les chromes des webParts dans
ltat dans lesquels il les a laiss. Comprenez bien que seul ltat dune webPart est sauv. Pour
vous en convaincre, modifier ltat dun contrle et vrifier que cet tat est perdu lorsque vous
vous r authentifiez.
Une question se pose : comment faire pour dfinir les attributs dune webPart tels que son
titre ou la description qui doit tre ache dans le tooltip ? En eet, dans notre page .aspx
nos trois contrles sont implicitement contenus dans des contrles de type GenericWebPart.
Puisque nous navons pas accs ces contrles, nous ne pouvons pas les paramtrer. Deux solutions existent ce problme :
Dans le cas de contrles standard ou .aspx nous sommes oblig de fixer des attributs qui
seront exploits par le GenericWebPart par exemple comme ceci :
...
<ZoneTemplate>
<PRATIQUE:UserCtrl title="Couleurs" ID="UserCtrl1" runat="server" />
<asp:GridView title="Employ
es" ID="Grid" DataSourceID="DataSrc"
runat="server"/>
<asp:Calendar title="Calendrier" ID="Calendar1"
runat="server"></asp:Calendar>
</ZoneTemplate>
...
On note que lintellisense ne connat pas ces attributs et que le compilateur gnre un avertissement.
Dans le cas dun contrle utilisateur vous pouvez implmenter linterface IWebPart qui
dfinit les attributs sous forme de proprits.
Le mode design
Le mode dune page contenant des webParts est dfini par la proprit DisplayMode{get;set;}
du contrle WebPartManager de la page. Par dfaut ce mode est BrowseDisplayMode et lutilisateur ne peut que minimiser/restaurer et fermer les webParts. Si vous passez en mode
980
DesignDisplayMode, il devient possible de changer lordre des webParts (comme illustr par
la Figure 23 -31). Ici aussi, chaque changement gnre un vnement postback qui permet de
sauver le nouvel tat des webParts.
Exemple 23-93 :
<%@ Page Language="C#" %>
...
<script runat="server">
protected void ButtonBrowse_Click(object sender, EventArgs e) {
WebPartManager1.DisplayMode = WebPartManager.BrowseDisplayMode;
}
protected void ButtonDesign_Click(object sender, EventArgs e) {
WebPartManager1.DisplayMode = WebPartManager.DesignDisplayMode;
}
</script>
<html xmlns="http://www.w3.org/1999/xhtml" >
<body>
<form id="Form1" runat="server">
...
<asp:Button ID="ButtonBrowse" runat="server"
OnClick="ButtonBrowse_Click" Text="Browse Mode" />
<asp:Button ID="ButtonDesign" runat="server"
OnClick="ButtonDesign_Click" Text="Design Mode" />
</form>
</body>
</html>
Catalogue de webParts
Nous avons vu que le chrome permet de rendre invisible une webPart aprs avoir slectionn le
verbe Close. Vous pouvez vous servir dun contrle de type PageCatalogPart pour rendre visible
une ou plusieurs webParts qui ont t fermes. Un tel contrle ache la liste des webParts fermes que contient la page courante. Il doit tre contenu dans un contrle de type CatalogZone.
Un CatalogZone nest ach que lorsque lon est en mode CatalogDisplayMode :
Exemple 23-94 :
<%@ Page Language="C#" %>
...
<script runat="server">
protected void ButtonCatalog_Click(object sender, EventArgs e) {
WebPartManager1.DisplayMode = WebPartManager.CatalogDisplayMode;
}
</script>
<html xmlns="http://www.w3.org/1999/xhtml" >
<body>
<form id="Form1" runat="server">
...
<asp:CatalogZone ID="CatalogZone1" runat="server">
WebParts
981
<ZoneTemplate>
<asp:PageCatalogPart ID="part1" runat="server"/>
</ZoneTemplate>
</asp:CatalogZone>
<asp:Button ID="ButtonCatalog" runat="server"
OnClick="ButtonCatalog_Click" Text="Catalog Mode" />
</form>
</body>
</html>
La notion de catalogue va beaucoup plus loin que la simple rapparition des webParts fermes.
Vous pouvez dclarer un catalogue de contrles que lutilisateur peut potentiellement ajouter
dans votre page au moyen dun contrle de type DeclarativeCatalogPart. Ce type de contrle
contient des dfinitions de webParts, un peu comme un WebPartZone.
Grce au type de contrles ImportCatalogPart, vous pouvez mme autoriser un utilisateur
importer des webParts dfinies dans un document XML. Veuillez vous rfrer la documentation de ce contrle dans les MSDN pour connatre le format dun tel fichier.
Ces deux contrles permettent donc dajouter dynamiquement des webParts. Notons quen interne, du ct serveur, un identificateur unique est assign chaque webPart. Les webParts ajoutes dynamiquement prsentent le verbe Delete qui permet de les dtruire. Comme le montre
la page suivante, il est aussi possible dajouter dynamiquement des webParts partir du code :
Exemple 23-95 :
<%@ Page Language="C#" %>
<script runat="server">
private static int calendarID = 0 ;
protected void ButtonAdd_Click(object sender, EventArgs e) {
Calendar calendar = new Calendar();
calendar.ID = "Calendar_" + calendarID++;
GenericWebPart wrapper = WebPartManager1.CreateWebPart(calendar);
WebPartManager1.AddWebPart(wrapper, WebPartZone1, 0);
}
</script>
<html xmlns="http://www.w3.org/1999/xhtml" >
<body>
<form id="Form2" runat="server">
<asp:SqlDataSource ID="DataSrc" runat="server" ConnectionString=
"server = localhost ; uid=sa ; pwd= ; database = ORGANISATION"
SelectCommand="SELECT * FROM EMPLOYES" />
<asp:WebPartManager ID="WebPartManager1" runat="server"/>
<asp:WebPartZone ID="WebPartZone1" runat="server"
LayoutOrientation="Horizontal"/>
<asp:Button id="ButtonAdd" runat="server"
Text="Ajout dun calendrier" OnClick="ButtonAdd_Click"/>
</form>
</body>
</html>
982
Le mode dition
Le mode ddition permet dditer tous les attributs des webParts dune page. Pour cela il faut
passer en mode EditDisplayMode. Les contrles contenus dans les contrles de type EditorZone
sont alors visibles. Ils peuvent tre de trois types :
AppearanceEditorPart (illustr par la Figure 23-34) : Permet dditer les proprits visuelles dune webPart telles que sont titre ou sa taille.
WebParts
Exemple 23-97 :
983
MyProviderCtrl.ascx
MyProviderCtrl.ascx.cs
using System.Web.UI ;
using System.Web.UI.WebControls.WebParts ;
public interface IColor { string SelectedColor { get;set ; } }
public partial class MyProviderCtrl : UserControl, IColor {
private string m_SelectedColor ;
public string SelectedColor {
get { return m_SelectedColor ; }
set { m_SelectedColor = value ; }
}
protected void Btn_Click(System.Object sender, System.EventArgs e){
m_SelectedColor = Couleur.SelectedItem.Value;
}
[ConnectionProvider("TestProviderConsumer")]
public IColor ProvideColor() { return this ; }
}
Le contrle serveur qui reprsente la webPart producteur (en loccurrence MyProviderCtrl)
doit supporter une interface connue du contrle serveur qui reprsente le consommateur (en
loccurrence MyProviderCtrl). Linterface IColor joue ici ce rle dintermdiaire :
Exemple 23-99 :
MyConsumerCtrl.ascx
984
Exemple 23-100 :
<%@ Page Language="C#" %>
<%@ Register TagPrefix="PRATIQUE" Src="~/Securized/MyProviderCtrl.ascx"
TagName="ProviderCtrl" %>
<%@ Register TagPrefix="PRATIQUE" Src="~/Securized/MyConsumerCtrl.ascx"
TagName="ConsumerCtrl" %>
<html xmlns="http://www.w3.org/1999/xhtml" >
<body>
<form id="Form1" runat="server">
<asp:WebPartManager ID="WebPartManager1" runat="server">
<StaticConnections>
<asp:WebPartConnection ID="MyCnx" ConsumerID="ConsumerCtrl1"
ProviderID="ProviderCtrl1" />
</StaticConnections>
</asp:WebPartManager>
<asp:WebPartZone ID="WebPartZone1" runat="server"
LayoutOrientation="Horizontal">
<ZoneTemplate>
<PRATIQUE:ProviderCtrl ID="ProviderCtrl1" runat="server" />
<PRATIQUE:ConsumerCtrl ID="ConsumerCtrl1" runat="server" />
</ZoneTemplate>
</asp:WebPartZone>
</form>
</body>
</html>
Cette page illustre une connexion construite statiquement. Vous pouvez aussi permettre
vos utilisateurs de crer dynamiquement des connexions. Pour cela, il faut passer en mode
ConnectDisplayMode et fournir un contrle de type ConnectionZone :
Exemple 23-101 :
<%@ Page Language="C#" %>
...
<script runat="server">
protected void ButtonBrowse_Click(object sender, EventArgs e) {
WebPartManager1.DisplayMode = WebPartManager.BrowseDisplayMode ;
}
protected void ButtonCnx_Click(object sender, EventArgs e) {
WebPartManager1.DisplayMode = WebPartManager.ConnectDisplayMode ;
}
</script>
<html xmlns="http://www.w3.org/1999/xhtml" >
<body>
<form id="Form1" runat="server">
<asp:WebPartManager ID="WebPartManager1" runat="server"/>
...
<asp:ConnectionsZone ID="ConnectionsZone1" runat="server"/>
<asp:Button ID="ButtonBrowse" runat="server"
OnClick="ButtonBrowse_Click" Text="Browse Mode" />
WebParts
985
24
Introduction au dveloppement
de Services Web avec .NET
Introduction
SOA : Architecture Oriente Service
La programmation et larchitecture oriente service (SOA pour Service Oriented Architecture) est
un complment rcent de la POO (Programmation Oriente Objet). La notion de service est
simple : un service est une application avec laquelle on interagit avec des messages. Un service
a donc des clients qui lui envoient des messages et auxquels il peut retourner des messages. Un
service peut aussi tre le client dun autre service. SOA est fond sur quatre principes cls :
988
La structure dutilisation dun service est dfinie sans ambigut par un unique contrat
prsent par le service ses clients :
Lvolution des langages objets illustre limportance croissante de la dissociation entre abstraction et implmentation. En eet, la notion dinterface est maintenant une composante
privilgie des langages modernes tels que C ou Java, ce qui nest pas le cas par exemple en
C++. SOA va plus loin en formalisant la structure dutilisation dun service dans un unique
contrat matrialis par un document XML. Ce contrat spcifie entre autres la structure des
informations contenues dans les messages entrant et sortant au moyen de schmas XSD.
Ce contrat peut contenir des commentaires et ainsi renseigner un humain sur la structure
dutilisation du service. Ce contrat peut aussi tre utilis par un programme pour gnrer
par exemple, des classes qui seront utilises ct client pour fabriquer les messages et les
envoyer au service. Le fait que la rception dun message ct service dclenche en interne
lappel une certaine mthode dune certaine classe est un dtail dimplmentation du
service et ne fait pas partie de son contrat.
La smantique dutilisation dun service, aussi appel stratgie (policy en anglais) est dfinie sans ambigut dans le contrat prsent par le service ses clients :
La plupart des langages objets ne prsentent pas de mcanismes simples pour signifier aux
clients dune interface les prconditions et les postconditions dutilisation. Ces conditions
sont vrifies au sein de limplmentation de linterface et si elles ne sont pas rigoureusement documentes, le client na aucun moyen de les connatre avec exactitude. En SOA,
cette smantique dutilisation fait partie intgrante du contrat prsent par le service ses
clients. Par exemple un service peut dclarer avec une stratgie quil naccepte de traiter
un message entrant qu la condition que celui-ci soit encrypt dune certaine manire.
Un service peut dclarer avec une stratgie quil nest accessible que de 8h 22h GMT ou
quil supporte un certain protocole transactionnel. linstar de la structure dutilisation,
les stratgies sont rdiges dans un langage XML non ambigu. Elles peuvent donc tre
consommes par un framework pour viter au dveloppeur de coder la logique spcifie (par
exemple un message sera automatiquement crypt dune certaine manire ct client par
un tel framework si telle est la stratgie du service cibl).
Introduction
989
Pour pouvoir obtenir linteroprabilit, les services web reposent dabord sur deux langages
XML simples et normaliss. En eet, en page 759 nous expliquons quun atout majeur de XML
est de pouvoir coder des donnes indpendamment dune plateforme dexcution :
Le langage XML SOAP (Simple Object Access Protocol) est utilis pour la rdaction des messages changs entre services. Des spcifications existent pour chaque protocole rseaux
pour dcrire comment acheminer un message SOAP.
Le langage XML WSDL (Web Service Definition Language) est utilis pour rdiger les contrats
prsents par les services leurs clients. Nous verrons que ce langage permet aussi de dfinir les liens entre le contrat dun service et son implmentation (autrement dit, il indique
quelle mthode de quelle classe doit tre invoque pour traiter tel type de message). Ainsi,
le contrat que prsente un service ses clients ne constitue quune partie dun document
WSDL puisque nous avons vu que ce dernier type dinformation na pas tre consomm
par les clients.
Description : Le langage WSDL est tendu par plusieurs spcifications WS-* qui permettent
de rdiger des contrats plus prcis. Par exemple, la spcification WS-SecurityPolicy permet au
contrat dun service de spcifier quil naccepte de traiter un message reu que si ce dernier
a t crypt dune certaine manire.
Dcouverte : Des spcifications dcrivent des implmentations de mcanismes de recherche selon des critres, de services web au travers dun rseau. Ainsi un client peut
dcouvrir lensemble des services qui rpondent ses besoins. Lorsque que le client dcide
990
Coordination et transaction : Des spcifications WS-* permettent de coordonner les activits de plusieurs services afin dobtenir un certain comportement global. La notion de
transaction distribue entre plusieurs services constitue un exemple dune telle coordination.
Un aspect trs intressant des spcifications WS-* est quelles permettent de composer les protocoles. Si deux protocoles ont des desseins orthogonaux, alors ils peuvent tre utiliss conjointement dune manire indpendante. Par exemple, un protocole de scurit destin rendre les
messages confidentiels en les cryptant peu optionnellement tre utilis conjointement avec un
protocole de livraison de message permettant de garantir au client que les messages ont bien
t reus par le service.
Cependant comprenez bien que lindpendance dans la composition des protocoles se fait dans
la mesure du possible. Les spcifications WS-* agissent dirents niveaux de larchitecture. Il
nest pas rare quune spcification WS-* compte sur une autre spcification dun niveau infrieur. Par exemple, les algorithmes de transaction distribue sur plusieurs services prsupposent
que la livraison des messages se fait dans lordre dans lequel ils sont envoys.
991
Le modle multi-diusion (dit aussi broadcast) permet denvoyer un mme message simultanment plusieurs services. Des variantes de ce modle permettent au client de sattendre une ou plusieurs rponses de la part des services contacts.
Ces modles reprsentent la plupart des changes de messages rels mais bien dautres modles
dchange plus ou moins compliqus sont imaginables et implmentables.
Localizer.asmx
992
La premire ligne indique ASP.NET que ce fichier dcrit un service web. lexcution, un service web est en fait une instance de la classe spcifie en premire ligne avec lattribut class, en
loccurrence, la classe LocalizationCorp.Localizer. Une telle instance est cre par ASP.NET
pour traiter chaque requte. On parle denvironnement sans tat puisquun mme objet ne peut
servir plusieurs requtes. Comprenez bien que selon vos besoin vous pouvez toujours stocker
des tats persistant entre les requtes du ct serveur, par exemple au moyen dune base de
donnes.
Dans cette premire ligne, nous indiquons ASP.NET le langage .NET utilis pour rdiger cette
classe, avec lattribut Language, en loccurrence le langage C . Tous comme une page ASPX, on
peut sparer le code source C des pages ASMX. Nous expliquons ceci en page 868.
On remarque que notre classe drive de la classe System.Web.Services.WebService. Une classe
reprsentant un service web ne drive pas ncessairement de la classe WebService. Cependant,
cela permet laccs de nombreuses fonctionnalits, comme la gestion des tats dune page sur
lautre. Une classe reprsentant un service web doit satisfaire les deux contraintes suivantes :
La classe doit tre publique et doit avoir un constructeur par dfaut public.
Toutes les web mthodes doivent tre marques avec lattribut System.Web.Services.
WebMethod. Les proprits de cet attribut configurent les fonctionnalits accessibles cette
mthode, comme son comportement relatif aux sessions. La proprit Description permet
de fournir une description pour la mthode. Veuillez consulter larticle WebMethodAttribute Members des MSDN pour plus de dtails.
La proprit Namespace de la classe WebService permet de nommer dune manire unique votre
service web. En gnral, on fournit une URL pour cette proprit mais ceci nest pas une obligation. Si on ne fournit pas explicitement dURL, lURL http://tempuri.org/ est prise par dfaut.
993
Dans tous les cas, la ressource localise lURL (si elle existe) ne sera jamais accde. Le fait
dutiliser une URL qui vous appartient permet de garantir quaucun autre service web au monde
ne portera le mme nom que le vtre. Voici comment nous aurions pu crire notre service web
pour utiliser cette proprit :
Exemple 24-2 :
...
namespace LocalizationCorp {
[WebService(Namespace="http://localizationcorp.com/localizer")]
public class Localizer : WebService {
...
Vous pouvez remarquer quil ny a pas de fichier dextension WSDL prsent dans le rpertoire hbergeant le service web. Pour obtenir ce document, il sut de taper lURL de
notre service web dans un navigateur, suivie de ?wsdl. Dans notre exemple cela donne :
http://localhost/localizationcorp/localizer.asmx?wsdl.
En fin de chapitre nous consacrons une section la description des documents WSDL.
Vous pouvez dj remarquer que notre classe de donnes LocalizationCorp.Town a t
convertie en un schma XSD inclus dans le document WSDL et dfini avec lespace de noms
http://localizationcorp.com/documents/data/.
994
Cette possibilit de test utilise la mthode Documentation pour gnrer les pages HTML. Si
vous dsactivez cette mthode vous navez plus accs aux tests partir dun navigateur. Cette
remarque se rvlera pertinente lorsque lon expliquera que la plateforme WSE compte sur la
mthode HttpSoap pour eectuer son travail. Ainsi, les traitements de WSE ne peuvent tre
tests partir de la mthode Documentation.
En production, pour plus de scurit il est conseill de dsactiver les mthodes autres que
HttpSoap. Cette dsactivation peut seectuer en ajoutant les lignes suivantes dans le fichier
web.config de votre service ou mme mieux, dans votre fichier machine.config (qui peut tre
trouv dans votre rpertoire dinstallation de .NET C:\WINDOWS\Microsoft.NET\Framework
\v2.0.XXXXX\CONFIG) :
<configuration>
<system.web>
<webServices>
<protocols>
<add name="HttpSoap1.2" />
<add name="HttpSoap" />
<remove name="HttpPost" />
<remove name="HttpGet" />
<remove name="HttpPostLocalhost"/>
<remove name="Documentation"/>
...
995
proxy dans un fichier source en langage C , VB.NET ou JScript, directement partir du contrat
WSDL du service. Par exemple cette commande gnre ce fichier :
wsdl.exe /out:LocalizerProxy.cs /language:C#
http://localhost/localizationcorp/Localizer.asmx?wsdl
Exemple :
LocalizerProxy.cs
//---------------------------------------------------------------------// <auto-generated>
//
This code was generated by a tool.
//
Runtime Version:2.0.XXXXX
//
//
Changes to this file may cause incorrect behavior and will be lost
//
if the code is regenerated.
// </auto-generated>
//---------------------------------------------------------------------using System ;
using System.ComponentModel ;
using System.Diagnostics ;
using System.Web.Services ;
using System.Web.Services.Protocols ;
using System.Xml.Serialization ;
[System.Diagnostics.DebuggerStepThroughAttribute()]
[System.ComponentModel.DesignerCategoryAttribute("code")]
[System.Web.Services.WebServiceBindingAttribute(
Name="LocalizerSoap", Namespace="http://tempuri.org/")]
public partial class Localizer :
System.Web.Services.Protocols.SoapHttpClientProtocol {
...
public Localizer() {
this.Url = "http://localhost/LocalizationCorp/Localizer.asmx" ;
}
[System.Web.Services.Protocols.SoapDocumentMethodAttribute(
"http://tempuri.org/GetTownFromLatLon",
...)]
[return: System.Xml.Serialization.XmlElementAttribute(
Namespace="http://localizationcorp.com/documents/data/",
IsNullable=true)]
public Town GetTownFromLatLon(double lat, double lon) {
object[] results = this.Invoke("GetTownFromLatLon", new object[] {
lat,
lon}) ;
return ((Town)(results[0])) ;
}
public System.IAsyncResult BeginGetTownFromLatLon(
double lat, double lon,
996
997
[System.SerializableAttribute()]
[System.Xml.Serialization.XmlTypeAttribute(
Namespace="http://localizationcorp.com/documents/data/")]
public partial class Town {
private string nameField ;
private string countryField ;
public string Name {
get { returnthis.nameField;} set {this.nameField = value;}
}
public string Country {
get {return this.countryField;} set {this.countryField = value;}
}
}
...
Il est intressant de remarquer que la classe Town a t rgnre partir du schma XSD prsent
dans le fichier WSDL du service. Certaine documentions conseillent de supprimer cette dfinition du fichier gnr et dencapsuler ces classes de donnes dans des bibliothques rfrences
la fois par le service et ses clients. Nous ne sommes pas daccord avec cette pratique qui va compltement lencontre dun des principes fondamentaux de lapproche SOA : un service et ses
clients ne partagent quun contrat. Aussi dans la suite nous nous contenterons parfaitement
de cette classe Town gnre.
Notez que la classe proxy drive de la classe SoapHttpClientProtocol. Cela lui confre un certains nombre de possibilits comme la configuration des mthodes dauthentification utiliser.
Vous pouvez maintenant crer un assemblage qui rfrence lassemblage System.Web.Services.
dll et qui contient le fichier source LocalizerProxy.cs ainsi que le fichier source suivant :
Exemple 24-3 :
using System ;
class Program {
static void Main() {
Localizer proxy = new Localizer();
Town town = proxy.GetTownFromLatLon(43.42, 7.15);
Console.WriteLine("Ville:" + town.Name + " Pays:" + town.Country) ;
}
}
998
Notez que Visual Studio utilise la technologie UDDI pour rechercher les services web distants.
Nous dtaillons cette technologie un peu plus loin dans ce chapitre.
Message requte
Expditeur = Client
Destinataire = Serveur
Service
(i.e. le serveur)
Message rponse
Expditeur = Serveur
Destinataire = Client
999
localizer.asmx
...
namespace LocalizationCorp {
[System.Web.Services.Protocols.SoapRpcService()]
public class Localizer : WebService {
...
Vous pouvez maintenant obtenir des mtadonnes exploitables par .NET Remoting dans lassemblage ProxyLocalizationCorp.dll en tapant la ligne de commande suivante. Notez que
pour excuter cette commande sur un service web hberg par IIS, il faut dsactiver lauthentification intgre de Windows sur le rpertoire virtuel concern. Si vous excutez votre service web
avec le serveur web de Visual Studio 2005, l aussi il faut dsactiver lauthentification intgre
avec le menu Proprit du projet Start Options Dcochez NTLM Authentication :
soapsuds.exe /url:http://localhost:80/LocalizationCorp/
Localizer.asmx?wsdl/oa:ProxyLocalizationCorp.dll
Le programme suivant, qui doit tre compil en rfrenant les assemblages ProxyLocalizationCorp.dll et System.Runtime.Remoting.dll utilise le service web partir de .NET Remoting. Un client .NET Remoting na pas besoin que le service web supporte le formatage RPC.
Vous pouvez donc maintenant enlever lattribut SoapRpcService en revenant lExemple 24-1.
Notez que les classes LocalizerSoap et Town sont dans lespace de noms InteropNS :
Exemple 24-5 :
using System ;
using System.Runtime.Remoting ;
using System.Runtime.Remoting.Channels ;
using System.Runtime.Remoting.Channels.Http ;
class Program {
static void Main() {
HttpChannel canalHttp = new HttpChannel(0) ;
ChannelServices.RegisterChannel( canalHttp, false ) ;
MarshalByRefObject obj = (MarshalByRefObject)
RemotingServices.Connect(
typeof(InteropNS.LocalizerSoap),
"http://localhost:80/LocalizationCorp/Localizer.asmx") ;
InteropNS.LocalizerSoap proxy = obj as InteropNS.LocalizerSoap;
InteropNS.Town town = proxy.GetTownFromLatLon(43.42, 7.15) ;
Console.WriteLine("Ville:" + town.Name + " Pays:" + town.Country) ;
}
}
Aprs cette incursion dans le monde de la pratique il est temps dapporter un peu plus de thorie
quant aux langages SOAP et WSDL.
1000
La possibilit dextension du langage SOAP pour implmenter des protocoles de communications (dencryption, dauthentification, transactionnel etc).
Les messages cods en langage SOAP peuvent tre achemins par tous types de protocoles
rseaux tels que HTTP, TCP, UDP ou SMTP.
Introduction SOAP
La version 1.0 de SOAP tait destine tre utilise par des technologies dobjets distribus. La
version 1.1 de SOAP est utilise depuis plusieurs annes principalement par les services web. Le
mot Object de lacronyme SOAP nest donc plus significatif. Aussi depuis la version 1.2 de SOAP,
le terme SOAP nest plus considr comme un acronyme.
WSE 3.0 travaille maintenant par dfaut avec la version 1.2. Dans la suite nous nous baserons donc sur la version 1.2. Vous pouvez obtenir une liste exhaustive des volutions de SOAP
1.1 vers SOAP 1.2 dans cet article http://www.idealliance.org/papers/xmle02/dx_xmle02/
papers/02-02-02/02-02-02.html. La spcification complte de SOAP 1.2 est divise en trois
documents accessibles aux URL http://www.w3.org/TR/soap12-part0/, http://www.w3.org/
TR/soap12-part1/ et http://www.w3.org/TR/soap12-part2/.
Un message SOAP contient un lment racine <Envelope>. Cet lment contient optionnellement un lment <Header> au dbut et obligatoirement un lment <Body> en dernire position. Donc un message SOAP ressemble ceci :
<soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/">
<soap:Header> <!-- optionel -->
<!-- Contient les donnees relatives au transport du message. -->
</soap:Header>
<soap:Body> <!-- obligatoire -->
<!-- Contient les donnees m
etiers. -->
</soap:Body>
</soap:Envelope>
Llment <body> contient les donnes utiles du message codes en XML. Par exemple, voici
quoi ressemble un message SOAP requte envoy par notre client au service localizer :
<soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/">
<soap:Body>
<GetTownFromLatLon xmlns="http://tempuri.org/">
<lat>43.42</lat>
<lon>7.15</lon>
</GetTownFromLatLon>
</soap:Body>
</soap:Envelope>
1001
Llment <Header> contient des donnes codes en XML, fabriques et consommes par des
extensions de SOAP telles que les implmentations des spcifications WS-*. Le langage SOAP
ne dfinit pas de sous lments de llment <Header>. Seules les spcifications WS-* dfinissent
des sous lments de llment <Header>. Chaque spcification implique dans lacheminement
dun message ajoute ses sous lments. Cest grce ce mcanisme que lon obtient la fois
lextensibilit du langage SOAP et la possibilit de composer les protocoles.
Voici un message SOAP qui contient un jeton de scurit sous la forme utilisateur/mot de passe
ajout par une implmentation de la spcification WS-Security. Il contient aussi des informations sur la source du message, qui ont t ajoutes par une implmentation de la spcification
WS-Addressing :
<soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/"
xmlns:wsa="http://schemas.xmlsoap.org/ws/2003/03/addressing"
xmlns:wsse="http://schemas.xmlsoap.org/ws/2002/12/secext/">
<soap:Header>
<!-- Les donnees relatives `
a WS-Addressing. -->
<wsa:From>
<wsa:Address>http://smacchia.com/client</wsa:Address>
</wsa:From>
...
<!-- Les donnees relatives `
a WS-Security. -->
<wsse:Security soap:mustUnderstand="1">
<wsse:UserName>psmacchia</wsse:UserName>
<wsse:Password wsse:Type="wsse:PasswordDigest">
yH/*kiGGdsdujg5%16?LHRVhbg...
</wsse:Password>
...
</wsse:Security>
...
</soap:Header>
<soap:Body>
...
</soap:Body>
</soap:Envelope>
1002
tcp
Nud
intermdiaire
udp
Nud
intermdiaire
Nud final
Nuds intermdiaires
Nuds du chemin
1003
SOAP. Cette autre faon de faire est connue sous le nom de formatage RPC / encodage encoded . Cette technique est adapte aux changes de messages requte/rponse synchrone modlisant un appel de mthode (do RPC). Lencodage encoded fait partie de la spcification de
SOAP et dcrit comment srialiser en XML des arguments entrant et sortant dune mthode en
se basant sur un ensemble de rgles. Ces rgles spcifient la faon dont les objets, les tableaux,
les structures et les graphes dobjets sont srialiss.
SOAP est maintenant principalement utilis pour coder des messages changs entre services.
Comme nous lavons mentionn le modle dchange de messages RPC style requte/rponse
synchrone nest plus quun modle dchange parmi dautres. Il ny a donc plus de raison de
le privilgier. Aussi les direntes implmentations des services web telles que ASP.NET/WSE
utilisent par dfaut le modle dencodage document/literal plutt que le modle RPC/encoded.
1004
Les lments <binding>, <port> et <service> utiliss pour prciser les liens entre un contrat
et une implmentation. En plus de lier les web mthodes aux oprations du service, ces liens
dcrivent dautres aspects de limplmentation tels que le protocole rseau ou lencodage
des donnes dans les messages SOAP.
Ainsi, certaines informations dcrites par la seconde catgorie dlments ne sont pas utiles aux
clients des services web. Elles ne font donc pas partie du contrat.
La spcification WS-Policy dcrit comment tendre WSDL pour rdiger des stratgies. Les stratgies sont des clauses du contrat permettant daugmenter sa prcision. Par exemple, le langage
WSDL ne spcifie pas comment expliciter le fait que les messages envoys un service doivent
tre encrypts. Il faut pour cela ajouter des stratgies de scurit dans le contrat. Ainsi, seule une
partie du contrat est eectivement rdige en WSDL :
Domaine de couverture des stratgies
1005
1006
</operation>
</binding>
<service name="Localizer">
<port name="LocalizerSoap" binding="s0:LocalizerSoap">
<soap:address
location="http://localhost/localizationcorp/localizer.asmx" />
</port>
</service>
</definitions>
Llment <definitions>
Llment <definitions> est llment racine de tous document WSDL. Cet lment inclut les
espaces de noms standard utiliss (SOAP, XSD etc) et ventuellement des espaces de noms propritaires.
Llment <Types>
On voit que llment <types> contient les schmas XSD des donnes qui sont contenues dans
la partie <Body> des messages SOAP changs. Notez que llment <types> est obligatoire mais
quil peut tre vide si vous importez les schmas XSD dans un lment racine <import> :
<?xml version="1.0" encoding="utf-8" ?>
<import namespace="http://localizationcorp.com/documents/data/"
location="http://localizationcorp.com/documents/data/schema.xsd" >
<definitions>
<types />
...
</definitions>
Llment <message>
Chaque lment <message> dfinit un message SOAP entrant, sortant ou derreur. Chaque lment <part> dun lment <message> rfrence un schma de donnes. Puisquun message
SOAP peut contenir plusieurs parties, un lment <message> peut contenir plusieurs lments
<part>.
Llment <operation>
Chaque lment <operation> dfinit les messages SOAP supports par chaque opration du
service web. Dans notre service web simple localizer, la seule opration est constitue dun
change de message synchrone style RPC aussi elle prsente un message entrant et un message
sortant. Une opration supportant un autre modle dchange de message pourrait ne dfinir
quun message dentre ou de sortie. Une opration a aussi la possibilit de prsenter un message
derreur en contenant un lment <fault>.
Le nom dune opration dfinie dans llment soap:operation est prfix par dfaut par lespace de nom http://tempuri.org/. LExemple 24-2 montre comment personnaliser cet espace
de nom.
1007
Llment <portType>
Un lment <portType> dfinit lensemble des oprations prsentes par un service. linstar de
note exemple les oprations ont la possibilit dtre dfinies directement dans llment <portType>. Elles peuvent tre aussi dfinies hors de cet lment puis rfrences comme ceci :
...
<operation name="GetTownFromLatLon">
<input message="s0:GetTownFromLatLonSoapIn" />
<output message="s0:GetTownFromLatLonSoapOut" />
</operation>
<portType name="LocalizerSoap">
<operation name="GetTownFromLatLon" />
</portType>
...
Si lon raisonne en termes de langage objet les types de llment <types> sapparentent
aux structures/classes de donnes, les lments <message> sapparentent aux signatures des
mthodes, les lments <operation> sapparentent aux mthodes et les lments <portType>
sapparentent aux interfaces. Intressons nous maintenant aux lments WSDL permettant
dtablir un lien entre un contrat et une implmentation.
Llment <binding>
Llment <binding> permet de prciser des informations sur limplmentation dun service.
Ces informations sont essentiellement :
Les protocoles dencodage des messages SOAP avec les lments <soap:body>.
Les noms des web mthodes utiliser pour chaque opration avec les lments <soap:operation>.
Comprenez bien que par exemple les lments <soap:operation> et <operation> sont dirents puisquils ne font pas parties du mme espace de noms. En outre, bien que certaines de
ces informations soient utiles aux clients pour consommer le service, elles ne contiennent pas
dinformation relative la smantique du service.
Llment <port>
Llment <port> permet de faire le lien entre un lment <binding> et une implmentation
dun service au moyen dun lment <soap:address>. Il est intressant que cette information
ne fasse pas partie de llment <binding> car cela donne un degr de libert supplmentaire
pour rediriger les messages SOAP entrant vers une autre implmentation.
Llment <service>
Llment <service> est une collection dlment <port>.
1008
<definitions>
<types />
<message />
<operation>
<message />
</operation>
<portType>
<operation />
</portType>
<binding>
<operation />
</binding>
<service>
<port>
<binding />
</port>
</service>
</definitions>
1009
en production avec .NET ds aujourdhui. Il est clair que vous bnficierez de vos acquis lorsque
vous devrez aborder les futures plateformes de dveloppement plus volues telles que Windows
Communication Foundation.
En plus de HTTP, WSE 3.0 permet dexploiter le protocole RPC ou un mode de transport optimis lorsquun service et son client sont dans le mme processus. Dans ces deux cas il nest bien
videmment pas utile dexploiter la technologie ASP.NET ni un serveur web tel que IIS.
Linstallation de WSE
WSE est tlchargeable gratuitement sur le site de Microsoft. En plus de lditeur de configuration WSE et de lassemblage Microsoft.Web.ServiceXX.dll vous trouverez dans le rpertoire
dinstallation des outils, de la documentation et des exemples de projets exploitant WSE.
La technologie de certification X509 (dcrite dans la section page 230) est largement exploite
dans le domaine de la scurit des services dvelopps avec WSE. Loutil X509 Certificate Tool
fourni avec WSE permet de faciliter la manipulation des certificats X509. WSE est livr avec deux
certificats X509 que vous pouvez utiliser pour tester les exemples fournis ainsi que vos propres
prototypes.
WSE est aussi livr avec loutil Policy Wizard qui permet de gnrer plus simplement vos stratgies.
Une fois que WSE est install sur votre machine de dveloppement vous pouvez lactiver trs
simplement sur vos projets Visual Studio (ASP.NET ou non). Il vous sut de cliquer droit sur
votre projet dans lexplorateur de solution et de slectionner WSE Settings 3.0.... Lditeur de WSE
sache alors et vous navez plus qu slectionner Enable this project for Web Services Enhancements. Dans le cas dun projet ASP.NET il faut aussi slectionner Enable Microsoft Web Services
Enhancements Soap Extensions.
1010
La description dtaille des extensions SOAP dpasse le cadre de cet ouvrage. Nanmoins vous
pouvez vous rfrer la documentation des MSDN concernant la classe SoapExtension. En
eet, celle-ci contient un exemple dextension SOAP particulirement instructif qui permet de
sauver dans un fichier de log les messages SOAP entrants et sortants. Pour faire fonctionner cet
exemple vous devez prendre trois prcautions :
Faire en sorte que lutilisateur Windows qui excute le processus dASP.NET (par dfaut
ASPNET) ait tous les droits daccs sur le rpertoire dans lequel lapplication web est stocke
afin de pouvoir crer et modifier les fichiers de log.
Utiliser un client dvelopp avec une classe proxy. En eet, si vous utilisez un navigateur
pour tester votre service vous nutiliserez pas la mthode HttpSoap et dans ce cas les extensions SOAP ne sont pas utilises.
Ajoutez ceci dans votre fichier web.config afin dactiver votre extension SOAP.
<configuration>
<system.web>
<webServices>
<soapExtensionTypes>
<add type="NomDeLaClasseAvecEspaceDeNoms,
NomDeLAssemblageContenantLaClasse",
priority="1" group="0" />
</soapExtensionTypes>
...
Notez que vous pouvez dcider de nappliquer une extension SOAP que sur certaines web
mthodes. Dans ce cas il ne faut pas rajouter ceci votre fichier web.config mais il faut
marquer chaque mthode concerne avec un attribut dextension SOAP. Plus dinformations ce sujet sont disponibles dans la documentation des MSDN concernant la classe
SoapExtensionAttribute.
Dans le cas dun projet non ASP.NET lditeur de configuration de WSE cre une nouvelle
classe proxy dont le nom se termine par Wse. Dans notre exemple il y a donc deux classes
proxy : Localizer et LocalizerWse. La classe Localizer drive de System.Web.Services.
Protocols.SoapHttpClientProtocol tandis que la classe LocalizerWse drive de Microsoft.
Web.Services2.WebServicesClientProtocol. Bien entendu vous nobtenez les services de WSE
que si vous utilisez la classe proxy LocalizerWse pour accder votre service.
1011
WS-ReliableMessage
La spcification WS-ReliableMessage permet de pallier labsence de garanties de certains protocoles rseaux utiliss pour acheminer les messages SOAP. Par exemple, le protocole rseau UDP
ne fournit ni mcanisme daccus rception pour informer lenvoyeur dun message quil a bien
t reu par son destinataire, ni mcanisme permettant de garantir lordre de rception dune
squence de messages par un destinataire, ni mcanisme pour permettre au destinataire dliminer des messages dupliqus. Bien entendu la spcification WS-ReliableMessage se place au
niveau le plus bas dans lempilement des spcifications WS-* et sera gre implicitement par les
environnements dexcutions.
UDDI et WS-Discovery
Les spcifications UDDI (Universal Description Discovery and Integration) et WS-Discovery permettent de dcouvrir des services web. Tandis que UDDI dfinit un systme centralis bas sur
des inscriptions et des recherches dans des annuaires, WS-Discovery exploite des algorithmes
types multi diusion o lon peut se passer dannuaires centraliss.
Les annuaires UDDI peuvent tre publiques, inter entreprise ou intra entreprise. Chaque
entre dans un annuaire reprsente un fournisseur de service. Une partie de cette entre est
consacre la prsentation du fournisseur de service (domaines dactivit, contacts, tarifs etc).
Une deuxime partie est ddie la description abstraite des services web prsents (lments
<types>, <import>, <message>, <portType> et <binding> du contrat WSDL). Les documents
reprsentant les descriptions abstraites des services sont nomms Types Models (tModel). La
recherche dun service se fait essentiellement en demandant lannuaire quels sont les services
qui supportent un certains tModel. Enfin chaque entre de lannuaire contient une troisime
partie qui stocke les liens vers les implmentations des services (lments <import> et <service>
du contrat WSDL).
WS-Discovery dfinit deux messages hello et bye permettant un service de se dclarer ou de
se retirer lorsquil se connecte ou se dconnecte un rseau. Ces messages sont relays par multi
diusion pour atteindre les clients potentiels du service. Ces derniers ont aussi leur disposition deux messages probe et resolve pour lancer une recherche dun service sur le rseau.
Pour limiter la consommation excessive de bande passante des protocoles multi diusion, WSDiscovery permet dimplmenter des services spciaux dit proxy de dcouverte pour sappuyer sur
une certaine centralisation de la liste des services disponibles sur le rseau.
1012
WS-Federation
La spcification WS-Federation tend la spcification WS-Trust pour aner le modle dauthentification didentits entre dirents domaines de confiance. WS-Federation permet un mme
utilisateur de naviguer entre dirents domaines fdrs, sans avoir sauthentifier explicitement chaque fois. WS-Federation dfinit notamment les messages SOAP que schangent les
domaines pour rendre une telle navigation possible. Dun point de vue pratique, cette spcification vise pallier plusieurs problmes couramment rencontrs dans les organisations et les
entreprises tels que :
La dicult pour obtenir les autorisations quun agent dune organisation avec laquelle on
collabore a dans le cadre dune opration donne.
La dicult pour authentifier une mme personne au travers de ses direntes adresses email (professionnelles, personnelles etc).
La dicult pour dfinir le degr de sensibilit des donnes prives dune identit en fonction des contextes des oprations quelle eectue.
WS-Coordination
La spcification WS-Coordination permet plusieurs services web de coordonner leurs oprations
dans le cadre dun travail quils ont raliser en commun. Chaque travail est identifi par un
identificateur unique nomm contexte de coordination. Cet identificateur se retrouve dans des
enttes SOAP de chaque message SOAP chang dans le cadre de ce travail. WS-Coordination
dfinit ces enttes ainsi que leurs significations (requte pour demander deectuer une opration, refus ou incapacit de faire une opration, terminaison dune opration, time out dune
opration etc). Remarquez que certains travaux en commun tels quune transaction 2PC ncessitent la prsence dun service qui joue le rle particulier de coordinateur. Dautres travaux
tels que llection dun service parmi plusieurs services nont pas besoin de coordinateurs. WSCoordination gre ces deux types de travaux et lensemble des travaux possibles est ouvert. Cette
extensibilit se traduit par dautres spcifications WS-* telles que WS-AtomicTransaction qui se
basent sur les possibilits de WS-Coordination pour dfinir des travaux prcis.
WS-AtomicTransaction et WS-BusinessActivity
La spcification WS-AtomicTransaction se sert des possibilits de WS-Coordination pour permettre deectuer des transactions ACID volatiles ou durables, distribues sur plusieurs services.
Pour cela WS-AtomicTransaction exploite les algorithmes styles 2PC. Cette spcification sera
implmente par le DTC (Distributed Transaction Coordinator) de Windows Vista. Elle supporte
notamment les notions de transactions volatiles et durables.
La spcification WS-BusinessActivity se sert des possibilits de WS-Coordination pour permettre
deectuer des transactions longues distribues sur plusieurs services. Les transactions longues
viennent pallier limpossibilit de verrouiller certaines ressources pendant la dure dune transaction ACID. Aussi, une telle ressource est mise jour ds le dbut de la transaction longue.
Sil savre que la transaction choue, le coordinateur en informe chaque participant. Libre
eux alors deectuer une action compensatoire pour remettre leurs ressources dans leurs tats
initiales. Une consquence de cette faon de procder est que lon na pas de notion disolation
entres plusieurs transactions longues. Ainsi, il faut tre conscient quun participant ne peut pas
forcment remettre une ressource dans son tat initial puisque cette dernire a pu tre modifie
entre temps dans le cadre dune autre transaction.
1013
WS-Enumeration
La spcification WS-Enumeration permet de demander une grosse quantit dinformation un
service. Une telle demande se traduit par ltablissement dune session WS-Enumeration qui
se charge de grer la squence des messages envoys du service vers le demandeur. Le suivit
de lavancement du curseur sur les informations transmettre peut se faire aussi bien du ct
demandeur que du ct service. Ce suivi peut mme ventuellement tre bascul durant une
mme session, par exemple du serveur vers le demandeur pour librer le serveur de la gestion
de lavancement lorsquil dtecte que le temps de latence entre les messages devient prohibitif.
WS-Eventing
La spcification WS-Enumeration permet dimplmenter dune manire standard le modle
dchange de message abonnement/notification dcrit en page 991.
WS-Management
La spcification WS-Management a pour but duniformiser la supervision des systmes dinformation. Le terme systme dinformation doit ici tre pris au sens large et englobe la fois le software (tels quun serveur IIS ou un systme dexploitation) et le hardware (tels quun SmartPhone
ou un Pocket PC). WS-Management dfinit des messages SOAP modlisant les oprations qui
sont exiges par toutes les solutions de supervision. Parmi ces oprations, citons la dcouverte
de la prsence de ressources de supervision, le paramtrage de ces ressources et lobtention de
renseignements sur ltat courant du systme. WS-Management est supporte par les acteurs
majeurs du march tels que Microsoft, Intel, AMD, Dell ou Sun.
Introduction WCF
(Windows Communication Framework)
WCF (Windows Communication Framework anciennement nomm Indigo) est un framework qui
sera une partie intgrante de Windows Vista. Il sera aussi disponible pour Windows XP. Ce framework vise unifier les direntes technologies de communication proposes par Microsoft jusquici savoir : ASMX, .NET Remoting, COM+/Enterprise Services, System.Messaging, MSMQ
et WSE. La compatibilit avec ces technologies permettra chaque entreprise de migrer son
rythme vers WCF.
WCF permet dtablir des communications entre des endpoints WCF (que lon pourrait traduire
par point de terminaison). Chaque endpoint est caractris par trois composantes :
Ses informations de liaison (binding) qui dfinissent le type de protocole avec lequel il sait
communiquer.
Ses contrats au sens o ce que lon a pu voir dans le prsent chapitre (structure et smantique dutilisation).
Larchitecture de WCF fait en sorte de bien sparer chacune de ces composantes. En tant que
dveloppeur, vous serez surtout concern par la notion de contrat puisque les notions dadresse
et dinformation de liaison ne sont significatives que durant le dploiement et la maintenance.
1014
WCF supportera la plupart des spcifications WS-* non encore implmentes par WSE que nous
venons de prsenter. La conception de WCF fait en sorte de masquer la complexit de ces spcifications aux dveloppeurs. Aussi, vous aurez accs toutes ces fonctionnalits au travers de
simples paramtres de fichiers de configuration et il est probable que vous nentendrez parler
des spcifications WS-* sous jacente que trs rarement.
A
Les mots-cls du langage C 2.0
Les mots-cls en italique existent dans le langage C++ mais ont une utilisation ou une signification dirente.
Le terme C 2 est prcis pour un mot-cl introduit avec cette version du langage. Trs peu
Rfrence
abstract
as
base
bool
break
byte
1016
case
catch
char
checked
class
Dfinition dune classe, page 396. La contrainte type valeur/type rfrence, page 477
const
continue
decimal
default
delegate
Les dlgations et les dlgus, page 378. Introduction aux mthodes anonymes de C 2, page 524
do
double
else
enum
event
explicit
extern
extern alias C 2
false
finally
fixed
float
for
foreach
get
1017
global C 2
goto
if
implicit
in
int
interface
internal
Les niveaux de visibilit des membres, page 417. Les niveaux de visibilit protg et interne protg, page 445
is
lock
long
namespace
new
null
object
operator
out
Rcupration dinformation partir dune mthode (les paramtres out), page 404
override
params
partial C 2
private
protected
Les niveaux de visibilit des membres, page 417. Les niveaux de visibilit protg et interne protg, page 445
1018
public
readonly
ref
return
Rcupration dinformation partir dune mthode (les paramtres out), page 404
sbyte
sealed
set
short
sizeof
stackalloc
static
string
struct
switch
this
throw
true
try
typeof
uint
ulong
unchecked
unsafe
1019
ushort
using
value
virtual
volatile
void
where
while
yield break C 2
yield return C 2
B
Nouveauts .NET 2.0
Assemblage
Lutilisation de lattribut AssemblyKeyFile pour signer un assemblage est maintenant viter.
Il est prfrable dutiliser les options /keycontainer et /keyfile du compilateur csc.exe ou les
nouvelles proprits du projet de Visual Studio 2005 (page 30).
Le nouvel attribut System.Runtime.CompilerServices.InternalsVisibleToAttribute permet de spcifier des assemblages qui ont accs aux types non publics de lassemblage sur lequel
il sapplique. On parle alors dassemblages amis (page 27).
Loutil ildasm.exe 2.0 prsente par dfaut la possibilit dobtenir des statistiques quant la
taille en octets de chaque section dun assemblage et quant lachage des informations sur les
mtadonnes. Avec ildasm.exe 1.x il fallait utiliser le commutateur /adv en ligne de commande
afin de les obtenir (page 24).
1022
CLR
Un bug majeur du CLR version 1.x rendant possible la modification dun assemblage sign
numriquement a t corrig en version 2.0 (page 28).
La classe System.GC prsente les nouvelles mthodes AddMemoryPressure() et RemoveMemoryPressure() permettant de fournir au GC une indication quant au volume des ressources non
gres dtenues (page 123). Une autre mthode CollectionCount(int generation) permet
dobtenir le nombre de collectes eectues pour une gnration donne (page 123).
De nouvelles possibilits ont t ajoutes loutil ngen.exe pour supporter les assemblages exploitant la rflexion et pour automatiser la mise jour de la version compile dun assemblage
lorsquune de ses dpendances volue (page 113).
Linterface ICLRRuntimeHost utilis partir du code non gr pour hberger le CLR vient remplacer linterface ICorRuntimeHost. Elle permet davoir accs une nouvelle API permettant au
CLR de dlguer un certains nombre de responsabilits cruciales telles que le chargement des
assemblages, la gestion des threads ou la gestion des allocations mmoire. Cette API nest pour
linstant quutilise par lhte du moteur dexcution de SQL Server 2005 (page 100).
Trois nouveaux mcanismes dnomms rgion dexcution contrainte (CER), finaliseur critique et
rgion critique (CR) permettent daccrotre la fiabilit des applications telles que SQL Server 2005
susceptibles de faire face des pnuries de ressources (page 125, page 129 et page 129).
Un mcanisme dit de portail de mmoire est exploitable pour valuer avant un traitement si
lon disposera dassez de mmoire (page 127).
Vous pouvez maintenant terminer rapidement un processus en appelant la mthode statique
FailFast() de la classe System.Environment. Cette mthode ne prend pas de prcautions telles
que lexcution des finaliseurs ou des blocs finally en attente (page 136).
Dlgu
Un dlgu peut rfrencer une mthode gnrique ou une mthode dun type gnrique. On
voit alors apparatre la notion de dlgu gnrique (page 491).
Grce aux nouvelles surcharges de la mthode Delegate.CreateDelegate(Type, Object, MethodInfo) il est possible de rfrencer une mthode statique et son premier argument partir
dun dlgu. Les appels aux dlgus nont alors pas besoin de ce premier argument et cette
pratique sapparente un appel de mthode dinstance (page 538).
Linvocation de mthodes au travers de dlgus est maintenant beaucoup plus performante.
Synchronisation
Vous pouvez passer simplement des informations un thread que vous crez grce la nouvelle
dlgation ParametrizedThreadStart. En outre, de nouveaux constructeurs de la classe Thread
vous permettent de fixer la taille maximale en octets de la pile dun thread (page 140).
1023
La classe Interlocked prsente de nouvelles mthodes et permet de traiter dautres types tels
que IntPtr ou double (page 145).
La classe WaitHandle prsente une nouvelle mthode statique SignalAndWait(). En outre,
toutes les classes drivant de WaitHandle prsentent une nouvelle mthode statique OpenExisting() (page 153).
La classe EventWaitHandle permet de se passer des deux classes AutoResetEvent et ManualResetEvent qui en drivent. De plus, elle permet de nommer un vnement et donc, de le partager
entre plusieurs processus (page 155).
La nouvelle classe Semaphore permet dexploiter des smaphores Windows partir de votre code
gr (page 157).
La nouvelle mthode SetMaxThreads() de la classe ThreadPool permet de modifier le nombre
de threads du pool de threads partir du code gr (page 167).
Le framework .NET 2. 0 propose des nouvelles classes qui permettent de capturer et de propager
le contexte dexcution du thread courant un autre thread (page 180).
Scurit
La classe System.Security.Policy.Gac permet de reprsenter un nouveau type de preuve bas
sur la prsence dun assemblage dans le rpertoire GAC. (page 188).
Les nouvelles classes de permissions suivantes ont t ajoutes : System.Security.Permissions.KeyContainerPermission,
System.Net.NetworkInformation.NetworkInformationPermission, System.Security.Permissions.DataProtectionPermission, System.Net.Mail.
SmtpPermission, System.Data.SqlClient.SqlNotificationPermission, System.Security.
Permissions.StorePermission, System.Configuration.UserSettingsPermission, System.
Transactions.DistributedTransactionPermission et System.Security.Permissions.GacIdentityPermission.
La classe IsolatedStorageFile prsente les nouvelles mthodes suivantes : GetUserStoreForApplication(), GetMachineStoreForAssembly(), GetMachineStoreForDomain() et GetMachineStoreForApplication() (page 205).
Le framework .NET 2005 permet de lancer un processus fils dans un contexte de scurit dirent
de celui de son processus parent (page 135).
Le framework .NET 2005 prsente de nouveaux types dans lespace de nom System.Security.
Principal permettant de reprsenter et de manipuler des identificateurs de scurit (page 209).
Le framework .NET 2005 prsente de nouveaux types dans lespace de nom System.Security.
AccessControl pour manipuler les droits daccs Windows (page 211).
Le framework .NET 2005 prsente de nouvelles implmentations dalgorithme de hachage disponibles dans lespace de noms System.Security.Cryptography.
Le framework .NET 2005 prsente plusieurs classes permettant davoir accs aux fonctionnalits
prsentes par lAPI de protection de donnes (DPAPI) de Windows (page 225).
La classe System.Configuration.Configuration permet de grer trs simplement le fichier de
configuration dune application. Notamment, vous pouvez lexploiter pour encrypter vos donnes de configuration (page 718).
Le framework .NET 2005 prsente de nouveaux types dans les espaces de noms System.
Security.Cryptography.X509Certificates et System.Security.Cryptography.Pkcs qui sont
1024
spcialiss dans la manipulation des standards de certification X.509 et CMS/Pkcs7 ainsi que dans
la manipulation des listes de certificats (page 230).
Le nouvel espace de noms System.Net.Security prsente les nouvelles classes SslStream et
NegociateStream qui permettent dutiliser les protocoles SSL, NTLM et Kerberos de scurisation de flots de donnes (page 660).
Rflexion/Attribut
Vous avez maintenant la possibilit de charger un assemblage en mode reflection only (page
237). Dailleurs, la classe AppDomain prsente le nouvel vnement ReflectionOnlyPreBindAssemblyResolve dclench juste avant le chargement dun assemblage destin tre utilis par
le mcanisme de rflexion (page 92).
Le framework .NET 2005 introduit la notion dattribut conditionnel. Un tel attribut a la particularit dtre pris en compte par le compilateur C 2 que si un certain symbole est dfini (page
255).
Interoprabilit
Les notions de pointeur sur fonction et de dlgu sont maintenant interchangeables grce
aux nouvelles mthodes GetDelegateForFunctionPointer() et GetFunctionPointerForDelegate() de la classe Marshal (page 272).
La classe HandleCollector permet de fournir au ramasse-miettes une estimation de la quantit
de ressources non gres couramment dtenues (page 277).
Les nouvelles classes SafeHandle et CriticalHandle permettent dexploiter les handles Windows
plus ecacement quavec la classe IntPtr (page 278).
Les outils tlbimp.exe et tlbexp.exe prsentent la nouvelle option /tlbreference permettant
de fournir explicitement une libraire de type sans passer par la base des registres. Cela permet
la cration denvironnements de compilation moins fragiles.
Visual Studio 2005 prsente des facilits pour exploiter partir dune application .NET la technologie reg free COM de Windows XP qui permet dutiliser une classe COM sans lenregistrer dans
la base des registres (page 287).
Les structures relatives la technologie COM telles que BINDPTR, ELEMDESC ou STATDATA ont
t dplaces de lespace de noms System.Runtime.InteropServices vers le nouvel espace de
noms System.Runtime.InteropServices.ComTypes. En outre, cet espace de noms contient de
nouvelles interfaces qui redfinissent certaines interfaces standard COM telles que IAdviseSink
ou IConnectionPoint.
Lespace de noms System.Runtime.InteropServices contient de nouvelles interfaces telles que
_Type, _MemberInfo ou _ConstructorInfo qui permettent au code non gr davoir accs au mcanisme de rflexion sur les lments du code gr. Bien entendu, les classes gres concernes
(Type, MemberInfo, ConstructorInfo etc) implmentent ces interfaces.
C 2.0
Le chapitre 13 est consacr la fonctionnalit phare de .NET 2.0 : la gnricit.
Le compilateur csc.exe prsente les nouvelles options /keycontainer, /keyfile, /delaysign
(page 33), /errorreport et /langversion.
1025
C 2.0 apporte les notions de qualificateur dalias despaces de noms (page 320) de qualificateur
global: : (page 321) et dalias externe (page 322) pour viter certains problmes de conflits
didentificateurs.
C 2.0 prsente les nouvelles directives de compilation #pragma warning disable et #pragma
warning restore (page 316).
Le compilateur C 2.0 est capable dinfrer la dlgation adquate lors de la cration dun dlgu (page 380). Ceci rend le code plus lisible.
Le framework .NET 2005 introduit la notion de type nullable exploitable partir dune syntaxe
spciale en C 2 (page 385).
C 2.0 permet dtaler la dfinition dun mme type sur plusieurs fichiers sources dun mme
module (page 392).
C 2.0 permet dassigner une visibilit dirente aux accesseurs dune proprit ou dun indexeur (page 419).
C 2.0 permet la dfinition de classes statiques (page 432).
C 2.0 permet de dclarer un champ de type tableau de taille fixe dlments de types primitifs
au sein dune structure (page 507).
Lintellisense de Visual Studio 2005 vous propose les balises XML des commentaires de types ///
(page 324).
Exceptions
La classe SecurityException et Visual Studio 2005 ont t amliors de faon vous permettre
de tester et de dboguer plus facilement votre code mobile (page 205).
Visual Studio 2005 prsente un assistant trs pratique pour obtenir la totalit des donnes relatives une exception qui survient durant le dboguage (page 510).
Visual Studio 2005 vous permet dtre inform lorsquun vnement problmatique connu du
CLR survient. Ces vnements se traduisent parfois par une exception gre (page 522).
Collection
Lensemble des types de collection du framework .NET a t compltement revu afin de tenir
compte des bnfices du support de la gnricit. Ces types font lobjet du chapitre 15. En page
595 nous exposons un tableau de correspondance entre les types des deux espaces de noms
System.Collections et System.Collections.Generic.
Dbogage
Lespace de noms System.Diagnostics prsente les nouveaux attributs DebuggerDisplayAttribute, DebuggerBrowsable, DebuggerTypeProxyAttribute et DebuggerVisualizerAttribute qui
vous permettent de personnaliser lachage des tats de vos objets durant le dbogage (page
617).
Vous avez maintenant accs la fonctionnalit Edit And Continue permettant de modifier le code
en cours de dbogage.
1026
Classes de bases
Les types primitifs (entier, boolen, nombre virgule flottante, dcimaux) prsentent la mthode TryParse() qui permet de parser une valeur dans une chane de caractres sans lancer
dexception en cas dchec (page 358).
Le framework .NET 2005 prsente plusieurs implmentations drives de la classe abstraite
System.StringComparer qui permettent de comparer des chanes de caractres en tenant
compte ou non de la culture et de la casse (page 585).
La nouvelle classe DriveInfo permet de reprsenter et de manipuler des volumes (page 607).
Le framework .NET 2005 introduit la notion de source de traage permettant un paramtrage
plus fin des traces (page 622). En outre, les nouvelles classes suivantes de listeners ont t
ajoutes : ConsoleTraceListener, DelimitedListTraceListener, XmlWriterTraceListener
et WebPageTraceListener. (page 621).
De nombreuses fonctionnalits ont t ajoutes la classe System.Console afin de rendre laffichage des donnes plus convivial (page 629).
Entres/Sorties
Le framework .NET 2005 prsente la nouvelle classe System.Net.HttpListener qui permet dexploiter la couche HTTP.SYS de Windows XP SP2 et Windows Server 2003 pour dvelopper un
serveur HTTP (page 654).
En .NET 2005, les classes de lespace de noms System.Web.Mail sont maintenant obsoltes. Pour
envoyer des mails, il faut utiliser les classes de lespace de noms System.Net.Mail. En outre,
le nouvel espace de noms System.Net.Mime contient des classes pour le support de la norme
MIME (page 656).
Des nouvelles mthodes permettent de lire ou dcrire dans un fichier en un seul appel (page
636).
De nouvelles classes sont disponibles pour compresser/dcompresser un flot de donnes (page
659).
Une nouvelle version non gre System.IO.UnmanagedMemoryStream de la classe MemoryStream
permet dviter la copie des donnes sur le tas des objets du CLR. Elle est donc plus ecace (page
659).
La nouvelle classe System.Net.FtpWebRequest implmente un client FTP (page 653).
Le nouvel espace de noms System.Net.NetworkInformation contient des types qui permettent
de connatre les interfaces rseaux disponibles sur la machine, dinterroger leurs tats, dtre
averti des changements dtats et dobtenir des statistiques sur le trafic (page 649).
Des services de cache de ressources web sont disponibles dans le nouvel espace de noms System.
Net.Cache (page 653).
La nouvelle classe System.IO.Ports.SerialPort permet dexploiter un port srie dune manire synchrone ou par vnement (page 660).
1027
Windows Forms 2.0 prsente la classe BackgroundWorker qui permet de standardiser le dveloppement dopration asynchrone au sein dun formulaire (page 676).
Lapparence (i.e le style visuel) des contrles est mieux gre avec Windows Forms 2.0 car il ny a
plus besoin davoir recours la DLL comctl32.dll pour obtenir un style la Windows XP (page
679).
Un rapide aperu des nouveaux contrles Windows Forms 2.0 est disponible en page 681.
Windows Forms 2.0 et Visual Studio 2005 contiennent un framework et des outils de dveloppement rapide de fentres de prsentation et ddition de donnes (page 688).
Windows Forms 2.0 prsente les nouvelles classes BufferedGraphicsContext et BufferedGraphics
qui permettent de grer finement un mcanisme de double buering (page 703).
ADO.NET 2.0
ASO.NET 2.0 prsente de nouvelles classes abstraites telles que DbConnection ou DbCommand
dans le nouvel espace de noms System.Data.Common qui implmentent les interfaces IDbConnection ou IDbCommand. Lutilisation de ces nouvelles classes pour sabstraire dun fournisseur
de donnes sous-jacent est maintenant prfrer lutilisation des interfaces (page 712).
ADO.NET 2.0 prsente une architecture de classes volues de type fabrique abstraite qui permet de produire du code daccs aux donnes compltement indpendant du fournisseur de
donnes sous-jacent (page 712).
Les quatre fournisseurs de donnes fournis avec le framework prsentent de nouvelles facilits
permettant de construire une chane de connexion indpendamment du fournisseur de donnes sous-jacent (page 716).
ADO.NET 2.0 prsente un framework permettant de naviguer programmatiquement dans le
schma dun SGBD (page 718).
Le moteur dindexation exploit en interne par le framework lorsque vous utilisez des instances
des classes DataSet et DataTable a t refait de faon tre plus performant lors du chargement
et de la manipulation des donnes.
Les instances des classes DataSet et DataTable sont maintenant srialisables au format binaire
grce la nouvelle proprit SerializationFormat RemotingFormat{get;set;}. Vous pouvez
vous attendre un gain dun facteur de 3 8 par rapport la srialisation XML.
La classe DataTable est moins dpendante de la classe DataSet car les possibilits XML de cette
dernire (dcrites en page 775) lui ont t ajoutes.
La nouvelle mthode DataTable DataView.ToTable() permet de construire une DataTable
ayant une copie du contenu dune vue (page 730).
ADO.NET 2.0 ore un pont entre le mode connect et le mode dconnect en permettant aux
classes DataSet/DataTable et DataReader de travailler ensembles (page 735).
Les DataSet typs de ADO.NET 2.0 prennent en compte directement la notion de relation entre
tables. En outre, grce aux types partiels, le code gnr et votre propre code sont spars dans
deux fichiers distincts (page 731). Enfin, vous pouvez coder des requtes SQL types avec la
nouvelle notion de TableAdapter (page 733).
ADO.NET 2.0 ore la possibilit doptimiser la sauvegarde dans une base des modifications
eectues sur un cache de donnes en permettant la mise jour par lot des modifications (page
727).
1028
Transaction
Le nouvel espace de noms System.Transactions (contenu dans la DLL Systems.Transactions.
dll) propose la fois un modle de programmation transactionnelle unifi et un nouveau moteur transactionnel dont la particularit est dtre trs performant sur certains types de transactions lgres (page 741).
XML
Les performances de toutes les implmentations utilises pour manipuler des donnes stockes
dans un format XML ont t amliores significativement (dun facteur 2 4 lors des scnarios
dutilisation classiques selon Microsoft).
La nouvelle classe System.Xml.XmlReaderSettings permet de prciser les vrifications qui
doivent tre faites lorsque lon lit des donnes XML au moyen dune classe drive de XmlReader
(page 766).
Il est possible de valider partiellement un arbre DOM charg dans une instance de XmlDocument
(page 771).
Il est maintenant possible de modifier un arbre DOM stock dans un XmlDocument au moyen
de lAPI type curseur de XPathNavigator (page 774).
La classe XslCompiledTransform remplace la classe XslTransform maintenant devenue obsolte. Son atout principal et de compiler les programmes XSLT en code MSIL avant dappliquer
une transformation. Selon Microsoft, cette nouvelle implmentation amliore les performances
dun facteur de 3 4 (page 774). En outre Visual Studio 2005 permet maintenant de dboguer
un programme XSLT (page 784).
Le support de la classe DataSet pour XML a t amlior. Vous pouvez maintenant charger des
schmas XSD avec des noms rpts dans dirents espaces de noms et charger du XML avec
plusieurs schmas en ligne. En outre, les mthodes de chargement et de sauvegarde des donnes
stockes en XML ont t ajoutes la classe DataTable (page 775).
La version 2005 de SQL Server apporte des nouvelles facilits quant lintgration de donnes
XML dans une base de donnes relationnelle (page 779).
La srialisation XML permet maintenant de srialiser les informations de type nullable et les
instances de types gnriques. En outre, il existe un nouvel outil sgen.exe permettant de pr
gnrer un assemblage contenant le code pour srialiser un type (page 779).
1029
.NET Remoting
Le nouveau canal IpcChannel est ddi la communication entre processus tournants sur la
mme machine. Son implmentation est base sur la notion de pipe nomm Windows (page
830).
Si vous utilisez un canal de type TCP, vous avez maintenant la possibilit dutiliser les protocoles
NTLM et Kerberos pour authentifier lutilisateur Windows sous lequel sexcute le client, pour
crypter les donnes changes et pour impersonifier vos requtes (page 816).
De nouveaux attributs de lespace de noms System.Runtime.Serialization permettent de grer les problmes inhrents lvolution dune classe dont les instances sont srialises (page
791).
Il est possible de consommer une instance dun type gnrique ferm avec la technologie .NET
Remoting, que vous soyez en mode CAO ou WKO (page 499).
ASP.NET 2.0
Visual Studio .NET 2005 est fourni avec un serveur web permettant de tester et de dboguer vos
applications web en cours de dveloppement (page 862).
Il est ais de se servir de la couche HTTP.SYS pour construire un serveur web qui hberge
ASP.NET sans aucunement avoir recours IIS (page 865).
ASP.NET 2.0 prsente un nouveau modle de construction des classes reprsentant les pages.
Ce modle est bas sur les classes partielles et est dirent de celui dASP.NET 1.x (page 869).
La directive CodeBehind dASP.NET v1.x nest plus supporte (page 871).
En ASP.NET 2.0, le modle de compilation dynamique a considrablement volu et se base
maintenant sur plusieurs nouveaux rpertoires standard (page 871). De plus, ASP.NET 2.0 prsente deux nouveaux modes de pr compilation (page 872 et page 872).
Pour pallier les eets des viewstate volumineux de ASP.NET 1.x, ASP.NET 2.0 stocke les informations dans la chane base64 plus ecacement et introduit la notion de controlstate (page 879).
ASP.NET 2.0 prsente une nouvelle technique pour permettre une page denvoyer un vnement postback une autre page (page 880).
Certains vnements ont t rajouts au cycle de vie dune page (page 883).
ASP.NET 2.0 prsente une infrastructure pour permettre deectuer le traitement dune mme
requte sur plusieurs threads du pool. Cela permet notamment dviter la pnurie de threads du
pool qui survient lorsque plusieurs traitements asynchrones longs sont raliss simultanment
(page 886).
De nouveaux vnements ont t ajouts la classe HttpApplication (voir les MSDN).
La manipulation des fichiers de configuration est simplifie grce lintellisense de Visual Studio
2005, une nouvelle interface web, une nouvelle interface graphique intgre dans la console de
configuration dIIS et grce des nouvelles classes de base permettant de manipuler programmatiquement et surtout, dune manire fortement type, les donnes XML de ces fichiers (page
890).
ASP.NET 2.0 prsente un framework permettant de grer dune manire standard les vnements qui surviennent dans la vie dune application web (page 906).
1030
Vous pouvez configurer ASP.NET 2.0 pour quil dtecte sil peut stocker lidentifiant de session
dans un cookie cot client et pour se mettre automatiquement en mode URI si le navigateur du
client ne permet pas les cookies (page 901).
ASP.NET 2.0 vous permet de fournir votre propre mcanisme de gestion de sessions ou de gestion dIDs de sessions (page 902).
Le moteur de cache dASP.NET 2.0 prsente plusieurs nouvelles possibilits intressantes. Vous
pouvez maintenant utiliser la sous directive VaryByControl dans vos pages (page 920). Vous
pouvez substituer des fragments de page dynamiques dans vos pages caches (page 923). Vous
pouvez associer des donnes caches des dpendances vers des tables dun SGBD type SQL
Server (page 926). Enfin, vous pouvez crer vos propres types de dpendances (page 928).
ASP.NET 2.0 prsente plusieurs nouveaux contrles serveur permettant de se lier dclarativement une source de donne (page 930).
ASP.NET 2.0 prsente une nouvelle hirarchie de contrles serveurs de prsentation et ddition
de donnes. Ces contrles ont tous la particularit de pouvoir exploiter un contrle de source
de donnes pour laccs en lecture et en criture (page 935).
ASP.NET 2.0 prsente une syntaxe simplifie pour les templates (page 939).
ASP.NET 2.0 prsente la notion de master page qui permet de rutiliser simplement un modle
de design de page sur les pages dun site (page . 947).
ASP.NET 2.0 prsente une infrastructure extensible permettant dinsrer dans vos pages des
contrles daide la navigation dans un site (page 954).
Avec ASP.NET 2.0 vous pouvez utiliser le mode dauthentification Forms sans avoir recours aux
cookies (page 962).
ASP.NET 2.0 permet de grer les donnes dauthentification des utilisateurs ainsi que les rles
auxquels ils peuvent appartenir dune manire standard au moyen dune base de donnes
(pages 962 et 964). En outre, plusieurs nouveaux contrles serveurs viennent grandement
simplifier le dveloppement dapplication ASP.NET supportant lauthentification (page 965).
ASP.NET 2.0 prsente un framework permettant de stocker et de manipuler dune manire standard les donnes personnelles chaque utilisateur (page 966).
ASP.NET 2.0 prsente un framework permettant de faciliter la maintenance de lapparence dun
site (page 971).
ASP.NET 2.0 prsente un framework permettant de crer des portails webs au moyen de ce que
lon nomme les webParts (page (page 973).
ASP.NET 2.0 prsente un framework permettant de modifier le rendu dune page HTML si la
requte HTTP initiatrice a t mise partir dun systme avec un cran de petite taille, type
systme mobile. Concrtement, le rendu de la plupart des contrles serveurs est modifi pour
tenir moins de place. Cette modification se fait grce des objets que lon appelle adaptateurs.
Les adaptateurs sont sollicits automatiquement et implicitement par ASP.NET au moment de
la phase du rendu. Larticle Inside the ASP.NET Mobile Controls des MSDN constitue un bon
point de dpart pour aborder cette facette dASP.NET 2.0.
Web Service
Les classes proxys gnres avec loutil wsdl.exe prsentent un nouveau modle dappel asynchrone annulable (page 998).
C
Introduction aux design patterns
Les design patterns objets sont des motifs dinteraction entre classes, des motifs dinteraction
entre objets, et des motifs de dfinition de classes. La notion de pattern est apparue dans les
annes 1970, dans le domaine de larchitecture classique (btiment etc). Elle a t popularise
dans le domaine de la programmation oriente objet la fin des annes 90 grce cet ouvrage
de rfrence :
Design Patterns, Elements of Reusable Object-Oriented Software
ADDISON-WESLEY 1994
Erich Gamma, Richard Helm, Ralph Johnson, John Vlissides
ISBN : 0-201-63361-2
La notion de pattern sest alors invite dans dautres domaines de la conception logicielle, notamment en architecture des applications distribues. Louvrage suivant, qui lui aussi fait rfrence,
met en vidence des motifs rcurrents (i.e des patterns) concernant la gestion de la persistance
et lchange de donnes entres les tiers dune application distribue :
Patterns of Enterprise Application Architecture
ADDISON-WESLEY 2002
Martin Fowler, David Rice, Matthew Foemmel, Edward Hieatt, Robert Mee, Randy Staord
ISBN : 0-321-12742-0
Microsoft sintresse et communique depuis quelques annes sur les patterns de conception logicielle avec ses technologies au travers de son groupe de travail Pattern & Practices (PAG). Plus
dinformations ce sujet sont disponibles lURL http://msdn.microsoft.com/practices/.
Les initis qui pensent en terme de patterns parviennent concevoir rapidement des architectures de meilleure qualit. De plus leurs capacits de communication et dabstraction sont bonifies grce aux noms des patterns. Le dveloppeur a tout intrt les assimiler et les utiliser.
Chaque pattern dfinit un comportement plus ou moins souple, qui rpond une ou plusieurs
problmatiques rcurrentes de conception logiciel. Voici quelques exemples de problmatiques
introduites dans le prsent ouvrage suivies du nom du pattern utilis pour les rsoudre :
1032
La cration des objets sans spcifier explicitement leur classe concrte (Fabrique abstraite).
Ce pattern est exploit par la technologie ADO.NET notamment pour viter une application de se coupler avec un fournisseur de donnes particulier (voir page 715). Il est aussi
utilis par la technologie .NET Remoting pour viter dexposer les implmentations des
objets serveurs aux clients (voir page 802).
Linterception des appels un objet (Proxy). Plus de dtails sont disponibles page 817.
La dfinition dune relation one-to-many entre un objet sujet et des objets de faon ce
que chacun des objets dpendants soient notifis des changements dtat de lobjet sujet
(Observateur). La notion dvnement de C constitue une excellente alternative limplmentation de ce pattern (voir page 411).
La possibilit de pouvoir accder aux lments dune collection dune manire squentielle
sans se proccuper de la reprsentation physique de ces lments (Itrateur). C 2.0 prsente
des facilits pour implmenter ce pattern (voir page 542).
Permettre des objets qui ne se connaissent pas dinteragir grce une classe qui encapsule
ces interactions (Mdiateur). Ce pattern est exploit par la technologie Windows Form pour
faciliter le dveloppement des formulaires (voir page 667).
Crer volont des objets partir dun objet prototype connu par une rfrence de type une
interface qui ne contient quune mthode de clonage (Prototype). Linterface du framework
System.ICloneable prsente page 347 est dans une certaine mesure adapte ce rle.
Maintenir la liste de changements eectus durant une opration et coordonner la persistance de ces changements en grant les ventuels problmes dus la concurrence (Unit de
travail). Ce pattern est utilis pour sauver les changements eectus sur les donnes dun
DataSet dans la technologie ADO.NET (voir page 725).
Obtenir une implmentation thread-safe dune classe (SyncRoot). Plus de dtails sont disponibles en page 149.
Vous pouvez complter cette lecture par larticle Discover the Design Patterns Youre Already Using in the .NET Framework de Rob Pierry disponible dans le numro de Juillet
2005 de MSDN Magazine lURL : http://msdn.microsoft.com/msdnmag/issues/05/07/
DesignPatterns/default.aspx.
D
Les outils
Vous pouvez consulter le site http://sharptoolbox.com qui maintient une liste trs complte
des outils disponibles autour du dveloppement pour la plateforme .NET. Voici la liste des outils dont nous parlons dans le prsent ouvrage :
Assemblages
Scurit
ildasm.exe page 21
Reflector page 24
resgen.exe page 35
ilasm.exe page 41
permcalc.exe page 82
ILMerge page 17
Interoprabilit
Construction/Dploiement
msbuild.exe page 49
GACUtil.exe page 65
mage.exe page 77
mageui.exe page 77
guidgen.exe page 20
C
Profiling
1034
Windows Forms
ASP.NET 2.0
Service web
Mapping Objet/Relationnel
Sql Server
Diagnostic
.NET Remoting
Enfin, nous signalons lexistence de loutil NDepend. Cet outil analyse statiquement le code
compil de nimporte quelle application .NET et nest aucunement intrusif. En plus de calculer
la plupart des mtriques de code partir de cette analyse il sait dtecter un certain nombre de
problmes de design.
Index
2 Phase Commit, 742
2PC, voir 2 Phase Commit, 742
abonn ( un vnement), 411
abstract factory, voir fabrique abstraite, 715
abstraction, 453
Access Control Element, 211
Access Control List, 211
accesseur (vnement), 412
accesseur (proprit), 407
ACE, voir Access Control Element, 211
ACID (proprits), 741
ACL, voir Access Control List, 211
activation dun objet, 795
Activator, voir System.Activator, 242
ActiveX, 283
activit COM+, 298
adaptateur, 280
add (accesseur dvnement), 412
administrateur de baux, 806
ADO, 707
ADO.NET, 707
adresse IP, 641
agrgation (dun objet sur un autre), 398
al.exe, 37
alatoire, 600
algbre relationnelle, 706
algorithme symtrique, 219, 221
alias, 320
alias externes, 322
allocation, 337
allocation dynamique, 338
allocation statique, 338
AllowPartiallyTrustedCallersAttribute, 198
amies (notion C++), 396
1036
AssemblyDef (table du manifeste), 18
AssemblyInfo.cs, 25
AssemblyRef (table de mtadonnes de type),
19
atomique (opration), 146
attribut, 248
attribut
AttributeTargets, 250
ConditionalAttribute, 314, 315
attribut conditionnel, 255
attribut de contexte, 845
attribut personnalis, 251
attribut XML, 761
Attribute, voir System.Attribute, 251
Authenticode, 73, 230
auto-descriptif (format), 759
AutoEventWireUp, 884
Automation, 109
AutoResetEvent, 153
autorisation, voir permission, 191
bac sable, 205
BackgroundWorker, 676
bail, 805
base (mot-cl), 447
Base Class Library, voir BCL, 6
base des registres, 613
Base64 (encodage), 35
Basic String, voir BSTR, 284
BCL, 6
bcp.exe, 739
BehaviorEditorPart, 982
bibliothque de types, 279
bien connu (objet), voir WKO, 796
big-endian, 635
BindingList<T>, voir System.
ComponentModel.BindingList<T>,
579
BindingNavigator, 689
bindingRedirect, 106
BindingSource, 691
bits par pixel, 698
blob, 189
bloc de restitution de code, 867
bool (mot-cl), 356
bootstrapper, 78
boucles, 327
boucles, 331
boxing, 350
Index
bpp, voir bits par pixel, 698
branchement, 327
break (mot-cl), 333
browser, voir navigateur, 859
BSTR, 284
buer, voir mmoire tampon, 637
BueredWebEventProvider, 907
BYOT, 296
byte (mot-cl), 354
bytecode, 41
Cw, 737
C++/CLI, 272
cte cte, 65
CA, voir Certificate Authorities, 231
cache (ASP.NET), 918
cache de tlchargement, 85
cache des images natives, 66, 113
cache global des assemblages, 64, 113
cale, 96
call context, 856
callback procedure, voir Procdure de rappel,
666
CamelCase, 326
canal, 830
canal metteur, 830
canal rcepteur, 830
CAO, 799
capacit (classe List<T>), 578
capacit (classe StringBuilder), 376
capture dune variable, 531
CAS, 186
Cascading Style Sheet, 970
caspol.exe, 196
Cassini, 862
catalogue COM+, 302
catch (mot-cl), 511
CategoryAttribute, 685
cctor, voir constructeur de classe, 431
CCW, 288
Cdecl, 266
CER, voir rgions dexcution contraintes, 126
certificat, 231
certificat racine, 231
Certificate Authorities, 231
Certificate Store, 231
certmgr.exe, 231
CGI, 860
ChangePassword, 966
Index
channel, voir canal, 830
channel sink providers, voir fournisseur
dintercepteurs de messages, 833
char (mot-cl), 357
chargement explicitement, 108
chargement implicite, 108
chargeur dassemblages, 103
chargeur de classe, 109, 191
checked, 359
checked exception, voir exception contrle,
515
chemin
chemin relatif, 611
chemin, 611
chemin
chemin absolu, 611
chrome (WebParts), 975
CICS, 297
CIL, voir IL, 41
class (mot-cl), 396
class constructor, voir constructeur de classe,
431
class interface, voir interface de classe, 291
class loader, voir chargeur de classe, 109
classe, 395
classe abstraite, 453
classe de base, 444
classe drive, 444
classe finalise, 448
classe polymorphe, 449
classe statique, 432
clavier (gestion du), 675
cl de session, 222
cl trangre (DB), 706
cl primaire(DB), 706
cl prive, 222
cl publique, 222
CLI, 129
Client Activated Object, voir CAO, 799
clipboard, 679
closure, voir fermeture, 536
CLR Profiler, 120
CLS, 130
CMS/Pkcs7, 231
Code Access Security, voir CAS, 186
code groups, voir groupes de code, 194
code mobile, 185
code natif, 265
code-behind, 868
1037
code-bloat, 472
code-inline, 866
CodeAccessPermission, voir System.Security.
CodeAccessPermission, 191
codebase, 106
codepage, voir page de code, 635
COFF, voir PE/COFF, 17
collecte, 117
collecte partielle, 118
COM+ application, 300
commit, 741
Common Langage Infrastructure, voir CLI,
129
Common Langage Runtime (CLR), 87
Common Type System, voir CTS, 342
comparaison selon lquivalence, 345
comparaison selon lidentit., 345
Comparer<T>, voir System.Collections.
Generic.Comparer<T>, 588
Comparison<T>, voir System.
Comparison<T>, 588
compartimentation, voir Boxing, 350
compatibilit consommateur, 130
compatibilit extenseur, 130
Compilation, 313
component services, voir Services de
composants, 305
composant (graphique), 667
composant COM, 279
composant logiciel enfichable, 305
composant servi, 299
composants, 15
COMTI, 297
conditions, 327
confiance aveugle (CAS), 198
confiance partielle (CAS), 198
Console, 629
Console Application, 666
ConsoleColor, 630
const (mot-cl), 398
Constrained Execution Regions, voir rgions
dexcution contraintes, 126
constructeur (dune classe), 422
constructeur de classe, 431
constructeur de copie, 349
constructeur de pile, 818
constructeur par dfaut, 422
constructeur statique, 431, 792
construction incrmentale, 54
1038
ConstructorInfo, voir System.Reflection.
ConstructorInfo, 243
container, voir conteneur, 678
conteneur, 678
Context, voir Context, 844
context-agile, 843
context-bound, 843
contexte (scurit), 168
contexte .NET, 842
contexte COM+, 300
contexte dexcution, voir Contexte .NET, 842
contexte gr, voir Contexte .NET, 842
contexte non gr, voir Contexte COM+, 842
contexte par dfaut, 842
continuation, 552
continue (mot-cl C ), 332
contrle (graphique), 667
contrle ActiveX, voir ActiveX, 283
contrle serveur web, 882
contrle serveurs HTML, 882
contrle utilisateur (ASP.NET), 912
contrainte (gnricit), 474
contrainte dintgrit (DB), 706
contraintes (DB), 728
contravariance, 485, 492
controlstate, 879, 917
cookies, 653
copie en profondeur, 348
copie superficielle, 348
coroutine, 552
covariance, 485, 492, 570
CR, voir rgion critique, 129
CreateUserWizard, 966
crible dEratosthne, 558
Critical Region, voir rgion critique, 129
CRMs, 297
CrossAppDomain, 831
CrossContextChannel, 849
csc.exe, 21
csc.exe, 317
CSS, voir Cascading Style Sheet, 970
ctor, voir constructeur, 422
CTS, 342
culture, 34
CurrentCulture, 40
CurrentUICulture, 38
curseur (flot de donnes), 633
custom attribute, voir attribut personnalis,
251
Index
DACL, voir Discretionary Access Control List,
211
Data Control Langage, 706
Data Definition Langage, 706
Data Manipulation Language, 706
Data Protection API, voir DPAPI, 225
Data Source Name, voir DSN, 717
DataGrid, 935
DataList, 940
DataSet typ, 731
DataView, 729
DataViewManager, 731
DbCommandBuilder, 726
DbConnectionStringBuilder, 716
DbProviderSupportedClasses, 716
DCL, voir Data Control Langage, 706
DCOM, 786
DDL, voir Data Definition Langage, 706
deadlocks, 144
decimal (mot-cl), 355
DeclarativeCatalogPart, 981
dcompartimentation, voir UnBoxing, 352
dcorateur, 657
deep copy, voir copie en profondeur, 348
default (mot-cl), 484
DeflateStream, 659
delaysign (option csc.exe), 34
delegate (mot-cl), 379
dlgation, 379
dlgu, 168, 344, 379
dlgu asynchrone, 172
dpassement de capacit, 359
dependentAssembly, 106
dployer des applications, 63
deployment pre-compilation, 871
DES, voir Digital Encryption Standard, 220
dsallocation, 337
descripteur de scurit, 211
dsrialisation, voir srialisation, 790
design pattern provider, 902, 956, 963, 964,
966
design-time, 678
destructeur, 423
DetailsView, 941
dterminisme, 146
devenv.exe, 49
Device Context, 695
dfsvc.exe, 83
Index
Digital Encryption Standard, 220
directives prprocesseur, 313
directory, voir rpertoire, 608
Discretionary Access Control List, 211
DISPID, 286
Dispose(), voir System.IDisposable, 425
Distributed Transaction Coordinator, 745
division par zro, 359
DLL hell, voir enfer des DLLs, 65
dllhost.exe, 303
DML, voir Data Manipulation Langage, 706
document XML, 759
DOM (Document Object Model), 769
Domain Specific Language, 8
domaine (DB), 705
domaine dapplication, 87
chargement des assemblages dune
manire neutre par rapport aux
domaines, 97
domaine dapplication par dfaut, 88
DOS (Disk Operating System), 665
dotnetfx.exe, 86
double (mot-cl), 355
double buering, 702
double pointeur, 505
double pointeur, 402
downcast, 452
download cache, voir cache de
tlchargement, 85
DPAPI, 225
drag & drop, 678
drapeau, 369
drive, voir volume, 607
droit daccs, 211
droits, voir permission, 191
droits daccs exclusifs (domaine de
synchronisation), 162
DSL, voir Domain Specific Language, 8
DSN, 717
DTC, voir Distributed Transaction
Coordinator, 745
DTP, 297
due time, voir chance de dmarrage, 171
durable (RM), 746
dynamic bind, voir lien dynamique, 238
early bind, voir lien prcoce, 238
cart type, 721
chance de dmarrage, 171
1039
ECMA, 9
ECMA, 29
lment XML, 761
EMF+ (Enhanced Meta File), 699
emprunt didentit, 210, 958
encapsulation, 418
encodage des caractres, 635
end point, voir point terminal, 796
endpoint WCF, 1013
enfer des DLLs, 65
Enregistration, 113
enregistrements (DB), 705
ensembles de permissions, 194
entte CLR, 17
entropie, 226
enum (mot-cl), 366
numrable, 539
numrateur, 539
numrations, 343
environnement lexical, 536
escalation policy, voir politique de lescalade,
129
espace de noms, 310
espace de noms
alias despace de noms, 311
tat dun objet, 396, 790
European Computer Manufacturers
Association, voir ECMA, 9
vnement, 155
vnement repositionnement
automatique, 156
vnement repositionnement manuel,
156
vnement, 411
vnement postback, 877
event (mot-cl), 412
EventDef, 19
EventLogWebEventProvider, 907
Evidence, voir System.Security.Policy.
Evidence, 190
evidence, voir preuve, 191
exception applicative, 125, 522
exception asynchrone, 125, 522
exception contrle, 515
exceptions
gestionnaire dexception, 519
exceptions, 509
exceptions vrifies, voir exception controle,
515
1040
Exit(), 136
explicit (mot-cl), 436
ExportedTypeDef (table du manifeste), 18
expression rgulire, 625
extension dinterface, 456
extension SOAP, 1009
extern (mot-cl), 266
eXtreme Programming, 7
fabrique abstraite, 715
facteur dexpansion, 579
facteur de chargement, 585
factory (design pattern), 802
FailFast(), 136
fentres de pile, 43, 267
fermeture, 536
fermeture, 552
fiber, voir fibre, 101
Fibonacci, 557
fibre, 101
fichier, 610
fichier de ressources, 16, 34
fichier PE, 17
fichiers cab, 71
fichiers cab, 71
fichiers image, 17
fichiers objets, 17
FieldPtr (table de mtadonnes de type), 20
FIFO, 580
file, voir fichier, 610
File (classe), voir System.IO.File, 610
file dattente, 580
FileDef (table du manifeste), 18
finaliseur, 118, 423
finaliseur critique, 129
finally (mot-cl), 515
firewall, voir pare feu, 805
fixed, 505
fixed (mot-cl), 505
flag, voir drapeau, 369
float (mot-cl), 355
focus, 675
foncteur, 536
foncteur, 592
foncteurs (notion C++), 433
fonction-objet, voir foncteur, 536
fonction-objet, voir foncteur, 592
fonctor, voir foncteur, 592
for (mot-cl), 332
Index
foreach (mot-cl), 332, 563
foreign key, voir cl trangre, 706
FormsIdentity, voir System.Web.Security.
FormsIdentity, 208
formulaire, 667
FormView, 944
forward-only (flot de donnes), 633
fournisseur dauthentification, 960
fournisseur dintercepteurs de messages, 833
fournisseur dorganisation de site, 956
fournisseur dutilisateurs, 962
fournisseur de donnes, 708
fournisseur de rles, 964
fournisseur de traitement dvnements, 906
fragmentation du tas, 117
Framework Configuration, 108
FreeBSD Unix, 10
FTP, 652
fuite de mmoire, 339
fuite de mmoire, 502
full runtime compilation, 871
Full trusted assemblies, 198
fully trusted assemblies, 194
fusion, 103
fuslogvw.exe, 106
GAC, 64
Gac (classe), voir System.Security.Policy.Gac,
188
GACUtil.exe, 66
GDI+, 695
gnration (ramasse-miettes), 117
Generic.Dictionary<K,V>, voir System.
Collections.Generic.Dictionary<K,V>,
583
gnricit, 467
GenericWebPart, 977
gestionnaire des tches, 134
global (mot-cl), 322
Global Assembly Cache, voir GAC, 64
Global.asax, 892
globalisation (culture), 34
goto (mot-cl), 334
Graphical Device Interface, voir GDI+, 695
groupe dutilisateurs, 207
groupe de validation, 910
groupes de code, 194
grow factor, voir facteur dexpension, 579
GUID, 821
Index
guidgen.exe, 20
GZipStream, 659
hte dexcution, 94
hte dexcution, 125
hte dun domaine dapplication, 191
hachage, 223
handle (C++/CLI), 275
handle (win32), 277
handle-recycling attack, 278
handler HTTP, 891
Hash, voir System.Security.Policy.Hash, 190
heap, voir tas, 338
hritage dimplmentation multiple, 445
hritage dimplmentation simple, 445
hritage de classe, 444
HRESULT, 285
HTTP.SYS, 654
IBindingList, 693
ICollection<T>, voir System.Collections.
Generic.ICollection<T>, 575
IComparable<T>, voir System.Collections.
Generic.IComparable<T>, 588
IComparer<T>, voir System.Collections.
Generic.IComparer<T>, 588
identificateurs de scurit, 209
identity permission, voir permission
didentit, 192
IDictionary<K,V>, voir System.Collections.
Generic.IDictionary<K,T>, 582
IDispatch, 109, 286
IDL, 279
idle, voir processus dinactivit, 139
IIS, 815
IJW, 272
IL, 41
ilasm.exe, 41
ildasm.exe, 21, 110
IList<T>, voir System.Collections.Generic.
IList<T>, 576
images natives, 113
immuable (objet), 370
immutable, voir immuable, 370
impersonation, voir emprunt didentit, 210
implicit (mot-cl), 436
ImportCatalogPart, 981
IMS, 297
in (mot-cl), 563
1041
in-place pre-compilation, 871
indexeur, 409
indicateur binaire, 143, 369
Indigo, voir Windows Communication
Framework, 1013
INETINFO.EXE, 861
InetInfo.exe, 861
Initialization Vector, voir vecteur
dinitialisation, 220
inlining, 112, 333
instance dune classe, 395
InsucientMemoryException, 128
int (mot-cl), 354
interblocage, voir deadlocks, 144
intercepteur de messages, 822
interface (mot-cl), 456
interface de classe, 291
Intermediate Language, voir IL, 41
internal, 418
internal call, 265
internal protected, 418, 445
InternalsVisibleToAttribute, voir System.
Runtime.CompilerServices.
InternalsVisibleToAttribute, 27
internationalisation, 34
interop assembly, voir assemblage
interoprable, 279
interruption software, 138
IP, 641
IPC, voir Inter Processus Communication,
830
IPermission, voir System.Security.
IPermission, 202
is (mot-cl), 464
isolated storage, voir stockage isol, 205
ISynchronizeInvoke, 180
It Just Works, voir IJW, 272
itrateur (design pattern), 539
IXPathNavigable, 772
jagged array, voir tableau irrgulier, 567
Jet, 740
jeton de cl publique, 30
jeton de mtadonnes, 46
jeton de scurit, 207
JIT, 112
Just In Time, voir JIT, 112
Juste temps, voir JIT, 112
1042
Kerberos, 660
keycontainer (option csc.exe), 30
keyfile (option csc.exe), 30
KeyValuePair<K,V>, voir System.Collections.
Generic.KeyValuePair<K,T>, 582
langage fonctionnel, 536
langage impratif, 536
late binding, voir lien tardif, 238
LayoutEditorPart, 982
lease, voir bail, 805
lease manager, voir administrateur de baux,
806
lien dynamique, 238
lien dynamique, 238
lien prcoce, 238
lien tardif, 233, 238, 239
lien tardif explicite, voir lien tardif, 239
lien tardif implicite, voir lien dynamique, 238
LIFO, 581
Lightweight Transaction Manager, 746
LinkedList<T>, voir System.Collections.
Generic.LinkedList<T>, 579
LinkedListNode<T>, voir System.Collections.
Generic.LinkedListNode<T>, 579
List<T>, voir System.Collections.Generic.
List<T>, 576
liste, 577
liste doublement lie, 579
little-endian, 635
load balancing, voir rpartition de charge, 824
load factor, voir facteur de chargement, 585
LocalDataStoreSlot, 177
localisation (culture), 34
log, 620
Login, 966
LoginName, 966
LoginStatus, 966
LoginView, 966
long (mot-cl), 354
loop, voir boucles, 331
LTM, voir Lightweight Transaction Manager,
746
machine tat, 334
machine virtuelle, 87
machine.config, 715
machine.config, 888
mage.exe, 77
Index
mageui.exe, 77
Main, 334
main thread, voir thread principal, 133
manifeste, 17
ManifestResourceDef (table du manifeste), 18
ManualResetEvent, 153
MarshalAs, voir System.Runtime.
InteropServices.MarshalAs, 271
Marshalling By Reference, voir MBR, 787
Marshalling By Value, voir MBV, 790
master page, 947
mathmatique (fonctions), 597
MBR, 787
MBV, 790
MD5 (algorithme de hachage), 30
MemberRef (table de mtadonnes de type),
19
MembershipUser, 964, 965
MemberwiseClone(), 348
membres, 396
membres dinstances, 430
membres partags, 430
mmoire tampon, 637
mmoire virtuelle, 134
memory gates, voir portail de mmoire, 128
memory leak, voir fuite de mmoire, 339
MEP, 990
Message Exchange Pattern, 990
message sink, voir intercepteur de messages,
822
Message Transmission Optimization
Mechanism, 1009
messages Windows, 666
metadata, voir mtadonnes, 17
metadata token, voir jeton de mtadonnes,
46
mtadonnes, 17
mtadonnes de lassemblage, 17
MethodDef (table de mtadonnes de type),
19
mthode abstraite, 453
mthode anonyme, 524
mthode finalise, 449
mthode gnrique, 487
mthode virtuelle, 449
mthode virtuelle pure, voir mthode
abstraite, 453
MethodInfo, voir System.Reflection.
MethodInfo, 245
Index
MethodPtr (table de mtadonnes de type),
20
MFC, 666
Microsoft SQL Server Desktop Engine, 740
Microsoft Transaction Server, voir MTS, 295
Microsoft.Win32.RegistryKey, 616
MIME, voir Multipurpose Internet Mail
Exchange, 657
modale (fentre), 674
mode connect, 707, 708
mode dactivation (COM+), 302
mode dappel (WKO), 798
mode dconnect, 707
mode non protg, voir mode non vrifiable,
502
mode non vrifiable, 502
mode noyau, 137
mode utilisateur, 137
modle relationnel, 705
modeless, voir modale, 674
module, 15
module de dploiement, 69
module HTTP, 891
module principal, 15
ModuleDef (table de mtadonnes de type),
19
ModuleRef (table de mtadonnes de type),
19
Mono, 9
monte en charge, 708
Moore (loi de), 137
moteur dexcution (CLR), 87
moteur transactionnel, 742
msbuild.exe, 49
mscorcfg.msc, 196
mscoree.dll, 96
mscorlib, 23
mscorlib.dll, 94
mscorsvr.dll, 94
mscorwks.dll, 94
MSDE, voir Microsoft SQL Server Desktop
Engine, 740
MSIL, voir IL, 41
MSMQ, 298
MTA, 285
MTOM, voir Message Transmission
Optimization Mechanism, 1009
MTS, 295
Multipurpose Internet Mail Exchange, 657
1043
multitche coopratif, 101, 138
multitche premptif, 101, 138
mutant, voir Mutex, 153
Mutex, 153
mutex nomm, 154
Nant, 49
Native Image Generator, voir ngen.exe, 113
navigateur, 859
NDepend, 1034
nested type, voir type encapsul, 416
.NET Framework Configuration, 196
.NET Framework Configuration, 108
new (mot-cl), 423, 451
ngen.exe, 113
NHibernate, 738
niveau de stratgie de scurit, 194
niveau de visibilit, 418
No Touch Deployment, 84
nud, 579
nud SOAP, 1001
nom fort (assemblages nom fort), 28
NT Lan Manager, 660
NTD, voir No Touch Deployment, 84
NTLM, voir NT Lan Manager, 660
null (mot-cl), 342
Nullable<T>, 386
Object Space, 738
ObjectDataSource, 932
ObjectHandle, 242
objet, 395
objet actif (ramasse-miettes), 117
objet crateur, 162
OCX, voir ActiveX, 283
ODBC, 708
OEM, 635
OLE Transaction, 297
OleDB, 708
oleview.exe, 289
one to many (relation), 724
oprateur binaire, 434
oprateur dindirection, 504
oprateur de comparaison, 438
oprateur de drfrencement, 504
oprateur de rsolution de porte, 396, 430
oprateur de transtypage explicite, 436
oprateur de transtypage implicite, 436
oprateur unaire, 433
1044
operator (mot-cl), 433
optimiste (gestion des transactions), 728
overloading, voir surcharge, 406
override (mot-cl), 449
P/Invoke, 265
P/Invoke Marshalling, 267
PAG, voir Pattern & Practices, 1031
page de code, 635
page de contenu, 948
PageCatalogPart, 980
Paint (vnement), 676
PAL, 10
palette, 698
paramtre nomm, 254
parametric polymorphism, voir
polymorphisme paramtr, 470
ParamPtr (table de mtadonnes de type), 20
parcours de la pile dappels, 187
pare feu, 805
partial (mot cl), 392
partial type, voir type dfini sur plusieurs
fichiers sources, 392
Partially trusted assemblies, 198
PascalCase, 326
passage dargument par rfrence, 400
passage dargument par valeur, 401
Passport, 208, 960
PassportIdentity, voir System.Web.Security.
PassportIdentity, 208
PasswordRecovery, 966
Path (classe), voir System.IO.Path, 611
Pattern & Practices, 1031
PE, voir fichier PE, 17
PE/COFF, 17
perfmon.exe, 116, 906
permcalc.exe, 82
permission, 191
permission de scurit, 193
permission elevation, 79
permission sets, voir ensembles de
permissions, 194
permissions didentit, 192
PermissionSet, 199
permview.exe, 204
pessimiste (gestion des transactions), 728
pile, 337
pile, 42
pile, 581
Index
ping, 808
pinvokeimpl, 273
pipe nomm, 830
pipeline (pattern), 554
pipeline HTTP, 891
pitching, 113
Platform Abstraction Layer, voir PAL, 10
Platform Invoke, voir P/Invoke, 265
plugin, 247
point dentre du programme, 335
point protg, 120
point protg, 141
point terminal, 796
pointeur, 344
pointeur gr, 502
pointeur non gr, 502
pointeur non gr de fonction, 272, 502
Policy, voir System.Security.Policy, 188
policy, voir stratgie, 988
policy assemblies, 194
Policy Wizard, 1009
politique de lescalade, 129
politique de principal (scurit), 216
polymorphisme, 238, 449
polymorphisme paramtr, 470
pool de connexions, 718
pool de threads, 167, 639
port (IP), 641
portabilit niveau binaire, 111
portail de mmoire, 128
porte (stockage isol), 205
#pragma managed, 273
#pragma unmanaged, 273
#pragma warning disable, 316
#pragma warning restore, 316
Preprocessing, 313
preuve, 191
primary key, voir cl primaire, 706
primary thread, voir thread principal, 133
principal, 207
PrincipalPermission, voir System.Security.
Permissions.PrincipalPermission, 217
priorit des oprateurs, 360
private, 418
privilge, voir permission, 191
probing, 105
procdure de finalisation, 173, 639, 646
procdure de rappel, 666
procdure stocke, 722
Index
process, voir processus, 133
processus, 94, 133
processus dinactivit, 139
processus fils, 134
processus parent, 134
processus lger, 87
programmation objet (POO), 395
programmation oriente aspect, 466
programmation procdurale, 395
promotable enlistment, 747
Promotable Single Phase Enlistment, 746
PropertyDef, 19
protected, 418, 445
prototype (design pattern), 350
provider, voir fournisseur de donnes, 708
proxy, 280, 300
proxy de dcouverte, 1011
proxy (web service), 994
proxy rel, 819
proxy transparent, 787
proxy transparent, 818
pseudo-attributs, 249
PSPE, voir Promotable Single Phase
Enlistment, 746
public, 418
public key token, voir jeton de cl publique,
30
publication dun objet, 820
Publisher, voir System.Security.Policy.
Publisher, 189
publisher policy, voir assemblage de stratgie
dditeur, 67
publisherPolicy, 106
qualificateurs dalias despace de noms, 321
quantum, 138
quantum, 147
Query Notification, 927
Queue<T>, voir System.Collections.Generic.
Queue<T>, 581
rle, 207
rle SOAP, 1002
race conditions, 144
race conditions, 144
RAD, 688
ramasse-miettes, 96, 116, 339
Rapid Application Development, voir RAD,
688
1045
RCW, 280
readonly (mot-cl), 398
real proxy, voir proxy rel, 819
records (DB), 706
recyclage de domaines dapplication, 126
rentrance, 164
REF _Ref110260763 \h Exemple 22, 25
rfrence (dun objet sur un autre), 398
rfrence dapplication Windows, 83
rfrence faible, 120
rfrence faible courte, 122
rfrence faible longue, 122
reference type, voir type rfrence, 339
Reflection Only, 237
Reflector, 24
rflexion (mcanisme de), 233
reg free COM, voir registration free COM, 287
regasm.exe, 292
regedit.exe, 614
regedt32.exe, 614
regex, 625
regexp, 625
rgion critique, 129
rgion dinterception de messages, 848
rgions dexcution contraintes, 126
registration free COM, 287
registre, voir base des registres, 613
registry, voir base des registres, 613
rgle daccs (ASP.NET), 959
regsvcs.exe, 304
regsvr32.exe, 292
regular expression, 625
relation (DB), 705
relation dquivalence, 346
Remote Procedure Call, 990
remove (accesseur dvnement), 412
rpartiteur, 138
rpartition de charge, 824
Repeater, 940
rpertoire, 608
rpertoire virtuel (IIS), 816
RequestCacheLevel, voir System.Net.Cache.
RequestCacheLevel, 653
resgen.exe, 35
Resource Manager, 742
resxgen.exe, 35
return (mot-cl), 404
reverse engineering, 24
RM, voir Resource Manager, 742
1046
rollback, 741
Rotor, voir Shared Source CLI, 10
row(DB), 705
RPC, 990
RSA, 223
RSA, 223
ruche, 614
Runtime Callable Wrapper, voir RCW, 280
runtime host, voir hte dexcution, 94
S/MIME, voir Secure/Multipurpose Internet
Mail Extensions, 231
SACL, voir SACL, 211
safe point, voir point protg, 120
SAFEARRAY, 285
same-box communication, voir Inter
Processus Communication, 830
sandbox, voir bac sable, 205
saut, 327
SAX, 766
sbyte (mot-cl), 354
scalable, 708
scheduler, voir rpartiteur, 138
schma dune relation, 705
scheme (URI), voir Mode daccs, 651
scope, voir porte, 205
SD, voir descripteur de scurit, 211
SDDL, voir Security Descriptor Definition
Language, 209
sealed (mot-cl), 448
section critique, 147
section de configuration, 60
Secure Socket Layer, 660
Secure/Multipurpose Internet Mail
Extensions, 231
SecureString, voir System.Security.
SecureString, 228
Security Descriptor Definition Language, 209
security policy, voir stratgie de scurit, 193
Security Support Provider Interface, 660
security token, voir jeton de scurit, 207
SecurityAction, voir System.Security.
Permissions.SecurityAction, 203
See Remote Procedure Call, 990
See System.ComponentModel.
ISynchronizeInvoke, 180
See System.Console, 629
See System.ConsoleColor, 630
Index
See System.Data.Common.
DbCommandBuilder, 726
See System.Data.Common.
DbConnectionStringBuilder, 716
See System.Data.Common.
DbProviderSupportedClasses, 716
See System.Data.DataView, 729
See System.Data.DataViewManager, 731
See System.Data.SqlClient.SqlBulkCopy, 739
See System.LocalDataStoreSlot, 177
See System.Security.AllowPartiallyTrustedCallersAttribute,
198
See System.Security.PermissionSet, 199
See System.ThreadStaticAttributes, 176
See Transaction Isolation Level, 750
See Types Models, 1011
seek, 633
SEH, 521
smaphore, 157
squence, 575
srialisation, 790
serveur dapplications, 295
serveur dentreprises, 301
service, 987
service dentreprise (COM+), 295
service de recouvrement, 743
Service Oriented Architecture, 987
service Windows, 815
serviced component, voir composant servi,
299
Services de composants (outil), 305
services web, 988
session logon, 207
session scurise, 222
SGBD, 705
sgen.exe, 782
SHA-1 (algorithme de hachage), 30
shadow copy, 108, 816, 891
shallow copy, voir copie superficielle, 348
Shared Source CLI, 10
SharePoint, 974
ShFusion.dll, 65
shim, voir cale, 96
short (mot-cl), 354
SID, voir identificateurs de scurit, 209
side by side, voir cte cte, 65
signature numrique, 29, 223
signature retarde, 33
Index
simple appel (mode dappel WKO), 798
single call, voir simple appel, 798
singleton, 422
singleton (mode dappel WKO), 798
Site, voir System.Security.Policy.Site, 188
SiteMap provider, voir fournisseur
dorganisation de site, 956
Sites de confiance, 189
Sites sensibles, 189
situation de comptition, voir race conditions,
144
sizeof (mot-cl), 504
SkipVerification, 502
slot de donnes, 177
Smalltalk, 420
sn.exe, 29
snap-in, voir composant logiciel enfichable,
305
SOA, voir Service Oriented Architecture, 987
Soapsuds.exe, 804
socket, 641
SortedDictionary<K,V>, voir System.
Collections.Generic.
SortedDictionary<K,V>, 583
souche, 112
source de traage, 622
souris (gestion de la), 675
SQL, 706
SQL Server, 708
SQL Server 2005 Express Edition, 740
SqlBulkCopy, 739
SqlDataSource, 932
SqlProfileProvider, 966
SQLXML, 778
SSCLI, voir Shared Source CLI, 10
SSL, voir Secure Socket Layer, 660
SSPI, voir Security Support Provider
Interface, 660
STA, 285
stack, voir pile, 42
stack builder sink, voir constructeur de pile,
818
stack frames, voir fentre de pile, 43, voir
fentre de pile, 267
stack overflow, 127
stack walk, voir parcours de la pile dappels,
187
Stack<T>, voir System.Collections.Generic.
Stack<T>, 581
1047
stackalloc, 508
stackalloc (mot-cl), 508
standard deviation, voir cart type, 721
StdCall, 266
STL, 592
stockage isol, 205
stratgie, 988
stratgie de scurit, 193
string, 343
string (mot-cl), 370
strong name, voir nom fort, 28
StrongName, voir System.Security.Policy.
StrongName, 189
StrongNamePublicKeyBlob, voir System.
Security.Permissions.
StrongNamePublicKeyBlob, 189
struct (mot-cl), 364
Structured Exception Handling, voir SEH, 521
structures, 343
stub, 112
stub (dune mthode), 239
stylesheet, 972
subrog (processus), 303
subroutine, 552
substitution post-cache, 923
sucre syntaxique, 392
super classe, 444
surcharge (de mthode), 406
surrogate, voir subrog, 303
Synchronization (attribut), voir System.
Runtime.Remoting.Contexts.
Synchronization, 151, voir System.
Runtime.Remoting.Contexts.
Synchronization, 160
System.Runtime.Remoting.Channels.
IChannel, 830
System.Activator, 241, 242
System.AppDomain, 89, 242
System.AppDomainSetup, 90
System.AsyncCallback, 173
System.Attribute, 251
System.Boolean, 357
System.Byte, 354
System.Char, 357
System.CLSCompliantAttribute, 131
System.Collections.ArrayList, 576
System.Collections.BitArray, 573
System.Collections.Generic.Dictionary<K,V>,
583
1048
System.Collections.Generic.ICollection<T>,
575
System.Collections.Generic.IComparable<T>,
588
System.Collections.Generic.IComparer<K>,
583
System.Collections.Generic.IComparer<T>,
588
System.Collections.Generic.
IDictionary<K,V>, 582
System.Collections.Generic.
IEqualityComparer<T>, 586
System.Collections.Generic.IList<T>, 576
System.Collections.Generic.
KeyValuePair<K,V>, 582, 587
System.Collections.Generic.LinkedList<T>,
579
System.Collections.Generic.
LinkedListNode<T>, 579
System.Collections.Generic.List<T>, 576
System.Collections.Generic.
SortedDictionary<K,V>, 583
System.Collections.Generic.Stack<T>, 581
System.Collections.Generics.Queue<T>, 581
System.Collections.IComparable, 588
System.Collections.IComparer, 588
System.Collections.IEnumerable, 539
System.Collections.IEnumerator, 539
System.Collections.Specialized.BitVector32,
574
System.Collections.Specialized.
StringCollection, 580
System.Comparison<T>, 588
System.ComponentModel.
BackgroundWorker, 676
System.ComponentModel.BindingList<T>,
579
System.ComponentModel.Component, 667
System.ComponentModel.IComponent, 678
System.ComponentModel.IContainer, 678
System.ComponentModel.IListSource, 929
System.ComponentModel.
ISynchronizeInvoke, 180
System.Configuration.
ApplicationSettingsBase, 61
System.Configuration.
LocalFileSettingsProvider, 63
System.Configuration.SettingsProvider, 63
System.Console, 629
Index
System.ConsoleColor, 630
System.ContextBoundObject, 843
System.CrossAppDomainDelegate, 91
System.Data.Common, 712
System.Data.Common.DbCommandBuilder,
726
System.Data.Common.
DbConnectionStringBuilder, 716
System.Data.Common.DbProviderFactories,
713
System.Data.Common.
DbProviderSupportedClasses, 716
System.Data.Constraint, 728
System.Data.DataView, 729
System.Data.DataViewManager, 731
System.Data.ForeignKeyConstraint, 728
System.Data.Rule, 729
System.Data.SqlClient.SqlBulkCopy, 739
System.Data.SqlTypes.SqlXml, 779
System.Data.UniqueConstraint, 728
System.DateTime, 601
System.Decimal, 355
System.Deletage, 382
System.Deployment, 80
System.Diagnostics.Debug, 620
System.Diagnostics.Process, 134
System.Diagnostics.ProcessStartInfo, 135
System.Diagnostics.Trace, 620
System.Diagnostics.TraceListener, 621
System.Diagnostics.TraceSource, 622
System.Double, 355
System.Drawing.Bitmap, 698
System.Drawing.Brush, 696
System.Drawing.Color, 696
System.Drawing.Graphics, 695
System.Drawing.Image, 698
System.Drawing.Imaging.ImageFormat, 698
System.Drawing.Imaging.Metafile, 699
System.Drawing.Imaging.PixelFormat, 698
System.Drawing.Pen, 696
System.EnterpriseServices.ContextUtil, 300
System.EnterpriseServices.
JustInTimeActivation, 296
System.EnterpriseServices.
RegistrationHelper, 304
System.EnterpriseServices.
ServicedComponent, 299
System.Enum, 368
System.Environment, 136, 336
Index
System.EventArgs, 411
System.EventHandler, 670
System.Exception, 512
System.GC, 122
System.Globalization, 601
System.IAsyncResult, 173
System.IDisposable, 425
System.Int16, 354
System.Int32, 354
System.Int64, 354
System.InteropServices.CriticalHandle, 278
System.InteropServices.SafeHandle, 278
System.IO, 607, 633
System.IO.BinaryReader, 634
System.IO.BinaryWriter, 634
System.IO.BueredStream, 658
System.IO.Compression, 659
System.IO.Directory, 608
System.IO.DirectoryInfo, 608
System.IO.DriveInfo, 607
System.IO.File, 610
System.IO.FileInfo, 610
System.IO.FileStream, 637
System.IO.FileSystemInfo, 608
System.IO.FileSystemWatcher, 612
System.IO.IsolatedStorage.
IsolatedStorageFile, 205
System.IO.MemoryStream, 659
System.IO.NotifyFilters, 612
System.IO.Path, 611
System.IO.Ports.SerialPort, 660
System.IO.Stream, 633
System.IO.StreamReader, 635, 637
System.IO.StreamWriter, 635, 637
System.IO.StringReader, 635
System.IO.StringWriter, 635
System.IO.TextReader, 635
System.IO.TextWriter, 635
System.IO.UnmanagedMemoryStream, 659
System.LocalDataStoreSlot, 177
System.Marshal.InteropServices.Marshal, 284
System.MarshalByRefObject, 789
System.Math, 597
System.MTAThread, 286
System.MulticastDelegate, 379
System.Net.Cache.RequestCacheLevel, 653
System.Net.Cache.RequestCachePolicy, 653
System.Net.Dns, 645
System.Net.FileWebRequest, 653
1049
System.Net.FileWebResponse, 653
System.Net.FtpWebRequest, 653
System.Net.FtpWebResponse, 653
System.Net.HttpListener, 654
System.Net.HttpWebRequest, 653
System.Net.Mail.Attachment, 656
System.Net.Mail.MailAddress, 656
System.Net.Mail.MailMessage, 656
System.Net.Mail.SmtpClient, 656
System.Net.Mime, 657
System.Net.NetworkInformation.
NetworkChange, 650
System.Net.NetworkInformation.
NetworkInterface, 649
System.Net.NetworkInformation.Ping, 650
System.Net.Security, 660
System.Net.Security.AuthenticatedStream,
660
System.Net.Security.NegociateSteam, 660
System.Net.Security.SslStream, 660
System.Net.Sockets.Socket, 641
System.Net.Sockets.TcpClient, 642
System.Net.Sockets.TcpListener, 642
System.Net.WebClient, 652
System.Nullable<T>, 386
System.Nullable<T>, 386
System.ObjectDisposedException, 426
System.ParamArrayAttribute, 405
System.Random, 600
System.Reflection, 234
System.Reflection.ConstructorInfo, 243
System.Reflection.Emit, 256
System.Reflection.MethodInfo, 245
System.Resources.ResourceManager, 38
System.Runtime.Remoting.Messaging.
OneWay, 175
System.Runtime.CompilerServices.
InternalsVisibleToAttribute, 27
System.Runtime.CompilerServices.
RuntimeHelpers, 126, 127, 432
System.Runtime.ConstrainedExecution.
CriticalFinalizer, 278
System.Runtime.ConstrainedExecution.
CriticalFinalizerObject, 129
System.Runtime.ConstrainedExecution.
CriticalFinalizerObject, 129
System.Runtime.InteropServices., 265
System.Runtime.InteropServices.
CallingConvention, 266
1050
System.Runtime.InteropServices.
ClassInterface, 291
System.RunTime.InteropServices.
COMVisible, 292
System.Runtime.InteropServices.DllImport,
265
System.Runtime.InteropServices.FieldOset,
270
System.Runtime.InteropServices.GCHandle,
275
System.RunTime.InteropServices.Guid, 291
System.Runtime.InteropServices.
HandleCollector, 277
System.Runtime.InteropServices.In, 271
System.Runtime.InteropServices.MarshalAs,
281
System.Runtime.InteropServices.MarshalAs,
271
System.Runtime.InteropServices.Out, 271
System.RunTime.InteropServices.ProgId, 293
System.Runtime.InteropServices.
StructLayout, 270
System.Runtime.MemoryFailPoint, 127
System.Runtime.Remoting.Channels.http.
HttpChannel, 830
System.Runtime.Remoting.Channels.
IChannelReceiver, 830
System.Runtime.Remoting.Channels.
IChannelSender, 830
System.Runtime.Remoting.Channels.
IClientChannelSink, 833
System.Runtime.Remoting.Channels.
IClientChannelSinkProvider, 834
System.Runtime.Remoting.Channels.
IClientFormatterSink, 833
System.Runtime.Remoting.Channels.Ipc.
IpcChannel, 830
System.Runtime.Remoting.Channels.
IServerChannelSink, 833
System.Runtime.Remoting.Channels.
IServerChannelSinkProvider, 834
System.Runtime.Remoting.Channels.
IServeurFormatterSink, 833
System.Runtime.Remoting.Channels.Tcp.
TcpChannel, 830
System.Runtime.Remoting.Contexts.
IContextProperty, 846
System.Runtime.Remoting.Contexts.Context,
844
Index
System.Runtime.Remoting.Contexts.
IContextAttribute, 845
System.Runtime.Remoting.Contexts.
Synchronization, 151, 160
System.Runtime.Remoting.
IRemotingTypeInfo, 824
System.Runtime.Remoting.Lifetime.
LifetimeServices, 807
System.Runtime.Remoting.Lifetime.ILease,
806
System.Runtime.Remoting.Lifetime.
ISponsor, 806
System.Runtime.Remoting.Messaging.
AsyncResult, 173
System.Runtime.Remoting.Messaging.
ILogicalThreadAnative, 856
System.Runtime.Remoting.Messaging.
IMessageSink, 822
System.Runtime.Remoting.Messaging.
MethodCallMessageWrapper, 829
System.Runtime.Remoting.ObjectHandle,
792
System.Runtime.Remoting.ObjRef, 820
System.Runtime.Remoting.Proxies.
ProxyAttribute, 827
System.Runtime.Remoting.Proxies.
RealProxy, 819
System.Runtime.Remoting.
RemotingException, 802
System.Runtime.Remoting.Services.
RemotingClientProxy, 805
System.Runtime.Serialization.ISerializable,
790
System.Runtime.Serialization.
OptionalFieldAttribute, 791
System.SByte, 354
System.Security.AccessControl, 154, 212
System.Security.AllowPartiallyTrustedCallersAttribute,
198
System.Security.CodeAccessPermission, 191
System.Security.Cryptography.CryptoStream,
664
System.Security.Cryptography.ProtectedData,
226
System.Security.Cryptography.
ProtectedMemory, 227
System.Security.IPermission, 202
Index
System.Security.Permissions.
PrincipalPermission, 217
System.Security.Permissions.
StrongNamePublicKeyBlob, 189
System.Security.Permissions.SecurityAction,
203
System.Security.PermissionSet, 199
System.Security.Policy, 188
System.Security.Policy.ApplicationDirectory,
188
System.Security.Policy.Evidence, 190
System.Security.Policy.Gac, 188
System.Security.Policy.Hash, 190
System.Security.Policy.Publisher, 189
System.Security.Policy.Site, 188
System.Security.Policy.StrongName, 189
System.Security.Policy.Url, 188
System.Security.Policy.Zone, 189
System.Security.Principal.WindowsIdentity,
208
System.Security.SecureString, 228
System.Security.SecurityException, 205
System.Security.SecurityZone, 189
System.Security.
SuppressUnmanagedCodeSecurity,
267
System.Security.SuppressUnmanagedCodeSecurityAttribute,
201
System.Serializable, 790
System.ServiceProcess, 815
System.Single, 355
System.STAThread, 286
System.StringComparer, 585
System.Text.RegularExpressions.RegEx, 627
System.Text.StringBuilder, 376
System.Threading.ApartmentState, 285
System.Threading.AutoResetEvent, 155
System.Threading.EventResetMode, 155
System.Threading.EventWaitHandle, 155
System.Threading.Interlocked, 146
System.Threading.Interlocked, 146
System.Threading.ManualResetEvent, 155
System.Threading.Monitor, 147
System.Threading.ParametrizedThreadStart,
140
System.Threading.ReaderWriterLock, 158
System.Threading.Semaphore, 157
System.Threading.Thread, 140
1051
System.Threading.ThreadPool, 167
System.Threading.ThreadStart, 140
System.Threading.ThreadState, 143
System.Threading.Timer, 171
System.Threading.WaitHandle, 153
System.ThreadStaticAttributes, 176
System.Timers.Timer, 169
System.Transaction.
IPromotableSinglePhaseNotification,
758
System.Transactions.
DistributedTransactionPermission,
754
System.Transactions.IEnlistmentNotification.
IEnlistmentNotification, 755
System.Transactions.
ISinglePhaseNotification, 755
System.Transactions.TransactionScope, 748
System.Type, 241, 243
System.UInt16, 354
System.UInt32, 354
System.UInt64, 354
System.Uri, 652
System.WeakReference, 121
System.Web.Caching.Cache, 925
System.Web.Caching.CacheDependency, 925
System.Web.Hosting.SimpleWorkerProcess,
864
System.Web.HttpApplication, 892
System.Web.HttpApplicationState, 898
System.Web.HttpCachePolicy, 918
System.Web.HttpContext, 893
System.Web.HttpListenerContext, 655
System.Web.IHttpHandler, 895
System.Web.IHttpModule, 893
System.Web.Management, 906
System.Web.Profile.ProfileProvider, 966
System.Web.Security.
ActiveDirectoryMembershipProvider,
963
System.Web.Security.FormsIdentity, 208
System.Web.Security.Membership, 963
System.Web.Security.MembershipProvider,
963
System.Web.Security.PassportIdentity, 208
System.Web.Security.RoleProvider, 965
System.Web.Security.Roles, 965
System.Web.Security.
SqlMembershipProvider, 963
1052
System.Web.Services.WebService, 992
System.Web.Services.SoapExtension, 1009
System.Web.Services.WebMethod, 992
System.Web.SiteMapProvider, 956
System.Web.TraceContext, 906
System.Web.UI System.Web.UI.
WebControls.WebParts.
PersonalizationProvider, 977
System.Web.UI.Control, 873
System.Web.UI.Controls.Style, 971
System.Web.UI.HtmlControls.HtmlControl,
883
System.Web.UI.HtmlControls.HtmlForm,
878
System.Web.UI.MasterPage, 949
System.Web.UI.Page, 861
System.Web.UI.WebControls.WebControl,
883
System.Web.UI.WebControls.WebParts.
WebPart, 977
System.Web.XmlSiteMapProvider.
XmlSiteMapProvider, 956
System.Windows.Forms.ClipBoard, 679
System.Windows.Forms.Control, 667
System.Windows.Forms.Control.DragDrop,
678
System.Windows.Forms.Help, 679
System.Windows.Forms.Menu, 679
System.Windows.Forms.MessageBox, 678
System.Windows.Forms.NotifyIcon, 679
System.Windows.Forms.Timer, 171, 679
System.Windows.Forms.ToolTips, 679
System.Xml.XmlDataDocument, 777
System.Xml.XmlDocument, 769
System.Xml.XmlNode, 769
System.Xml.XmlNodeList, 769
System.Xml.XmlNodeReader, 766
System.Xml.XmlReader, 766
System.Xml.XmlReaderSettings, 767
System.Xml.XmlSerializer, 779
System.Xml.XmlTextReader, 766
System.Xml.XmlTextWriter, 766, 768
System.Xml.XmlWriter, 766
System.Xml.XPath.XPathDocument, 772
System.Xml.XPath.XPathNavigator, 772, 773
System.Xml.Xsl.XslCompiledTransform, 774
systme de persistance, 705
T-SQL, 706
Index
table de hachage, 583
TableAdapter, 733
tableau, 565
tableau dchiquet, voir tableau irrgulier,
567
tableau irrgulier, 567
tas, 338
tas gr, 116
task manager, voir gestionnaire des tches, 134
TCP/IP, 642
template, 939
template (C++/CLI), 275
templates (C++), 471
thme, 971
this (mot-cl), 420
thread, 133
thread
Abort(), 142
CurrentThread, 140
Interrupt(), 142
Join(), 141
Multiple Apartement Thread, 167
Name, 140
Resume(), 141
Single Apartment Thread, 167
Sleep(), 141
Suspend(), 141
thread background, 142
thread foreground, 142
thread gr, 137
thread I/O, 167
thread lger, 101
thread local storage, 177
thread logique, 101
thread ouvrier, 167
thread principal, 133
thread-safe, 150, 160
ThreadStaticAttributes, 176
ThreadStore, 137
throw (mot-cl), 513
tiers de confiance, 231
TIL, 750
time slices, voir quantum, 138
timeout, 145
Timer, 702
timer, 679
TIP, 296
tlbexp.exe, 289
tlbimp.exe, 279
Index
TLS, voir thread local storage, 177, voir
Transport Layer Security, 660
tModel, 1011
trace.axd, 905
TraceWebEventProvider, 907
transaction, 741
transaction dpendante, 752
transaction distribue, 741
Transaction Isolation Level, 750
transaction manager, voir moteur
transactionnel, 742
transparent proxy, voir proxy transparent, 787
Transport Layer Security, 660
transtypage explicite, 505
transtypage implicite, 505
tray icon, 679
try (mot-cl), 511
tuple (DB), 705
Type, voir System.Type, 243
type blittable, 268
type construit, voir type gnrique, 471
type construit ferm, voir type gnrique
ferm, 471
type construit ouvert, voir type gnrique
ouvert, 471
type dfini sur plusieurs fichiers sources, 392
type lmentaire, 343
type encapsul, 416
type gnrique, 469, 471
type gnrique ferm, 471
type gnrique ouvert, 471
type library, voir bibliothque de types, 279
type nullable, 386
type paramtre, 470
type partiel, voir type dfini sur plusieurs
fichiers sources, 392
type rfrence, 339
type valeur, 339
TypeDef (table de mtadonnes de type), 19
typeof (mot-cl), 241
TypeRef (table de mtadonnes de type), 19
Types Models, 1011
types primitifs, 343
UDDI, voir Universal Description Discovery
and Integration, 1011
UDP/IP, 642
UDT, voir User Defined Type, 737
uint (mot-cl), 354
1053
ulong (mot-cl), 354
UnBoxing, 352
UNICODE, 635
union, 270
unit dexcution, voir thread, 133
unit fonctionnelle, voir processus, 133
Universal Description Discovery and
Integration, 1011
unsafe (mot-cl), 502
URI, 651
URI relatif, 652
URL, 104
Url, voir System.Security.Policy.Url, 188
User Defined Type, 737
UserScopedSettingAttribute, 61
ushort (mot-cl), 354
using (mot-cl), 320, 426
UTF, 635
UTF-8, 635, 761
valeur de hachage, 18, 189
value (mot-cl), 408
value type, voir type valeur, 339
variable, 396
variance, 721
VARIANT, 285
vecteur dinitialisation, 220
verbatim, 611
verbatim (string), 372
verbe (WebParts), 975
virtual (mot-cl), 449
Visual Studio .NET, 25
void (mot-cl), 404
voir expression rgulire, 625
voir Message Exchange Pattern, 990
voir type primitif, 343
volatile (mot-cl), 145
volatile (RM), 746
volume, 607
VSHost, 83
W3C, 9, 769
w3wp.exe, 861
WCF, 1013
weak reference, voir Rfrence faible, 120
web form, 861
web garden, 890
web mthode, 991
Web Service Enhancement, 1008
1054
Index
WS-MetadataExchange, 1011
WS-Policy, 1009
WS-PolicyAttachment, 1011
WS-ReliableMessage, 1011
WS-SecureConversation, 1009
WS-Security, 1009
WS-SecurityPolicy, 1009
WS-Trust, 1009
WSDL, 1003
wsdl.exe, 994
WSE, voir Web Service Enhancement, 1008
WYSIWYG, 667
X.509, 231
X/Open, 297
X509 Certificate Tool, 1009
XA, 297
XA (transaction), 743
XCopy (dploiement), 63
XML, 759
XP, voir eXtreme Programming, 7
XPath, 763
XPathNodeIterator, 773
XPathNodeIterator, 773
XQuery, 764
XSD, 762
xsd.exe, 731, 782
XSLT, 763
yield break (mot-cl), 546
yield return (mot-cl), 542
zone, 188