You are on page 1of 473

Sauf

mention contraire, le contenu de cet ouvrage est publi sous la licence :


Creative Commons BY-NC-SA 2.0
La copie de cet ouvrage est autorise sous rserve du respect des conditions de la licence
Texte complet de la licence disponible sur : http://creativecommons.org/licenses/by-ncsa/2.0/fr/
Conception couverture : Sophie Bai
Illustrations chapitres : Fan Jiyong
OpenClassrooms 2015 - ISBN : 979-10-90085-84-8

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

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

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

Quallez-vous apprendre en lisant ce livre ?


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

Comment lire ce livre ?


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

Suivez lordre des chapitres


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

Pratiquez en mme temps


Pratiquez rgulirement. Nattendez pas davoir fini de lire ce livre pour allumer votre
ordinateur et faire vos propres essais.

Ce livre est issu dOpenClassrooms


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

Remerciements
Je souhaite remercier un certain nombre de personnes qui, de prs ou de loin, ont
contribu la naissance de cet ouvrage :
ma femme Delphine qui me soutient au quotidien et moffre chaque jour une raison
davancer dans la vie ses cts ;
Jrmie, mon ami-tmoin-compagnon-de-dev , qui a bien voulu relire mes
premiers essais et qui a toujours une nouvelle ide dvelopper ;
Anna, Jonathan, Mathieu, Pierre et toute lquipe de Simple IT ;
tous les relecteurs et particulirement Julien Patte (alias Orwell), qui ma donn
dexcellents conseils ;
tous les lecteurs qui ont contribu son amlioration grce leurs commentaires
prcieux et leur envie de voir le livre termin.
Bonne lecture !

Premire partie

Les rudiments du langage C#

Chapitre 1

Introduction au C#

Dans ce tout premier chapitre, nous allons dcouvrir ce quest le C#, son histoire et son
rapport avec le framework .NET. Dailleurs, vous ne savez pas ce quest un framework ?
Ce nest pas grave, tout ceci sera expliqu !
Nous verrons dans ce chapitre ce que sont les applications informatiques et comment des
langages de programmation volus comme le C# nous permettent de raliser de telles
applications.
Et ce nest que le dbut alors ouvrez grands vos yeux, chaussez vos lunettes et
explorons ce monde merveilleux !

Avant propos
A qui sadresse ce tutoriel ?
Aux dbutants ! Pas besoin davoir fait du dveloppement avant pour suivre ce tutoriel !
Je vais donc faire de mon mieux pour dtailler au maximum mes explications, cest
promis.
Mon but est rellement de rendre ce tutoriel accessible pour les dbutants.
Bien sr, il y en a peut-tre parmi vous qui ont dj fait du C, du C++, du Java
Evidemment, si vous avez dj fait du dveloppement informatique, ce sera plus facile
pour vous (surtout pour la premire partie qui prsente les bases du langage). Attention
nanmoins de ne pas vouloir aller trop vite : le C# ressemble dautres langages mais il a
quand mme ses spcificits !

Esprit du tutoriel
Nous allons dcouvrir ensemble de nombreuses choses en apprenant dvelopper en C#.
Il y aura bien entendu des TP pour vous faire pratiquer, afin que vous puissiez vous rendre
compte de ce que vous tes capables de faire aprs avoir lu plusieurs chapitres plus
thoriques.

Nanmoins, je veux que vous soyez actifs ! Ne vous contentez pas de lire passivement
mes explications, mme lorsque les chapitres sont plutt thoriques ! Testez les codes et
les manipulations au fur et mesure. Essayez les petites ides que vous avez pour
amliorer ou adapter lgrement le code. Sortez un peu des sentiers battus du tutoriel :
cela vous fera pratiquer et vous permettra de dcouvrir rapidement si vous avez compris
ou non le chapitre.
Pas dinquitude, si jamais vous bloquez sur quoi que ce soit qui nest pas expliqu dans
ce cours, la communaut qui sillonne les forums saura vous apporter son aide prcieuse.

Dure dapprentissage
Il faut compter plusieurs semaines pour lire, comprendre et assimiler ce tutoriel. Une fois
assimiles toutes les notions prsentes, il vous faudra plusieurs mois pour atteindre un
niveau solide en C#. Aprs tout, cest en forgeant quon devient forgeron.
Dailleurs merci de minformer du temps que vous a pris la lecture de ce cours pour
que je puisse indiquer aux autres lecteurs une dure moyenne de lecture.

Cest tout ?
Non rassurez-vous, le tutoriel est loin dtre fini, vous ne voyez donc pas toutes les
parties. Vous dcouvrirez dans ce tutoriel le dbut des rudiments du dveloppement en C#.
Petit petit je complterai le tutoriel pour ajouter la suite des rudiments du langage.
Ensuite, je prsenterai la programmation oriente objet et comment en faire avec le C#.
Enfin, pour aller plus loin, nous tudierons laccs aux donnes et dautres surprises
encore.
Le dbut de ce cours sera plutt thorique. Pour savoir coder, il faut commencer par
apprendre les bases du langage, cest un passage oblig.
Petit petit jintroduirai la pratique pour illustrer certains points importants ; cela vous
permettra de mieux comprendre des fonctionnements et surtout de bien mmoriser le
cours.

Allez plus loin !


Nhsitez pas regarder dautres tutoriels portant sur le sujet. Il est toujours bon de
diversifier ses sources pour avoir diffrentes approches du sujet.
De manire gnrale, je vous recommande de ne pas hsiter tester les codes que je
prsenterai au fur et mesure. Surtout, si vous avez des ides pour les amliorer un peu,
faites-le ! Ca ne marchera pas tous les coups, mais cela vous fera beaucoup plus
progresser que vous ne le pensez ! Ne comptez donc pas uniquement sur les TP pour
pratiquer, ce serait dommage.

Sachez aussi que je suis ouvert toutes remarques, critiques, questions, portant sur ce
tutoriel. Nhsitez donc pas poster des commentaires, surtout si votre message peut tre
utile pour dautres personnes. Par contre, veuillez ne pas menvoyer de MP, sauf en cas de
force majeure, parce que je naurai pas le temps de vous rpondre individuellement, et que
sil sagit dune demande daide, les forums sont l pour a et on vous y rpondra plus
rapidement que moi.

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

Comment sont cres les applications informatiques ?


Une application informatique : quest-ce que cest ?
Comme vous le savez, votre ordinateur excute des applications informatiques pour
effectuer des tches. Ce sont des logiciels comme :

Un traitement de texte
Un navigateur internet
Un jeu vido

Votre ordinateur ne peut excuter ces applications informatiques que si elles sont crites
dans le seul langage quil comprend, le binaire.
Techniquement, le binaire est reprsent par une suite de 0 et de 1.

Il nest bien sr pas raisonnablement possible de raliser une grosse application en binaire,
cest pour a quil existe des langages de programmation qui permettent de simplifier
lcriture dune application informatique.

Comment crer des programmes simplement ?


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

Avec des langages traditionnels comme le C et le C++, on crit des instructions


simplifies, lisibles par un humain comme :
printf("Bonjour");

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

Cette mthode est efficace et a fait ses preuves. De nombreuses personnes dveloppent
toujours en C et C++ aujourdhui. Nanmoins, ces langages ont aussi un certain nombre
de dfauts dus leur anciennet. Par exemple, un programme compil (binaire) ne
fonctionne que sur la plateforme pour laquelle il a t compil. Cela veut dire que si vous
compilez sous Windows, vous obtenez un programme qui fonctionne sous Windows
uniquement (et sur un type de processeur particulier). Impossible de le faire tourner sous
Mac OS X ou Linux simplement, moins de le recompiler sous ces systmes
dexploitation (et deffectuer au passage quelques modifications).

Les programmes binaires ont ce dfaut : ils ne fonctionnent que pour un type de machine.
Pour les dveloppeurs qui crivent le code, cest assez fastidieux grer.
Langages rcents : le code manag

Les langages rcents, comme le C# et le Java, rsolvent ce problme de compatibilit tout


en ajoutant de nombreuses fonctionnalits apprciables au langage, ce qui permet de

raliser des programmes beaucoup plus efficacement.


La compilation en C# ne donne pas un programme binaire, contrairement au C et au C++.
Le code C# est en fait transform dans un langage intermdiaire (appel CIL ou MSIL)
que lon peut ensuite distribuer tout le monde. Ce code, bien sr, nest pas excutable
lui-mme, car lordinateur ne comprend que le binaire.
Regardez bien ce schma pour comprendre comment cela fonctionne :

Le code en langage intermdiaire (CIL) correspond au programme que vous allez


distribuer. Sous Windows, il prend lapparence dun .exe comme les programmes
habituels, mais il ne contient en revanche pas de binaire.
Lorsquon excute le programme CIL, celui-ci est lu par un autre programme (une
machine analyser les programmes, appele CLR) qui le compile cette fois en vrai
programme binaire. Cette fois, le programme peut sexcuter, ouf !
Ca complique bien les choses quand mme ! Est-ce bien utile ?
Cela offre beaucoup de souplesse au dveloppeur. Le code en langage intermdiaire (CIL)
peut tre distribu tout le monde. Il suffit davoir install la machine CLR sur son
ordinateur, qui peut alors lire les programmes en C# et les compiler la vole en
binaire. Avantage : le programme est toujours adapt lordinateur sur lequel il tourne.
La CLR vrifie aussi la scurit du code ; ainsi en C du code mal pens (par
exemple une mauvaise utilisation des pointeurs) peut entraner des problmes
pour votre PC, ce que vous risquez beaucoup moins avec le C#. De plus, la CLR
dispose du JIT debugger qui permet de lancer Visual Studio si une erreur
survient dans un programme .NET pour voir ce qui a caus cette erreur. On
parle de code manag.
Cette complexit ralentit lgrement la vitesse dexcution des programmes (par rapport
au C ou au C++), mais la diffrence est aujourdhui vraiment ngligeable par rapport aux
gains que cela apporte.
Donc, en thorie, il est possible dutiliser nimporte quelle application compile en
langage intermdiaire partir du moment o il y a une implmentation du CLR
disponible.
En ralit, il ny a que sous Windows quil existe une implmentation complte du CLR.
Il existe cependant une implmentation partielle sous Linux : Mono. Cela veut dire que si
votre programme utilise des fonctionnalits qui ne sont pas couvertes par Mono, il ne

fonctionnera pas.
En conclusion, dans la pratique, le .NET est totalement exploitable sous
Windows, ailleurs non.

Excutables ou assemblages ?
Jai dit juste au dessus que le C# tait compil en langage intermdiaire et quon le
retrouve sous la forme dun .exe comme les programmes habituels.
Cest vrai ! (Je ne mens jamais ).
Par contre, cest un peu incomplet.
Il est possible de crer des programmes (.exe) qui pourront directement tre excut par le
CLR, mais il est galement possible de crer des bibliothques sous la forme dun fichier
possdant lextension .dll .
On appelle ces deux formes de programmes des assemblages, mais on utilise globalement
toujours le mot anglais assembly .
Les fichiers .exe sont des assemblys de processus
Les fichiers .dll sont des assemblys de bibliothques
Concrtement, cela signifie que le fichier .exe servira lancer une application et quune
dll pourra tre partage entre plusieurs applications .exe afin de rutiliser du code dj
crit.
Nous verrons un peu plus loin comment ceci est possible.
Il est noter quun raccourci est souvent fait avec le terme assembly. On a
tendance voir que le mot assembly sert dsigner uniquement les
bibliothques dont lextension est .dll.

Quest-ce que le framework .NET ?


Jai commenc vous parler du C# qui tait une brique du framework .NET. Il est temps
den savoir un peu plus sur ce fameux framework.
Commenons par le commencement : comment cela se prononce ?
Citation : Shakespeare
DOTTE NETTE
Citation : maitre Capello
POINT NETTE
Je vous accorde que le nom est bizarre, point trop net pourrions-nous dire
Surtout que le nom peut tre trompeur. Avec lomniprsence dinternet, son abrviation

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

Le C# est un langage de programmation permettant dutiliser le framework .NET.


Cest le langage phare de Microsoft.
Le framework .NET est une norme bote fonctionnalits permettant la cration

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

Chapitre 2

Crer un projet avec Visual C# 2010 express

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

Que faut-il pour dmarrer ?


Jespre vous avoir donn envie de dmarrer lapprentissage du C#, cependant, il nous
manque encore quelque chose pour pouvoir sereinement attaquer cet apprentissage.
Bien sr, vous allez avoir besoin dun ordinateur, mais a priori, vous lavez dj Sil
nest pas sous Windows, mais sous linux, vous pouvez utiliser Mono qui va permettre
dutiliser le C# sous linux. Cependant, Mono nest pas aussi complet que le C# et le
framework .NET sous Windows, en lutilisant vous risquez de passer ct de certaines
parties du tutoriel.
Pour reprendre la mtaphore du chantier, on peut dire quil va galement nous manquer un
chef de chantier. Il nest pas forcment ncessaire en thorie, mais dans la pratique il se
rvle indispensable pour mener bien son chantier.
Ce chef de chantier cest en fait loutil de dveloppement. Il va nous fournir les outils
pour orchestrer nos dveloppements.
Cest entre autres :
Un puissant diteur
Un compilateur
Un environnement dexcution
Lditeur de texte va nous servir crer des fichiers contenant des instructions en langage
C#.
Le compilateur va servir transformer ces fichiers en une suite dinstructions
comprhensibles par lordinateur, comme nous lavons dj vu.

Le moteur dexcution va permettre de faire les actions informatiques correspondantes


(afficher une phrase, ragir au clic de la souris, etc.), cest le CLR dont on a dj parl.
Enfin, nous aurons besoin dune base de donnes. Nous y reviendrons plus en dtail
ultrieurement, mais la base de donnes est un endroit o seront stockes les donnes de
notre application. Cest un lment indispensable mesure que lapplication grandit.

Installer Visual C# 2010 express


Nous avons donc besoin de notre chef de chantier, loutil de dveloppement. Cest un
logiciel qui va nous permettre de crer des applications et qui va nous fournir les outils
pour orchestrer nos dveloppements. La gamme de Microsoft est riche en outils
professionnels de qualit pour le dveloppement, notamment grce Visual Studio.
Notez que cet outil de dveloppement se nomme galement un IDE pour
Integrated Development Environment ce qui signifie Environnement de
dveloppement intgr .
Nous aurons recours au terme IDE rgulirement.
Pour apprendre et commencer dcouvrir lenvironnement de dveloppement, Microsoft
propose gratuitement Visual Studio dans sa version express. Cest une version allge de
lenvironnement de dveloppement qui permet de faire plein de choses, mais avec des
outils en moins par rapport aux versions payantes. Rassurez-vous, ces versions gratuites
sont trs fournies et permettent de faire tout ce dont on a besoin pour apprendre le C# et
suivre ce tutoriel.
Pour raliser des applications denvergure, il pourra cependant tre judicieux dinvestir
dans loutil complet et ainsi bnficier de fonctionnalits complmentaires qui permettent
damliorer, de faciliter et dindustrialiser les dveloppements.
Pour dvelopper en C# gratuitement et crer des applications Windows, nous allons avoir
besoin de Microsoft Visual C# 2010 Express que vous pouvez tlcharger en vous
rendant sur cette page.
Pour rsumer :
Visual Studio est la version payante de loutil de dveloppement.
Microsoft Visual C# 2010 Express est une version allge et gratuite de Visual
Studio, ddie au dveloppement en C#. Exactement ce quil nous faut
Cliquez sur Visual C# 2010 Express et choisissez la langue qui vous convient. Puis cliquez
sur Tlchargez.


Une fois lexcutable tlcharg, il ne reste plus qu le lancer et linstallation dmarre :

Cliquez sur Suivant pour dmarrer linstallation :

Vous devez prsent lire la licence dutilisation du logiciel et laccepter pour pouvoir
continuer linstallation :

Une application sans donnes, cest plutt rare. Cest un peu comme un site de-commerce
sans produits, un traitement de texte sans fichiers ou le site du zro sans tutoriel. On risque
de vite sennuyer .
Heureusement, le programme dinstallation nous propose dinstaller Microsoft SQL
Server 2008 express Service Pack 1 .
Microsoft propose en version gratuite un serveur de base de donnes allg. Il va nous
permettre de crer facilement une base de donnes et de lutiliser depuis nos applications
en C#.
Nous lavons dj voqu et nous y reviendrons plus en dtail dans un chapitre
ultrieur mais une base de donnes est un norme endroit o sont stockes les
donnes de notre application.
Nous avons galement voqu dans lintroduction quil tait possible de raliser des
applications qui ressemblent des applications Windows mais dans un navigateur, que
nous avons appel clients riches . Silverlight va justement permettre de crer ce genre
dapplication.
Cochez donc tout pour installer Silverlight et Sql Server et cliquez sur suivant :

Cliquez sur Installer en changeant ventuellement le dossier dinstallation :

Linstallation dmarre (vous devez tre connect Internet) :

Une fois linstallation termine cliquez sur Quitter.

A lheure o jcris ces lignes, il existe un service pack pour visual studio, le service pack
1. Cest un ensemble de corrections qui permettent damliorer la stabilit du logiciel.
Je vous invite tlcharger et installer ce service pack.
Vous voil avec votre copie de Visual C# express qui va vous permettre de crer des
programmes en C# gratuitement et facilement. Linstallation de loutil de dveloppement
est termine.
Notez que, bien que gratuite, vous aurez besoin denregistrer votre copie de
Visual C# express avant 30 jours. Cest une opration rapide et ncessitant un
compte Windows Live. Aprs cela, vous pourrez utiliser Visual C# express sans
retenues.
En rsum, nous avons install un outil de dveloppement, Visual C# 2010 dans sa version
express et une base de donnes, SQL Server express 2008.
Nous avons tous les outils ncessaires et nous allons pouvoir dmarrer (enfin !)
lapprentissage et la pratique du C#.

Dmarrer Visual C# 2010 Express


Nous allons vrifier que linstallation de Visual C# express a bien fonctionn. Et pour ce

faire, nous allons le dmarrer et commencer prendre en main ce formidable outil de


dveloppement.
Il vous semblera surement trs complexe au dbut mais vous allez voir, si vous suivez ce
tutoriel pas pas, vous allez apprendre les fonctionnalits indispensables. Elles seront
illustres par des copies dcrans vous permettant de plus facilement vous y retrouver. A
force dutiliser Visual C# express, vous verrez que vous vous sentirez de plus en plus
laise et peut-tre oserez-vous aller fouiller dans les menus ?
Commencez par dmarrer Visual C# 2010 Express. Le logiciel souvre sur la page de
dmarrage de Visual C# 2010 Express :

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

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

noter que si vous navez install que Visual C# express, vous aurez la mme
fentre que moi. Si vous disposez de la version payante de Visual Studio, alors
la fentre sera surement plus garnie. De mme, il y aura plus de choses si vous
avez install dautres outils de la gamme Express.
Ce que nous faisons ici, cest utiliser ce quon appelle un modle (plus couramment
appel par son quivalent anglais : template ) de cration de projet.
Si vous naviguez lintrieur des diffrents modles, vous pourrez constater que Visual
C# nous propose des modles de projets plus ou moins compliqus. Ces modles sont trs
utiles pour dmarrer un projet car toute la configuration du projet est dj faite. Le nombre
de modles peut tre diffrent en fonction de votre version de Visual Studio ou du nombre
de versions express installes.
Lapplication Console est la forme de projet pouvant produire une application excutable
la plus simple. Elle permet de raliser un programme qui va sexcuter dans la console
noire qui ressemble une fentre ms-dos, pour les dinosaures comme moi qui ont connu
cette poque A noter que les projets de type Bibliothque de classes permettent de
gnrer des assemblys de bibliothques (.dll).
Dans cette console, nous allons pouvoir notamment afficher du texte simple.
Ce type de projet est parfait pour dmarrer lapprentissage du C# car il ny a besoin que de
savoir comment afficher du texte pour commencer alors que pour raliser une application
graphique par exemple, il y a beaucoup dautres choses savoir.
En bas de la fentre de cration de projet, nous avons la possibilit de choisir un nom pour
le projet, ici ConsoleApplication1. Changeons le nom de notre application, par exemple

MaPremiereApplication, dans la zone correspondante.


Cliquons sur OK pour valider la cration de notre projet.
Visual C# Express cre alors pour nous les fichiers composant une application console
vide, qui utilise le C# comme langage et que nous avons nomm MaPremiereApplication.
Avant toute chose, nous allons enregistrer le projet. Allons dans le menu Fichier >
Enregistrer (ou utiliser le raccourci bien connu ctrl+s) :

Visual C# Express nous ouvre la fentre denregistrement de projet :

Nous pouvons donner un nom, prciser un emplacement o nous souhaitons que les
fichiers soient enregistrs et un nom de solution. Une case cocher pr-coche nous
propose de crer un rpertoire pour la solution. Cest ce que nous allons faire et cliquons
sur Enregistrer.
noter que pour les versions payantes de Visual Studio, le choix de
lemplacement et le nom de la solution sont renseigner au moment o lon
cre le projet. Une diffrence subtile

Analyse rapide de lenvironnement de dveloppement et


du code gnr

Allons dans lemplacement renseign (ici


c:\users\nico\documents\visual studio 2010\Projects), nous pouvons constater que

Visual C# Express a cr un rpertoire MaPremiereApplication, cest le fameux rpertoire


pour la solution quil nous a propos de crer.
Dans ce rpertoire, nous remarquons notamment un fichier MaPremiereApplication.sln.
Cest ce quon appelle le fichier de solution ; il sagit juste dun container de projets qui
va nous permettre de visualiser nos projets dans visual C# express.
En loccurrence, pour linstant, nous avons un seul projet dans la solution: lapplication
MaPremiereApplication, que nous retrouvons dans le sous rpertoire
MaPremiereApplication et qui contient notamment le fichier de projet :
MaPremiereApplication.csproj.
Le fichier dcrivant un projet crit en C# est prfix par cs (csproj).
Il y a encore un fichier digne dintrt (pour linstant) dans ce rpertoire, il sagit du
fichier Program.cs. Les fichiers dont lextension est .cs contiennent du code C#, cest dans
ce fichier que nous allons commencer taper nos premires lignes de code
noter que lensemble des fichiers contenant des instructions crites dans un
langage de programmation est appel le code source . Par extension, le
code correspond des instructions crites dans un langage de programmation.
Si nous retournons dans linterface de Visual C# express, nous pouvons retrouver quelque
chose comme a :

La zone verte numro 1 contient les diffrents fichiers ouverts sous la forme dun onglet.
On voit que par dfaut, Visual C# nous a cr et ouvert le fichier Program.cs.
Dans la zone rouge numro 2, cest lditeur de code. Il affiche le contenu du fichier
ouvert. Nous voyons des mots que nous ne comprenons pas encore. Cest du code qui a
t automatiquement gnr par Visual C#. Nous pouvons observer que les mots sont de

diffrentes couleurs. En effet, lditeur Visual C# express possde ce quon appelle une
coloration syntaxique, cest--dire que certains mots cls sont colors dune couleur
diffrente en fonction de leur signification ou de leur contexte afin de nous permettre de
nous y retrouver plus facilement.
La zone numro 3 en violet est lexplorateur de solutions, cest ici que lon voit le contenu
de la solution sur laquelle nous travaillons en ce moment. En loccurrence, il sagit de la
solution MaPremiereApplication qui contient un unique projet
MaPremiereApplication . Ce projet contient plusieurs sous lments :
Properties : contient des proprits de lapplication, on ne sen occupe pas pour

linstant
Rfrences : contient les rfrences de lapplication, on ne sen occupe pas pour

linstant
Program.cs est le fichier qui a t gnr par Visual C# et qui contient le code C#. Il

nous intresse fortement !!


La zone 4 en brun est la zone contenant les proprits de ce sur quoi nous travaillons en ce
moment. Ici, nous avons le curseur positionn sur le projet, il ny a pas beaucoup
dinformations except le nom du fichier de projet. Nous aurons loccasion de revenir sur
cette fentre plus tard.
La zone 5 en jaune nest pas affiche au premier lancement, elle contient la liste des
erreurs, des avertissements et des messages de notre application. Nous verrons comment
lafficher un peu plus bas.
La zone 6 en noir est la barre doutils, elle possde plusieurs boutons que nous pourrons
utiliser, notamment pour excuter notre application.

Ecrire du texte dans notre application


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

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

Nous venons dcrire une instruction qui va afficher la phrase Hello World !!, pour
linstant vous avez juste besoin de savoir a. Nous tudierons plus en dtail ultrieurement
quoi cela correspond exactement.

Lexcution du projet
Ca y est ! Nous avons crit notre premier code qui affiche un message trs
populaire. Mais pour le moment, a ne fait rien. On veut voir ce que a donne
!!!
Comme je vous comprends.
La premire chose faire est de transformer le langage C# que nous venons dcrire en
programme excutable. Cette phase sappelle la gnration de la solution sous Visual
C#. On lappelle souvent la compilation ou en anglais le build .
Allez dans le menu Dboguer et cliquez sur Gnrer la solution :

Visual C# lance alors la gnration de la solution et on voit dans la barre des taches en bas
gauche quil travaille jusqu nous indiquer que la gnration a russi :

Si nous allons dans le rpertoire contenant la solution, nous pouvons voir dans le
rpertoire MaPremiereApplication\MaPremiereApplication\bin\Release quil y a deux
fichiers :

MaPremiereApplication.exe
MaPremiereApplication.pdb
Le premier est le fichier excutable, possdant lextension .exe, qui est le rsultat du
processus de gnration. Il sagit bien de notre application.
Le second est un fichier particulier quil nest pas utile de connaitre pour linstant, nous
allons lignorer.
Excutons notre application en lanant le fichier excutable depuis lexplorateur de
fichiers. Dception, nous voyons peine un truc noirtre qui saffiche et qui se referme
immdiatement. Que sest-il pass ?
En fait, lapplication sest lance, a affich notre message et sest termine
immdiatement. Et tout a un brin trop rapidement a ne va pas tre pratique tout a.
Heureusement, Visual C# express arrive la rescousse. Retournons dans notre IDE
prfr. Nous allons ajouter un bouton dans la barre doutils. Javoue ne pas comprendre
pourquoi ce bouton est manquant dans linstallation par dfaut. Nous allons remdier ce
problme en cliquant sur la petite flche qui est ct de la barre doutils tout droite et
qui nous ouvre le menu droulant permettant dajouter ou supprimer des boutons et
cliquez sur Personnaliser :

Cliquez sur Ajouter une commande :

Allez dans la catgorie dboguer et choisissez Excuter sans dbogage puis cliquez
sur OK :

Enfin, fermez la fentre. Vous avez dsormais un nouveau bouton dans la barre doutils :

Si vous avez eu la flemme dajouter le bouton prcdemment, vous pouvez utiliser le


raccourci ctrl+F5 ou bien cliquer sur ce nouveau bouton pour excuter lapplication depuis
Visual c#. La console souvre nous dlivrant le message tant attendu :

Le message est dsormais visible car Visual C# nous demande dappuyer sur une touche
pour que lapplication se termine, ce qui nous laisse donc le temps dapprcier lexcution
de notre superbe programme.
Wahouu, a y est, notre premire application en C# !!!
Je suis fier de nous, mais nous nallons pas en rester l, nous sommes dsormais fin pars
pour apprendre le C#.
En rsum

Visual C# Express est loutil de dveloppement gratuit de Microsoft permettant de


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

Chapitre 3

La syntaxe gnrale du C#

Nous allons aborder ici la syntaxe gnrale du langage de programmation C# dans le cadre
dune application console. Il est en effet possible de crer plein de choses diffrentes avec
le C# comme une application web, des jeux, etc.
Dans cette optique, nous allons utiliser trs souvent linstruction :
Console.WriteLine(); que nous avons vue au chapitre prcdent et qui est une
instruction ddie laffichage sur la console. Cest une instruction qui va savrer trs
pratique pour notre apprentissage car nous pourrons avoir une reprsentation visuelle de
ce que nous allons apprendre.
Il est globalement rare quune application ne doive afficher que du texte, sans aucune mise
en forme. Vous verrez en fin de tutoriel comment raliser des applications un peu plus
volues graphiquement.
Prparez vous, nous plongeons petit petit dans lunivers du C#. Dans ce chapitre, nous
allons nous attaquer la syntaxe gnrale du C# et nous serons capable de reconnatre les
lignes de code et de quoi elles se composent.

Ecrire une ligne de code


Les lignes de code crites avec le langage de dveloppement C# doivent scrire dans des
fichiers dont lextension est .cs. Nous avons vu dans le chapitre prcdent que nous avons
crit dans le fichier Program.cs qui est le fichier qui a t gnr par Visual C# lors de la
cration du projet. Nous y avons notamment rajout une instruction permettant dafficher
du texte.
Les lignes de code C# se lisent et scrivent de haut en bas et de gauche droite, comme
un livre normal.
Aussi, une instruction crite avant une autre sera en gnral excute avant celle-ci.
Attention, chaque ligne de code doit tre correcte syntaxiquement sinon le
compilateur ne saura pas le traduire en langage excutable.
Par exemple, si la fin de mon instruction, je retire le point-virgule ou si jorthographie
mal le mot WriteLine, jaurai :

Visual C# Express me signale quil y a un problme en mettant en valeur un manque au


niveau de la fin de linstruction et il me souligne galement le mot WritLine .
Dans la fentre du bas, il mindique quil a deux erreurs et me donne des prcisions sur
celles-ci avec ventuellement des pistes pour rsoudre ces erreurs.
Si je tente de lancer mon application (raccourci ctrl+F5), Visual C# Express va tenter de
compiler et dexcuter lapplication. Ceci ntant pas possible, il maffichera un message
indiquant quil y a des erreurs.

Ce sont des erreurs de compilation quil va falloir rsoudre si lon souhaite que
lapplication console puisse sexcuter.
Nous allons voir dans les chapitres suivant comment crire correctement des instructions

en C#. Mais il est important de noter lheure actuelle que le C# est sensible la casse,
ce qui veut dire que les majuscules comptent !
Ainsi le mot WriteLine et le mot WriTEline sont deux mots bien distincts et
peuvent potentiellement reprsenter deux instructions diffrentes. Ici, le deuxime mot est
incorrect car il nexiste pas.
Rappelez-vous bien que la casse est dterminante pour que lapplication puisse
compiler.

Le caractre de terminaison de ligne


En gnral, une instruction en code C# scrit sur une ligne et se termine par un pointvirgule.
Ainsi, linstruction que nous avons vue plus haut :
Console.WriteLine("Hello World !!");

se termine au niveau du point-virgule.


Il aurait t possible de remplacer le code crit :
class Program
{
static void Main(string[] args)
{
Console.WriteLine("Hello World !!");
}
}

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

ou encore :
class Program
{
static void Main(string[] args)
{
Console


.WriteLine("Hello World !!"

);
}
}

En gnral, pour que le code soit le plus lisible possible, on crit une instruction par ligne
et on indente le code de faon ce que les blocs soient lisibles.
Un bloc de code est dlimit par des accolades { et }. Nous y reviendrons plus
tard.
Indenter signifie que chaque ligne de code qui fait partie dun mme bloc de
code commence avec le mme retrait sur lditeur. Ce sont soit des tabulations,
soit des espaces qui permettent de faire ce retrait.
Visual C# express nous aide pour faire correctement cette indentation quand nous crivons

du code. Il peut galement remettre toute la page en forme avec la combinaison de touche
: ctrl+k+ctrl+d.
Dcortiquons prsent cette ligne de code :
Console.WriteLine("Hello World !!");

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

Les commentaires
Pour faciliter la comprhension du code ou pour se rappeler un point prcis, il est possible
de mettre des commentaires dans son code. Les commentaires sont ignors par le
compilateur et nont quune valeur informative pour le dveloppeur.
Dans un fichier de code C# (.cs), on peut crire des commentaires de 2 faons diffrentes :
Soit en commenant son commentaire par /* et en le terminant par */ ce qui permet
dcrire un commentaire sur plusieurs lignes.
Soit en utilisant // et tout ce qui se trouve aprs sur la mme ligne est alors un
commentaire.
Visual C# express colore les commentaires en vert pour faciliter leurs identifications.
/* permet d'afficher du texte
sur la console */
Console.WriteLine("Hello World !!"); // ne pas oublier le point virgule

A noter quon peut commenter plusieurs lignes de code avec le raccourci clavier
ctrl+k + ctrl+c et dcommenter plusieurs lignes de code avec le raccourci
clavier ctrl+k+ctrl+u.

La compltion automatique
Visual C# express est un formidable outil qui nous facilite tout moment la tche,

notamment grce la compltion automatique.


La compltion automatique est le fait de proposer de complter automatiquement ce que
nous sommes en train dcrire en se basant sur ce que nous avons le droit de faire.
Par exemple, si vous avez cherch crire linstruction :
Console.WriteLine("Hello World !!");

vous avez pu constater que lors de lappui sur la touche C, Visual C# express nous affiche
une fentre avec tout ce qui commence par C :

Au fur et mesure de la saisie, il affine les propositions pour se positionner sur la plus
pertinente. Il est possible de valider la proposition en appuyant sur la touche Entre. Non
seulement cela nous conomise des appuis de touches, paresseux comme nous sommes,
mais cela nous permet galement de vrifier la syntaxe de ce que nous crivons et
dobtenir galement une mini-aide sur ce que nous essayons dutiliser.

Ainsi, finies les fautes de frappe qui rsultent en une erreur de compilation ou les listes de
mots cls dont il faut absolument retenir lcriture.
De la mme faon, une fois que vous avez fini de saisir Console vous allez saisir le
point . et Visual C# express va nous proposer toute une srie dinstruction en rapport
avec le dbut de linstruction :

Nous pourrons ainsi facilement finir de saisir WriteLine et ceci sans erreur dcriture,
ni problme de majuscule.
En rsum

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

Chapitre 4

Les variables

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

Quest-ce quune variable ?


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

On appelle ceci la dclaration de la variable age .


Le mot cl int permet dindiquer au compilateur que la variable age est un entier
numrique. int correspond au dbut d integer qui veut dire entier en anglais.

Ici, la variable age na pas t initialise, elle ne pourra pas tre utilise car le
compilateur ne sait pas quelle valeur il y a dans la variable age.
Pour linitialiser (on parle galement daffecter une valeur ) on utilisera loprateur
gal ( = ).
int age;
age = 30;

Notre variable age possde dsormais lentier numrique 30 comme valeur.


Linitialisation dune variable peut galement se faire au mme moment que sa
dclaration. Ainsi, on pourra remplacer le code prcdent par :
int age = 30;

Pour dclarer une variable en C#, on commence toujours par indiquer son type (int, ici un
entier) et son nom (age). Il faudra imprativement affecter une valeur cette variable avec
loprateur = , soit sur la mme instruction que la dclaration, soit un peu plus loin dans
le code, mais dans tous les cas, avant lutilisation de cette variable.
Nous pouvons tout moment demander la valeur contenue dans la variable age, par
exemple :
int age = 30;
Console.WriteLine(age); // affiche 30

Il est possible de modifier la valeur de la variable nimporte quel moment grce


lemploi de loprateur = que nous avons aperu :
int age = 30;
Console.WriteLine(age); // affiche 30
age = 20;
Console.WriteLine(age); // affiche 20

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

Cest ce quon appelle le camel case.


Attention, suivant le principe de sensibilit la casse, il faut faire attention car
ageduvisiteur et ageDuVisiteur seront deux variables diffrentes :
int ageduvisiteur = 30;
int ageDuVisiteur = 20;

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

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

Les diffrents types de variables


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

ou encore un dcimal avec :


decimal soldeCompteBancaire = 100;

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


bool estVrai = true;

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

On ne peut par exemple pas stocker le prnom Nicolas dans un entier.


Les principaux types de base du framework .NET sont :
Type

Description

byte

Entier de 0 255

short

Entier de -32768 32767

int

Entier de -2147483648 2147483647

long

Entier de -9223372036854775808 9223372036854775807

float

Nombre simple prcision de -3,402823e38 3,402823e38

double

Nombre double prcision de -1,79769313486232e308 1,79769313486232e308

decimal Nombre dcimal convenant particulirement aux calculs financiers (en raison de ses nombres significatifs
aprs la virgule)
char

Reprsente un caractre

string

Une chaine de caractre

bool

Une valeur boolenne (vrai ou faux)

Vous verrez plus loin quil existe encore dautres types dans le framework .NET et quon
peut galement construire les siens.

Affectations, oprations, concatnation


Il est possible deffectuer des oprations sur les variables et entre les variables. Nous
avons dj vu comment affecter une valeur une variable grce loprateur =.
int age = 30;
string prenom = "nicolas";

Note : dans ce paragraphe, je vais vous donner plusieurs exemples


daffectations. Ces affectations seront faites sur la mme instruction que la
dclaration pour des raisons de concision. Mais ces exemples sont videmment
fonctionnels pour des affectations qui se situent un endroit diffrent de la
dclaration.
En plus de la simple affectation, nous pouvons galement faire des oprations, par
exemple :
int resultat = 2 * 3;

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

Les oprateurs + , * , / ou encore - (que nous navons pas encore utilis)


servent bien videmment faire les oprations mathmatiques qui leur correspondent,
savoir respectivement laddition, la multiplication, la division et la soustraction.
Vous aurez donc surement devin que la variable resultat contient 6 et que la moyenne
vaut 25.
Il est noter que les variables contiennent une valeur qui ne peut voluer quen affectant
une nouvelle valeur cette variable.
Ainsi, si jai le code suivant :
int age1 = 20;
int age2 = 30;
int moyenne = (age1 + age2) / 2;
age2 = 40;

la variable moyenne vaudra toujours 25 mme si jai chang la valeur de la variable


age2 . En effet, lors du calcul de la moyenne, jai rang dans mon classeur la valeur 25
grce loprateur daffectation = et jai referm mon classeur. Le fait de changer la
valeur du classeur age2 ninfluence en rien le classeur moyenne dans la mesure o
il est ferm. Pour le modifier, il faudrait r-excuter lopration daffectation de la variable
moyenne, en crivant nouveau linstruction de calcul, cest--dire :
int age1 = 20;
int age2 = 30;
int moyenne = (age1 + age2) / 2;
age2 = 40;
moyenne = (age1 + age2) / 2;

Loprateur + peut galement servir concatner des chaines de caractres, par

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

Dautres oprateurs particuliers existent que nous ne trouvons pas dans les cours de
mathmatiques. Par exemple, loprateur ++ qui permet de raliser une incrmentation de
1, ou loprateur qui permet de faire une dcrmentation de 1.
De mme, les oprateurs que nous avons dj vus peuvent se cumuler loprateur = pour
simplifier une opration qui prend une variable comme oprande et cette mme variable
comme rsultat.
Par exemple :
int age = 20;
age = age + 10; // age contient 30 (addition)
age = age++; // age contient 31 (incrmentation de 1)
age = age--; // age contient 30 (dcrmentation de 1)
age += 10; // quivalent age = age + 10 (age contient 40)
age /= 2; // quivalent age = age / 2 => (age contient 20)

Comme nous avons pu le voir dans nos cours de mathmatiques, il est possible de grouper
des oprations avec des parenthses pour agir sur leurs priorits.
Ainsi, linstruction prcdemment vue :
int moyenne = (age1 + age2) / 2;

effectue bien la somme des deux ges avant de les diviser par 2, car les parenthses sont
prioritaires.
Cependant, linstruction suivante :
int moyenne = age1 + age2 / 2;

aurait commenc par diviser lage2 par 2 et aurait ajout lage1, ce qui naurait plus rien
voir avec une moyenne. En effet, la division est prioritaire par rapport laddition.
Attention, la division ici est un peu particulire.
Prenons cet exemple :
int moyenne = 5 / 2;
Console.WriteLine(moyenne);

Si nous lexcutons, nous voyons que moyenne vaut 2.


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

double moyenne = 5.0 / 2.0;


Console.WriteLine(moyenne);

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

Les caractres spciaux dans les chaines de caractres


En ce qui concerne laffectation de chaines de caractres, vous risquez davoir des
surprises si vous tentez de mettre des caractres spciaux dans des variables de type
string.
En effet, une chaine de caractres tant dlimite par des guillemets , comment faire
pour que notre chaine de caractres puisse contenir des guillemets ?
Cest l quintervient le caractre spcial \ qui sera mettre juste devant le guillemet, par
exemple le code suivant :
string phrase = "Mon prnom est \"Nicolas\"";
Console.WriteLine(phrase);

affichera :

Si vous avez test par vous-mme linstruction Console.WriteLine et enchain plusieurs


instructions qui crivent des lignes, vous avez pu remarquer que nous passions la ligne
chaque fois. Cest le rle de linstruction WriteLine qui affiche la chaine de caractres et
passe la ligne la fin de la chane de caractres.
Nous pouvons faire la mme chose en utilisant le caractre spcial \n . Il permet de
passer la ligne chaque fois quil est rencontr.
Ainsi, le code suivant :
string phrase = "Mon prnom est \"Nicolas\"";

Console.WriteLine(phrase);
Console.WriteLine("Passe\n\nla\nligne\n\n\n");

affichera :

o nous remarquons bien les divers passages la ligne.


Vous me diriez quon pourrait enchaner les Console.WriteLine et vous auriez raison.
Mais les caractres spciaux nous permettent de faire dautres choses comme une
tabulation par exemple grce au caractre spcial \t . Le code suivant :
Console.WriteLine("Choses faire :");
Console.WriteLine("\t - Arroser les plantes");
Console.WriteLine("\t - Laver la voiture");

permettra dafficher des tabulations, comme illustr ci-dessous :

Nous avons vu que le caractre \ tait un caractre spcial et quil permettait de dire au
compilateur que nous voulions lutiliser combin la valeur qui le suit, permettant d avoir
une tabulation ou un retour la ligne. Comment pourrons-nous avoir une chaine de
caractres qui contienne ce fameux caractre ?

Le principe est le mme, il suffira de faire suivre ce fameux caractre spcial de lui-mme
:
string fichier = "c:\\repertoire\\fichier.cs";
Console.WriteLine(fichier);

Ce qui donnera :

Pour ce cas particulier, il est galement possible dutiliser la syntaxe suivante en utilisant
le caractre spcial @ devant la chaine de caractres :
string fichier = @"c:\repertoire\fichier.cs"; // contient : c:\repertoire\fichier.cs

Bien sur, nous pouvons stocker des caractres spciaux dans des variables pour faire par
exemple :
string sautDeLigne = "\n";
Console.WriteLine("Passer" + sautDeLigne + "" +
sautDeLigne + "la" + sautDeLigne + "ligne");

Dans ce cas, la variable sautDeLigne peut tre remplace par une espce de
variable qui existe dj dans le framework .NET, savoir
Environment.NewLine.
Ce qui permet davoir le code suivant :
Console.WriteLine("Passer" + Environment.NewLine + "" +
Environment.NewLine + "la" + Environment.NewLine + "ligne");

permettant dafficher :

Notez quil est possible de passer la ligne lors de lcriture dune instruction
C# comme je lai fait dans le dernier bout de code afin damliorer la lisibilit.
Noubliez pas que cest le point-virgule qui termine linstruction.
Environment.NewLine ? Une espce de variable ? Quest-ce que cest que cette
chose l ?
En fait, je triche un peu sur les mots. Pour faciliter la comprhension, on peut considrer
que Environment.NewLine est une variable, au mme titre que la variable sautDeLigne
que nous avons dfini. En ralit, cest un peu plus complexe quune variable. Nous
dcouvrirons plus loin de quoi il sagit vraiment.
En rsum

Une variable est une zone mmoire permettant de stocker une valeur dun type
particulier.
Le C# possde plein de types prdfinis, comme les entiers (int), les chanes de
caractres (string), etc.
On utilise loprateur = pour affecter une valeur une variable.
Il est possible de faire des oprations entre les variables.

Chapitre 5

Les instructions conditionnelles

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

Les oprateurs de comparaison


Une condition se construit grce des oprateurs de comparaison. On dnombre
plusieurs oprateurs de comparaisons, les plus courants sont :
Oprateur

Description

==

Egalit

!=

Diffrence

>

Suprieur

<

Infrieur

>=

Suprieur ou gal

<=

Infrieur ou gal

&&

ET logique

||

OU logique

Ngation

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

Linstruction if

Linstruction if permet dexcuter du code si une condition est vraie (if = si en anglais).
Par exemple :
decimal compteEnBanque = 300;
if (compteEnBanque >= 0)
Console.WriteLine("Votre compte est crditeur");

Ici, nous avons une variable contenant le solde de notre compte en banque. Si notre solde
est suprieur ou gal 0 alors nous affichons que le compte est crditeur.
Pour afficher que le compte est dbiteur, on pourrait tester si la valeur de la variable est
infrieure 0 et afficher que le compte est dbiteur :
decimal compteEnBanque = 300;
if (compteEnBanque >= 0)
Console.WriteLine("Votre compte est crditeur");
if (compteEnBanque < 0)
Console.WriteLine("Votre compte est dbiteur");

Une autre solution est dutiliser le mot cl else, qui veut dire sinon en anglais.
Si la valeur est vraie, alors on fait quelque chose, sinon, on fait autre chose , ce qui se
traduit en C# par :
decimal compteEnBanque = 300;
if (compteEnBanque >= 0)
Console.WriteLine("Votre compte est crditeur");
else
Console.WriteLine("Votre compte est dbiteur");

Il faut bien se rendre compte que linstruction if teste si une valeur est vraie (dans
lexemple prcdent la comparaison compteEnBanque >= 0).
On a vu rapidement dans les chapitres prcdents quil existait un type de variable qui
permettait de stocker une valeur vraie ou fausse : le type bool, autrement appel boolen
(boolean en anglais).
Ainsi, il sera galement possible de tester la valeur dun boolen. Lexemple prcdent
peut aussi scrire :
decimal compteEnBanque = 300;
bool estCrediteur = (compteEnBanque >= 0);
if (estCrediteur)
Console.WriteLine("Votre compte est crditeur");
else
Console.WriteLine("Votre compte est dbiteur");

noter que les parenthses autour de linstruction de comparaison sont facultatives, je les
ai crites ici pour clairement identifier que la variable estCrediteur va contenir une
valeur qui est le rsultat de lopration de comparaison compte en banque est suprieur
ou gal 0 , en loccurrence vrai.
Voici dautres exemples pour vous permettre dapprhender plus prcisment le
fonctionnement du type bool :
int age = 30;
bool estAgeDe30Ans = age == 30;
Console.WriteLine(estAgeDe30Ans); // affiche True
bool estSuperieurA10 = age > 10;
Console.WriteLine(estSuperieurA10); // affiche True
bool estDifferentDe30 = age != 30;
Console.WriteLine(estDifferentDe30); // affiche False

Un type bool peut prendre deux valeurs, vrai ou faux, qui scrivent avec les mots cls
true et false.
bool estVrai = true;

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

Il est galement possible de combiner les tests grce aux oprateurs de logique
conditionnelle, par exemple && qui correspond loprateur ET.
Dans lexemple qui suit, nous affichons le message de bienvenue uniquement si le login
est Nicolas ET que le mot de passe est test . Si lun des deux ne correspond pas,
nous irons dans linstruction else.
string login = "Nicolas";
string motDePasse = "test";
if (login == "Nicolas" && motDePasse == "test")
Console.WriteLine("Bienvenue Nicolas");
else
Console.WriteLine("Login incorrect");

Remarquons ici que nous avons utilis le test dgalit == , ne pas


confondre avec loprateur daffection = . Cest une erreur classique de
dbutant.
Dautres oprateurs de logiques existent, nous avons notamment loprateur || qui
correspond au OU logique :
if (civilite == "Mme" || civilite == "Mlle")
Console.WriteLine("Vous tes une femme");
else
Console.WriteLine("Vous tes un homme");

Lexemple parle de lui-mme ; si la civilit de la personne est Mme ou Mlle, alors nous
avons faire avec une femme.
A noter ici que si la premire condition du if est vraie alors la deuxime ne sera pas
value. Cest un dtail ici, mais cela peut savrer important dans certaines situations
dont une que nous verrons un peu plus loin.
Un autre oprateur trs courant est la ngation que lon utilise avec loprateur ! . Par
exemple :
bool estVrai = true;
if (!estVrai)
Console.WriteLine("C'est faux !");
else
Console.WriteLine("C'est vrai !");

Ce test pourrait se lire ainsi : Si la ngation de la variable estVrai est vraie, alors on
crira cest faux .
La variable estVrai tant gale true, sa ngation vaut false.
Dans cet exemple, le programme nous affichera donc linstruction correspondant au else,
savoir Cest vrai ! .
Rappelez-vous, nous avons dit quune instruction se finissait en gnral par un pointvirgule. Comment cela se fait-il alors quil ny ait pas de point-virgule la fin du if ou du
else ?
Et si nous crivions lexemple prcdent de cette faon ?
bool estVrai = true;
if (!estVrai) Console.WriteLine("C'est faux !");
else Console.WriteLine("C'est vrai !");

Ceci est tout fait valable et permet de voir o sarrte vraiment linstruction grce au

point-virgule. Cependant, nous crivons en gnral ces instructions de la premire faon


afin que celles-ci soient plus lisibles.
Vous aurez loccasion de rencontrer dans les chapitres suivants dautres instructions qui ne
se terminent pas obligatoirement par un point-virgule.
Nous verrons dans le chapitre suivant comment excuter plusieurs instructions
aprs une instruction conditionnelle en les groupant dans des blocs de code,
dlimits par des accolades { et } .
Remarquons enfin quil est possible denchaner les tests de manire traiter plusieurs
conditions en utilisant la combinaison else-if. Cela donne :
if (civilite == "Mme")
Console.WriteLine("Vous tes une femme");
else if (civilite == "Mlle")
Console.WriteLine("Vous tes une femme non marie");
else if (civilite == "M.")
Console.WriteLine("Vous tes un homme");
else
Console.WriteLine("Je n'ai pas pu dterminer votre civilit");

Linstruction Switch
Linstruction switch peut tre utilise lorsquune variable peut prendre beaucoup de
valeurs. Elle permet de simplifier lcriture.
Ainsi, linstruction suivante :
string civilite = "M.";
if (civilite == "M.")
Console.WriteLine("Bonjour monsieur");
if (civilite == "Mme")
Console.WriteLine("Bonjour madame");
if (civilite == "Mlle")
Console.WriteLine("Bonjour mademoiselle");

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

Switch commence par valuer la variable qui lui est passe entre parenthses. Avec le

mot cl case on numre les diffrents cas possible pour la variable et on excute les
instructions correspondante jusquau mot cl break qui signifie que lon sort du switch.
Nous pouvons galement indiquer une valeur par dfaut en utilisant le mot cl default,
ainsi dans lexemple suivant tout ce qui nest pas M. ou Mme ou Mlle donnera
laffichage dun Bonjour inconnu :
switch (civilite)
{

case "M." :
Console.WriteLine("Bonjour monsieur");
break;
case "Mme":
Console.WriteLine("Bonjour madame");
break;
case "Mlle":
Console.WriteLine("Bonjour mademoiselle");
break;
default:
Console.WriteLine("Bonjour inconnu");
break;
}

Nous pouvons galement enchainer plusieurs cas pour quils fassent la mme chose, ce
qui reproduit le fonctionnement de loprateur logique OU ( || ). Par exemple, on pourra
remplacer lexemple suivant :
string mois = "Janvier";
if (mois == "Mars" || mois == "Avril" || mois == "Mai")
Console.WriteLine("C'est le printemps");
if (mois == "Juin" || mois == "Juillet" || mois == "Aout")
Console.WriteLine("C'est l't");
if (mois == "Septembre" || mois == "Octobre" || mois == "Novembre")
Console.WriteLine("C'est l'automne");
if (mois == "Decembre" || mois == "Janvier" || mois == "Fvrier")
Console.WriteLine("C'est l'hiver");

par :
switch (mois)
{
case "Mars":
case "Avril":
case "Mai":
Console.WriteLine("C'est le printemps");
break;
case "Juin":
case "Juillet":
case "Aout":
Console.WriteLine("C'est l't");
break;
case "Septembre":
case "Octobre":
case "Novembre":
Console.WriteLine("C'est l'automne");
break;
case "Dcembre":
case "Janvier":
case "Fvrier":
Console.WriteLine("C'est l'hiver");
break;
}

Qui allge quand mme lcriture et la rend beaucoup plus lisible.


En rsum

Les instructions conditionnelles permettent dexcuter des instructions seulement si


une condition est vrifie.
On utilise en gnral le rsultat dune comparaison dans une instruction
conditionnelle.
Le C# possde beaucoup doprateurs de comparaison, comme loprateur dgalit
==, loprateur de supriorit >, dinfriorit <, etc.

Chapitre 6

Les blocs de code et la porte dune variable

Nous avons rgulirement utilis dans le chapitre prcdent les accolades ouvrantes et
fermantes : \{ et \}. Nous avons rapidement dit que ces accolades servaient crer des
blocs de code.
Lutilisation daccolades implique galement une autre subtilit. Vous lavez vu dans le
titre du chapitre, il sagit de la porte dune variable.
Regardons prsent comment cela fonctionne.

Les blocs de code


Les blocs de code permettent de grouper plusieurs instructions qui vont sexcuter dans le
mme contexte.
Cela peut tre le cas par exemple aprs un if, nous pourrions souhaiter effectuer plusieurs
instructions. Par exemple :
decimal compteEnBanque = 300;
if (compteEnBanque >= 0)
{
Console.WriteLine("Votre compte est crditeur");
Console.WriteLine("Voici comment ouvrir un livret ");
}
else
{
Console.WriteLine("Votre compte est dbiteur");
Console.WriteLine("Noubliez pas que les frais de dcouverts sont de ");
}

Ici, nous enchanons deux Console.WriteLine en fonction du rsultat de la comparaison de


compteEnBanque avec 0.
Les blocs de code seront utiles ds quon voudra regrouper plusieurs instructions. Cest le
cas pour les instructions conditionnelles mais nous verrons beaucoup dautres utilisations,
comme le switch que nous avons vu juste au-dessus, les boucles ou les mthodes que nous
allons aborder dans le chapitre suivant.

La porte dune variable


C# porte on se croirait au cours de musique
En fait, la porte dune variable est la zone de code dans laquelle une variable est

utilisable. Elle correspond en gnral au bloc de code dans lequel est dfinie la variable.
Ainsi, le code suivant :
static void Main(string[] args)
{
string prenom = "Nicolas";
string civilite = "M.";
if (prenom == "Nicolas")
{
int age = 30;
Console.WriteLine("Votre age est : " + age);
switch (civilite)
{
case "M.":
Console.WriteLine("Vous tes un homme de " + age + " ans");
break;
case "Mme":
Console.WriteLine("Vous tes une femme de " + age + " ans");
break;
}
}
if (age >= 18)
{
Console.WriteLine(prenom + ", vous tes majeur");
}
}

est incorrect et provoquera une erreur de compilation. En effet, nous essayons daccder
la variable age en dehors du bloc de code o elle est dfinie. Nous voyons que cette
variable est dfinie dans le bloc qui est excut lorsque le test dgalit du prnom avec la
chaine Nicolas est vrai alors que nous essayons de la comparer 18 dans un endroit o
elle nexiste plus.
Nous pouvons utiliser age sans aucun problme dans tout le premier if, et mme dans
les sous blocs de code, comme cest le cas dans le sous bloc du switch, mais pas en dehors
du bloc de code dans lequel la variable est dfinie. Ainsi, la variable prenom est
accessible dans le dernier if car elle a t dfinie dans un bloc pre.
Vous noterez quici, la compltion nous est utile. En effet, Visual C# express propose de
nous complter le nom de la variable dans un bloc o elle est accessible. Dans un bloc o
elle ne lest pas, la compltion automatique ne nous la propose pas.
Comme Visual C# express est malin comme une machine, si la compltion ne propose pas
ce que vous souhaitez, cest probablement que vous ny avez pas le droit. Une des
explications peut tre que la porte ne vous lautorise pas.
Pour corriger lexemple prcdent, il faut dclarer la variable age au mme niveau que
la variable prnom.
Ok, mais alors, pourquoi on ne dclarerait pas tout au dbut une bonne fois pour
toute ? Cela viterait ces erreurs non ?
Evidemment non, vous verrez quil nest pas possible de faire cela. Gnralement,
lutilisation de variables accessibles de partout est une mauvaise pratique de
dveloppement (cest ce quon appelle des variables globales ). Mme si on peut avoir
un quivalent en C#, il faut se rappeler que plus une variable est utilise dans la plus petite
porte possible, mieux elle sera utilise et plus elle sera pertinente.
Je vous conseille donc dessayer de dterminer le bloc de code minimal o lutilisation de
la variable est adapte.
En rsum

Un bloc de code permet de regrouper des instructions qui commencent par \{ et qui
finissent par \}.
Une variable dfinie lintrieur dun bloc de code aura pour porte ce bloc de code.

Chapitre 7

Les mthodes

lment indispensable de tout programme informatique, une mthode regroupe un


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

Crer une mthode


Le but de la mthode est de factoriser du code afin dviter davoir rpter sans arrt le
mme code et ceci pour deux raisons essentielles :
Dj parce que lhomme est un tre paresseux qui utilise son intelligence pour viter
le travail inutile.
Ensuite parce que si jamais il y a quelque chose corriger dans ce bout de code et sil
est dupliqu plusieurs endroits, alors nous allons devoir faire une correction dans
tous ces endroits. Si le code est factoris un unique endroit, nous ferons une unique
correction. (Oui oui, encore la paresse mais aussi cela permet dviter doublier un
bout de code dans un endroit cach).
Ce souci de factorisation est connu comme le principe DRY qui est lacronyme des
mots anglais Dont Repeat Yourself , ce qui veut bien sr dire : Ne vous rptez pas
. Le but de ce principe est de ne jamais ( quelques exceptions prs bien sr ) avoir
rcrire la mme ligne de code.
Par exemple, imaginons quelques instructions qui soccupent dcrire un message de
bienvenue avec le nom de lutilisateur. Le code C# pourrait tre :
Console.WriteLine("Bonjour Nicolas");
Console.WriteLine("-------" + Environment.NewLine);
Console.WriteLine("\tBienvenue dans le monde merveilleux du C#");

Note : Dans linstruction Console.WriteLine que nous utilisons rgulirement,

WriteLine est une mthode.


Si plus tard, on veut r-afficher le message de bienvenue, il faudra rcrire ces 4 lignes de
codes. Sauf si nous utilisons une mthode :
static void AffichageBienvenue()
{
Console.WriteLine("Bonjour Nicolas");
Console.WriteLine("-------" + Environment.NewLine);
Console.WriteLine("\tBienvenue dans le monde merveilleux du C#");
}

Dans lexemple prcdent, je dfinis une mthode qui sappelle AffichageBienvenue.


Linstruction :
static void AffichageBienvenue()

est ce quon appelle la signature de la mthode. Elle nous renseigne sur les paramtres de
la mthode et sur ce quelle va renvoyer.
Le mot cl void signifie que la mthode ne renvoie rien. Les parenthses vides la fin de
la signature indiquent que la mthode na pas de paramtres.
Cest la forme de la mthode la plus simple possible.
Le mot cl static ne nous intresse pas pour linstant, mais sachez quil sert indiquer
que la mthode est toujours disponible et prte tre utilise. Dans ce contexte, il est
obligatoire. Nous y reviendrons.
En-dessous de la signature de la mthode, nous retrouvons les accolades. Elles permettent
de dlimiter la mthode. Le bloc de code ainsi form constitue ce quon appelle le corps
de la mthode .
En rsum, pour dclarer une mthode, nous aurons :
Signature de la mthode
{
Bloc de code de la mthode
}

Nous pouvons dsormais appeler (cest--dire : excuter) cette mthode dans notre
programme grce son nom. Par exemple, ici je lappelle trs facilement 2 fois de suite :
static void Main(string[] args)
{
AffichageBienvenue();
AffichageBienvenue();
}
static void AffichageBienvenue()
{
Console.WriteLine("Bonjour Nicolas");
Console.WriteLine("-------" + Environment.NewLine);
Console.WriteLine("\tBienvenue dans le monde merveilleux du C#");
}

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

La mthode spciale Main()

La signature de la mthode que lon vient de crer ne vous rappelle rien ? Mais si, lautre
bloc au-dessus de notre mthode, qui ressemble lui aussi une mthode.
Il sagit dune mthode spciale, la mthode Main().
Elle a t gnre par Visual C# express lorsque nous avons cr le projet Console.
Cette mthode est en fait le point dentre de lapplication, cest--dire que quand le CLR
tente dexcuter notre application, il recherche cette mthode afin de pouvoir commencer
excuter des instructions partir delle. Sil ne la trouve pas, alors, il ne pourra pas
excuter notre application. Cest pour cela quil est important que cette mthode soit
accessible de partout ; rappelez-vous, cest grce au mot cl static que nous aurons
loccasion dtudier plus en dtail ultrieurement.
Visual C# express nous garde bien de cette erreur. En effet, si vous supprimez cette
mthode (ou que vous enlevez le mot cl static) et que vous tentez de compiler notre
application, vous aurez le message derreur suivant :
Citation : Compilateur

Erreur 1 Le programme C:\Users\Nico\Documents\Visual Studio


2010\Projects\C#\MaPremiereApplication\MaPremiereApplication\obj\x86\Release\MaPremiereA
ne contient pas une mthode Main statique approprie pour un point dentre
Le message derreur est clair. Il a besoin dune mthode Main() pour dmarrer.
Voil pour cette mthode spciale Main(). Elle est indispensable dans tout
programme excutable et cest par l que le programme dmarre.
Les lecteurs attentifs auront remarqu que cette mthode possde des choses dans la
signature, entre les parenthses Des paramtres ! Dcouvrons-les dans le prochain
chapitre

Paramtres dune mthode


Super, nous savons crer des mthodes. Nous allons pouvoir crer une mthode qui
permet de souhaiter la bienvenue la personne qui vient de se connecter notre
application par exemple. Si cest Nicolas qui vient de se connecter, nous allons pouvoir
appeler la mthode AffichageBienvenueNicolas(). Si cest Jrmie, nous appellerons la
mthode AffichageBienvenueJeremie(), etc
static void AffichageBienvenueNicolas()
{
Console.WriteLine("Bonjour Nicolas");
Console.WriteLine("-------" + Environment.NewLine);
Console.WriteLine("\tBienvenue dans le monde merveilleux du C#");
}
static void AffichageBienvenueJeremie()
{
Console.WriteLine("Bonjour Jrmie");
Console.WriteLine("-------" + Environment.NewLine);
Console.WriteLine("\tBienvenue dans le monde merveilleux du C#");
}

Bof finalement, ce nest pas si super que a en fait. Alors que nous venions juste
dvoquer le principe DRY, nous nous retrouvons avec deux mthodes quasiment
identiques qui ne diffrent que dune toute petite chose.
Cest l quinterviennent les paramtres de mthodes. Nous lavons voqu au paragraphe
prcdent, il est possible de passer des paramtres une mthode. Ainsi, nous pourrons
utiliser les valeurs de ces paramtres dans le corps de nos mthodes, les mthodes en
deviendront dautant plus gnriques.
Dans notre exemple daffichage de message de bienvenue, il est vident que le nom de
lutilisateur sera un paramtre de la mthode.
Les paramtres scrivent lintrieur des parenthses qui suivent le nom de la mthode.
Nous devons indiquer le type du paramtre ainsi que le nom de la variable qui le
reprsentera au sein de la mthode.
Il est possible de passer plusieurs paramtres une mthode, on les sparera avec une
virgule. Par exemple :
static void DireBonjour(string prenom, int age)
{
Console.WriteLine("Bonjour " + prenom);
Console.WriteLine("Vous avez " + age + " ans");
}

Ici, la mthode DireBonjour prend en paramtres une chaine de caractres prenom et un


entier age.
La mthode affiche Bonjour ainsi que le contenu de la variable prenom. De mme, juste
en dessous, elle affiche lge qui a t pass en paramtres.
Nous pourrons appeler cette mthode de cette faon, depuis la mthode Main() :
static void Main(string[] args)
{
DireBonjour("Nicolas", 30);
DireBonjour("Jrmie", 20);
}

Et nous aurons :

Bien sr, il est obligatoire de fournir en paramtres dune mthode une variable du mme
type que le paramtre. Sinon, le compilateur sera incapable de mettre la donne qui a t

passe dans le paramtre. Dailleurs, si vous ne fournissez pas le bon paramtre, vous
aurez droit une erreur de compilation.
Par exemple, si vous appelez la mthode avec les paramtres suivants :
DireBonjour(10, 10);

Vous aurez lerreur de compilation suivante :


Citation : Compilateur
impossible de convertir de int en string
Il est videmment possible de passer des variables une mthode, cela fonctionne de la
mme faon :
string prenom = "Nicolas";
DireBonjour(prenom, 30);

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

Retour dune mthode


Une mthode peut aussi renvoyer une valeur, par exemple un calcul. Cest souvent
dailleurs son utilit premire.
On pourrait imaginer par exemple une mthode qui calcule la longueur de lhypotnuse
partir des 2 cts dun triangle. Sachant que a + b = c, nous pouvons imaginer une
mthode qui prend en paramtres la longueur des 2 cots, fait la somme de leur carrs et
renvoie la racine carr du rsultat. Cest ce que fait la mthode suivante :
static double LongueurHypotenuse(double a, double b)
{
double sommeDesCarres = a * a + b * b;
double resultat = Math.Sqrt(sommeDesCarres);
return resultat;
}

Continuons ignorer le mot cl static. Vous aurez remarqu que la signature de la

mthode commence par le mot cl double, qui indique que la mthode va nous renvoyer
une valeur du type double. Comme on la vu, double a et double b sont deux paramtres
de la mthode et sont du type double.
La mthode Math.Sqrt est une mthode du framework .NET, au mme titre que la
mthode Console.WriteLine, qui permet de renvoyer la racine carre dun nombre. Elle
prend en paramtre un double et nous retourne une valeur de type double galement qui
correspond la racine carre du paramtre. Cest tout naturellement que nous stockons ce
rsultat dans une variable grce loprateur daffectation = .
la fin de la mthode, le mot cl return indique que la mthode renvoie la valeur la
mthode qui la appele. Ici, nous renvoyons le rsultat.
Cette mthode pourra sutiliser ainsi :
static void Main(string[] args)
{
double valeur = LongueurHypotenuse(1, 3);
Console.WriteLine(valeur);
valeur = LongueurHypotenuse(10, 10);
Console.WriteLine(valeur);
}

Comme prcdemment, nous utilisons une variable pour stocker le rsultat de lexcution
de la mthode.
Ce qui produira comme rsultat :

noter quil est galement possible de se passer dune variable intermdiaire pour stocker
le rsultat. Ainsi, nous pourrons par exemple faire :
Console.WriteLine("Le rsultat est : " + LongueurHypotenuse(1, 3));

Avec cette criture le rsultat renvoy par la mthode LongueurHypotenuse est directement
concatn la chaine Le rsultat est : et est pass en paramtre la mthode
Console.WriteLine.
Remarquez quon a fait lopration a*a pour mettre a au carr. On aurait galement pu
faire Math.Pow(a, 2) qui permet de faire la mme chose, la diffrence est que Pow permet
de mettre la puissance que lon souhaite. Ainsi, Math.Pow(a, 3) permet de mettre a
au cube.

Il faut savoir que le mot cl return peut apparaitre nimporte quel endroit de la mthode.
Il interrompt alors lexcution de celle-ci et renvoie la valeur passe. Ce mot-cl est
obligatoire, sans cela la mthode ne compilera pas.
Il est galement primordial que tous les chemins possibles dune mthode renvoient
quelque chose. Les chemins sont dtermins par les instructions conditionnelles que nous
avons vues prcdemment.
Ainsi, lexemple suivant est correct :
static string Conjugaison(string genre)
{
if (genre == "homme")
return "";
else
return "e";
}

car peu importe la valeur de la variable genre , la mthode renverra une chaine.
Alors que celui-ci :
static string Conjugaison(string genre)
{
if (genre == "homme")
return "";
else
{
if (genre == "femme")
return "e";
}
}

est incorrect. En effet, que renvoie la mthode si la variable genre contient autre chose
que homme ou femme ?
En gnral, Visual C# express nous indiquera quil dtecte un problme avec une erreur de
compilation.
Nous pourrons corriger ceci avec par exemple :
static string Conjugaison(string genre)
{
if (genre == "homme")
return "";
else
{
if (genre == "femme")
return "e";
}
return "";
}

noter que correspond une chaine vide et peut galement scrire :


string.Empty.
Nous avons vu dans le chapitre prcdent quil tait possible de crer des mthodes qui ne
retournent rien. Dans ce cas, on peut utiliser le mot cl return sans valeur qui le suit pour
stopper lexcution de la mthode. Par exemple :
static void Bonjour(string prenom)
{
if (prenom == "inconnu")
return;
Console.WriteLine("Bonjour " + prenom);
}

Ainsi, si la variable prenom vaut inconnu , alors nous quittons la mthode Bonjour

et linstruction Console.WriteLine ne sera pas excute.


En rsum

Une mthode regroupe un ensemble dinstructions pouvant prendre des paramtres et


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

Chapitre 8

Tableaux, listes et numrations

Dans les chapitres prcdents, nous avons pu utiliser les types de base du framework
.NET, comme int, string, double, etc. Nous allons dcouvrir ici dautres types qui vont
savrer trs utiles dans la construction de nos applications informatiques.
Une fois bien matriss, vous ne pourrez plus vous en passer !

Les tableaux
Voici le premier nouveau type que nous allons tudier, le type tableau . En dclarant
une variable de type tableau, nous allons en fait utiliser une variable qui contient une suite
de variables du mme type. Prenons cet exemple :
string[] jours = new string[] { "Lundi", "Mardi", "Mercredi", "Jeudi", "Vendredi", "Samedi",
"Dimanche" };

Nous dfinissons ici un tableau de chaine de caractres qui contient 7 chaines de


caractres, savoir les jours de la semaine. Ne faites pas trop attention loprateur new
pour linstant, nous y reviendrons plus tard ; il permet simplement de crer le tableau.
Les crochets [] qui suivent le nom du type permettent de signaler au compilateur que
nous souhaitons utiliser un tableau de ce type-l, ici le type string.
Un tableau, cest un peu comme une armoire dans laquelle on range des variables. Chaque
variable est pose sur une tagre. Pour accder la variable qui est pose sur une tagre,
on utilise le nom de larmoire et on indique lindice de ltagre o est stocke la variable,
en utilisant des crochets [] :
Console.WriteLine(jours[3]); // affiche Jeudi
Console.WriteLine(jours[0]); // affiche Lundi
Console.WriteLine(jours[10]); // provoque une erreur d'excution car l'indice n'existe pas

Attention, le premier lment du tableau se situe lindice 0 et le dernier se


situe lindice taille du tableau 1 , cest--dire 6 dans notre exemple. Si on
tente daccder un indice qui nexiste pas, lapplication lvera une erreur.
Sans anticiper sur le chapitre sur les boucles, il est possible de parcourir lensemble dun
tableau avec linstruction suivante :
string[] jours = new string[] { "Lundi", "Mardi", "Mercredi", "Jeudi", "Vendredi", "Samedi",
"Dimanche" };
for (int i = 0; i < jours.Length; i++)
{

Console.WriteLine(jours[i]);
}

Nous y reviendrons plus tard mais pour comprendre, ici nous parcourons les lments de 0
taille-1 et nous affichons llment du tableau correspondant lindice en cours.
Ce qui nous donne :

Revenons prsent sur la dclaration du tableau :


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

Cette criture permet de crer un tableau qui contient 7 lments et daffecter une valeur
chaque lment du tableau. Il sagit en fait ici dune criture simplifie. Cette criture est
quivalente celle-ci :
string[] jours = new string[7];
jours[0] = "Lundi";
jours[1] = "Mardi";
jours[2] = "Mercredi";
jours[3] = "Jeudi";
jours[4] = "Vendredi";
jours[5] = "Samedi";
jours[6] = "Dimanche";

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

Avec cette instruction, le tableau sera class par ordre alphabtique. Vous aurez loccasion
de voir dautres mthodes dans des chapitres ultrieurs.
Ainsi, le code suivant :
string[] jours = new string[] { "Lundi", "Mardi", "Mercredi", "Jeudi", "Vendredi", "Samedi",
"Dimanche" };
Array.Sort(jours);

for (int i = 0; i < jours.Length; i++)


{
Console.WriteLine(jours[i]);
}

produira :

Ce qui est trs inutile .


Le tableau jours est ce que lon appelle un tableau une dimension. Il est galement
possible de crer des tableaux N dimensions, il est cependant assez rare de dpasser 2
dimensions. Cela est utile lorsque lon manipule des matrices.
Nous ntudierons pas les tableaux plus dune dimension dans ce tutoriel car ils risquent
de vraiment peu vous servir dans vos premires applications. Par contre, le type suivant
vous servira abondamment.

Les listes
Un autre type que nous allons utiliser foison est la liste. Nous allons voir comment ce
type fonctionne mais sans en faire une tude exhaustive car elle pourrait tre bien longue
et ennuyeuse. Regardons cet exemple :
List<int> chiffres = new List<int>(); // cration de la liste
chiffres.Add(8); // chiffres contient 8
chiffres.Add(9); // chiffres contient 8, 9
chiffres.Add(4); // chiffres contient 8, 9, 4
chiffres.RemoveAt(1); // chiffres contient 8, 4
foreach (int chiffre in chiffres)
{
Console.WriteLine(chiffre);
}

La premire ligne permet de crer la liste. Nous reviendrons sur cette instruction un peu
plus bas dans le chapitre. Il sagit dune liste dentiers.
Nous ajoutons des entiers la liste grce la mthode Add(). Nous ajoutons en
loccurrence les entiers 8, 9 et 4.

La mthode RemoveAt() permet de supprimer un lment en utilisant son indice, ici nous
supprimons le deuxime entier, cest--dire 9.
Comme les tableaux, le premier lment de la liste commence lindice 0.

Aprs cette instruction, la liste contient les entiers 8 et 4.


Enfin, nous parcourons les lments de la liste grce linstruction foreach. Nous y
reviendrons en dtail lors du chapitre sur les boucles. Pour linstant, nous avons juste
besoin de comprendre que nous affichons tous les lments de la liste.
Ce qui donne :

Les lecteurs assidus auront remarqus que la construction de la liste est un peu
particulire. Passons sur le mot cl new qui permet de crer la liste, nous y reviendrons
plus en dtail dans un prochain chapitre. Par contre, on observe lutilisation de chevrons
<> pour indiquer le type de la liste. Pour avoir une liste dentier, il suffit dindiquer le type
int lintrieur des chevrons. Ainsi, il ne sera pas possible dajouter autre chose quun
entier dans cette liste. Par exemple, linstruction suivante provoque une erreur de
compilation :
List<int> chiffres = new List<int>(); // cration de la liste
chiffres.Add("chaine"); // ne compile pas

Evidemment, on ne peut pas ajouter des choux une liste de carottes !


De la mme faon, si lon souhaite crer une liste de chaine de caractres, nous pourrons
utiliser le type string lintrieur des chevrons :
List<string> chaines = new List<string>(); // cration de la liste
chaines.Add("chaine"); // compilation OK
chaines.Add(1); // compilation KO, on ne peut pas ajouter un entier dans une liste de chaines de
caractres

Les listes possdent des mthodes bien pratiques qui permettent toutes sortes doprations
sur la liste. Par exemple, la mthode IndexOf() permet de rechercher un lment dans la
liste et de renvoyer son indice.

List<string> jours = new List<string>();


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

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

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

un peu comme pour les tableaux.

Liste ou tableau ?
Vous aurez remarqu que les deux types, tableau et liste, se ressemblent beaucoup.
Essayons de voir ce qui les diffrencie afin de savoir quel type choisir entre les deux.
En fait, une des grosses diffrences est que le tableau peut tre multidimensionnel. Cest
un point important mais dans le cadre de vos premires applications, il est relativement
rare davoir sen servir. Ils serviront globalement plus souvent dans le dveloppement de
jeux ou lorsque lon souhaite faire des calculs 3D.
Par contre, le plus important pour nous est que le type tableau est de taille fixe alors que la
liste est de taille variable. On peut ajouter sans problmes un nouvel lment grce la
mthode Add(). De mme, on peut supprimer des lments avec les mthodes Remove alors
quavec un tableau, on peut seulement remplacer les valeurs existantes et il nest pas
possible daugmenter sa capacit.
Globalement, vous verrez que dans vos applications, vous utiliserez plutt les listes pour

par exemple afficher une liste de produits, une liste de clients, etc
Gardez quand mme dans un coin de lesprit les tableaux, ils pourront vous aider dans des
situations prcises.

Les numrations
Un type particulier que nous allons galement utiliser est lnumration. Cela correspond
comme son nom lindique une numration de valeur.
Par exemple, il pourrait tre trs facile de reprsenter les jours de la semaine dans une
numration plutt que dans un tableau.
On dfinit lnumration de cette faon, grce au mot cl enum :
enum Jours
{
Lundi,
Mardi,
Mercredi,
Jeudi,
Vendredi,
Samedi,
Dimanche
}

noter quon ne peut pas dfinir cette numration nimporte o, pour linstant,
contentons-nous de la dfinir en dehors de la mthode Main().
Pour tre tout fait prcis, une numration est un type dont toutes les valeurs dfinies
sont des entiers. La premire vaut 0, et chaque valeur suivante prend la valeur prcdente
augmente de 1. Cest--dire que Lundi vaut 0, Mardi vaut 1, etc
Il est possible de forcer des valeurs toutes ou certaines valeurs de lnumration, les
valeurs non forces prendront la valeur prcdente augmente de 1 :
enum Jours
{
Lundi = 5, // lundi vaut 5
Mardi, // mardi vaut 6
Mercredi = 9, // mercredi vaut 9
Jeudi = 10, // jeudi vaut 10
Vendredi, // vendredi vaut 11
Samedi, // samedi vaut 12
Dimanche = 20 // dimanche vaut 20
}

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

La seule diffrence cest que les valeurs quil est possible daffecter notre variable sont
figes et font partie des valeurs dfinies dans lnumration.

Pour pouvoir accder un lment de lnumration, il faudra utiliser le nom de


lnumration suivi de loprateur point . suivi encore de la valeur de lnumration
choisie. Par exemple :
Jours lundi = Jours.Lundi;
Console.WriteLine(lundi);

soit dans notre programme :


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

Ce qui nous donne :

Nous pourrons galement nous servir des numrations pour faire des tests de
comparaisons, comme par exemple :
if (jourDeLaSemaine == Jours.Dimanche || jourDeLaSemaine == Jours.Samedi)
{
Console.WriteLine("Bon week-end");
}

Sachez que le framework .NET utilise beaucoup les numrations. Il est important de
savoir les manipuler.
Vous aurez peut-tre remarqu que lorsquon affiche la valeur dune
numration, la console nous affiche le nom de lnumration et non pas sa

valeur entire. a peut paratre trange, mais cest parce que le C# fait un
traitement particulier dans le cadre dune numration laffichage.

En rsum

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

Chapitre 9

Utiliser le framework .NET

Comme on la dj voqu, le framework .NET est une norme bote outils qui contient
beaucoup de mthodes permettant de construire toutes sortes dapplications.
Nous allons avoir besoin rgulirement dutiliser les lments du framework .NET pour
raliser nos applications. Il est donc grand temps dapprendre savoir le manipuler !
Rentrons tout de suite dans le vif du sujet.

Linstruction using
Nous allons sans arrt solliciter la puissance du framework .NET. Par exemple, nous
pouvons lui demander de nous donner la date courante.
Pour ce faire, on utilisera linstruction :
Console.WriteLine(DateTime.Now);

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

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


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

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

qui a t gnre par Visual C# express au dbut du fichier.


Elle indique au compilateur que nous allons utiliser (using) le namespace System dans
notre page. Ainsi, il nest plus obligatoire de prfixer laccs DateTime par System. .
Cest une espce de raccourci.
Pour conserver lanalogie avec les rpertoires et les fichiers, on peut dire que nous avons
ajout le rpertoire System dans le path.
Pour rsumer, linstruction :
System.Console.WriteLine(System.DateTime.Now);

est quivalente aux deux instructions :


using System;

et
Console.WriteLine(DateTime.Now);

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

Un clic droit sur le mot permettra douvrir un menu droulant, de choisir Rsoudre et
dimporter le using correspondant automatiquement.

La bibliothque de classes .NET


Vous aurez loccasion de dcouvrir que le framework .NET fourmille despace de noms
contenant plein de mthodes de toutes sortes permettant de faire un peu tout et nimporte
quoi. Une vraie caverne dali-baba.
Parmi les nombreuses fonctionnalits du framework .NET, nous avons une mthode qui
nous permet de rcuprer lutilisateur courant (au sens utilisateur windows ), on pourra
par exemple utiliser linstruction suivante :
System.Console.WriteLine(System.Environment.UserName);

Qui nous affichera :

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


using System;

et
Console.WriteLine(Environment.UserName);

Pour produire le mme rsultat.


Petit petit vous allez retenir beaucoup dinstructions et despaces de nom du framework
.NET mais il est inimaginable de tout retenir. Il existe une documentation rpertoriant tout
ce qui compose le framework .NET, cest ce quon appelle la MSDN library. Elle est
accessible cette adresse : http://msdn.microsoft.com/fr-fr/library/.
Par exemple, la documentation de la proprit que nous venons dutiliser est consultable
cette adresse.
Nous avons dsign la caverne dali-baba par le mot framework .NET . Pour tre plus
prcis, la dsignation exacte de cette caverne est : la bibliothque de classe .NET . Le
mot framework .NET est en gnral utilis par abus de langage (et vous avez remarqu
que jai moi-mme abus du langage !) et reprsente galement cette bibliothque de
classe. Nous reviendrons plus tard sur ce quest exactement une classe, pour linstant,
vous pouvez voir a comme des composants ; une bibliothque de composants.

Rfrencer une assembly


Ca y est, nous savons accder au framework .NET.
Mais dailleurs, comment se fait-il que nous puissions accder aux mthodes du
framework .NET sans nous poser de question ? Il est magique ce framework ?
O se trouve le code qui permet de rcuprer la date du jour ?
Judicieuse question. Si on y rflchit, il doit falloir beaucoup de code pour renvoyer la
date du jour ! Dj, il faut aller la lire dans lhorloge systme, il faut peut-tre ladapter au
fuseau horaire, la formater dune faon particulire en fonction de la langue, etc. Tout a
est dj fait, heureusement, dans la bibliothque de mthodes du framework .NET.
Alors, o est-elle cette bibliothque ? Et le reste ?
Dans des assemblys bien sr. Comme on a vu, les assemblys possdent des fragments de
code compils en langage intermdiaire. Sils sont rutilisables, ils se trouvent dans des
fichiers dont lextension est .dll.
Le framework .NET est compos dune multitude dassemblys qui sont installs sur votre
systme dexploitation, dans le GAC (global assembly cache) qui est un endroit o sont
stockes ces assemblys afin de les rendre accessible depuis nos programmes.
Pour pouvoir utiliser ces assemblys dans notre programme, nous devons indiquer que nous
voulons les utiliser. Pour a, il faut les rfrencer dans le projet.
Si lon dplie les rfrences dans notre explorateur de solutions, nous pouvons voir que
nous avons dj pas mal de rfrences qui ont t ajoutes par Visual C# express :

Ce sont des assemblys qui sont trs souvent utilises, cest pour a que Visual C# express
nous les a automatiquement rfrences. Toujours ce souci de nous simplifier le travail,
quil est sympa !
Prenons par exemple la rfrence System.Xml. Son nom nous suggre que dedans est runi
tout ce quil nous faut pour manipuler le XML.
Commenons taper System.Xml., la compltion automatique nous propose plusieurs
choses.

On ne sait pas du tout quoi elle sert, mais dclarons par exemple une variable de
lnumration ConformanceLevel :
System.Xml.ConformanceLevel level;

et compilons. Pas derreur de compilation.


Si vous supprimez la rfrence System.Xml. (bouton droit, Supprimer),

et que vous compilez nouveau, vous pouvez voir que ConformanceLevel est dsormais
soulign en rouge, signe quil y a un problme.

Par ailleurs, la compilation provoque lerreur suivante :


Citation : Compilateur
Le type ou le nom despace de noms ConformanceLevel nexiste pas dans lespace de
noms System.Xml (une rfrence dassembly est-elle manquante ?)
Loin dtre bte, Visual C# express nous affiche un message derreur plutt explicite.
En effet, cette numration est introuvable car elle est dfinie dans une assembly qui nest
pas rfrence. Cest comme si on nous demandait de prendre le marteau pour enfoncer un
clou, mais que le marteau nest pas cot de nous, mais au garage bien rang !
Il faut donc rfrencer le marteau afin de pouvoir lutiliser.
Pour ce faire, faisons un clic sur Rfrences et ajoutons une rfrence.

Ici, nous avons plusieurs onglets (selon la version de Visual Studio que vous possdez,
vous aurez peut-tre une prsentation lgrement diffrente).

Chaque onglet permet dajouter une rfrence une assemblys.


Longlet .NET permet de rfrencer une assembly prsente dans le GAC, cest ici que
nous viendrons chercher les assemblys du framework .NET
Longlet COM permet de rfrencer une dll COM. Vous ne savez pas ce quest COM ?
On peut dire que cest lanctre de lassembly. Techniquement, on ne rfrence pas
directement une dll COM, mais Visual C# express gnre ce quon appelle un
wrapper permettant dutiliser la dll COM. Un wrapper est en fait une espce de
traducteur permettant dutiliser la dll COM comme si ctait une assembly. Mais ne
nous attardons pas sur ce point qui ne va pas nous servir, nous qui sommes dbutants.
Longlet Projets permet de rfrencer des assemblys qui se trouvent dans notre
solution. Dans la mesure o notre solution ne contient quun seul projet, longlet sera
vide. Nous verrons plus tard comment utiliser cet onglet lorsque nous ajouterons des
assemblys nos solutions.
Longlet Parcourir va permettre de rfrencer une assembly depuis un emplacement
sur le disque dur. Cela peut-tre une assembly tierce fournie par un collgue, ou par
un revendeur, etc
Enfin, longlet Rcent, comme son nom le suggre, permet de rfrencer des
assemblys rcemment rfrences.
Retournons nos moutons, repartons sur longlet .NET et recherchons lassembly que nous
avons supprime, savoir System.Xml.

Une fois trouve, appuyez sur OK. Maintenant que la rfrence est ajoute, nous pouvons
nouveau utiliser cette numration
Ouf !
Je ne comprends pas, jai supprim toutes les rfrences, mais jarrive quand
mme utiliser la date, le nom de lutilisateur ou la mthode
Console.WriteLine. Si jai bien compris, je ne devrais pas pouvoir utiliser des
mthodes dont les assemblys ne sont pas rfrences Cest normal ?
Eh oui, il existe une assembly qui napparait pas dans les rfrences et qui contient tout le
cur du framework .NET. Cette assembly doit obligatoirement tre rfrence, il sagit de
mscorlib.dll. Comme elle est obligatoire, elle est rfrence par dfaut ds que lon cre
un projet.

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

Cela veut donc dire quon utilise la date du jour en utilisant lassembly obligatoire
mscorlib et la fonctionnalit se trouve dans lespace de nom System , comme dj vu.
Il ny a donc rien faire pour pouvoir utiliser DateTime.Now.
En imaginant que nous ayons besoin de faire un programme qui utilise des nombres
complexes, vous allez surement avoir besoin du type Complex, trouv dans la
documentation.
Pour lutiliser, comme indiqu, il va falloir rfrencer lassembly System.Numerics.dll.
Rien de plus simple, rptons la procdure pour rfrencer une assembly et allons trouver
System.Numerics.dll :

Ensuite, nous pourrons utiliser par exemple le code suivant :


Complex c = Complex.One;
Console.WriteLine(c);
Console.WriteLine("Partie relle : " + c.Real);
Console.WriteLine("Partie imaginaire : " + c.Imaginary);
Console.WriteLine(Complex.Conjugate(Complex.FromPolarCoordinates(1.0, 45 * Math.PI / 180)));

Sachant quil aura bien sr fallu rajouter le :


using System.Numerics;

permettant davoir viter de prfixer Complex par System.Numerics. Mais a, vous aviez
trouv tout seul, nest-ce pas ?
Ce qui nous donnera :

En rsum

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

Chapitre 10

TP : Bonjour cest le week-end

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

Instructions pour raliser le TP


Le but est de crer une petite application qui affiche un message diffrent en fonction du
nom de lutilisateur et du moment de la journe :
Bonjour XXX pour la tranche horaire 9h <-> 18h, les lundi, mardi, mercredi, jeudi et
vendredi
Bonsoir XXX pour la tranche horaire 18h <-> 9h, les lundi, mardi, mercredi, jeudi
Bon week-end XXX pour la tranche horaire vendredi 18h <-> lundi 9h
Peut-tre cela vous parait simple, dans ce cas, foncez et ralisez cette premire application
tout seul .
Sinon, dcortiquons un peu lnonc de ce TP pour viter dtre perdu.
Pour raliser ce premier TP, vous allez avoir besoin de plusieurs choses.
Dans un premier temps, il faut afficher le nom de lutilisateur, cest une chose que nous
avons dj faite en allant puiser dans les fonctionnalits du framework .NET.
Vous aurez besoin galement de rcuprer lheure courante pour la comparer aux tranches
horaires souhaites. Vous avez dj vu comment rcuprer la date courante. Pour pouvoir
rcuprer lheure de la date courante, il vous faudra utiliser linstruction
DateTime.Now.Hour qui renvoie un entier reprsentant lheure du jour.
Pour comparer lheure avec des valeurs entires il vous faudra utiliser les oprateurs de
comparaisons et les instructions conditionnelles que nous avons vus prcdemment.
Pour traiter le cas spcial du jour de la semaine, vous aurez besoin que le framework .NET
vous indique quel jour nous sommes. Cest le rle de linstruction
DateTime.Now.DayOfWeek qui est une numration indiquant le jour de la semaine. Les
diffrentes valeurs sont consultables cette adresse.
Pour plus de clart, nous les reprenons ici :
Valeur

Traduction

Sunday

Dimanche

Monday

Lundi

Tuesday

Mardi

Wednesday Mercredi
Thursday

Jeudi

Friday

Vendredi

Saturday

Samedi

Il ne restera plus qu comparer deux valeurs dnumration, comme on la vu au chapitre


sur les numrations.
Voil, vous avez tous les outils en main pour raliser ce premier TP. Nhsitez pas
revenir sur les chapitres prcdents si vous avez un doute sur la syntaxe ou sur les
instructions raliser. On ne peut pas apprendre un langage par cur du premier coup.
vous de jouer !

Correction
Vous tes autoriss lire cette correction uniquement si vous vous tes arrach les
cheveux sur ce TP ! Je vois quil vous en reste, encore un effort !
Quoique, si vous avez russi avec brio le TP, vous pourrez galement comparer votre
travail au mien.
Quoi quil en soit, voici la correction que je propose. Bien videmment, il peut y en avoir
plusieurs, mais elle contient les informations ncessaires pour la ralisation de ce TP.
Premire chose faire : crer un projet de type console. Jai ensuite ajout le code suivant
:
static void Main(string[] args)
{
if (DateTime.Now.DayOfWeek == DayOfWeek.Saturday || DateTime.Now.DayOfWeek == DayOfWeek.Sunday)
{
// nous sommes le week-end
AfficherBonWeekEnd();
}
else
{
// nous sommes en semaine
if (DateTime.Now.DayOfWeek == DayOfWeek.Monday && DateTime.Now.Hour < 9)
{
// nous sommes le lundi matin
AfficherBonWeekEnd();
}
else
{
if (DateTime.Now.Hour >= 9 && DateTime.Now.Hour < 18)
{
// nous sommes dans la journe
AfficherBonjour();
}
else
{
// nous sommes en soire
if (DateTime.Now.DayOfWeek == DayOfWeek.Friday && DateTime.Now.Hour >= 18)
{
// nous sommes le vendredi soir

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

Dtaillons un peu ce code :


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

Aller plus loin


Ce TP ntait pas trs compliqu, il nous a permis de vrifier que nous avions bien
compris le principe des if et que nous sachions appeler des lments du framework .NET.
Nous aurions pu simplifier lcriture de lapplication en compliquant en peu les tests avec
une combinaison de conditions.
Par exemple, on pourrait avoir :
if (DateTime.Now.DayOfWeek == DayOfWeek.Saturday ||
DateTime.Now.DayOfWeek == DayOfWeek.Sunday ||
(DateTime.Now.DayOfWeek == DayOfWeek.Monday && DateTime.Now.Hour < 9) ||
(DateTime.Now.DayOfWeek == DayOfWeek.Friday && DateTime.Now.Hour >= 18))
{
// nous sommes le week-end
AfficherBonWeekEnd();
}
else
{
// nous sommes en semaine

if (DateTime.Now.Hour >= 9 && DateTime.Now.Hour < 18)


{
// nous sommes dans la journe
AfficherBonjour();
}
else
{
AfficherBonsoir();
}
}

Le premier test permet de vrifier que nous sommes soit samedi, soit dimanche, soit que
nous sommes lundi et que lheure est infrieure 9, soit que nous sommes vendredi et que
lheure est suprieure 18.
Nous avons, pour ce faire, combin les tests avec loprateur logique OU : ||.
Remarquons que les parenthses nous permettent dagir sur lordre dvaluation des
conditions. Pour que ce soit le week-end, il faut bien sr tre vendredi et que lheure soit
suprieure 18 ou lundi et que lheure soit infrieure 9 ou samedi ou dimanche.
On pourrait encore simplifier en vitant de solliciter chaque fois le framework .NET
pour obtenir la date courante. Il suffit de stocker la date courante dans une variable de type
DateTime. Ce qui donnerait :
DateTime dateCourante = DateTime.Now;
if (dateCourante.DayOfWeek == DayOfWeek.Saturday ||
dateCourante.DayOfWeek == DayOfWeek.Sunday ||
(dateCourante.DayOfWeek == DayOfWeek.Monday && dateCourante.Hour < 9) ||
(dateCourante.DayOfWeek == DayOfWeek.Friday && dateCourante.Hour >= 18))
{
// nous sommes le week-end
AfficherBonWeekEnd();
}
else
{
// nous sommes en semaine
if (dateCourante.Hour >= 9 && dateCourante.Hour < 18)
{
// nous sommes dans la journe
AfficherBonjour();
}
else
{
AfficherBonsoir();
}
}

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

Chapitre 11

Les boucles

Nous les avons voques rapidement un peu plus tt en parlant des tableaux et des listes.
Dans ce chapitre nous allons dcrire plus prcisment les boucles.
Elles sont souvent utilises pour parcourir des lments numrables, comme le sont les
tableaux ou les listes. Elles peuvent galement tre utilises pour effectuer la mme action
tant quune condition nest pas ralise.

La boucle For
La premire instruction que nous avons aperue est la boucle for. Elle permet de rpter
un bout de code tant quune condition est vraie. Souvent cette condition est un compteur.
Nous pouvons par exemple afficher un message 50 fois avec le code suivant :
int compteur;
for (compteur = 0; compteur < 50; compteur++)
{
Console.WriteLine("Bonjour C#");
}

Ce qui donne :

Nous dfinissons ici un entier compteur . Il est initialis 0 en dbut de boucle

(compteur = 0). Aprs chaque excution du bloc de code du for, cest--dire chaque
itration, il va afficher Bonjour C# .
la fin de chaque itration, la variable compteur est incrmente (compteur++) et nous
recommenons lopration tant que la condition compteur < 50 est vraie.
Bien sr, la variable compteur est accessible dans la boucle et change de valeur chaque
itration.
int compteur;
for (compteur = 0; compteur < 50; compteur++)
{
Console.WriteLine("Bonjour C# " + compteur);
}

Ce qui donne :

Ce prcdent code affichera la valeur de la variable aprs chaque bonjour, donc de 0 49.
En effet, quand compteur passe 50, la condition nest plus vraie et on passe aux
instructions suivantes.
En gnral, on utilise la boucle for pour parcourir un tableau. Ainsi, nous pourrons utiliser
le compteur comme indice pour accder aux lments du tableau :
string[] jours = new string[] { "Lundi", "Mardi", "Mercredi", "Jeudi", "Vendredi", "Samedi",
"Dimanche" };
int indice;
for (indice = 0; indice < 7; indice++)
{
Console.WriteLine(jours[indice]);
}

Dans cette boucle, comme la premire itration indice vaut 0, nous afficherons llment
du tableau la position 0, savoir Lundi .
litration suivante, indice passe 1, nous affichons llment du tableau la position 1,
savoir Mardi , et ainsi de suite.
Ce qui donne :

Attention ce que lindice ne dpasse pas la taille du tableau, sinon laccs un indice en
dehors des limites du tableau provoquera une erreur lexcution. Pour viter ceci, on
utilise en gnral la taille du tableau comme condition de fin :
string[] jours = new string[] { "Lundi", "Mardi", "Mercredi", "Jeudi", "Vendredi", "Samedi",
"Dimanche" };
int indice;
for (indice = 0; indice < jours.Length; indice++)
{
Console.WriteLine(jours[indice]);
}

Ici jours.Length renvoie la taille du tableau, savoir 7.


Il est trs courant de boucler sur un tableau en passant tous les lments un par un, mais il
est possible de changer les conditions de dpart, les conditions de fin, et llment qui
influe sur la condition de fin.
Ainsi, lexemple suivant :
int[] chiffres = new int[] { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 };
for (int i = 9; i > 0; i -= 2)
{
Console.WriteLine(chiffres[i]);
}

Permet de parcourir le tableau de 2 en 2 en commenant par la fin.


Ce qui nous donne :

Vous avez pu voir que dans cet exemple, nous avons dfini lentier i directement dans
linstruction for, cest une commodit qui nous permet davoir la variable i qui nexiste
qu lintrieur de la boucle, car sa porte correspond aux accolades qui dlimitent le for.
Attention, il est possible de faire un peu tout et nimporte quoi dans ces boucles. Aussi il
peut arriver que lon se retrouve avec des bugs, comme des boucles infinies.
Par exemple, le code suivant :
for (int indice = 0; indice < 7; indice--)
{
Console.WriteLine("Test" + indice);
}

est une boucle infinie. En effet, on modifie la variable indice en la dcrmentant. Sauf que
la condition de sortie de la boucle est valable pour un indice qui dpasse ou gale la valeur
7, ce qui narrivera jamais.
Si on excute lapplication avec ce code, la console va afficher linfini le mot Test
avec son indice. La seule solution pour quitter le programme sera de fermer brutalement
lapplication.
Lautre solution est dattendre que le programme se termine
Mais tu viens de dire que la boucle tait infinie ?
Oui cest vrai, mais en fait, ici on se heurte un cas limite du C#. Cest cause de la
variable indice. indice est un entier que lon dcrmente. Au dbut il vaut zro, puis -1,
puis -2, etc
Lorsque la variable indice arrive la limite infrieure que le type int est capable de grer,
cest--dire -2147483648 alors il y a ce quon appelle un dpassement de capacit. Sans
rentrer dans les dtails, il ne peut pas stocker un entier plus petit et donc il boucle et repart
lentier le plus grand, cest--dire 2147483647.
Donc pour rsumer, lindice fait :
0
-1

-2

-2147483647
-2147483648
+2147483647
Et comme l, il se retrouve suprieur 7, la boucle se termine.

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

La boucle Foreach
Comme il est trs courant dutiliser les boucles pour parcourir un tableau ou une liste, le
C# dispose dun oprateur particulier : foreach que lon peut traduire par pour chaque
: pour chaque lment du tableau faire
string[] jours = new string[] { "Lundi", "Mardi", "Mercredi", "Jeudi", "Vendredi", "Samedi",
"Dimanche" };
foreach (string jour in jours)
{
Console.WriteLine(jour);
}

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

Ce qui produira :

Linstruction foreach fonctionne aussi avec les listes, par exemple le code suivant :
List<string> jours = new List<string> { "Lundi", "Mardi", "Mercredi", "Jeudi", "Vendredi", "Samedi",
"Dimanche" };
foreach (string jour in jours)
{
Console.WriteLine(jour);
}

nous permettra dafficher tous les jours de la semaine contenus dans la liste des jours.
Attention, la boucle foreach est une boucle en lecture seule. Cela veut dire quil nest pas
possible de modifier llment de litration en cours.
Par exemple, le code suivant :
List<string> jours = new List<string> { "Lundi", "Mardi", "Mercredi", "Jeudi", "Vendredi", "Samedi",
"Dimanche" };
foreach (string jour in jours)
{
jour = "pas de jour !";
}

provoquera lerreur de compilation suivante :


Citation : Compilateur
Impossible dassigner jour, car il sagit dun variable ditration foreach
Notez dailleurs que lquipe de traduction de Visual C# express a quelques progrs faire

Si nous souhaitons utiliser une boucle pour changer la valeur de notre liste ou de notre
tableau, il faudra passer par une boucle for :
List<string> jours = new List<string> { "Lundi", "Mardi", "Mercredi", "Jeudi", "Vendredi", "Samedi",
"Dimanche" };
for (int i = 0; i < jours.Count; i++ )
{
jours[i] = "pas de jour !";
}

Il vous arrivera aussi surement un jour (a arrive tous les dveloppeurs) de vouloir

modifier une liste en elle mme lors dune boucle foreach. Cest--dire lui rajouter un
lment, le supprimer, etc
Cest galement impossible car partir du moment o lon rentre dans la boucle foreach,
la liste devient non-modifiable.
Prenons lexemple combien classique de la recherche dune valeur dans une liste pour la
supprimer. Nous serions tent de faire :
List<string> jours = new List<string> { "Lundi", "Mardi", "Mercredi", "Jeudi", "Vendredi", "Samedi",
"Dimanche" };
foreach (string jour in jours)
{
if (jour == "Mercredi")
jours.Remove(jour);
}

ce qui semblerait tout fait correct et en plus, ne provoque pas derreur de compilation.
Sauf que si vous excutez lapplication, vous aurez lerreur suivante :

qui nous plante notre application. Le programme nous indique que la collection a t
modifie et que lopration dnumration peut ne pas sexcuter .
Il est donc impossible de faire notre suppression ainsi.
Comment tu ferais toi ?
Et bien, plusieurs solutions existent. Celle qui vient en premier lesprit est dutiliser une
boucle for par exemple :
for (int i = 0 ; i < jours.Count ; i++)
{
if (jours[i]== "Mercredi")
jours.Remove(jours[i]);
}

Cette solution est intressante ici, mais elle peut poser un problme dans dautres
situations. En effet, vu que nous supprimons un lment de la liste, nous allons nous
retrouver avec une incohrence entre lindice en cours et llment que nous essayons
datteindre. En effet, lorsque le jour courant est Mercredi, lindice i vaut 2. Si lon
supprime cet lment, cest Jeudi qui va se retrouver en position 2. Et nous allons rater son

analyse car la boucle va continuer lindice 3, qui sera Vendredi.


On peut viter ce problme en parcourant la boucle lenvers :
for (int i = jours.Count - 1; i >= 0; i--)
{
if (jours[i] == "Mercredi")
jours.Remove(jours[i]);
}

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

La boucle While
La boucle while est en gnral moins utilise que for ou foreach. Cest la boucle qui va
nous permettre de faire quelque chose tant quune condition nest pas vrifie. Elle
ressemble de loin la boucle for, mais la boucle for se spcialise dans le parcours de
tableau tandis que la boucle while est plus gnrique.
Par exemple :
int i = 0;
while (i < 50)
{
Console.WriteLine("Bonjour C#");
i++;
}

Permet dcrire Bonjour C# 50 fois de suite.


Ici, la condition du while est value en dbut de boucle.
Dans lexemple suivant :
int i = 0;
do
{
Console.WriteLine("Bonjour C#");
i++;
}
while (i < 50);

La condition de sortie de boucle est value la fin de la boucle. Lutilisation du mot cl


do permet dindiquer le dbut de la boucle.
Concrtement cela veut dire que dans le premier exemple, le code lintrieur de la
boucle peut ne jamais tre excut, si par exemple lentier i est initialis 50. A contrario,

on passera au moins une fois dans le corps de la boucle du second exemple, mme si
lentier i est initialis 50 car la condition de sortie de boucle est value la fin.
Les conditions de sorties de boucles ne portent pas forcment sur un compteur ou un
indice, nimporte quelle expression peut tre value. Par exemple :
string[] jours = new string[] { "Lundi", "Mardi", "Mercredi", "Jeudi", "Vendredi", "Samedi",
"Dimanche" };
int i = 0;
bool trouve = false;
while (!trouve)
{
string valeur = jours[i];
if (valeur == "Jeudi")
{
trouve = true;
}
else
{
i++;
}
}
Console.WriteLine("Trouv l'indice " + i);

Le code prcdant va rpter les instructions contenues dans la boucle while tant que le
boolen trouve sera faux (cest--dire quon va sarrter ds que le boolen sera vrai).
Nous analysons la valeur lindice i, si la valeur est celle cherche, alors nous passons le
boolen vrai et nous pourrons sortir de la boucle. Sinon, nous incrmentons lindice pour
passer au suivant.
Attention encore aux valeurs de sorties de la boucle. Si nous ne trouvons pas la chaine
recherche, alors i continuera sincrmenter ; le boolen ne passera jamais vrai, nous
resterons bloqu dans la boucle et nous risquons datteindre un indice qui nexiste pas
dans le tableau.
Il serait plus prudent que la condition porte galement sur la taille du tableau, par exemple
:
string[] jours = new string[] { "Lundi", "Mardi", "Mercredi", "Jeudi", "Vendredi", "Samedi",
"Dimanche" };
int i = 0;
bool trouve = false;
while (i < jours.Length && !trouve)
{
string valeur = jours[i];
if (valeur == "Jeudi")
{
trouve = true;
}
else
{
i++;
}
}
if (!trouve)
Console.WriteLine("Valeur non trouve");
else
Console.WriteLine("Trouv l'indice " + i);

Ainsi, si lindice est suprieur la taille du tableau, nous sortons de la boucle et nous
liminons le risque de boucle infinie.
Une erreur classique est que la condition ne devienne jamais vraie cause dune erreur de
programmation. Par exemple, si joublie dincrmenter la variable i, alors chaque
passage de la boucle janalyserai toujours la premire valeur du tableau et je natteindrai
jamais la taille maximale du tableau, condition qui me permettrait de sortir de la boucle.

Je ne le rpterai jamais assez : bien faire attention aux conditions de sortie


dune boucle.

Les instructions break et continue


Il est possible de sortir prmaturment dune boucle grce linstruction break. Ds
quelle est rencontre, elle sort du bloc de code de la boucle. Lexcution du programme
continue alors avec les instructions situes aprs la boucle. Par exemple :
int i = 0;
while (true)
{
if (i >= 50)
{
break;
}
Console.WriteLine("Bonjour C#");
i++;
}

Le code prcdent pourrait potentiellement produire une boucle infinie. En effet, la


condition de sortie du while est toujours vraie. Mais lutilisation du mot cl break nous
permettra de sortir de la boucle ds que i atteindra la valeur 50.
Certes ici, il suffirait que la condition de sortie porte sur lvaluation de lentier i. Mais il
peut arriver des cas o il pourra tre judicieux dutiliser un break (surtout lors dun
dmnagement !).
Cest le cas pour lexemple suivant. Imaginons que nous voulions vrifier la prsence
dune valeur dans une liste. Pour la trouver, on peut parcourir les lments de la liste et
une fois trouve, on peut sarrter. En effet, il sera inutile de continuer parcourir le reste
des lments :
List<string> jours = new List<string> { "Lundi", "Mardi", "Mercredi", "Jeudi", "Vendredi", "Samedi",
"Dimanche" };
bool trouve = false;
foreach (string jour in jours)
{
if (jour == "Jeudi")
{
trouve = true;
break;
}
}

Nous nous conomisons ici danalyser les 3 derniers lments de la liste.


Il est galement possible de passer litration suivante dune boucle grce lutilisation
du mot-cl continue. Prenons lexemple suivant :
for (int i = 0; i < 20; i++)
{
if (i % 2 == 0)
{
continue;
}
Console.WriteLine(i);
}

Ici, loprateur % est appel modulo . Il permet dobtenir le reste de la division.


Lopration i % 2 renverra donc 0 quand i sera pair. Ainsi, ds quun nombre pair est
trouv, nous passons litration suivante grce au mot cl continue. Ce qui fait que nous

nallons afficher que les nombres impairs.


Ce qui donne :

Bien sr, il aurait t possible dinverser la condition du if pour ne faire le


Console.WriteLine() que dans le cas o i % 2 != 0. vous de choisir lcriture que vous
prfrez en fonction des cas que vous rencontrerez.
En rsum

On utilise la boucle for pour rpter des instructions tant quune condition nest pas
vrifie, les lments de la condition changeant chaque itration.
On utilise en gnral la boucle for pour parcourir un tableau, avec un indice qui
sincrmente chaque itration.
La boucle foreach est utilise pour simplifier le parcours des tableaux ou des listes.
La boucle while permet de rpter des instructions tant quune condition nest pas
vrifie. Cest la boucle la plus souple.
Il faut faire attention aux conditions de sortie dune boucle afin dviter les boucles
infinies qui font planter lapplication.

Chapitre 12

TP : Calculs en boucle

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

Instructions pour raliser le TP


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

Sachant que le premier rsultat de cet exemple vaut 55 (1 + 2 + 3 + 4 + 5 + 6 + 7 + 8 + 9 +


10 = 55) et le deuxime 5050.
La deuxime mthode acceptera une liste de double en paramtres et devra renvoyer la
moyenne des doubles de la liste. Par exemple :
List<double> liste = new List<double> { 1.0, 5.5, 9.9, 2.8, 9.6};
Console.WriteLine(CalculMoyenne(liste));

Le rsultat de cet exemple vaut 5.76.


Enfin, la dernire mthode devra dans un premier temps construire une liste dentiers de 1
100 qui sont des multiples de 3 (3, 6, 9, 12, ). Dans un second temps, construire une
autre liste dentiers de 1 100 qui sont des multiples de 5 (5, 10, 15, 20, ). Et dans un
dernier temps, il faudra calculer la somme des entiers qui sont communs aux deux listes
vous devez bien sur trouver 315 comme rsultat
Voil, cest vous de jouer

Bon, allez, je vais quand mme vous donner quelques conseils pour dmarrer. Vous ntes
pas oblig de les lire si vous vous sentez capables de raliser cet exercice tout seul.
Vous laurez videmment compris, il va falloir utiliser des boucles.
Je ne donnerai pas de conseils pour la premire mthode, cest juste pour vous chauffer
.
Pour la deuxime mthode, vous allez avoir besoin de diviser la somme de tous les
doubles par la taille de la liste. Vous ne savez sans doute pas comment obtenir cette taille.
Le principe est le mme que pour la taille dun tableau et vous laurez sans doute trouv si
vous fouillez un peu dans les mthodes de la liste. Toujours est-il que pour obtenir la taille
dune liste, on va utiliser liste.Count, avec par exemple :
int taille = liste.Count;

Enfin, pour la dernire mthode, vous allez devoir trouver tous les multiples de 3 et de 5.
Le plus simple, mon avis, pour calculer tous les multiples de 3, est de faire une boucle
qui dmarre 3 et davancer de 3 en 3 jusqu la valeur souhaite. Et pareil pour les
multiples de 5.
Ensuite, il sera ncessaire de faire deux boucles imbriques afin de dterminer les
intersections. Cest--dire parcourir la liste de multiple de 3 et lintrieur de cette boucle,
parcourir la liste des multiples de 5. On compare les deux lments, sils sont gaux, cest
quils sont communs aux deux listes.
Voil, vous devriez avoir tous les lments en main pour russir ce TP, cest vous

Correction
Jimagine que vous avez russi ce TP, non pas sans difficults, mais force de
ttonnements, vous avez sans doute russi. Bravo.
Si vous navez mme pas essay, pas bravo .
Quoi quil en soit, voici la correction que je propose.
Premire mthode :
static int CalculSommeEntiers(int borneMin, int borneMax)
{
int resulat = 0;
for (int i = borneMin; i <= borneMax ; i++)
{
resulat += i;
}
return resulat;
}

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

static double CalculMoyenne(List<double> liste)


{
double somme = 0;
foreach (double valeur in liste)
{
somme += valeur;
}
return somme / liste.Count;
}

Ici, le principe est grosso modo le mme, la diffrence est que la mthode retourne un
double et accepte une liste de double en paramtres. Ici, nous utilisons la boucle foreach
pour parcourir tous les lments que nous ajoutons un rsultat. Enfin, nous retournons ce
rsultat divis par la taille de la liste.
Troisime mthode :
static int CalculSommeIntersection()
{
List<int> multiplesDe3 = new List<int>();
List<int> multiplesDe5 = new List<int>();
for (int i = 3; i <= 100; i += 3)
{
multiplesDe3.Add(i);
}
for (int i = 5; i <= 100; i += 5)
{
multiplesDe5.Add(i);
}
int somme = 0;
foreach (int m3 in multiplesDe3)
{
foreach (int m5 in multiplesDe5)
{
if (m3 == m5)
somme += m3;
}
}
return somme;
}

Peut-tre la plus complique


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

m5 vaut 5

On compare 3 5, ils sont diffrents


On passe litration suivante de la liste multiplesDe5
m5 vaut 10
On compare 3 10, ils sont diffrents
Etc jusqu ce quon ait fini de parcourir la liste des multiplesDe5
On passe litration suivante de la liste multiplesDe3
m3 vaut 6
On rentre dans la boucle qui parcoure la liste multiplesDe5
m5 vaut 5
On compare 6 5, ils sont diffrents
On passe litration suivante de la liste multiplesDe5
Etc

Aller plus loin


Vous avez remarqu que dans la deuxime mthode, jutilise une boucle foreach pour
parcourir la liste :
static double CalculMoyenne(List<double> liste)
{
double somme = 0;
foreach (double valeur in liste)
{
somme += valeur;
}
return somme / liste.Count;
}

Il est aussi possible de parcourir les listes avec un for et daccder aux lments de la liste
avec un indice, comme on le fait pour un tableau.
Ce qui donnerait :
static double CalculMoyenne(List<double> liste)
{
double somme = 0;
for (int i = 0; i < liste.Count; i++)
{
somme += liste[i];
}
return somme / liste.Count;
}

Notez quon se sert de liste.Count pour obtenir la taille de la liste et quon accde
llment courant avec liste[i].
Je reconnais que la boucle foreach est plus explicite, mais cela peut tre utile de connaitre
le parcours dune liste avec la boucle for. A vous de voir.
Vous aurez galement not ma technique pour avoir des multiples de 3 et de 5. Il y en a
plein dautres. Je pense une en particulier et qui fait appel des notions que nous avons
vu auparavant : la division entire et la division de double.
Pour savoir si un nombre est un multiple dun autre, il suffit de les diviser entre eux et de
voir sil y a un reste la division. Pour ce faire, on peut le faire de deux faons. Soit en
comparant la division entire avec la division double et en vrifiant que le rsultat est
le mme. Si le rsultat est le mme, cest quil ny a pas de chiffres aprs la virgule :

for (int i = 1; i <= 100; i++)


{
if (i / 3 == i / 3.0)
multiplesDe3.Add(i);
if (i / 5 == i / 5.0)
multiplesDe5.Add(i);
}

Cette technique fait appel des notions que nous navons pas encore vues :
comment cela se fait-il quon puisse comparer un entier un double ? Nous le
verrons un peu plus tard.
Lautre solution est dutiliser loprateur modulo que nous avons vu prcdemment qui
fait justement a :
for (int i = 1; i <= 100; i++)
{
if (i % 3 == 0)
multiplesDe3.Add(i);
if (i % 5 == 0)
multiplesDe5.Add(i);
}

Lavantage de ces deux techniques est quon peut construire les deux listes de multiples en
une seule boucle.
Voil, cest fini pour ce TP. Nhsitez pas vous faire la main sur ces boucles car il est
fondamental que vous les maitrisiez. Faites-vous plaisir, tentez de les repousser dans leurs
limites, essayez de rsoudre dautres problmes, Limportant, cest de pratiquer.

Deuxime partie

Un peu plus loin avec le C#

Chapitre 13

Les conversions entre les types

Nous avons vu que le C# est un langage qui possde plein de types de donnes diffrents :
entier, dcimal, chane de caractres, etc.
Dans nos programmes, nous allons trs souvent avoir besoin de manipuler des donnes
entre elles alors quelles ne sont pas forcment du mme type. Lorsque cela sera possible,
nous aurons besoin de convertir un type de donnes en un autre.

Entre les types compatibles : Le casting


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

Description

short

Entier de -32768 32767

int

Entier de -2147483648 2147483647

Ils ne diffrent que par leur capacit. Le short ne pourra pas contenir le nombre 100000
par exemple, alors que lint le pourra.
Nous ne lavons pas encore fait, mais le C# nous autorise faire :
short s = 200;
int i = s;

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

int i = 100000;
short s = i;

nous aurons lerreur de compilation suivante :


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

Et oui, comment pouvons-nous faire rentrer 100000 dans un type qui ne peut aller que
jusqu 32767 ? Le compilateur le sait bien.
Vous remarquerez que nous aurons cependant la mme erreur si nous tentons de faire :
int i = 200;
short s = i;

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


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

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


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

Nous utilisons ce quon appelle un cast explicite.


Pour faire un tel cast, il suffit de prcder la variable convertir du nom du type souhait
entre parenthses. Ce qui permet dindiquer notre compilateur que nous savons ce que
nous faisons, et que lentier tiendra correctement dans le petit entier.
Mais attention, le compilateur nous fait confiance. Nous sommes le boss ! Cela implique
une certaine responsabilit, il ne faut pas faire nimporte quoi.
Si nous faisons :
int i = 40000;
short s = (short)i;
Console.WriteLine(s);
Console.WriteLine(i);

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

Le rsultat nest pas du tout celui attendu. Nous avons fait nimporte quoi, le C# nous a
punis.
En fait, plus prcisment, il sest pass ce quon appelle un dpassement de capacit. Nous
lavons dj vu lors du chapitre sur les boucles. Ici, il sest produit la mme chose. Le petit
entier est all sa valeur maximale puis a boucl en repartant de sa valeur minimale.
Bref, tout a pour dire que nous obtenons un rsultat inattendu. Il faut donc faire attention
ce que lon fait et honorer la confiance que nous porte le compilateur. Bien faire
attention ce que lon caste.
Caste Remarquons cet anglicisme. Nous utiliserons souvent le verbe caster,
qui signifiera bien sr que nous convertissons des types qui se ressemblent entre
eux, pour le plus grand malheur des professeurs de franais.
Dune faon similaire lentier et au petit entier, lentier et le double se ressemblent un
peu. Ce sont tous les deux des types permettant de contenir des nombres. Le premier
permet de contenir des nombres entiers, le deuxime pouvant contenir des nombres
virgules.
Ainsi, nous pouvons faire le code C# suivant :
int i = 20;
double d = i;

En effet, un double est plus prcis quun entier. Il est capable davoir des chiffres aprs la
virgule alors que lentier ne le peut pas. Ce qui fait que le double est entirement capable
de stocker toute la valeur dun entier sans perdre dinformation dans cette affectation.
Par contre, comme on peut sy attendre, linverse nest pas possible. Le code suivant :
double prix = 125.55;
int valeur = prix;

produira lerreur de compilation suivante :


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

En effet, un double tant plus prcis quun entier, si nous mettons ce double dans lentier
nous allons perdre notamment les chiffres aprs la virgule.

Il restera encore une fois demander au compilateur de nous faire confiance !


Ok, ceci est un double, mais ce nest pas grave, je veux lutiliser comme un
entier ! Oui oui, mme si je sais que je vais perdre les chiffres aprs la virgule .
Ce qui scrit en C# de cette manire, comme nous lavons vu :
double prix = 125.55;
int valeur = (int)prix; // valeur vaut 125

Nous faisons prcder la variable prix du type dans lequel nous voulons la convertir en
utilisant les parenthses.
En loccurrence, on peut se servir de ce cast pour rcuprer la partie entire dun
nombre virgule.
Cest plutt sympa comme instruction. Mais noubliez-pas que cette opration est possible
uniquement avec les types qui se ressemblent entre eux.
Par exemple, le cast suivant est impossible :
string nombre = "123";
int valeur = (int)nombre;

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

Le message derreur est plutt explicite. Il ne nous propose mme pas dutiliser
de cast, il considre que les types sont vraiment trop diffrents !
Nous avons vu prcdemment que les numrations reprsentaient des valeurs entires. Il
est donc possible de rcuprer lentier correspondant une valeur de lnumration grce
un cast.
Par exemple, en considrant lnumration suivante :
enum Jours
{
Lundi = 5, // lundi vaut 5
Mardi, // mardi vaut 6
Mercredi = 9, // mercredi vaut 9
Jeudi = 10, // jeudi vaut 10
Vendredi, // vendredi vaut 11
Samedi, // samedi vaut 12
Dimanche = 20 // dimanche vaut 20
}

Le code suivant :
int valeur = (int)Jours.Lundi; // valeur vaut 5

convertit lnumration en entier.


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

Entre les types incompatibles


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

Cette mthode, bien que simple dutilisation, pose un inconvnient dans le cas o la
chaine ne reprsente pas un entier. ce moment-l, la conversion va chouer et le C# va
renvoyer une erreur au moment de lexcution.
Par exemple :
string chaineAge = "vingt ans";
int age = Convert.ToInt32(chaineAge);

Si vous excutez ce bout de code, vous verrez :

La console nous affiche ce que lon appelle une exception. Il sagit tout simplement dune
erreur. Nous aurons loccasion dtudier plus en dtail les exceptions dans les chapitres
ultrieurs.
Pour linstant, on a juste besoin de voir que ceci ne nous convient pas. Lerreur est
explicite Le format de la chaine dentre est incorrect , mais cela se passe au
moment de lexcution et notre programme plante lamentablement.

Nous verrons dans le chapitre sur les exceptions comment grer les erreurs.
En interne, la mthode Convert.ToInt32() utilise une autre mthode pour faire la
conversion, il sagit de la mthode int.Parse(). Donc plutt que dutiliser une mthode
qui en appelle une autre, nous pouvons nous en servir directement, par exemple :
string chaineAge = "20";
int age = int.Parse(chaineAge); // age vaut 20

Bien sr, il se passera exactement la mme chose que prcdemment quand la chaine ne
reprsentera pas un entier correct.
Il existe par contre une autre faon de convertir une chaine en entier qui ne provoque pas
derreur quand le format nest pas correct et qui nous informe si la conversion sest bien
passe ou non, cest la mthode int.TryParse() qui sutilise ainsi :
string chaineAge = "ab20cd";
int age;
if (int.TryParse(chaineAge, out age))
{
Console.WriteLine("La conversion est possible, age vaut " + age);
}
else
{
Console.WriteLine("Conversion impossible");
}

Et nous aurons :

La mthode int.TryParse nous renvoie vrai si la conversion est bonne, faux sinon. Nous
pouvons donc effectuer une action en fonction du rsultat grce aux if/else. On passe en
paramtres la chaine convertir et la variable o lon veut stocker le rsultat de la
conversion. Le mot cl out signifie juste que la variable est modifie par la mthode,
nous verrons exactement de quoi il sagit dans un chapitre ultrieur.
Les mthodes que nous venons de voir Convert.ToString() ou int.TryParse() se
dclinent en gnral pour tous les types de bases, par exemple double.TryParse() ou
Convert.ToDecimal(), etc
En rsum

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

Chapitre 14

Lire le clavier dans la console

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

Lire une phrase


Lorsque nous lui en donnerons la possibilit, lutilisateur de notre programme pourra saisir
des choses grce son clavier. Nous pouvons lui demander son ge, sil veut terminer
notre application, lui permettre de saisir un mail, etc
Il nous faut donc un moyen lui permettant de saisir des caractres en tapant sur son clavier.
Nous pourrons faire cela grce la mthode Console.ReadLine:
string saisie = Console.ReadLine();

Lorsque notre application rencontre cette instruction, elle se met en pause et attend une
saisie de la part de lutilisateur. La saisie sarrte lorsque lutilisateur valide ce quil a crit
avec la touche entre. Ainsi les instructions suivantes :
Console.WriteLine("Veuillez saisir une phrase et valider avec la touche \"Entre\"");
string saisie = Console.ReadLine();
Console.WriteLine("Vous avez saisi : " + saisie);

produiront :

Vous aurez remarqu que nous stockons le rsultat de la saisie dans une variable de type
chaine de caractres. Cest bien souvent le seul type que nous aurons notre disposition
lors des saisies utilisateurs. Cela veut bien sr dire que si vous avez besoin de manipuler
lge de la personne, il faudra la convertir en entier. Par exemple :
bool ageIsValid = false;
int age = -1;
while (!ageIsValid)
{
Console.WriteLine("Veuillez saisir votre age");
string saisie = Console.ReadLine();
if (int.TryParse(saisie, out age))
ageIsValid = true;
else
{
ageIsValid = false;
Console.WriteLine("L'age que vous avez saisi est incorrect");
}
}
Console.WriteLine("Votre ge est de : " + age);

Et nous aurons :

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

Lire un caractre
Il peut arriver que nous ayons besoin de ne saisir quun seul caractre, pour cela nous
allons pouvoir utiliser la mthode Console.ReadKey().
Nous pourrons nous en servir par exemple pour faire une pause dans notre application. Le
code suivant rclame lattention de lutilisateur avant de dmarrer un calcul hautement
important :
Console.WriteLine("Veuillez appuyer sur une touche pour dmarrer le calcul");
Console.ReadKey(true);
int somme = 0;
for (int i = 0; i < 100; i++)
{
somme += i;
}
Console.WriteLine(somme);

Ce qui nous donnera :

noter que nous avons pass true en paramtre de la mthode afin dindiquer au C# que
nous ne souhaitions pas que notre saisie apparaisse lcran. Si le paramtre avait t
false et que javais par exemple appuy sur h pour dmarrer le calcul, le rsultat se serait
vu prcd dun disgracieux h

Dailleurs, comment faire pour savoir quelle touche a t saisie ?


Il suffit dobserver le contenu de la variable renvoye par la mthode Console.ReadKey.
Elle renvoie en loccurrence une variable du type ConsoleKeyInfo. Nous pourrons
comparer la valeur Key de cette variable qui est une numration du type ConsoleKey.
Par exemple :
Console.WriteLine("Voulez-vous continuer (O/N) ?");
ConsoleKeyInfo saisie = Console.ReadKey(true);
if (saisie.Key == ConsoleKey.O)
{
Console.WriteLine("On continue");
}
else
{
Console.WriteLine("On s'arrte");
}

Nous remarquons grce la compltion automatique que lnumration ConsoleKey


possde plein de valeur :

Nous comparons donc la valeur ConsoleKey.O qui reprsente la touche o.


noter que le type ConsoleKeyInfo est ce quon appelle une structure et que Key est
une proprit de la structure. Nous reviendrons dans la partie suivante sur ce que cela veut
vraiment dire. Pour linstant, considrez juste quil sagit dune variable volue qui
permet de contenir plusieurs choses
En rsum

La mthode Console.ReadLine nous permet de lire des informations saisies par


lutilisateur au clavier.
Il est possible de lire toute une phrase termine par la touche Entre ou seulement un
unique caractre.

Chapitre 15

Utiliser le dbogueur

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

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

Pour tudier le dbogueur, reprenons la dernire mthode du prcdent TP :


static void Main(string[] args)
{
Console.WriteLine(CalculSommeIntersection());
}
static int CalculSommeIntersection()
{
List<int> multiplesDe3 = new List<int>();
List<int> multiplesDe5 = new List<int>();

for (int i = 1; i <= 100; i++)


{
if (i % 3 == 0)
multiplesDe3.Add(i);
if (i % 5 == 0)
multiplesDe5.Add(i);
}
int somme = 0;
foreach (int m3 in multiplesDe3)
{
foreach (int m5 in multiplesDe5)
{
if (m3 == m5)
somme += m3;
}
}
return somme;
}

Mettre un point darrt et avancer pas pas


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

Il est possible de mettre autant de points darrts que nous le souhaitons, nimporte quel
endroit de notre code.
Lanons lapplication avec F5, nous pouvons voir que Visual C# express sarrte sur la
ligne prvue en la surlignant en jaune :

Nous en profitons pour remarquer au niveau du carr rouge que le dbogueur est en pause
et quil est possible soit de continuer lexcution (triangle) soit de larrter (carr) soit de
redmarrer le programme depuis le dbut (carr avec une flche blanche dedans).
Nous pouvons dsormais excuter notre code pas pas, en nous servant des icnes cot
ou des raccourcis claviers suivants :

Utilisons la touche F10 pour continuer lexcution du code pas pas. La ligne suivante se
trouve surligne son tour. Appuyons nouveau sur la touche F10 pour aller
linstruction suivante, il ny en a plus le programme se termine.
Vous me direz, cest bien beau, mais, nous ne sommes pas passs dans la mthode
CalculSommeIntersection() . Et oui, cest parce que nous avons utilis la touche F10 qui
est le pas pas principal. Pour rentrer dans la mthode, il aurait fallu utiliser la touche F11
qui est le pas pas dtaill.
Si nous souhaitons que le programme se poursuive pour aller jusquau prochain point
darrt, il suffit dappuyer sur le triangle (play) ou sur F5.
Relanons notre programme en mode dbogage, et cette fois-ci, lorsque le dbogueur

sarrte sur notre point darrt, appuyons sur F11 pour rentrer dans le corps de la mthode.
Voil, nous sommes dans la mthode CalculSommeIntersection(). Continuons appuyer
plusieurs fois sur F10 afin de rentrer dans le corps de la boucle for.

Observer des variables


ce moment-l du dbogage, si nous passons la souris sur la variable i, qui est lindice de
la boucle, Visual C# express va nous afficher une mini-information :

Il nous indique que i vaut 1, ce qui est normal, nous sommes dans la premire itration de
la boucle. Si nous continuons le parcours en appuyant sur F10 plusieurs fois, nous voyons
que la valeur de i augmente, conformment ce qui est attendu.
Maintenant, mettons un point darrt - F9 - sur la ligne :
multiplesDe3.Add(i);

et poursuivons lexcution en appuyant sur F5. Il sarrte au moment o i vaut 3.


Appuyons sur F10 pour excuter lajout de i la liste et passons la souris sur la liste :

Visual C# express nous indique que la liste multiplesDe3 a son Count qui vaut 1. Si
nous cliquons sur le + pour dplier la liste :

nous pouvons voir que 3 a t ajout dans le premier emplacement de la liste (indice 0). Si
nous continuons lexcution plusieurs fois, nous voyons que les listes se remplissent au fur
et mesure.
Enlevez le point darrt sur la ligne en appuyant nouveau sur F9 et mettez un nouveau
point darrt sur la ligne :
int somme = 0;

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

Grce au dbogueur, nous pouvons voir vraiment tout ce qui nous intresse, cest une des
grandes forces du dbogueur et cest un atout vraiment trs utile pour comprendre ce quil
se passe dans un programme (en gnral, a se passe mal !).
Il est galement possible de voir les variables locales en regardant en bas dans la fentre
Variables locales . Dans cette fentre, vous pourrez observer les variables qui sont dans la
porte courante. Il existe galement une fentre Espion qui permet de la mme faon
de surveiller une ou plusieurs variables prcises.

Revenir en arrire
Nom de Zeus, Marty ! On peut revenir dans le pass ?
Cest presque a Doc ! Si pour une raison, vous souhaitez r-excuter un bout de code,
parce que vous navez pas bien vu ou que vous avez rat linformation quil vous fallait,
vous pouvez forcer le dbogueur revenir en arrire dans le programme. Pour cela, vous
avez deux solutions, soit vous faites un clic droit lendroit souhait et vous choisissez
llment de menu Dfinir linstruction suivante :

soit vous dplacez avec la souris la petite flche jaune sur la gauche qui indique lendroit
o nous en sommes.

Il faut faire attention car ce retour en arrire nen est pas compltement un. En effet, si
vous revenez au dbut de la boucle qui calcule les multiples et que vous continuez
lexcution, vous verrez que la liste continue se remplir. la fin de la boucle, au lieu de
contenir 33 lments, la liste des multiples de 3 en contiendra 66. En effet, aucun
moment nous navons vid la liste et donc le fait de r-excuter cette partie de code risque
de provoquer des comportements inattendus. Ici, il vaudrait mieux revenir au dbut de la
mthode afin que la liste soit de nouveau cre.
Mme si a ne vous saute pas aux yeux pour linstant, vous verrez lusage que cette
possibilit est bien pratique. Dautant plus quand vous aurez bien assimil toutes les
notions de porte de variable.
Il est important de noter que, bien que puissant, le dbogueur de la version
gratuite de Visual Studio est vraiment moins puissant que celui des versions
payantes. Il y a plein de choses en moins que lon ne peut pas faire.
Mais rassurez-vous, celui-ci est quand mme dj bien avanc et permet de faire beaucoup
de choses.

La pile des appels


Une autre fentre importante regarder est la pile des appels, elle permet dindiquer o
nous nous trouvons et par o nous sommes passs pour arriver cet endroit-l.
Par exemple si vous appuyez sur F11 et que vous rentrez dans la mthode

CalculSommeIntersection() vous pourrez voir dans la pile des appels que vous tes dans

la mthode CalculSommeIntersection() qui a t appele depuis la mthode Main() :

Si vous cliquez sur la ligne du Main(), Visual C# express vous ramne automatiquement
lendroit o a t fait lappel. Cette ligne est alors surligne en vert pour bien faire la
diffrence avec le surlignage en jaune qui est vraiment lendroit o se trouve le dbogueur.
Cest trs pratique quand on a beaucoup de mthodes qui sappellent les unes la suite des
autres.

La pile des appels est galement affiche lorsquon rencontre une erreur, elle
permettra didentifier plus facilement o est le problme. Vous lavez vu dans le
chapitre sur les conversions entre les types incompatibles.
En tous cas, le dbogueur est vraiment un outil forte valeur ajoute. Je ne vous ai
prsent que le strict minimum ncessaire et indispensable. Mais croyez-moi, vous aurez
loccasion dy revenir .
En rsum

Le dbogueur est un outil trs puissant permettant dinspecter le contenu des


variables lors de lexcution dun programme.
On peut sarrter un endroit de notre application grce un point darrt.

Le dbogueur permet dexcuter son application pas pas et de suivre son volution.

Chapitre 16

TP : le jeu du plus ou du moins

Wahou, nous augmentons rgulirement le nombre de choses que nous savons. Cest
super. Nous commenons tre capables dcrire des applications qui ont un peu plus de
panache !
Enfin moi, jy arrive ! Et vous ? Cest ce que nous allons vrifier avec ce TP.
Savoir interagir avec son utilisateur est important. Voici donc un petit TP sous forme de
cration dun jeu simple qui va vous permettre de vous entraner. Lide est de raliser le
jeu classique du plus ou du moins

Instructions pour raliser le TP


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

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

Correction
Voici ma correction de ce TP.
Bien sr, il existe beaucoup de faon de raliser ce petit jeu. Sil fonctionne, cest que
votre solution est bonne. Ma solution fonctionne, la voici :

static void Main(string[] args)


{
int valeurATrouver = new Random().Next(0, 100);
int nombreDeCoups = 0;
bool trouve = false;
Console.WriteLine("Veuillez saisir un nombre compris entre 0 et 100 (exclu)");
while (!trouve)
{
string saisie = Console.ReadLine();
int valeurSaisie;
if (int.TryParse(saisie, out valeurSaisie))
{
if (valeurSaisie == valeurATrouver)
trouve = true;
else
{
if (valeurSaisie < valeurATrouver)
Console.WriteLine("Trop petit");
else
Console.WriteLine("Trop grand");
}
nombreDeCoups++;
}
else
Console.WriteLine("La valeur saisie est incorrecte, veuillez recommencer");
}
Console.WriteLine("Vous avez trouv en " + nombreDeCoups + " coup(s)");
}

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

Aller plus loin


Il est bien sr toujours possible damliorer le jeu. Nous pourrions par exemple ajouter un
contrle sur les bornes de la saisie. Ainsi, si lutilisateur saisit un nombre suprieur ou gal
100 ou infrieur 0, nous pourrions lui rappeler les bornes du nombre alatoire.
De mme, la fin, plutt que dafficher coup(s) , nous pourrions tester la valeur du
nombre de coups. Sil est gal 1, on affiche coup au singulier, sinon coups au
pluriel.
La boucle pourrait galement tre lgrement diffrente. Plutt que de tester la condition
de sortie sur un boolen, nous pourrions utiliser le mot cl break. De mme, nous
pourrions allger lcriture avec le mot cl continue.
Par exemple :
static void Main(string[] args)
{
int valeurATrouver = new Random().Next(0, 100);
int nombreDeCoups = 0;
Console.WriteLine("Veuillez saisir un nombre compris entre 0 et 100 (exclu)");
while (true)
{
string saisie = Console.ReadLine();
int valeurSaisie;
if (!int.TryParse(saisie, out valeurSaisie))
{
Console.WriteLine("La valeur saisie est incorrecte, veuillez recommencer");
continue;
}
if (valeurSaisie < 0 || valeurSaisie >= 100)
{
Console.WriteLine("Vous devez saisir un nombre entre 0 et 100 exclu");
continue;
}
nombreDeCoups++;
if (valeurSaisie == valeurATrouver)
break;
if (valeurSaisie < valeurATrouver)
Console.WriteLine("Trop petit");
else
Console.WriteLine("Trop grand");
}

if (nombreDeCoups == 1)
Console.WriteLine("Vous avez trouv en " + nombreDeCoups + " coup");
else
Console.WriteLine("Vous avez trouv en " + nombreDeCoups + " coups");
}

Tout ceci est une question de got. Je prfre personnellement la version prcdente
naimant pas trop les break et les continue. Mais aprs tout, chacun fait comme il prfre,
limportant est que nous amusions crire le programme et y jouer.
Voil, ce TP est termin.
Vous avez pu voir finalement que nous tions tout fait capables de raliser des petites
applications rcratives. Personnellement, jai commenc mamuser faire de la
programmation en ralisant toute sorte de petits programmes de ce genre.
Je vous encourage fortement essayer de crer dautres programmes vous-mmes,
pourquoi pas proposer aux autres vos ides.
Plus vous vous entranerez faire des petits programmes simples et plus vous russirez
apprhender les subtilits de ce que nous avons appris.
Bien sr, plus tard, nous serons capables de raliser des applications plus compliques
Cela vous tente ? Alors continuons la lecture.

Chapitre 17

La ligne de commande

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

Quest-ce que la ligne de commande ?


La ligne de commande, cest ce qui nous permet dexcuter nos programmes. Trs
prsente lpoque o Windows existait peu, elle a tendance disparaitre de nos
utilisations. Mais pas le fond de son fonctionnement.
En gnral, la ligne de commande sert passer des arguments un programme. Par
exemple, pour ouvrir un fichier texte avec le bloc-notes (notepad.exe), on peut le faire de
deux faons diffrentes. Soit on ouvre le bloc-notes et on va dans le menu Fichier >
Ouvrir puis on va chercher le fichier pour louvrir. Soit on utilise la ligne de commande
pour louvrir directement.
Pour ce faire, il suffit dutiliser la commande :
notepad c:\test.txt

et le fichier c:\test.txt souvre directement dans le bloc-notes.


Ce quil sest pass derrire, cest que nous avons demand dexcuter le programme
notepad.exe avec le paramtre c:\test.txt. Le programme notepad a analys sa ligne de
commande, il y a trouv un paramtre et il a ouvert le fichier correspondant.
Superbe ! Nous allons apprendre faire pareil !

Passer des paramtres en ligne de commande


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

On peut galement le faire depuis une invite de commande (Menu Dmarrer > Accessoires
> Invite de commande) :

Ceci nous ouvre une console noire, comme celle que lon connait bien et dans laquelle
nous pouvons taper des instructions :

Mais nous, ce qui nous intresse surtout, cest de pouvoir le faire depuis Visual C#
Express afin de pouvoir passer des arguments notre programme.
Pour ce faire, on va aller dans les proprits de notre projet (bouton droit sur le projet,
Proprits) puis nous allons dans longlet Dboguer et nous voyons une zone de texte
permettant de mettre des arguments la ligne de commande. Rajoutons par exemple
Bonjour Nico :

Voil, maintenant lorsque nous excuterons notre application, Visual C# express lui
passera les arguments que nous avons dfinis en paramtre de la ligne de commande.
A noter que dans ce cas, les arguments Bonjour Nico ne seront valables que lorsque
nous excuterons lapplication travers Visual C# express. Evidemment, si nous
excutons notre application par linvite de commande, nous aurons besoin de repasser les
arguments au programme pour quil puisse les exploiter.
Cest bien ! Sauf que pour linstant, a ne nous change pas la vie ! Il faut apprendre
traiter ces paramtres

Lire la ligne de commande


On peut lire le contenu de la ligne de commande de deux faons diffrentes. Vous vous
rappelez de la mthode Main() ? On a vu que Visual C# express gnrait cette mthode
avec des paramtres. Et bien ces paramtres, vous ne devinerez jamais ! Ce sont les
paramtres de la ligne de commande. Oh joie !
static void Main(string[] args)
{
}

La variable args est un tableau de chaine de caractres. Sachant que chaque paramtre est
dlimit par des espaces, nous retrouverons chacun des paramtres un indice du tableau
diffrent.
Ce qui fait que si jutilise le code suivant :
foreach (string parametre in args)
{
Console.WriteLine(parametre);
}

et que je lance mon application avec les paramtres dfinis prcdemment, je vais obtenir :

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

foreach (string parametre in Environment.GetCommandLineArgs())


{
Console.WriteLine(parametre);
}

Ce qui donne :

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

En rsum

On peut passer des paramtres une application en utilisant la ligne de commande.

Le programme C# peut lire et interprter ces paramtres.


Ces paramtres sont spars par des espaces.

Chapitre 18

TP : Calculs en ligne de commande

Et voici un nouveau TP qui va nous permettre de rcapituler un peu tout ce que nous
avons vu.
Au programme, des if, des switch, des mthodes, des boucles et de la ligne de
commande bien sr !
Grce nos connaissances grandissantes, nous arrivons augmenter la difficult de nos
exercices et cest une bonne chose. Vous verrez que dans le moindre petit programme,
vous aurez besoin de toutes les notions que nous avons apprises.
Allez, ne tranons pas trop et vous de jouer travailler.

Instructions pour raliser le TP


Lobjectif de ce TP est de raliser un petit programme qui permet deffectuer une
opration mathmatique partir de la ligne de commande.
Il va donc falloir crire un programme que lon pourra appeler avec des paramtres. En
imaginant que le programme sappelle MonProgramme , on pourrait lutiliser ainsi
pour faire la somme de deux nombres :
MonProgramme somme 2 5

Ce qui renverra bien sr 7.


Pour effectuer une multiplication, nous pourrons faire :
MonProgramme multiplication 2 5

et nous obtiendrons 10.


Enfin, pour calculer une moyenne, nous pourrons faire :
MonProgramme moyenne 1 2 3 4 5

ce qui renverra 3.
Les rgles sont les suivantes :
Il doit y avoir 2 et seulement 2 nombres composant laddition et la multiplication
La moyenne peut contenir autant de nombres que souhait
Si le nombre de paramtres est incorrect, alors le programme affichera un message
daide explicite
Si le nombre attendu nest pas un double correct, on affichera le message daide
Plutt simple non ?
Alors, vous de jouer, il ny a pas de subtilit.

Correction
STOP.
Je relve les copies.
Jai dit quil ny avait pas de subtilit, mais jai un peu menti en fait. Javoue, ctait pour
que vous alliez au bout du dveloppement sans aide.
Pour vrifier que vous avez trouv la subtilit, essayez votre programme avec les
paramtres suivants : MonProgramme addition 5,5 2,5
Obtenez-vous 8 ?
Ctait peu prs la seule subtilit, il fallait juste savoir quun nombre virgule scrivait
avec une virgule plutt quavec un point. Si nous crivons 2.5, alors le C# ne sait pas faire
la conversion.
En vrai, cest un peu plus compliqu que a et nous le verrons dans un prochain
chapitre, mais lcriture du nombre virgule est dpendant de ce quon appelle
la culture courante que lon peut modifier dans les paramtres horloge, langue
et rgion du panneau de configuration de Windows. Si lon change le format
de la date, de lheure ou des nombres on change la culture courante, et la
faon dont les nombres virgules (et autres) sont grs est diffrente. Nous en
reparlerons plus prcisment dans un chapitre ultrieur.
Quoiquil en soit, voici ma correction du TP :
static void Main(string[] args)
{
if (args.Length == 0)
{
AfficherAide();
}
else
{
string operateur = args[0];
switch (operateur)
{
case "addition":
Addition(args);
break;
case "multiplication":
Multiplication(args);
break;
case "moyenne":
Moyenne(args);
break;
default:
AfficherAide();
break;
}
}
}
static void AfficherAide()
{
Console.WriteLine("Utilisez l'application de la manire suivante :");
Console.WriteLine("MonProgamme addition 2 5");
Console.WriteLine("MonProgamme multiplication 2 5");
Console.WriteLine("MonProgamme moyenne 2 5 10 11");
}

static void Addition(string[] args)


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

Disons que cest plus long que difficile


Oui je sais, lors de laddition et de la multiplication, jai parcouru lensemble des
paramtres alors que jaurais pu utiliser uniquement args[1] et args[2]. Lavantage cest
que si je supprime la condition sur le nombre de paramtres, alors mon addition pourra
fonctionner avec autant de nombres que je le souhaite Vous aurez remarqu que je
commence mon parcours lindice 1, do la pertinence de lutilisation de la boucle for
plutt que la boucle foreach.

Notez quand mme lutilisation du mot cl return afin de sortir prmaturment de la


mthode en cas de problme.
videmment, il y a plein de faons de raliser ce TP, la mienne en est une, il y en a
dautres.
Jespre que vous aurez fait attention au calcul de la moyenne. Nous retirons 1 la
longueur du tableau pour obtenir le nombre de paramtres. Il faut donc faire attention
lordre des parenthses afin que le C# fasse dabord la soustraction avant la division, alors
que sans a, la division est prioritaire la soustraction.
Sinon, cest lerreur de calcul assure, indigne de notre superbe application.

Aller plus loin


Ce code est trs bien.
Sisi, cest moi qui lai fait .
Sauf quil y a quand mme un petit problme dordre architectural.
Il a t plutt simple crire en suivant lnonc du TP mais on peut faire encore mieux.
On peut le rendre plus facilement maintenable en le r-architecturant diffremment.
Observez le code suivant :
static void Main(string[] args)
{
bool ok = false;
if (args.Length > 0)
{
string operateur = args[0];
switch (operateur)
{
case "addition":
ok = Addition(args);
break;
case "multiplication":
ok = Multiplication(args);
break;
case "moyenne":
ok = Moyenne(args);
break;
}
}
if (!ok)
AfficherAide();
}
static void AfficherAide()
{
Console.WriteLine("Utilisez l'application de la manire suivante :");
Console.WriteLine("MonProgamme addition 2 5");
Console.WriteLine("MonProgamme multiplication 2 5");
Console.WriteLine("MonProgamme moyenne 2 5 10 11");
}
static bool Addition(string[] args)
{
if (args.Length != 3)
return false;
double somme = 0;
for (int i = 1; i < args.Length; i++)
{
double valeur;
if (!double.TryParse(args[i], out valeur))
return false;

somme += valeur;
}
Console.WriteLine("Rsultat de l'addition : " + somme);
return true;
}
static bool Multiplication(string[] args)
{
if (args.Length != 3)
return false;
double resultat = 1;
for (int i = 1; i < args.Length; i++)
{
double valeur;
if (!double.TryParse(args[i], out valeur))
return false;
resultat *= valeur;
}
Console.WriteLine("Rsultat de la multiplication : " + resultat);
return true;
}
static bool Moyenne(string[] args)
{
double total = 0;
for (int i = 1; i < args.Length; i++)
{
double valeur;
if (!double.TryParse(args[i], out valeur))
return false;
total += valeur;
}
total = total / (args.Length - 1);
Console.WriteLine("Rsultat de la moyenne : " + total);
return true;
}

Nous naffichons laide plus qu un seul endroit en utilisant le fait que les mthodes
renvoient un boolen indiquant si lopration a t possible ou pas.
Dune manire gnrale, il est important dessayer de rendre son code le plus maintenable
possible, comme nous lavons fait ici.
Voil, jespre que vous aurez russi ce TP.
Vous pouvez bien sr complter notre calculatrice lmentaire en y rajoutant des notions
plus compliques, comme la racine carre ou la mise la puissance. ce propos, comme
les mthodes Math.Sqrt() ou Math.Pow(), il existe de nombreuses mthodes permettant de
faire des oprations de toutes sortes. Vous pouvez les consulter cette adresse dans la
documentation en ligne. (oui, je suis daccord, ce nest pas tous les jours que nous aurons
envie de calculer la tangente hyperbolique dun angle mais sait-on jamais ).
En tous cas, nhsitez pas faire des variations sur les diffrents TP que je vous ai
proposs, il est toujours intressant de sortir des sentiers battus.

Troisime partie

Le C#, un langage orient objet

Chapitre 19

Introduction la programmation oriente objet

Dans ce chapitre, nous allons essayer de dcrire ce quest la programmation oriente objet
(abrge souvent en POO). Je dis bien essayer , car pour tre compltement traite, la
POO ncessiterait quon lui ddie un ouvrage entier !
Nous allons tcher daller lessentiel et de rester proche de la pratique. Ce nest pas trs
grave si tous les concepts abstraits ne sont pas parfaitement apprhends : il est impossible
dapprendre la POO en deux heures. Cela ncessite une longue pratique, beaucoup
dempirisme et des approfondissements thoriques.
Ce qui est important ici, cest de comprendre les notions de base et de pouvoir les utiliser
dans de petits programmes.
Aprs cette mise au point, attaquons sans plus tarder la programmation oriente objet !

Quest-ce quun objet ?


Vous avez pu voir prcdemment que jai utilis de temps en temps le mot objet et que
le mot-cl new est apparu comme par magie Il a t difficile de ne pas trop en parler
et il est temps den savoir un peu plus.
Alors quest-ce quun objet ?
Si on prend le monde rel (si si, vous allez voir, vous connaissez), nous sommes
entours dobjets : une chaise, une table, une voiture, etc. Ces objets forment un tout.
Ils possdent des proprits (la chaise possde 4 pieds, elle est de couleur bleue, etc.).
Ces objets peuvent faire des actions (la voiture peut rouler, klaxonner, etc.).
Ils peuvent galement interagir entre eux (lobjet conducteur dmarre la voiture,
lobjet voiture fait tourner lobjet volant, etc.).
Il faut bien faire attention distinguer ce quest lobjet et ce quest la dfinition dun objet
La dfinition de lobjet (ou structure de lobjet) permet dindiquer ce qui compose un
objet, cest--dire quelles sont ses proprits, ses actions etc. Comme par exemple le fait
quune chaise ait des pieds ou quon puisse sasseoir dessus.
Par contre, lobjet chaise est bien concret. On peut donc avoir plusieurs objets chaises : on
parle galement dinstances. Les objets chaises, ce sont bien celles concrtes que lon voit
devant nous autour de lobjet table pour dmarrer une partie de belote.
On peut faire lanalogie avec notre dictionnaire qui nous dcrit ce quest une chaise. Le

dictionnaire dcrit en quoi consiste lobjet, et linstance de lobjet reprsente le concret


associ cette dfinition. Chaque objet a sa propre vie et est diffrent dun autre. Nous
pouvons avoir une chaise bleue, une autre rouge, une autre avec des roulettes, une casse

Bon, finalement la notion dobjet est plutt simple quand on la ramne ce quon connait
dj .
Sachant quun objet en programmation cest comme un objet du monde rel mais ce nest
pas forcment restreint au matriel. Un chien est un objet. Des concepts comme lamour
ou une ide sont galement des objets, tandis quon ne dirait pas cela dans le monde rel.
En conclusion :
La dfinition (ou structure) dun objet est un concept abstrait, comme une dfinition
dans le dictionnaire. Cette dfinition dcrit les caractristiques dun objet (la chaise a
des pieds, lhomme a des jambes, etc.). Cette dfinition est unique comme une
dfinition dans le dictionnaire.
Un objet ou une instance est la ralisation concrte de la structure de lobjet. On peut
avoir de multiples instances, comme les 100 voitures sur le parking devant chez moi.
Elles peuvent avoir des caractristiques diffrentes (une voiture bleue, une voiture
lectrique, une voiture 5 portes, etc.)

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

interne.

Hritage
Un autre lment important dans la programmation oriente objet que nous allons aborder
est lhritage.
Ah bon ? Les objets aussi peuvent mourir et transmettre leur patrimoine ?
Eh bien cest presque comme en droit, part que lobjet ne meurt pas et quil ny pas de
taxe sur lhritage.
Cest--dire quun objet dit pre peut transmettre certaines de ses caractristiques un
autre objet dit fils .
Pour cela, on pourra dfinir une relation dhritage entre eux. Sil y a une relation
dhritage entre un objet pre et un objet fils, alors lobjet fils hrite de lobjet pre. On
dit galement que lobjet fils est une spcialisation de lobjet pre ou quil drive de
lobjet pre.
En langage plus courant on peut galement dire que lobjet fils est une sorte dobjet
pre.
Des exemples !!
On dit souvent quun petit exemple vaut bien un long discours, alors prenons par exemple
lobjet chien et imaginons ses caractristiques tires du monde rel en utilisant
lhritage :
Lobjet chien est une sorte dobjet mammifre
Lobjet mammifre est une sorte dobjet animal
Lobjet animal est une sorte dobjet tre vivant
Chaque pre est un peu plus gnral que son fils. Et inversement, chaque fils est un peu
plus spcialis que son pre. Avec lexemple du dessus, un mammifre est un peu plus
gnral quun chien, ltre vivant tant encore plus gnral quun mammifre.
Il est possible pour un pre davoir plusieurs fils, par contre, linverse est impossible, un
fils ne peut pas avoir plusieurs pres. Et oui, cest triste mais cest comme a, cest le
rgne du pre clibataire avec plusieurs enfants charge !
Ainsi, un objet chat peut galement tre un fils de lobjet mammifre . Un objet
vgtal peut galement tre fils de lobjet tre vivant .
Ce quon peut reproduire sur le schma suivant. Chaque bulle reprsentant un objet et
chaque flche reprsente lhritage entre les objets.

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

vous comprendrez surement mieux grce la pratique.

Polymorphisme - Substitution
Polymorphisme

Le mot polymorphisme suggre quune chose peut prendre plusieurs formes. Sous ce
terme un peu barbare se cachent plusieurs notions de lorient objet qui sont souvent
sources derreurs. Je vais volontairement passer rapidement sur certains points qui ne vont
pas nous servir pour me concentrer sur ceux qui sont importants pour ce tutoriel. Jespre
que vous ne men voudrez pas et que vous maccorderez quelques drogations qui
pourraient dplaire aux puristes.
En fait, on peut dire quune manifestation du polymorphisme est la capacit pour un objet
de faire une mme action avec diffrents types dintervenants. Cest ce quon appelle le
polymorphisme ad hoc ou le polymorphisme paramtr . Par exemple, notre objet
voiture peut rouler sur la route, rouler sur lautoroute, rouler sur la terre si elle est quipe
de pneus adquats, rouler au fond de leau si elle est amphibie, etc
Concrtement ici, je fais interagir un objet voiture avec un objet autoroute ou un
objet terre par laction qui consiste rouler . Cela peut paraitre anodin dcrit
ainsi, mais nous verrons ce que cela implique avec le C#.
Substitution

La substitution est une autre manifestation du polymorphisme. Pas de rgime ou de


balance dans ce terme-l, il sagit simplement de la capacit dun objet fils redfinir des
caractristiques ou des actions dun objet pre.
Prenons par exemple un objet mammifre qui sait faire laction se dplacer . Les objets
qui drivent du mammifre peuvent potentiellement avoir se dplacer dune manire
diffrente. Par exemple, lobjet homme va se dplacer sur ses deux jambes et donc
diffremment de lobjet dauphin qui se dplacera grce ses nageoires ou bien encore
diffremment de lobjet homme accident qui va avoir besoin de bquilles pour saider
dans son dplacement.
Tous ces mammifres sont capables de se dplacer, mais chacun va le faire dune manire
diffrente. Ceci est donc possible grce la substitution qui permet de redfinir un
comportement hrit. Ainsi, chaque fils sera libre de rcrire son propre comportement, si
celui de son pre ne lui convient pas.

Interfaces
Un autre concept important de la programmation oriente objet est la notion dinterface.
Linterface est un contrat que sengage respecter un objet. Il indique en
gnral un comportement.

Prenons un exemple dans notre monde rel et connu : les prises de courant. Elles
fournissent de llectricit 220V avec deux trous et (souvent) une prise de terre. Peu
importe ce quil y a derrire, du courant alternatif de la centrale du coin, un
transformateur, quelquun qui pdale, nous saurons coup sr que nous pouvons
brancher nos appareils lectriques car ces prises sengagent nous fournir du courant
alternatif avec le branchement adquat.
Elles respectent le contrat ; elles sont branchables . Ce dernier terme est un peu barbare
et peut faire mal aux oreilles. Mais vous verrez que suffixer des interfaces par un able
est trs courant ( ) et permet dtre plus prcis sur la smantique de linterface. Able
est galement un suffixe qui fonctionne en anglais et les interfaces du framework .NET
finissent pour la plupart par able .
noter que les interfaces ne fournissent quun contrat, elles ne fournissent pas
dimplmentation cest--dire pas de code C#.
Les interfaces indiquent que les objets qui choisissent de respecter ce contrat auront
forcment telle action ou telle caractristique mais elles nindiquent pas comment faire,
cest--dire quelles nont pas de code C# associ. Chaque objet respectant cette interface
(on parle dobjet implmentant une interface) sera responsable de coder la fonctionnalit
associe au contrat.
Pour manipuler ces prises, nous pourrons utiliser cette interface en disant : allez hop,
tous les branchables, venez par ici, on a besoin de votre courant . Peu importe que lobjet
implmentant cette interface soit une prise murale, une prise relie une dynamo ou autre,
nous pourrons manipuler ces objets par leur interface et donc brancher nos prises
permettant dalimenter nos appareils.
Contrairement lhritage, un objet est capable dimplmenter plusieurs interfaces. Par
exemple, une pompe chaleur peut tre Chauffante et Refroidissante . Notez quen
franais, nous pourrons galement utiliser le suffixe ante . En anglais, nous aurons plus
souvent able .
Nous en avons termin avec la thorie sur les interfaces. Il est fort probable que vous ne
saisissiez pas encore tout lintrt des interfaces ou ce quelles sont exactement. Nous
allons y revenir avec des exemples concrets et vous verrez des utilisations des interfaces
dans le cadre du framework .NET qui vous claireront davantage.

quoi sert la programmation oriente objet ?


Nous avons dcrit plusieurs concepts de la programmation oriente objet mais nous
navons pas encore dit quoi elle allait nous servir.
En fait, on peut dire que la POO est une faon de dvelopper une application qui consiste
reprsenter (on dit galement modliser ) une application informatique sous la forme
dobjets, ayant des proprits et pouvant interagir entre eux. La modlisation oriente
objet est proche de la ralit ce qui fait quil sera relativement facile de modliser une
application de cette faon. De plus, les personnes non-techniques pourront comprendre et
ventuellement participer cette modlisation.
Cette faon de modliser les choses permet galement de dcouper une grosse application,
gnralement floue, en une multitude dobjets interagissant entre eux. Cela permet de
dcouper un gros problme en plus petits afin de le rsoudre plus facilement.

Utiliser une approche oriente objet amliore galement la maintenabilit. Plus le temps
passe et plus une application est difficile maintenir. Il devient difficile de corriger des
choses sans tout casser ou dajouter des fonctionnalits sans provoquer une rgression par
ailleurs. Lorient objet nous aide ici limiter la casse en proposant une approche o les
modifications internes un objet naffectent pas tout le reste du code, grce notamment
lencapsulation.
Un autre avantage de la POO est la rutilisabilit. Des objets peuvent tre rutiliss ou
mme tendus grce lhritage. Cest le cas par exemple de la bibliothque de classes du
framework .NET que nous avons dj utilise. Cette bibliothque nous fournit par
exemple tous les objets permettant de construire des applications graphiques. Pas besoin
de rinventer toute la mcanique pour grer des fentres dans une application, le
framework .NET sait dj faire tout a. Nous avons juste besoin dutiliser un objet
fentre , dans lequel nous pourrons mettre un objet bouton et un objet zone de texte
. Ces objets hritent tous des mmes comportements, comme le fait dtre cliquable ou
slectionnable, etc. De mme, des composants tout faits et prts lemploi peuvent tre
vendus par des entreprises tierces (systme de log, contrles utilisateurs amliors, etc ).
Il faut savoir que la POO, cest beaucoup plus que a et nous en verrons des subtilits plus
loin, mais comprendre ce quest un objet est globalement suffisant pour une grande partie
du tutoriel.
En rsum

Lapproche oriente objet permet de modliser son application sous la forme


dinteractions entre objets.
Les objets ont des proprits et peuvent faire des actions.
Ils masquent la complexit dune implmentation grce lencapsulation.
Les objets peuvent hriter de fonctionnalits dautres objets sil y a une relation
dhritage entre eux.

Chapitre 20

Crer son premier objet

Ah, enfin un peu de concret et surtout de code. Dans ce chapitre, nous allons appliquer les
notions que nous avons vues sur la programmation oriente objet pour continuer notre
apprentissage du C#.
Mme si vous navez pas encore apprhend exactement o la POO pouvait nous mener,
ce nest pas grave. Les notions saffineront au fur et mesure de la lecture du tutoriel. Il
est temps pour nous de commencer crer des objets, les faire hriter entre eux, etc.
Bref, jouer, grce ces concepts, avec le C#.
Vous verrez quavec un peu de pratique tout sclaircira ; vous saisirez lintrt de la POO
et comment tre efficace avec le C#.

Tous les types C# sont des objets


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

Les classes
Dans le chapitre prcdent, nous avons parl des objets mais nous avons galement parl
de la dfinition de lobjet, de sa structure. Eh bien, cest exactement a, une classe.

Une classe est une manire de reprsenter un objet. Le C# nous permet de crer
des classes.
Nous avons dj pu voir une classe dans le code que nous avons utilis prcdemment et
qui a t gnr par Visual C# Express, la classe Program. Nous ny avons pas fait trop
attention, mais voil peu prs quoi elle ressemblait :
class Program
{
static void Main(string[] args)
{
}
}

Cest elle qui contenait la mthode spciale Main() qui sert de point dentre
lapplication.
Nous pouvons dcouvrir avec des yeux neufs le mot cl class qui comme son nom le
suggre permet de dfinir une classe, cest--dire la structure dun objet.
Rappelez-vous, les objets peuvent avoir des caractristiques et faire des actions.
Ici, cest exactement ce quil se passe. Nous avons dfini la structure dun objet Program
qui contient une action : la mthode Main().
Vous aurez remarqu au passage que pour dfinir une classe, nous utilisons nouveau les
accolades permettant de crer un bloc de code qui dlimite la classe.
Passons sur cette classe particulire et lanons-nous dans la cration dune classe qui nous
servira crer des objets. Par exemple, une classe Voiture.
noter quil est possible de crer une classe plusieurs endroits dans le code, mais en
gnral, nous utiliserons un nouveau fichier, du mme nom que la classe, qui lui sera
entirement ddi.
Une rgle dcriture commune beaucoup de langage de programmation est que chaque
fichier doit avoir une seule classe.
Faisons un clic droit sur notre projet pour ajouter une nouvelle classe :

Visual C# express nous ouvre sa fentre permettant de faire lajout dun lment en se
positionnant sur llment Classe. Nous pourrons donner un nom cette classe : Voiture.

Vous remarquerez que les classes commencent en gnral par une majuscule.
Visual C# Express nous gnre le code suivant :
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace MaPremiereApplication
{
class Voiture
{
}
}

Nous retrouvons le mot-cl class suivi du nom de notre classe Voiture et les accolades
ouvrantes et fermantes permettant de dlimiter notre classe. Notons galement que cette
classe fait partie de lespace de nom MaPremireApplication qui est lespace de nom par
dfaut de notre projet. Pour nous simplifier le travail, Visual C# Express nous a galement
inclus quelques espaces de noms souvent utiliss.
Mais pour linstant cette classe ne fait pas grand-chose.
Comme cette classe est vide, elle ne possde ni proprits, ni actions. Nous ne pouvons
absolument rien faire avec part en crer une instance, cest--dire un objet. Cela se fait
grce lutilisation du mot-cl new. Nous y reviendrons plus en dtail plus tard, mais cela
donne :
static void Main(string[] args)
{
Voiture voitureNicolas = new Voiture();
Voiture voitureJeremie = new Voiture();
}

Nous avons cr deux instances de lobjet Voiture et nous les stockons dans les variable
voitureNicolas et voitureJeremie.
Si vous vous rappelez bien, nous aurions logiquement d crire :
MaPremiereApplication.Voiture voitureNicolas = new MaPremiereApplication.Voiture();

Ou alors positionner le using qui allait bien, permettant dinclure lespace de nom
MaPremiereApplication.
En fait, ici cest superflu vu que nous crons les objets depuis la mthode Main() qui fait
partie de la classe Program faisant partie du mme espace de nom que notre classe.

Les mthodes
Nous venons de crer notre objet Voiture mais nous ne pouvons pas en faire grand-chose.
Ce qui est bien dommage. a serait bien que notre voiture puisse klaxonner par exemple si
nous sommes bloqus dans des embouteillages. Bref, que notre voiture soit capable de
faire des actions. Qui dit action dit mthode .
Nous allons pouvoir dfinir des mthodes faisant partie de notre objet Voiture. Pour ce
faire, il suffit de crer une mthode, comme nous lavons dj vu, directement dans le
corps de la classe :
class Voiture
{
void Klaxonner()
{
Console.WriteLine("Pouet !");
}
}

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

Cela ressemble beaucoup ce que nous avons dj fait. En fait, nous avons dj utilis des
mthodes sur des objets. Rappelez-vous, nous avons utilis la mthode Add() permettant
dajouter une valeur une liste :
List<int> maListe = new List<int>();
maListe.Add(1);

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

Ce qui peut en fait scrire :


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

Nous crons un objet du type Random grce new puis nous appelons la mthode Next() qui
prend en paramtres les bornes du nombre alatoire que nous souhaitons obtenir (0 tant
inclus et 100 exclu). Puis nous stockons le rsultat dans un entier.
Et oui, nous avons manipul quelques objets sans le savoir
Revenons notre embouteillage et compilons le code nous permettant de faire klaxonner
notre voiture :
Voiture voitureNicolas = new Voiture();
voitureNicolas.Klaxonner();

Impossible de compiler, le compilateur nous indique lerreur suivante :


MaPremiereApplication.Voiture.Klaxonner()' est inaccessible en raison de son niveau de protection

Diantre ! Dj un premier chec dans notre apprentissage de lobjet !


Vous aurez devin grce au message derreur que la mthode Klaxonner() semble
inaccessible. Nous expliquerons un peu plus bas de quoi il sagit. Pour linstant, nous
allons juste prfixer notre mthode du mot-cl public, comme ceci :
class Voiture
{
public void Klaxonner()
{
Console.WriteLine("Pouet !");
}
}

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

Wahou, notre premire action dun objet.


Bien sr, ces mthodes peuvent galement avoir des paramtres et renvoyer un rsultat.
Par exemple :
class Voiture
{
public bool VitesseAutorisee(int vitesse)
{
if (vitesse > 90)
return false;

else
return true;
}
}

Cette mthode accepte une vitesse en paramtre et si elle est suprieure 90, alors la
vitesse nest pas autorise. Cette mthode pourrait galement scrire :
class Voiture
{
public bool VitesseAutorisee(int vitesse)
{
return vitesse <= 90;
}
}

En effet, nous souhaitons renvoyer faux si la vitesse est suprieure 90 et vrai si la vitesse
est infrieure ou gale.
Donc en fait, nous souhaitons renvoyer la valeur du rsultat de la comparaison
dinfriorit ou dgalit de la vitesse 90, cest--dire :
class Voiture
{
public bool VitesseAutorisee(int vitesse)
{
bool estVitesseAutorisee = vitesse <= 90;
return estVitesseAutorisee;
}
}

Ce que nous pouvons crire finalement en une seule ligne comme prcdemment.
Il est bien sr possible davoir plusieurs mthodes dans une mme classe et elles peuvent
sappeler entre elles, par exemple :
class Voiture
{
public bool VitesseAutorisee(int vitesse)
{
return vitesse <= 90;
}
public void Klaxonner()
{
if (!VitesseAutorisee(180))
Console.WriteLine("Pouet !");
}
}

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

Notion de visibilit
Ok, le mot-cl public nous a bien sauv la vie, mais quest-ce donc ?
En fait, je lai rapidement voqu et nous nous sommes bien rendu compte que sans ce
mot-cl, cest impossible de compiler car la mthode nest pas accessible.
Le mot-cl public sert indiquer que notre mthode peut tre accessible depuis dautres
classes ; en loccurrence dans notre exemple depuis la classe Program. Cest--dire que
sans ce mot-cl, il est impossible dautres objets dutiliser cette mthode.
Pour faire en sorte quune mthode soit inaccessible, nous pouvons utiliser le mot-cl

private. Ce mot-cl permet davoir une mthode qui nest accessible que depuis la classe

dans laquelle elle est dfinie.


Prenons lexemple suivant :
class Voiture
{
public bool Demarrer()
{
if (ClesSurLeContact())
{
DemarrerLeMoteur();
return true;
}
return false;
}
public void SortirDeLaVoiture()
{
if (ClesSurLeContact())
PrevenirLUtilisateur();
}
private bool ClesSurLeContact()
{
// faire quelque chose pour vrifier
return true;
}
private void DemarrerLeMoteur()
{
// faire quelque chose pour dmarrer le moteur
}
private void PrevenirLUtilisateur()
{
Console.WriteLine("Bip bip bip");
}
}

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

Description

public

Accs non restreint

protected

Accs depuis la mme classe ou depuis une classe drive

private

Accs uniquement depuis la mme classe

internal

Accs restreint la mme assembly

protected internal Accs restreint la mme assembly ou depuis une classe drive

Les visibilits qui vont le plus vous servir sont reprsentes par les mots-cls public et

private. Nous verrons que le mot-cl protected va servir un peu plus tard quand nous

parlerons dhritage. Notez quinternal pourra tre utilis une fois que nous aurons bien
maitris toutes les notions.
Ces mots-cls sont utilisables avec beaucoup dautres concepts. Nous avons utilis les
mthodes pour les illustrer mais ceci est galement valable pour les classes ou les
proprits que nous allons dcouvrir juste aprs.
Oui mais, au dbut, nous avons pu dclarer une classe sans prciser de
visibilit et pareil pour la premire mthode qui ne compilait pas cest
normal ?
Oui, il existe des visibilits par dfaut suivant les types dclars. Vous aurez compris par
exemple que la visibilit par dfaut dune mthode est prive si lon ne spcifie pas le mot
cl.
Pour viter tout risque et toute ambigit, il est recommand de toujours indiquer la
visibilit. Ce que nous ferons dsormais dans ce tutoriel, maintenant que nous savons de
quoi il sagit.

Les proprits
Des objets cest bien. Des actions sur ces objets, cest encore mieux. Il nous manque
encore les caractristiques des objets. Cest l quinterviennent les proprits.
Sans le savoir, vous avez dj utilis des proprits, par exemple dans le code suivant :
string[] jours = new string[] { "Lundi", "Mardi", "Mercredi", "Jeudi", "Vendredi", "Samedi",
"Dimanche" };
for (int i = 0; i < jours.Length; i++)
{
Console.WriteLine(jours[i]);
}

Dans la boucle, nous utilisons jours.Length. Nous utilisons en fait la proprit Length du
tableau jours , un tableau tant bien sr un objet.
Nous avons pu utiliser dautres proprits, par exemple dans linstruction suivante :
List<string> jours = new List<string> { "Lundi", "Mardi", "Mercredi", "Jeudi", "Vendredi", "Samedi",
"Dimanche" };
for (int i = 0; i < jours.Count; i++)
{
Console.WriteLine(jours[i]);
}

Ici, Count est la proprit de la liste jours .


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

Les variables membres :

Ici en fait, un objet peut avoir une caractristique sous la forme dune variable publique
qui fait partie de la classe. Pour ce faire, il suffit de dfinir simplement la variable
lintrieur des blocs de code dlimitant la classe et de lui donner la visibilit public.
Rajoutons par exemple une chaine de caractres permettant de stocker la couleur de la
voiture :
public class Voiture
{
public string Couleur;
}

Notez que jai rajout les visibilits pour la classe et pour la variable.
Grce ce code, la chaine de caractres Couleur est dsormais une caractristique de la
classe Voiture. Nous pourrons lutiliser en faisant suivre notre objet de loprateur .
suivi du nom de la variable. Ce qui donne :
Voiture voitureNicolas = new Voiture();
voitureNicolas.Couleur = "rouge";
Voiture voitureJeremie = new Voiture();
voitureJeremie.Couleur = "verte";

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

Les proprits :
Les proprits sont en fait des variables volues. Elles sont mi-chemin entre une
variable et une mthode.
Prenons cet exemple :
public class Voiture
{
private int vitessePrivee;
public int Vitesse
{
get
{
return vitessePrivee;
}
set
{
vitessePrivee = value;
}
}
}

Nous pouvons voir que nous dfinissons dans un premier temps une variable prive,
vitessePrivee de type entier. Comme prvu, cette variable est masque pour les
utilisateurs dobjets Voiture. Ainsi, le code suivant :
Voiture voitureNicolas = new Voiture();
voitureNicolas.vitessePrivee = 50;

provoquera lerreur de compilation dsormais bien connue :


MaPremiereApplication.Voiture.vitessePrivee' est inaccessible en raison de son niveau de protection

Par contre, nous en avons profit pour dfinir la proprit Vitesse, de type int. Pour ceci,
nous avons dfini une partie de la proprit avec le mot cl get suivi dun return
vitessePrivee. Et juste en dessous, nous avons utilis le mot cl set, suivi de
vitessePrivee = value.
Ce que nous avons fait, cest dfinir la possibilit de lire la proprit grce au mot cl get.
Ici, la lecture de la proprit nous renvoie la valeur de la variable prive. De la mme
faon, nous avons dfini la possibilit daffecter une valeur la proprit en utilisant le
mot cl set et en affectant la valeur la variable prive.
Les blocs de code dlimits par get et set se comportent un peu comme des mthodes,
elles ont un corps qui est dlimit par des accolades et dans le cas du get, elle doit
renvoyer une valeur du mme type que la proprit.
noter que dans le cas du set, value est un mot cl qui permet de dire : la valeur
que nous avons affecte la proprit .
Prenons lexemple suivant :
Voiture voitureNicolas = new Voiture();
voitureNicolas.Vitesse = 50;
Console.WriteLine(voitureNicolas.Vitesse);

La premire instruction instancie bien sr une voiture.


La deuxime instruction consiste appeler le bloc de code set de Vitesse qui met la valeur
50 dans la pseudo-variable value qui est stocke ensuite dans la variable prive.
Lorsque nous appelons la troisime instruction, nous lisons la valeur de la proprit et
pour ce faire, nous passons par le get qui nous renvoie la valeur de la variable prive.
Ok, cest super, mais dans ce cas, pourquoi passer par une proprit et pas par
une variable ? Mme sil parait que les variables ne doivent jamais tre
publiques
Eh bien parce que dans ce cas-l, la proprit peut faire un peu plus que simplement
renvoyer une valeur. Et aussi parce que nous masquons la structure interne de notre classe
ceux qui veulent lutiliser.
Nous pourrions trs bien envisager daller lire la vitesse dans les structures internes du
moteur, ou en faisant un calcul pouss par rapport au coefficient du vent et de lge du
capitaine (ou plus vraisemblablement en allant lire la valeur en base de donnes). Et ici,
nous pouvons tirer parti de la puissance des proprits pour masquer tout a lappelant
qui lui na besoin que dune vitesse.
Par exemple :
private int vitessePrivee;
public int Vitesse
{
get
{
int v = vitesseDesRoues * rayon * coefficient; // ce calcul est compltement farfelu !
MettreAJourLeCompteur();
AdapterLaVitesseDesEssuieGlaces();
return v;
}
set
{
// faire la mise jour des variables internes

MettreAJourLeCompteur();
AdapterLaVitesseDesEssuieGlaces();
}
}

En faisant tout a dans le bloc de code get, nous masquons les rouages de notre classe
lutilisateur. Lui, il na besoin que dobtenir la vitesse, sans sencombrer du compteur ou
des essuie-glaces.
Bien sr, la mme logique peut sadapter au bloc de code qui permet daffecter une valeur
la proprit, set.
Il est galement possible de rendre une proprit en lecture seule, cest--dire non
modifiable par les autres objets. Il pourra par exemple sembler bizarre de positionner une
valeur la vitesse alors quen fait, pour mettre jour la vitesse, il faut appeler la mthode
AppuyerPedale(double forceAppui).
Pour empcher les autres objets de pouvoir directement mettre jour la proprit Vitesse,
il suffit de ne pas dclarer le bloc de code set et de ne garder quun get. Par exemple :
public class Voiture
{
private int vitessePrivee;
public int Vitesse
{
get
{
// faire des calculs
return vitessePrivee;
}
}
}

Ce qui fait que si nous tentons daffecter une valeur la proprit Vitesse depuis une autre
classe, par exemple :
Voiture voitureNicolas = new Voiture();
voitureNicolas.Vitesse = 50;

Nous aurons lerreur de compilation suivante :


La proprit ou l'indexeur 'MaPremiereApplication.Voiture.Vitesse' ne peut pas tre assignil est en
lecture seule

Le compilateur nous indique donc trs justement quil est impossible de faire cette
affectation car la proprit est en lecture seule.
Il devient donc impossible pour un utilisateur de cette classe de modifier la vitesse de cette
faon.
De mme, il est possible de dfinir une proprit pour quelle soit accessible en criture
seule. Il suffit de dfinir uniquement le bloc de code set :
private double acceleration;
public double Acceleration
{
set
{
acceleration = value;
}
}

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


Console.WriteLine(voitureNicolas.Acceleration);

Nous aurons lerreur de compilation attendue :


La proprit ou l'indexeur 'MaPremiereApplication.Voiture.Acceleration' ne peut pas tre utilis dans

ce contexte, car il lui manque l'accesseur get

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

Les proprits auto-implmentes :


Les proprits auto-implmentes sont une fonctionnalit que nous allons beaucoup
utiliser. Il sagit de la dfinition dune proprit de manire trs simplifie qui va nous
servir dans la grande majorit des cas o nous aurons besoin dcrire des proprits. Dans
ce tutoriel, nous lutiliserons trs souvent.
Ainsi, le code suivant que nous avons dj vu :
private int vitesse;
public int Vitesse
{
get
{
return vitesse;
}
set
{
vitesse = value;
}
}

est un cas trs frquent dutilisation de proprits. Nous exposons ici une variable prive
travers les proprits get et set. Lcriture du dessus est quivalente la suivante :
public int Vitesse { get; set; }

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

Appuyez sur tab ou entre pour slectionner cet extrait de code et appuyez ensuite sur tab
pour que Visual C# gnre lextrait de code correspondant la proprit autoimplmente. Vous aurez :

Ici, int est surlign et vous pouvez, si vous le souhaitez, changer le type de la proprit,
par exemple string. Appuyez nouveau sur tab et Visual C# surligne le nom de la
proprit, que vous pouvez nouveau changer. Appuyez enfin sur entre pour terminer la
saisie et vous aurez une belle proprit auto-implmente :

Vous avez pu remarquer quen commenant taper prop , Visual C# express vous a
propos dautres extraits de code, par exemple propfull qui va gnrer la proprit
complte telle quon la vue un peu plus haut.
Dautres extraits de code existent, comme propg qui permet de dfinir une proprit en
lecture seule.
En effet, comme au chapitre prcdent, il est possible de dfinir des proprits autoimplmentes en lecture seule ou en criture seule avec une criture simplifie. Dans le
cas des proprits auto-implmentes, il y a cependant une subtilit.
Pour avoir de la lecture seule, nous devons indiquer que laffectation est prive, comme on

peut le voir en utilisant lextrait de code propg . Visual C# express nous gnre le bout
de code suivant :
public int Vitesse { get; private set; }

En positionnant une proprit dcriture en prive, Visual C# express autorise la classe


Voiture modifier la valeur de Vitesse, que ce soit par une mthode ou par une proprit.
A noter que si nous navions pas mis private set et que nous avions simplement
supprim le set, alors la compilation aurait t impossible. En effet, il savre difficile
dexploiter une proprit auto-implmente en lecture alors que nous navons pas la
possibilit de lui donner une valeur. Ou inversement.
Alors, pourquoi nous avoir parl de la possibilit de compltement supprimer la
lecture ou lcriture ? Ce nest pas plus intressant de toujours mettre la
proprit en private ?
Si cest une proprit auto-implmente, videmment que si. Par contre, si nous
nutilisons pas une proprit auto-implmente et que nous utilisons une variable membre
pour sauvegarder la valeur de la proprit, nous pourrons ventuellement modifier ou lire
la valeur de la variable partir dune mthode ou dune autre proprit.
Bref, maintenant que vous connaissez les deux syntaxes, vous pourrez utiliser la plus
adapte votre besoin.
Voil pour les proprits.
noter que quand nous avons beaucoup de proprits initialiser sur un objet, nous
pouvons utiliser une syntaxe plus concise. Par exemple, les instructions suivantes :
Voiture voitureNicolas = new Voiture();
voitureNicolas.Couleur = "Bleue";
voitureNicolas.Marque = "Peugeot";
voitureNicolas.Vitesse = 50;

sont quivalentes linstruction :


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

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

On utilise des classes pour reprsenter quasiment la plupart des objets.


On utilise le mot-cl class pour dfinir une classe et le mot-cl new pour linstancier.
Une classe peut possder des caractristiques (les proprits) et peut faire des actions
(les mthodes).

Chapitre 21

Manipuler des objets

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

Le constructeur
Le constructeur est une mthode particulire de lobjet qui permet de faire des choses au
moment de la cration dun objet, cest--dire au moment o nous utilisons le mot-cl new.
Il est en gnral utilis pour initialiser des valeurs par dfaut dun objet.
Par exemple, si nous voulons que lors de la cration dune voiture, elle ait
automatiquement une vitesse, nous pouvons faire :
public class Voiture
{
public int Vitesse { get; set; }
public Voiture()
{
Vitesse = 5;
}
}

Le constructeur est en fait une mthode spciale qui a le mme nom que la classe et qui ne
possde pas de type de retour. Elle est appele lors de la cration de lobjet, avec new.
Pour illustrer le comportement du constructeur, ajoutons une mthode Rouler notre
classe, de cette faon :
public class Voiture
{
public int Vitesse { get; set; }
public Voiture()
{
Vitesse = 5;
}
public void Rouler()
{
Console.WriteLine("Je roule " + Vitesse + " km/h");

}
}

Que nous pourrons appeler ainsi :


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

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

Le constructeur est la premire mthode tre appele lors de la cration dun objet.
Cest lendroit appropri pour faire des initialisations, ou pour charger des valeurs, etc.
Le constructeur que nous avons vu est ce quon appelle le constructeur par dfaut, car il
ne possde pas de paramtres.
Il est possible de passer des paramtres un constructeur, pour initialiser des variables de
notre classe avec des valeurs. Pour ce faire, nous devons dclarer un constructeur avec un
paramtre ; par exemple :
public class Voiture
{
public int Vitesse { get; set; }
public Voiture()
{
Vitesse = 5;
}
public Voiture(int vitesse)
{
Vitesse = vitesse;
}
public void Rouler()
{
Console.WriteLine("Je roule " + Vitesse + " km/h");
}
}

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

faon :
Voiture voitureNicolas = new Voiture(20);

Aprs ceci, la variable voitureNicolas aura une vitesse de 20.


Bien sr, nous pourrions faire la mme chose sans utiliser de constructeur, en affectant une
valeur la proprit aprs avoir instanci lobjet. Ce qui fonctionnerait tout fait.
Sauf quil ne se passe pas exactement la mme chose. Le constructeur est vraiment appel
en premier, ds quon utilise new pour crer un objet. Les proprits sont affectes
fatalement aprs, donc tout dpend de ce que lon veut faire.
Donc, oui, on pourrait affecter une valeur des proprits pour faire ce genre
dinitialisation juste aprs avoir instanci notre objet mais cela nous oblige crire une
instruction supplmentaire qui pourrait ne pas paraitre vidente ou obligatoire.
Une chose est sre avec le constructeur, cest que nous sommes obligs dy passer et ceci,
peu importe la faon dont on utilise la classe.
Linitialisation devient donc obligatoire, on vite le risque quune proprit soit nulle.
noter quil est possible de cumuler les constructeurs tant quils ont chacun des
paramtres diffrents. Dans notre exemple, nous pourrons donc crer des voitures de deux
faons diffrentes :
Voiture voitureNicolas = new Voiture(); // vitesse vaut 5
Voiture voitureJeremie = new Voiture(20); // vitesse vaut 20

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

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

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

voitureNicolas.Vitesse = 5;

provoqueront lerreur de compilation suivante :


Utilisation d'une variable locale non assigne 'voitureNicolas'

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

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

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

vous naurez pas derreur la compilation, par contre vous aurez :

Comme nous lavons dj vu, le programme nous affiche une exception ; nous avons dit
que ctait simplement une erreur qui faisait planter notre programme.

Pas bien ! Surtout que cela se passe au moment de lexcution. Nous perdons toute
crdibilit !
Ici, le programme nous dit que la rfrence dun objet nest pas dfinie une instance dun
objet.
Concrtement, cela veut dire que nous essayons de travailler sur un objet null.
Pour viter ce genre derreur lexcution, il faut imprativement instancier ses objets, en
utilisant loprateur new, comme nous lavons dj vu.
Il nest cependant pas toujours pertinent dinstancier un objet dont on pourrait ne pas avoir
besoin. Le C# nous offre donc la possibilit de tester la nullit dun objet. Il suffit
dutiliser loprateur de comparaison == en comparant un objet au mot-cl null, par
exemple :
string prenom = "Nicolas";
Voiture voiture = null;
if (prenom == "Nicolas")
voiture = new Voiture { Vitesse = 50 };
if (voiture == null)
{
Console.WriteLine("Vous n'avez pas de voiture");
}
else
{
voiture.Rouler();
}

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

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

// faire autre chose


}

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

Le mot-cl this
Lorsque nous crivons le code dune classe, le mot-cl this reprsente lobjet dans lequel
nous nous trouvons. Il permet de clarifier ventuellement le code, mais Il est gnralement
facultatif.
Ainsi, pour accder une variable de la classe ou ventuellement une mthode, nous
pouvons les prfixer par this. . Par exemple, nous pourrions crire notre classe de
cette faon :
public class Voiture
{
public int Vitesse { get; set; }
public string Couleur { get; set; }
public Voiture()
{
this.Vitesse = 5;
}
public void Rouler()
{
Console.WriteLine("Je roule " + this.Vitesse + " km/h");
}
public void Accelerer(int acceleration)
{
this.Vitesse += acceleration;
this.Rouler();
}
}

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

Vous remarquerez que le paramtre de la mthode ChangerVitesse() et la proprit ou

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

Les classes possdent une mthode particulire, appele linstanciation de lobjet :


le constructeur.
Une instance dune classe peut tre initialise avec une valeur nulle grce au mot-cl
null.
Le mot-cl this reprsente lobjet en cours de la classe.

Chapitre 22

La POO et le C#

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

Des types, des objets, type valeur et type rfrence


Ok, je sais maintenant crer des objets, mais je me rappelle quau dbut du
tutoriel, nous avons manipul des int et des string et que tu as appel a des
types ; et aprs, tu nous dis que tout est objet Tu serais pas en train de
raconter nimporte quoi ?
Eh bien non, perspicace lecteur !
Prcisons un peu maintenant que vous avez de meilleures connaissances. Jai bien dit que
tout tait objet, je le maintiens, mme sous la torture. Cest--dire que mme les types
simples comme les entiers int ou les chanes de caractres sont des objets.
Jen veux pour preuve ce simple exemple :
int a = 10;
string chaine = a.ToString();
chaine = "abc" + chaine;
string chaineEnMajuscule = chaine.ToUpper();
Console.WriteLine(chaineEnMajuscule);
Console.WriteLine(chaineEnMajuscule.Length);

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

String.

Mais alors, pourquoi utiliser string et non pas String ?


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

sont quivalentes celles-ci :


System.Int32 a = 10;
System.String chaine = "abc";

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

Ici, la variable contient vraiment lentier 5. Alors que pour linstanciation suivante :
Voiture voitureNicolas = new Voiture();

La variable voitureNicolas contient une rfrence vers lobjet en mmoire.


On peut imaginer que le type rfrence est un peu comme si on disait que ma maison se
situe 9 rue des bois . Ladresse a t crite sur un bout de papier et rfrence ma maison
qui ne se situe bien sr pas au mme endroit que le bout de papier. Si je veux vraiment
voir lobjet maison, il va falloir que jaille voir o cest indiqu sur le bout de papier. Cest
ce que fait le type rfrence, il va voir en mmoire ce quil y a vraiment dans lobjet.
Alors que le type valeur pourrait ressembler un billet de banque par exemple. Je peux me
balader avec, cest marqu 500 dessus (oui, je suis riche !) et je peux payer directement
avec sans que le fait de donner le billet implique daller chercher le contenu la banque.
Le type valeur contient la vraie valeur qui en gnral est assez petite et facile
stocker.
Le type rfrence ne contient quun lien vers un plus gros objet stock ailleurs.
Cette manire diffrente de grer les types et les objets implique plusieurs choses.
Dans la mesure o les types valeur possdent vraiment la valeur de ce quon y stocke, une
copie de la valeur est effectue chaque fois que lon fait une affectation. Cela est

possible car ces types sont relativement petits et optimiss. Cela savre impossible pour
un objet qui est trop gros et trop complexe.
Cest un peu compliqu de copier toute ma maison alors que cest un peu plus simple de
recopier ce quil y a sur le bout de papier .
Ainsi, lexemple suivant :
int a = 5;
int b = a;
b = 6;
Console.WriteLine(a);
Console.WriteLine(b);

affichera les valeurs 5 puis 6. Ce qui est le rsultat que lon attend.
En effet, la variable a a t initialise 5.
On a ensuite affect a b . La valeur 5 sest copie (duplique) dans la
variable b .
Puis nous avons affect 6 b .
Ce qui parait tout fait logique.
Par contre, lexemple suivant :
Voiture voitureNicolas = new Voiture();
voitureNicolas.Couleur = "Bleue";
Voiture voitureJeremie = voitureNicolas;
voitureJeremie.Couleur = "Verte";
Console.WriteLine(voitureNicolas.Couleur);
Console.WriteLine(voitureJeremie.Couleur);

affichera verte et verte.


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

Hritage
Nous avons vu pour linstant la thorie de lhritage. Que les objets chiens hritaient des
comportements des objets Animaux, que les labradors hritaient des comportements des
chiens, etc.
Passons maintenant la pratique et crons une classe Animal et une classe Chien qui en
hrite. Nous allons crer des classes relativement courtes et nous nous limiterons dans le
nombre dactions ou de proprits de celles-ci. Par exemple, nous pourrions imaginer que
la classe Animal possde une proprit NombreDePattes qui est un entier et une mthode
Respirer qui affiche le dtail de laction. Ce qui donne :
public class Animal
{
public int NombreDePattes { get; set; }
public void Respirer()
{
Console.WriteLine("Je respire");
}
}

La classe Chien drive de la classe Animal et peut donc hriter de certains de ses
comportements. En loccurrence, la classe Chien hritera de tout ce qui est public ou
protg, identifis comme vous le savez dsormais par les mots cls public et protected.
Le chien sait galement faire quelque chose qui lui est propre, savoir aboyer. Il
possdera donc une mthode supplmentaire. Ce qui donne :
public class Chien : Animal
{
public void Aboyer()
{
Console.WriteLine("Wouaf !");
}
}

On reprsente la notion dhritage en ajoutant aprs la classe le caractre : suivi de la


classe mre.
Ici, nous avons dfini une classe publique Chien qui hrite de la classe Animal.
Nous pouvons ds prsent crer des objets Animal et des objets Chien, par exemple :
Animal animal = new Animal { NombreDePattes = 4 };
animal.Respirer();
Console.WriteLine();
Chien chien = new Chien { NombreDePattes = 4 };
chien.Respirer();
chien.Aboyer();

Si nous excutons ce code, nous aurons :

Nous nous rendons bien compte que lobjet Chien, bien que nayant pas dfini la proprit
NombreDePattes ou la mthode Respirer() dans le corps de sa classe, est capable davoir
des pattes et de faire laction respirer.
Il a hrit ces comportements de lobjet Animal, en tous cas, ceux qui sont publiques.
Rajoutons deux variables membres de la classe Animal :
public class Animal
{
private bool estVivant;
public int age;
public int NombreDePattes { get; set; }
public void Respirer()
{
Console.WriteLine("Je respire");
}
}

Lentier age est public alors que le boolen estVivant est priv. Si nous tentons de les
utiliser depuis la classe fille Chien, comme ci-dessous :
public class Chien : Animal
{
public void Aboyer()
{
Console.WriteLine("Wouaf !");
}
public void Vieillir()
{
age++;
}
public void Naissance()
{
age = 0;
estVivant = true; // Erreur > 'MaPremiereApplication.Animal.estVivant'
// est inaccessible en raison de son niveau de protection
}
}

Nous voyons quil est tout fait possible dutiliser la variable age depuis la mthode
Vieillir() alors que lutilisation du boolen estVivant provoque une erreur de
compilation.

Vous avez bien compris que celui-ci tait inaccessible car il est dfini comme membre
priv. Pour lutiliser, on pourra le rendre public par exemple.
Il existe par contre un autre mot cl qui permet de rentre des variables/proprits/mthodes
inaccessibles depuis un autre objet tout en le rendant accessible depuis des classes filles. Il
sagit du mot cl protected.
Si nous lutilisons la place de private pour dfinir la visibilit du boolen estVivant,
nous pourrons nous rendre compte que la classe Chien peut dsormais compiler :
public class Animal
{
protected bool estVivant;
[ Extrait de code supprim ]
}
public class Chien : Animal
{
[ Extrait de code supprim ]
public void Naissance()
{
age = 0;
estVivant = true; // compilation OK
}
}

Par contre, cette variable est toujours inaccessible depuis dautres classes, comme lest
galement une variable prive. Dans notre classe Program, linstruction suivante :
chien.estVivant = true;

provoquera lerreur de compilation que nous connaissons dsormais bien :


'MaPremiereApplication.Animal.estVivant' est inaccessible en raison de son niveau de protection

Le mot cl protected prend tout son intrt ds que nous avons faire avec lhritage.
Nous verrons un peu plus bas dautres exemples de ce mot cl.
Nous avons dit dans lintroduction quun objet B qui drive de lobjet A est une sorte
dobjet A. Dans notre exemple du dessus, le Chien est une sorte dAnimal.
Cela veut dire que nous pouvons utiliser un chien en tant quanimal. Par exemple, le code
suivant :
Animal animal = new Chien { NombreDePattes = 4 };

est tout fait correct. Nous disons que notre variable animal, de type Animal est une
instance de Chien.
Avec cette faon dcrire, nous avons rellement instanci un objet Chien mais celui-ci
sera trait en tant quAnimal. Cela veut dire quil sera capable de Respirer() et davoir des
pattes. Par contre, mme si en vrai, notre objet serait capable daboyer, le fait quil soit
manipul en tant quAnimal nous empche de pouvoir le faire Aboyer.
Cela veut dire que le code suivant :
Animal animal = new Chien { NombreDePattes = 4 };
animal.Respirer();
animal.Aboyer(); // erreur de compilation

provoquera une erreur de compilation pour indiquer que la classe Animal ne contient
aucune dfinition pour la mthode Aboyer(). Ce qui est normal, un animal ne sait pas
forcment aboyer

Quel est lintrt alors dutiliser le chien en tant quanimal ?


Bonne question.
Pour y rpondre, nous allons enrichir notre classe Animal, garder notre classe Chien et
crer une classe Chat qui hrite galement dAnimal. Ce pourrait tre :
public class Animal
{
protected string prenom;
public void Respirer()
{
Console.WriteLine("Je suis " + prenom + " et je respire");
}
}
public class Chien : Animal
{
public Chien(string prenomDuChien)
{
prenom = prenomDuChien;
}
public void Aboyer()
{
Console.WriteLine("Wouaf !");
}
}
public class Chat : Animal
{
public Chat(string prenomDuChat)
{
prenom = prenomDuChat;
}
public void Miauler()
{
Console.WriteLine("Miaou");
}
}

Nous forons les chiens et les chats avoir un nom, hrit de la classe Animal, grce au
constructeur afin de pouvoir les identifier facilement.
Le chat garde le mme principe que le chien, sauf que nous avons une mthode Miauler()
la place de la mthode Aboyer() Ce qui est somme toute logique.
Lide est de pouvoir utiliser nos chiens et nos chats ensemble comme des animaux, par
exemple en utilisant une liste.
Pour illustrer ce fonctionnement, donnons vie quelques chiens et quelques chats grce
nos pouvoirs de dveloppeur et mettons-les dans une liste :
List<Animal> animaux = new List<Animal>();
Animal milou = new Chien("Milou");
Animal dingo = new Chien("Dingo");
Animal idefix = new Chien("Idfix");
Animal tom = new Chat("Tom");
Animal felix = new Chat("Flix");
animaux.Add(milou);
animaux.Add(dingo);
animaux.Add(idefix);
animaux.Add(tom);
animaux.Add(felix);

Nous avons dans un premier temps instanci une liste danimaux laquelle nous avons
rajout 3 chiens et 2 chats, chacun tant considr comme un animal puisquils sont tous

des sortes danimaux, grce lhritage.


Maintenant, nous navons plus que des animaux dans la liste.
Il sera donc possible de les faire tous respirer en une simple boucle :
foreach (Animal animal in animaux)
{
animal.Respirer();
}

ce qui donnera :

Et voil, cest super simple.


Imaginez le bonheur de No sur son arche quand il a compris que grce la POO, il
pouvait faire respirer tous les animaux en une seule boucle ! Quel travail conomis.
Peu importe ce quil y a dans la liste, des chiens, des chats, des hamsters, nous savons que
ce sont tous des animaux et quils savent tous respirer.
Vous avez sans doute remarqu que nous faisons la mme chose dans le constructeur de la
classe Chien et dans celui de la classe Chat. Deux fois la mme chose Ce nest pas
terrible. Peut-tre y a-t-il un moyen de factoriser tout a ?
Effectivement, il est possible galement dcrire nos classes de cette faon :
public class Animal
{
protected string prenom;
public Animal(string prenomAnimal)
{
prenom = prenomAnimal;
}
public void Respirer()
{
Console.WriteLine("Je suis " + prenom + " et je respire");
}
}
public class Chien : Animal
{
public Chien(string prenomDuChien) : base(prenomDuChien)
{
}
public void Aboyer()

{
Console.WriteLine("Wouaf !");
}
}
public class Chat : Animal
{
public Chat(string prenomDuChat) : base(prenomDuChat)
{
}
public void Miauler()
{
Console.WriteLine("Miaou");
}
}

Quest-ce qui change ?


Eh bien la classe Animal possde un constructeur qui prend en paramtre un prnom et qui
le stocke dans sa variable prive. Cest elle qui fait le travail dinitialisation.
Il devient alors possible pour les constructeurs des classes filles dappeler le constructeur
de la classe mre afin de faire laffectation du prnom. Pour cela, on utilise les deux points
suivis du mot cl base qui signifie appelle-moi le constructeur de la classe du dessus
auquel nous passons la variable en paramtres.
Avec cette criture un peu barbare, il devient possible de factoriser des initialisations qui
ont un sens pour toutes les classes filles. Dans notre cas, je veux que tous les objets qui
drivent dAnimal puissent facilement dfinir un prnom.
Il faut aussi savoir que si nous appelons le constructeur par dfaut dune classe qui
nappelle pas explicitement un constructeur spcialis dune classe mre, alors celui-ci
appellera automatiquement le constructeur par dfaut de la classe dont il hrite. Modifions
nouveau nos classes pour avoir :
public class Animal
{
protected string prenom;
public Animal()
{
prenom = "Marcel";
}
public void Respirer()
{
Console.WriteLine("Je suis " + prenom + " et je respire");
}
}
public class Chien : Animal
{
public void Aboyer()
{
Console.WriteLine("Wouaf !");
}
}
public class Chat : Animal
{
public Chat(string prenomDuChat)
{
prenom = prenomDuChat;
}
public void Miauler()
{
Console.WriteLine("Miaou");
}
}

Ici, la classe Animal met un prnom par dfaut dans son constructeur. Le chien na pas de
constructeur et le chat en a un qui accepte un paramtre.
Il est donc possible de crer un Chien sans quil ait de prnom mais il est obligatoire den
dfinir un pour le chat. Sauf que lorsque nous instancierons notre objet chien, il appellera
automatiquement le constructeur de la classe mre et tous nos chiens sappelleront Marcel
:
static void Main(string[] args)
{
List<Animal> animaux = new List<Animal>();
Animal chien = new Chien();
Animal tom = new Chat("Tom");
Animal felix = new Chat("Flix");
animaux.Add(chien);
animaux.Add(tom);
animaux.Add(felix);
foreach (Animal animal in animaux)
{
animal.Respirer();
}
}

Ce qui donne :

Il est galement possible dappeler un constructeur partir dun autre constructeur.


Prenons lexemple suivant :
public class Voiture
{
private int vitesse;
public Voiture(int vitesseVoiture)
{
vitesse = vitesseVoiture;
}
}

Si nous souhaitons rajouter un constructeur par dfaut qui initialise la vitesse 10 par
exemple, nous pourrons faire :
public class Voiture
{
private int vitesse;

public Voiture()
{
vitesse = 10;
}
public Voiture(int vitesseVoiture)
{
vitesse = vitesseVoiture;
}
}

Ou encore :
public class Voiture
{
private int vitesse;
public Voiture() : this(10)
{
}
public Voiture(int vitesseVoiture)
{
vitesse = vitesseVoiture;
}
}

Ici, lutilisation du mot cl this, suivi dun entier permet dappeler le constructeur qui
possde un paramtre entier au dbut du constructeur par dfaut.
Inversement, nous pouvons appeler le constructeur par dfaut dune classe depuis un
constructeur possdant des paramtres afin de pouvoir bnficier des initialisations de
celui-ci :
public class Voiture
{
private int vitesse;
private string couleur;
public Voiture()
{
vitesse = 10;
}
public Voiture(string couleurVoiture) : this()
{
couleur = couleurVoiture;
}
}

Puisque nous sommes parler dhritage, il faut savoir que tous les objets que nous crons
ou qui sont disponibles dans le framework .NET hritent dun objet de base. On parle en
gnral dun super objet . Lintrt de driver dun tel objet est de permettre tous les
objets davoir certains comportements en commun, mais galement de pouvoir
ventuellement tous les traiter en tant quobjet.
Notre super objet est reprsent par la classe Object qui dfinit plusieurs mthodes. Vous
les avez dj vues si vous avez regard dans la compltion automatique aprs avoir cr
un objet.
Prenons une classe toute vide, par exemple :
public class ObjetVide
{
}

Si nous instancions cet objet et que nous souhaitons lutiliser, nous verrons que la
compltion automatique nous propose des mthodes que nous navons jamais cres :

Nous voyons plusieurs mthodes, comme la mthode Equals ou GetHashCode ou GetType


ou encore ToString.
Comme vous lavez compris, ce sont des mthodes qui sont dfinies dans la classe Object.
La mthode ToString par exemple permet dobtenir une reprsentation de lobjet sous la
forme dune chaine de caractres.
Cest une mthode qui va souvent nous servir, nous y reviendrons un peu plus tard.
Ce super-objet est du type Object, mais on utilise gnralement son alias object.
Ainsi, il est possible dutiliser tous nos objets comme des object et ainsi utiliser les
mthodes qui sont dfinies sur la classe Object. Ce qui nous permet de faire :
static void Main(string[] args)
{
ObjetVide monObjetVide = new ObjetVide();
Chien chien = new Chien();
int age = 30;
string prenom = "Nicolas";
AfficherRepresentation(monObjetVide);
AfficherRepresentation(chien);
AfficherRepresentation(age);
AfficherRepresentation(prenom);
}
private static void AfficherRepresentation(object monObjetVide)
{
Console.WriteLine(monObjetVide.ToString());
}

Ce qui donne :

Comme indiqu, la mthode ToString() permet dafficher la reprsentation par dfaut


dun objet. Vous aurez remarqu quil y a une diffrence suivant ce que nous passons. En
effet, la reprsentation par dfaut des types rfrence correspond au nom du type, savoir
son espace de nom suivi du nom de sa classe. Pour ce qui est des types valeur, il contient
en gnral la valeur du type, lexception des structures que nous navons pas encore vues
et que nous aborderons un peu plus loin.
Lintrt dans cet exemple de code est de voir que nous pouvons manipuler tout comme un
object. Dune manire gnrale, vous aurez peu loccasion de traiter vos objets en tant
quobject car il est vraiment plus intressant de profiter pleinement du type, lobject tant
peu utilisable.
Notez que lhritage de object est automatique. Nul besoin dutiliser la syntaxe
dhritage que nous avons dj vue.
Jen profite maintenant que vous connaissez la mthode ToString() pour parler dun point
qui a peut-tre titill vos cerveaux.
Dans la premire partie, nous avions fait quelque chose du genre :
int vitesse = 20;
string chaine = "La vitesse est " + vitesse + " km/h";

La variable vitesse est un entier. La chane La vitesse est est une chaine de caractres.
Nous essayons dajouter un entier une chane alors que jai dit quils ntaient pas
compatibles entre eux ! Et pourtant cela fonctionne.
Effectivement, cest bizarre. Nous concatnons une chane un entier avec loprateur
+ et nous concatnons encore une chane.
Et si je fais linverse :
int vitesse = 20 + "40";

cela provoque une erreur de compilation. Cest logique, on ne peut pas ajouter un entier et
une chane de caractres. Alors pourquoi cela fonctionne dans lautre sens ?
Ce quil se passe en fait dans linstruction :
string chaine = "La vitesse est " + vitesse + " km/h";

cest que le compilateur se rend compte que nous concatnons une chane avec un autre
objet, peu importe que ce soit un entier ou un objet complexe. Alors pour que a
fonctionne, il demande une reprsentation de lobjet sous la forme dune chane de
caractres. Nous avons vu que ceci se faisait en appelant la mthode ToString() qui est
hrite de lobjet racine Object.
Linstruction est donc quivalente :
string chaine = "La vitesse est " + vitesse.ToString() + " km/h";

Dans le cas dun type valeur comme un entier, la mthode ToString() renvoie la
reprsentation interne de la valeur, savoir 20. Dans le cas dun objet complexe, elle
aurait renvoy le nom du type de lobjet.
Avant de terminer, il est important dindiquer que le C# nautorise pas lhritage multiple.
Ainsi, si nous possdons une classe Carnivore et une classe EtreVivant, il ne sera pas
possible de faire hriter directement un objet Homme de lobjet Carnivore et de lobjet
EtreVivant.
Ainsi, le code suivant :
public class Carnivore
{
}
public class EtreVivant
{
}
public class Homme : Carnivore, EtreVivant
{
}

provoquera lerreur de compilation suivante :


La classe 'MaPremiereApplication.Homme' ne peut pas avoir plusieurs classes de base :
'MaPremiereApplication.Carnivore' et 'EtreVivant'

Il est impossible de driver de deux objets en mme temps.


Si par contre, cela est pertinent, nous pourrons faire un hritage en cascade afin que
Carnivore drive de EtreVivant et que Homme drive de Carnivore :
public class Carnivore : EtreVivant
{
}
public class EtreVivant
{
}
public class Homme : Carnivore
{
}

Cependant, il nest pas toujours pertinent doprer de la sorte. Notre Homme pourrait tre
la fois Carnivore et Frugivore, cependant cela na pas de sens quun carnivore soit
galement frugivore, ou linverse.
Oui mais tu as dit que chaque objet drivait du super objet Object, sil drive
dune autre classe comme un chien drive dun animal, a fait bien deux classes
dont il drive
Effectivement, mais dans ce cas-l, ce nest pas pareil. Comme il est automatique de

driver de object, cest comme si on avait le chien qui hrite de animal qui hrite luimme de object. Le C# est assez malin pour a.

Substitution
Nous avons vu juste avant lutilisation de la mthode ToString() qui permet dobtenir la
reprsentation dun objet sous forme de chaine de caractres. En loccurrence, vous
conviendrez avec moi que la reprsentation de notre classe Chien nest pas
particulirement exploitable. Le nom du type cest bien, mais ce nest pas trs parlant.
a serait pas mal que quand nous demandons dafficher un chien, nous obtenions le nom
du chien, vous ne trouvez pas ?
Cest l quintervient la substitution.
Nous en avons parl dans lintroduction la POO, la substitution permet de redfinir un
comportement dont lobjet a hrit afin quil corresponde aux besoins de lobjet fils.
Typiquement, ici, la mthode ToString() du super-objet ne nous convient pas et dans le
cas de notre chien, nous souhaitons la redfinir, en crire une nouvelle version.
Pour cet exemple, simplifions notre classe Chien afin quelle nait quune proprit pour
stocker son prnom :
public class Chien
{
public string Prenom { get; set; }
}

Pour redfinir la mthode ToString() nous allons devoir utiliser le mot cl override qui
signifie que nous souhaitons substituer la mthode existante afin de remplacer son
comportement, ce que nous pourrons crire en C# avec :
public class Chien
{
public string Prenom { get; set; }
public override string ToString()
{
return "Je suis un chien et je m'appelle " + Prenom;
}
}

Le mot cl override se met avant le type de retour de la mthode, comme on peut le voir
ci-dessus.
Si nous appelons dsormais la mthode ToString de notre objet Chien :
Chien chien = new Chien { Prenom = "Max" };
Console.WriteLine(chien.ToString());

notre programme va utiliser la nouvelle version de la mthode ToString() et nous aurons :

Et voil un bon moyen dutiliser la substitution, la reprsentation de notre objet est quand
mme plus parlante.
Adaptons dsormais cet exemple nos classes.
Pour montrer comment faire, reprenons notre classe Chien qui possde une mthode
Aboyer() :
public class Chien
{
public void Aboyer()
{
Console.WriteLine("Wouaf !");
}
}

Nous pourrions imaginer crer une classe ChienMuet qui drive de la classe Chien et qui
hrite donc de ses comportements.
Mais, que penser dun chien muet qui serait capable daboyer ? Cela na pas de sens !
Il faut donc redfinir cette fichue mthode.
Utilisons alors le mot cl override comme nous lavons vu pour obtenir :
public class ChienMuet : Chien
{
public override void Aboyer()
{
Console.WriteLine("...");
}
}

Crons un chien muet puis faisons-le aboyer, cela donne :


ChienMuet pauvreChien = new ChienMuet();
pauvreChien.Aboyer();

Sauf que nous rencontrons un problme. Si nous tentons de compiler ce code, Visual C#
express nous gnre une erreur de compilation :
'MaPremiereApplication.Program.ChienMuet.Aboyer()' : ne peut pas substituer le membre hrit
'MaPremiereApplication.Program.Chien.Aboyer()', car il n'est pas marqu comme virtual, abstract ou
override.

En ralit, pour pouvoir crer une mthode qui remplace une autre, il faut quune
condition supplmentaire soit vrifie : il faut que la mthode remplacer sannonce

comme candidate la substitution. Cela veut dire que lon ne peut pas substituer
nimporte quelle mthode, mais seulement celles qui acceptent de ltre.
Cest le cas pour la mthode ToString que nous avons vue prcdemment. Les
concepteurs du framework .NET ont autoris cette ventualit. Heureusement, sinon, nous
serions bien embts .
Pour marquer notre mthode Aboyer de la classe Chien comme candidate ventuelle la
substitution, il faut la prfixer du mot cl virtual. Ainsi, elle annonce ses futures filles
que si elles le souhaitent, elles peuvent redfinir cette mthode.
Cela se traduit ainsi dans le code :
public class Chien
{
public virtual void Aboyer()
{
Console.WriteLine("Wouaf !");
}
}
public class ChienMuet : Chien
{
public override void Aboyer()
{
Console.WriteLine("...");
}
}

Dsormais, linstanciation de lobjet est possible et nous pourrons avoir notre code :
ChienMuet pauvreChien = new ChienMuet();
pauvreChien.Aboyer();

qui affichera :

Parfait !
Tout est rentr dans lordre.
Le message derreur, quoique peu explicite, nous mettait quand mme sur la bonne voie.
Visual C# express nous disait quil fallait que la mthode soit marque comme virtual, ce
que nous avons fait. Il proposait galement quelle soit marque abstract, nous verrons un
peu plus loin ce que a veut dire. Visual C# express indiquait enfin que la mthode pouvait
tre marque override.

Cela veut dire quune classe fille de ChienMuet peut galement redfinir la mthode
Aboyer() afin quelle colle ses besoins. Elle nest pas marque virtual mais elle est
marque override. Par exemple :
public class ChienMuetAvecSyntheseVocale : ChienMuet
{
public override void Aboyer()
{
Console.WriteLine("bwarf !");
}
}

Il y a encore un dernier point que nous navons pas abord. Il sagit de la capacit pour
une classe fille de redfinir une mthode tout en conservant la fonctionnalit de la
mthode de la classe mre.
Imaginons notre classe Animal qui possde une mthode Manger() :
public class Animal
{
public virtual void Manger()
{
Console.WriteLine("Mettre les aliments dans la bouche");
Console.WriteLine("Mastiquer");
Console.WriteLine("Avaler");
Console.WriteLine("...");
}
}

Notre classe Chien pourra sappuyer sur le comportement de la mthode Manger() de la


classe Animal pour crer sa propre action personnelle.
Cela se passe en utilisant nouveau le mot cl base qui reprsente la classe mre. Nous
pourrons par exemple appeler la mthode Manger() de la classe mre afin de rutiliser son
fonctionnement. Cela donne :
public class Chien : Animal
{
public override void Manger()
{
Console.WriteLine("Rclamer manger au matre");
base.Manger();
Console.WriteLine("Remuer la queue");
}
}

Dans cet exemple, je fais quelque chose avant dappeler la mthode de la classe mre, puis
je fais quelque chose dautre aprs. Maintenant, si nous faisons manger notre chien :
Chien chien = new Chien();
chien.Manger();

nous aurons :

Nous voyons bien avec cet exemple comment la classe fille peut rutiliser les mthodes de
sa classe mre.
A noter quon peut galement parler de spcialisation ou de redfinition la
place de la substitution.

Polymorphisme
Nous avons dit quune manifestation du polymorphisme tait la capacit dune classe
effectuer la mme action sur diffrents types dintervenants. Il sagit de la surcharge,
appel aussi polymorphisme ad hoc.
Concrtement, cela veut dire quil est possible de dfinir la mme mthode avec des
paramtres en entre diffrents.
Si vous vous rappelez bien, cest quelque chose que nous avons dj fait sans le savoir.
Devinez
Oui, cest a, avec la mthode Console.WriteLine.
Nous avons pu afficher des chaines de caractres, mais aussi des entiers, mme des types
double, et plus rcemment des objets. Comment est-ce possible alors que nous avons dj
vu quil tait impossible de passer des types en paramtres dune mthode qui ne
correspondent pas sa signature ?
Ainsi, lexemple suivant :
public class Program
{
static void Main(string[] args)
{
Math math = new Math();
int a = 5;
int b = 6;
int resultat = math.Addition(a, b);
double c = 1.5;
double d = 5.0;
resultat = math.Addition(c, d); // erreur de compilation
}
}

public class Math


{
public int Addition(int a, int b)
{
return a + b;
}
}

provoquera une erreur de compilation lorsque nous allons essayer de passer des variables
du type double notre mthode qui prend des entiers en paramtres.
Pour que ceci fonctionne, nous allons rendre polymorphe cette mthode en dfinissant
nouveau cette mme mthode mais en lui faisant prendre des paramtres dentres
diffrents :
public class Program
{
static void Main(string[] args)
{
Math math = new Math();
int a = 5;
int b = 6;
int resultat = math.Addition(a, b);
double c = 1.5;
double d = 5.0;
double resultatDouble = math.Addition(c, d); // ca compile, youpi
}
}
public class Math
{
public int Addition(int a, int b)
{
return a + b;
}
public double Addition(double a, double b)
{
return a + b;
}
}

Nous avons ainsi crit deux formes diffrentes de la mme mthode. Une qui accepte des
entiers et lautre qui accepte des double.
Ce code fonctionne dsormais correctement.
Il est bien sr possible dcrire cette mthode avec beaucoup de paramtres de types
diffrents, mme une classe Chien, en imaginant que le fait dadditionner 2 chiens
corresponde au fait dadditionner leur nombre de pattes :
public class Math
{
public int Addition(int a, int b)
{
return a + b;
}
public double Addition(double a, double b)
{
return a + b;
}
public int Addition(Chien c1, Chien c2)
{
return c1.NombreDePattes + c2.NombreDePattes;
}
}

Attention, jai toujours indiqu quil tait possible dajouter une nouvelle forme la mme

mthode en changeant les paramtres dentre. Vous ne pourrez pas le faire en changeant
uniquement le paramtre de retour. Ce qui fait que cet exemple ne pourra pas compiler :
public class Math
{
public int Addition(int a, int b)
{
return a + b;
}
public double Addition(int a, int b)
{
return a + b;
}
}

Les deux mthodes acceptent deux entiers en paramtres et renvoient soit un entier, soit un
double. Le compilateur ne sera pas capable de choisir quelle mthode utiliser lorsque nous
essayerons dappeler cette mthode. Les mthodes doivent se diffrencier avec les
paramtres dentre.
Lorsque nous avons plusieurs signatures possibles pour la mme mthode, vous
remarquerez que la compltion automatique nous propose alors plusieurs alternatives :

Visual C# Express nous indique quil a trois mthodes possibles qui sappellent
Math.Addition. Pour voir la signature des autres mthodes, il suffit de cliquer sur les
flches, ou dutiliser les flches du clavier :

Cest ce quil se passe dans la mthode Console.WriteLine :

Nous voyons ici quil existe 19 critures de la mthode WriteLine, la cinquime prenant
en paramtres un dcimal.
Notez que pour crire plusieurs formes de cette mthode, nous pouvons galement jouer
sur le nombre de paramtres.
La mthode :
public int Addition(int a, int b, int c)
{
return a + b + c;
}

sera bien une nouvelle forme de la mthode Addition.


Nous avons galement vu dans le chapitre sur les constructeurs dune classe quil tait
possible de cumuler les constructeurs avec des paramtres diffrents. Il sagit nouveau
du polymorphisme. Il nous permet de dfinir diffrents constructeurs sur nos objets.

La conversion entre les objets avec le casting


Nous avons dj vu dans la partie prcdente quil tait possible de convertir les types qui
se ressemblent entre eux.
Cela fonctionne galement avec les objets.
Plus prcisment, cela veut dire que nous pouvons convertir un objet en un autre
seulement sil est une sorte de lautre objet. Nous avons vu dans les chapitres prcdents
quil sagissait de la notion dhritage.
Ainsi, si nous avons dfini une classe Animal et que nous dfinissons une classe Chien qui
hrite de cette classe Animal :
public class Animal
{
}
public class Chien : Animal
{
}

nous pourrons alors convertir le chien en animal dans la mesure o le chien est une sorte
danimal :
Chien medor = new Chien();
Animal animal = (Animal)medor;

Nous utilisons pour ce faire un cast, comme nous lavons dj fait pour les types intgrs
(int, bool, etc.).
Il suffit de prfixer la variable convertir du type entre parenthses dans lequel nous
souhaitons le convertir.
Ici, nous pouvons convertir facilement notre Chien en Animal.
Par contre, il est impossible de convertir un chien en voiture, car il ny a pas de relation

dhritage entre les deux. Ainsi les instructions suivantes :


Chien medor = new Chien();
Voiture voiture = (Voiture)medor;

provoqueront une erreur de compilation.


Nous avons prcdemment utilis lhritage afin de mettre des chiens et des chats dans
une liste danimaux.
Nous avions fait quelque chose du genre :
List<Animal> animaux = new List<Animal>();
Animal chien = new Chien();
Animal chat = new Chat();
animaux.Add(chien);
animaux.Add(chat);

Il serait plus logique en fait dcrire les instructions suivantes :


List<Animal> animaux = new List<Animal>();
Chien chien = new Chien();
Chat chat = new Chat();
animaux.Add((Animal)chien);
animaux.Add((Animal)chat);

Dans ce cas, nous crons un objet Chien et un objet Chat que nous mettons dans une liste
dobjets Animal grce une conversion utilisant un cast.
En fait, ce cast est inutile et nous pouvons simplement crire :
List<Animal> animaux = new List<Animal>();
Chien chien = new Chien();
Chat chat = new Chat();
animaux.Add(chien);
animaux.Add(chat);

La conversion est implicite, comme lorsque nous avions utilis un object en paramtres
dune mthode et que nous pouvions lui passer tous les types qui drivent d object.
Nous avions galement vu que nous ne pouvions traiter les chiens et les chats que comme
des animaux partir du moment o nous les mettions dans une liste. Avec les objets
suivants :
public class Animal
{
public void Respirer()
{
Console.WriteLine("Je respire");
}
}
public class Chien : Animal
{
public void Aboyer()
{
Console.WriteLine("Waouf");
}
}
public class Chat : Animal
{
public void Miauler()
{
Console.WriteLine("Miaou");
}
}

Nous pouvions utiliser une boucle pour faire respirer tous nos animaux :

List<Animal> animaux = new List<Animal>();


Chien chien = new Chien();
Chat chat = new Chat();
animaux.Add(chien);
animaux.Add(chat);
foreach (Animal animal in animaux)
{
animal.Respirer();
}

Mais impossible de faire aboyer le chien, ni miauler le chat.


Si vous tentez de remplacer dans la boucle Animal par Chien, avec :
foreach (Chien c in animaux)
{
c.Aboyer();
}

Vous pourrez faire aboyer le premier lment de la liste qui est effectivement un chien, par
contre il y aura un plantage au deuxime lment de la liste car il sagit dun chat :

Lorsque notre programme a tent de convertir un animal qui est un chat en chien, il nous a
fait comprendre quil napprciait que moyennement. Les chiens naiment pas trop les
chats dune manire gnrale, alors en plus, un chat qui essaie de se faire passer pour un
chien : cest une dclaration de guerre !
Voil pourquoi notre programme a lev une exception. Il lui tait impossible de convertir
un Chat en Chien.
Il est cependant possible de tester si une variable correspond un objet grce au mot-cl
is. Ce qui nous permettra de faire la conversion adquate et de nous viter une erreur
lexcution :
foreach (Animal animal in animaux)
{
if (animal is Chien)
{
Chien c = (Chien)animal;
c.Aboyer();
}
if (animal is Chat)
{
Chat c = (Chat)animal;

c.Miauler();
}
}

Nous testons avec le mot-cl is si lanimal est une instance dun Chien ou dun chat. Le
code du dessus nous permettra dutiliser dans la boucle lanimal courant comme un chien
ou un chat en fonction de ce quil est vraiment, grce au test.
Ce qui produira :

Le fait de tester ce quest vraiment lanimal avant de le convertir est une scurit
indispensable pour viter le genre derreur du dessus.
Cest linconvnient du cast explicite. Il convient trs bien si nous sommes certains du
type dans lequel nous souhaitons en convertir un autre. Par contre, si la conversion nest
pas possible, alors nous aurons une erreur.
Lorsque nous ne sommes pas certains du rsultat du cast, mieux vaut tester si linstance
dun objet correspond bien lobjet lui-mme.
Cela peut se faire comme nous lavons vu avec le mot-cl is, mais galement avec un
autre cast qui sappelle le cast dynamique. Il se fait en employant le mot-cl as.
Ce cast dynamique vrifie que lobjet est bien convertible. Si cest le cas, alors il fait un
cast explicite pour renvoyer le rsultat de la conversion, sinon, il renvoie une rfrence
nulle.
Le code du dessus peut donc scrire :
foreach (Animal animal in animaux)
{
Chien c1 = animal as Chien;
if (c1 != null)
{
c1.Aboyer();
}
Chat c2 = animal as Chat;
if (c2 != null)
{
c2.Miauler();
}
}

On utilise le mot-cl as en le faisant prcder de la valeur tenter de convertir et en le


faisant suivre du type dans lequel nous souhaitons la convertir.

Fonctionnellement, nous faisons la mme chose dans les deux codes. Vous pouvez choisir
lcriture que vous prfrez, mais sachez que cest ce dernier qui est en gnral utilis car
il est prconis par Microsoft et est un tout petit peu plus performant.
Un petit dtail encore. Il est possible de convertir un type valeur, comme un int ou un
string en type rfrence en utilisant ce quon appelle le boxing. Rien voir avec le fait de
taper sur les types valeur. Comme nous lavons vu, les types valeur et les types rfrence
sont grs diffremment par .NET. Aussi, si nous convertissons un type valeur en type
rfrence, .NET fait une opration spciale automatiquement.
Ainsi le code suivant :
int i = 5;
object o = i; // boxing

effectue un boxing automatique de lentier en type rfrence. Cest ce boxing automatique


qui nous permet de manipuler les types valeur comme des object.
Cest aussi ce qui nous a permis plus haut de passer un entier en paramtre une mthode
qui acceptait un object.
En interne, ce quil se passe cest que object se voit attribuer une rfrence vers une copie
de la valeur de i.
Ainsi, modifier o ne modifiera pas i. Ce code :
int i = 5;
object o = i; // boxing
o = 6;
Console.WriteLine(i);
Console.WriteLine(o);

affiche 5 puis 6.
Le contraire est galement possible, ce quon appelle lunboxing. Seulement, celui-ci a
besoin dun cast explicite afin de pouvoir compiler. Cest--dire :
int i = 5;
object o = i; // boxing
int j = (int)o; // unboxing

ici nous reconvertissons la rfrence vers la valeur de o en entier et nous effectuons


nouveau une copie de cette valeur pour la mettre dans j. Ainsi le code suivant :
int i = 5;
object o = i; // boxing
o = 6;
int j = (int)o; // unboxing
j = 7;
Console.WriteLine(i);
Console.WriteLine(o);
Console.WriteLine(j);

affichera en toute logique 5 puis 6 puis 7.


noter que ces oprations sont chronophages, elles sont donc faire le moins
possible.
En rsum

Les objets peuvent tre des types valeur ou des types rfrence. Les variables de type
valeur possdent la valeur de lobjet, comme un entier. Les variables de type
rfrence possdent une rfrence vers lobjet en mmoire.

Tous les objets drivent de la classe de base Object.


On peut substituer une mthode grce au mot-cl override si elle sest dclare
candidate la substitution grce au mot-cl virtual.
La surcharge est le polymorphisme permettant de faire varier les types des paramtres
dune mme mthode ou leurs nombres.
Il est possible grce au cast de convertir un type en un autre type, sils ont une
relation dhritage.

Chapitre 23

Notions avances de POO en C#

Dans ce chapitre, nous allons continuer dcouvrir comment nous pouvons faire de
lorient objet avec le C#. Nous allons pousser un peu plus loin en dcouvrant les
interfaces et en manipulant les classes statiques et abstraites.
la fin de ce chapitre, vous serez capables de faire des objets encore plus volus et vous
devriez tre capables de crer un vrai petit programme orient objet !

Comparer des objets


Nous avons vu dans la premire partie quil tait possible de comparer facilement des
types valeur grce aux oprateurs de comparaison. En effet, vu que des variables de ces
types possdent directement la valeur que nous lui affectons, on peut facilement comparer
un entier avec la valeur 5, ou un entier avec un autre entier. Par contre, cela ne fonctionne
pas avec les objets. En effet, nous avons vu que les variables qui reprsentent des
instances dobjet contiennent en fait une rfrence vers linstance.
Cela na pas vraiment de sens de comparer des rfrences. De plus, en imaginant que je
veuille vraiment comparer deux voitures, sur quels critres puis-je dterminer quelles
sont gales ? La couleur ? La vitesse ?
Sans rien faire, la comparaison en utilisant par exemple loprateur dgalit ==
permet simplement de vrifier si les rfrences pointent vers le mme objet.
Pour les exemples suivants, nous nous baserons sur la classe Voiture suivante :
public class Voiture
{
public string Couleur { get; set; }
public string Marque { get; set; }
public int Vitesse { get; set; }
}

Ainsi, si nous crivons :


Voiture voitureNicolas = new Voiture();
voitureNicolas.Couleur = "Bleue";
Voiture voitureJeremie = voitureNicolas;
voitureJeremie.Couleur = "Verte";
if (voitureJeremie == voitureNicolas)
{
Console.WriteLine("Les objets rfrencent la mme instance");
}

Ce code affichera la chaine Les objets rfrencent la mme instance car effectivement,

nous avons affect la rfrence de voitureNicolas voitureJeremie. (Ce qui implique


galement que la modification de la voiture de Jrmie affecte galement la voiture de
Nicolas, comme nous lavons dj vu).
Par contre, le code suivant :
Voiture voitureNicolas = new Voiture();
Voiture voitureJeremie = new Voiture();
if (voitureJeremie == voitureNicolas)
{
Console.WriteLine("Les objets rfrencent la mme instance");
}

naffichera videmment rien car ce sont deux instances diffrentes.


Sil savre quil est vraiment pertinent de pouvoir comparer deux voitures entre elles, il
faut savoir que cest quand mme possible. La premire chose faire est de dfinir les
critres de comparaison.
Par exemple, nous navons qu dire que deux voitures sont identiques quand la couleur, la
marque et la vitesse sont gales. Je sais, cest un peu irrel, mais cest pour lexemple.
Ainsi, nous pourrons par exemple vrifier que deux voitures sont gales avec linstruction
suivante :
if (voitureNicolas.Couleur == voitureJeremie.Couleur && voitureNicolas.Marque ==
voitureJeremie.Marque && voitureNicolas.Vitesse == voitureJeremie.Vitesse)
{
Console.WriteLine("Les deux voitures sont identiques");
}

La comparaison dgalit entre deux objets, cest en fait le rle de la mthode Equals()
dont chaque objet hrite de la classe mre Object. A part pour les types valeur, le
comportement par dfaut de la mthode Equals() est de comparer les rfrences des
objets. Seulement, il est possible de dfinir un comportement plus appropri pour notre
classe Voiture, grce la fameuse spcialisation.
Comme on la dj vu, on utilise le mot cl override. Ceci est possible dans la mesure o
la classe Object a dfini la mthode Equals comme virtuelle, avec le mot cl virtual.
Ce qui donne :
public class Voiture
{
public string Couleur { get; set; }
public string Marque { get; set; }
public int Vitesse { get; set; }
public override bool Equals(object obj)
{
Voiture v = obj as Voiture;
if (v == null)
return false;
return Vitesse == v.Vitesse && Couleur == v.Couleur && Marque == v.Marque;
}
}

Remarquons que la mthode Equals prend en paramtre un object. La premire chose


faire est donc de vrifier que nous avons rellement une voiture, grce au cast dynamique.
Ensuite, il ne reste qu comparer les proprits de linstance courante et de lobjet pass
en paramtre.
Pour faire une comparaison entre deux voitures, nous pourrons utiliser le code suivant :
Voiture voitureNicolas = new Voiture { Vitesse = 10, Marque = "Peugeot", Couleur = "Grise"};
Voiture voitureJeremie = new Voiture { Vitesse = 10, Marque = "Peugeot", Couleur = "Grise" };
if (voitureNicolas.Equals(voitureJeremie))
{

Console.WriteLine("Les objets ont les mmes valeurs dans leurs proprits");


}

Nos deux voitures sont identiques car leurs marques, leurs couleurs et leurs vitesses sont
identiques :

Facile de comparer .
Sauf que vous aurez peut-tre remarqu que la compilation de ce code provoque un
avertissement. Il ne sagit pas dune erreur, mais Visual C# express nous informe quil faut
faire attention :

Il nous dit que nous avons substitu la mthode Equals() sans avoir redfini la mthode
GetHashCode(). Nous navons pas besoin ici de savoir quoi sert vraiment la mthode
GetHashCode(), mais si vous voulez en savoir plus, nhsitez pas consulter la
documentation.
Toujours est-il que nous devons rajouter une spcialisation de la mthode GetHashCode(),
dont le but est de renvoyer un identifiant plus ou moins unique reprsentant lobjet, ce qui
donnera :
public class Voiture
{
public string Couleur { get; set; }
public string Marque { get; set; }
public int Vitesse { get; set; }
public override bool Equals(object obj)
{
Voiture v = obj as Voiture;
if (v == null)
return false;
return Vitesse == v.Vitesse && Couleur == v.Couleur && Marque == v.Marque;
}
public override int GetHashCode()

{
return Couleur.GetHashCode() * Marque.GetHashCode() * Vitesse.GetHashCode();
}
}

Nous nous servons du fait que chaque variable de la classe possde dj un identifiant
obtenu avec la mthode GetHashCode(). En combinant chaque identifiant de chaque
proprit nous pouvons en crer un nouveau.
Ici, la classe est complte et prte tre compare. Elle pourra donc fonctionner
correctement avec tous les algorithmes dgalit du framework .NET. Notez quand mme
que devoir substituer ces deux mthodes est une opration relativement rare, nous lavons
tudi pour la culture.
Ce qui est important retenir cest ce fameux warning. La conclusion tirer est que notre
faon de comparer, bien que fonctionnelle pour notre voiture, nest pas parfaite.
Pourquoi ? Parce quen ayant substitu la mthode Equals(), nous croyons que la
comparaison est bonne sauf que le compilateur nous apprend que ce nest pas le cas.
Heureusement quil tait l ce compilateur. Comme cest une erreur classique, il est
capable de la dtecter. Mais si cest autre chose et quil ne le dtecte pas ?
Cela manque dune uniformisation tout a, vous ne trouvez pas ? Il faudrait quelque chose
qui nous assure que la classe est correctement comparable. Une espce de contrat que
lobjet sengagerait respecter pour tre sr que toutes les comparaisons soient valides.
Un contrat ? Un truc qui finit par able ? a me rappelle quelque chose a. Mais oui, les
interfaces.

Les interfaces
Une fois nest pas coutume, plutt que de commencer par tudier le plus simple, nous
allons tudier le plus logique puis nous reviendrons sur le plus simple.
Cest--dire que nous allons pousser un peu plus loin la comparaison en nous servant des
interfaces et nous reviendrons ensuite sur comment crer une interface.
Nous avons donc dit que les interfaces taient un contrat que sengageait respecter un
objet. Cest tout fait ce dont on a besoin ici. Notre objet doit sengager fonctionner
pour tous les types de comparaison. Il doit tre comparable. Mais comparable ne veut pas
forcment dire gal , nous devrions tre aussi capables dindiquer si un objet est
suprieur un autre.
Pourquoi ? Imaginons que nous possdions un tableau de voitures et que nous souhaitions
le trier comme on a vu dans un chapitre prcdent. Pour les entiers ctait une opration
plutt simple, avec la mthode Array.Sort() ils taient automatiquement tris par ordre
croissant. Mais dans notre cas, comment un tableau sera capable de trier nos voitures ?
Nous voici donc en prsence dun cas concret dutilisation des interfaces. Linterface
IComparable permet de dfinir un contrat de mthodes destines la prise en charge de la
comparaison entre deux instances dun objet.
Une fois ces mthodes implmentes, nous serons certains que nos objets seront
comparables correctement.
Pour cela, nous allons faire en sorte que notre classe implmente linterface.
Reprenons notre classe Voiture avec uniquement ses proprits et faisons lui implmenter
linterface IComparable.

Pour implmenter une interface, on utilisera la mme syntaxe que pour hriter dune
classe, cest--dire quon utilisera les deux points suivis du type de linterface. Ce qui
donne :
public class Voiture : IComparable
{
public string Couleur { get; set; }
public string Marque { get; set; }
public int Vitesse { get; set; }
}

Il est conventionnel de dmarrer le nom dune interface par un I majuscule.


Cest le cas de toutes les interfaces du framework .NET.
Si vous tentez de compiler ce code, vous aurez le message derreur suivant :

Le compilateur nous rappelle lordre : nous annonons que nous souhaitons respecter le
contrat de comparaison, sauf que nous navons pas la mthode adquate !
Eh oui, le contrat indique ce que nous nous engageons faire mais pas la faon de le faire.
Limplmentation de la mthode est notre charge.
Toujours dans loptique de simplifier la tche du dveloppeur, Visual C# Express nous
aide pour implmenter les mthodes dun contrat. Faites un clic droit sur linterface et
choisissez dans le menu contextuel Implmenter linterface et Implmenter
linterface :

Visual C# Express nous gnre le code suivant :


public class Voiture : IComparable
{
public string Couleur { get; set; }
public string Marque { get; set; }
public int Vitesse { get; set; }
public int CompareTo(object obj)
{
throw new NotImplementedException();
}
}

Cest--dire la signature de la mthode quil nous manque pour respecter le contrat de


linterface et un contenu que nous ne comprenons pas pour linstant. Nous y reviendrons
plus tard, pour linstant, vous navez qu supprimer la ligne :
throw new NotImplementedException();

Il ne reste plus qu crire le code de la mthode.


Pour ce faire, il faut dfinir un critre de tri. Trier des voitures na pas trop de sens, aussi
nous dirons que nous souhaitons les trier suivant leurs vitesses.
Pour respecter correctement le contrat, nous devons respecter la rgle suivante qui se
trouve dans la documentation de la mthode de linterface :
Si une voiture est infrieure une autre, alors nous devons renvoyer une valeur
infrieure 0, disons -1.
Si elle est gale, alors nous devons renvoyer 0.
Enfin, si elle est suprieure, nous devons renvoyer une valeur suprieure 0, disons
1.
Ce qui donne :
public int CompareTo(object obj)
{
Voiture voiture = (Voiture)obj;
if (this.Vitesse < voiture.Vitesse)
return -1;
if (this.Vitesse > voiture.Vitesse)
return 1;
return 0;
}

noter que la comparaison seffectue entre lobjet courant et un objet qui lui est pass en
paramtres. Pour que ce soit un peu plus clair, jai utilis le mot-cl this qui permet de
bien identifier lobjet courant et lobjet pass en paramtres.
Comme il est facultatif, nous pouvons le supprimer.
Vous aurez galement remarqu que jutilise un cast explicite avant de comparer. Ceci
permet de renvoyer une erreur si jamais lobjet comparer nest pas du bon type. En effet,
que devrais-je renvoyer si jamais lobjet quon me passe nest pas une voiture ? Une
erreur, cest trs bien.
Le code est suffisamment explicite pour que nous comprenions facilement ce que lon doit
faire : comparer les vitesses.
Il est possible de simplifier grandement le code car pour comparer nos deux voitures, nous
effectuons la comparaison sur la valeur dun entier, ce qui est plutt trivial.
Dautant plus que lentier, en bon objet comparable, possde galement la mthode
CompareTo().
Ce qui fait quil est possible dcrire notre mthode de comparaison de cette faon :
public int CompareTo(object obj)
{
Voiture voiture = (Voiture)obj;
return Vitesse.CompareTo(voiture.Vitesse);
}

En effet, Vitesse tant un type intgr, il implmente dj correctement la comparaison.


Cest dailleurs pour a que le tableau dentier que nous avons vu prcdemment a t
capable de se trier facilement.
En ayant implment cette interface, nous pouvons dsormais trier des tableaux de
Voiture :
Voiture[] voitures = new Voiture[] { new Voiture { Vitesse = 100 }, new Voiture { Vitesse = 40 }, new
Voiture { Vitesse = 10 }, new Voiture { Vitesse = 40 }, new Voiture { Vitesse = 50 } };
Array.Sort(voitures);
foreach (Voiture v in voitures)

{
Console.WriteLine(v.Vitesse);
}

Ce qui donne :

Voil pour le tri, mais si je peux me permettre, je trouve que ce code-l est un peu moche.
Jy reviendrai un peu plus tard.
Nous avons donc implment notre premire interface. Finalement, ce ntait pas si
compliqu. Voyons prsent comment crer nos propres interfaces.
Une interface se dfinit en C# comme une classe, sauf quon utilise le mot-cl interface
la place de class.
En tant que dbutant, vous aurez rarement besoin de crer des interfaces. Cependant, il est
utile de savoir le faire. Par contre, il sera beaucoup plus frquent que vos classes
implmentent des interfaces existantes du framework .NET, comme nous venons de le
faire.
Voyons prsent comment crer une interface et examinons le code suivant :
public interface IVolant
{
int NombrePropulseurs { get; set; }
void Voler();
}

Note : comme une classe, il est recommand de crer les interfaces dans un
fichier part. Rappelez-vous galement de la convention qui fait que les
interfaces doivent commencer par un i majuscule.
Nous dfinissons ici une interface IVolant qui possde une proprit de type int et une
mthode Voler() qui ne renvoie rien.
Voil, cest tout simple.
Nous avons cr une interface. Les objets qui choisiront dimplmenter cette interface
seront obligs davoir une proprit entire NombrePropulseurs et une mthode Voler()
qui ne renvoie rien. Rappelez-vous que linterface ne contient que le contrat et aucune
implmentation. Cest--dire que nous ne verrons jamais de corps de mthode dans une
interface ni de variables membres ; uniquement des mthodes et des proprits. Un

contrat.
Notez quand mme quil ne faut pas dfinir de visibilit sur les membres dune interface.
Nous serons obligs de dfinir les visibilits en public sur les objets implmentant
linterface.
Crons dsormais deux objets Avion et Oiseau qui implmentent cette interface, ce qui
donne :
public class Oiseau : IVolant
{
public int NombrePropulseurs { get; set; }
public void Voler()
{
Console.WriteLine("Je vole grce " + NombrePropulseurs + " ailes");
}
}
public class Avion : IVolant
{
public int NombrePropulseurs { get; set; }
public void Voler()
{
Console.WriteLine("Je vole grce " + NombrePropulseurs + " moteurs");
}
}

Grce ce contrat, nous savons maintenant que nimporte lequel de ces objets saura voler.
Il est possible de traiter ces objets comme des objets volants, un peu comme ce que nous
avions fait avec les classes mres, en utilisant linterface comme type pour la variable. Par
exemple :
IVolant oiseau = new Oiseau { NombrePropulseurs = 2 };
oiseau.Voler();

Nous instancions vraiment un objet Oiseau, mais nous le manipulons en tant que IVolant.
Un des intrts dans ce cas sera de pouvoir manipuler des objets qui partagent un
comportement de la mme faon :
Oiseau oiseau = new Oiseau { NombrePropulseurs = 2 };
Avion avion = new Avion { NombrePropulseurs = 4 };
List<IVolant> volants = new List<IVolant> { oiseau, avion };
foreach (IVolant volant in volants)
{
volant.Voler();
}

Ce qui produira :

Grce linterface, nous avons pu mettre dans une mme liste des objets diffrents, qui
nhritent pas entre eux mais qui partagent une mme interface, cest--dire un mme
comportement : IVolant. Pour accder ces objets, nous devrons utiliser leurs interfaces.
Il sera possible quand mme de caster nos IVolant en Avion ou en Oiseau, si jamais nous
souhaitons rajouter une proprit propre lavion.
Par exemple je rajoute une proprit NomDuCommandant mon avion mais qui ne fait pas
partie de linterface :
public class Avion : IVolant
{
public int NombrePropulseurs { get; set; }
public string NomDuCommandant { get; set; }
public void Voler()
{
Console.WriteLine("Je vole grce " + NombrePropulseurs + " moteurs");
}
}

Cela veut dire que lobjet Avion pourra affecter un nom de commandant mais quil ne sera
pas possible dy accder par linterface :
IVolant avion = new Avion { NombrePropulseurs = 4, NomDuCommandant = "Nico" };
Console.WriteLine(avion.NomDuCommandant); // erreur de compilation

Lerreur de compilation nous indique que IVolant ne possde pas de dfinition pour
NomDuCommandant. Ce qui est vrai !
Pour accder au nom du commandant, nous pourrons tenter de caster nos IVolant en
Avion. Si le cast est valide, alors nous pourrons accder notre proprit :
Oiseau oiseau = new Oiseau { NombrePropulseurs = 2 };
Avion avion = new Avion { NombrePropulseurs = 4, NomDuCommandant = "Nico" };
List<IVolant> volants = new List<IVolant> { oiseau, avion };
foreach (IVolant volant in volants)
{
volant.Voler();
Avion a = volant as Avion;
if (a != null)
{
Console.WriteLine(a.NomDuCommandant);
}
}

Voil, cest tout simple et a ressemble un peu ce quon a dj vu.


Note : lorsque nous avons abord la boucle foreach, jai dit quelle nous servait
parcourir des lments numrables . En disant a, je disais en fait que la
boucle foreach fonctionne avec tous les types qui implmentent linterface
IEnumerable. Maintenant que vous savez ce quest une interface, vous
comprenez mieux ce quoi je faisais vraiment rfrence.
Il faut galement noter que les interfaces peuvent hriter entre elles, comme cest le cas
avec les objets.
Cest--dire que je vais pouvoir dclarer une interface IVolantMotorise qui hrite de
linterface IVolant.
public interface IVolant
{
int NombrePropulseurs { get; set; }
void Voler();
}
public interface IVolantMotorise : IVolant
{
void DemarrerLeMoteur();
}

Ainsi, ma classe Avion qui implmentera IVolantMotorise devra obligatoirement


implmenter les mthodes/proprits de IVolant ainsi que la mthode de IVolantMotorise
:
public class Avion : IVolantMotorise
{
public void DemarrerLeMoteur()
{
}
public int NombrePropulseurs { get; set;}
public void Voler()
{
}
}

Enfin, et nous nous arrterons l pour les interfaces, il est possible pour une classe
dimplmenter plusieurs interfaces. Il suffira pour cela de sparer les interfaces par une
virgule et dimplmenter bien sr tout ce quil faut derrire. Par exemple :
public interface IVolant
{
void Voler();
}
public interface IRoulant
{
void Rouler();
}
public class Avion : IVolant, IRoulant
{
public void Voler()
{
Console.WriteLine("Je vole");
}
public void Rouler()
{
Console.WriteLine("Je Roule");
}

Les classes et les mthodes abstraites


Une classe abstraite est une classe particulire qui ne peut pas tre instancie.
Concrtement, cela veut dire que nous ne pourrons pas utiliser loprateur new.
De la mme faon, une mthode abstraite est une mthode qui ne contient pas
dimplmentation, cest--dire pas de code.
Pour tre utilisables, les classes abstraites doivent tre hrites et les mthodes redfinies.
En gnral, les classes abstraites sont utilises comme classe de base pour dautres classes.
Ces classes fournissent des comportements mais nont pas vraiment dutilit avoir une
vie propre. Ainsi, les classes filles qui en hritent pourront bnficier de leurs
comportements et devront ventuellement en remplacer dautres.
Cest comme une classe incomplte qui ne demande qu tre complte.
Si une classe possde une mthode abstraite, alors la classe doit absolument tre abstraite.
Linverse nest pas vrai, une classe abstraite peut possder des mthodes non abstraites.
Vous aurez rarement besoin dutiliser les classes abstraites en tant que dbutant mais elles
pourront vous servir pour combiner la puissance des interfaces lhritage.
Bon, voil pour la thorie, passons un peu la pratique.
Rappelez-vous nos chiens et nos chats qui drivent dune classe mre Animal. Nous
savons quun Animal est capable de se dplacer. Cest un comportement quont en
commun tous les animaux. Il est tout fait logique de dfinir une mthode SeDeplacer()
au niveau de la classe Animal. Sauf quun chien ne se dplace pas forcment comme un
dauphin. Lun a des pattes, lautre des nageoires. Et mme si le chien et le chat semblent
avoir un dplacement relativement proche, ils ont chacun des subtilits. Le chat a un
mouvement plus gracieux, plus flin.
Bref, on se rend compte que nous sommes obligs de redfinir la mthode SeDeplacer()
dans chaque classe fille de la classe Animal. Et puis de toute faon, sommes-nous vraiment
capables de dire comment se dplace un animal dans labsolu ?
La mthode SeDeplacer est une candidate parfaite pour tre une mthode abstraite.
Rappelez-vous, la mthode abstraite ne possde pas dimplmentation et chaque classe
fille de la classe possdant cette mthode abstraite devra la spcialiser. Cest exactement
ce quil nous faut.
Pour dclarer une mthode comme tant abstraite, il faut utiliser le mot-cl abstract et ne
fournir aucune implmentation de la mthode, cest--dire que la dclaration de la
mthode doit se terminer par un point-virgule :
public class Animal
{
public abstract void SeDeplacer();
}

Si nous tentons de compiler cette classe, nous aurons lerreur suivante :


'MaPremiereApplication.Animal.SeDeplacer()' est abstrait, mais est contenu dans la classe non
abstraite 'MaPremiereApplication.Animal'

Ah oui cest vrai, on a dit quune classe qui contient au moins une mthode abstraite tait
forcment abstraite. Cest le mme principe que pour la mthode, il suffit dutiliser le mot-

cl abstract devant le mot-cl class :


public abstract class Animal
{
public abstract void SeDeplacer();
}

Voil, notre classe peut compiler tranquillement.


Nous avons galement dit quune classe abstraite pouvait contenir des mthodes concrtes
et que ctait dailleurs une des grandes forces de ce genre de classes. En effet, il est tout
fait pertinent que des animaux puissent mourir. Et l, a se passe pour tout le monde de la
mme faon, le cur arrte de battre et on ne peut plus rien faire. Cest triste, mais cest
ainsi.
Cela veut dire que nous pouvons crer une mthode Mourir() dans notre classe abstraite
qui possde une implmentation.
Cela donne :
public abstract class Animal
{
private Coeur coeur;
public Animal()
{
coeur = new Coeur();
}
public abstract void SeDeplacer();
public void Mourir()
{
coeur.Stop();
}
}

Avec une classe Cur qui ne fait pas grand-chose :


public class Coeur
{
public void Battre()
{
Console.WriteLine("Boom boom");
}
public void Stop()
{
Console.WriteLine("Mon coeur s'arrte de battre");
}
}

Comme prvu, il nest pas possible dinstancier un objet Animal. Si nous tentons
lopration :
Animal animal = new Animal();

nous aurons lerreur de compilation suivante :


Impossible de crer une instance de la classe abstraite ou de l'interface
'MaPremiereApplication.Animal'

Par contre, il est possible de crer une classe Chien qui drive de la classe Animal :
public class Chien : Animal
{
}

Cette classe ne pourra pas compiler dans cet tat car il faut obligatoirement redfinir la
mthode abstraite SeDeplacer(). Cela se fait en utilisant le mot-cl override, comme on

la dj vu.
Vous aurez srement remarqu que la mthode abstraite nutilise pas le mot-cl virtual
comme cela doit absolument tre le cas lors de la substitution dune mthode dans une
classe non-abstraite. Il est ici implicite et ne doit pas tre utilis, sinon nous aurons une
erreur de compilation. Et puis cela nous arrange, un seul mot-cl, cest largement suffisant
!
Nous devons donc spcialiser la mthode SeDeplacer, soit en crivant la mthode la
main, soit en utilisant encore une fois notre ami Visual C# Express.
Il suffit de faire un clic droit sur la classe dont hrite Chien (en loccurrence Animal) et de
cliquer sur Implmenter une classe abstraite :

Visual C# Express nous gnre donc la signature de la mthode substituer :


public class Chien : Animal
{
public override void SeDeplacer()
{
throw new NotImplementedException();
}
}

Il ne reste plus qu crire le corps de la mthode SeDeplacer, par exemple :


public class Chien : Animal
{
public override void SeDeplacer()
{
Console.WriteLine("Waouf ! Je me dplace avec mes 4 pattes");
}
}

Ainsi, nous pourrons crer un objet Chien et le faire se dplacer puis le faire mourir car il
hrite des comportements de la classe Animal. Paix son me.
Chien max = new Chien();
max.SeDeplacer();
max.Mourir();

ce qui donnera :

Vous pouvez dsormais vous rendre compte de la ralit de la phrase que jai crite en
introduction :
[Les classes abstraites] pourront vous servir pour combiner la puissance des interfaces
lhritage .
En effet, la classe abstraite peut fournir des implmentations alors que linterface ne
propose quun contrat. Cependant, une classe concrte ne peut hriter que dune seule
classe mais peut implmenter plusieurs interfaces.
La classe abstraite est un peu mi-chemin entre lhritage et linterface.

Les classes partielles


Les classes partielles offrent la possibilit de dfinir une classe en plusieurs fois. En
gnral, ceci est utilis pour dfinir une classe sur plusieurs fichiers, si par exemple la
classe devient trs longue. Il pourra ventuellement tre judicieux de dcouper la classe en
plusieurs fichiers pour regrouper des fonctionnalits qui se ressemblent. On utilise pour
cela le mot-cl partial.
Par exemple, si nous avons un fichier qui contient la classe Voiture suivante :
public partial class Voiture
{
public string Couleur { get; set; }
public string Marque { get; set; }
public int Vitesse { get; set; }
}

Nous pourrons complter sa dfinition dans un autre fichier pour lui rajouter par exemple
des mthodes :
public partial class Voiture
{
public string Rouler()
{
return "Je roule " + Vitesse + " km/h";
}
}

la compilation, Visual C# Express runit les deux classes en une seule et lobjet

fonctionne comme toute autre classe qui ne serait pas forcment partielle.
noter quil faut imprativement que les deux classes possdent le mot-cl partial pour
que cela soit possible, sinon Visual C# Express gnrera une erreur de compilation :
Modificateur partiel manquant sur la dclaration de type 'MaPremiereApplication.Voiture' ; une autre
dclaration partielle de ce type existe

Vous allez me dire que ce nest pas super utile comme fonctionnalit. Et je vous dirais que
vous avez raison. Sauf dans un cas particulier.
Les classes partielles prennent de lintrt quand une partie du code de la classe est gnr
par Visual C# Express. Cest le cas pour la plupart des plateformes qui servent
dvelopper des vraies applications. Par exemple ASP.NET pour un site internet, WPF pour
une application Windows, Silverlight pour un client riche, etc.
Cest aussi le cas lorsque nous gnrons de quoi permettre daccder une base de
donnes.
Dans ce cas-l, disons pour simplifier que Visual C# Express va nous gnrer tout un tas
dinstructions pour nous connecter la base de donnes ou pour rcuprer des donnes.
Toutes ces instructions seront mises dans une classe partielle que nous pourrons enrichir
avec nos besoins.
Lintrt est que si nous re-gnrons une nouvelle version de notre classe, seul le fichier
gnr sera impact. Si nous avions modifi le fichier pour enrichir la classe avec nos
besoins, nous aurions perdu tout notre travail. Vu que grce aux classes partielles, ce code
est situ dans un autre fichier, il nest donc pas perdu. Pour notre plus grand bonheur !
noter que le mot-cl partial peut se combiner sans problmes avec dautres mots-cls,
comme abstract par exemple que nous venons de voir.
Il est frquent aussi de voir des classes partielles utilises quand plusieurs dveloppeurs
travaillent sur la mme classe. Le fait de sparer la classe en deux fichiers permet de
travailler sans se marcher dessus.
Nous aurons loccasion de voir des classes partielles gnrs plus tard dans le tutoriel.

Classes statiques et mthodes statiques


Nous avons dj vu le mot-cl static en premire partie de ce tutoriel. Il nous a bien
encombrs. Nous nous le sommes trimball pendant un moment, puis il a disparu !
Il est temps de revenir sur ce mot-cl afin de comprendre exactement de quoi il sagit,
maintenant que nous avons plus de notions et que nous connaissons les objets et les
classes.
Jusqu prsent, nous avons utilis le mot-cl static uniquement sur les mthodes et jai
vaguement expliqu quil servait indiquer que la mthode est toujours disponible et prte
tre utilise. Pas trs convaincant, comme explication mais comme vous tes polis,
vous ne maviez rien dit.
En fait, le mot-cl static permet dindiquer que la mthode dune classe nappartient pas
une instance de la classe. Nous avons vu que jusqu prsent, nous devions instancier
nos classes avec le mot-cl new pour avoir des objets.
Ici, static permet de ne pas instancier lobjet mais davoir accs cette mthode en
dehors de tout objet.
Nous avons dj utilis beaucoup de mthodes statiques, je ne sais pas si vous avez fait

attention, mais maintenant que vous connaissez les objets, la mthode suivante ne vous
parat pas bizarre ?
Console.WriteLine("Bonjour");

Nous utilisons la mthode WriteLine de la classe Console sans avoir cr dobjet Console.
trange.
Il sagit, vous laurez devin, dune mthode statique qui est accessible en dehors de toute
instance de Console. Dailleurs, la classe entire est une classe statique. Si nous essayons
dinstancier la classe Console avec :
Console c = new Console();

Nous aurons les messages derreurs suivant :


Impossible de dclarer une variable de type static 'System.Console'
Impossible de crer une instance de la classe static 'System.Console'

Nous avons dit que la mthode spciale Main() est obligatoirement statique. Cela permet
au CLR, qui va excuter notre application, de ne pas avoir besoin dinstancier la classe
Program pour dmarrer notre application. Il a juste appeler la mthode Program.Main()
afin de dmarrer notre programme.
Comme la mthode Main() est utilisable en dehors de toute instance de classe, elle ne peut
appeler que des mthodes statiques. En effet, comment pourrait-elle appeler des mthodes
dun objet alors quelle nen a mme pas conscience ?
Cest pour cela que nous avons t obligs de prfixer chacune de nos premires mthodes
par le mot-cl static.
Revenons nos objets. Ils peuvent contenir des mthodes statiques ou des variables
statiques. Si une classe ne contient que des choses statiques alors elle peut devenir
galement statique.
Une mthode statique est donc une mthode qui ne travaille pas avec les membres
(variables ou autres) non statiques de sa propre classe.
Rappelez-vous un peu plus haut, nous avions cr une classe Math qui servait faire des
additions, afin dillustrer le polymorphisme :
public class Math
{
public int Addition(int a, int b)
{
return a + b;
}
}

que nous utilisions de cette faon :


Math math = new Math();
int resultat = math.Addition(5, 6);

Ici, la mthode addition sert additionner 2 entiers. Elle est compltement indpendante
de la classe Math et donc des instances de lobjet Math.
Nous pouvons donc en faire une mthode statique, pour cela il suffira de prfixer du motcl static son type de retour :
public class Math
{
public static int Addition(int a, int b)
{
return a + b;
}

Et nous pourrons alors utiliser laddition sans crer dinstance de la classe Math, mais
simplement en utilisant le nom de la classe suivi du nom de la mthode statique :
int resultat = Math.Addition(5, 6);

Exactement comme nous avons fait pour Console.WriteLine.


De la mme faon, nous pouvons rajouter dautres mthodes, comme la multiplication :
public class Math
{
public static int Addition(int a, int b)
{
return a + b;
}
public static long Multiplication(int a, int b)
{
return a * b;
}
}

Que nous appellerons de la mme faon :


long resultat = Math.Multiplication(5, 6);

noter que la classe Mathest toujours instanciable mais quil nest pas possible dappeler
les mthodes qui sont statiques depuis un objet Math. Le code suivant :
Math math = new Math();
long resultat = math.Multiplication(5, 6);

provoquera lerreur de compilation :


Le membre 'MaPremiereApplication.Math.Multiplication(int, int)' est inaccessible avec une rfrence
d'instance ; qualifiez-le avec un nom de type

Ok, mais quoi a sert de pouvoir instancier un objet Math si nous ne pouvons
accder aucune de ses mthodes, vu quelles sont statiques ?
Absolument rien ! Si une classe ne possde que des membres statiques, alors il est
possible de rendre cette classe statique grce au mme mot-cl. Celle-ci deviendra noninstanciable, comme la classe Console :
public static class Math
{
public static int Addition(int a, int b)
{
return a + b;
}
}

Ainsi, si nous tentons dinstancier lobjet Math, nous aurons une erreur de compilation.
En gnral, les classes statiques servent regrouper des mthodes utilitaires qui partagent
une mme fonctionnalit. Ici, la classe Math permettrait dy ranger toutes les mthodes du
style addition, multiplication, racine carre, etc
Ah, on me fait signe que cette classe existe dj dans le framework .NET et quelle
sappelle galement Math. Elle est range dans lespace de nom System. Souvenez-vous,
nous lavons utilise pour calculer la racine carre. Cette classe est statique, cest la
version aboutie de la classe que nous avons commenc crire.
Par contre, ce nest pas parce quune classe possde des mthodes statiques quelle est
obligatoirement statique. Il est aussi possible davoir des membres statiques dans une

classe qui possde des membres non statiques.


Par exemple notre classe Chien, qui possde un prnom et qui sait aboyer :
public class Chien
{
private string prenom;
public Chien(string prenomDuChien)
{
prenom = prenomDuChien;
}
public void Aboyer()
{
Console.WriteLine("Wouaf ! Je suis " + prenom);
}
}

pourrait possder une mthode permettant de calculer lge dun chien dans le rfrentiel
des humains.
Comme beaucoup le savent, il suffit de multiplier lge du chien par 7.
public class Chien
{
private string prenom;
public Chien(string prenomDuChien)
{
prenom = prenomDuChien;
}
public void Aboyer()
{
Console.WriteLine("Wouaf ! Je suis " + prenom);
}
public static int CalculerAge(int ageDuChien)
{
return ageDuChien * 7;
}
}

Ici, la mthode CalculerAge() est statique car elle ne travaille pas directement avec une
instance dun chien.
Nous pouvons lappeler ainsi :
Chien hina = new Chien("Hina");
hina.Aboyer();
int ageReferentielHomme = Chien.CalculerAge(4);
Console.WriteLine(ageReferentielHomme);

Ce qui donne :

Vous me direz quil est possible de faire en sorte que la mthode travaille sur une instance
dun objet Chien, ce qui serait peut-tre plus judicieux ici. Il suffirait de rajouter une
proprit Age au Chien et de transformer la mthode pour quelle ne soit plus statique. Ce
qui donnerait :
public class Chien
{
private string prenom;
public int Age { get; set; }
public Chien(string prenomDuChien)
{
prenom = prenomDuChien;
}
public void Aboyer()
{
Console.WriteLine("Wouaf ! Je suis " + prenom);
}
public int CalculerAge()
{
return Age * 7;
}
}

Que lon pourrait appeler de cette faon :


Chien hina = new Chien("Hina") { Age = 5 };
int ageReferentielHomme = hina.CalculerAge();
Console.WriteLine(ageReferentielHomme);

Ici, cest plus une histoire de conception. Cest vous de dcider, mais sachez que cest
possible.
Il est galement possible dutiliser le mot-cl static avec des proprits. Imaginions que
nous souhaitions avoir un compteur sur le nombre dinstances de la classe Chien. Nous
pourrions crer une proprit statique de type entier qui sincrmente chaque fois que
lon cre un nouveau chien :
public class Chien
{
public static int NombreDeChiens { get; set; }
private string prenom;

public Chien(string prenomDuChien)


{
prenom = prenomDuChien;
NombreDeChiens++;
}
public void Aboyer()
{
Console.WriteLine("Wouaf ! Je suis " + prenom);
}
}

Ici, la proprit NombreDeChiens est statique et est incrmente chaque passage dans le
constructeur.
Ainsi, si nous crons plusieurs chiens :
Chien chien1 = new Chien("Max");
Chien chien2 = new Chien("Hina");
Chien chien3 = new Chien("Laika");
Console.WriteLine(Chien.NombreDeChiens);

Nous pourrons voir combien de fois nous sommes passs dans le constructeur :

Pour une variable statique, cela se passe de la mme faon quavec les proprits
statiques.

Les classes internes


Les classes internes (nested class en anglais) sont un mcanisme qui permet davoir des
classes dfinies lintrieur dautres classes.
Cela peut tre utile si vous souhaitez restreindre laccs dune classe uniquement sa
classe mre.
Par exemple :
public class Chien
{
private Coeur coeur = new Coeur();
public void Mourir()
{

coeur.Stop();
}
private class Coeur
{
public void Stop()
{
Console.WriteLine("The end");
}
}
}

Ici, la classe Cur ne peut tre utilise que par la classe Chien car elle est prive. Nous
pourrions galement mettre la classe en protected afin quune classe drive de la classe
Chien puisse galement utiliser la classe Cur :
public class ChienSamois : Chien
{
private Coeur autreCoeur = new Coeur();
}

Avec ces niveaux de visibilit, une autre classe comme la classe Chat ne pourra pas se
servir de ce cur.
Si nous mettons la classe Cur en public ou internal, elle sera utilisable par tout le monde
; comme une classe normale. Dans ce cas, nos chats pourront avoir :
public class Chat
{
private Chien.Coeur coeur = new Chien.Coeur();
public void Mourir()
{
coeur.Stop();
}
}

Notez que nous prfixons la classe par le nom de la classe qui contient la classe Cur.
Dans ce cas, lintrt dutiliser une classe interne est moindre. Cela permet ventuellement
de regrouper les classes de manire smantique, et encore, cest plutt le but des espaces
de noms.
Voil pour les classes internes. Cest une fonctionnalit souvent peu utilise, mais voil,
vous la connaissez dsormais .

Les types anonymes et le mot cl var


Le mot-cl var est un truc de feignant !
Il permet de demander au compilateur de dduire le type dune variable au moment o
nous la dclarons.
Par exemple le code suivant :
var prenom = "Nicolas";
var age = 30;

est quivalent au code suivant :


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

Le mot-cl var sert indiquer que nous ne voulons pas nous proccuper de ce quest le
type et que cest au compilateur de le trouver.

Cela implique quil faut que la variable soit initialise (et non nulle) au moment o elle est
dclare afin que le compilateur puisse dduire le type de la variable, en loccurrence, il
devine quen lui affectant Nicolas , il sagit dune chane de caractres.
Je ne recommande pas lutilisation de ce mot-cl, car le fait de ne pas mettre le type de la
variable fait perdre de la clart au code. Ici, cest facile. On dduit facilement nous aussi
que le type de prenom est string et que le type de age est int. Mais cest aussi parce quon
est trop fort !
Par contre, si jamais la variable est initialise grce une mthode :
var calcul = GetCalcul();

il va falloir aller regarder la dfinition de la mthode afin de savoir le type de retour et


connaitre ainsi le type de la variable. Des contraintes dont on na pas besoin alors quil est
aussi simple dindiquer le vrai type et que cest dautant plus lisible.
Mais alors, pourquoi en parler ?
Parce que ce mot-cl peut servir dans un cas prcis, celui des types anonymes. Lorsquil
est conjointement utilis avec loprateur new, il permet de crer des types anonymes,
cest--dire des types dont on na pas dfini la classe au pralable. Une classe sans nom.
Cette classe est dduite grce son initialisation :
var unePersonneAnonyme = new { Prenom = "Nico", Age = 30 };

Ici nous crons une variable qui contient une proprit Prenom et une proprit Age.
Forcment, il est impossible de donner un type cette variable vu quelle na pas de
dfinition. Cest pour cela quon utilise le mot-cl var. On sait juste que la variable
unePersonneAnonyme possde deux proprits, un prnom et un ge. En interne, le
compilateur va gnrer un nom de classe pour ce type anonyme, mais il na pas de sens
pour nous.
En loccurrence, si nous crivons le code suivant o GetType() (hrit de la classe object)
renvoie le nom du type :
var unePersonneAnonyme = new { Prenom = "Nico", Age = 30 };
Console.WriteLine(unePersonneAnonyme.GetType());

nous aurons :

Ce qui est effectivement sans intrt pour nous !


Jusquici, les types anonymes peuvent sembler ne pas apporter dintrt, tant il est simple
de dfinir une classe Personne possdant une proprit Prenom et une proprit Age. Mais
cela permet dutiliser ces classes comme des classes usage unique lorsque nous ne
souhaitons pas nous encombrer dun fichier possdant une classe qui va nous servir
uniquement un seul endroit.
Paresse, souci de clart du code, tout ceci est un peu ml dans la cration dun type
anonyme. vous de lutiliser quand bon vous semble.
Vous verrez plus tard que les types anonymes sont souvent utiliss dans les mthodes
dextensions Linq. Nous en reparlerons.
noter que lorsque nous crons un tableau, par exemple :
string[] jours = new string[] { "Lundi", "Mardi", "Mercredi", "Jeudi", "Vendredi", "Samedi",
"Dimanche" };

il est galement possible de lcrire ainsi :


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

cest--dire sans prciser le type du tableau aprs le new.


Le compilateur dduit le type partir de linitialisation. Ce nest pas un type anonyme en
soit, mais le principe de dduction est le mme.
En rsum

Une interface est un contrat que sengage respecter un objet.


Il est possible dimplmenter plusieurs interfaces dans une classe.
Une classe abstraite est une classe qui possde au moins une mthode ou proprit
abstraite. Elle ne peut pas tre instancie.
Une classe concrte drivant dune classe abstraite est oblige de substituer les
membres abstraits.
Il est possible de crer des types anonymes grce loprateur new suivi de la
description des proprits de ce type. Les instances sont manipules grce au mot-cl
var.

Chapitre 24

TP Programmation Oriente Objet

Dans ce TP, nous allons essayer de mettre en pratique ce que nous avons appris en
programmation oriente objet avec le C#.
Difficile davoir un exercice qui ncessite toutes les notions que nous avons apprises.
Aussi, certaines sont laisses de ct.
Avec ce TP, vous aurez loccasion de vous entraner crer des classes, manipuler
lhritage et vous pourrez vous confronter des situations un peu diffrentes de la thorie
!
Le but du TP est de crer une mini application de gestion bancaire o nous pourrons grer
des comptes qui peuvent faire des oprations bancaires entre eux. Ne vous attendez pas
non plus refaire les applications de la banque de France, on est l pour sentrainer. Ce
TP sera dcompos en deux parties.

Instructions pour raliser le TP


Alors voici lnonc de la premire partie du TP.
Dans notre mini application bancaire tous les montants utiliss seront des dcimaux.
Nous allons devoir grer des comptes bancaires. Un compte est compos :
Dun Solde (calcul, mais non modifiable) ;
Dun Propritaire (nom du propritaire du compte) ;
Dune liste doprations interne permettant de garder lhistorique du compte, non
accessible par les autres objets ;
Dune mthode permettant de Crediter() le compte, prenant une somme en
paramtre ;
Dune mthode permettant de Crediter() le compte, prenant une somme et un
compte en paramtres, crditant le compte et dbitant le compte pass en paramtres ;
Dune mthode permettant de Debiter() le compte, prenant une somme en paramtre
;
Dune mthode permettant de Dbiter() le compte, prenant une somme et un compte
bancaire en paramtres, dbitant le compte et crditant le compte pass en paramtres
;
Dune mthode qui permet dafficher le rsum dun compte.

Un compte courant est une sorte de compte et est compos :


De tout ce qui compose un compte ;
Dun dcouvert autoris, non modifiable, dfini louverture du compte ;
Le rsum dun compte courant affiche le solde, le propritaire, le dcouvert autoris
ainsi que les oprations sur le compte.
Un compte pargne entreprise est une sorte de compte et est compos :
De tout ce qui compose un compte ;
Dun taux dabondement, dfini louverture du compte en fonction de
lanciennet du salari. Un taux est un double compris entre 0 et 1. (5% = 0.05) ;
Le solde doit tenir compte du taux dabondement, mais labondement nest pas
calcul lors des transactions car lentreprise verse labondement quand elle le
souhaite ; le rsum dun compte pargne entreprise affiche le solde, le
propritaire, le taux dabondement ainsi que les oprations sur le compte.
Une opration bancaire est compose :
Dun montant ;
Dun type de mouvement, crdit ou dbit.
Crer une telle application avec les informations suivantes :
Le compte courant de Nicolas a un dcouvert autoris de 2000
Le compte pargne entreprise de Nicolas a un taux de 2%
Le compte courant de Jrmie a un dcouvert autoris de 500
Nicolas touche son salaire de 100, pas cher pay !
Il fait le plein de sa voiture : 50
Il met de ct sur son compte pargne entreprise la coquette somme de 20
Puis il reoit un cadeau de la banque de 100 car il a ouvert son compte pendant la
priode promotionnelle
Il remet ses 20 sur son compte bancaire car finalement, il ne se sent pas mga en
scurit
Jrmie achte un nouveau PC : 500
Puis il rembourse ses dettes Nicolas : 200
Lapplication doit indiquer les soldes de chacun de ces comptes :
Solde compte courant de Nicolas : 250
Solde compte pargne de Nicolas : 102,00
Solde compte courant de Jrmie : -700

Ensuite, nous afficherons le rsum du compte courant de Nicolas et de son compte


pargne entreprise. Ce qui nous donnera :
Rsum du compte de Nicolas
*******************************************
Compte courant de Nicolas
Solde : 250
Dcouvert autoris : 2000

Oprations :
+100
-50
-20
+20
+200
*******************************************

Rsum du compte pargne de Nicolas


########################################################
Compte pargne entreprise de Nicolas
Solde : 102,00
Taux : 0,02

Oprations :
+20
+100
-20
########################################################

Bon, jai bien expliqu, ce nest pas si compliqu, sauf si bien sr vous navez pas lu ou
pas compris les chapitres prcdents. Dans ce cas, nhsitez pas y rejeter un coup dil.
Sinon, il suffit de bien dcomposer tout en crant les classes les unes aprs les autres.
Bon courage !

Correction
Ne regardez pas tout de suite la correction. Faites le TP. Il est important de manipuler les
classes. Vous allez faire a trs rgulirement. Cela doit devenir un rflexe et pour que
cela le devienne, il faut pratiquer !
Toujours est-il que voici ma correction. Peut-tre que le plus dur est de modliser
correctement lapplication ?
Il y a plusieurs solutions bien sr pour crer ce petit programme, voici celle que je
propose.
Tout dabord, nous devons manipuler des comptes courant et des comptes pargne
entreprise, qui sont des sortes de comptes.
On en dduit quun compte courant hrite dun compte. De mme, un compte pargne
entreprise hrite galement dun compte. Dailleurs, un compte a-t-il vraiment une raison
dtre part entire ? Nous ne crons jamais de compte gnraliste , seulement des
comptes spcialiss. Nous allons donc crer la classe compte en tant que classe abstraite.
Ce qui nous donnera les classes suivantes :
public abstract class Compte
{
}
public class CompteCourant : Compte
{
}
public class CompteEpargneEntreprise : Compte
{
}

Nous avons galement dit quun compte tait compos dune liste doprations qui
possdent un montant et un type de mouvement. Le type de mouvement pouvant prendre 2

valeurs, il parait logique dutiliser une numration :


public enum Mouvement
{
Credit,
Debit
}

La classe dopration tant :


public class Operation
{
public Mouvement TypeDeMouvement { get; set; }
public decimal Montant { get; set; }
}

Voil pour la base.


Ensuite, la classe Compte doit possder un nom de propritaire et un solde qui peut tre
redfini dans la classe CompteEpargneEntreprise. Ce solde est calcul partir dune liste
doprations. Le solde est donc une proprit en lecture seule, virtuelle car ncessitant
dtre redfinie :
public abstract class Compte
{
protected List<Operation> listeOperations;
public string Proprietaire { get; set; }
public virtual decimal Solde
{
get
{
decimal total = 0;
foreach (Operation operation in listeOperations)
{
if (operation.TypeDeMouvement == Mouvement.Credit)
total += operation.Montant;
else
total -= operation.Montant;
}
return total;
}
}
}

La liste des oprations est une variable membre, dclare en protected car nous allons en
avoir besoin dans la classe CompteCourant qui en hrite, afin dafficher un rsum des
oprations.
La proprit Solde nest pas trs complique en soit, il suffit de parcourir la liste des
oprations et en fonction du type de mouvement, ajouter ou retrancher le montant. Comme
cest une proprit en lecture seule, seul le get est dfini.
Noubliez pas que la liste doit tre initialise avant dtre utilise, sinon nous aurons une
erreur. Le constructeur est un endroit appropri pour le faire :
public abstract class Compte
{
// [...]
// [Code prcdent enlev pour plus de lisibilit]
// [...]
public Compte()
{
listeOperations = new List<Operation>();
}
}

La classe doit ensuite possder des mthodes permettant de crditer ou de dbiter le

compte, ce qui donne :


public abstract class Compte
{
// [...]
// [Code prcdent enlev pour plus de lisibilit]
// [...]
public void Crediter(decimal montant)
{
Operation operation = new Operation { Montant = montant, TypeDeMouvement = Mouvement.Credit};
listeOperations.Add(operation);
}
public void Crediter(decimal montant, Compte compte)
{
Crediter(montant);
compte.Debiter(montant);
}
public void Debiter(decimal montant)
{
Operation operation = new Operation { Montant = montant, TypeDeMouvement = Mouvement.Debit };
listeOperations.Add(operation);
}
public void Debiter(decimal montant, Compte compte)
{
Debiter(montant);
compte.Crediter(montant);
}
}

Le principe est de crer une opration et de lajouter la liste des oprations. Lastuce ici
consiste rutiliser les mthodes de la classe pour crire les autres formes des mthodes,
ce qui simplifie le travail et facilitera les ventuelles futures oprations de maintenance.
Enfin, chaque classe drive de la classe Compte doit afficher un rsum du compte. Nous
pouvons donc forcer ces classes devoir implmenter cette mthode en utilisant une
mthode abstraite :
public abstract class Compte
{
// [...]
// [Code prcdent enlev pour plus de lisibilit]
// [...]
public abstract void AfficherResume();
}

Voil pour la classe Compte. En toute logique, cest elle qui contient le plus de mthodes
afin de factoriser le maximum de code commun dans la classe mre.
Passons la classe CompteEpargneEntreprise, qui hrite de la classe Compte. Elle possde
un taux dabondement qui est dfini la cration du compte. Il est donc ici intressant de
forcer le positionnement de ce taux lors de la cration de la classe, cest--dire en utilisant
un constructeur avec un paramtre :
public class CompteEpargneEntreprise : Compte
{
private double tauxAbondement;
public CompteEpargneEntreprise(double taux)
{
tauxAbondement = taux;
}
}

Ce taux est utilis pour calculer le solde du compte en faisant la somme de toutes les

oprations et en appliquant le taux. Ce qui revient appeler le calcul du solde de la classe


mre et lui appliquer le taux. Nous substituons donc la proprit Solde et utilisons le
calcul fait dans la classe Compte:
public class CompteEpargneEntreprise : Compte
{
// [...]
// [Code prcdent enlev pour plus de lisibilit]
// [...]
public override decimal Solde
{
get
{
return base.Solde * (decimal)(1 + tauxAbondement);
}
}
}

Rien de plus simple, en utilisant le mot-cl base pour appeler le solde de la classe mre.
Vous noterez galement que nous avons eu besoin de caster le taux qui est un double afin
de pouvoir le multiplier un dcimal.
Enfin, cette classe se doit de fournir une implmentation de la mthode AfficherResume()
:
public class CompteEpargneEntreprise : Compte
{
// [...]
// [Code prcdent enlev pour plus de lisibilit]
// [...]
public override void AfficherResume()
{
Console.WriteLine("########################################################");
Console.WriteLine("Compte pargne entreprise de " + Proprietaire);
Console.WriteLine("\tSolde : " + Solde);
Console.WriteLine("\tTaux : " + tauxAbondement);
Console.WriteLine("\n\nOprations :");
foreach (Operation operation in listeOperations)
{
if (operation.TypeDeMouvement == Mouvement.Credit)
Console.Write("\t+");
else
Console.Write("\t-");
Console.WriteLine(operation.Montant);
}
Console.WriteLine("########################################################");
}
}

Il sagit dune banale mthode daffichage o nous parcourons la liste contenant les
oprations et affichons le montant en fonction du type de mouvement.
Voil pour la classe CompteEpargneEntreprise.
Il ne reste plus que la classe CompteCourant qui doit galement driver de Compte :
public class CompteCourant : Compte
{
}

Un compte courant doit possder un dcouvert autoris, dfini la cration du compte


courant. Nous utilisons comme avant un constructeur avec un paramtre :
public class CompteCourant : Compte
{
private decimal decouvertAutorise;
public CompteCourant(decimal decouvert)
{

decouvertAutorise = decouvert;
}
}

Il ne restera plus qu fournir une implmentation de la mthode daffichage du rsum :


public class CompteCourant : Compte
{
// [...]
// [Code prcdent enlev pour plus de lisibilit]
// [...]
public override void AfficherResume()
{
Console.WriteLine("*******************************************");
Console.WriteLine("Compte courant de " + Proprietaire);
Console.WriteLine("\tSolde : " + Solde);
Console.WriteLine("\tDcouvert autoris : " + decouvertAutorise);
Console.WriteLine("\n\nOprations :");
foreach (Operation operation in listeOperations)
{
if (operation.TypeDeMouvement == Mouvement.Credit)
Console.Write("\t+");
else
Console.Write("\t-");
Console.WriteLine(operation.Montant);
}
Console.WriteLine("*******************************************");
}
}

Voil pour nos objets. Cela en fait un petit paquet. Mais ce nest pas fini, il faut maintenant
crer des comptes et faire des oprations.
Lnonc consiste faire les instanciations suivantes, depuis notre mthode Main() :
CompteCourant compteNicolas = new CompteCourant(2000) { Proprietaire = "Nicolas" };
CompteEpargneEntreprise compteEpargneNicolas = new CompteEpargneEntreprise(0.02) { Proprietaire =
"Nicolas" };
CompteCourant compteJeremie = new CompteCourant(500) { Proprietaire = "Jrmie" };
compteNicolas.Crediter(100);
compteNicolas.Debiter(50);
compteEpargneNicolas.Crediter(20, compteNicolas);
compteEpargneNicolas.Crediter(100);
compteEpargneNicolas.Debiter(20, compteNicolas);
compteJeremie.Debiter(500);
compteJeremie.Debiter(200, compteNicolas);
Console.WriteLine("Solde compte courant de " + compteNicolas.Proprietaire + " : " +
compteNicolas.Solde);
Console.WriteLine("Solde compte pargne de " + compteEpargneNicolas.Proprietaire + " : " +
compteEpargneNicolas.Solde);
Console.WriteLine("Solde compte courant de " + compteJeremie.Proprietaire + " : " +
compteJeremie.Solde);
Console.WriteLine("\n");

Rien dextraordinaire.
Il ne reste plus qu afficher le rsum des deux comptes demands :
Console.WriteLine("Rsum du compte de Nicolas");
compteNicolas.AfficherResume();
Console.WriteLine("\n");
Console.WriteLine("Rsum du compte pargne de Nicolas");
compteEpargneNicolas.AfficherResume();
Console.WriteLine("\n");

Nous en avons fini avec la premire partie du TP !


Si vous avez bien compris comment construire des classes, comment les faire hriter entre

elles et les interfaces, ce TP na pas d vous poser trop de problmes.

Aller plus loin


Il y a plusieurs choses que je naime pas dans ce code. La premire est la rptition. Dans
les deux mthodes AfficherResume() des classes CompteCourant et
CompteEpargneEntreprise on affiche les oprations de la mme faon. Si jamais je dois
modifier ce code (bug ou volution), je devrais le faire dans les deux classes, au risque
den oublier une. Il faut donc factoriser ce code. Il existe pour cela plusieurs solutions
simples.
La premire est dutiliser une mthode statique pour afficher la liste des oprations. Cette
mthode pourrait tre situe dans une classe utilitaire qui prend en paramtre la liste des
oprations :
public static class Helper
{
public static void AfficheOperations(List<Operation> operations)
{
Console.WriteLine("\n\nOprations :");
foreach (Operation operation in operations)
{
if (operation.TypeDeMouvement == Mouvement.Credit)
Console.Write("\t+");
else
Console.Write("\t-");
Console.WriteLine(operation.Montant);
}
}
}

Il ne reste plus qu utiliser cette mthode statique depuis nos mthodes


AfficherResume(), par exemple pour le compte pargne entreprise :
public override void AfficherResume()
{
Console.WriteLine("########################################################");
Console.WriteLine("Compte pargne entreprise de " + Proprietaire);
Console.WriteLine("\tSolde : " + Solde);
Console.WriteLine("\tTaux : " + tauxAbondement);
Helper.AfficheOperations(listeOperations);
Console.WriteLine("########################################################");
}

La deuxime solution est de crer la mthode AfficheOperations() dans la classe abstraite


Compte, ce qui permet de saffranchir de passer la liste en paramtres vu que la classe
Compte la connait dj :
protected void AfficheOperations()
{
Console.WriteLine("\n\nOprations :");
foreach (Operation operation in listeOperations)
{
if (operation.TypeDeMouvement == Mouvement.Credit)
Console.Write("\t+");
else
Console.Write("\t-");
Console.WriteLine(operation.Montant);
}
}

La mthode a tout intrt tre dclare en protected, afin quelle puisse servir aux
classes filles mais pas lextrieur. Elle sutilisera de cette faon, par exemple dans la

classe CompteEpargneEntreprise :
public override void AfficherResume()
{
Console.WriteLine("########################################################");
Console.WriteLine("Compte pargne entreprise de " + Proprietaire);
Console.WriteLine("\tSolde : " + Solde);
Console.WriteLine("\tTaux : " + tauxAbondement);
AfficheOperations();
Console.WriteLine("########################################################");
}

Ds que lon peut factoriser du code, il ne faut pas hsiter. Si nous avons demain besoin de
crer un nouveau type de compte, nous serons ravis de pouvoir nous servir de mthodes
toutes prtes nous simplifiant le travail.
Il pourrait tre intressant galement dencapsuler lenregistrement dune opration dans
une mthode. Ce qui permet de moins se rpter (mme si ici, le code est vraiment petit)
mais qui permet surtout de sparer la logique denregistrement dune opration afin que
cela soit plus facile ultrieurement modifier, maintenir ou complexifier. Par exemple, via
une mthode :
private void EnregistrerOperation(Mouvement typeDeMouvement, decimal montant)
{
Operation operation = new Operation { Montant = montant, TypeDeMouvement = typeDeMouvement};
listeOperations.Add(operation);
}

Cela permet galement de faire en sorte que le type de mouvement soit un paramtre de la
mthode.

Deuxime partie du TP
Nous voici maintenant dans la deuxime partie du TP. La banque souhaite proposer un
nouveau type de compte, le livret ToutBnef. Dans cette banque, le livret ToutBnf
fonctionne comme le compte pargne entreprise. Cest--dire quil accepte un taux en
paramtres et applique ce taux au moment de la restitution du solde.
La premire ide qui vient lesprit est crer une classe LivretToutBenef qui hrite de
CompteEpargneEntreprise. Mais ceci pose un problme si jamais le compte pargne
entreprise doit voluer, et cest justement ce que le directeur de la banque vient de me
confier. Donc, il vous interdit juste titre dhriter de ses fonctionnalits.
Ce que vous allez donc faire ici, cest de considrer que le fait quun compte puisse faire
des bnfices soit en fait un comportement qui est fourni au moment o on instancie un
compte. Il existe plusieurs comportements dont on doit fournir les implmentations :
Le comportement de bnfice taux fixe
Le comportement de bnfice alatoire
Le comportement o il ny a aucun bnfice
Chaque comportement est une classe qui respecte le contrat suivant :
public interface ICalculateurDeBenefice
{
decimal CalculeBenefice(decimal solde);
double Taux { get; }
}

crivez donc ces trois classes de comportement ainsi que le livret ToutBnf qui possde
un taux fixe de 2.75% et qui a t crdit une premire fois de 800 et une seconde fois de
200. Affichez enfin son rsum qui devra tenir compte du taux du calculateur de
bnfice.
Rcrivez ensuite la classe CompteCourant de manire ce quelle ait un comportement o
il ny a pas de bnfice.
Enfin, la classe CompteEpargneEntreprise subira une volution pour fonctionner avec un
comportement de bnfice alatoire (tir entre 0 et 1).
Cest parti.

Correction
Ici cest un peu plus compliqu. Vous ntes sans doute pas compltement familiers avec
les notions dinterfaces, aussi avant de vous livrer la correction, je vais vous donner
quelques pistes.
Le fait davoir un comportement est finalement trs simple. Il suffit davoir un membre
priv dans la classe LivretToutBenef du type de linterface. Ce membre priv sera affect
la valeur passe en paramtre du constructeur. Cest--dire :
public class LivretToutBenef : Compte
{
private ICalculateurDeBenefice calculateurDeBenefice;
public LivretToutBenef(ICalculateurDeBenefice calculateur)
{
calculateurDeBenefice = calculateur;
}
}

Dsormais, les oprations devront se faire avec cette variable, calculateurDeBenefice. On


aura galement besoin dinstancier au pralable le comportement voulu et de le passer en
paramtres du constructeur. Retournez donc tenter de raliser ce TP avant de voir la suite
de la correction .
Cest fait ? Alors voici ma correction.
Nous avions donc cette interface impose :
public interface ICalculateurDeBenefice
{
decimal CalculeBenefice(decimal solde);
double Taux { get; }
}

Nous avons besoin dcrire plusieurs classes qui implmentent cette interface. La premire
est la classe de bnfice taux fixe :
public class BeneficeATauxFixe : ICalculateurDeBenefice
{
private double taux;
public BeneficeATauxFixe(double tauxFixe)
{
taux = tauxFixe;
}
public decimal CalculeBenefice(decimal solde)
{
return solde * (decimal)(1 + taux);
}

public double Taux


{
get
{
return taux;
}
}
}

Nous avons dit quelle devait prendre un taux en paramtres, le constructeur est lendroit
indiqu pour cela. Ensuite, la mthode de calcul est trs simple, il suffit dappliquer la
formule au solde. Enfin, la proprit retourne le taux.
La classe suivante est la classe de bnfice alatoire. L, pas besoin de paramtres, il suffit
de tirer le nombre alatoire dans le constructeur grce la mthode NextDouble(), ce qui
donne :
public class BeneficeAleatoire : ICalculateurDeBenefice
{
private double taux;
private Random random;
public BeneficeAleatoire()
{
random = new Random();
taux = random.NextDouble();
}
public decimal CalculeBenefice(decimal solde)
{
return solde * (decimal)(1 + taux);
}
public double Taux
{
get
{
return taux;
}
}
}

Enfin, il faudra crer la classe avec aucun bnfice :


public class AucunBenefice : ICalculateurDeBenefice
{
public decimal CalculeBenefice(decimal solde)
{
return solde;
}
public double Taux
{
get
{
return 0;
}
}
}

Rien de sorcier !
L o cela se complique un peu, cest pour la classe LivretToutBenef. Elle doit bien sr
driver de la classe de base Compte et possder un membre priv de type
ICalculateurDeBenefice :
public class LivretToutBenef : Compte
{
private ICalculateurDeBenefice calculateurDeBenefice;
public LivretToutBenef(ICalculateurDeBenefice calculateur)

{
calculateurDeBenefice = calculateur;
}
public override decimal Solde
{
get
{
return calculateurDeBenefice.CalculeBenefice(base.Solde);
}
}
public override void AfficherResume()
{
Console.WriteLine("^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^");
Console.WriteLine("Livret ToutBnf de " + Proprietaire);
Console.WriteLine("\tSolde : " + Solde);
Console.WriteLine("\tTaux : " + calculateurDeBenefice.Taux);
AfficheOperations();
Console.WriteLine("^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^");
}
}

Dans le constructeur, la variable est initialise avec un objet de type


ICalculateurDeBenefice. Ensuite, pour calculer le solde, il suffit dappeler la mthode
CalculeBenefice avec le solde de base en paramtres. De mme, pour faire apparatre le
taux dans le rsum, on pourra utiliser la proprit Taux de linterface.
Il ne reste qu souscrire un Livret ToutBnf en lui passant un objet de bnfice taux
fixe en paramtre du constructeur, et de faire les oprations bancaires demandes. Ce qui
donne :
ICalculateurDeBenefice beneficeATauxFixe = new BeneficeATauxFixe(0.275);
LivretToutBenef livretToutBenefNicolas = new LivretToutBenef(beneficeATauxFixe);
livretToutBenefNicolas.Crediter(800);
livretToutBenefNicolas.Crediter(200);
Console.WriteLine("Rsum du livret Toutbnef");
livretToutBenefNicolas.AfficherResume();
Console.WriteLine("\n");

Ce qui donne :
Rsum du livret Toutbnef
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
Livret ToutBnf de
Solde : 1275,000
Taux : 0,275

Oprations :
+800
+200
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

Maintenant, nous devons adapter la classe CompteEpargneEntreprise pour quelle puisse


fonctionner avec un comportement. Cela devient tout simple et se rapproche beaucoup du
livret ToutBnf :
public class CompteEpargneEntreprise : Compte
{
private ICalculateurDeBenefice calculateurDeBenefice;
public CompteEpargneEntreprise(ICalculateurDeBenefice calculateur)
{
calculateurDeBenefice = calculateur;
}
public override decimal Solde
{
get

{
return calculateurDeBenefice.CalculeBenefice(base.Solde);
}
}
public override void AfficherResume()
{
Console.WriteLine("########################################################");
Console.WriteLine("Compte pargne entreprise de " + Proprietaire);
Console.WriteLine("\tSolde : " + Solde);
Console.WriteLine("\tTaux : " + calculateurDeBenefice.Taux);
AfficheOperations();
Console.WriteLine("########################################################");
}
}

Il faut maintenant changer linstanciation de lobjet CompteEpargneEntreprise en lui


passant en paramtres un objet de type bnfice alatoire :
ICalculateurDeBenefice beneficeAleatoire = new BeneficeAleatoire();
CompteEpargneEntreprise compteEpargneNicolas = new CompteEpargneEntreprise(beneficeAleatoire) {
Proprietaire = "Nicolas" };

Reste le compte courant qui suit le mme principe :


public class CompteCourant : Compte
{
private decimal decouvertAutorise;
private ICalculateurDeBenefice calculateurDeBenefice;
public CompteCourant(decimal decouvert, ICalculateurDeBenefice calculateur)
{
decouvertAutorise = decouvert;
calculateurDeBenefice = calculateur;
}
public override decimal Solde
{
get
{
return calculateurDeBenefice.CalculeBenefice(base.Solde);
}
}
public override void AfficherResume()
{
Console.WriteLine("*******************************************");
Console.WriteLine("Compte courant de " + Proprietaire);
Console.WriteLine("\tSolde : " + Solde);
Console.WriteLine("\tDcouvert autoris : " + decouvertAutorise);
Console.WriteLine("\tTaux : " + calculateurDeBenefice.Taux);
AfficheOperations();
Console.WriteLine("*******************************************");
}
}

Que lon pourra instancier avec un objet de type aucun bnfice :


ICalculateurDeBenefice aucunBenefice = new AucunBenefice();
CompteCourant compteNicolas = new CompteCourant(2000, aucunBenefice) { Proprietaire = "Nicolas" };

Et voil.
Lavantage ici est davoir spar les responsabilits dans diffrentes classes. Si jamais
nous crons un nouveau compte qui est rmunr grce un bnfice taux fixe, il suffira
de rutiliser ce comportement et le tour est jou.
noter que les trois calculs de la proprit Solde sont identiques, il pourrait tre judicieux
de le factoriser dans la classe mre Compte. Ceci implique que la classe mre possde ellemme le membre protg du type de linterface.
Voil pour ce TP. Jespre que vous aurez russi avec brio toutes les crations de classes et

que vous ne vous tes pas mlangs dans les mots-cls.


Vous verrez que vous aurez trs souvent besoin dcrire des classes dans ce genre afin de
crer une application. Cest un lment indispensable du C# quil est primordial de
matriser.
Nhsitez pas faire des variations sur ce TP ou crer dautres petits programmes
simples vous permettant de vous entrainer.
Voil pour ce TP. Jespre que vous aurez russi avec brio toutes les crations de classes et
que vous ne vous tes pas mlangs dans les mots-cls.
Vous verrez que vous aurez trs souvent besoin dcrire des classes de ce genre afin de
crer une application. Cest un lment indispensable du C# quil est primordial de
matriser.
Nhsitez pas faire des variations sur ce TP ou crer dautres petits programmes
simples vous permettant de vous entrainer.

Chapitre 25

Mode de passage des paramtres une mthode

Dans les chapitres prcdents, nous avons dcrit comment on pouvait passer des
paramtres une mthode. Nous avons galement vu que les types du framework .NET
pouvaient tre des types valeur ou des types rfrence. Ceci influence la faon dont sont
traits les paramtres dune mthode. Nous allons ici prciser un peu ce fonctionnement.
Dans ce chapitre, je vais illustrer mes propos en utilisant des mthodes statiques crites
dans la classe Program, gnre par Visual C# Express. Le but est de simplifier lcriture et
de ne pas sencombrer dobjets inutiles. videmment, tout ce que nous allons voir
fonctionne galement de la mme faon avec les mthodes non statiques prsentes dans
des objets.

Passage de paramtres par valeur


Tout au dbut du tutoriel, nous avons appel des mthodes en passant des types simples
(int, string, etc.). Nous avons vu quil sagissait de types valeur qui possdent
directement la valeur dans la variable.
Lorsque nous passons des types valeur en paramtre dune mthode, nous utilisons un
passage de paramtre par valeur.
Dcortiquons lexemple suivant :
static void Main(string[] args)
{
int age = 30;
Doubler(age);
Console.WriteLine(age);
}
public static void Doubler(int valeur)
{
valeur = valeur * 2;
Console.WriteLine(valeur);
}

Nous dclarons dans la mthode Main() une variable age, de type entier, laquelle nous
affectons la valeur 30.
Nous appelons ensuite la mthode Doubler() en lui passant cette variable en paramtre.
Ce quil se passe cest que le compilateur fait une copie de la valeur de la variable age
pour la mettre dans la variable valeur de la mthode Doubler(). La variable valeur a une
porte gale au corps de la mthode Doubler().
Nous modifions ensuite la valeur de la variable valeur en la multipliant par deux. tant

donn que la variable valeur a reu une copie de la variable age, cest--dire que le
compilateur a dupliqu la valeur 30, le fait de modifier la variable valeur ne change en
rien la valeur de la variable age.
En effet, si nous excutons le code du dessus, nous allons avoir :

Nous passons 30 la mthode Doubler() qui calcule le double de la variable valeur. On


affiche 60. Lorsquon revient dans la mthode Main(), nous retrouvons la valeur initiale de
la variable age. Elle na bien sr pas t modifie car la mthode Doubler() a travaill sur
une copie.
Rappelez-vous que ceci est possible car les types intgrs sont facilement copiables, car
peu volus.

Passage de paramtres en mise jour


Oui mais si je veux modifier la valeur ?
Pour pouvoir modifier la valeur du paramtre pass, il faut indiquer que la variable est en
mode mise jour . Cela se fait grce au mot-cl ref que nous pourrons utiliser ainsi
:
static void Main(string[] args)
{
int age = 30;
Doubler(ref age);
Console.WriteLine(age);
}
public static void Doubler(ref int valeur)
{
valeur = valeur * 2;
}

Comme on peut sen douter, ce code affiche 60.


Le mot-cl ref sutilise avant la dfinition du paramtre dans la mthode. Cela implique

quil soit galement utilis au moment dappeler la mthode.


Vous aurez remarqu que le mot-cl utilis est ref et quil ressemble beaucoup au mot
rfrence . Ce nest videmment pas un hasard.
En fait, avec ce mot-cl nous demandons au compilateur de passer en paramtre une
rfrence vers la variable age plutt que den faire une copie. Ainsi, la mthode Doubler()
rcupre une rfrence et la variable valeur rfrence alors la mme valeur que la variable
age. Ceci implique que toute modification de la valeur rfrence provoque un
changement sur la variable source puisque les variables rfrencent la mme valeur.
Voil pourquoi la variable est modifie aprs le passage dans la mthode Doubler().
Bien sr, il aurait tout fait t possible de faire en sorte que la mthode Doubler()
renvoie un entier contenant la valeur passe en paramtre multiplie par 2 :
static void Main(string[] args)
{
int age = 30;
age = Doubler(age);
Console.WriteLine(age);
}
public static int Doubler(int valeur)
{
return valeur * 2;
}

Cest ce que nous avons toujours fait auparavant. Voici donc une autre faon de faire qui
peut tre bien utile quand il y a plus dune valeur renvoyer.

Passage des objets par rfrence


Cest aussi comme a que cela fonctionne pour les objets. Nous avons vu que les variables
qui stockent des objets possdent en fait la rfrence de lobjet. Le fait de passer un objet
une mthode quivaut passer la rfrence de lobjet en paramtres. Ainsi, cest comme si
on utilisait le mot-cl ref implicitement.
Le code suivant :
static void Main(string[] args)
{
Voiture voiture = new Voiture { Couleur = "Grise" };
Repeindre(voiture);
Console.WriteLine(voiture.Couleur);
}
public static void Repeindre(Voiture voiture)
{
voiture.Couleur = "Bleue";
}

va donc crer un objet Voiture et le passer en paramtres la mthode Repeindre().


Comme Voiture est un type rfrence, il ny a pas de duplication de lobjet et une
rfrence est passe la mthode.
Lorsque nous modifions la proprit Couleur de la voiture, nous modifions bien le mme
objet que celui prsent dans la mthode Main().
Nous aurons donc :

Il est noter quand mme que la variable voiture de la mthode Repeindre est une copie
de la variable voiture de la mthode Main() qui contiennent toutes les deux une rfrence
vers lobjet de type Voiture. Cela veut dire que lon accde bien au mme objet, do le
rsultat, mais que les deux variables sont indpendantes.
Si nous modifions directement la variable, avec par exemple :
static void Main(string[] args)
{
Voiture voiture = new Voiture { Couleur = "Grise" };
Repeindre(voiture);
Console.WriteLine(voiture.Couleur);
}
public static void Repeindre(Voiture voiture)
{
voiture.Couleur = "Bleue";
voiture = null;
}

Alors, ce code continuera fonctionner, car la variable voiture de la mthode Main() ne


vaut pas null. Le fait de modifier la variable de la mthode Repeindre, qui est une copie,
naffecte en rien la variable de la mthode Main().
Par contre, elle est affecte si nous utilisons le mot-cl ref. Par exemple le code suivant :
static void Main(string[] args)
{
Voiture voiture = new Voiture { Couleur = "Grise" };
Repeindre(ref voiture);
Console.WriteLine(voiture.Couleur);
}
public static void Repeindre(ref Voiture voiture)
{
voiture.Couleur = "Bleue";
voiture = null;
}

provoquera une erreur. En effet, cette fois-ci, cest bien la rfrence qui est passe nulle
et pas une copie de la variable contenant la rfrence
Une subtile diffrence.

Passage de paramtres en sortie


Enfin, le dernier mode de passage est le passage de paramtres en sortie. Il permet de faire
en sorte quune mthode force linitialisation dune variable et que lappelant rcupre la
valeur initialise.
Nous avons dj vu ce mode de passage quand nous avons tudi les conversions.
Souvenez-vous de ce code :
string chaine = "1234";
int nombre;
if (int.TryParse(chaine, out nombre))
Console.WriteLine(nombre);
else
Console.WriteLine("Conversion impossible");

La mthode TryParse permet de tester la conversion dune chane. Elle renvoie vrai ou
faux en fonction du rsultat de la conversion et met jour lentier qui est pass en
paramtre en utilisant le mot-cl out .
Si la conversion russit, alors lentier nombre est initialis avec la valeur de la conversion,
calcule dans la mthode TryParse.
Nous pouvons utiliser ensuite la variable nombre car le mot-cl out garantit que la variable
sera initialise dans la mthode.
En effet, si nous prenons lexemple suivant :
static void Main(string[] args)
{
int age = 30;
int ageDouble;
Doubler(age, out ageDouble);
}
public static void Doubler(int age, out int resultat)
{
}

Nous aurons une erreur de compilation :


Le paramtre de sortie 'resultat' doit tre assign avant que le contrle quitte la mthode actuelle

En effet, il faut absolument que la variable resultat qui est marque en sortie ait une
valeur avant de pouvoir sortir de la mthode.
Nous pourrons corriger cet exemple avec :
static void Main(string[] args)
{
int age = 30;
int ageDouble;
Doubler(age, out ageDouble);
}
public static void Doubler(int age, out int resultat)
{
resultat = age * 2;
}

Aprs lappel de la mthode Doubler(), ageDouble vaudra donc 60.


Oui, mais quel intrt par rapport un return normal ?

Ici, aucun. Cela est pertinent quand nous souhaitons renvoyer plusieurs valeurs, comme
cest le cas pour la mthode TryParse qui renvoie le rsultat de la conversion et si la
conversion sest bien passe.
Bien sr, si lon naime pas trop le mot-cl out, il est toujours possible de crer un objet
contenant deux valeurs que lon retournera lappelant, par exemple :
public class Program
{
static void Main(string[] args)
{
string nombre = "1234";
Resultat resultat = TryParse(nombre);
if (resultat.ConversionOk)
Console.WriteLine(resultat.Valeur);
}
public static Resultat TryParse(string chaine)
{
Resultat resultat = new Resultat();
int valeur;
resultat.ConversionOk = int.TryParse(chaine, out valeur);
resultat.Valeur = valeur;
return resultat;
}
}
public class Resultat
{
public bool ConversionOk { get; set; }
public int Valeur { get; set; }
}

Ici, notre mthode TryParse renvoie un objet Resultat qui contient les deux valeurs
rsultantes de la conversion.
En rsum

Le type dune variable passe en paramtres dune mthode influence la faon dont
elle est traite.
Un passage par valeur effectue une copie de la valeur de la variable et la met dans la
variable de la mthode.
Un passage par rfrence effectue une copie de la rfrence mais continue de pointer
sur le mme objet.
On utilise le mot-cl ref pour passer une variable de type valeur une mthode afin
de la modifier.

Chapitre 26

Les structures

Nous allons aborder dans ce chapitre les structures qui sont une nouvelle sorte dobjets
que nous pouvons crer dans des applications C#.
Les structures sont presque comme des classes. Elles permettent galement de crer des
objets, possdent des variables ou proprits, des mthodes et mme un constructeur, mais
avec quelques subtiles diffrences
Dcouvrons les ds prsent.

Une structure est presque une classe


Une des premires diffrences est la faon dont .NET gre ces deux objets. Nous avons vu
que les classes taient des types rfrence. Les variables de type rfrence ne possdent
donc pas la valeur de lobjet mais une rfrence vers cet objet en mmoire. La structure
quant elle est un type valeur et contient donc directement la valeur de lobjet.
Une autre diffrence est que la structure, bien qutant un objet, ne peut pas utiliser les
principes dhritage. On ne peut donc pas hriter dune structure et une structure ne peut
pas hriter des comportements dun objet.

quoi sert une structure ?


Les structures vont tre utiles pour stocker des petits objets qui vont avoir tendance tre
souvent manipuls, comme les int ou les bool que nous avons dj vus.
La raison tient dans un seul mot : performance.
tant gres en mmoire diffremment, les structures sont optimises pour amliorer les
performances des petits objets. Comme il ny a pas de rfrence, on utilisera directement
lobjet sans aller le chercher via sa rfrence. On gagne donc un peu de temps lorsquon a
besoin de manipuler ces donnes.
Cest tout fait pertinent pour des programmes o la vitesse est dterminante, comme les
jeux vido.
Donc dans ce cas, autant utiliser tout le temps des structures, non ?

Eh bien non, dj vous vous priveriez de tous les mcanismes dhritage que nous avons
vus. Ensuite, si nous surchargeons trop la mmoire avec des structures, loptimisation
prvue par .NET risque de se retourner contre nous et notre application pourrait tre plus
lente que si nous avions utilis des classes.
Dune manire gnrale, et moins de savoir exactement ce que vous faites ou davoir
mesur les performances, vous allez utiliser plus gnralement les classes que les
structures.
Vous pouvez ce sujet aller lire les recommandations de Microsoft.

Crer une structure


Pour crer une structure, nous utiliserons le mot-cl struct, comme nous avions utilis le
mot-cl class pour crer une classe :
public struct Personne
{
public string Prenom { get; set; }
public int Age { get; set; }
}

Pour instancier cette structure, nous pourrons utiliser le mot-cl new, comme pour les
classes. La diffrence est que la variable sera un type valeur, avec les consquences que ce
type impose en matire de gestion en mmoire ou de passages par paramtres :
Personne nicolas = new Personne() { Prenom = "Nicolas", Age = 30 };
Console.WriteLine(nicolas.Prenom + " a " + nicolas.Age + " ans");

Comme nous avons dit, il est impossible quune structure hrite dune autre structure ou
dun objet. Sauf bien sr du fameux type de base object, pour qui cest automatique. Une
structure hrite donc des quelques mthodes dObject (comme ToString()) que nous
pouvons ventuellement spcialiser :
public struct Personne
{
public string Prenom { get; set; }
public int Age { get; set; }
public override string ToString()
{
return Prenom + " a " + Age + " ans";
}
}

Et nous pourrons avoir :


Personne nicolas = new Personne() { Prenom = "Nicolas", Age = 30 };
Console.WriteLine(nicolas.ToString());

Qui renverra :
Nicolas a 30 ans

Comme pour les classes, il est possible davoir des constructeurs sur une structure
lexception du constructeur par dfaut qui est interdit.
Aussi le code suivant :
public struct Personne
{
public Personne()
{

}
}

provoquera lerreur de compilation suivante :


Les structures ne peuvent pas contenir de constructeurs exempts de paramtres explicites

Par contre, les autres formes des constructeurs sont possibles, comme :
public struct Personne
{
private int age;
public Personne(int agePersonne)
{
age = agePersonne;
}
}

Qui sutilisera comme pour une classe :


Personne nicolas = new Personne(30);

Attention, si vous tentez dutiliser des proprits ou des mthodes dans le constructeur
dune structure, vous allez avoir un problme.
Par exemple le code suivant :
public struct Personne
{
private int age;
public Personne(int agePersonne)
{
AffecteAge(agePersonne);
}
private void AffecteAge(int agePersonne)
{
age = agePersonne;
}
}

provoquera les erreurs de compilation suivantes :


L'objet 'this' ne peut pas tre utilis avant que tous ses champs soient assigns
Le champ 'MaPremiereApplication.Personne.age' doit tre totalement assign avant que le contrle soit
retourn l'appelant

Alors quavec une classe, ce code serait tout fait correct.


Pour corriger ceci, il faut absolument initialiser tous les champs avant de faire quoi que ce
soit avec lobjet, comme lindique lerreur.
Nous pourrons par exemple faire :
public struct Personne
{
private int age;
public Personne(int agePersonne)
{
age = 0;
AffecteAge(agePersonne);
}
private void AffecteAge(int agePersonne)
{
age = agePersonne;
}
}

Ce qui peut sembler tout fait inutile dans ce cas-l. Mais comme le compilateur fait
certaines vrifications, il sera impossible de compiler un code de ce genre sans que toutes

les variables soient initialises explicitement.


Par contre, vous aurez un souci si vous utilisez des proprits automatiques. Si nous
tentons de faire :
public struct Personne
{
public int Age { get; set; }
public Personne(int agePersonne)
{
Age = agePersonne;
}
}

nous nous retrouverons avec la mme erreur de compilation.


Pour la corriger, il faudra appeler le constructeur par dfaut de la structure qui va
permettre dinitialiser toutes les variables de la classe :
public struct Personne
{
public int Age { get; set; }
public Personne(int agePersonne) : this()
{
Age = agePersonne;
}
}

Cela se fait comme pour les classes, en utilisant le mot-cl this suivi de parenthses qui
permettront dappeler le constructeur par dfaut.
Rappelez-vous que le constructeur par dfaut soccupe dinitialiser toutes les variables
dune classe ou dune structure.

Passage de structures en paramtres


Comme il sagit dun type valeur, chaque fois que nous passerons une structure en
paramtres dune mthode, une copie de lobjet sera faite.
Ainsi, tout fait logiquement, le code suivant :
static void Main(string[] args)
{
Personne nicolas = new Personne() { Age = 30 };
FaitVieillir(nicolas);
Console.WriteLine(nicolas.Age);
}
private static void FaitVieillir(Personne personne)
{
personne.Age++;
}

affichera 30 bien que nous modifions lge de la personne dans la mthode.


Comme nous lavons dj vu, la mthode travaille sur une copie de la structure.
Cela veut bien sr dire que si nous souhaitons modifier une structure partir dune
mthode, nous devrons utiliser le mot-cl ref :
static void Main(string[] args)
{
Personne nicolas = new Personne() { Age = 30 };
FaitVieillir(ref nicolas);
Console.WriteLine(nicolas.Age);
}
private static void FaitVieillir(ref Personne personne)

{
personne.Age++;
}

Cela vaut pour tous les types valeur.


Prenez quand mme garde. Si la structure est trs grosse, le fait den faire une copie
chaque utilisation de mthode risque dtre particulirement chronophage et pourra
perturber les performances de votre application.

Dautres structures ?
Vous laurez peut-tre devin, mais les entiers que nous avons dj vus et que nous
utilisions grce au mot-cl int sont en fait des structures.
tant trs souvent utiliss et nayant pas non plus normment de choses stocker, ils sont
crs en tant que structures et sont optimiss par .NET pour que nos applications
sexcutent de faon optimale. Ce qui est un choix tout fait pertinent.
Cest le cas galement pour les bool, les double, etc
Toutefois, dautres objets, comme la classe String, sont bel et bien des classes.
Dune manire gnrale, vous allez crer peu de structures en tant que dbutant. Il sera
plus judicieux de crer des classes ds que vous en avez besoin. En effet, plus vos objets
sont gros et plus ils auront intrt tre des classes pour viter dtre recopis chaque
utilisation.
Lutilisation de structures pourra se rvler pertinente dans des situations bien prcises,
mais en gnral, il faut bien maitriser les rouages du framework .NET pour que les
bnfices de leurs utilisations se fassent ressentir.
Dans tous les cas, il sera important de mesurer (avec par exemple des outils de profilage)
le gain de temps avant de mettre des structures partout.
En rsum

Les structures sont des types valeur qui sont optimiss par le framework .NET.
Une structure est un objet qui ressemble beaucoup une classe, mais qui possde des
restrictions.
Les structures possdent des proprits, des mthodes, des constructeurs, etc.
Il nest pas possible dutiliser lhritage avec les structures.

Chapitre 27

Les gnriques

Les gnriques sont une fonctionnalit du framework .NET apparus avec la version 2.
Vous vous en souvenez peut-tre, nous avons cit le mot dans le chapitre sur les tableaux
et les listes. Ils permettent de crer des mthodes ou des classes qui sont indpendantes
dun type. Il est trs important de connatre leur fonctionnement car cest un mcanisme
cl qui permet de faire beaucoup de choses, notamment en termes de rutilisabilit et
damlioration des performances.
Noubliez pas vos tubes daspirine et voyons prsent de quoi il retourne !

Quest-ce que les gnriques ?


Avec les gnriques, vous pouvez crer des mthodes ou des classes qui sont
indpendantes dun type. On les appellera des mthodes gnriques et des types
gnriques.
Nous en avons dj utilis, rappelez-vous, avec la liste. La liste est une classe comme nous
en avons dj vu plein, sauf quelle a la capacit dtre utilise avec nimporte quel autre
type, comme les entiers, les chanes de caractres, les voitures
Cela permet dviter de devoir crer une classe ListeInt, une classe ListeString, une
classe ListeVoiture, etc On pourra utiliser cette classe avec tous les types grce aux
chevrons :
List<string> listeChaine = new List<string>();
List<int> listeEntier = new List<int>();
List<Voiture> listeVoiture = new List<Voiture>();

Nous indiquons entre les chevrons le type qui sera utilis avec le type gnrique.
Oui mais, si nous voulons pouvoir mettre nimporte quel type dobjet dans une
liste, il suffirait de crer une ListeObject ? Puisque tous les objets drivent
dobject
En fait, cest le choix qui avait t fait dans la toute premire version de .NET. On utilisait
lobjet ArrayList qui possde une mthode Add prenant en paramtre un object. Cela
fonctionne. Sauf que nous nous trouvions faces des limitations :
Premirement, nous pouvions mlanger nimporte quel type dobjet dans la liste, des
entiers, des voitures, des chiens, etc. Cela devenait une classe fourre-tout et nous ne

savions jamais ce quil y avait dans la liste.


Deuximement, mme si nous savions quil ny avait que des entiers dans la liste,
nous tions obligs de le traiter en tant quobject et donc dutiliser le boxing et
lunboxing pour mettre les objets dans la liste ou pour les rcuprer.
Cela engendrait donc confusion et perte de performance. Grce aux gnriques, il devenait
donc possible de crer des listes de nimporte quel type et nous tions certains du type que
nous allions rcuprer dans la liste.

Les types gnriques du framework .NET


Le framework .NET possde beaucoup de classes et dinterfaces gnriques, notamment
dans lespace de nom System.Collections.Generic.
La liste est la classe gnrique que vous utiliserez srement le plus. Mais beaucoup
dautres sont votre disposition. Citons par exemple la classe Queue<> qui permet de grer
une file dattente style FIFO (first in, first out : premier entr, premier sorti) :
Queue<int> file = new Queue<int>();
file.Enqueue(3);
file.Enqueue(1);
int valeur = file.Dequeue(); // valeur contient 3
valeur = file.Dequeue(); // valeur contient 1

Citons encore le dictionnaire dlment qui est une espce dannuaire o lon accde aux
lments grce une cl :
Dictionary<string, Personne> annuaire = new Dictionary<string, Personne>();
annuaire.Add("06 01 02 03 04", new Personne { Prenom = "Nicolas"});
annuaire.Add("06 06 06 06 06", new Personne { Prenom = "Jeremie" });
Personne p = annuaire["06 06 06 06 06"]; // p contient la proprit Prenom valant Jeremie

Loin de moi lide de vous numrer toutes les collections gnriques du framework
.NET, le but est de vous montrer rapidement quil existe beaucoup de classes gnriques
dans le framework .NET.

Crer une mthode gnrique


Nous commenons cerner lintrt des gnriques. Sachez quil est bien sr possible de
crer vos propres classes gnriques ou vos propres mthodes.
Commenons par les mthodes, cela sera plus simple.
Il est globalement intressant dutiliser un type gnrique partout o nous pourrions avoir
un object.
Nous avions cr une classe AfficheRepresentation() qui prenait un objet en paramtres,
ce qui pourrait tre :
public static class Afficheur
{
public static void Affiche(object o)
{
Console.WriteLine("Afficheur d'objet :");
Console.WriteLine("\tType : " + o.GetType());
Console.WriteLine("\tReprsentation : " + o.ToString());

}
}

Nous avons ici utilis une classe statique permettant dafficher le type dun objet et sa
reprsentation. Nous pouvons lutiliser ainsi :
int i = 5;
double d = 9.5;
string s = "abcd";
Voiture v = new Voiture();

Afficheur.Affiche(i);
Afficheur.Affiche(d);
Afficheur.Affiche(s);
Afficheur.Affiche(v);

Rappelez-vous, chaque fois quon passe dans cette mthode, lobjet est box en type
object quand il sagit dun type valeur.
Nous pouvons amliorer cette mthode en crant une mthode gnrique. Regardons ce
code :
public static class Afficheur
{
public static void Affiche<T>(T a)
{
Console.WriteLine("Afficheur d'objet :");
Console.WriteLine("\tType : " + a.GetType());
Console.WriteLine("\tReprsentation : " + a.ToString());
}
}

Cette mthode fait exactement la mme chose mais avec les gnriques.
Dans un premier temps, la mthode annonce quelle va utiliser un type gnrique
reprsent par la lettre T entre chevrons.
Il est conventionnel que les types gnriques soient utiliss avec T.
Cela veut dire que tout type utilis dans cette mthode dclar avec T sera du type pass
la mthode. Ainsi, la variable a est du type gnrique qui sera prcis lors de lappel
cette mthode.
Comme a est un objet, nous pouvons appeler la mthode GetType() et la mthode
ToString() sur cet objet.
Pour afficher un objet, nous pourrons faire :
int i = 5;
double d = 9.5;
string s = "abcd";
Voiture v = new Voiture();

Afficheur.Affiche<int>(i);
Afficheur.Affiche<double>(d);
Afficheur.Affiche<string>(s);
Afficheur.Affiche<Voiture>(v);

Dans le premier appel, nous indiquons que nous souhaitons afficher i dont le type
gnrique est int. Ce quil se passe, cest comme si le CLR crait la surcharge de la
mthode Affiche, prenant un entier en paramtre :
public static void Affiche(int a)
{
Console.WriteLine("Afficheur d'objet :");
Console.WriteLine("\tType : " + a.GetType());
Console.WriteLine("\tReprsentation : " + a.ToString());

De mme pour laffichage suivant, o lon indique le type double entre les chevrons. Cest
comme si le CLR crait la surcharge prenant un double en paramtre :
public static void Affiche(double a)
{
Console.WriteLine("Afficheur d'objet :");
Console.WriteLine("\tType : " + a.GetType());
Console.WriteLine("\tReprsentation : " + a.ToString());
}

Et ceci pour tous les types utiliss, savoir ici int, double, string et Voiture.
noter que dans cet exemple, nous pouvons remplacer les quatre lignes suivantes :
Afficheur.Affiche<int>(i);
Afficheur.Affiche<double>(d);
Afficheur.Affiche<string>(s);
Afficheur.Affiche<Voiture>(v);

Par :
Afficheur.Affiche(i);
Afficheur.Affiche(d);
Afficheur.Affiche(s);
Afficheur.Affiche(v);

En effet, nul besoin de prciser quel type nous souhaitons traiter ici, le compilateur est
assez malin pour le dduire du type de la variable. La variable i tant un entier, il est
obligatoire que le type gnrique soit un entier. Il est donc facultatif ici de le prciser.
Une fois quil est prcis entre les chevrons, le type gnrique sutilise dans la mthode
comme nimporte quel autre type. Nous pouvons avoir autant de paramtres gnriques
que nous le voulons dans les paramtres et utiliser le type gnrique dans le corps de la
mthode. Par exemple la mthode suivante :
public static void Echanger<T>(ref T t1, ref T t2)
{
T temp = t1;
t1 = t2;
t2 = temp;
}

permet dchanger le contenu de deux variables entre elles. Cest donc une mthode
gnrique puisquelle prcise entre les chevrons que nous pourrons utiliser le type T.
En paramtres de la mthode, nous passons deux variables de types gnriques.
Dans le corps de la mthode, nous crons une variable du type gnrique qui sert de
mmoire temporaire puis nous changeons les rfrences des deux variables.
Nous pourrons utiliser cette mthode ainsi :
int i = 5;
int j = 10;
Echanger(ref i, ref j);
Console.WriteLine(i);
Console.WriteLine(j);
Voiture v1 = new Voiture { Couleur = "Rouge" };
Voiture v2 = new Voiture { Couleur = "Verte" };
Echanger(ref v1, ref v2);
Console.WriteLine(v1.Couleur);
Console.WriteLine(v2.Couleur);

Qui donnera :
10
5
Verte

Rouge

Il est bien sr possible de crer des mthodes prenant en paramtres plusieurs types
gnriques diffrents. Il suffit alors de prciser autant de types diffrents entre les
chevrons quil y a de types gnriques diffrents :
static void Main(string[] args)
{
int i = 5;
int j = 5;
double d = 9.5;
Console.WriteLine(EstEgal(i, j));
Console.WriteLine(EstEgal(i, d));
}
public static bool EstEgal<T, U>(T t, U u)
{
return t.Equals(u);
}

Ici, la mthode EstEgal() prend en paramtres potentiellement deux types diffrents. Nous
lappelons une premire fois avec deux entiers et ensuite avec un entier et un double.

Crer une classe gnrique


Une classe gnrique fonctionne comme pour les mthodes. Cest une classe o nous
pouvons indiquer de 1 N types gnriques. Cest comme cela que fonctionne la liste que
nous avons dj beaucoup manipule.
En fait, la liste nest quune espce de tableau volu. Nous pourrions trs bien imaginer
crer notre propre liste sur ce principe, sachant que cest compltement absurde car elle
sera forcment moins bien que cette classe, mais cest pour ltude.
Le principe est davoir un tableau plus ou moins dynamique qui grossit si jamais le
nombre dlments devient trop grand pour sa capacit.
Pour dclarer une classe gnrique, nous utiliserons nouveau les chevrons la fin de la
ligne qui dclare la classe :
public class MaListeGenerique<T>
{
}

Nous allons raliser une implmentation toute basique de cette classe histoire de voir un
peu quoi ressemble une classe gnrique. Cette classe na dintrt que pour tudier les
gnriques, vous lui prfrerez videmment la classe List<> du framework .NET.
Nous avons besoin de trois variables prives. La capacit de la liste, le nombre dlments
dans la liste et le tableau gnrique.
public class MaListeGenerique<T>
{
private int capacite;
private int nbElements;
private T[] tableau;
public MaListeGenerique()
{
capacite = 10;
nbElements = 0;
tableau = new T[capacite];
}
}

Notons la dclaration du tableau. Il utilise le type gnrique. Concrtement, cela veut dire
que quand nous utiliserons la liste avec un entier, nous aurons un tableau dentiers.
Lorsque nous utiliserons la liste avec un objet Voiture, nous aurons un tableau de Voiture,
etc.
Nous initialisons ces variables membres dans le constructeur, en dcidant compltement
arbitrairement que la capacit par dfaut de notre liste est de 10 lments. La dernire
ligne instancie le tableau en lui indiquant sa taille.
Il reste implmenter lajout dans la liste :
public class MaListeGenerique<T>
{
[Code enlev pour plus de clart]
public void Ajouter(T element)
{
if (nbElements >= capacite)
{
capacite *= 2;
T[] copieTableau = new T[capacite];
for (int i = 0; i < tableau.Length; i++)
{
copieTableau[i] = tableau[i];
}
tableau = copieTableau;
}
tableau[nbElements] = element;
nbElements++;
}
}

Il sagit simplement de mettre la valeur que lon souhaite ajouter lemplacement adquat
dans le tableau. Nous le mettons en dernire position, cest--dire lemplacement
correspondant au nombre dlments.
Au dbut, nous avons commenc par vrifier si le nombre dlments tait suprieur la
capacit du tableau. Si cest le cas, alors nous devons augmenter la capacit du tableau,
jai ici dcid encore compltement arbitrairement que je doublais la capacit. Il ne reste
plus qu crer un nouveau tableau de cette nouvelle capacit et copier les lments du
premier tableau dans celui-ci.
Vous aurez not que le paramtre de la mthode Ajouter est bien du type gnrique.
Pour le plaisir, rajoutons enfin une mthode permettant de rcuprer llment un indice
donn :
public class MaListeGenerique<T>
{
[Code enlev pour plus de clart]
public T ObtenirElement(int indice)
{
return tableau[indice];
}
}

Il sagit juste daccder au tableau pour renvoyer la valeur lindice concern. Llment
intressant ici est de constater que le type de retour de la mthode est bien du type
gnrique.
Cette liste peut sutiliser de la manire suivante :
MaListeGenerique<int> maListe = new MaListeGenerique<int>();
maListe.Ajouter(25);
maListe.Ajouter(30);
maListe.Ajouter(5);

Console.WriteLine(maListe.ObtenirElement(0));
Console.WriteLine(maListe.ObtenirElement(1));
Console.WriteLine(maListe.ObtenirElement(2));
for (int i = 0; i < 30; i++)
{
maListe.Ajouter(i);
}

Ici, nous utilisons la liste avec un entier, mais elle fonctionnerait tout aussi bien avec un
autre type.
Nhsitez pas passer en debug dans la mthode Ajouter() pour observer ce quil se passe
exactement lors de laugmentation de capacit.
Voil comment on cre une classe gnrique !
Une fois quon a compris que le type gnrique sutilise comme nimporte quel autre type,
cela devient assez facile.
Rappelez-vous, toute classe qui manipule des object est susceptible dtre amliore en
utilisant les gnriques.

La valeur par dfaut dun type gnrique


Vous aurez remarqu dans limplmentation de la liste du dessus que si nous essayons
dobtenir un lment du tableau un indice qui nexiste pas, nous aurons une erreur. Ce
comportement est une bonne chose, il est important de grer les cas limites. En
loccurrence ici, on dlgue au tableau la gestion du cas limite.
On pourrait envisager de grer nous-mmes ce cas limite en affichant un message et en
renvoyant une valeur nulle. Seulement, pour un objet Voiture, qui est un type rfrence, il
est tout fait pertinent davoir null. Mais pour un int, qui est un type valeur, cela na pas
de sens.
Cest l quintervient le mot-cl default.
Comme son nom lindique, il renvoie la valeur par dfaut du type. Pour un type rfrence,
cest null, pour un type valeur cest 0. Ce qui donnerait :
public T ObtenirElement(int indice)
{
if (indice < 0 || indice >= nbElements)
{
Console.WriteLine("L'indice n'est pas bon");
return default(T);
}
return tableau[indice];
}

Les interfaces gnriques


Les interfaces peuvent aussi tre gnriques. Dailleurs, a me fait penser que plus haut, je
vous ai indiqu quun code tait moche et que jen parlerai plus tard
Il sagissait du chapitre sur les interfaces o nous avions implments linterface
IComparable.
Nous souhaitions comparer des voitures entre elles et nous avions obtenu le code suivant :
public class Voiture : IComparable

{
public string Couleur { get; set; }
public string Marque { get; set; }
public int Vitesse { get; set; }
public int CompareTo(object obj)
{
Voiture voiture = (Voiture)obj;
return Vitesse.CompareTo(voiture.Vitesse);
}
}

Je souhaite pouvoir comparer des voitures entre elles, mais le framework .NET me fournit
un object en paramtres de la mthode CompareTo(). Quelle ide ! Comme si je voulais
pouvoir comparer des voitures avec des chats ou des chiens.
Cela me force en plus faire un cast. Pourquoi il ne me passe pas directement une Voiture
en paramtres ?
Vous le sentez venir et vous avez raison. Un object ! Berk, oserais-je dire ! Et sans parler
des performances.
Cest l o les gnriques vont pouvoir voler notre secours. Linterface IComparable date
de la premire version du framework .NET. Le C# ne possdait pas encore les types
gnriques.
Depuis leur apparition, il est possible dimplmenter la version gnrique de cette
interface.
Pour cela, nous faisons suivre linterface du type que nous souhaitons utiliser entre les
chevrons. Cela donne :
public class Voiture : IComparable<Voiture>
{
public string Couleur { get; set; }
public string Marque { get; set; }
public int Vitesse { get; set; }
public int CompareTo(Voiture obj)
{
return Vitesse.CompareTo(obj.Vitesse);
}
}

Nous devons toujours implmenter la mthode CompareTo() sauf que nous avons
dsormais un objet Voiture en paramtres, ce qui nous vite de le caster.

Les restrictions sur les types gnriques


Une mthode ou une classe gnrique cest bien, mais peut-tre voulons-nous quelles ne
fonctionnent pas avec tous les types.
Aussi, le C# permet de dfinir des restrictions sur les types gnriques. Pour ce faire, on
utilise le mot-cl where.
Il existe 6 types de restrictions :
Contrainte

Description

where T : struct

Le type gnrique doit tre un type valeur

where T : class

Le type gnrique doit tre un type rfrence

where T : new()

Le type gnrique doit possder un constructeur par dfaut

where T : IMonInterface Le type gnrique doit implmenter linterface IMonInterface


where T : MaClasse

Le type gnrique doit driver de la classe MaClasse

where T1 : T2

Le type gnrique doit driver du type gnrique T2

Par exemple, nous pouvons dfinir une restriction sur une mthode gnrique afin quelle
naccepte en type gnrique que des types qui implmentent une interface.
Soit linterface suivante :
public interface IVolant
{
void DeplierLesAiles();
void Voler();
}

Qui est implmente par deux objets :


public class Avion : IVolant
{
public void DeplierLesAiles()
{
Console.WriteLine("Je dplie mes ailes mcaniques");
}
public void Voler()
{
Console.WriteLine("J'allume le moteur");
}
}
public class Oiseau : IVolant
{
public void DeplierLesAiles()
{
Console.WriteLine("Je dplie mes ailes d'oiseau");
}
public void Voler()
{
Console.WriteLine("Je bats des ailes");
}
}

Nous pouvons crer une mthode gnrique qui soccupe dinstancier ces objets et
dappeler les mthodes de linterface :
public static T Creer<T>() where T : IVolant, new()
{
T t = new T();
t.DeplierLesAiles();
t.Voler();
return t;
}

Ici, la restriction se porte sur deux niveaux. Il faut dans un premier temps que le type
gnrique implmente linterface IVolant et possde galement un constructeur, bref quil
soit instanciable.
Nous pouvons donc utiliser cette mthode de cette faon :
Oiseau oiseau = Creer<Oiseau>();
Avion a380 = Creer<Avion>();

Nous appelons la mthode Crer() avec le type gnrique Oiseau, qui implmente bien
IVolant et qui est aussi instanciable. Grce cela, nous pouvons utiliser loprateur new
pour crer notre type gnrique, appeler les mthodes de linterface et renvoyer linstance.
Ce qui donne :

Je dplie mes ailes doiseau


Je bats des ailes
Je dplie mes ailes mcaniques
Jallume le moteur

Si nous tentons dutiliser la mthode avec un type qui nimplmente pas linterface
IVolant, comme :
Voiture v = Creer<Voiture>();

Nous aurons lerreur de compilation suivante :


Le type 'MaPremiereApplication.Voiture' ne peut pas tre utilis comme paramtre de type 'T' dans le
type ou la mthode gnrique 'MaPremiereApplication.Program.Creer<T>()'. Il n'y a pas de conversion
de rfrence implicite de 'MaPremiereApplication.Voiture' en 'MaPremiereApplication.IVolant'.

Globalement, il nous dit que lobjet Voiture nimplmente pas IVolant.


Oui mais dans ce cas, plutt que dutiliser une mthode gnrique, pourquoi la
mthode ne renvoie pas IVolant ?
Cest une judicieuse remarque. Mais qui ncessite quelques modifications de code. En
effet, il faudrait indiquer quel type instancier.
Nous pourrions par exemple faire :
public enum TypeDeVolant
{
Oiseau,
Avion
}
public static IVolant Creer(TypeDeVolant type)
{
IVolant volant;
switch (type)
{
case TypeDeVolant.Oiseau:
volant = new Oiseau();
break;
case TypeDeVolant.Avion:
volant = new Avion();
break;
default:
return null;
}
volant.DeplierLesAiles();
volant.Voler();
return volant;
}

Et instancier nos objets de cette faon :


Oiseau oiseau = (Oiseau)Creer(TypeDeVolant.Oiseau);
Avion a380 = (Avion)Creer(TypeDeVolant.Avion);

Ce qui complique un peu les choses et rajoute des cast dont on pourrait volontiers se
passer. De plus, si nous crons un nouvel objet qui implmente cette interface, il faudrait
tout modifier.
Avouez quavec les types gnriques, cest quand mme plus propre.
Nous pouvons bien sr avoir des restrictions sur les types gnriques dune classe.
Pour le montrer, nous allons crer une classe dont lobjectif est davoir des types valeur
qui pourraient ne pas avoir de valeur. Pour les types rfrence, il suffit dutiliser le mot-cl
null. Mais pour les types valeur comme les entiers, nous navons rien pour indiquer que
ceux-ci nont pas de valeur.

Par exemple :
public class TypeValeurNull<T> where T : struct
{
private bool aUneValeur;
public bool AUneValeur
{
get { return aUneValeur; }
}
private T valeur;
public T Valeur
{
get
{
if (aUneValeur)
return valeur;
throw new InvalidOperationException();
}
set
{
aUneValeur = true;
valeur = value;
}
}
}

Ici, nous utilisons une classe possdant un type gnrique qui sera un type valeur, grce
la condition where T : struct. Cette classe encapsule le type gnrique pour indiquer
avec un boolen si le type a une valeur ou pas.
Ne faites pas attention la ligne :
throw new InvalidOperationException();

qui permet juste de renvoyer une erreur, nous tudierons les exceptions un peu plus loin.
Elle pourra sutiliser ainsi :
TypeValeurNull<int> entier = new TypeValeurNull<int>();
if (!entier.AUneValeur)
{
Console.WriteLine("l'entier n'a pas de valeur");
}
entier.Valeur = 5;
if (entier.AUneValeur)
{
Console.WriteLine("Valeur de l'entier : " + entier.Valeur);
}

Et si nous souhaitons avoir pareil pour un autre type valeur, il ny a rien faire de plus :
TypeValeurNull<double> valeur = new TypeValeurNull<double>();

Cest quand mme super pratique comme classe !! Mais ne rvons-pas, cette ide ne vient
pas de moi. Cest en fait une fonctionnalit du framework .NET : les types nullables.

Les types nullables


En fait, la classe que nous avons vue au-dessus existe dj dans le framework .NET, et en
mieux ! videmment. Elle permet de faire exactement ce que jai dcrit, cest--dire
permettre un type valeur davoir une valeur nulle.
Elle est mieux faite dans la mesure o elle tire parti de certaines fonctionnalits du
framework .NET qui en simplifie lcriture. Il sagit de la classe Nullable<>.
Aussi, nous pourrons crer un entier pouvant tre null grce au code suivant :

Nullable<int> entier = null;


if (!entier.HasValue)
{
Console.WriteLine("l'entier n'a pas de valeur");
}
entier = 5;
if (entier.HasValue)
{
Console.WriteLine("Valeur de l'entier : " + entier.Value);
}

Le principe est grosso modo le mme sauf que nous pouvons utiliser le mot-cl null ou
affecter directement la valeur lentier en utilisant loprateur daffectation, sans passer
par la proprit Valeur. Il peut aussi tre compar au mot-cl null ou tre utilis avec
loprateur +, etc. Ceci est possible grce certaines fonctionnalits du C# que nous
navons pas vues et qui sortent de ltude de ce tutoriel.
Cette classe est tellement pratique que le compilateur a t optimis pour simplifier son
criture. En effet, utiliser Nullable<> est un peu long pour nous autres informaticiens qui
sommes des paresseux.
Aussi, lcriture :
Nullable<int> entier = null;

peut se simplifier en :
int? entier = null;

cest le point dinterrogation qui remplace la dclaration de la classe Nullable<>.


En rsum

Avec les gnriques, vous pouvez crer des mthodes ou des classes qui sont
indpendantes dun type. On les appellera des mthodes gnriques et des types
gnriques.
On utilise les chevrons <> pour indiquer le type dune classe ou dune mthode
gnrique.
Les interfaces peuvent aussi tre gnriques, comme linterface IEnumerable<>.
Les types nullables constituent un exemple dutilisation trs pratique des classes
gnriques.

Chapitre 28

TP types gnriques

Ahh, un peu de pratique histoire de vrifier que nous avons bien compris les gnriques.
Cest un concept assez facile apprhender mais relativement difficile mettre en uvre.
Quand en ai-je besoin ? Comment ?
Voici donc un petit exercice qui va vous permettre dessayer de mettre en uvre une
classe gnrique.

Instructions pour raliser la premire partie du TP


Dans la premire partie du TP, nous allons raliser une liste chane. Il sagit du grand
classique des TP dinformatique en C. Je vous rappelle le principe.
La liste chane permet de naviguer dlment en lment. Quand nous sommes sur le
premier lment, le suivant est accessible par sa proprit Suivant. Lorsque nous accdons
au suivant, llment prcdent est accessible par la proprit Precedent et le suivant
toujours accessible par la proprit Suivant. Sil ny a pas de prcdent ou pas de suivant,
llment est null :

Si on insre un lment la position 1, les autres se dcalent :

Voil, il faut donc crer une telle liste chane dlments. Le but est bien sr de faire en
sorte que llment soit gnrique.
Nhsitez pas rflchir un peu avant de vous lancer. Cela pourrait paratre un peu
simpliste, mais en fait cela occasionne quelques nuds au cerveau.
Toujours est-il que je souhaiterais disposer dune proprit en lecture seule permettant
daccder au premier lment ainsi quune autre proprit galement en lecture seule
permettant daccder au dernier lment. Bien sr, il faut pouvoir naviguer dlment en
lment avec des proprits prcdent et suivant.
Il faut videmment une mthode permettant dajouter un lment la fin de la liste. Nous
aurons galement besoin dune mthode permettant daccder un lment partir de son
indice et enfin dune mthode permettant dinsrer un lment un indice, dcalant tous
les suivants. Voil pour la cration de la classe !
Ensuite, notre programme instanciera notre liste chane pour lui ajouter les entiers 5, 10
et 4. Puis nous afficherons les valeurs de cette liste en nous basant sur la premire
proprit et en naviguant dlment en lment.
Nous afficherons ensuite les diffrents lments en utilisant la mthode daccs un
lment par son indice.
Enfin, nous insrerons la valeur 99 la premire position (position 0), puis la valeur 33
la deuxime position et enfin la valeur 30 nouveau la deuxime position.
Puis nous afficherons tout ce beau monde.
Fin de lnonc, ouf !
Pour ceux qui nont pas besoin daide, les explications sont termines. Ouvrez vos Visual
C# Express (ou vos Visual Studio si vous tes riches ) et vos claviers.
Pour les autres, je vais essayer de vous guider un peu plus en essayant tout de mme de ne
pas trop vous donner dindications non plus.
En fait, votre liste chaine nest pas vraiment une liste, comme pourrait ltre la List<>
que nous connaissons. Cette liste chaine possde un point dentre qui est le premier
lment. Lajout du premier lment est trs simple, il suffit de mettre jour une proprit.
Pour ajouter llment suivant, il faut en fait brancher la proprit Suivant du premier
lment llment que nous sommes en train dajouter. Et inversement, la proprit
Precedent de llment que nous souhaitons ajouter sera mise jour avec le premier
lment.
On se rend compte que llment est un peu plus complexe quun simple type. Nous allons
donc avoir une classe gnrique possdant trois proprits (Precedent, Suivant et Valeur).
Et nous aurons galement une classe du mme type gnrique possdant la proprit

Premier et la proprit Dernier et les mthodes dajout, dobtention de llment et

dinsertion.
Allez, je vous en ai assez dit. vous de jouer !

Correction
Pas si facile hein ?
Mais bon, comme vous tes super entrains, cela na pas d vous poser trop de problmes.
Voici la correction que je propose.
La premire chose faire est de crer la classe gnrique permettant de stocker un lment
:
public class Chainage<T>
{
public Chainage<T> Precedent { get; set; }
public Chainage<T> Suivant { get; set; }
public T Valeur { get; set; }
}

Cest une classe gnrique toute simple qui possde une valeur du type gnrique et deux
proprits du mme type que llment pour obtenir le prcdent ou le suivant.
Peut-tre que la plus grande difficult rside ici, de bien modliser la classe qui permet
dencapsuler llment.
Il faudra ensuite crer la liste gnrique et ses mthodes :
public class ListeChainee<T>
{
}

La liste chaine possde galement un type gnrique. Nous crons sa proprit Premier :
public class ListeChainee<T>
{
public Chainage<T> Premier { get; private set; }
}

L, cest trs simple, il sagit juste dune proprit en lecture seule stockant le premier
lment. Cest la mthode Ajouter() qui permettra de mettre jour cette valeur. Notez
quand mme que nous utilisons le type gnrique comme type gnrique de la classe
encapsulante.
Par contre, pour la proprit Dernier, cest un peu plus compliqu. Pour la retrouver, nous
allons parcourir tous les lments partir de la proprit Premier. Ce qui donne :
public class ListeChainee<T>
{
[Code supprim pour plus de clart]
public Chainage<T> Dernier
{
get
{
if (Premier == null)
return null;
Chainage<T> dernier = Premier;
while (dernier.Suivant != null)
{
dernier = dernier.Suivant;
}
return dernier;
}

}
}

On parcourt les lments en bouclant sur la proprit Suivant, tant que celle-ci nest pas
nulle. Il sagit l dun parcours assez classique o on utilise une variable temporaire qui
passe au suivant chaque itration.
Nous pouvons prsent crer la mthode Ajouter :
public class ListeChainee<T>
{
[Code supprim pour plus de clart]
public void Ajouter(T element)
{
if (Premier == null)
{
Premier = new Chainage<T> { Valeur = element };
}
else
{
Chainage<T> dernier = Dernier;
dernier.Suivant = new Chainage<T> { Valeur = element, Precedent = dernier };
}
}
}

Cette mthode traite dans un premier temps le cas du premier lment. Il sagit
simplement de mettre jour la proprit Premier. De mme, grce au calcul interne de la
proprit Dernier, il sera facile dajouter un nouvel lment en se branchant sur la
proprit Suivant du dernier lment.
Notez que vu que nous ne la renseignons pas, la proprit Suivant du nouvel lment sera
bien null.
Pour obtenir un lment un indice donn, il suffira de reprendre le mme principe que
lors du parcours pour obtenir le dernier lment, sauf quil faudra sarrter au bon moment
:
public class ListeChainee<T>
{
[Code supprim pour plus de clart]
public Chainage<T> ObtenirElement(int indice)
{
Chainage<T> temp = Premier;
for (int i = 1; i <= indice; i++)
{
if (temp == null)
return null;
temp = temp.Suivant;
}
return temp;
}
}

Ici, plusieurs solutions. Jai choisi dutiliser une boucle for. Nous aurions trs bien pu
garder la boucle while comme pour la proprit Dernier.
Enfin, il ne reste plus qu insrer un lment :
public class ListeChainee<T>
{
[Code supprim pour plus de clart]
public void Inserer(T element, int indice)
{
if (indice == 0)
{
Chainage<T> temp = Premier;
Premier = new Chainage<T> { Suivant = temp, Valeur = element };

temp.Precedent = Premier;
}
else
{
Chainage<T> elementAIndice = ObtenirElement(indice);
if (elementAIndice == null)
Ajouter(element);
else
{
Chainage<T> precedent = elementAIndice.Precedent;
Chainage<T> temp = precedent.Suivant;
precedent.Suivant = new Chainage<T> { Valeur = element, Precedent = precedent,
Suivant = temp };
}
}
}
}

Nous traitons dans un premier temps le cas o lon doit insrer len-tte. Il suffit de mettre
jour la valeur du premier en ayant au pralable dcal ce dernier dun cran. Attention, si
Premier est null, nous allons avoir un problme. Dans ce cas, soit nous laissons le
problme, en effet, peut-on vraiment insrer un lment avant les autres sil ny en a pas ?
Soit nous grons le cas et dcidons dinsrer llment en tant que Premier :
public class ListeChainee<T>
{
[Code supprim pour plus de clart]
public void Inserer(T element, int indice)
{
if (indice == 0)
{
if (Premier == null)
Premier = new Chainage<T> { Valeur = element };
else
{
Chainage<T> temp = Premier;
Premier = new Chainage<T> { Suivant = temp, Valeur = element };
temp.Precedent = Premier;
}
}
else
{
Chainage<T> elementAIndice = ObtenirElement(indice);
if (elementAIndice == null)
Ajouter(element);
else
{
Chainage<T> precedent = elementAIndice.Precedent;
Chainage<T> temp = precedent.Suivant;
precedent.Suivant = new Chainage<T> { Valeur = element, Precedent = precedent,
Suivant = temp };
}
}
}
}

Pour les autres cas, si nous tentons dinsrer un indice qui nexiste pas, nous insrons
la fin en utilisant la mthode Ajouter() existante. Sinon, on intercale le nouvel lment
dans la liste en prenant soin de brancher le prcdent sur notre nouvel lment et de
brancher le suivant sur notre nouvel lment.
Voil pour notre classe.
Reste utiliser notre classe :
static void Main(string[] args)
{
ListeChainee<int> listeChainee = new ListeChainee<int>();
listeChainee.Ajouter(5);
listeChainee.Ajouter(10);
listeChainee.Ajouter(4);

Console.WriteLine(listeChainee.Premier.Valeur);
Console.WriteLine(listeChainee.Premier.Suivant.Valeur);
Console.WriteLine(listeChainee.Premier.Suivant.Suivant.Valeur);
Console.WriteLine("*************");
Console.WriteLine(listeChainee.ObtenirElement(0).Valeur);
Console.WriteLine(listeChainee.ObtenirElement(1).Valeur);
Console.WriteLine(listeChainee.ObtenirElement(2).Valeur);
Console.WriteLine("*************");
listeChainee.Inserer(99, 0);
listeChainee.Inserer(33, 2);
listeChainee.Inserer(30, 2);
Console.WriteLine(listeChainee.ObtenirElement(0).Valeur);
Console.WriteLine(listeChainee.ObtenirElement(1).Valeur);
Console.WriteLine(listeChainee.ObtenirElement(2).Valeur);
Console.WriteLine(listeChainee.ObtenirElement(3).Valeur);
Console.WriteLine(listeChainee.ObtenirElement(4).Valeur);
Console.WriteLine(listeChainee.ObtenirElement(5).Valeur);
}

Ce qui nous donnera :

Instructions pour raliser la deuxime partie du TP


Bon, cest trs bien de pouvoir accder un lment par son indice. Mais une liste sur
laquelle on ne peut pas faire un foreach, cest quand mme bien dommage.
Attaquons dsormais la deuxime partie du TP. Toujours dans loptique de manipuler les
gnriques, nous allons faire en sorte que notre liste chaine puisse tre parcourable en
utilisant un foreach.
Nous avons dit plus haut quil suffisait dimplmenter linterface IEnumerable. En
loccurrence, nous allons implmenter sa version gnrique, vu que nous travaillons avec
une classe gnrique.
Voil le but de ce TP. Si vous vous le sentez, allez-y !
Je pense par contre que vous allez avoir besoin dtre un peu guids car cest une
opration un peu particulire.
Vous laurez devin, il faut que notre liste implmente linterface IEnumerable<T>. Le fait
dimplmenter cette interface va vous forcer implmenter deux mthodes
GetEnumerator(), la version normale et la version explicite. Sachez ds prsent que les

deux mthodes feront exactement la mme chose.


Mais, quest-ce quil raconte ? Implmenter une interface explicitement ? On
na jamais vu a !
Cest vrai ! Allez, je vous en parle aprs la correction. Pour linstant, cela ne devrait pas
vous perturber car les deux mthodes font exactement la mme chose. En loccurrence,
elles renverront un Enumerator personnalis.
Il va donc falloir crer cet Enumerator qui va soccuper de la mcanique permettant de
naviguer dans notre liste. Il sagit dune nouvelle classe qui va devoir implmenter
linterface IEnumerator<T>, cest--dire :
public class ListeChaineeEnumerator<T> : IEnumerator<T>
{
}

Cette interface permet dindiquer que notre enumrateur va respecter le contrat lui
permettant de fonctionner avec un foreach.
Avec cette interface, vous allez devoir implmenter :
La proprit Current.
La proprit explicite Current (qui sera la mme chose que la prcdente).
La mthode MoveNext qui permet de passer llment suivant.
La mthode Reset, qui permet de revenir au dbut de la liste.
Et la mthode Dispose.
La mthode Dispose est en fait hrite de linterface IDisposable dont hrite linterface
IEnumerator<T>. Cest une interface particulire qui offre lopportunit de faire tout ce
quil faut pour nettoyer la classe, cest--dire librer les variables qui en auraient besoin.
En loccurrence, ici nous naurons rien faire mais il faut quand mme que la mthode
soit prsente. Elle sera donc vide.
Pour implmenter les autres mthodes, il faut que lnumrateur connaisse la liste quil
doit numrer. Il faudra donc que la classe ListeChaineeEnumerator prenne en paramtre
de son constructeur la liste numrer. Dans ce constructeur, on initialise la variable
membre indice qui contient lindice courant.
La proprit Current renverra llment lindice courant.
La mthode MoveNext passe llment suivant et renvoie faux sil ny a plus dlments,
vrai sinon.
Enfin la mthode Reset repasse lindice sa valeur initiale.
noter que la valeur initiale de lindice est -1, car la boucle foreach commence par
appeler la mthode MoveNext qui commence par aller llment suivant, cest--dire
llment 0.
Il ne reste plus qu vous dire exactement quoi mettre dans les mthodes GetEnumerator de
la liste chaine, car vous ne trouverez peut-tre pas du premier coup :
public IEnumerator<T> GetEnumerator()
{
return new ListeChaineeEnumerator<T>(this);
}
IEnumerator IEnumerable.GetEnumerator()
{
return new ListeChaineeEnumerator<T>(this);

Cest vous de jouer pour la suite.

Correction
Encore moins facile. Tant quon ne la pas fait une premire fois, implmenter linterface
IEnumerable est un peu droutant. Aprs, cest toujours pareil.
Voici donc ma correction. Tout dabord, la liste chane doit implmenter IEnumerable<T>,
ce qui donne :
public class ListeChainee<T> : IEnumerable<T>
{
[Code identique au TP prcdent]
public IEnumerator<T> GetEnumerator()
{
return new ListeChaineeEnumerator<T>(this);
}
IEnumerator IEnumerable.GetEnumerator()
{
return new ListeChaineeEnumerator<T>(this);
}
}

L, cest du tout cuit vu que je vous avais donn la solution un peu plus tt ! Jespre
que vous avez au moins russi a.
Maintenant, il faut donc crer un nouvel numrateur personnalis en lui passant notre
liste chaine en paramtres.
Cet numrateur doit implmenter linterface IEnumerator, ce qui donne :
public class ListeChaineeEnumerator<T> : IEnumerator<T>
{
}

Comme prvu, il faut donc un constructeur qui prend en paramtre la liste chaine :
public class ListeChaineeEnumerator<T> : IEnumerator<T>
{
private int indice;
private ListeChainee<T> listeChainee;
public ListeChaineeEnumerator(ListeChainee<T> liste)
{
indice = -1;
listeChainee = liste;
}
public void Dispose()
{
}
}

Cette liste sera enregistre dans une variable membre de la classe. Tant que nous y
sommes, nous ajoutons un indice priv que nous initialisons -1, comme dj expliqu.
Notez galement que la mthode Dispose() est vide. Reste implmenter le reste des
mthodes :
public class ListeChaineeEnumerator<T> : IEnumerator<T>
{
private int indice;
private ListeChainee<T> listeChainee;
public ListeChaineeEnumerator(ListeChainee<T> liste)
{

indice = -1;
listeChainee = liste;
}
public void Dispose()
{
}
public bool MoveNext()
{
indice++;
Chainage<T> element = listeChainee.ObtenirElement(indice);
return element != null;
}
public T Current
{
get
{
Chainage<T> element = listeChainee.ObtenirElement(indice);
if (element == null)
return default(T);
return element.Valeur;
}
}
object IEnumerator.Current
{
get { return Current; }
}
public void Reset()
{
indice = -1;
}
}

Commenons par la mthode MoveNext(). Elle passe lindice suivant et renvoie faux ou
vrai en fonction de si on arrive au bout de la liste ou pas. Noubliez pas que cest la
premire mthode qui sera appele dans le foreach, donc pour passer llment suivant,
on incrmente lindice pour le positionner llment 0. Cest pour cela que lindice a t
initialis -1. On utilise ensuite la mthode existante de la liste pour obtenir llment
un indice afin de savoir si notre liste peut continuer snumrer.
La proprit Current renvoie llment lindice courant, pour cela on utilise lindice pour
accder llment courant, en utilisant les mthodes de la liste. Lautre proprit Current
fait la mme chose, il suffit dappeler la proprit Current.
Enfin, la mthode Reset permet de rinitialiser lnumrateur en retournant lindice
initial.
Finalement, ce nest pas si compliqu que a. Mais il faut avouer que la premire fois,
cest un peu droutant.
mon sens, cest un bon exercice pratique. Peut-tre que mes explications ont suffi
vous guider. Sans doute avez-vous d regarder un peu la documentation de IEnumerable
sur internet. Dans tous les cas, devoir implmenter une interface du framework .NET est
une situation que vous allez frquemment devoir rencontrer. Il est bon de sy entrainer !

Aller plus loin


Vous me direz quil fallait le deviner quon avait besoin dune classe indpendante qui
permettait de grer lnumrateur.

En fait, ce nest pas obligatoire. On peut trs bien faire en sorte que notre classe gre la
liste chaine et son numrateur. Il suffit de faire en sorte que la liste chaine implmente
galement IEnumerator<T> et de grer la logique lintrieur de la classe.
Par contre, ce nest pas recommand. Dune manire gnrale il est bien quune classe
nait soccuper que dune seule chose. On appelle cela le principe de responsabilit
unique (en anglais SRP : Single Responsibility Principle). Plus une classe fait de choses et
plus une modification impacte les autres choses. Ici, il est judicieux de garder le
dcouplage des deux classes.
Il y a quand mme une chose que lon peut amliorer dans le code de la correction. En
effet, cette liste nest pas extrmement optimise car lorsque nous obtenons un lment,
nous re-parcourons toute la liste depuis le dbut, notamment dans le cas de la gestion de
lnumrateur. Il pourrait tre judicieux qu chaque foreach, nous ne parcourions pas
tous les lments et quon vite dappeler continuellement la mthode ObtenirElement().
Cela pourrait se faire en liminant lindice et en utilisant une variable de type Chainage<T>,
par exemple :
public class ListeChaineeEnumerator<T> : IEnumerator<T>
{
private Chainage<T> courant;
private ListeChainee<T> listeChainee;
public ListeChaineeEnumerator(ListeChainee<T> liste)
{
courant = null;
listeChainee = liste;
}
public void Dispose()
{
}
public bool MoveNext()
{
if (courant == null)
courant = listeChainee.Premier;
else
courant = courant.Suivant;
return courant != null;
}
public T Current
{
get
{
if (courant == null)
return default(T);
return courant.Valeur;
}
}
object IEnumerator.Current
{
get { return Current; }
}
public void Reset()
{
courant = null;
}
}

Ici, cest la variable courant qui nous permet ditrer au fur et mesure de la liste chaine.
Cest le mme principe que dans la mthode ObtenirElement, sauf quon ne re-parcoure
pas toute la liste chaque fois. Dans cet exemple, loptimisation est ngligeable. Elle peut

savrer intressante si notre liste grossit normment. Dans tous les cas, a ne fait pas de
mal daller plus vite.
Remarquons avant de terminer quil est possible de simplifier encore la classe grce un
mot-cl que nous dcouvrirons dans la partie suivante : yield. Il permet de crer
facilement des numrateurs. Ce qui fait que le code complet de la liste chaine pourra
tre :
public class ListeChainee<T> : IEnumerable<T>
{
public Chainage<T> Premier { get; private set; }
public Chainage<T> Dernier
{
get
{
if (Premier == null)
return null;
Chainage<T> dernier = Premier;
while (dernier.Suivant != null)
{
dernier = dernier.Suivant;
}
return dernier;
}
}
public void Ajouter(T element)
{
if (Premier == null)
{
Premier = new Chainage<T> { Valeur = element };
}
else
{
Chainage<T> dernier = Dernier;
dernier.Suivant = new Chainage<T> { Valeur = element, Precedent = dernier };
}
}
public Chainage<T> ObtenirElement(int indice)
{
Chainage<T> temp = Premier;
for (int i = 1; i <= indice; i++)
{
if (temp == null)
return null;
temp = temp.Suivant;
}
return temp;
}
public void Inserer(T element, int indice)
{
if (indice == 0)
{
if (Premier == null)
Premier = new Chainage<T> { Valeur = element };
else
{
Chainage<T> temp = Premier;
Premier = new Chainage<T> { Suivant = temp, Valeur = element };
temp.Precedent = Premier;
}
}
else
{
Chainage<T> elementAIndice = ObtenirElement(indice);
if (elementAIndice == null)
Ajouter(element);
else
{
Chainage<T> precedent = elementAIndice.Precedent;

Chainage<T> temp = precedent.Suivant;


precedent.Suivant = new Chainage<T> { Valeur = element, Precedent = precedent,
Suivant = temp };
}
}
}
public IEnumerator<T> GetEnumerator()
{
Chainage<T> courant = Premier;
while (courant != null)
{
yield return courant.Valeur;
courant = courant.Suivant;
}
}
IEnumerator IEnumerable.GetEnumerator()
{
return GetEnumerator();
}
}

Remarquons que nous navons plus besoin de la classe ListeChaineeEnumerator.


Limplmentation devient trs facile. Nous reviendrons sur ce mot-cl dans la partie
suivante.

Implmenter une interface explicitement


Jen profite ici pour faire un apart sur limplmentation dinterface explicite.
Jai choisi dlibrment de ne pas le mettre dans le chapitre des interfaces car cest un cas
relativement rare mais qui se produit justement quand on implmente linterface
IEnumerable<T>.
Cela vient du fait que linterface IEnumerable, non gnrique, expose une proprit
Current. De mme, linterface IEnumerable<T>, gnrique, qui hrite de IEnumerable,
expose galement une proprit Current.
Il y a donc une ambigut car les deux proprits portent le mme nom, mais ne renvoient
pas la mme chose. Ce qui est contraire aux rgles que nous avons dj vues. Pour faire la
diffrence, il suffira de prfixer la proprit par le nom de linterface et de ne pas mettre le
mot-cl public.
Limplmentation explicite a galement un intrt dans le code suivant :
public interface ICarnivore
{
void Manger();
}
public interface IFrugivore
{
void Manger();
}
public class Homme : ICarnivore, IFrugivore
{
public void Manger()
{
Console.WriteLine("Je mange");
}
}
class Program
{
static void Main(string[] args)

{
Homme homme = new Homme();
homme.Manger();
((ICarnivore)homme).Manger();
((IFrugivore)homme).Manger();
}
}

Ici, ce code compile car la classe Homme implmente la mthode Manger qui est commune
aux deux interfaces. Par contre, il nest pas possible de faire la distinction entre le fait de
manger en tant quhomme, en tant que ICarnivore ou en tant que IFrugivore.
Ce code affichera :
Je mange
Je mange
Je mange

Si cest le comportement attendu, tant mieux. Si ce nest pas le cas, il va falloir


implmenter au moins une des interfaces de manire explicite :
public class Homme : ICarnivore, IFrugivore
{
public void Manger()
{
Console.WriteLine("Je mange");
}
void IFrugivore.Manger()
{
Console.WriteLine("Je mange en tant que IFrugivore");
}
void ICarnivore.Manger()
{
Console.WriteLine("Je mange en tant que ICarnivore");
}
}

Avec ce code, notre exemple affichera :


Je mange
Je mange en tant que ICarnivore
Je mange en tant que IFrugivore

Si vous vous rappelez, nous avions vu au moment du chapitre sur les interfaces que Visual
C# Express nous proposait de nous aider dans limplmentation de linterface. Par le
bouton droit, vous aviez galement accs sous menu implmenter linterface
explicitement . Vous pouvez vous en servir dans ce cas prcis.
Je marrte l sur limplmentation dinterface explicite, mme sil y aurait dautres points
voir. Globalement dans la vraie vie, ils ne vous serviront jamais.
Voil pour ce TP. Nous avons cr une classe gnrique permettant de grer les listes
chaines. Ceci nous a permis de manipuler ces types -combien indispensables et de nous
entrainer la gnricit.
Nous en avons mme profit pour voir comment faire en sorte quune classe soit
numrable, en implmentant la version gnrique de IEnumerable.
Notez bien sr que cette classe est fonctionnellement incomplte. Il aurait t judicieux de
rajouter une mthode permettant de supprimer un lment par exemple. Dailleurs,
nhsitez pas la crer et la proposer si vous souhaitez continuer vous entrainer.
Dautres mthodes pourraient tre intressantes, comme vider la liste dun seul coup
Jespre que ce TP na pas t trop compliqu raliser.
noter quune classe qui fait peu prs le mme travail existe dans le framework .NET,

elle sappelle LinkedList.

Chapitre 29

Les mthodes dextension

En gnral, pour ajouter des fonctionnalits une classe, nous pourrons soit modifier le
code source de la classe, soit crer une classe drive de notre classe et ajouter ces
fonctionnalits.
Dans ce chapitre nous allons voir quil existe un autre moyen pour tendre une classe :
ceci est possible grce aux mthodes dextension.
Elles sont intressantes si nous navons pas la main sur le code source de la classe ou si la
classe nest pas drivable.
Partons la dcouverte de ces fameuses mthodes

Quest-ce quune mthode dextension


Comme son nom lindique, une mthode dextension permet dtendre une classe en lui
rajoutant des mthodes.
Ceci est pratique lorsque nous ne possdons pas le code source dune classe et quil
savre difficile de la modifier.
Dune manire gnrale, lorsque lon souhaite modifier une classe dont on na pas le code
source, on utilise une classe drive. Ceci est impossible avec des objets qui ne sont pas
drivables, comme cest le cas pour les structures comme int ou double. Mais galement
comme cest le cas pour la classe String. En effet, String nest pas drivable. Nous
verrons plus tard comment cela est possible, mais pour linstant, admettons-le.
Si vous ne me croyez pas, vous pouvez toujours tenter de compiler le code suivant :
public class StringEvoluee : String
{
}

Crer une mthode dextension


Si par exemple nous souhaitons crer une mthode permettant de crypter une chaine de
caractres dans un format que nous seuls comprenons, il serait judicieux de crer une
mthode dans une classe StringCryptee qui drive de string.
Comme ceci nest pas possible, la seule chose qui nous reste cest de crer une mthode

statique utilitaire faisant cet encodage :


class Program
{
static void Main(string[] args)
{
string chaineNormale = "Bonjour tous";
string chaineCryptee = Encodage.Crypte(chaineNormale);
Console.WriteLine(chaineCryptee);
chaineNormale = Encodage.Decrypte(chaineCryptee);
Console.WriteLine(chaineNormale);
}
}
public static class Encodage
{
public static string Crypte(string chaine)
{
return Convert.ToBase64String(Encoding.Default.GetBytes(chaine));
}
public static string Decrypte(string chaine)
{
return Encoding.Default.GetString(Convert.FromBase64String(chaine));
}
}

Bon, ok, notre encodage secret ne lest pas tant que a .


Il savre que jutilise ici un encodage en base 64, algorithme archi connu. Mais bon, cest
pour lexemple.

Utiliser une mthode dextension


Ces mthodes statiques jouent bien leur rle. Mais il est possible de faire en sorte que ces
deux mthodes deviennent des mthodes dextensions de la classe string. Il suffit
dutiliser le mot-cl this devant le premier paramtre de la mthode afin de crer une
mthode dextension :
public static class Encodage
{
public static string Crypte(this string chaine)
{
return Convert.ToBase64String(Encoding.Default.GetBytes(chaine));
}
public static string Decrypte(this string chaine)
{
return Encoding.Default.GetString(Convert.FromBase64String(chaine));
}
}

Nous pourrons dsormais utiliser ces mthodes comme si elles faisaient parties de la
classe String :
string chaineNormale = "Bonjour tous";
string chaineCryptee = chaineNormale.Crypte();
Console.WriteLine(chaineCryptee);
chaineNormale = chaineCryptee.Decrypte();
Console.WriteLine(chaineNormale);

Pas mal non ?


De plus, si nous regardons dans la compltion automatique, nous pourrons voir apparatre
nos fameuses mthodes :

Plutt pratique.
videmment, en crant une mthode dextension, nous navons bien sr pas accs aux
mthodes prives ou variables membres internes la classe. La preuve, les mthodes
dextensions sont des mthodes statiques qui travaillent hors de toute instance de classe.
Ces mthodes doivent donc tre statiques et situes lintrieur dune classe
statique.
Par contre, il faut faire attention lespace de nom o se situent nos mthodes
dextensions. Si le using correspondant nest pas inclus, nous ne verrons pas les mthodes
dextension.
Remarquez que les mthodes dextension fonctionnent aussi avec les interfaces. Plus
prcisment, elles viennent tendre toutes les classes qui implmentent une interface.
Par exemple, avec deux classes implmentant linterface IVolant :
public interface IVolant
{
void Voler();
}
public class Oiseau : IVolant
{
public void Voler()
{
Console.WriteLine("Je vole");
}
}
public class Avion : IVolant
{
public void Voler()
{
Console.WriteLine("Je vole");
}
}

si je cre une mthode dextension prenant en paramtres un IVolant, prfix par this :
public static class Extentions
{
public static void Atterir(this IVolant volant)
{
Console.WriteLine("J'attris");
}
}

je pourrais accder cette mthode pour les objets Avion et Oiseau :


Avion a = new Avion();

Oiseau b = new Oiseau();


a.Atterir();
b.Atterir();

noter que le framework .NET se sert de ceci pour proposer un grand nombre de
mthodes dextension sur les objets implmentant IEnumerable, nous les tudierons un peu
plus tard.
En rsum

Une mthode dextension permet dtendre une classe en lui rajoutant des mthodes.
Il nest pas recommand dutiliser des mthodes dextension lorsquon dispose dj
du code source de la classe ou quon peut facilement en crer un type driv.
On utilise le mot-cl this en premier paramtre dune classe statique pour tendre
une classe.

Chapitre 30

Dlgus, vnements et expressions lambdas

Dans ce chapitre, nous allons aborder les dlgus, les vnements et les expressions
lambdas. Les dlgus et les vnements sont des types du framework .NET que nous
navons pas encore vus. Ils permettent dadresser des solutions notamment dans le cadre
dune programmation par vnements, comme cest le cas lorsque nous ralisons des
applications ncessitant de ragir une action faite par un utilisateur. Nous verrons dans
ce chapitre que les expressions lambdas vont de pair avec les dlgus.

Les dlgus (delegate)


Les dlgus (en anglais delegate) en C# ne soccupent pas de la classe, ni du personnel
Ils permettent de crer des variables spciales. Ce sont des variables qui pointent vers
une mthode.
Cest un peu comme les pointeurs de fonctions en C ou C++, sauf quici on sait
exactement ce que lon utilise, car le C# est un langage fortement typ.
Le dlgu va nous permettre de dfinir une signature de mthode et avec lui, nous
pourrons pointer vers nimporte quelle mthode qui respecte cette signature.
En gnral, on utilise un dlgu quand on veut passer une mthode en paramtres dune
autre mthode.
Un petit exemple sera sans doute plus parlant quun long discours. Ainsi, le code suivant :
public class TrieurDeTableau
{
private delegate void DelegateTri(int[] tableau);
}

cre un dlgu priv la classe TrieurDeTableau qui permettra de pointer vers des
mthodes qui ne retournent rien (void) et qui acceptent un tableau dentier en paramtres.
Cest justement le cas des mthodes TriAscendant() et TriDescendant() que nous allons
rajouter la classe (a tombe bien !) :
public class TrieurDeTableau
{
private delegate void DelegateTri(int[] tableau);
private void TriAscendant(int[] tableau)
{
Array.Sort(tableau);
}
private void TriDescendant(int[] tableau)

{
Array.Sort(tableau);
Array.Reverse(tableau);
}
}

Vous aurez compris que la mthode TriAscendant utilise la mthode Array.Sort pour trier
un tableau par ordre croissant. Inversement, la mthode TriDescendant() trie le tableau
par ordre dcroissant en triant par ordre croissant et en inversant le tableau ensuite.
Il ne reste plus qu crer une mthode dans la classe permettant dutiliser le tri ascendant
et le tri descendant, grce notre dlgu :
public class TrieurDeTableau
{
[Code supprim pour plus de clart]
public void DemoTri(int[] tableau)
{
DelegateTri tri = TriAscendant;
tri(tableau);
foreach (int i in tableau)
{
Console.WriteLine(i);
}
Console.WriteLine();
tri = TriDescendant;
tri(tableau);
foreach (int i in tableau)
{
Console.WriteLine(i);
}
}
}

Nous voyons ici que dans la mthode DemoTri nous commenons par dclarer une variable
du type du dlgu DelegateTri, qui est le dlgu que nous avons cr. Puis nous faisons
pointer cette variable vers la mthode TriAscendant().
Nul besoin ici dutiliser les parenthses, mais juste le nom de la mthode. Il
sagit seulement dune affectation.
Nous invoquons ensuite la mthode TriAscendant() travers la variable qui va permettre
de trier le tableau par ordre croissant avant dafficher son contenu. Cette fois-ci, il faut
bien sr utiliser les parenthses car nous invoquons la mthode.
Puis nous faisons pointer la variable vers la mthode TriDescendant() qui va nous
permettre de faire la mme chose mais avec un tri dcroissant.
Nous pouvons appeler cette classe de cette faon :
static void Main(string[] args)
{
int[] tableau = new int[] { 4, 1, 6, 10, 8, 5 };
new TrieurDeTableau().DemoTri(tableau);
}

Notre code affichera au final les entiers tris par ordre croissant, puis les mmes entiers
tris par ordre dcroissant.
Ok, mais pourquoi utiliser ce dlgu ? On pourrait trs bien appeler dabord la
mthode de tri ascendant et ensuite la mthode de tri descendant. Comme on la
toujours fait !

Eh bien, lintrt ici est que le dlgu est trs souple et va permettre de rorganiser le
code (on parle galement de refactoriser du code). Ainsi, en rajoutant la mthode
suivante dans la classe :
private void TrierEtAfficher(int[] tableau, DelegateTri methodeDeTri)
{
methodeDeTri(tableau);
foreach (int i in tableau)
{
Console.WriteLine(i);
}
}

Nous pourrons grandement simplifier la mthode DemoTri :


public void DemoTri(int[] tableau)
{
TrierEtAfficher(tableau, TriAscendant);
Console.WriteLine();
TrierEtAfficher(tableau, TriDescendant);
}

Ce qui produira le mme rsultat que prcdemment. Quavons-nous fait ici ?


Nous avons utilis le dlgu comme paramtre dune mthode. Ce dlgu est ensuite
utilis pour invoquer une mthode que nous aurons passe en paramtres. Cest ce que
nous faisons en disant dutiliser la mthode TrierEtAfficher une premire fois avec la
mthode TriAscendant() et une deuxime fois avec la mthode TriDescendant().
Plutt pas mal non ?
Il est mme possible de dfinir la mthode qui sera utilise lintrieur de
TrierEtAfficher() sans avoir lcrire compltement dans le corps de la classe.
Cela peut tre utile si la mthode est voue ntre utilise que dans cette unique situation
et quelle nest jamais appele un autre endroit.
Par exemple, plutt que de dfinir compltement la mthode TriAscendant(), je pourrais
la dfinir directement au moment de lappel de la mthode :
public class TrieurDeTableau
{
private delegate void DelegateTri(int[] tableau);
private void TrierEtAfficher(int[] tableau, DelegateTri methodeDeTri)
{
methodeDeTri(tableau);
foreach (int i in tableau)
{
Console.WriteLine(i);
}
}
public void DemoTri(int[] tableau)
{
TrierEtAfficher(tableau, delegate(int[] leTableau)
{
Array.Sort(leTableau);
});
Console.WriteLine();
TrierEtAfficher(tableau, delegate(int[] leTableau)
{
Array.Sort(leTableau);
Array.Reverse(leTableau);
});
}
}

Ainsi, je naurai plus besoin de la mthode TriAscendant() ni de la mthode


TriDescendant().
Le fait de dfinir la mthode directement au niveau du paramtre dappel est ce quon
appelle utiliser une mthode anonyme . Anonyme car la mthode na pas de nom.
Elle na de vie qu cet endroit-l.
La syntaxe est un peu particulire, mais au lieu dutiliser une variable de type delegate
qui pointe vers une mthode, cest comme si on crivait directement la mthode.
On utilise le mot-cl delegate suivi de la dclaration du paramtre. videmment, le
dlgu anonyme doit respecter la signature de DelegateTri que nous avons dfinie plus
haut. Enfin, nous faisons suivre avec un bloc de code qui correspond au corps de la
mthode anonyme.
noter que le fait dutiliser le mot-cl delegate revient en fait crer une classe
qui drive de System.Delegate et qui implmente la logique de base dun
dlgu. Le C# nous masque tout ceci afin dtre au maximum efficace.

Diffusion multiple, le Multicast


Il faut galement savoir que le dlgu peut tre multicast, cest dire quil peut pointer
vers plusieurs mthodes.
Amliorons le premier exemple :
public class TrieurDeTableau
{
private delegate void DelegateTri(int[] tableau);
private void TriAscendant(int[] tableau)
{
Array.Sort(tableau);
foreach (int i in tableau)
{
Console.WriteLine(i);
}
Console.WriteLine();
}
private void TriDescendant(int[] tableau)
{
Array.Sort(tableau);
Array.Reverse(tableau);
foreach (int i in tableau)
{
Console.WriteLine(i);
}
}
public void DemoTri(int[] tableau)
{
DelegateTri tri = TriAscendant;
tri += TriDescendant;
tri(tableau);
}
}

Ici, jutilise Console.WriteLine directement dans chaque mthode de tri afin de bien voir
le rsultat du tri du tableau.
Limportant est de voir que dans la mthode DemoTri, je commence par crer un dlgu
que je fais pointer vers la mthode TriAscendant. Puis jajoute ce dlgu, avec

loprateur +=, une nouvelle mthode, savoir TriDescendant.


Dsormais, le fait dinvoquer le dlgu va invoquer en fait les deux mthodes. Ce qui
produira en sortie :
1
4
5
6
8
10
10
8
6
5
4
1

Ce dtail prend toute son importance avec les vnements que nous verrons plus loin.
noter que le rsultat de ce code est videmment identique en utilisant les mthodes
anonymes :
public void DemoTri(int[] tableau)
{
DelegateTri tri = delegate(int[] leTableau)
{
Array.Sort(leTableau);
foreach (int i in tableau)
{
Console.WriteLine(i);
}
Console.WriteLine();
};
tri += delegate(int[] leTableau)
{
Array.Sort(tableau);
Array.Reverse(tableau);
foreach (int i in tableau)
{
Console.WriteLine(i);
}
};
tri(tableau);
}

Il faut quand mme remarquer que lordre dans lequel sont appeles les
mthodes nest pas garanti et ne dpend pas forcment de lordre dans lequel
nous les avons ajoutes au dlgu.

Les dlgus gnriques Action et Func


Cest trs bien tout a, mais cela veut dire qu chaque fois que je vais avoir
besoin dutiliser un dlgu, je vais devoir crer un nouveau type en utilisant le
mot-cl delegate ?
Pas forcment, cest l quinterviennent les dlgus gnriques Action et Func. Action est
un dlgu qui permet de pointer vers une mthode qui ne renvoie rien et qui peut accepter
jusqu 16 types diffrents.
Cela veut dire que le code prcdent peut tre remplac par :
public class TrieurDeTableau
{

private void TrierEtAfficher(int[] tableau, Action<int[]> methodeDeTri)


{
methodeDeTri(tableau);
foreach (int i in tableau)
{
Console.WriteLine(i);
}
}
public void DemoTri(int[] tableau)
{
TrierEtAfficher(tableau, delegate(int[] leTableau)
{
Array.Sort(leTableau);
});
Console.WriteLine();
TrierEtAfficher(tableau, delegate(int[] leTableau)
{
Array.Sort(tableau);
Array.Reverse(tableau);
});
}
}

Notez que la diffrence se situe au niveau du paramtre de la mthode TrierEtAfficher


qui prend un Action<int[]>. En fait, cela est quivalent crer un dlgu qui ne renvoie
rien et qui prend un tableau dentier en paramtre.
Si notre mthode avait deux paramtres, il aurait suffi dutiliser la forme de Action avec
plusieurs paramtres gnriques, par exemple Action<int[], string> pour avoir une
mthode qui ne renvoie rien et qui prend un tableau dentier et une chaine de caractres en
paramtres.
Lorsque la mthode renvoie quelque chose, on peut utiliser le dlgu Func<T>, sachant
quici, cest le dernier paramtre gnrique qui sera du type de retour du dlgu. Par
exemple :
public class Operations
{
public void DemoOperations()
{
double division = Calcul(delegate(int a, int b)
{
return (double)a / (double)b;
}, 4, 5);
double puissance = Calcul(delegate(int a, int b)
{
return Math.Pow((double)a, (double)b);
}, 4, 5);
Console.WriteLine("Division : " + division);
Console.WriteLine("Puissance : " + puissance);
}
private double Calcul(Func<int, int, double> methodeDeCalcul, int a, int b)
{
return methodeDeCalcul(a, b);
}
}

Ici, dans la mthode Calcul, on utilise le dlgu Func pour indiquer que la mthode prend
deux entiers en paramtres et renvoie un double.
Si nous utilisons cette classe avec le code suivant :
class Program
{
static void Main(string[] args)

{
new Operations().DemoOperations();
}
}

Nous aurons :
Division : 0,8
Puissance : 1024

Les expressions lambdas


Non, il ne sagit pas dune expression qui danse la lambada, mais dune faon simplifie
dcrire les dlgus que nous avons vus au-dessus.
Ainsi, le code suivant :
DelegateTri tri = delegate(int[] leTableau)
{
Array.Sort(leTableau);
};

peut galement scrire de cette faon :


DelegateTri tri = (leTableau) =>
{
Array.Sort(leTableau);
};

Cette syntaxe est particulire. La variable leTableau permet de spcifier le paramtre


dentre de lexpression lambda. Ce paramtre est crit entre parenthses. Ici, pas besoin
dindiquer son type vu quil est connu par la signature associe au dlgu. On utilise
ensuite la flche => pour dfinir lexpression lambda qui sera utilise. Elle scrit de la
mme faon quune mthode, dans un bloc de code.
Lexpression lambda (leTableau) => se lit : leTableau conduit .
Dans le corps de la mthode, nous voyons que nous utilisons la variable leTableau de la
mme faon que prcdemment.
Dans ce cas prcis, il est encore possible de raccourcir lcriture car la mthode ne
contient quune seule ligne, on pourra alors lcrire de cette faon :
TrierEtAfficher(tableau, leTableau => Array.Sort(leTableau));

Sil ny a quun seul paramtre lexpression lambda, on peut omettre les


parenthses.
Quand il y a deux paramtres, on les spare par une virgule. noter quon nindique nulle
part le type de retour sil y en a un.
Notre expression lambda remplaant le calcul de la division peut donc scrire ainsi :
double division = Calcul((a, b) =>
{
return (double)a / (double)b;
}, 4, 5);

Lorsque linstruction possde une unique ligne, on peut encore simplifier lcriture, ce qui
donne :
double division = Calcul((a, b) => (double)a / (double)b, 4, 5);

Pourquoi tout ce blabla sur les delegate et les expressions lambdas ?

Pour deux raisons :


parce que les dlgus sont la base des vnements ;
cause des mthodes dextensions LINQ.
Nous parlerons dans la partie suivante des mthodes dextensions LINQ. Quant aux
vnements, explorons-les immdiatement !

Les vnements
Les vnements sont un mcanisme du C# permettant une classe dtre notifie dun
changement.
Par exemple, on peut vouloir sabonner un changement de prix dune voiture.
La base des vnements est le dlgu. On pourra stocker dans un vnement un ou
plusieurs dlgus qui pointent vers des mthodes respectant la signature de lvnement.
Un vnement est dfini grce au mot-cl event. Prenons cet exemple :
public class Voiture
{
public delegate void DelegateDeChangementDePrix(decimal nouveauPrix);
public event DelegateDeChangementDePrix ChangementDePrix;
public decimal Prix { get; set; }
public void PromoSurLePrix()
{
Prix = Prix / 2;
if (ChangementDePrix != null)
ChangementDePrix(Prix);
}
}

Dans la classe Voiture, nous dfinissons un dlgu qui ne retourne rien et qui prend en
paramtre un dcimal. Nous dfinissons ensuite un vnement bas sur ce dlgu, avec
comme nous lavons vu, lutilisation du mot-cl event. Enfin, dans la mthode de
promotion, aprs un changement de prix (division par 2), nous notifions les ventuels
objets qui se seraient abonns cet vnement en invoquant lvnement et en lui
fournissant en paramtre le nouveau prix.
noter que nous testons dabord sil y a un abonn lvnement (en testant sil est
diffrent de null) avant de le lever.
Pour sabonner cet vnement, il suffit dutiliser le code suivant :
class Program
{
static void Main(string[] args)
{
new DemoEvenement().Demo();
}
}
public class DemoEvenement
{
public void Demo()
{
Voiture voiture = new Voiture { Prix = 10000 };
Voiture.DelegateDeChangementDePrix delegateChangementDePrix = voiture_ChangementDePrix;
voiture.ChangementDePrix += delegateChangementDePrix;
voiture.PromoSurLePrix();

}
private void voiture_ChangementDePrix(decimal nouveauPrix)
{
Console.WriteLine("Le nouveau prix est de : " + nouveauPrix);
}
}

Nous crons une voiture, et nous crons un dlgu du mme type que lvnement. Nous
le faisons pointer vers une mthode qui respecte la signature du dlgu. Ainsi, chaque
changement de prix, la mthode voiture_ChangementDePrix va tre appele et le paramtre
nouveauPrix possdera le nouveau prix qui vient dtre calcul.
Appelons la promotion en invoquant la mthode ChangementDePrix(), nous pouvons nous
rentre compte que lapplication nous affiche le nouveau prix qui est lancien divis par 2.
Lorsque nous commenons crire le code qui va permettre de nous abonner
lvnement, la compltion automatique nous propose facilement de crer une mthode
qui respecte la signature de lvnement. Il suffit de taper lvnement de rajouter un += et
il nous propose dinsrer tout automatiquement si nous appuyons sur la tabulation :

Ce qui gnre le code suivant :


voiture.ChangementDePrix += new Voiture.DelegateDeChangementDePrix(voiture_ChangementDePrix);

ainsi que la mthode :


void voiture_ChangementDePrix(decimal nouveauPrix)
{
throw new NotImplementedException();
}

On peut aisment simplifier labonnement avec :


voiture.ChangementDePrix += voiture_ChangementDePrix;

comme on la dj vu. Notez que vous pouvez galement rajouter la visibilit private sur
la mthode gnre afin que cela soit plus explicite.
private void voiture_ChangementDePrix(decimal nouveauPrix)
{
}

Lutilisation du += permet dajouter un nouveau dlgu lvnement. Il sera


ventuellement possible dajouter un autre dlgu avec le mme oprateur, ainsi deux
mthodes seront dsormais notifies en cas de changement de prix. Inversement, il est
possible de se dsabonner dun vnement en utilisant loprateur -= .
Les vnements sont beaucoup utiliss dans les applications en C#, comme les
applications clients lourds dveloppes avec WPF par exemple. Ce sont des applications

comme un traitement de texte ou un navigateur internet. Par exemple, lorsque lon clique
sur un bouton, un vnement est lev.
Ces vnements utilisent en gnral une construction base du dlgu EventHandler ou
sa version gnrique EventHandler<>. Ce dlgu accepte deux paramtres. Le premier de
type object qui reprsente la source de lvnement, cest--dire lobjet qui a lev
lvnement. Le second est une classe qui drive de la classe de base EventArgs.
Rcrivons notre exemple avec ce nouveau handler. Nous avons donc besoin en premier
lieu dune classe qui drive de la classe EventArgs :
public class ChangementDePrixEventArgs : EventArgs
{
public decimal Prix { get; set; }
}

Plus besoin de dclaration de dlgu, nous utilisons directement EventHandler dans notre
classe Voiture :
public class Voiture
{
public event EventHandler<ChangementDePrixEventArgs> ChangementDePrix;
public decimal Prix { get; set; }
public void PromoSurLePrix()
{
Prix = Prix / 2;
if (ChangementDePrix != null)
ChangementDePrix(this, new ChangementDePrixEventArgs { Prix = Prix });
}
}

Et notre dmo devient :


class Program
{
static void Main(string[] args)
{
new DemoEvenement().Demo();
}
}
public class DemoEvenement
{
public void Demo()
{
Voiture voiture = new Voiture { Prix = 10000 };
voiture.ChangementDePrix += voiture_ChangementDePrix;
voiture.PromoSurLePrix();
}
private void voiture_ChangementDePrix(object sender, ChangementDePrixEventArgs e)
{
Console.WriteLine("Le nouveau prix est de : " + e.Prix);
}
}

Remarquons la mthode voiture_ChangementDePrix qui prend dsormais deux paramtres.


Le premier reprsente lobjet Voiture, si nous en avions besoin, nous pourrions lutiliser
avec un cast adquat. Le second reprsente lobjet contenant le prix de la voiture en
promotion.
noter quen gnral, nous allons beaucoup utiliser les vnements dfinis par le
framework .NET. Il est cependant assez rare davoir en dfinir un soi-mme.
En rsum

Les dlgus permettent de crer des variables pointant vers des mthodes.
Les dlgus sont la base des vnements.
On utilise les expressions lambdas pour simplifier lcriture des dlgus.
Les vnements sont un mcanisme du C# permettant une classe dtre notifie
dun changement.

Chapitre 31

Grer les erreurs : les exceptions

Nous avons parl rapidement des erreurs dans nos applications C# en disant quil
sagissait dexceptions. Cest le moment den savoir un peu plus et surtout dapprendre
les grer ! Dans ce chapitre, nous allons apprendre comment crer et intercepter une
exception.

Intercepter une exception


Rappelez-vous de ce code :
string chaine = "dix";
int valeur = Convert.ToInt32(chaine);

Si nous lexcutons, nous aurons lerreur suivante :

Lapplication nous affiche un message derreur et lapplication plante lamentablement


produisant un rapport derreur.
Ce quil se passe en fait, cest que lors de la conversion, si le framework .NET narrive pas
convertir correctement la chaine de caractres en entier, il lve une exception. Cela veut
dire quil informe le programme quil rencontre un cas limite qui ncessite dtre gr.
Si ce cas limite nest pas gr, alors lapplication plante et cest le CLR qui intercepte

lerreur et qui fait produire un rapport au systme dexploitation.


Pourquoi une exception et pas un message derreur ?
Lintrt des exceptions est quelles sont types. Finis les codes derreurs
incomprhensibles. Cest le type de lexception, cest--dire sa classe, qui va nous
permettre didentifier le problme.
Pour viter le plantage de lapplication, nous devons grer ces cas limites et intercepter les
exceptions.
Pour ce faire, il faut encadrer les instructions pouvant atteindre des cas limites avec le bloc
dinstruction , par exemple :
try
{
string chaine = "dix";
int valeur = Convert.ToInt32(chaine);
Console.WriteLine("Ce code ne sera jamais affich");
}
catch (Exception)
{
Console.WriteLine("Une erreur s'est produite dans la tentative de conversion");
}

Si nous excutons ce bout de code, lapplication ne plantera plus et affichera quune erreur
sest produite
Nous avons attrap lexception leve par la mthode de conversion grce au mot-cl
catch.
Cette construction nous permet de surveiller lexcution dun bout de code, situ dans le
bloc try et sil y a une erreur, alors nous interrompons son excution pour traiter lerreur
en allant dans le bloc catch.
La suite du code dans le try, savoir laffichage de la ligne avec Console.WriteLine, ne
sera jamais excut car lorsque la conversion choue, il saute directement au bloc catch.
Inversement, il est possible de ne jamais passer dans le bloc catch si les instructions ne
provoquent pas derreur :
try
{
string chaine = "10";
int valeur = Convert.ToInt32(chaine);
Console.WriteLine("Conversion OK");
}
catch (Exception)
{
Console.WriteLine("Nous ne passons jamais ici");
}

Ainsi le code ci-dessus affichera bien uniquement que la conversion est bonne. Et en toute
logique, il ne passera pas dans le bloc de traitement derreur, car il ny en a pas eu.
Il est possible dobtenir des informations sur lexception en utilisant un paramtre dans le
bloc catch :
try
{
string chaine = "dix";
int valeur = Convert.ToInt32(chaine);
}
catch (Exception ex)
{
Console.WriteLine("Il y a un eu une erreur, plus d'informations ci-dessous :");

Console.WriteLine();
Console.WriteLine("Message d'erreur : " + ex.Message);
Console.WriteLine();
Console.WriteLine("Pile d'appel : " + ex.StackTrace);
Console.WriteLine();
Console.WriteLine("Type de l'exception : " + ex.GetType());
}

Ici, nous affichons le message derreur, la pile dappel et le type de lexception, ce qui
donne :

Dune manire gnrale, la mthode ToString() de lexception fournit des informations


suffisantes pour identifier lerreur :
try
{
string chaine = "dix";
int valeur = Convert.ToInt32(chaine);
}
catch (Exception ex)
{
Console.WriteLine("Il y a un eu une erreur : " + ex.ToString());
}

Ce qui donne :

Les exceptions peuvent tre de beaucoup de formes. Ici nous remarquons que lexception
est de type System.FormatException. Cette exception est utilise en gnral lorsque le
format dun paramtre ne correspond pas ce qui est attendu. En loccurrence ici nous
attendons un paramtre de type chaine de caractres qui reprsente un entier.
Cest une exception spcifique qui est ddie un type derreur prcis.
Il faut savoir que comme beaucoup dautres objets du framework .NET, les exceptions
spcifiques drivent dune classe de base, savoir la classe Exception. Il existe une
hirarchie entre les exceptions. Globalement, deux grandes familles dexceptions existent :
ApplicationException et SystemException. Elles drivent toutes les deux de la classe de
base Exception. La premire est utilise lorsque des erreurs rcuprables sur des
applications apparaissent, la seconde est utilise pour toutes les exceptions gnres par le
framework .NET.
Par exemple, lexception que nous avons vue, FormatException drive directement de
SystemException qui drive elle-mme de la classe Exception.
Le framework .NET dispose de beaucoup dexceptions correspondant beaucoup de
situations. Notons encore au passage une autre exception bien connue des dveloppeurs
qui est la NullReferenceException. Elle se produit lorsquon essaie daccder un objet
qui vaut null. Par exemple :
Voiture voiture = null;
voiture.Vitesse = 10;

Vous aurez remarqu que dans la construction suivante :


try
{
string chaine = "dix";
int valeur = Convert.ToInt32(chaine);
}
catch (Exception ex)
{
Console.WriteLine("Il y a un eu une erreur : " + ex.ToString());
}

nous voyons que le bloc catch prend en paramtre la classe de base Exception.
Cela veut dire que nous souhaitons intercepter toutes les exceptions qui drivent de
Exception ; cest--dire en fait toutes les exceptions, car pour avoir une exception, elle

doit forcment driver de la classe Exception.


Cest utile lorsque nous voulons attraper toutes les exceptions. Mais savons-nous
forcment quoi faire dans le cas de toutes les erreurs ?
Il est possible dtre plus prcis afin de nattraper quun seul type dexception. Il suffit de
prciser le type de lexception attendu comme paramtre du catch.
Par exemple le code suivant nous permet dintercepter toutes les exceptions du type
FormatException :
try
{
string chaine = "dix";
int valeur = Convert.ToInt32(chaine);
}
catch (FormatException ex)
{
Console.WriteLine(ex);
}

Cela veut par contre dire que si nous avons une autre exception ce moment-l, du style
NullReferenceException, lexception ne sera pas attrape. Ce qui fait que le code suivant
va planter :
try
{
Voiture v = null;
v.Vitesse = 10;
}
catch (FormatException ex)
{
Console.WriteLine("Erreur de format : " + ex);
}

En effet, nous demandons la surveillance de lexception FormatException uniquement.


Ainsi, lexception NullReferenceException ne sera pas attrape.

Intercepter plusieurs exceptions


Pour attraper les deux exceptions, il est possible denchainer les blocs catchavec des
paramtres diffrents :
try
{
// code provoquant une exception
}
catch (FormatException ex)
{
Console.WriteLine("Erreur de format : " + ex);
}
catch (NullReferenceException ex)
{
Console.WriteLine("Erreur de rfrence nulle : " + ex);
}

Dans ce code, cela veut dire que si le code surveill provoque une FormatException, alors
nous afficherons le message Erreur de format . Sil provoque une
NullReferenceException, alors nous afficherons le message Erreur de rfrence nulle
.
Notez bien que nous ne pouvons rentrer chaque fois que dans un seul bloc catch.
Une autre solution serait dattraper une exception plus gnraliste par exemple

SystemException dont drive FormatException et NullReferenceException :


try
{
// code provoquant une exception
}
catch (SystemException ex)
{
Console.WriteLine("Erreur systme : " + ex);
}

Par contre, il faut savoir que le code prcdent attrape toutes les exceptions qui drivent de
SystemException. Cest le cas de FormatException et NullReferenceException, mais cest
aussi le cas pour beaucoup dautres exceptions.
Lorsquon surveille un bloc de code, on commence en gnral par surveiller toutes les
exceptions les plus fines possibles qui nous intressent et on remonte en considrant les
exceptions plus gnrales, jusqu terminer par la classe Exception :
try
{
// code provoquant une exception
}
catch (FormatException ex)
{
Console.WriteLine("Erreur de format : " + ex);
}
catch (NullReferenceException ex)
{
Console.WriteLine("Erreur de rfrence nulle : " + ex);
}
catch (SystemException ex)
{
Console.WriteLine("Erreur systme autres que FormatException et NullReferenceException : " + ex);
}
catch(Exception ex)
{
Console.WriteLine("Toutes les autres exceptions : " + ex);
}

chaque excution, cest le bloc catch qui se rapproche le plus de lexception leve qui
est utilis. Cest un peu comme une opration conditionnelle. .NET vrifie dans un
premier temps que lexception nest pas une FormatException. Si ce nest pas le cas, il
vrifiera quil na pas faire une NullReferenceException. Ensuite, il vrifiera quil ne
sagit pas dune SystemException. Enfin, il interceptera toutes les exceptions dans le
dernier bloc de code car Exception tant la classe mre, toutes les exceptions sont
interceptes avec ce type.
A noter quil est possible dimbriquer les si cela savre pertinent. Par exemple :
string saisie = Console.ReadLine();
try
{
int entier = Convert.ToInt32(saisie);
Console.WriteLine("La saisie est un entier");
}
catch (FormatException)
{
try
{
double d = Convert.ToDouble(saisie);
Console.WriteLine("La saisie est un double");
}
catch (FormatException)
{
Console.WriteLine("La saisie n'est ni un entier, ni un double");
}
}

Ce code nous permet de tester si la saisie est un entier. Si une exception se produit, alors
nous tentons de le convertir en double. Sil y a encore une exception, alors nous affichons
un message indiquant que les deux conversions ont chou.

Le mot-cl finally
Nous avons vu que lorsquun code tait surveill dans un bloc , on sortait du bloc try si
jamais une exception tait leve, pour atterrir dans le bloc catch.
Cela veut dire quil est impossible de garantir quune instruction sera excute dans notre
code, si jamais une exception nous fait sortir du bloc.
Cest l quintervient le mot-cl finally. Il permet dindiquer que dans tous les cas, un
code doit tre excut, quune exception intervienne ou pas.
Par exemple :
try
{
string saisie = Console.ReadLine();
int valeur = Convert.ToInt32(saisie);
Console.WriteLine("Vous avez saisi un entier");
}
catch (FormatException)
{
Console.WriteLine("Vous avez saisi autre chose qu'un entier");
}
finally
{
Console.WriteLine("Merci d'avoir saisi quelque chose");
}

Nous demandons une saisie utilisateur. Nous tentons de convertir cette saisie en entier. Si
la conversion fonctionne, nous restons dans le bloc try et nous affichons :

Si la conversion lve une FormatException, nous affichons :

Dans les deux cas, nous passons obligatoirement dans le bloc finally pour afficher le
remerciement.
Le bloc finally est utile par exemple quand il sagit de librer la mmoire, denregistrer
des donnes, dcrire dans un fichier de log, etc.
Il est important de remarquer que peu importe la construction, le bloc finally est toujours
excut. Ainsi, mme si on relve une exception dans le bloc catch :
try
{
Convert.ToInt32("ppp");
}
catch (FormatException)
{
throw new NotImplementedException();
}
finally
{
Console.WriteLine("Je suis quand mme pass ici");
}

nous afficherons toujours notre message


Ou mme lorsque nous souhaitons sortir dune mthode avec le mot-cl return :
static void Main(string[] args)
{
MaMethode();
}
private static void MaMethode()
{
try
{
Convert.ToInt32("ppp");
}
catch (FormatException)
{
return;
}
finally
{
Console.WriteLine("Je suis quand mme pass ici");
}
}

Ici, le bloc finally est quand mme excut.

Lever une exception


Il est possible de dclencher soi-mme la leve dune exception. Cest utile si nous
considrons que notre code a atteint un cas limite, quil soit fonctionnel ou technique.
Pour lever une exception, nous utilisons le mot-cl throw, suivi dune instance dune
exception. Imaginons par exemple une mthode permettant de calculer la racine carre
dun double. Nous pouvons obtenir un cas limite lorsque nous tentons de passer un double
ngatif :
public static double RacineCarree(double valeur)
{
if (valeur <= 0)
throw new ArgumentOutOfRangeException("valeur", "Le paramtre doit tre positif");
return Math.Sqrt(valeur);
}

Ici, nous instancions une ArgumentOutOfRangeException en utilisant un constructeur deux


paramtres. Ceux-ci permettent dindiquer le nom du paramtre ainsi que le message
derreur. Cette exception est une exception du framework .NET utilise pour indiquer
quun paramtre est en dehors des plages de valeurs autorises. Cest exactement ce quil
nous faut ici.
Puis nous levons lexception avec le mot-cl throw.
Nous pouvons utiliser la mthode ainsi :
static void Main(string[] args)
{
try
{
double racine = RacineCarree(-5);
}
catch (Exception ex)
{
Console.WriteLine("Impossible d'effectuer le calcul : " + ex.Message);
}
}

Ce qui donnera :

Il aurait t possible de lever une exception plus gnrique avec la classe de base

Exception :
throw new Exception("Le paramtre doit tre positif");

Mais noubliez pas que plus lexception est finement type, plus elle sera facile traiter
prcisment. Cela permet dviter dattraper toutes les exceptions dans le mme catch
avec la classe de base Exception.
noter que lorsque notre programme rencontre le mot-cl throw, il arrte lexcution du
programme pour partir dans le bloc correspond (sil existe). Cela veut dire quune
mthode qui doit renvoyer un paramtre pourra compiler si son chemin se termine par une
leve dexception, comme cest le cas pour le calcul de la racine carre.

Propagation de lexception
Il est important de noter que lorsquun bout de code se situe dans un bloc , tout le code qui
est dessous est surveill, mme sil y a plusieurs mthodes qui sappellent les unes les
autres.
Par exemple, si nous appelons depuis la mthode Main() une Methode1() qui appelle une
Methode2() qui appelle une Methode3() qui lve une exception, alors nous serons capable
de lintercepter depuis la mthode Main() avec un :
static void Main(string[] args)
{
try
{
Methode1();
}
catch (NotImplementedException)
{
Console.WriteLine("On intercepte l'exception de la mthode 3");
}
}
public static void Methode1()
{
Methode2();
}
public static void Methode2()
{
Methode3();
}
public static void Methode3()
{
throw new NotImplementedException();
}

noter quune NotImplementedException est une exception utilise pour indiquer quun
bout de code na pas encore t implment.
Il est galement possible dattraper une exception, de la traiter et de choisir quelle
continue se propager.
Par exemple, imaginons que nous avons un bloc qui nous permet de surveiller tout notre
programme et que nous ayons surveiller un bout de code ailleurs dans le programme qui
peut produire une situation limite :
static void Main(string[] args)
{
try

{
MaMethode();
}
catch (Exception ex)
{
// ici, on intercepte toutes les erreurs possibles en indiquant qu'un problme inattendu
s'est produit
Console.WriteLine("L'application a rencontr un problme, un mail a t envoy
l'administrateur");
EnvoyerExceptionAdministrateur(ex);
}
}
public static void MaMethode()
{
try
{
Console.WriteLine("Veuillez saisir un entier :");
string chaine = Console.ReadLine();
int entier = Convert.ToInt32(chaine);
}
catch (FormatException)
{
Console.WriteLine("La saisie n'est pas un entier");
}
catch (Exception ex)
{
EnregistrerErreurDansUnFichierDeLog(ex);
throw;
}
}

Jai une saisie faire et convertir en entier. Si la conversion choue, je suis capable de
lindiquer lutilisateur en surveillant la FormatException. Par contre, si une exception
inattendue se produit, je souhaite pouvoir faire quelque chose, en loccurrence enregistrer
lexception dans un fichier, mais comme cest un cas limite non prvu je souhaite que
lexception continue se propager afin quelle soit attrape par le bloc qui permettra
denvoyer un mail ladministrateur.
Dans ce cas, jutilise un catch gnraliste pour traiter les exceptions inattendues afin de
loguer lexception puis jutilise directement le mot-cl throw afin de permettre de relever
lexception.

Crer une exception personnalise


Grce au typage fort des exceptions, il est pratique dutiliser un type dexception pour
reconnaitre un cas limite, comme une erreur de conversion ou une exception de rfrence
nulle.
Nous allons pouvoir utiliser certaines de ces exceptions pour nos besoins, comme ce que
nous avions fait avec lexception ArgumentOutOfRangeException.
Bien sr, il est possible de crer nous-mmes nos exceptions afin de lever nos propres
exceptions correspondant des cas limites fonctionnels ou techniques.
Par exemple, imaginons un site de-commerce qui affiche une page correspondant au
descriptif dun produit afin de pouvoir le commander. Nous chargeons le produit. Si le
produit nest plus en stock alors il peut tre judicieux de lever une exception afin que le
site puisse grer ce cas limite et afficher un message en consquence.
Crons donc notre exception personnalise : ProduitNonEnStockException
Pour ce faire, il suffit de crer une classe qui drive de la classe de base Exception :

public class ProduitNonEnStockException : Exception


{
}

Qui pourra tre utilise ainsi :


static void Main(string[] args)
{
try
{
ChargerProduit("TV HD");
}
catch (ProduitNonEnStockException ex)
{
Console.WriteLine("Erreur : " + ex.Message);
}
}
public static Produit ChargerProduit(string nomProduit)
{
Produit produit = new Produit(); // remplacer par le chargement du produit
if (produit.Stock <= 0)
throw new ProduitNonEnStockException();
return produit;
}

Il serait intressant de pouvoir rendre lexception plus explicite en modifiant par exemple
la proprit message de lexception. Pour ce faire, il suffit dutiliser la surcharge du
constructeur prenant une chaine de caractres en paramtre afin de pouvoir mettre jour la
proprit Message (qui est en lecture seule) :
public class ProduitNonEnStockException : Exception
{
public ProduitNonEnStockException() : base("Le produit n'est pas en stock")
{
}
}

Nous pouvons galement crer un constructeur qui prend le nom du produit en paramtre
afin de rendre le message encore plus prcis :
public class ProduitNonEnStockException : Exception
{
public ProduitNonEnStockException(string nomProduit) : base("Le produit " + nomProduit + " n'est
pas en stock")
{
}
}

Que nous pourrons utiliser ainsi :


public static Produit ChargerProduit(string nomProduit)
{
Produit produit = new Produit(); // remplacer par le chargement du produit
if (produit.Stock <= 0)
throw new ProduitNonEnStockException(nomProduit);
return produit;
}

Ce qui donne :

noter que pour construire cette exception personnalise, nous avons driv de la classe
de base Exception. Il aurait pu galement tre possible de driver de la classe
ApplicationException pour conserver une hirarchie cohrente dexceptions.
En rsum

Les exceptions permettent de grer les cas limites dune mthode.


On utilise le bloc pour encapsuler un bout de code surveiller.
Il est possible de crer des exceptions personnalises en drivant de la classe de base
Exception.
On peut lever tout moment une exception grce au mot-cl throw.
Les exceptions ne doivent pas servir masquer les bugs.

Chapitre 32

TP vnements et mto

Bienvenue dans le dernier TP de cette partie.


Tenez bon, aprs on change de domaine pour aborder dautres notions.
Dans ce TP, nous allons pratiquer les vnements. Le but est de savoir en crer un et de
pouvoir sy abonner pour tre notifi dune information.
Vous tes prts ? Alors cest parti !

Instructions pour raliser le TP


Nous allons raliser ici un mini simulateur de mto qui sera utilis par un statisticien afin
den faire des statistiques (logique ). Bon, cest le contexte, mais cest juste un
exemple. Nesprez pas non plus raliser un vrai simulateur mto dans ce TP.
Bref.
Nous devons crer un simulateur de mto. Lorsque celui-ci est dmarr, il gnre autant
de nombres alatoires que demand, des nombres entre 0 et 100.
Si le nombre alatoire est infrieur 5, alors cela veut dire que le temps est au soleil.
Sil est suprieur ou gal 5 et infrieur 50, alors nous aurons des nuages.
Sil est suprieur ou gal 50 et infrieur 90, alors nous aurons de la pluie. Sinon, nous
aurons de lorage.
Un vnement sera lev chaque changement de temps. Le but de notre statisticien est de
sabonner aux vnements du simulateur mto afin de compter le nombre de fois o il a
fait soleil et le nombre de fois o le temps a chang. Il affichera ensuite son rapport en
indiquant ces deux rsultats et le pourcentage de fois o il a fait soleil. (Je veux bien que
ce pourcentage soit un entier).
Cest tout pour lnonc. Maintenant, vous avez assez de connaissances pour que je ne
dtaille pas plus.
Bon courage !

Correction
Allez, cest parti pour la correction.
Tout dabord, nous devons crer notre simulateur mto. Il sera reprsent par une classe :
public class SimulateurMeteo

{
}

Nous aurons galement besoin de quelque chose pour reprsenter le temps, soleil, nuage,
pluie et orage. Une numration semble approprie :
public enum Temps
{
Soleil,
Nuage,
Pluie,
Orage
}

Enfin, nous aurons notre statisticien :


public class Statisticien
{
}

Commenons par le simulateur de mto. Nous aurons besoin de plusieurs variables


membres afin de stocker notre gnrateur de nombre alatoires, le dernier temps quil a
fait et le nombre de rptitions.
Le nombre de rptitions pourra tre un paramtre du constructeur :
public class SimulateurMeteo
{
private Temps? ancienTemps;
private int nombreDeRepetitions;
private Random random;
public SimulateurMeteo(int nombre)
{
random = new Random();
ancienTemps = null;
nombreDeRepetitions = nombre;
}
}

tant donn que nous allons dterminer plusieurs nombres alatoires, il est pertinent de ne
pas r-allouer chaque fois le gnrateur de nombre alatoire. Cest pour cela quon le
cre une unique fois dans le constructeur de la classe.
Crons dsormais une mthode permettant de dmarrer le simulateur et codons les rgles
mtiers du simulateur :
public class SimulateurMeteo
{
//[] Code supprim pour plus de clart []
public void Demarrer()
{
for (int i = 0; i < nombreDeRepetitions; i++)
{
int valeur = random.Next(0, 100);
if (valeur < 5)
GererTemps(Temps.Soleil);
else
{
if (valeur < 50)
GererTemps(Temps.Nuage);
else
{
if (valeur < 90)
GererTemps(Temps.Pluie);
else
GererTemps(Temps.Orage);
}
}
}
}

Cest trs simple, on boucle sur le nombre de rptitions. Un nombre alatoire est
dtermin chaque itration. La mthode GererTemps prend en paramtre le temps
dtermin partir du nombre alatoire.
Cest cette mthode GererTemps qui aura pour but de lever un vnement quand le temps
change :
public class SimulateurMeteo
{
public delegate void IlFaitBeauDelegate(Temps temps);
public event IlFaitBeauDelegate QuandLeTempsChange;
// [] Code supprim pour plus de clart []
private void GererTemps(Temps temps)
{
if (ancienTemps.HasValue && ancienTemps.Value != temps && QuandLeTempsChange != null)
QuandLeTempsChange(temps);
ancienTemps = temps;
}
}

Ici, jai choisi de crer un seul vnement quand le temps change et de lui indiquer le
temps quil fait en paramtres.
Nous avons donc besoin dun dlgu qui prend un Temps en paramtres et qui ne renvoie
rien. (Cest dailleurs souvent le cas des vnements). Puis nous avons besoin dun
vnement du type du dlgu.
Ensuite, si le temps a chang et que quelquun sest abonn lvnement, alors nous
levons lvnement.
Il ne reste plus qu remplir notre classe Statisticien. Cette classe a besoin de travailler
sur une instance de la classe SimulateurMeteo, nous pouvons donc lui en passer une dans
les paramtres du constructeur. Nous utiliserons galement des variables membres prives
permettant de stocker ses analyses :
public class Statisticien
{
private SimulateurMeteo simulateurMeteo;
private int nombreDeFoisOuLeTempsAChange;
private int nombreDeFoisOuIlAFaitSoleil;
public Statisticien(SimulateurMeteo simulateur)
{
simulateurMeteo = simulateur;
nombreDeFoisOuLeTempsAChange = 0;
nombreDeFoisOuIlAFaitSoleil = 0;
}
public void DemarrerAnalyse()
{
simulateurMeteo.QuandLeTempsChange += simulateurMeteo_QuandLeTempsChange;
simulateurMeteo.Demarrer();
}
public void AfficherRapport()
{
Console.WriteLine("Nombre de fois o le temps a chang : " + nombreDeFoisOuLeTempsAChange);
Console.WriteLine("Nombre de fois o il a fait soleil : " + nombreDeFoisOuIlAFaitSoleil);
Console.WriteLine("Pourcentage de beau temps : " + nombreDeFoisOuIlAFaitSoleil * 100 /
nombreDeFoisOuLeTempsAChange + " %");
}
private void simulateurMeteo_QuandLeTempsChange(Temps temps)
{
if (temps == Temps.Soleil)
nombreDeFoisOuIlAFaitSoleil++;

nombreDeFoisOuLeTempsAChange++;
}
}

Notons que dans la mthode DemarrerAnalyse, nous nous abonnons lvnement de


changement de temps. La mthode qui est appele lors de la notification est trs simple,
elle incrmente les compteurs.
Enfin, laffichage du rapport est trivial. Ici, comme nous navons que des entiers, la
division produira un entier galement.
Il ne reste plus qu faire fonctionner nos objets :
class Program
{
static void Main(string[] args)
{
SimulateurMeteo simulateurMeteo = new SimulateurMeteo(1000);
Statisticien statisticien = new Statisticien(simulateurMeteo);
statisticien.DemarrerAnalyse();
statisticien.AfficherRapport();
}
}

Ici, je travaille sur 1000 rptitions.


Lorsque jexcute lapplication, jobtiens :

videmment, vu que nous travaillons avec des nombres alatoires, chacun aura un rsultat
diffrent.
Et voil, cest termin pour ce TP. Notre application est fonctionnelle
Termin ? mmmhhh pas tout fait, allons un peu plus loin.

Aller plus loin


En ltat, ce code est fonctionnel. Cest parfait. Mais que se passe-t-il si nous dmarrons
plusieurs fois lanalyse ?
Nous pouvons essayer :
static void Main(string[] args)
{

SimulateurMeteo simulateurMeteo = new SimulateurMeteo(1000);


Statisticien statisticien = new Statisticien(simulateurMeteo);
statisticien.DemarrerAnalyse();
statisticien.AfficherRapport();
statisticien.DemarrerAnalyse();
statisticien.AfficherRapport();
statisticien.DemarrerAnalyse();
statisticien.AfficherRapport();
}

Nous obtiendrons quelque chose du genre :

Les valeurs augmentent alors quelles ne devraient pas.


Eh oui, nous ne rinitialisons pas les entiers permettant de stocker les statistiques. Ce nest
pas forcment un bug ici, comme je navais rien dit dans lnonc, il peut paraitre
pertinent de continuer incrmenter ces valeurs, comme a, je peux travailler sur une
moyenne.
Bon, disons que nous souhaitons les rinitialiser chaque fois, il suffit de remettre zro
les compteurs dans la mthode :
public void DemarrerAnalyse()
{
nombreDeFoisOuLeTempsAChange = 0;
nombreDeFoisOuIlAFaitSoleil = 0;
simulateurMeteo.QuandLeTempsChange += simulateurMeteo_QuandLeTempsChange;
simulateurMeteo.Demarrer();
}

Cependant, les compteurs augmentent toujours, moins vite, mais quand mme !
Je suis sr que vous lavez devin et que vous navez-vous-mme pas fait lerreur. En fait,
cela vient de labonnement lvnement. Chaque fois que nous dmarrons lanalyse,
nous nous rabonnons lvnement. Comme lvnement est multidiffusion, nous
rajoutons en fait chaque fois un appel la mthode avec le +=. Ce qui veut dire qu la
deuxime fois, nous appellerons la mthode deux fois, ce qui fera doubler les rsultats.
la troisime fois, ils triplent
Nous avons donc une erreur. Soit il faut sabonner une seule fois lvnement, par
exemple dans le constructeur, soit nous pouvons nous dsabonner la fin de lanalyse,
quand ce nest plus utile de recevoir lvnement.

Cest cela que je souhaite montrer. Il suffit de faire :


public void DemarrerAnalyse()
{
nombreDeFoisOuLeTempsAChange = 0;
nombreDeFoisOuIlAFaitSoleil = 0;
simulateurMeteo.QuandLeTempsChange += simulateurMeteo_QuandLeTempsChange;
simulateurMeteo.Demarrer();
simulateurMeteo.QuandLeTempsChange -= simulateurMeteo_QuandLeTempsChange;
}

On utilise loprateur -= pour enlever la mthode du dlgu.


Dune manire gnrale, il est bienvenu de se dsabonner dun vnement
lorsque lon sait quon ne va plus sen servir.
Cela permet dviter dencombrer la mmoire qui ne saurait pas forcment se librer toute
seule. Je nen dis pas plus car ceci est un concept avanc de gestion de mmoire. Gardez
seulement lesprit que si on a lopportunit de se dsabonner dun vnement, il faut le
faire.
Enfin, nous pouvons simplifier notre code en ne crant pas notre dlgu. Effectivement,
dans la mesure o celui-ci possde un seul paramtre, il est possible de le remplacer par le
dlgu Action<T>.
Il faut juste supprimer la dclaration du dlgu et remplacer la dclaration de
lvnement par :
public class SimulateurMeteo
{
public event Action<Temps> QuandLeTempsChange;
}

Voil pour ce TP sur les vnements. Il nous a permis de nous entrainer un peu sur les
vnements et de continuer nous entrainer sur la modlisation dapplications en utilisant
la POO.
Pas trs compliqu en soit, mais on peut vite se rendre compte quune application peut
fonctionner dans un cas, mais avoir un comportement inadapt dans un autre cas.
Dune manire gnrale, il est important de retenir quil faut se dsabonner dun
vnement quand cela est possible.

Quatrime partie

C# avanc

Chapitre 33

Crer un projet bibliothques de classes

Pour linstant, nous navons cr que des projets de type application console. Il existe
plein dautres modles de projet. Un des plus utiles est le projet permettant de crer des
bibliothques de classes.
Il sagit dun projet qui va permettre de contenir des classes que nous pourrons utiliser
dans des applications. Exactement comme la bibliothque de classes du framework .NET
qui nous permet dutiliser la classe Math ou les exceptions, ou plein dautres choses
Ce type de projet permet donc de crer une assembly sous la forme dun fichier avec
lextension .dll. Ces assemblys sont donc des bibliothques qui seront utilisables par
nimporte quelle application utilisant un langage compatible avec le framework .NET.

Pourquoi crer une bibliothque de classes ?


Globalement pour deux raisons que nous allons dtailler :
Rutilisabilit
Architecture
Rutilisabilit

Comme indiqu en introduction, le projet de type bibliothque de classes permet dobtenir


des assemblys avec lextension .dll.
Nous pouvons y mettre tout le code C# que nous voulons, notamment des classes qui
auront un intrt tre utilises plusieurs endroits ou partages par plusieurs
applications. Cest le cas des assemblys du framework .NET. Elles possdent plein de
code trs utile que nous aurons avantage utiliser pour crer nos applications.
De la mme faon, nous allons pouvoir crer des classes qui pourront tre rutilises
plusieurs endroits. Comme notre classe permettant de grer les listes chaines. Nimporte
quel programme qui utilise des listes chaines aura intrt ne pas tout rinventer et
simplement rutiliser ce code prt lemploi. Il suffira pour ce faire de rutiliser cette
classe en rfrenant lassembly, comme nous lavons dj vu.
Architecture :

Lautre avantage dans la cration de bibliothques de classes est de pouvoir architecturer


son application de manire faciliter sa mise en place, sa maintenabilit et lvolutivit du
code. Larchitecture informatique cest comme larchitecture dune maison. Si nous
mettons la douche au mme endroit que le compteur lectrique ou que la machine laver
est juste ct de la chambre coucher, cela peut poser des problmes. De mme, que
penser dun architecte sil place les toilettes ct du lit. Une maison mal architecture est
une maison o il ne fait pas bon vivre. De mme quune application mal architecture est
une application o il ne fait pas bon faire la maintenance applicative !
Architecturer son application est important surtout si lapplication est grosse. Par exemple,
une bonne pratique dans une application informatique est la dcomposition en couches.
Nous nallons pas faire ici un cours darchitecture, mais le but est de sparer les
composantes de lapplication. Un grand nombre dapplications de gestions est compose
dune couche de prsentation, dune couche mtier et dune couche daccs aux donnes,
les couches communiquant entre-elles.
Le but est de limiter les modifications dune couche lors de la modification dune autre. Si
toutes les couches taient mlanges alors chaque modification impliquerait une srie de
modifications en chaine.
On peut faire une analogie avec un plat de lasagnes et un plat de spaghetti. Il est difficile
de toucher un spaghetti sans toucher les autres. Cependant, il pourrait tre envisageable
de toucher la couche du dessus du plat de lasagnes pour rajouter un peu de fromage sans
perturber ce quil y a dessous. Miam.
Il est donc intressant davoir une bibliothque de classes qui gre laccs aux donnes,
une autre qui gre les rgles mtier et une autre qui soccupe dafficher le tout.
Je marrte l sur larchitecture, vous aurez loccasion de vous y confronter bien assez tt
.

Crer un projet de bibliothque de classe


Pour crer une bibliothque de classes, on utilise lassistant de cration de nouveau projet
(Fichier > Nouveau > Nouveau projet), comme on la fait pour une application console
sauf quici, on utilisera le modle bibliothque de classes . Ne le faisons pas encore.

Si nous faisons cela, Visual C# Express va nous crer une nouvelle solution contenant le
projet de bibliothques de classes. Cest possible sauf quen gnral, on ajoute une
bibliothque de classes pour quelle serve dans le cadre dune application. Cela veut dire
que nous pouvons ajouter ce nouveau projet notre solution actuelle.
Ainsi, celle-ci contiendra deux projets :
Lapplication console, qui sera excutable par le systme dexploitation
La bibliothque de classes, qui sera utilise par lapplication.
Pour ce faire, on peut faire un clic droit sur notre solution et faire Ajouter >
Nouveau Projet.

Ici, on choisit le projet de type bibliothque de classes, sans oublier de lui donner un nom,
par exemple MaBibliotheque .

Visual C# Express nous cr notre projet et lajoute la solution, comme on peut le voir
dans lexplorateur de solutions.

Il le gnre avec un fichier Class1.cs que nous pouvons supprimer.


Nous pouvons voir que notre application console est en gras. Cela veut dire que cest ce
projet que Visual C# Express va tenter de dmarrer lorsque nous utiliserons ctrl + F5. Si ce
nest pas le cas, il peut tenter de dmarrer la bibliothque, ce qui na pas de sens vu quelle
na pas de mthode Main(). Dans ce cas, vous pourrez le changer en faisant un clic droit
sur le projet console et en choisissant Dfinir comme projet de dmarrage .
Dans ce projet, nous pourrons dsormais crer nos classes en ajoutant des nouvelles
classes au projet, comme on la dj fait avec lapplication console.
Ajoutons par exemple la classe suivante :
namespace MaBibliotheque
{
public class Bonjour
{
public void DireBonjour()
{
Console.WriteLine("Bonjour depuis la bibliothque MaBibliotheque");

}
}
}

Proprits de la bibliothque de classe


Si nous ouvrons les proprits de notre projet (bouton droit sur le projet, Proprits) :

La fentre des proprits saffiche :

Nous pouvons voir diffrentes informations et notamment dans longlet Application que
le projet possde un nom dassembly, qui correspond ici au nom du projet ainsi quun
espace de nom par dfaut, qui correspond galement au nom du projet. Le nom de
lassembly servira identifier lassembly, alors que lespace de nom par dfaut sera celui
donn lorsque nous ajouterons une nouvelle classe (ou autre) notre projet. Cet espace de

nom pourra tre chang, mais en gnral on garde un nom cohrent avec les arborescences
de notre projet.
Notons au passage quil y a une option permettant dindiquer la version du framework
.NET que nous souhaitons utiliser. Ici, nous gardons la version 4.

Gnrer et utiliser une bibliothque de classe


Nous pouvons alors compiler cette bibliothque de classes, soit individuellement, soit en
compilant tout le projet.
Rendons nous dans le rpertoire o nous avons sauvegard notre bibliothque, nous
voyons dans le rpertoire de sortie (chez moi : C:\Users\Nico-

PC\Documents\Visual Studio 2010\Projects\C#\MaPremiereApplication\MaBibliotheque\bin\Re

quil y a un fichier du nom de notre projet dont lextension est .dll.


Cest notre bibliothque de classes (mme sil ny a quune classe dedans !). Elle possde
le mme nom que celui que nous avons vu dans les proprits du projet.
Jen profite pour vous faire remarquer qu son ct, il y a galement un fichier du mme
nom avec lextension .pdb. Je peux enfin vous rvler de quoi il sagit. Ce fichier contient
les informations de dbogages, utiles lorsque nous dboguons notre application. Sans ce
fichier, impossible de dboguer lintrieur du code de notre classe.
Revenons lassembly gnre. Cest cette dll quil faudra rfrencer dans notre projet,
comme nous avons vu au chapitre sur le rfrencement dassemblys. Si vous avez un
doute, nhsitez pas retourner le consulter. Ainsi, nous serons en mesure dutiliser la
classe de notre bibliothque.
Rappelez-vous, pour rfrencer une assembly, nous faisons un clic droit sur les rfrences
du projet et slectionnons Ajouter une rfrence . Nous pourrons rfrencer notre
bibliothque soit en utilisant longlet Parcourir, qui va nous permettre de pointer
directement le fichier dll contenant nos classes :

soit en rfrenant directement le projet de bibliothque de classes qui se trouve dans


notre solution en utilisant longlet Projet :

Cest ce choix qui doit tre privilgi lorsque notre solution contient les projets
rfrencer.
Gnralement, vos bibliothques vont voluer en mme temps que votre programme, donc
il est judicieux de les avoir dans la mme solution que son application. Nous pourrons
donc faire des rfrences projet.
Si cependant les bibliothques sont stables et ne sont pas sujettes voluer, alors vous
pourrez les rfrencer directement partir des dll, vous y gagnerez en temps de
compilation.
Utilisons dsormais notre classe avec le code suivant :
MaBibliotheque.Bonjour bonjour = new MaBibliotheque.Bonjour();
bonjour.DireBonjour();

Vous pouvez aussi bien sr inclure lespace de nom MaBibliotheque pour viter davoir
prfixer notre classe. En gnral, lespace de nom dune bibliothque est diffrent de celui
de lapplication.
Notez que pour quune classe, comme la classe Bonjour, puisse tre utilise par une
application rfrenant son assembly, elle doit tre dclare en public afin quelle soit
visible par tout le monde.

Le mot-cl internal
Nous avons dj vu ce mot-cl lorsque nous parlions de visibilit avec notamment les
autres mots-cls public, private et protected.
Cest avec les assemblys que le mot-cl internal prend tout son sens. Il permet dindiquer
quune classe, mthode ou proprit ne sera accessible qu lintrieur dune assembly.

Cela permet par exemple quune classe soit utilisable par toutes les autres classes de cette
assembly mais quelle ne puisse pas tre utilise par une application rfrenant
lassembly.
Par exemple crons dans notre bibliothque de classes les trois classes suivantes :
public class Client
{
private string login;
public string Login
{
get { return login; }
}
private string motDePasse;
public string MotDePasse
{
get { return motDePasse.Crypte(); }
}
public Client(string loginClient, string motDePasseClient)
{
login = loginClient;
motDePasse = motDePasseClient;
}
}
public static class Generateur
{
public static string ObtenirIdentifiantUnique()
{
Random r = new Random();
string chaine = string.Empty;
for (int i = 0; i < 10; i++)
{
chaine += r.Next(0, 100);
}
return chaine.Crypte();
}
}
internal static class Encodage
{
internal static string Crypte(this string chaine)
{
return Convert.ToBase64String(Encoding.Default.GetBytes(chaine));
}
internal static string Decrypte(this string chaine)
{
return Encoding.Default.GetString(Convert.FromBase64String(chaine));
}
}

Les deux premires sont des classes publiques qui peuvent tre utilises depuis nimporte
o, comme depuis notre programme principal :
class Program
{
static void Main(string[] args)
{
Client client = new Client("Nico", "12345");
Console.WriteLine(client.MotDePasse);
Console.WriteLine(Generateur.ObtenirIdentifiantUnique());
}
}

Par contre, la classe Encodage nest accessible que pour les deux autres classes, car elles
sont dans la mme assembly. Cela veut dire que si nous tentons de lutiliser depuis notre
mthode Main() :

static void Main(string[] args)


{
string chaine = "12345".Crypte();
Encodage.Crypte("12345");
}

Nous aurons des erreurs de compilation.


Cet exemple permet dillustrer lintrt pas toujours vident du mot-cl internal.
noter quil existe enfin le mot-cl protected internal qui permet dindiquer que des
lments sont accessibles un niveau internal pour les classes dune mme assembly
mais protected pour les autres assemblys, ce qui permet dappliquer les principes de
substitutions ou dhritage.
Voil, vous avez vu comment crer une bibliothque de classes. Nhsitez pas crer ce
genre de projet afin dy mettre toutes les classes qui peuvent tre utilises par plusieurs
applications. Comme a, il suffit dune simple rfrence pour accder au code qui y est
contenu.
Vous vous en servirez galement pour mieux architecturer vos applications, le code sen
trouvera plus clair et plus maintenable.
La traduction en anglais de bibliothque est library , vous verrez souvent ce
mot l sur internet. Vous le verrez galement mal francis avec librairie, ce qui
est videmment une erreur.
En rsum

Un projet de bibliothque de classes permet de regrouper des classes pouvant tre


utilises entre plusieurs applications.
Les bibliothques de classes gnrent des assemblys avec lextension .dll.
Elles permettent galement de mieux architecturer un projet.

Chapitre 34

Plus loin avec le C# et .NET

Maintenant que nous en savons plus, nous allons pouvoir aborder quelques notions qui me
paraissent importantes et que nous navons pas encore traites. Ce sera loccasion
dapprofondir nos connaissances et de comprendre un peu mieux certains points qui
auraient pu vous paratre obscurs.
Nous allons voir des instructions C# supplmentaires qui vont nous permettre de
complter nos connaissances en POO. Nous en profiterons galement pour dtailler
comment le framework .NET gre les types en mmoire. Vous en saurez plus sur le
formatage des chanes et aurez un aperu du ct obscur du framework .NET, la rflexion
!
Bref, tout plein de nouvelles cordes nos arcs nous permettant dtre de plus en plus
efficace avec le C#. Ces nouveaux concepts vous serviront sans doute moins souvent, mais
sont importants connatre.

Empcher une classe de pouvoir tre hrite


Parmi ces nouveaux concepts, nous allons voir comment il est possible de faire en sorte
quune classe ne puisse pas tre hrite. Rappelez-vous, un moment jai dit quon ne
pouvait pas crer une classe qui spcialise la classe String
Cest quoi ce mystre ? Nous, quand nous crons une classe, nimporte qui peut
en hriter. Pourquoi pas la classe String ?
Cest parce que je vous avais cach le mot-cl sealed. Il permet dempcher la classe
dtre hrite. Tout simplement.
Pourquoi vouloir empcher dtendre une classe en la drivant ?
Pour plusieurs raisons, mais gnralement nous allons recourir ce mot-cl lorsquune
classe est trop spcifique une mthode ou une bibliothque et que lon souhaite
empcher quelquun de pouvoir obtenir du code instable en tendant son fonctionnement.
Cest typiquement le cas pour la classe String. Il y a beaucoup de classes dans le
framework .NET qui sont marques comme sealed afin dviter que lon puisse faire
nimporte quoi.
Il faut par contre faire attention car lemploi de ce mot-cl restreint normment la faon
dont on pourrait employer cette classe.
Pour dclarer une classe sealed (scelle en franais) il suffit de prcder le mot-cl class

du mot-cl sealed :
public sealed class ClasseImpossibleADeriver
{
}

Ainsi, toute tentative dhritage :


public class MaClasse : ClasseImpossibleADeriver
{
}

se soldera par une erreur de compilation dshonorante :


impossible de driver du type sealed 'MaPremiereApplication.ClasseImpossibleADeriver'

noter que le mot-cl sealed fonctionne galement pour les mthodes, dans ce cas, cela
permet dempcher la substitution dune mthode. Reprenons notre exemple du chien
muet :
public class Chien
{
public virtual void Aboyer()
{
Console.WriteLine("Wouf");
}
}
public class ChienMuet : Chien
{
public sealed override void Aboyer()
{
Console.WriteLine("...");
}
}

Ici, nous marquons la mthode comme sealed, ce qui fait quune classe qui drive de
ChienMuet ne sera pas capable de remplacer la mthode permettant daboyer. Le code
suivant :
public class ChienAvecSyntheseVocale : ChienMuet
{
public override void Aboyer()
{
Console.WriteLine("Bwarf");
}
}

entrainera lerreur de compilation :


'MaPremiereApplication.ChienAvecSyntheseVocale.Aboyer()' : ne peut pas se substituer un membre
hrit 'MaPremiereApplication.ChienMuet.Aboyer()', car il est sealed

Voil pour ce mot-cl, utiliser en connaissance de cause.

Prcisions sur les types et gestion mmoire


Nous avons vu beaucoup de types diffrents au cours des chapitres prcdents. Nous
avons vu les structures, les classes, les numrations, les dlgus, etc.
Certains sont des types valeur et dautres des types rfrence. Et ils sont tous des objets.
Voici un petit schma rcapitulatif des types que nous avons dj vus :

Nous avons dit brivement quils taient grs diffremment par le framework .NET et
que les variables de type valeur contenaient directement la valeur, alors que les variables
de type rfrence possdent un lien vers un objet en mmoire.
Ce quil se passe en fait quand nous dclarons une variable de type valeur, cest quil cre
directement la valeur en mmoire dans ce quon appelle la pile . Cest une zone
mmoire (limite) o il est trs rapide de mettre et de chercher des valeurs. De plus, cette
mmoire na pas besoin dtre libre par le ramasse-miettes (que nous verrons un peu
plus bas). Cest pour cela que pour des raisons de performances on peut tre amen
choisir les types valeur.
Par exemple lorsque nous faisons :
int age = 10;
double pourcentage = 5.5;

il se passe ceci en mmoire :

La variable age est une adresse mmoire contenant la valeur 10. Cest la mme chose pour
la variable pourcentage sauf que lemplacement mmoire est plus important car on peut
stocker plus de choses dans un double que dans un int.
En ce qui concerne les types valeur, lorsque nous instancions par exemple une classe, le
C# instancie la variable maVoiture dans la pile et lui met dedans une rfrence vers le vrai
objet qui lui est allou sur une zone mmoire de capacit plus importante, mais accs

plus lent, que lon appelle le tas . Comme il est gr par le framework .NET, on
lappelle le tas manag .
Si nous avons le code suivant :
public class Voiture
{
public int Vitesse { get; set; }
public string Marque { get; set; }
}
Voiture maVoiture = new Voiture { Vitesse = 10, Marque = "Peugeot" };

Nous aurons en mmoire :

noter que lorsquune variable sort de sa porte et quelle nest plus utilisable par qui que
ce soit, alors la case mmoire sur la pile est marque comme de nouveau libre.
Voil grosso modo comment est gre la mmoire.
Il manque encore un lment fondamental du mcanisme de gestion mmoire : le
ramasse-miettes.
Nous en avons dj parl brivement, le ramasse miette sert librer la mmoire qui nest
plus utilise dans le tas manag. Il y aurait de quoi crire un gros tutoriel rien que sur son
fonctionnement, aussi nous allons expliquer rapidement comment il fonctionne sans trop
rentrer dans les dtails.
Le ramasse-miettes est souvent dnomm par sa traduction anglaise, le garbage
collector.
Pourquoi librer la mmoire ?
On a vu que quand une variable sort de porte, alors la case mmoire sur la pile est
marque comme nouveau libre. Cest trs bien avec les types valeur qui sont uniquement
sur la pile. Mais avec les types rfrence ?
Que donne le code suivant lorsque nous quittons la mthode CreerVoiture ?
public class Voiture
{
public int Vitesse { get; set; }
public string Marque { get; set; }
}
static void Main(string[] args)
{
CreerVoiture();
}
public static void CreerVoiture()
{
Voiture maVoiture = new Voiture { Vitesse = 10, Marque = "Peugeot" };
}

Nous avons dit que la mmoire sur la pile redevenait libre. La rfrence vers lobjet est
donc casse. Cependant, la mmoire sur le tas est toujours occupe.

Cela veut dire que notre objet est toujours en mmoire sauf que la variable maVoiture
nexiste plus et donc, ne rfrence plus cet objet.
Dans un langage comme le C++, nous aurions t obligs de vider explicitement la
mmoire rfrence par la variable.
En C#, pas besoin ! Cest le ramasse-miettes qui le fait pour nous. Encore un truc de
fainant a.
En fait, cest plutt une scurit quun truc de fainant. Cest la garantie que la mmoire
utilise par les objets qui nexistent plus est vide et, du coup, redevient disponible pour
de nouveaux objets. Grce au ramasse-miettes, nous vitons ce que lon appelle les fuites
mmoires.
Super gnial a. Mais, il passe quand ce ramasse-miettes ?
La rponse est simple : on ne sait pas quand il passe .
En fait, il passe quand il a besoin de mmoire ou quand lapplication semble ne rien faire.
videmment, lorsque le ramasse-miettes passe, les performances de lapplication sont un
petit peu pnalises. Cest pour cela que lalgorithme de passage du ramasse-miettes est
optimis pour essayer de dranger le moins possible lapplication, lors des moments
dinactivit par exemple. Par contre, quand il y a besoin de mmoire, il ne se pose pas la
question : il passe quand mme.
Il y aurait beaucoup de choses dire encore sur ce mcanisme mais arrtons-nous l.
Retenons que le ramasse-miettes est un superbe outil qui nous permet de garantir
lintgrit de notre mmoire et quil sefforce au mieux de nous embter le moins possible.
Il est important de librer les ressources dont on na plus besoin quand cest possible, par
exemple en se dsabonnant dun vnement ou lorsque nous avons utilis des ressources
natives (ce que nous ne verrons pas dans ce tutoriel) ou lors daccs des fichiers ou des
bases de donnes.

Masquer une mthode


Dans la partie prcdente sur la substitution, nous avons vu quon pouvait substituer une
mthode grce au mot-cl override. Cette mthode devait dailleurs stre dclare
comme candidate cette substitution grce au mot-cl virtual.

Rappelez-vous de ce code :
public class Chien
{
public virtual void Aboyer()
{
Console.WriteLine("Wouaf !");
}
}
public class Caniche : Chien
{
public override void Aboyer()
{
Console.WriteLine("Wiiff");
}
}

Si nous instancions des chiens et des caniches de cette faon :


static void Main(string[] args)
{
Chien chien = new Chien();
Caniche caniche = new Caniche();
Chien canicheTraiteCommeUnChien = new Caniche();
chien.Aboyer();
caniche.Aboyer();
canicheTraiteCommeUnChien.Aboyer();
}

Nous aurons :
Wouaf !
Wiiff
Wiiff

En effet, le premier objet est un chien et les deux suivants sont des caniches. Grce la
substitution, nous faisons aboyer notre chien, notre caniche et notre caniche qui se fait
manipuler comme un chien.
Il est possible de masquer des mthodes qui ne sont pas forcment marques comme
virtuelles grce au mot-cl new.
ce moment-l, la mthode ne se substitue pas la mthode drive mais la masque pour
les types drivs. Voyons cet exemple :
public class Chien
{
public void Aboyer()
{
Console.WriteLine("Wouaf !");
}
}
public class Caniche : Chien
{
public new void Aboyer()
{
Console.WriteLine("Wiiff");
}
}

Remarquons que la mthode Aboyer() de la classe Chien nest pas marque virtual et que
la mthode Aboyer de la classe Caniche est marque avec le mot-cl new. Il ne sagit pas ici
de substitution. Il sagit juste de deux mthodes qui portent le mme nom, mais la
mthode Aboyer() de la classe Caniche se charge de masquer la mthode Aboyer() de la
classe mre.
Ce qui fait que les prcdentes instanciations :

static void Main(string[] args)


{
Chien chien = new Chien();
Caniche caniche = new Caniche();
Chien canicheTraiteCommeUnChien = new Caniche();
chien.Aboyer();
caniche.Aboyer();
canicheTraiteCommeUnChien.Aboyer();
}

produiront cette fois-ci :


Wouaf !
Wiiff
Wouaf !

Cest le dernier cas qui peut surprendre. Rappelez-vous, il ne sagit pas dun
remplacement de mthode cette fois-ci, et donc le fait de manipuler le caniche en tant que
Chien ne permet pas de remplacer le comportement de la mthode Aboyer(). Dans ce cas,
on appelle bien la mthode Aboyer correspondant au type Chien, ce qui a pour effet
dafficher Wouaf ! .
Si nous navions pas utilis le mot-cl new, nous aurions eu le mme comportement mais
le compilateur nous aurait avertis de ceci grce un avertissement :
'MaPremiereApplication.Caniche.Aboyer()' masque le membre hrit
'MaPremiereApplication.Chien.Aboyer()'. Utilisez le mot cl new si le masquage est intentionnel.
</citation>

Il nous prcise que nous masquons effectivement la mthode de la classe mre et que si
cest fait exprs, alors il faut lindiquer grce au mot-cl new afin de lui montrer que nous
savons ce que nous faisons.
Notez quand mme que dans la majorit des cas, en POO, ce que vous voudrez faire cest
bien substituer la mthode et non la masquer.
Cest le mme principe pour les variables, il est galement possible de masquer une
variable, quoiquen gnral, cela reflte plutt une erreur dans le code. Si nous faisons :
public class Chien
{
protected int age;
}
public class Caniche : Chien
{
private int age;
public Caniche()
{
age = 0;
}
public override string ToString()
{
return "J'ai " + age + " ans";
}
}

Alors le compilateur nous indique que la variable age de la classe Caniche masque celle
dont nous avons hrit de la classe Chien. Si cest intentionnel, il nous propose de la
marquer avec le mot-cl new. Sinon, cela nous permet de constater que cette variable est
peut-tre inutile (srement dailleurs !)

Le mot-cl yield
Nous avons vu dans le TP sur les gnriques comment crer un numrateur. Cest une
tche un peu lourde qui ncessite pas mal de code.
Omettons un instant que la classe String soit numrable et crons pour lexemple une
classe qui permette dnumrer une chaine de caractres. Nous aurions pu faire quelque
chose comme a :
public class ChaineEnumerable : IEnumerable<char>
{
private string chaine;
public ChaineEnumerable(string valeur)
{
chaine = valeur;
}
public IEnumerator<char> GetEnumerator()
{
return new ChaineEnumerateur(chaine);
}
IEnumerator IEnumerable.GetEnumerator()
{
return new ChaineEnumerateur(chaine);
}
}
public class ChaineEnumerateur : IEnumerator<char>
{
private string chaine;
private int indice;
public ChaineEnumerateur(string valeur)
{
indice = -1;
chaine = valeur;
}
public char Current
{
get { return chaine[indice]; }
}
public void Dispose()
{
}
object IEnumerator.Current
{
get { return Current; }
}
public bool MoveNext()
{
indice++;
return indice < chaine.Length;
}
public void Reset()
{
indice = -1;
}
}

Nous avons une classe ChaineEnumerable qui encapsule une chaine de caractres de type
string. Et une classe ChaineEnumerateur qui permet de parcourir une chaine et de
renvoyer chaque itration un caractre, reprsent par le type char (qui est un alias de la

structure System.Char).
Rien de bien sorcier, mais un code plutt lourd.
Regardons prsent le code suivant :
public class ChaineEnumerable : IEnumerable<char>
{
private string chaine;
public ChaineEnumerable(string valeur)
{
chaine = valeur;
}
public IEnumerator<char> GetEnumerator()
{
for (int i = 0; i < chaine.Length; i++)
{
yield return chaine[i];
}
}
IEnumerator IEnumerable.GetEnumerator()
{
return GetEnumerator();
}
}

Il fait la mme chose, mais dune manire bien simplifie. Nous remarquons lapparition
du mot-cl yield. Il permet de crer facilement des numrateurs. Il permet, combin au
mot-cl return, de renvoyer un lment dune collection et de passer llment suivant.
Il peut tre combin au mot-cl break galement pour permettre darrter lnumration.
Ce qui veut dire que nous aurions pu crire la mthode GetEnumerator() galement de
cette faon :
public IEnumerator<char> GetEnumerator()
{
int i = 0;
while (true)
{
if (i == chaine.Length)
yield break;
yield return chaine[i];
i++;
}
}

Ce qui est plus laid, je le conois , mais qui permet dillustrer le mot-cl yield break.
Le mot-cl yield permet galement de renvoyer un IEnumerable (ou sa version gnrique),
ainsi un peu btement, on pourrait faire :
static void Main(string[] args)
{
foreach (string prenom in ObtenirListeDePrenoms())
{
Console.WriteLine(prenom);
}
}
public static IEnumerable<string> ObtenirListeDePrenoms()
{
yield return "Nicolas";
yield return "Jrmie";
yield return "Delphine";
}

Cet exemple va surtout me permettre dillustrer ce quil se passe exactement grce au


mode debug.
Mettez un point darrt au dbut du programme et lancez-le en mode debug :

Appuyez sur F11 plusieurs fois pour rentrer dans la mthode ObtenirListeDePrenoms, nous
voyons lindicateur se dplacer sur les diffrentes parties de linstruction foreach. Puis
nous arrivons sur la premire instruction yield return :

Appuyons nouveau sur F11 et nous pouvons constater que nous sortons de la mthode et
que nous rentrons nouveau dans le corps de la boucle foreach qui va nous afficher le
premier prnom. Continuons les F11 jusqu repasser sur le mot-cl in et rentrer
nouveau dans la mthode ObtenirListeDePrenoms() et nous pouvons constater que nous
retournons directement au deuxime mot-cl yield :

Et ainsi de suite jusqu ce quil ny ait plus rien dans la mthode


ObtenirListeDePrenoms() et que du coup le foreach se termine.
la premire lecture, ce nest pas vraiment ce comportement que nous aurions attendu
Voil pour le principe du yield return.
noter quun yield break nous aurait fait sortir directement de la boucle foreach.
Le mot-cl yield participe ce quon appelle lexcution diffre. On value la mthode
ObtenirListeDePrenoms() quau moment o on parcoure le rsultat avec la boucle
foreach, ce qui pourrait paratre surprenant.
Par exemple, si nous faisons :
public static IEnumerable<string> ObtenirListeDePrenoms()
{
yield return "Nicolas";
yield return "Jrmie";
yield return "Delphine";
}
static void Main(string[] args)
{
IEnumerable<string> prenoms = ObtenirListeDePrenoms();
Console.WriteLine("On fait des choses");
foreach (string prenom in prenoms)
{
Console.WriteLine(prenom);
}
}

Et que nous mettons un point darrt sur le Console.WriteLine et un autre point darrt
dans la mthode ObtenirListeDePrenoms(), alors trangement, on va sarrter dans un
premier temps sur le point darrt positionn sur la ligne o il y a le Console.WriteLine et
ensuite nous passerons dans le point darrt positionn dans la mthode
ObtenirListeDePrenoms().
En effet, on ne passera dans la mthode que lorsque nous itrerons explicitement sur les
prnoms grce au foreach.
Nous reviendrons sur cette excution diffre un peu plus loin. Cest le mot-cl yield qui
fait a.

Le formatage de chaines, de dates et la culture

Pour linstant, lorsque nous avons eu besoin de mettre en forme des chaines de caractres,
nous avons utilis la mthode Console.WriteLine avec en paramtres des chaines
concatnes entre elles grce loprateur +. Par exemple :
int age = 30;
Console.WriteLine("J'ai " + age + " ans");

Il est important de savoir quil est possible de formater la chaine un peu plus simplement
et surtout beaucoup plus efficacement. Ceci est possible grce la mthode
string.Format. Le code prcdent peut par exemple tre remplac par :
int age = 30;
string chaine = string.Format("J'ai {0} ans", age);
Console.WriteLine(chaine);

La mthode string.Format accepte en premier paramtre un format de chaine. Ici, nous


lui indiquons une chaine de caractres avec au milieu un caractre spcial : {0}. Il permet
de dire : remplace-moi le {0} avec le premier paramtre qui va suivre , en loccurrence
ici la variable age. Si nous avons plusieurs valeurs, il est possible dutiliser les caractres
spciaux {1}, puis {2}, etc chaque nombre reprsentant lindice correspondant au
paramtre. Par exemple :
double valeur1 = 10.5;
double valeur2 = 4.2;
string chaine = string.Format("{0} multipli par {1} s'crit \"{0} * {1}\" et donne comme rsultat :
{2}", valeur1, valeur2, valeur1 * valeur2);
Console.WriteLine(chaine);

Ce qui donne :
10,5 multipli par 4,2 s'crit "10,5 * 4,2" et donne comme rsultat : 44,1

Vous avez pu remarquer quil est possible dutiliser le mme paramtre plusieurs
endroits.
La mthode Console.WriteLine dispose aussi de la capacit de formater des chaines de
caractres. Ce qui veut dire que le code prcdent peut scrire :
double valeur1 = 10.5;
double valeur2 = 4.2;
Console.WriteLine("{0} multipli par {1} s'crit \"{0} * {1}\" et donne comme rsultat : {2}",
valeur1, valeur2, valeur1 * valeur2);

Ce qui revient absolument au mme.


Parlons culture dsormais. Nayez pas peur, rien voir avec des questions pour des
champions, la culture en informatique correspond tout ce qui a trait aux diffrences entre
les langues et les paramtres locaux. Par exemple, en France nous ncrivons pas les dates
de la mme faon quaux tats-Unis. En France, nous cririons le jour de Nol 2011 de
cette faon :
Citation : En France
Le 25/12/2011
Alors quaux tats-Unis, ils lcriraient :
Citation : Aux tats-Unis
The 12/25/2011
En inversant donc le mois et le jour.
De la mme faon, il existe des diffrences lorsque nous crivons les nombres virgule.

En France, nous cririons :


Citation : En France
123,45
Alors quaux tats-Unis, ils lcriraient :
Citation : Aux tats-Unis
123.45
Un point la place dune virgule, subtile diffrence.
On a donc souvent des diffrences entre les langues. Et mme plus, on peut avoir des
diffrences entre les langues en fonction de lendroit o elles sont parles. Citons par
exemple le franais de France et le franais du Canada.
Il existe donc au sein du systme dexploitation tout une liste de rgions reprsentes
par un couple de lettres. Par exemple, le franais est reprsent par les lettres fr. Et mme
plus prcisment, le franais de France est reprsent par fr-FR alors que celui du Canada
est reprsent par fr-CA.
Cest ce que lon retrouve dans les paramtres de notre systme dexploitation lorsquon
va (sous Windows 7) dans le panneau de configuration, Horloge langue et rgion :

Puis Modifier le format de la date, de lheure ou des nombres .

Nous pouvons adapter le format celui que nous prfrons :

Bref, ce choix, nous le retrouvons dans notre application si nous rcuprons la culture
courante :
Console.WriteLine(System.Threading.Thread.CurrentThread.CurrentCulture);

Avec ceci, nous aurons :


fr-FR

Ce qui est intressant, cest que le formatage des chiffres ou des dates change en fonction
de la culture. Ainsi, si nous utilisons le franais de France, pour le code suivant :
public static void Main(string[] args)
{
Affiche();
}
public static void Affiche()
{
Console.WriteLine(System.Threading.Thread.CurrentThread.CurrentCulture);
decimal dec = 5.5M;
double dou = 4.8;
DateTime date = new DateTime(2011, 12, 25);
Console.WriteLine("Dcimal : {0}", dec);
Console.WriteLine("Double : {0}", dou);
Console.WriteLine("Date : {0}", date);
}

nous aurons :
fr-FR
Dcimal : 5,5

Double : 4,8
Date : 25/12/2011 00:00:00

Il est possible de changer la culture courante de notre application, si par exemple nous
souhaitons quelle soit multiculture, en utilisant le code suivant :
System.Threading.Thread.CurrentThread.CurrentCulture = new CultureInfo("en-US");

Ici par exemple, jindique mon application que je souhaite travailler avec la culture des
tats-Unis. Ainsi, le code prcdent, en changeant juste la culture :
public static void Main(string[] args)
{
System.Threading.Thread.CurrentThread.CurrentCulture = new CultureInfo("en-US");
Affiche();
}
public static void Affiche()
{
Console.WriteLine(System.Threading.Thread.CurrentThread.CurrentCulture);
decimal dec = 5.5M;
double dou = 4.8;
DateTime date = new DateTime(2011, 12, 25);
Console.WriteLine("Dcimal : {0}", dec);
Console.WriteLine("Double : {0}", dou);
Console.WriteLine("Date : {0}", date);
}

produira un rsultat lgrement diffrent :


en-US
Dcimal : 5.5
Double : 4.8
Date : 12/25/2011 12:00:00 AM

Il sagit exactement de la mme information, mais formate diffremment.


noter quil existe encore une autre information de culture, savoir la langue du systme
dexploitation que lon retrouve dans la culture de linterface graphique, dans la proprit :
Console.WriteLine(System.Threading.Thread.CurrentThread.CurrentUICulture);

Il sagit ici de la CurrentUICulture, ne pas confondre avec la CurrentCulture.


Correspondant la langue du systme dexploitation, nous nous en servirons plus
volontiers pour rendre une application multi-langue.
Pour finir sur le formatage des chaines, il est possible dutiliser des caractres spciaux
enrichis pour changer le formatage des types numriques ou de la date.
Par exemple, il est possible dafficher la date sous une forme diffrente avec :
DateTime date = new DateTime(2011, 12, 25);
Console.WriteLine("La date est {0}", date.ToString("dd-MM-yyyy"));

Ici, dd sert afficher le jour, MM le mois et yyyy lanne sur quatre chiffre, ce qui donne :
La date est 25-12-2011

Il y a beaucoup doptions diffrentes que vous pouvez retrouver dans la documentation.


Cela fonctionne dans le mme genre pour les numriques, qui nous permettent par
exemple dafficher le nombre de diffrentes faons. Ici par exemple, jutilise la notation
scientifique :
double valeur = 123.45;
Console.WriteLine("Format scientifique : {0:e}", valeur);

Ce qui donne :
Format scientifique : 1,234500e+002

Pour les nombres, il existe diffrents formatages que lon peut retrouver dans le tableau cidessous :
Format

Description

Remarque

{0:c}

Monnaie

Attention, la console affiche mal le caractre

{0:d}

Dcimal

Ne fonctionne quavec les types sans virgules

{0:e}

Notation scientifique

{0:f}

Virgule flottante

{0:g}

Gnral

{0:n}

Nombre avec formatage


pour les milliers

{0:r}

Aller-retour

Permet de garantir que la valeur numrique convertie en chaine pourra tre


convertie nouveau en valeur numrique identique

{0:x}

Hxadcimal

Ne fonctionne quavec les types sans virgules

Valeur par dfaut

noter que lon peut galement avoir du formatage grce la mthode ToString(). Il
suffit de passer le format en paramtres, comme ici o cela me permet dafficher
uniquement le nombre de chiffres aprs la virgule qui mintressent ou des chiffres
significatifs :
double valeur = 10.0 / 3;
Console.WriteLine("Avec 3 chiffres significatifs et 3 chiffres aprs la virgule : {0}",
valeur.ToString("000.###"));

Qui donne :
Avec 3 chiffres significatifs et 3 chiffres aprs la virgule : 003,333

Mettre le format dans la mthode ToString() fonctionne aussi pour la date :


DateTime date = new DateTime(2011, 12, 25);
Console.WriteLine(date.ToString("MMMM"));

Ici, je naffiche que le nom du mois :


dcembre

Voil pour le formatage des chaines de caractres.


Lister tous les formatages possibles serait une tche bien ennuyeuse. Vous aurez
loccasion de rencontrer dautres formatages qui permettent de faire un peu tout et
nimporte quoi. Vous pourrez retrouver la plupart des formatages partir de ce lien.

Les attributs
Dans la liste des choses qui vont vous servir, on peut rajouter les attributs.
Ils sont utiliss pour fournir des informations complmentaires sur une mthode, une
classe, variables, etc. Ils vont nous servir rgulirement dans le framework .NET et nous
aurons mme la possibilit de crer nos propres attributs.
Un attribut est dclar entre crochets avec le nom de sa classe. Il peut se mettre au-dessus
dune classe, dune mthode, dune proprit, etc.
Par exemple, dans le code ci-dessous, je positionne lattribut Obsolete au-dessus de la

dclaration dune mthode :


[Obsolete("Utilisez plutt la mthode ToString() pour avoir une reprsentation de l'objet")]
public void Affiche()
{
// ...
}

Ici, jutilise un attribut existant du framework .NET, lattribut Obsolete qui permet
dindiquer quune mthode est obsolte et quil ne vaudrait mieux pas lutiliser. En
gnral, il est possible de fournir une information permettant de dire pourquoi la mthode
est obsolte et par quoi il faut la remplacer. Ainsi, si nous tentons dutiliser cette mthode :
public class Program
{
static void Main(string[] args)
{
new Program().Affiche();
}
[Obsolete("Utilisez plutt la mthode ToString() pour avoir une reprsentation de l'objet")]
public void Affiche()
{
// ...
}
}

Le compilateur nous affichera lavertissement suivant :


'MaPremiereApplication.Program.Affiche()' est obsolte : 'Utilisez plutt la mthode ToString() pour
avoir une reprsentation de l'objet'

Cet attribut est un exemple, il en existe beaucoup dans le framework .NET et vous aurez
loccasion den voir dautres dans les chapitres suivants.
Remarquons que la classe sappelle ObsoleteAttribute et peut sutiliser soit suffixe par
Attribute, soit sans, et dans ce cas, ce suffixe est ajout automatiquement.
Grce lhritage, nous allons nous aussi pouvoir dfinir nos propres attributs. Il suffit
pour cela de driver de la classe de base Attribute. Par exemple, dans sa forme la plus
simple :
public class DescriptionClasseAttribute : Attribute
{
}

Nous pourrons alors utiliser notre attribut sur une classe :


[DescriptionClasse]
public class Chien
{
}

Super, mme si a ne nous sert encore rien .


Problme, jai appel mon attribut DescriptionClasse et je peux quand mme lutiliser sur
une mthode :
[DescriptionClasse]
public class Chien
{
[DescriptionClasse]
public void Aboyer()
{
}
}

Heureusement, il est possible dindiquer des restrictions sur les attributs en indiquant par
exemple quil nest valable que pour les classes. Cela se fait avec un attribut :

[AttributeUsage(AttributeTargets.Class, AllowMultiple = true)]


public class DescriptionClasseAttribute : Attribute
{
}

Ici par exemple, jindique que mon attribut nest valable que sur les classes et quil est
possible de le mettre plusieurs fois sur la classe. Le code prcdent o lattribut est
positionn sur une mthode ne compilera donc plus, avec lerreur de compilation suivante
:
L'attribut 'DescriptionClasse' n'est pas valide dans ce type de dclaration. Il n'est valide que dans
les dclarations 'class'.

Ce qui nous convient parfaitement.


Il existe dautres attributs qui permettent par exemple de nautoriser des
attributs que sur les mthodes.
Il est intressant de pouvoir fournir des informations complmentaires notre attribut.
Pour cela, on peut rajouter des proprits, ou avoir une surcharge complmentaire du
constructeur, comme on la fait pour le message de lattribut Obsolete :
[AttributeUsage(AttributeTargets.Class, AllowMultiple = true)]
public class DescriptionClasseAttribute : Attribute
{
public string Description { get; set; }
public DescriptionClasseAttribute()
{
}
public DescriptionClasseAttribute(string description)
{
Description = description;
}
}

Ce qui nous permettra dutiliser notre attribut de ces deux faons :


[DescriptionClasse(Description = "Cette classe correspond un chien")]
[DescriptionClasse("Elle drive de la classe Animal")]
public class Chien : Animal
{
}

Cest trs bien ces attributs, mais nous ne sommes pas encore capables de les exploiter.
Voyons comment le faire.

La rflexion
La rflexion en C# ne donne pas mal la tte, quoique
Cest une faon de faire de lintrospection sur nos types de manire obtenir des
informations sur eux, cest--dire sur les mthodes, les proprits, etc. La rflexion permet
galement de charger dynamiquement des types et de faire de lintrospection sur les
assemblys.
Cest un mcanisme assez complexe et plutt couteux en termes de temps. Le point de
dpart est la classe Type qui permet de dcrire nimporte quel type. Le type dune classe
peut tre obtenu grce au mot-cl typeof :

Type type = typeof(string);

Cet objet Type nous permet dobtenir plein dinformations sur nos classes au moment de
lexcution. Par exemple si le type est une classe, cest--dire ni un type valeur, ni une
interface, alors le code suivant :
Type type = typeof(string);
Console.WriteLine(type.IsClass);

nous renverra true. En effet, la proprit IsClass permet dindiquer si le type est une
classe.
Nous pouvons aussi obtenir le nom de mthodes grce la mthode GetMethods() :
Type type = typeof(string);
foreach (MethodInfo infos in type.GetMethods())
{
Console.WriteLine(infos.Name);
}

Qui nous donne :

Pas trs exploitable comme rsultat, mais bon cest pour lexemple. Il y a encore pleins
dinfos dans cet objet Type, qui permettent dobtenir ce que nous voulons.
Cela est possible grce aux mtadonnes qui sont des informations de descriptions des
types.
Vous me voyez venir eh oui, nous allons pouvoir obtenir nos attributs !
Pour connaitre la liste des attributs dun type, on peut utiliser la mthode statique
Attribute.GetCustomAttributes(), en lui passant en paramtre le System.Type qui est
hrit de la classe abstraite MemberInfo. Si lon souhaite rcuprer un attribut particulier,
on peut le passer en paramtre de la mthode :
Attribute[] lesAttributs = Attribute.GetCustomAttributes(type, typeof(DescriptionClasseAttribute));

Ceci va nous permettre de rcuprer les attributs de notre classe et en loccurrence,


dobtenir leurs description. Cest ce que permet le code suivant :
public class Program
{
static void Main(string[] args)
{
new DemoAttributs().Demo();

}
}
public class DemoAttributs
{
public void Demo()
{
Animal animal = new Animal();
Chien chien = new Chien();
VoirDescription(animal);
VoirDescription(chien);
}
public void VoirDescription<T>(T obj)
{
Type type = typeof(T);
if (!type.IsClass)
return;
Attribute[] lesAttributs = Attribute.GetCustomAttributes(type,
typeof(DescriptionClasseAttribute));
if (lesAttributs.Length == 0)
Console.WriteLine("Pas de description pour la classe " + type.Name + "\n");
else
{
Console.WriteLine("Description pour la classe " + type.Name);
foreach (DescriptionClasseAttribute attribut in lesAttributs)
{
Console.WriteLine("\t" + attribut.Description);
}
}
}
}

Nous commenons par instancier un objet animal et un objet chien. Puis nous demandons
leurs descriptions.
La mthode gnrique VoirDescription() soccupe dans un premier temps dobtenir le
type de la classe grce typeof. On soctroie une vrification permettant de nous assurer
que le type est bien une classe. Ceci permet dviter daller plus loin car de toute faon,
notre attribut ne sapplique quaux classes. Puis nous utilisons la mthode
GetCustomAttributes pour obtenir les attributs de type DescriptionClasseAttribute. Et
sil y en a, alors on les affiche.
Ce qui donne :
Pas de description pour la classe Animal
Description pour la classe Chien
Elle drive de la classe Animal
Cette classe correspond un chien

Voil pour les attributs, vous aurez loccasion de rencontrer des attributs du framework
.NET un peu plus loin dans ce tutoriel, mais globalement, il est assez rare davoir crer
soi-mme ses attributs.
En ce qui concerne la rflexion, il faut bien comprendre que le sujet est beaucoup plus
vaste que a. Nous avons cependant vu ici un aperu de ce que permet de faire la
rflexion, en illustrant le mcanisme dintrospection sur les attributs.
En rsum

Il est possible dempcher une classe dtre drive grce au mot-cl sealed.
Le garbage collector est le mcanisme permettant de librer la mmoire alloue sur le
tas manag qui nest plus rfrence dans une application.

Le mot-cl yield permet de crer des numrateurs facilement et participe au


mcanisme dexcution diffre.
Le framework .NET gre les diffrents formats des chanes grce la culture de
lapplication.
La rflexion est un mcanisme dintrospection sur les types permettant dobtenir des
informations sur ces derniers lors de lexcution.

Chapitre 35

La configuration dune application

Dans le cycle de vie dune application, il est frquent que de petites choses changent.
Imaginons que mon application doive se connecter un serveur FTP pour sauvegarder les
donnes sur lesquelles jai travaill. Pendant mes tests, effecuts en local, mon serveur
FTP pourrait avoir ladresse ftp://localhost avec un login et un mot de passe test.
videmment, lors de lutilisation relle de mon application, ladresse changera, et le login
et le mot de passe varieront en fonction de lutilisateur.
Il faut tre capable de changer facilement ces paramtres sans avoir modifier le code ni
recompiler lapplication.
Cest l quinterviennent les fichiers de configuration : ils permettent de stocker toutes ces
petites choses qui servent faire varier notre application. Pour les clients lourds, comme
nos applications console, il sagit du fichier app.config : un fichier XML qui contient la
configuration de notre application. Il stocke des chanes de connexion une base de
donnes, une url de service web, des prfrences utilisateurs, etc. Bref, tout ce qui va
permettre de configurer notre application !

Rappel sur les fichiers XML


Vous ne connaissez pas les fichiers XML ? Si vous voulez en savoir plus, nhsitez pas
faire un petit tour sur internet, cest un format trs utilis dans linformatique.
Pour faire court, le fichier XML est un langage de balise, un peu comme le HTML, o lon
dcrit de linformation. Les balises sont des valeurs entoures de < et > qui dcrivent la
smantique de la donne. Par exemple :
<prenom>Nicolas</prenom>

La balise <prenom> est ce quon appelle une balise ouvrante, cela signifie que ce qui se
trouve aprs (en loccurrence la chaine Nicolas ) fait partie de cette balise jusqu ce
que lon rencontre la balise fermante </prenom> qui est comme la balise ouvrante
lexception du / prcdant le nom de la balise.
Le XML est un fichier facile lire par nous autres humains. On en dduit assez facilement
que le fichier contient la chaine Nicolas et quil sagit smantiquement dun prnom.
Une balise peut contenir des attributs permettant de donner des informations sur la
donne. Les attributs sont entours de guillemets et et font partis de la balise. Par
exemple :

<client nom="Nicolas" age="30"></client>

Ici, la balise client possde un attribut nom ayant la valeur Nicolas et un attribut
age ayant la valeur 30 . Encore une fois, cest trs facile lire pour un humain.
Il est possible que la balise nait pas de valeur, comme cest le cas dans lexemple cidessus. On peut dans ce cas-l remplacer la balise ouvrante et la balise fermante par cet
quivalent :
<client nom="Nicolas" age="30"/>

Enfin, et nous allons terminer notre aperu rapide du XML avec un dernier point. Il est
important de noter que le XML peut imbriquer ses balises et quil ne peut possder quun
seul lment racine, ce qui nous permet davoir une hirarchie de donnes. Par exemple
nous pourrons avoir :
<listesDesClient>
<client type="Particulier">
<nom>Nicolas</nom>
<age>30</age>
</client>
<client type="Professionel">
<nom>Jrmie</nom>
<age>40</age>
</client>
</listesDesClient>

On voit tout de suite que le fichier dcrit une liste de deux clients. Nous en avons un qui
est un particulier, qui sappelle Nicolas et qui a 30 ans alors que lautre est un
professionnel, prnomm Jrmie et qui a 40 ans.

Crer le fichier de configuration


Pourquoi utiliser un fichier de configuration ?
pour viter de mettre des valeurs en dur dans le code. Imaginons que nous utilisions
une url de service web dans notre application, si lurl change, on aimerait ne pas
avoir recompiler le code.
pour viter dutiliser la base de registre comme cela a beaucoup t fait dans les
premires versions de Windows et de donner les droits de modification de base de
registre.
Ces fichiers permettent davoir des informations de configuration, potentiellement types.
De plus, le framework .NET dispose de mthodes pour y accder facilement.
Lintrt dutiliser un fichier XML plutt quun fichier binaire est que ce fichier est lisible
et comprhensible facilement. On peut galement le modifier la main sans un systme
volu permettant de faire des modifications.
Voil plein de raisons pour lesquelles on va utiliser ces fichiers.
Pour ajouter un fichier de configuration : faisons un clic droit sur le projet Ajouter >
Nouvel lment

Et choisissons le modle de fichier Fichier de configuration de lapplication :

Gardez lui son nom et validez sa cration.


Ce fichier est presque vide :
<?xml version="1.0" encoding="utf-8" ?>
<configuration>
</configuration>

Il possde sur sa premire ligne un marqueur permettant didentifier le fichier comme


tant un fichier XML et une balise ouvrante configuration, suivie de sa balise fermante.
Cest la structure de base du fichier de configuration. Nous mettrons nos sections de
configuration lintrieur de cette balise.

Nous retrouvons notre fichier de configuration dans notre projet et nous pouvons le voir
dans lexplorateur de documents :

Compilons notre application et rendons-nous dans le rpertoire o le fichier excutable est


gnr. Nous y trouvons galement un fichier qui porte le mme nom que notre excutable
et dont lextension est .exe.config. Cest bien notre fichier de configuration. Visual C#
Express le dploie au mme endroit que notre application et lui donne un nom qui va lui
permettre dtre exploit par notre application. Pour tre utilisables, ces fichiers doivent se
situer dans le mme rpertoire que lexcutable.

Lecture simple dans la section de configuration prdfinie


: AppSettings
Avoir un fichier de configuration, cest bien. Mais il faut savoir lire lintrieur si nous
souhaitons pouvoir lexploiter.
Il existe plusieurs faons dindiquer des valeurs de configuration, nous les dcouvrirons
tout au long de ce chapitre.
La plus simple est dutiliser la section AppSettings . Cest une section o on peut
mettre, comme son nom le suggre aux anglophones, les proprits de lapplication. Pour
cela, on utilisera un systme de cl / valeur pour stocker les informations.
Par exemple, modifiez le fichier de configuration pour avoir :
<configuration>
<appSettings>
<add key="prenom" value="nicolas"/>
<add key="age" value="30"/>
</appSettings>
</configuration>

En voyant ce fichier de configuration, ltre humain comprend assez facilement que nous
allons avoir deux paramtres de configuration. Un premier qui est le prnom et qui va
valoir Nicolas. Un deuxime qui est lge et qui sera de 30.
Le savoir en tant qutre humain, cest bien. Pouvoir y accder dans notre programme
informatique, cest encore mieux.
Voyons comment faire.
La premire chose faire est de rfrencer, si ce nest dj fait, lassembly qui contient
toutes les classes permettant de grer la configuration. Elle sappelle (sans surprise)
System.Configuration :

Pour accder au fichier de configuration, nous allons utiliser la classe statique


ConfigurationManager. Pour accder aux informations contenues dans la section
AppSettings, nous utiliserons sa proprit AppSettings. Et nous pourrons accder aux
lments de la configuration en utilisant loprateur dindexation : [].
Ce qui donne :
string prenom = ConfigurationManager.AppSettings["prenom"];
string age = ConfigurationManager.AppSettings["age"];
Console.WriteLine("Prnom : " + prenom + " / Age : " + age);

On peut galement utiliser des index numriques pour y accder, mais je trouve que cela
manque vraiment de clart :
string prenom = ConfigurationManager.AppSettings[0];
string age = ConfigurationManager.AppSettings[1];
Console.WriteLine("Prnom : " + prenom + " / Age : " + age);

De plus, cela oblige connatre lordre dans lequel les cls ont t mises dans le fichier.
Il est possible aussi de boucler sur toutes les valeurs contenues dans la section :
foreach (string cle in ConfigurationManager.AppSettings)
{
Console.WriteLine("Cl : " + cle + " / Valeur : " + ConfigurationManager.AppSettings[cle]);
}

La proprit AppSettings est en fait du type NameValueCollection. Il sagit dune


collection dlments de type chaine de caractres qui sont accessibles partir dune cl
de type chaine de caractres galement. Une valeur est donc associe une cl.
noter que la casse de la cl nest pas importante pour accder la valeur associe la
cl. Le code suivant :
string prenom = ConfigurationManager.AppSettings["PRENOM"];

renverra bien notre valeur de configuration.


Cependant, si la valeur nexiste pas, nous obtiendrons la valeur null dans la chaine de

caractres.
Il est important de remarquer que nous rcuprons des chaines et que nous aurons besoin
potentiellement de faire une conversion pour manipuler lge en tant quentier.
Rien ne vous empche dcrire une petite mthode dextension maintenant que vous savez
faire :
public static class ConfigurationManagerExtensions
{
public static int ObtenirValeurEntiere(this NameValueCollection appSettings, string cle)
{
string valeur = appSettings[cle];
return Convert.ToInt32(valeur);
}
}

Que nous pourrons utiliser avec :


int age = ConfigurationManager.AppSettings.ObtenirValeurEntiere("age");

Lecture des chaines de connexion la base de donnes


Les chaines de connexion reprsentent un type de configuration particulier. Elles vont
servir pour les applications ayant besoin de se connecter une base de donnes. On va y
stocker tout ce dont on a besoin, comme le nom du serveur ou les identifiants pour sy
connecter
Nous y reviendrons plus en dtail plus tard, mais regardons la configuration suivante :
<configuration>
<connectionStrings>
<add name="MaConnection" providerName="System.Data.SqlClient"
connectionString="Data Source=.\SQLEXPRESS; Initial Catalog=Base1; Integrated Security=true"/>
<add name="MaConnection2" providerName="System.Data.SqlClient"
connectionString="Data Source=.\SQLEXPRESS; Initial Catalog=Base2; Integrated Security=true"/>
</connectionStrings>
</configuration>

Nous dfinissons ici deux chaines de connexion qui permettent de se connecter en


authentification Windows (Integrated Security=true) sur un serveur hberg en local
(.\SQLEXPRESS) dont le nom est SQLEXPRESS, qui utilisent SQL SERVER
(providerName=System.Data.SqlClient), qui pointent sur les bases de donnes Base1 et
Base2, identifies chacune par les noms MaConnection et MaConnection2.
Pour obtenir les informations de configuration individuellement, il faudra utiliser la
proprit ConnectionStrings de la classe ConfigurationManager en y accdant par leurs
noms :
ConnectionStringSettings chaineConnexion = ConfigurationManager.ConnectionStrings["MaConnection"];
Console.WriteLine(chaineConnexion.Name);
Console.WriteLine(chaineConnexion.ConnectionString);
Console.WriteLine(chaineConnexion.ProviderName);

Nous pourrons toutes les obtenir avec une boucle foreach :


foreach (ConnectionStringSettings valeur in ConfigurationManager.ConnectionStrings)
{
Console.WriteLine(valeur.ConnectionString);
}

Nous utiliserons les chaines de connexion dans le chapitre sur laccs aux donnes.

Crer sa propre section de configuration partir dun


type prdfini
Il est possible de crer sa propre section de configuration partir dun type prdfini. Par
exemple pour crer une section du mme genre que la section appSettings, qui utilise une
paire cl/valeur, on peut utiliser un DictionarySectionHandler. Il existe plusieurs types
prdfinis, que nous allons tudier ci-dessous.
DictionarySectionhandler
DictionarySectionhandler est une classe qui fournit les informations de configuration des

paires cl / valeur dune section de configuration.


Oui mais si tu nous dis que cest un systme de cl / valeur, cest comme pour
les AppSettings que nous avons vus. Pourquoi utiliser une section particulire
DictionarySectionhandler alors ?
Le but de pouvoir faire des sections particulires est dorganiser smantiquement son
fichier de configuration, pour dcouper logiquement son fichier au lieu davoir tout dans
la mme section.
Regardons cette configuration :
<configuration>
<configSections>
<section name="InformationsUtilisateur" type="System.Configuration.DictionarySectionHandler" />
</configSections>
<InformationsUtilisateur>
<add key="login" value="nico" />
<add key="motdepasse" value="12345" />
<add key="age" value="30" />
</InformationsUtilisateur>
</configuration>

La premire chose voir est que nous indiquons notre application que nous dfinissons
une section de configuration du type DictionarySectionHandler et qui va sappeler
InformationsUtilisateur.
Cela permet ensuite de dfinir notre propre section InformationsUtilisateur, qui
ressemble beaucoup la section AppSettings sauf quici, on se rend tout de suite compte
quil sagit dinformations utilisateur.
Pour accder notre section depuis notre programme, nous devrons utiliser le code
suivant :
Hashtable section = (Hashtable)ConfigurationManager.GetSection("InformationsUtilisateur");

On utilise la mthode GetSection de la classe ConfigurationManager pour obtenir la


section dont nous passons le nom en paramtre. On reoit en retour une table de hachage
(HashTable) et nous pourrons lutiliser de cette faon pour obtenir les valeurs de nos cls :
Console.WriteLine(section["login"]);
Console.WriteLine(section["MOTDEPASSE"]);
Console.WriteLine(section["age"]);

Nous pouvons boucler sur les valeurs de la section en utilisant le code suivant :

foreach (DictionaryEntry d in section)


{
Console.WriteLine("Cl : " + d.Key + " / Valeur : " + d.Value);
}

NameValueSectionHandler

La section de type NameValueSectionHandler ressemble beaucoup la section prcdente.


Observons la configuration suivante :
<configuration>
<configSections>
<section name="InformationsUtilisateur" type="System.Configuration.NameValueSectionHandler" />
</configSections>
<InformationsUtilisateur>
<add key="login" value="nico" />
<add key="motdepasse" value="12345" />
<add key="age" value="30" />
</InformationsUtilisateur>
</configuration>

Cest la mme que juste prcdemment, lexception du type de la section, qui cette foisci est NameValueSectionHandler. Ce qui implique que nous obtenons un type diffrent en
retour de lappel la mthode GetSection, savoir un NameValueCollection :
NameValueCollection section =
(NameValueCollection)ConfigurationManager.GetSection("InformationsUtilisateur");

La rcupration des informations de configuration se fait de la mme faon :


Console.WriteLine(section["login"]);
Console.WriteLine(section["MOTDEPASSE"]);
Console.WriteLine(section["age"]);

Ou encore avec une boucle pour toutes les rcuprer :


foreach (string cle in section)
{
Console.WriteLine("Cl : " + cle + " / Valeur : " + section[cle]);
}

vous de voir lequel des deux vous prfrez, mais dans tous les cas, il faudra fonctionner
avec un systme de cl valeur.
SingleTagSectionHandler

Ce troisime type permet de grer une section diffrente des deux prcdentes. Il sera
possible davoir autant dattributs que lon souhaite dans la section. Prenez par exemple
cet exemple de configuration :
<configuration>
<configSections>
<section name="MonUtilisateur" type="System.Configuration.SingleTagSectionHandler" />
</configSections>
<MonUtilisateur prenom="Nico" age="30" adresse="9 rue des bois"/>
</configuration>

Nous voyons que je peux mettre autant dattributs que je le souhaite. Par contre, il ne sera
possible de faire apparaitre la section MonUtilisateur quune seule fois, alors que dans
les sections prcdentes, nous avions une liste de cl / valeur.
Nous pourrons rcuprer notre configuration avec le code suivant :
Hashtable section = (Hashtable)ConfigurationManager.GetSection("MonUtilisateur");
Console.WriteLine(section["prenom"]);

Console.WriteLine(section["age"]);
Console.WriteLine(section["adresse"]);

Attention par contre, cette fois-ci la casse est importante pour obtenir la valeur de notre
attribut.
Notons notre boucle habituelle permettant de retrouver tous les attributs de notre section :
foreach (DictionaryEntry d in section)
{
Console.WriteLine("Attribut : " + d.Key + " / Valeur : " + d.Value);
}

Voil pour les sections utilisant un type prdfini.

Les groupes de sections


Super, nous savons dfinir des sections de configuration. Elles nous permettent
dorganiser un peu mieux notre fichier de configuration. Par contre, si les sections se
multiplient, cela va nouveau tre le bazar.
Heureusement, les groupes de sections sont l pour remettre de lordre.
Comme son nom lindique, un groupe de section va permettre de regrouper plusieurs
sections. Le but est de clarifier le fichier de configuration.
Regardons lexemple suivant :
<configuration>
<configSections>
<sectionGroup name="Utilisateur">
<section name="ParametreConnexion" type="System.Configuration.SingleTagSectionHandler" />
<section name="InfoPersos" type="System.Configuration.DictionarySectionHandler" />
</sectionGroup>
</configSections>
<Utilisateur>
<ParametreConnexion Login="Nico" MotDePasse="12345" Mode="Authentification Locale"/>
<InfoPersos>
<add key="prenom" value="Nicolas" />
<add key="age" value="30" />
</InfoPersos>
</Utilisateur>
</configuration>

Nous voyons ici que jai dfini un groupe qui sappelle Utilisateur, en utilisant la balise
sectionGroup, contenant deux sections de configuration.
Remarquons plus bas le contenu des sections et nous remarquons que la balise
<Utilisateur> contient nos sections de configuration comme prcdemment.
Pour obtenir nos valeurs de configuration, la seule chose qui change est la faon de
charger la section. Ici, nous mettons le nom de la section prcde du nom du groupe. Ce
qui donne :
Hashtable section1 = (Hashtable)ConfigurationManager.GetSection("Utilisateur/ParametreConnexion");
Hashtable section2 = (Hashtable)ConfigurationManager.GetSection("Utilisateur/InfoPersos");

Aprs, la faon de rcuprer les valeurs de configuration de chaque section reste la mme.
Avouez que cest quand mme plus clair non ?

Crer une section de configuration personnalise

Nous allons tudier rapidement comment crer des sections de configuration


personnalises. Pour cela, il faut crer une section en drivant de la classe
ConfigurationSection.
La classe ConfigurationSection permet de reprsenter une section dun fichier de
configuration. Donc, en toute logique, nous pouvons enrichir cette classe avec nos
proprits. Il suffit pour cela de dcorer nos propres proprits avec lattribut
ConfigurationProperty. Ce qui donne :
public class PersonneSection : ConfigurationSection
{
[ConfigurationProperty("age", IsRequired = true)]
public int Age
{
get { return (int)this["age"]; }
set { this["age"] = value; }
}
[ConfigurationProperty("prenom", IsRequired = true)]
public string Prenom
{
get { return (string)this["prenom"]; }
set { this["prenom"] = value; }
}
}

Le grand intrt ici est de pouvoir typer les proprits. Ainsi, nous pouvons avoir une
section de configuration qui travaille avec un entier par exemple. Tout est fait par la classe
mre ici et il suffit dutiliser ses proprits indexes en y accdant par son nom.
Pour que notre section personnalise soit reconnue, il faut la dclarer avec notre nouveau
type :
<configSections>
<section name="PersonneSection" type="MaPremiereApplication.PersonneSection, MaPremiereApplication"
/>
</configSections>

Le nom du type est constitu du nom complet du type (espace de nom + nom de la classe)
suivi dune virgule et du nom de lassembly.
Ici, lespace de nom est le mme que lassembly car jai cr mes classes la racine du
projet. Si vous avez un doute, vous devez vrifier lespace de nom dans lequel est dclare
la classe.
Ensuite, nous pourrons dfinir notre section, ce qui donne au final :
<configuration>
<configSections>
<section name="PersonneSection" type=" MaPremiereApplication.PersonneSection,
MaPremiereApplication " />
</configSections>
<PersonneSection prenom="nico" age="30"/>
</configuration>

Pour accder aux informations contenues dans la section, il faudra charger la section
comme dhabitude :
PersonneSection section = (PersonneSection)ConfigurationManager.GetSection("PersonneSection");
Console.WriteLine(section.Prenom + " a " + section.Age + " ans");

Ce qui est intressant de remarquer ici, cest quon accde directement nos proprits via
notre section personnalise. Ce qui est une grande force et permet de travailler avec un
entier et une chaine de caractres ici.
Il faudra faire attention deux choses ici.

La premire est la casse. Comme on la vu dans le code, le nom est crit en minuscule. Il
faudra tre cohrent entre le nom indiqu dans lattribut ConfigurationProperty, celui
indiqu en paramtre de loprateur dindexation et celui crit dans le fichier de
configuration. Tout doit tre orthographi de la mme faon, sinon nous aurons une
exception du genre :

De mme, si nous ne saisissons pas une valeur entire dans lattribut age, il va y avoir un
problme de conversion :

Crer une section personnalise avec une collection


Dans le paragraphe du dessus, on constate quon ne peut dfinir quun lment dans notre
section. Il pourrait tre intressant dans certains cas davoir une section personnalise qui
puisse contenir plusieurs lments, par exemple pour avoir une liste de personnes.
Pour ce faire, on utilisera la classe ConfigurationPropertyCollection.

La premire chose est de crer un lment en drivant de la classe ConfigurationElement.


Cet lment va ressembler beaucoup ce quon a fait pour crer une section personnalise
:
public class ClientElement : ConfigurationElement
{
private static readonly ConfigurationPropertyCollection _proprietes;
private static readonly ConfigurationProperty age;
private static readonly ConfigurationProperty prenom;
static ClientElement()
{
prenom = new ConfigurationProperty("prenom", typeof(string), null,
ConfigurationPropertyOptions.IsKey);
age = new ConfigurationProperty("age", typeof(int), null,
ConfigurationPropertyOptions.IsRequired);
_proprietes = new ConfigurationPropertyCollection { prenom, age };
}
public string Prenom
{
get { return (string)this["prenom"]; }
set { this["prenom"] = value; }
}
public int Age
{
get { return (int)this["age"]; }
set { this["age"] = value; }
}

protected override ConfigurationPropertyCollection Properties
{
get { return _proprietes; }
}
}

Ici, je dfinis deux proprits, Prenom et Age, qui me permettent bien sr dy stocker un
prnom et un ge qui sont respectivement une chaine de caractres et un entier. noter
que nous avons besoin de dcrire ces proprits dans le constructeur statique de la classe.
Pour cela, il faut lui indiquer son nom, cest--dire la chaine qui sera utilise comme
attribut dans llment de la section de configuration. Puis nous lui indiquons son type,
pour cela on utilise le mot-cl typeof qui permet justement de renvoyer le type (dans le
sens objet Type) dun type. Enfin nous lui indiquons une option, par exemple le prnom
sera la cl de mon lment (qui est une valeur unique et obligatoire saisir) et lge, qui
sera un lment obligatoire saisir galement.
Ensuite, nous avons besoin dutiliser cette classe travers une collection dlments. Pour
ce faire, il faut crer une classe qui drive de ConfigurationElementCollection :
public class ClientElementCollection : ConfigurationElementCollection
{
public override ConfigurationElementCollectionType CollectionType
{
get { return ConfigurationElementCollectionType.BasicMap; }
}
protected override string ElementName
{
get { return "Client"; }
}
protected override ConfigurationPropertyCollection Properties
{
get { return new ConfigurationPropertyCollection(); }
}
public ClientElement this[int index]
{

get { return (ClientElement)BaseGet(index); }


set
{
if (BaseGet(index) != null)
{
BaseRemoveAt(index);
}
BaseAdd(index, value);
}
}
public new ClientElement this[string nom]
{
get { return (ClientElement)BaseGet(nom); }
}
public void Add(ClientElement item)
{
BaseAdd(item);
}
public void Remove(ClientElement item)
{
BaseRemove(item);
}
public void RemoveAt(int index)
{
BaseRemoveAt(index);
}
public void Clear()
{
BaseClear();
}
protected override ConfigurationElement CreateNewElement()
{
return new ClientElement();
}
protected override object GetElementKey(ConfigurationElement element)
{
return ((ClientElement)element).Prenom;
}
}

Ces classes ont toujours la mme structure. Ce qui est important de voir est quon utilise
lintrieur la classe ClientElement pour indiquer le type de la collection. Nous indiquons
galement le nom de la balise qui sera utilise dans le fichier de configuration, cest la
chaine Client que renvoie la proprit ElementName. Enfin, jai la possibilit de dfinir
ma cl en substituant la mthode GetElementKey. Le reste des mthodes appellent les
mthodes de la classe mre.
Enfin, il faut crer notre section personnalise, qui drive comme dhabitude de
ConfigurationSection :
public class ListeClientSection : ConfigurationSection
{
private static readonly ConfigurationPropertyCollection proprietes;
private static readonly ConfigurationProperty liste;
static ListeClientSection()
{
liste = new ConfigurationProperty(string.Empty, typeof(ClientElementCollection), null,
ConfigurationPropertyOptions.IsRequired | ConfigurationPropertyOptions.IsDefaultCollection);
proprietes = new ConfigurationPropertyCollection { liste };
}
public ClientElementCollection Listes
{
get { return (ClientElementCollection)base[liste]; }

}
public new ClientElement this[string nom]
{
get { return Listes[nom]; }
}
protected override ConfigurationPropertyCollection Properties
{
get { return proprietes; }
}
}

Notons dans cette classe comment nous utilisons loprateur dindexation pour renvoyer
un lment partir de sa cl et renvoyer la liste des lments.
Maintenant, nous pouvons crire notre configuration :
<configuration>
<configSections>
<section name="ListeClientSection" type="MaPremiereApplication.ListeClientSection,
MaPremiereApplication" />
</configSections>
<ListeClientSection>
<Client prenom="Nicolas" age="30"/>
<Client prenom="Jrmie" age="20"/>
</ListeClientSection>
</configuration>

Nous avons besoin nouveau de dfinir la section en indiquant son type. Puis nous
pouvons crer notre section et positionner notre liste de clients.
Pour accder cette section, nous pouvons charger notre section comme avant avec la
mthode GetSection :
ListeClientSection section =
(ListeClientSection)ConfigurationManager.GetSection("ListeClientSection");

Puis nous pouvons soit itrer sur les lments de notre section :
foreach (ClientElement clientElement in section.Listes)
{
Console.WriteLine(clientElement.Prenom + " a " + clientElement.Age + " ans");
}

Soit accder un lment partir de sa cl :


ClientElement elementNicolas = section["Nicolas"];
Console.WriteLine(elementNicolas.Prenom + " a " + elementNicolas.Age + " ans");
ClientElement elementJeremie = section["Jrmie"];
Console.WriteLine(elementJeremie.Prenom + " a " + elementJeremie.Age + " ans");

Ce qui donnera :
Nicolas a 30 ans
Jrmie a 20 ans

Voil pour ce petit tour sur la configuration dune application. Nous y avons dcouvert
plusieurs faons dy stocker des paramtres utilisables par notre application. Il faut savoir
que beaucoup de composants du framework .NET sont intimement lis ce fichier de
configuration, comme une application web cre avec ASP.NET ou lorsquon utilise des
services web.
Il est important de remarquer que ce fichier est un fichier de configuration dapplication. Il
y a dautres endroits du mme genre pour stocker de la configuration pour les applications
.NET, comme le machine.config qui est un fichier de configuration partag par toutes les
applications de la machine. Il y a un hritage entre les diffrents fichiers de configuration.

Si lon dfinit une configuration au niveau machine (dans le machine.config), il est


possible de la redfinir pour notre application (app.config). En gnral, le fichier
machine.config se trouve dans le rpertoire dinstallation du framework .NET, cest--dire
dans un sous rpertoire du systme dexploitation, dpendant de la version du framework
.NET installe. Chez moi par exemple, il se trouve dans le rpertoire :
C:\Windows\Microsoft.NET\Framework\v4.0.30319\Config.
Enfin, remarquons quil est possible de faire des modifications du fichier de configuration
directement depuis le code de notre application. Cest un point qui est rarement utilis et
que jai choisi de ne pas prsenter pour que le chapitre ne soit pas trop long.
En rsum

Les fichiers de configuration sont des fichiers XML qui possdent les paramtres de
configuration de notre application.
Pour les applications console, ils sappellent app.config.
On peut dfinir toutes sortes de valeurs de configuration grce aux sections
prdfinies ou en ajoutant son propre type de section personnalise.

Chapitre 36

Introduction LINQ

LINQ signifie Language INtegrated Query. Cest un ensemble dextensions du langage


permettant de faire des requtes sur des donnes en faisant abstraction de leur type. Il
permet dutiliser facilement un jeu dinstructions supplmentaires afin de filtrer des
donnes, faire des slections, etc. Il existe plusieurs domaines dapplications pour LINQ :
Linq To Entities ou Linq To SQL qui utilisent ces extensions de langage sur les bases
de donnes.
Linq To XML qui utilise ces extensions de langage pour travailler avec les fichiers
XML.
Linq To Object qui permet de travailler avec des collections dobjets en mmoire.
Ltude de LINQ ncessiterait un livre en entier, aussi nous allons nous concentrer sur la
partie qui va le plus nous servir en tant que dbutant et qui va nous permettre de
commencer travailler simplement avec cette nouvelle syntaxe, savoir Linq To Object. Il
sagit dextensions permettant de faire des requtes sur les objets en mmoire et
notamment sur toutes les listes ou collections. En fait, sur tout ce qui implmente
IEnumerable<>.

Les requtes Linq


Les requtes Linq proposent une nouvelle syntaxe permettant dcrire des requtes qui
ressemblent de loin des requtes SQL. Pour ceux qui ne connaissent pas le SQL, il sagit
dun langage permettant de faire des requtes sur les bases de donnes.
Pour utiliser ces requtes, il faut ajouter lespace de noms adquat, savoir :
using System.Linq;

Ce using est en gnral inclus par dfaut lorsquon cre un nouveau fichier.
Jusqu maintenant, si nous voulions afficher les entiers dune liste dentiers qui sont
strictement suprieur 5, nous aurions fait :
List<int> liste = new List<int> { 4, 6, 1, 9, 5, 15, 8, 3 };
foreach (int i in liste)
{
if (i > 5)
{
Console.WriteLine(i);
}

Grce Linq To Object, nous allons pouvoir filtrer en amont la liste afin de ne parcourir
que les entiers qui nous intressent, en faisant :
List<int> liste = new List<int> { 4, 6, 1, 9, 5, 15, 8, 3 };
IEnumerable<int> requeteFiltree = from i in liste
where i > 5
select i;
foreach (int i in requeteFiltree)
{
Console.WriteLine(i);
}

Qui donnera :
6
9
15
8

Nous avons ici cr une requte Linq qui contient des mots-cls, comme from, in, where et
select. Ici, cette requte veut dire que nous partons (from) de la liste liste en analysant
chaque entier de la liste dans (in) la variable i o (where) i est suprieur 5. Dans ce cas, il
est slectionn comme faisant partie du rsultat de la requte grce au mot-cl select, qui
fait un peu office de return.
Cette requte renvoie un IEnumerable<int>. Le type gnrique est ici le type int car cest
le type de la variable i qui est retourne par le select. Le fait de renvoyer un
IEnumerable<> va permettre potentiellement de rutiliser le rsultat de cette requte pour
un filtre successif ou une expression diffrente. En effet, Linq travaille sur des
IEnumerable<> Nous pourrions par exemple ordonner cette liste par ordre croissant
grce au mot-cl orderby. Cela donnerait :
List<int> liste = new List<int> { 4, 6, 1, 9, 5, 15, 8, 3 };
IEnumerable<int> requeteFiltree = from i in liste
where i > 5
select i;
IEnumerable<int> requeteOrdonnee = from i in requeteFiltree
orderby i
select i;
foreach (int i in requeteOrdonnee)
{
Console.WriteLine(i);
}

qui pourrait galement scrire en une seule fois avec :


List<int> liste = new List<int> { 4, 6, 1, 9, 5, 15, 8, 3 };
IEnumerable<int> requete = from i in liste
where i > 5
orderby i
select i;
foreach (int i in requete)
{
Console.WriteLine(i);
}

Et nous aurons :
6
8
9
15

Lintrt est que grce ces syntaxes, nous pouvons combiner facilement plusieurs filtres
et construire des requtes plus ou moins complexes.

Par exemple, imaginons des clients :


public class Client
{
public int Identifiant { get; set; }
public string Nom { get; set; }
public int Age { get; set; }
}

que lon souhaiterait savoir majeurs, puis tris par Age puis par Nom, nous pourrions faire :
List<Client> listeClients = new List<Client>
{
new Client { Identifiant = 1, Nom = "Nicolas", Age = 30},
new Client { Identifiant = 2, Nom = "Jrmie", Age = 20},
new Client { Identifiant = 3, Nom = "Delphine", Age = 30},
new Client { Identifiant = 4, Nom = "Bob", Age = 10}
};
IEnumerable<string> requete = from client in listeClients
where client.Age > 18
orderby client.Age, client.Nom
select client.Nom;
foreach (string prenom in requete)
{
Console.WriteLine(prenom);
}

Ce qui donnera :
Jrmie
Delphine
Nicolas

Notez ici que mon select renvoie le nom du client, qui est un string. Il est donc
normal que ma requte renvoie un IEnumerable<string> car jai choisi quelle ne
slectionne que les noms, qui sont de type string.
Il est assez frquent de construire des objets anonymes lintrieur dune requte. Par
exemple, plutt que de renvoyer uniquement le nom du client, je pourrais galement
renvoyer lAge, mais comme je nai pas besoin de lidentifiant, je peux crer un objet
anonyme juste avec ces deux proprits :
List<Client> listeClients = new List<Client>
{
new Client { Identifiant = 1, Nom = "Nicolas", Age = 30},
new Client { Identifiant = 2, Nom = "Jrmie", Age = 20},
new Client { Identifiant = 3, Nom = "Delphine", Age = 30},
new Client { Identifiant = 4, Nom = "Bob", Age = 10},
};
var requete = from client in listeClients
where client.Age > 18
orderby client.Age , client.Nom
select new { client.Nom, client.Age };
foreach (var obj in requete)
{
Console.WriteLine("{0} a {1} ans", obj.Nom, obj.Age);
}

Et nous aurons :
Jrmie a 20 ans
Delphine a 30 ans
Nicolas a 30 ans

Mon objet anonyme contient ici juste une proprit Nom et une proprit Age. noter que
je suis oblig ce moment-l dutiliser le mot-cl var pour dfinir la requte, car je nai
pas de type donner cette requte. De mme, dans le foreach je dois utiliser le mot-cl

var pour dfinir le type anonyme.

Les requtes peuvent tre de plus en plus compliques, comme faisant des jointures. Par
exemple, rajoutons une classe Commande :
public class Commande
{
public int Identifiant { get; set; }
public int IdentifiantClient { get; set; }
public decimal Prix { get; set; }
}

Je peux crer des commandes pour des clients :


List<Client> listeClients = new List<Client>
{
new Client { Identifiant = 1, Nom = "Nicolas", Age = 30},
new Client { Identifiant = 2, Nom = "Jrmie", Age = 20},
new Client { Identifiant = 3, Nom = "Delphine", Age = 30},
new Client { Identifiant = 4, Nom = "Bob", Age = 10},
};
List<Commande> listeCommandes = new List<Commande>
{
new Commande{ Identifiant = 1, IdentifiantClient = 1, Prix = 150.05M},
new Commande{ Identifiant = 2, IdentifiantClient = 2, Prix = 30M},
new Commande{ Identifiant = 3, IdentifiantClient = 1, Prix = 99.99M},
new Commande{ Identifiant = 4, IdentifiantClient = 1, Prix = 100M},
new Commande{ Identifiant = 5, IdentifiantClient = 3, Prix = 80M},
new Commande{ Identifiant = 6, IdentifiantClient = 3, Prix = 10M},
};

Et grce une jointure, rcuprer avec ma requte le nom du client et le prix de sa


commande :
var liste = from commande in listeCommandes
join client in listeClients on commande.IdentifiantClient equals client.Identifiant
select new { client.Nom, commande.Prix };
foreach (var element in liste)
{
Console.WriteLine("Le client {0} a pay {1}", element.Nom, element.Prix);
}

Ce qui donne :
Le client Nicolas a pay 150,05
Le client Jrmie a pay 30
Le client Nicolas a pay 99,99
Le client Nicolas a pay 100
Le client Delphine a pay 80
Le client Delphine a pay 10

On utilise le mot-cl join pour faire la jointure entre les deux listes puis on utilise le motcl on et le mot-cl equals pour indiquer sur quoi on fait la jointure.
noter que ceci peut se raliser en imbriquant galement les from et en filtrant sur
lgalit des identifiants clients :
var liste = from commande in listeCommandes
from client in listeClients
where client.Identifiant == commande.IdentifiantClient
select new { client.Nom, commande.Prix };
foreach (var element in liste)
{
Console.WriteLine("Le client {0} a pay {1}", element.Nom, element.Prix);
}

Il est intressant de pouvoir regrouper les objets qui ont la mme valeur. Par exemple pour
obtenir toutes les commandes, groupes par client, on ferra :

var liste = from commande in listeCommandes


group commande by commande.IdentifiantClient;
foreach (var element in liste)
{
Console.WriteLine("Le client : {0} a ralis {1} commande(s)", element.Key, element.Count());
foreach (Commande commande in element)
{
Console.WriteLine("\tPrix : {0}", commande.Prix);
}
}

Ici, cela donne :


Le client : 1 a ralis 3 commande(s)
Prix : 150,05
Prix : 99,99
Prix : 100
Le client : 2 a ralis 1 commande(s)
Prix : 30
Le client : 3 a ralis 2 commande(s)
Prix : 80
Prix : 10

Il est possible de cumuler le group by avec notre jointure prcdente histoire davoir
galement le nom du client :
var liste = from commande in listeCommandes
join client in listeClients on commande.IdentifiantClient equals client.Identifiant
group commande by new {commande.IdentifiantClient, client.Nom};
foreach (var element in liste)
{
Console.WriteLine("Le client {0} ({1}) a ralis {2} commande(s)", element.Key.Nom,
element.Key.IdentifiantClient, element.Count());
foreach (Commande commande in element)
{
Console.WriteLine("\tPrix : {0}", commande.Prix);
}
}

Et nous obtenons :
Le client Nicolas (1) a ralis 3 commande(s)
Prix : 150,05
Prix : 99,99
Prix : 100
Le client Jrmie (2) a ralis 1 commande(s)
Prix : 30
Le client Delphine (3) a ralis 2 commande(s)
Prix : 80
Prix : 10

noter que le group by termine la requte, un peu comme le select. Ainsi, si lon veut
slectionner quelque chose aprs le group by, il faudra utiliser le mot-cl into et la
syntaxe suivante :
Ce qui donnera :
var liste = from commande in listeCommandes
join client in listeClients on commande.IdentifiantClient equals client.Identifiant
group commande by new {commande.IdentifiantClient, client.Nom} into commandesGroupees
select
new
{
commandesGroupees.Key.IdentifiantClient,
commandesGroupees.Key.Nom,
NombreDeCommandes = commandesGroupees.Count()
};
foreach (var element in liste)
{
Console.WriteLine("Le client {0} ({1}) a ralis {2} commande(s)", element.Nom,

element.IdentifiantClient, element.NombreDeCommandes);
}

Avec pour rsultat :


Le client Nicolas (1) a ralis 3 commande(s)
Le client Jrmie (2) a ralis 1 commande(s)
Le client Delphine (3) a ralis 2 commande(s)

Lintrt dutiliser le mot-cl into est galement de pouvoir enchainer avec une autre
jointure ou autre filtre permettant de continuer la requte.
Il est galement possible dutiliser des variables lintrieur des requtes grce au mot-cl
let. Cela va nous permettre de stocker des rsultats temporaires pour les rutiliser ensuite
:
var liste = from commande in listeCommandes
join client in listeClients on commande.IdentifiantClient equals client.Identifiant
group commande by new {commande.IdentifiantClient, client.Nom} into commandesGroupees
let total = commandesGroupees.Sum(c => c.Prix)
where total > 50
orderby total
select new
{
commandesGroupees.Key.IdentifiantClient,
commandesGroupees.Key.Nom,
NombreDeCommandes = commandesGroupees.Count(),
PrixTotal = total
};
foreach (var element in liste)
{
Console.WriteLine("Le client {0} ({1}) a ralis {2} commande(s) pour un total de {3}",
element.Nom, element.IdentifiantClient, element.NombreDeCommandes, element.PrixTotal);
}

Par exemple, ici jutilise le mot-cl let pour stocker le total dune commande groupe
dans la variable total (nous verrons la mthode Sum() un tout petit peu plus bas), ce qui
me permet ensuite de filtrer avec un where pour obtenir les commandes dont le total est
suprieur 50 et de les trier par ordre de prix croissant.
Ce qui donne :
Le client Delphine (3) a ralis 2 commande(s) pour un total de 90
Le client Nicolas (1) a ralis 3 commande(s) pour un total de 350,04

Nous allons nous arrter l pour cet aperu des requtes LINQ. Nous avons pu voir que le
C# dispose dun certain nombre de mots-cls qui permettent de manipuler nos donnes de
manire trs puissante mais dune faon un peu inhabituelle.
Cette faon dcrire des requtes LINQ sappelle en anglais la sugar syntax , que lon
peut traduire par sucre syntaxique . Il dsigne de manire gnrale les constructions
dun langage qui facilitent la rdaction du code sans modifier lexpressivit du langage.
Voyons prsent ce quil y a derrire cette jolie syntaxe.

Les mthodes dextensions Linq


En fait, toute la sugar syntax que nous avons vue prcdemment repose sur un certain
nombre de mthodes dextensions qui travaillent sur les types IEnumerable<T>. Par
exemple, la requte suivante :
List<int> liste = new List<int> { 4, 6, 1, 9, 5, 15, 8, 3 };
IEnumerable<int> requeteFiltree = from i in liste

where i > 5
select i;
foreach (int i in requeteFiltree)
{
Console.WriteLine(i);
}

scrit vritablement :
List<int> liste = new List<int> { 4, 6, 1, 9, 5, 15, 8, 3 };
IEnumerable<int> requeteFiltree = liste.Where(i => i > 5);
foreach (int i in requeteFiltree)
{
Console.WriteLine(i);
}

Nous utilisons la mthode dextensions Where() en lui fournissant une expression lambda
servant de prdicat pour filtrer la liste.
Cest de cette faon que le compilateur traduit la sugar syntax. Elle nest donc quune
faon plus jolie dutiliser ces mthodes dextensions.
Chaque mthode dextension renvoie un IEnumerable<T> ce qui permet denchainer
facilement les filtres successifs. Par exemple, rajoutons une date et un nombre darticles
notre classe Commande :
public class Commande
{
public int Identifiant { get; set; }
public int IdentifiantClient { get; set; }
public decimal Prix { get; set; }
public DateTime Date { get; set; }
public int NombreArticles { get; set; }
}

Avec la requte suivante :


IEnumerable<Commande> commandesFiltrees = listeCommandes.
Where(commande => commande.Prix > 100).
Where(commande => commande.NombreArticles > 10).
OrderBy(commande => commande.Prix).
ThenBy(commande => commande.DateAchat);

Nous pouvons obtenir les commandes dont le prix est suprieur 100, o le nombre
darticles est suprieur 10, tries par prix puis par date dachat.
De plus, ces mthodes dextensions font beaucoup plus de choses que ce que lon peut
faire avec la sugar syntax. Il existe pas mal de mthodes intressantes, que nous ne
pourrons pas toutes tudier.
Regardons par exemple la mthode Sum() (qui a t utilise dans le paragraphe prcdent)
qui permet de faire la somme des lments dune liste ou la mthode Average() qui permet
de faire la moyenne :
List<int> liste = new List<int> { 4, 6, 1, 9, 5, 15, 8, 3 };
Console.WriteLine("Somme : {0}", liste.Sum());
Console.WriteLine("Moyenne : {0}", liste.Average());

Qui nous renvoie dans ce cas :


Somme : 51
Moyenne : 6,375

Tout est dj fait , pratique !


videmment, les surcharges de ces deux mthodes dextensions ne fonctionnent quavec
des types int ou double ou decimal Qui envisagerait de faire une moyenne sur une
chaine ?

Par contre, il est possible de dfinir une expression lambda dans la mthode Sum() afin de
faire la somme sur un lment dun objet, comme le prix de notre commande :
decimal prixTotal = listeCommandes.Sum(commande => commande.Prix);

Dautres mthodes sont bien utiles. Par exemple la mthode dextension Take() nous
permet de rcuprer les X premiers lments dune liste :
IEnumerable<Client> extrait = listeClients.OrderByDescending(client => client.Age).Take(5);

Ici, je trie dans un premier temps ma liste par ge dcroissant, et je prends les 5 premiers.
Ce qui signifie que je prends les 5 plus vieux clients de ma liste.
Et sil y en a que 3 ? et bien il prendra uniquement les 3 premiers.
Suivant le mme principe, on peut utiliser la mthode First() pour obtenir le premier
lment dune liste :
List<int> liste = new List<int> { 4, 6, 1, 9, 5, 15, 8, 3 };
int premier = liste.Where(i => i > 5).First();

O jobtiens le premier lment de la liste qui est strictement suprieur 5.


noter que le filtre peut galement se faire dans lexpression lambda de la mthode
First() :
int premier = liste.First(i => i > 5);

Ce qui revient exactement au mme.


Attention, sil ny a aucun lment dans la liste, alors la mthode First() lve lexception
:
Exception non gre : System.InvalidOperationException: La squence ne contient aucun lment.

Il est possible dans ce cas-l dviter une exception avec la mthode FirstOrDefault() qui
renvoie la valeur par dfaut du type de la liste (0 si cest un type valeur, null si cest un
type rfrence) :
Client nicolas = listeClients.FirstOrDefault(client => client.Nom == "Nicolas");
if (nicolas == null)
Console.WriteLine("Client non trouv");

Ici, je cherche le premier des clients dont le nom est Nicolas. Sil nest pas trouv, alors
FirstOrDefault() me renvoie null, sinon, il me renvoie bien sr le bon objet Client.
Dans le mme genre, nous pouvons compter grce la mthode Count() le nombre
dlments dune source de donnes suivant un critre :
int nombreClientsMajeurs = listeClients.Count(client => client.Age >= 18);

Ici, jobtiendrais le nombre de clients majeurs dans ma liste.


De la mme faon quavec la sugar syntax, il est possible de faire une slection prcise
des donnes que lon souhaite extraire, grce la mthode Select() :
var requete = listeClients.Where(client => client.Age >= 18).Select(client => new { client.Age,
client.Nom }) ;

Cela me permettra dobtenir une requte contenant les clients majeurs. noter quil y a
aura dedans des objets anonymes possdant une proprit Age et une proprit Nom.
Bien sr, nous retrouverons nos jointures avec la mthode dextension Join() ou les
groupes avec la mthode GroupBy().
Il existe beaucoup de mthodes dextensions et il nest pas envisageable dans ce tutoriel
de toutes les dcrire.

Je vais finir en vous parlant des mthodes ToList() et ToArray() qui comme leurs noms le
suggrent, permettent de forcer la requte tre mise dans une liste ou dans un tableau :
List<Client> lesPlusVieuxClients = listeClients.OrderByDescending(client =>
client.Age).Take(5).ToList();

ou
Client[] lesPlusVieuxClients = listeClients.OrderByDescending(client =>
client.Age).Take(5).ToArray();

Plutt que davoir un IEnumerable<>, nous obtiendrons cette fois-ci une List<> ou un
tableau. Le fait dutiliser ces mthodes dextensions a des consquences que nous allons
dcrire.

Excution diffre
Alors, les mthodes dextensions LINQ ou sa syntaxe sucre cest bien joli, mais quel est
lintrt de sen servir plutt que dutiliser des boucles foreach, des if ou autres choses ?
Dj, parce quil y a plein de choses dj toutes faites, la somme, la moyenne, la
rcupration de X lments, etc.
Mais aussi pour une autre raison plus importante : lexcution diffre.
Nous en avons dj parl, lexcution diffre est possible grce au mot-cl yield. Les
mthodes dextensions Linq utilisent fortement ce principe.
Cela veut dire que lorsque nous construisons une requte, elle nest pas excute tant
quon itre pas sur le contenu de la requte. Ceci permet de stocker la requte, dempiler
ventuellement des filtres ou des jointures et de ne pas calculer le rsultat tant quon nen
a pas explicitement besoin.
Ainsi, imaginons que nous souhaitions trier une liste dentiers, avant nous aurions fait :
List<int> liste = new List<int> { 4, 6, 1, 9, 5, 15, 8, 3 };
liste.Sort();
liste.Add(7);
foreach (int i in liste)
{
Console.WriteLine(i);
}

Ce qui aurait affich en toute logique la liste trie puis la fin lentier 7 rajout, cest-dire :
1
3
4
5
6
8
9
15
7

Avec Linq, nous allons pouvoir faire :


List<int> liste = new List<int> { 4, 6, 1, 9, 5, 15, 8, 3 };
var requete = liste.OrderBy(e => e);
liste.Add(7);

foreach (int i in requete)


{
Console.WriteLine(i);
}

Et si nous excutons ce code, nous aurons :


1
3
4
5
6
7
8
9
15

Bien que nous ayons ajout la valeur 7 aprs avoir tri la liste avec OrderBy, on se rend
compte que tous les entiers sont quand mme tris lorsque nous les affichons.
En effet, la requte na t excute quau moment du foreach. Ceci implique donc que le
tri va tenir compte de lajout du 7 la liste. La requte est construite en mmorisant les
conditions comme notre OrderBy, mais cela fonctionne galement avec un where, et tout
ceci nest excut que lorsquon le demande explicitement, cest--dire avec un foreach
dans ce cas-l.
En fait, tant que le C# nest pas oblig de parcourir les lments numrables alors il ne le
fait pas. Ce qui permet denchainer les ventuelles conditions et viter les parcours
inutiles. Par exemple, dans le cas ci-dessous, il est inutile dexcuter le premier filtre :
List<int> liste = new List<int> { 4, 6, 1, 9, 5, 15, 8, 3 };
IEnumerable<int> requete = liste.Where(i => i > 5);
// plein de choses qui n'ont rien voir avec la requete
requete = requete.Where(i => i > 10);

car le deuxime filtre a tout intrt tre combin au premier afin dtre simplifi.
Et encore, ici, on nutilise mme pas la requte, il y a encore moins dintrt effectuer
nos filtres si nous ne nous servons pas du rsultat.
Ceci peut paratre inattendu, mais cest trs important dans la faon dont Linq sen sert
afin doptimiser ses requtes. Ici, le parcours en mmoire pourrait paratre peu couteux,
mais dans la mesure o Linq doit fonctionner aussi bien avec des objets, quavec des bases
de donnes ou du XML (ou autres), cette optimisation prend tout son sens.
Le maitre mot est la performance, primordial quand on accde aux bases de donnes.
Cette excution diffre est garde pour le plus tard possible. Cest--dire que le fait de
parcourir notre boucle va obligatoirement entrainer lvaluation de la requte afin de
pouvoir retourner les rsultats cohrents.
Il en va de mme pour certaines autres oprations, comme la mthode Sum(). Comment
pourrions-nous faire la somme de tous les lments si nous ne les parcourons pas ?
Cest aussi le cas pour les mthodes ToList() et ToArray().
Par contre, ce nest pas le cas pour les mthodes Where, ou Take, etc
Il est important de connaitre ce mcanisme. Lexcution diffre est trs puissante et
connatre son fonctionnement permet de savoir exactement ce que nous faisons et
pourquoi nous pourrions obtenir parfois des rsultats tranges.

Rcapitulatif des oprateurs de requtes

Pour terminer avec Linq, voici un tableau rcapitulatif des diffrents oprateurs de
requte. Nous ne les avons pas tous tudis ici car cela serait bien vite lassant. Mais grce
leurs noms et leurs types, il est assez facile de voir quoi ils servent afin de les utiliser
dans la construction de nos requtes.
Type

Oprateur de requte

Excution
diffre

Tri des donnes

OrderBy, OrderByDescending, ThenBy, ThenByDescending, Reverse

Oui

Oprations
ensemblistes

Distinct, Except, Intersect, Union

Oui

Filtrage des donnes

OfType, Where

Oui

Oprations de
quantificateur

All, Any, Contains

Non

Oprations de
projection

Select, SelectMany

Oui

Partitionnement des
donnes

Skip, SkipWhile, Take, TakeWhile

Oui

Oprations de jointure

Join, GroupJoin

Oui

Regroupement de
donnes

GroupBy, ToLookup

Oui

Oprations de
gnration

DefaultIfEmpty, Empty, Range, Repeat

Oui

Oprations dgalit

SequenceEqual

Non

Oprations dlment

ElementAt, ElementAtOrDefault, First, FirstOrDefault, Last, LastOrDefault, Non


Single, SingleOrDefault

Conversion de types de AsEnumerable, AsQueryable, Cast, OfType, ToArray, ToDictionary, ToList,


donnes
ToLookup

Non

Oprations de
concatnation

Concat

Oui

Oprations
dagrgation

Aggregate, Average, Count, LongCount, Max, Min, Sum

Non

Nhsitez pas consulter la documentation de ces mthodes dextensions ou aller voir


des exemples sur internet. Il y a beaucoup de choses faire avec ces mthodes. Il est
important galement de bien savoir les matriser afin dviter les problmes de
performances. En effet, lvaluation systmatique des expressions peut tre coteuse,
surtout quand cest imbriqu dans des boucles.
utiliser judicieusement.
Voil pour ce petit aperu de Linq !
Rappelez-vous bien que Linq est une abstraction qui permet de manipuler des sources de
donnes diffrentes. Nous avons vu son utilisation avec les objets implmentant
IEnumerable<>, avec ce quon appelle Linq To Objects.
Il est possible de faire du Linq en allant manipuler des donnes en base de donnes, on
utilisera pour cela Linq To SQL ou Linq To Entity.
De mme, il est possible de manipuler les fichiers XML avec Linq To XML.
Linq apporte des mthodes dextensions et une syntaxe complmentaire afin dtre

efficace avec la manipulation de sources de donnes.


Sachez enfin quil est possible de requter nimporte quelle source de donnes partir du
moment o un connecteur spcifique a t dvelopp. Cela a t fait par exemple pour
interroger Google ou Amazon, mais aussi pour requter sur active directory, ou JSON, etc.
En rsum

Linq consiste en un ensemble dextensions du langage permettant de faire des


requtes sur des donnes en faisant abstraction de leur type.
Il existe plusieurs domaines dapplications de Linq, comme Linq to Object, Linq to
Sql, etc.
La sugar syntax ajoute des mots-cls qui permettent de faire des requtes qui
ressemblent aux requtes faites avec le langage SQL.
Derrire cette syntaxe se cache un bon nombre de mthodes dextension qui tirent
parti des mcanismes dexcution diffre.

Chapitre 37

Utiliser Entity Framework pour accder aux bases


de donnes (indit)

Nous allons voir dans ce chapitre comment connecter nos applications une base de
donnes. Bien quil soit possible de travailler avec quasiment nimporte quelle base de
donnes (oracle, mysql,), le framework .NET offre toute sa puissance en fonctionnant
avec SQL Server.
Si vous vous souvenez, lors de linstallation de Visual C# Express, nous avons galement
install Microsoft SQL Server 2008 express Service Pack 1. SQL Server 2008 Express est
un moteur de base de donnes light, idal pour travailler en local sur son PC. Dans un
environnement de production, nous aurons tout intrt travailler avec la version
complte de SQL Server, mais pour ce livre, la version express est amplement suffisante.
En plus elle est gratuite !

Les bases de donnes et la modlisation


Une base de donnes est un gros espace de stockage structur qui permet denregistrer
efficacement de trs grandes quantits dinformations. Cest un lment incontournable
dans tout systme informatique.
Dans une base de donnes, on peut stocker des informations unitaires, comme le nom dun
client, son age, son adresse,etc. Ce sont les champs. Ces champs sont regroups
smantiquement dans des tables. Par exemple, la table des clients contient les
informations relatives au client Nicolas, au client Jrmie, etc. Ces informations sont
appeles des enregistrements.
Lorsque lon reprsente les donnes de son application, nous allons avoir besoin de
plusieurs tables. Par exemple, pour reprsenter les donnes dune application de
commerce, nous pouvons avoir une table Produits , une table Rayons , une table
Clients , une table Commandes , etc
Il peut y avoir des relations entre les tables, par exemple un rayon peut contenir de 0 N
produits, un client peut passer de 0 N commandes, etc.
On appelle ces bases de donnes des bases de donnes relationnelles.
Nous pouvons lire le contenu des tables ou insrer des nouvelles valeurs grce au langage
SQL. Nous allons trs peu nous en servir mais il est la base de tout requtage en base de
donnes. Nous nallons pas faire ici de cours sur le SQL mais nous allons lutiliser

certains endroits par souci de simplicit.


La premire chose faire est de rflchir ce dont on a besoin comme modle de donnes
dans notre application. Pour ce chapitre, nous allons nous prendre pour une application de
commerce, dans le genre site de-commerce, sauf que nous sommes spcialiss dans la
cration dapplications console.
Nous allons commencer par modliser les rayons et les produits. On a dit quun rayon
pouvait tre compos de 0 N produits. De mme, un produit peut appartenir 0 ou N
rayons.
Un rayon possde un identifiant, un nom et une description. Un produit possde un
identifiant, un nom, un prix, un stock et une url vers son image.
Ce qui nous donne :

On appelle ce schma un modle conceptuel de donnes.

Entity Framework et le mapping objet relationnel


Finalement, la modlisation que nous avons faite juste au-dessus ressemble beaucoup la
modlisation oriente objet que nous commenons maitriser. On pourrait trs bien avoir
des objets Produit, des objets Rayon. Un rayon pourrait contenir des produits, etc. Ne
pourrions-nous pas essayer de reprsenter la base de donnes sous la forme dun modle
orient objet plutt quun modle relationnel ?
Cest le principe de ce que lon appelle un ORM (object relationnal mapping) que lon
traduit en franais par mapping objet-relationnel . LORM est un outil qui permet de
gnrer une couche daccs aux donnes oriente objet partir dune base de donnes
relationnelle en dfinissant des correspondances entre cette base de donnes et des objets.
Il existe plusieurs outils dORM pour .NET permettant de gnrer des objets C#, comme
nhibernate, Entity Framework,
Nous allons utiliser ici Entity framework. Cest lORM de Microsoft. Il est totalement
intgr Visual C# Express ou aux autres versions. Son travail consiste, entre autres, en
plusieurs choses :
Il permet de modliser ses donnes et de gnrer la base correspondante
Il permet galement de gnrer un modle partir dune base de donnes existante
Il permet de grer tous les accs la base de donnes (lecture, criture, suppression,
)
Le grand intrt est que nous allons travailler directement avec des objets et cest lui qui
soccupera de tout ce qui est persistance dans la base de donnes.

Allez, stop le blabla, passons son utilisation.


Commenons par crer une application console :

prsent, ajoutons un nouvel lment de type ADO.NET Entity Data Model notre projet,
que lon va appeler ModelCommerce.edmx :

Cest ce type de fichier qui va nous permettre de modliser nos donnes. Lassistant
souvre et nous choisissons :

On voit apparatre une nouvelle fentre intitule Entity Data Model Designer. Cette
fentre designer va nous permettre de modliser nos donnes. Elle nous donne
galement accs la boite outils :

Lorsque nous accdons la boite outils, nous pouvons voir plusieurs lments. Celui qui
nous intresse est lentit :

Nous allons pouvoir modliser nos entits en les faisant glisser sur le designer. Nous la
voyons apparatre sur le designer :

Nous pouvons renommer cette entit soit en allant modifier la proprit Nom dans la fentre
de proprits, soit en cliquant directement sur le nom dans le designer. Appelons cette
entit Rayon.
Nous pouvons constater que cette entit est gnre avec une proprit par dfaut : ID.
Cest lidentifiant de notre rayon. Cet identifiant possde des proprits que nous pouvons
voir dans la fentre de proprits :

Nous pouvons par exemple voir (ou modifier) son nom (ID) et son type (Int32, qui est
lquivalent de int). Nous voyons galement que cette proprit est la cl dentit. Ce qui
veut dire que cest ce qui va nous permettre didentifier notre rayon de manire unique.
(Cest un peu plus complexe que a, mais retenons ce point).
Nous pouvons rajouter une nouvelle proprit lentit, pour cela il suffit de faire un clic
droit sur lentit et dajouter une proprit scalaire.

Une nouvelle proprit apparait dans notre entit Rayon.

Nous pouvons la renommer en Nom. Nous pouvons voir que par dfaut elle est du type
String, ce qui nous va trs bien.

Rajoutons une troisime proprit Description, toujours de type String mais qui pourra
tre nulle, il suffit de dclarer la proprit Nullable True :

a y est, notre entit Rayon est modlise.


Rajoutons maintenant une nouvelle entit, Produit, qui possde galement un identifiant,
un nom de type String et un prix de type Decimal. Pour mettre le type Decimal, il suffit
de changer le type dans la fentre des proprits :

Ensuite rajoutons une proprit Stock de type Int32 et une proprit UrlImage de type
String.
Nous obtenons deux superbes entits :

Il est temps de relier les entits entre elles grce une association. Pour cela, cliquez droit
sur lentit Rayon et choisissez dajouter une association :

Un nouvel cran souvre qui permet de dfinir lassociation. Indiquons que la multiplicit
est plusieurs sur les deux entits, ce qui permet de dire quun rayon peut contenir de 0
N produits et inversement, un produit peut tre contenu dans 0 N rayons. Notons au
passage que les choix possibles sont 1, 0 ou 1 et plusieurs. Cest ce qui nous permet
dindiquer la cardinalit de nos relations. Changez ensuite le nom des proprits de
navigation en rajoutant un s Produit et Rayon :

Le designer est mis jour avec la relation et on peut voir apparaitre des proprits de
navigation dans les entits. Lentit Rayon a une proprit Produits, ce qui va permettre
dobtenir la liste des produits dun Rayon. De mme, lentit Produit possde une proprit
de navigation Rayons qui va permettre dobtenir la liste des rayons qui contiennent le
produit :

Nous allons encore faire une petite modification ce modle. Slectionnez lentit Rayon.
Nous pouvons voir dans ses proprits que le nom du jeu dentit vaut RayonJeu, modifiezle en Rayons :

Faites pareil pour lentit Produit, changez ProduitJeu en Produits.


Voil, tout a, cest notre modle. Il faut maintenant faire en sorte que notre base de
donnes soit cohrente avec le modle. Il suffit de faire un clic droit sur le designer et de
choisir de gnrer la base de donnes partir du modle :

Une nouvelle fentre souvre nous permettant de choisir notre source de donnes. Celle-ci
tant vide, cliquez sur Nouvelle connexion :

Une nouvelle fentre apparait nous permettant de choisir notre source de donnes :

Attention, ici dans la version express de Visual C#, il nest possible de choisir
que parmi 2 options, un fichier de base de donnes ou Microsoft SQL Server
compact. Dans dautres versions express (notamment la version permettant de
faire du dveloppement web) et dans les versions payantes de Visual Studio, il

est possible de choisir directement un serveur de base de donnes.


Cela aurait t plus pratique. Tant pis, nous allons faire avec ; choisissons le fichier de
base de donnes. Il faut lui donner un emplacement et un nom, par exemple dans le
rpertoire des projets, je lappelle basecommerce.mdf. Ensuite, pour pouvoir sy connecter,
nous utiliserons lauthentification Windows :

la validation, il nous demande si on veut crer le fichier de base de donnes. Rpondez


oui.

Puis nous arrivons sur un rcapitulatif et nous voyons en bas la chaine de connexion la
base de donnes.

Nous pouvons choisir soit denregistrer les paramtres de connexion, soit de ne pas le
faire, en cochant ou dcochant cette case. Dans tous les cas, cette chaine de connexion ne
nous servira pas en ltat. Cliquez sur suivant.
Le designer dEntity Framework nous a finalement cr un fichier contenant des
instructions SQL quil nous propose denregistrer. Ces instructions SQL vont permettre de
gnrer les tables de la base de donnes :

Nous allons revenir sur ces instructions. Ce fichier souvre galement dans Visual C#
Express :

Nous en avons termin pour linstant avec le designer.

Installer loutil de gestion de base de donnes et prparer


les donnes
Nous avons le serveur de base de donnes, qui a t install en mme temps que Visual C#
Express. Nous avons le script permettant de gnrer le modle de donnes. Il nous manque
un outil permettant de crer la base de donnes et dexcuter le script. Cest loutil de
gestion de base de donnes.
Rendez-vous sur http://www.microsoft.com/downloads/fr- [] -4B76A8564A2B pour
installer Microsoft SQL Server 2008 Management Studio Express . Notez quil ne
sagit que des outils puisque nous avons dj install un serveur de base donnes. Il existe
cependant dautres installations qui cumulent le serveur de base de donnes ainsi que les
outils.
Tlchargez linstalleur qui vous convient et excutez le, vous obtenez :

Choisissez Installation et poursuivez. Choisissez ensuite lajout de fonctionnalits une


installation existante :

On poursuit linstallation et on arrive sur :

Installer le tout. Puis nous arrivons sur le choix de ce quil faut installer. Sur lcran qui
suit :

Mme si le nom est trompeur, nous devons effectuer une nouvelle installation de SQL
Server 2008. Ce nest pas tout fait une installation dune nouvelle instance car nous
avons dj une instance installe. Nous pouvons le voir juste au-dessous sur la copie
dcran, o linstance existante dj installe sappelle SQLEXPRESS. Retenons bien le
nom de cette instance, il nous servira un peu plus loin.
Puis nous arrivons sur lcran suivant qui nous indique que nous allons ajouter loutil de
gestion de base :

Poursuivez linstallation et voil, nous avons termin cette installation .


Maintenant, nous pouvons dmarrer SQL Server Management Studio :

Au dmarrage, il nous demande de nous connecter notre instance de base de donnes.


Par dfaut, linstance sappelle SQLEXPRESS, comme nous lavons vu, et nous pouvons
nous y connecter en la prfixant par le nom de notre machine ou bien simplement en
utilisant le point . , ce qui donne .\SQLEXPRESS. Conservez lauthentification
Windows :

Vous arrivez dans loutil et vous pouvez voir dans lexplorateur dobjets gauche quil ny
a pas (encore) de base de donnes :

Nous allons devoir crer notre base de donnes. Faites un clic droit sur le dossier
Base de donnes et choisissez Nouvelle base de donnes :

Donnez-lui le mme nom que le fichier de base de donnes que nous avions
prcdemment cr : basecommerce.

Et validez. La base de donnes est cre.


Nous la voyons apparaitre dans lexplorateur dobjets et nous voyons galement quil ny
a pas de tables dedans.

Cliquons maintenant sur Nouvelle requte, une fentre vide souvre o nous allons coller
le contenu du fichier SQL qui a t gnr par le designer de Visual C# Express.
Parlons un peu du contenu de ce script. Remarquons dj que les commentaires sont
prfixs par , mais, malins comme nous sommes, nous les aurions reconnus, en plus ils
sont en vert.
Ensuite, la ligne :
USE [basecommerce];

permet dindiquer que nous allons nous positionner sur la base basecommerce. Si jamais
vous navez pas donn le mme nom la base de donnes, cest ici quil faut le changer.
Allons un peu plus bas, nous voyons linstruction :
CREATE TABLE [dbo].[Rayons] (
[ID] int IDENTITY(1,1) NOT NULL,
[Nom] nvarchar(max) NOT NULL,
[Description] nvarchar(max) NULL
);

qui permet de crer la table qui contient les rayons, suivi du mme genre dinstruction qui
permet de crer la table contenant les produits. Ne nous attardons pas trop dessus mais
nous pouvons voir la syntaxe permettant de crer la table (avec CREATE TABLE) et la
syntaxe permettant de crer les champs de la table, ainsi que leurs types.
Aprs la cration des tables, et comme lindiquent les commentaires pour les anglophones,
la suite est une histoire de cl primaire et de cl trangre.
Cl primaire ? Cl trangre ? Cest quoi a ?
Ce sont des notions de base de donnes. Sans trop rentrer dans les dtails, je vais vous
expliquer rapidement de quoi il sagit.
Une cl primaire est une contrainte dunicit qui permet didentifier de manire unique
un enregistrement dans une table. La cl primaire correspond dans notre cas lidentifiant
dun rayon ou lidentifiant dun produit dans leurs tables respectives. noter quelles

ont une proprit complmentaire, savoir un auto-incrment. Cest--dire que cest SQL
Server qui va soccuper de numroter automatiquement ces identifiants, en les
incrmentant chaque insertion.
Une cl trangre est une contrainte qui garantit lintgrit rfrentielle entre deux tables.
Elle identifie une colonne dune autre table. Cela permet de faire des liens smantiques
entre les tables.
Vous navez pas besoin de savoir exactement ce quil se passe dans ce script
SQL. Nous le regardons vite fait pour la culture, mais il faut juste savoir
lexcuter afin quil nous cre les tables.
Ce petit apart termin, retournons dans SQL Server management studio et collons-y notre
requte. Il ne reste plus qu excuter le script en cliquant sur le bouton excuter :

Comme tout sest bien pass, nous pouvons rafraichir lexplorateur dobjets et constater
que les nouvelles tables sont cres :

Maintenant, nous avons besoin de donnes dans ces tables. Il y a plusieurs faons de la
faire. La premire est dutiliser le designer de SQL Server Management Studio, la seconde
serait dutiliser un script SQL, la troisime serait dutiliser du code C#.
Regardons la premire solution et cliquons droit sur la table produit pour Modifier les
200 lignes du haut :

Puis nous pouvons insrer des valeurs comme sur limage :

Ne le faites pas, car pour vous viter du travail, je lai fait pour vous grce la deuxime
mthode, le script SQL :
INSERT INTO [Produits] ([Nom],[Prix],[Stock],[UrlImage])
VALUES ('Tl HD', 299, 50, 'tele.jpg')
INSERT INTO [Produits] ([Nom],[Prix],[Stock],[UrlImage])
VALUES ('Console de jeux', 150, 25, 'console.jpg')
INSERT INTO [Produits] ([Nom],[Prix],[Stock],[UrlImage])
VALUES ('Canap', 400, 10, 'canape.jpg')
INSERT INTO [Produits] ([Nom],[Prix],[Stock],[UrlImage])
VALUES ('Cuisinire', 280, 20, 'cuisiniere.jpg')
INSERT INTO [Produits] ([Nom],[Prix],[Stock],[UrlImage])
VALUES ('Bouilloire', 19, 100, 'bouilloire.jpg')
INSERT INTO [Produits] ([Nom],[Prix],[Stock],[UrlImage])
VALUES ('Lit 2 places', 149, 15, 'lit.jpg')
INSERT INTO [Produits] ([Nom],[Prix],[Stock],[UrlImage])
VALUES ('Pull', 39.99, 25, 'pull.jpg')
INSERT INTO [Produits] ([Nom],[Prix],[Stock],[UrlImage])
VALUES ('T-shirt', 19.99, 20, 'tshirt.jpg')
INSERT INTO [Produits] ([Nom],[Prix],[Stock],[UrlImage])
VALUES ('Pyjama', 15.15, 4, 'pyjama.jpg')
INSERT INTO [Produits] ([Nom],[Prix],[Stock],[UrlImage])
VALUES ('Tablette PC', 350, 44, 'tablette.jpg')
INSERT INTO [Produits] ([Nom],[Prix],[Stock],[UrlImage])
VALUES ('Smartphone', 319.99, 40, 'smartphone.jpg')

Il vous suffit dexcuter ce script pour insrer les donnes. Nous nallons pas dtailler la
syntaxe de ce script, mais il est quand mme assez facile lire comme a. noter que
nous navons pas besoin dindiquer didentifiant car il est auto-incrment par SQL

Server.
Crons maintenant des rayons, avec la mme technique :
INSERT INTO [Rayons] ([Nom],[Description])
VALUES ('Salon', 'Tout ce qu''on trouve dans un salon')
INSERT INTO [Rayons] ([Nom],[Description])
VALUES ('Cuisine', 'Venez dcouvrir l''univers de la cuisine')
INSERT INTO [Rayons] ([Nom],[Description])
VALUES ('Dormir', null)
INSERT INTO [Rayons] ([Nom],[Description])
VALUES ('Hi-Tech', 'Les produits hi-tech')
INSERT INTO [Rayons] ([Nom],[Description])
VALUES ('Vtements', null)

Nous pouvons voir le contenu de ces tables en faisant un clic droit, puis Slectionner les
1000 lignes du haut :

Ce qui permet de voir la table Produits :

Il ny a plus qu relier les produits et les rayons. Pour cela, il faut relier les identifiants
entre eux.
Par exemple, avec le script suivant jindique que le rayon Salon (identifiant 1) contient la
tl HD (identifiant 1), la console de jeux (identifiant 2), le canap (identifiant 3), la
tablette PC (identifiant 10).
Jindique galement que le rayon Cuisine (identifiant 2) contient la cuisinire (identifiant
4), ainsi que la bouilloire (identifiant 5). Et ainsi de suite :
INSERT INTO [RayonProduit] ([Rayons_ID],[Produits_ID])
VALUES (1, 1)
INSERT INTO [RayonProduit] ([Rayons_ID],[Produits_ID])

VALUES (1, 2)
INSERT INTO [RayonProduit] ([Rayons_ID],[Produits_ID])
VALUES (1, 3)
INSERT INTO [RayonProduit] ([Rayons_ID],[Produits_ID])
VALUES (1, 10)
INSERT INTO [RayonProduit] ([Rayons_ID],[Produits_ID])
VALUES (2, 4)
INSERT INTO [RayonProduit] ([Rayons_ID],[Produits_ID])
VALUES (2, 5)
INSERT INTO [RayonProduit] ([Rayons_ID],[Produits_ID])
VALUES (3, 3)
INSERT INTO [RayonProduit] ([Rayons_ID],[Produits_ID])
VALUES (3, 6)
INSERT INTO [RayonProduit] ([Rayons_ID],[Produits_ID])
VALUES (3, 9)
INSERT INTO [RayonProduit] ([Rayons_ID],[Produits_ID])
VALUES (4, 1)
INSERT INTO [RayonProduit] ([Rayons_ID],[Produits_ID])
VALUES (4, 2)
INSERT INTO [RayonProduit] ([Rayons_ID],[Produits_ID])
VALUES (4, 10)
INSERT INTO [RayonProduit] ([Rayons_ID],[Produits_ID])
VALUES (4, 11)
INSERT INTO [RayonProduit] ([Rayons_ID],[Produits_ID])
VALUES (5, 7)
INSERT INTO [RayonProduit] ([Rayons_ID],[Produits_ID])
VALUES (5, 8)
INSERT INTO [RayonProduit] ([Rayons_ID],[Produits_ID])
VALUES (5, 9)

Voil, notre base de donnes est prte. Nous allons pouvoir utiliser cette base et ses
donnes dans notre code C#.

Se connecter la base de donnes, lire et crire


Il est temps de se connecter notre base de donnes depuis notre application C#.
Pour cela, nous avons besoin de la chaine de connexion la base de donnes. Nous avons
dj parl de la chaine de connexion, elle contient toutes les informations ncessaires pour
se connecter la base de donnes, savoir le nom du serveur, le nom de la base, les
identifiants de connexion, le type de connexion, etc. Nous en avons galement vu un
aperu lorsque nous avons utilis lassistant de gnration de modle sauf que je vous ai
indiqu que cette chaine de connexion nallait pas tre bonne pour nos besoins. En effet,
nous avons besoin quelle pointe vers notre serveur de base de donnes et pas vers le
fichier temporaire que nous avons cr pour les besoins de lassistant.
Cette chaine de connexion a toute sa place dans le fichier de configuration de
lapplication, que nous avons dj tudi. Si vous ne lavez pas dj ajout, il est temps de
le faire. Et vous pouvez le remplir avec la configuration suivante :
<?xml version="1.0" encoding="utf-8" ?>
<configuration>
<connectionStrings>
<add name="NotreBaseDeDonnees"

connectionString="metadata=res://*/ModelCommerce.csdl|res://*/ModelCommerce.ssdl|res://*/ModelCommerc
e.msl;provider=System.Data.SqlClient;provider
connection string='data source=.\SQLEXPRESS;Initial Catalog=basecommerce;integrated
security=True'"
providerName="System.Data.EntityClient" />
</connectionStrings>
</configuration>

Nous indiquons ici que notre chaine de connexion va tre accessible par le nom

NotreBaseDeDonnees. Il y a plein dinformations dans lattribut connectionString, mais ce

qui nous intresse surtout, cest dindiquer la source de donnes (data


source=.\SQLEXPRESS) ce qui va nous permettre dindiquer que notre serveur est
accessible cette adresse, puis le nom de la base que nous avons cre (Initial
Catalog=basecommerce) et que nous utilisons lauthentification Windows (integrated
security=True). Le reste permet de donner des informations de description du modle.
Enfin, nous indiquons que nous utilisons les mthodes dEntity Framework pour laccs
aux donnes, travers le nom du type System.Data.EntityClient.
Bref, beaucoup de ces informations sont issues de la chaine de connexion gnre par
Visual C# Express, nous avons simplement chang le mode de connexion pour quil
corresponde nos besoins.
Maintenant, nous allons pouvoir accder la chaine de connexion avec le
ConfigurationManager que nous connaissons bien dsormais :
string chaineConnexion =
ConfigurationManager.ConnectionStrings["NotreBaseDeDonnees"].ConnectionString;

(Vous naurez bien sr pas oubli de rfrencer lassembly System.Configuration).


Retournons dans notre designer et cliquons dessus pour observer les proprits du modle.
Nous allons modifier le nom du conteneur dentits pour y mettre un nom un peu plus
parlant, savoir BaseDeDonnees :

Cest le point dentre de notre accs aux donnes. Il sagit en fait dune classe qui a t
gnre par le designer dEntity Framework.
Une classe gnre ? Ou a ?
Vous la trouverez dans lexplorateur de solutions en dpliant le fichier
ModelCommerce.edmx. Il sagit dun fichier intitul ModelCommerce.Designer.cs qui contient
la dfinition des classes gnres et toute la logique permettant daccder la base de
donnes. Nous pouvons louvrir, mais le code est assez verbeux et nous risquons de nous
perdre. Faisons confiance Visual C# Express, nous allons utiliser son code gnr les
yeux ferms. Notez quand mme que le code gnr possde des classes prfixes par le

mot-cl partial. Je vais y revenir plus loin.


Vous ne vous en rendez peut-tre pas encore compte, mais loutil dORM Entity
Framework nous simplifie normment la tche (et je pse mon mot, normment) car
non seulement il gnre toutes les classes reprsentant les donnes en base (comme la
classe Produit ou la classe Rayon) mais il soccupe galement de nous simplifier la
cration, la lecture ou la modification des donnes en base.
Si nous avions d le faire la main comme ctait le cas avant lutilisation dORMs, cela
aurait mrit une centaine de pages supplmentaires dexplications et de code
comprendre. L, nous nous positionnons en tant quutilisateur de ces classes gnres et
vous allez voir que cest facile utiliser et vous nimaginez pas le plaisir que cest de
constater que lORM a travaill pour nous.
Nous pouvons dsormais instancier la classe BaseDeDonnees gnre et lui passant en
paramtre la chaine de connexion :
string chaineConnexion =
ConfigurationManager.ConnectionStrings["NotreBaseDeDonnees"].ConnectionString;
BaseDeDonnees baseDeDonnees = new BaseDeDonnees(chaineConnexion);

Vous voil connects la base de donnes.


Nous allons pouvoir utiliser les objets que Visual C# Express a gnr travers cette
variable de type BaseDeDonnees, comme par exemple la proprit Rayons qui nous permet
daccder aux rayons de notre base :
foreach (Rayon rayon in baseDeDonnees.Rayons)
{
Console.WriteLine("{0} ({1})", rayon.Nom, rayon.Description);
}

Ici, nous pouvons parcourir la liste des rayons avec un foreach car la proprit Rayons est
du type ObjectSet<> qui implmente IEnumerable<>.
Ce qui donne :
Salon (Tout ce qu'on trouve dans un salon)
Cuisine (Venez dcouvrir l'univers de la cuisine)
Dormir ()
Hi-Tech (Les produits hi-tech)
Vtements ()

De mme, nous pouvons parcourir tous les produits grce la proprit Produits :
foreach (Produit produit in baseDeDonnees.Produits)
{
Console.WriteLine("{0} : {1}", produit.Nom, produit.Prix);
}

Ce qui donne :
Tl HD : 299
Console de jeux : 150
Canap : 400
Cuisinire : 280
Bouilloire : 19
Lit 2 places : 149
Pull : 40
T-shirt : 20
Pyjama : 15
Tablette PC : 350
Smartphone : 320

Et ce qui est formidable, cest qutant donn que la proprit Produits est numrable,
nous allons pouvoir y faire toutes les requtes LINQ que nous le souhaitons, par exemple :

IEnumerable<Produit> produits = from produit in baseDeDonnees.Produits


where produit.Prix > 150
orderby produit.Prix descending
select produit;
foreach (Produit produit in produits)
{
Console.WriteLine("{0} : {1}", produit.Nom, produit.Prix);
}

Nous obtenons tous les produits dont le prix est suprieur 150, tris par prix dcroissant :
Canap : 400
Tablette PC : 350
Smartphone : 320
Tl HD : 299
Cuisinire : 280

Pratique !
Tout le SQL ncessaire pour renvoyer cette liste de produits filtre a t gnr par Entity
Framework. Nous navons rien faire dautre que dutiliser le C#.
Et voil. Avouez que cest quand mme super simple, non ?
Avouez galement que, si vous avez lhabitude de tout faire la main dans un autre
langage de programmation, vous tes merveills.
Jexagre peut-tre un peu, mais Entity Framework nous fait gagner un temps
considrable au dveloppement mais galement tout au long de la vie de lapplication.
Merci lui de nous avoir gnr tout le code adquat. propos de gnration de code,
souvenez-vous que les classes gnres sont partielles. Nous en avons dj parl dans le
chapitre ddi, mais je vous rappelle le but ici. Il sagit de permettre dajouter des
fonctionnalits la classe sans avoir modifier le fichier ModelCommerce.Designer.cs. En
effet, chaque fois que nous faisons une modification sur notre modle (ajout dentit,
changement de nom, etc.), il re-gnre toutes les classes de ce fichier. Si nous avions
modifi des choses la main dedans, elles vont disparatre Le mot-cl partial nous
offre lopportunit dajouter des fonctionnalits la classe depuis un autre fichier.
Nous pouvons en profiter pour rajouter nos propres mthodes, par exemple une mthode
qui renvoie les produits dont le prix est suprieur un prix pass en paramtre. Il suffit de
dclarer une classe partielle du mme nom que la classe BaseDeDonnees, situe dans le
mme espace de nom et de rajouter des mthodes. Par exemple :
public partial class BaseDeDonnees
{
public IEnumerable<Produit> ProduitsPlusCherQue(decimal prix)
{
return from produit in Produits
where produit.Prix > prix
select produit;
}
}

Nous pourrons donc utiliser cette mthode de cette faon :


foreach (Produit produit in baseDeDonnees.ProduitsPlusCherQue(200))
{
Console.WriteLine("{0} : {1}", produit.Nom, produit.Prix);
}

Ce qui donnera :
Tl HD : 299
Canap : 400
Cuisinire : 280
Tablette PC : 350
Smartphone : 320

Remarquons que chaque Rayon possde galement une proprit Produits, cest la
proprit de navigation que nous avons renomme prcdemment. Entity Framework a
donc compris quil y avait une relation entre les rayons et les produits et il permet
daccder aux produits qui font partie du rayon, grce cette proprit. Ainsi, nous
pouvons crire un code, comme le suivant, qui accde la proprit Produits dun rayon
et permet dafficher la liste de tous les produits de chaque rayon :
foreach (Rayon rayon in baseDeDonnees.Rayons)
{
Console.WriteLine("{0} ({1})", rayon.Nom, rayon.Description);
foreach (Produit produit in rayon.Produits)
{
Console.WriteLine("\t{0} : {1}", produit.Nom, produit.Prix);
}
}

Sauf quici nous rencontrons un problme. Si nous excutons ce bout de code, nous aurons
lexception suivante :
Exception non gre : System.Data.EntityCommandExecutionException: Une erreur s'est produite lors de
l'excution de la dfinition de la commande.
Pour plus de dtails, consultez l'exception interne. ---> System.InvalidOperationException: Un
DataReader associ cette Command est dj ouvert.
Il doit d'abord tre ferm.
[]

Pourquoi un tel problme ?


En fait, cela vient de la faon dont sont rcupres les donnes. Lorsque nous accdons
la proprit Rayons, Entity Framework gnre une requte en base de donnes pour
rcuprer la liste des rayons. Puis lintrieur de la boucle, lorsque nous accdons la
proprit Produits dun rayon, il gnre nouveau une requte pour rcuprer les produits
de ce rayon. Il y a donc deux connexions la base de donnes en mme temps, et a, il ne
sait pas faire par dfaut.
Il y a plusieurs faons de corriger le problme. La premire est de changer la chaine de
connexion en rajoutant une directive permettant de prciser quon autorise laccs
multiple, savoir :
multipleactiveresultsets=True;

La chaine de connexion devient donc :


<add name="NotreBaseDeDonnees"

connectionString="metadata=res://*/ModelCommerce.csdl|res://*/ModelCommerce.ssdl|res://*/ModelCommerc
e.msl;provider=System.Data.SqlClient;provider
connection string='data source=.\SQLEXPRESS;Initial Catalog=basecommerce;integrated
security=True;multipleactiveresultsets=True;'"
providerName="System.Data.EntityClient" />

Avec ce changement, si nous excutons ce code, nous aurons :


Salon (Tout ce qu'on trouve dans un salon)
Tl HD : 299
Console de jeux : 150
Canap : 400
Tablette PC : 350
Cuisine (Venez dcouvrir l'univers de la cuisine)
Cuisinire : 280
Bouilloire : 19
Dormir ()
Canap : 400

Lit 2 places : 149


Pyjama : 15
Hi-Tech (Les produits hi-tech)
Tl HD : 299
Console de jeux : 150
Tablette PC : 350
Smartphone : 320
Vtements ()
Pull : 40
T-shirt : 20
Pyjama : 15

La deuxime solution est de faire en sorte que la premire requte soit termine avant
lexcution des suivantes. Pour cela, il suffit de forcer lvaluation de la requte en
utilisant par exemple un ToList() :
foreach (Rayon rayon in baseDeDonnees.Rayons.ToList())
{
Console.WriteLine("{0} ({1})", rayon.Nom, rayon.Description);
foreach (Produit produit in rayon.Produits)
{
Console.WriteLine("\t{0} : {1}", produit.Nom, produit.Prix);
}
}

Cest parce quEntity Framework bnficie de lexcution diffre, et donc le ToList()


rsout le problme en forant lexcution de la requte.
Enfin, la dernire solution est de faire en sorte que la premire requte qui charge les
rayons inclue galement le chargement des produits. Ainsi, il ny a quune seule et unique
requte qui charge tout. Cela se passe avec la mthode Include, en prcisant le nom de la
proprit de navigation charger :
foreach (Rayon rayon in baseDeDonnees.Rayons.Include("Produits"))
{
Console.WriteLine("{0} ({1})", rayon.Nom, rayon.Description);
foreach (Produit produit in rayon.Produits)
{
Console.WriteLine("\t{0} : {1}", produit.Nom, produit.Prix);
}
}

Il y a plusieurs choses remarquer l-dedans.


Nous avons vu quEntity Framework est capable daller chercher les donnes lies entre
elles grce aux proprits de navigation. Les deux premiers scnarios couverts tirent parti
de ce quon appelle le lazy loading que lon peut traduire en chargement paresseux .
Cela veut dire que cest uniquement lorsque lon accde la proprit Produits quEntity
Framework va aller lire le contenu associ en base de donnes. Ceci implique qu chaque
tentative daccs la proprit Produits dun rayon, Entity Framework va effectuer une
requte en base de donnes pour ramener les produits concerns. Cest trs bien si on fait
a une ou deux fois, mais dans notre cas au final on fait autant de requte quil y a de
rayons. Ce qui nest pas trs performant
Le troisime scnario montre lutilisation de la mthode Include qui permet de tout
rapatrier en une seule requte, ce qui est videmment plus performant.
Faites bien attention votre utilisation dEntity Framework, autant ce nest pas
trs grave pour une petite application, autant cela peut le devenir pour des
applications avec des grosses bases de donnes.
Alors, vous ne trouvez pas que la lecture en base de donnes est particulirement aise ?
Merci Entity Framework !

Notons que nous pouvons galement accder aux rayons dans lesquels sont positionns les
produits grce la proprit Rayons. Nous pourrions pourquoi pas nous en servir pour
afficher le nombre de rayons dans lesquels le produit est prsent.
Lcriture en base de donnes est tout aussi aise. Le principe est dajouter des valeurs
notre objet de base de donnes et de sauvegarder les modifications.
Pour ajouter un nouveau rayon, il suffit dappeler la mthode AddObject disponible sur la
proprit Rayons. Il ne faudra pas oublier dappeler la mthode SaveChanges qui soccupe
dinsrer physiquement les valeurs en base de donnes. Pour crer un nouveau rayon, il
suffira dinstancier un objet Rayon, de renseigner des proprits, de crer des produits et de
les ajouter au rayon, par exemple :
Rayon rayon = new Rayon();
rayon.Nom = "Vins";
rayon.Description = "Venez dcouvrir notre slection des plus grands chteaux";
Produit produit1 = new Produit();
produit1.Nom = "Chteau ronto";
produit1.Prix = 9.99M;
produit1.Stock = 60;
produit1.UrlImage = " complter";
Produit produit2 = new Produit();
produit2.Nom = "Chteau toro";
produit2.Prix = 15;
produit2.Stock = 6;
produit2.UrlImage = " complter";
rayon.Produits.Add(produit1);
rayon.Produits.Add(produit2);
baseDeDonnees.Rayons.AddObject(rayon);
baseDeDonnees.SaveChanges();

Ainsi, si nous raffichons la liste des rayons, nous pourrons voir un rayon de plus
contenant des produits en plus
Salon (Tout ce qu'on trouve dans un salon)
Tl HD : 299
Console de jeux : 150
Canap : 400
Tablette PC : 350
Cuisine (Venez dcouvrir l'univers de la cuisine)
Cuisinire : 280
Bouilloire : 19
Dormir ()
Canap : 400
Lit 2 places : 149
Pyjama : 15
Hi-Tech (Les produits hi-tech)
Tl HD : 299
Console de jeux : 150
Tablette PC : 350
Smartphone : 320
Vtements ()
Pull : 40
T-shirt : 20
Pyjama : 15
Vins (Venez dcouvrir notre slection des plus grands chteaux)
Chteau ronto : 10
Chteau toro : 15

De mme, si vous allez voir en base de donnes, vous aurez bien les nouveaux lments :

On observe la cration dun nouveau rayon, de deux nouveaux produits et nous avons bien
dans la table de relation les nouveaux produits relis au nouveau rayon.
Il est galement possible dajouter un produit un rayon. Nous pouvons le faire de deux
manires diffrentes. La premire est dajouter un Produit directement dans la collection
Produits dun rayon, Il sera directement ajout dans le rayon de notre choix. La deuxime
est dajouter un produit dans la collection Produits et si nous voulons quil soit prsent
dans un rayon, il faudra que sa proprit Rayons contienne les rayons dans lesquels nous
souhaitons ajouter le produit.
Voyons la premire mthode :
Rayon rayon = baseDeDonnees.Rayons.First(r => r.Nom == "Vins");
Produit produit = new Produit();
produit.Nom = "Chateau pinire";
produit.Prix = 12.50M;
produit.Stock = 40;
produit.UrlImage = "vin.jpg";
produit.Rayons.Add(rayon);
baseDeDonnees.SaveChanges();

Nous commenons par rcuprer un rayon, puis nous instancions un objet de type Produit.
Enfin, nous faisons le lien entre le produit et le rayon en ajoutant le rayon la collection
Rayons de notre objet produit. Comme dhabitude, la mthode SaveChanges() permet de
faire persister les informations.
La seconde mthode est un peu plus simple apprhender, il suffit dinstancier un objet
Produit et de lajouter la collection Produits dun rayon :
Rayon rayon = baseDeDonnees.Rayons.First(r => r.Nom == "Vins");
Produit produit = new Produit();
produit.Nom = "Chateau rro";

produit.Prix = 3.20M;
produit.Stock = 10;
produit.UrlImage = "vin1.jpg";
rayon.Produits.Add(produit);
baseDeDonnees.SaveChanges();

Dans les deux cas, Entity Framework arrive faire le lien entre un rayon et un produit. Le
chteau pinire et le chteau rro ont bien t rajout
Vous pouvez galement vrifier en base de donnes que la relation entre le rayon et le
produit a bien t faite.
Il est galement possible de modifier des enregistrements en base de donnes. Le principe
est de modifier llment concern dans lobjet de base de donnes et dappeler la
mthode SaveChanges() :
Rayon rayon = baseDeDonnees.Rayons.First(r => r.Nom == "Vins");
rayon.Description = "Les meilleurs vins";
baseDeDonnees.SaveChanges();

Enfin, nous pouvons aussi supprimer des donnes en base. Le principe est le mme que
pour lajout. Nous appelons une mthode qui soccupe de la suppression et nous appelons
la mthode SaveChanges.
Par contre, il faut faire attention lintgrit des donnes. On ne peut pas supprimer un
rayon qui a des produits dedans. Il faut commencer par retirer la relation entre les produits
et le rayon :
Rayon rayon = baseDeDonnees.Rayons.First(r => r.Nom == "Vins");
rayon.Produits.Clear();
baseDeDonnees.DeleteObject(rayon);
baseDeDonnees.SaveChanges();

Le fait dappeler la mthode Clear() sur les produits du rayon vide le rayon de ses
produits. Il ny a donc plus de produits dans ce rayon, mais les produits existent toujours
en base de donnes car ils nont pas t supprims physiquement. Cest important car ces
produits peuvent galement tre prsents dans dautres rayons, nous ne pouvons donc pas
les supprimer.
Ensuite, on utilise la mthode DeleteObject pour supprimer un lment de la base de
donnes. Ici, nous supprimons le rayon et nous validons les modifications avec
SaveChanges().
Notez que si nous navions pas vid le rayon de ses produits, la suppression du rayon
aurait t impossible car comme il existe une relation entre les produits et les rayons et
que notre base de donnes possde une contrainte dintgrit (la cl trangre) entre les
produits et le rayon, la suppression aurait provoqu une erreur. En effet, la table
RayonProduit contiendrait un identifiant de rayon qui nexisterait plus. Cest impossible !
La contrainte de la cl trangre est l pour nous assurer que nous ne faisons pas
nimporte quoi dans la base de donnes et quelle est toujours cohrente. Si nous lavions
fait, nous aurions eu lexception suivante :
Exception non gre : System.Data.UpdateException: Une erreur s'est produite lors de la mise jour
des entres.
Pour plus d'informations, consultez l'exception interne. ---> System.Data.SqlClient.SqlException:
L'instruction DELETE est en conflit avec la contrainte REFERENCE "FK_RayonProduit_Rayon".
Le conflit s'est produit dans la base de donnes "basecommerce", table "dbo.RayonProduit", column
'Rayons_ID'.
L'instruction a t arrte. []

Remarquez que nous aurons dsormais des produits orphelins. Tout dpend de ce que lon

veut faire dsormais. Souhaitons-nous quils soient supprims galement vu quils ne sont
plus dans aucun rayon ? Souhaitons-nous quils restent prsents pour pouvoir les ajouter
ultrieurement un autre rayon ?
a, cest vous qui dcidez . Maintenant que vous savez supprimer des objets, vous
pouvez faire comme bon vous semble.
Notons avant de terminer quil est tout fait possible de faire plusieurs ajouts,
modifications ou suppressions en mme temps. Il suffira de terminer toutes les instructions
par la mthode SaveChanges() qui sarrangera pour tout faire persister.
En rsum

Entity Framework est loutil de mapping objet relationnel de Microsoft permettant de


travailler sur une base de donnes relationnelle avec une approche oriente objet.
Entity Framework est capable de modliser des donnes et de gnrer les tables
correspondantes en base de donnes sans quil soit ncessaire de matriser le SQL.
Il simplifie grandement la lecture et lcriture des donnes en base et tire parti, si
besoin, des mcanismes de chargement paresseux.

Chapitre 38

Les tests unitaires

Une des grandes proccupations des crateurs de logiciels est dtre certains que leur
application informatique fonctionne et surtout quelle fonctionne dans toutes les situations
possibles. Nous avons tous dj vu notre systme dexploitation planter, ou bien notre
logiciel de traitement de texte nous faire perdre les 50 pages de rapport que nous tions en
train de taper. Ou encore, un lment inattendu dans un jeu o lon arrive passer travers
un mur alors quon ne devrait pas
Bref, pour tre sr que son application fonctionne, il faut faire des tests.

Quest-ce quun test unitaire et pourquoi en faire ?


Un test constitue une faon de vrifier quun systme informatique fonctionne.
Tester son application cest bien. Il faut absolument le faire. Cest en gnral une pratique
plutt laisse de ct et rbarbative. Il y a plusieurs faons de faire des tests. Celle qui
semble la plus naturelle est celle qui se fait manuellement. On lance son application, on
clique partout, on regarde si elle fonctionne.
Celle que je vais prsenter ici constitue une pratique automatise visant sassurer que des
bouts de code fonctionnent comme il faut et que tous les scnarios dun dveloppement
sont couverts par un test. Lorsque les tests couvrent tous les scnarios dun code, nous
pouvons assurer que notre code fonctionne. De plus, cela permet de faire des oprations de
maintenance sur le code tout en tant certain que ce code naura pas subi de rgressions.
De la mme faon, les tests sont un filet de scurit lorsquon souhaite refactoriser son
code ou loptimiser.
Cela permet dans certains cas davoir un guide pendant le dveloppement, notamment
lorsquon pratique le TDD. Le Test Driven Development (TDD) (ou en Franais
dveloppement pilot par les tests) est une mthode de dveloppement de logiciel qui
prconise dcrire les tests unitaires avant dcrire le code source dun logiciel. Nous y
reviendrons ultrieurement.
Un test est donc un bout de code qui permet de tester un autre code.
En gnral, un test se dcompose en trois partie, suivant le schma AAA , qui
correspond aux mots anglais Arrange, Act, Assert , que lon peut traduire en franais
par Arranger, Agir, Auditer.

Arranger : Il sagit dans un premier temps de dfinir les objets, les variables
ncessaires au bon fonctionnement de son test (initialiser les variables, initialiser les
objets passer en paramtres de la mthode tester, etc.).
Agir : Ensuite, il sagit dexcuter laction que lon souhaite tester (en gnral,
excuter la mthode que lon veut tester, etc.)
Auditer : Et enfin de vrifier que le rsultat obtenu est conforme nos attentes.

Notre premier test


Imaginons que nous voulions tester une mthode toute simple qui fait laddition entre
deux nombres, par exemple la mthode suivante :
public static int Addition(int a, int b)
{
return a + b;
}

Faire un test consiste crire des bouts de code permettant de sassurer que le code
fonctionne. Cela peut-tre par exemple :
static void Main(string[] args)
{
// arranger
int a = 1;
int b = 2;
// agir
int resultat = Addition(a, b);
// auditer
if (resultat != 3)
Console.WriteLine("Le test a rat");
}

Ici, le test passe bien, ouf !


Pour tre complet, le test doit couvrir un maximum de situations, il faut donc tester notre
code avec dautres valeurs, et ne pas oublier les valeurs limites :
static void Main(string[] args)
{
int a = 1;
int b = 2;
int resultat = Addition(a, b);
if (resultat != 3)
Console.WriteLine("Le test a rat");
a = 0;
b = 0;
resultat = Addition(a, b);
if (resultat != 0)
Console.WriteLine("Le test a rat");
a = -5;
b = 5;
resultat = Addition(a, b);
if (resultat != 0)
Console.WriteLine("Le test a rat");
}

Voil pour le principe. Ici, nous considrons avoir crit suffisamment de tests pour nous
assurer que cette mthode est bien fonctionnelle.
Bien sr, cette mthode tait par dfinition fonctionnelle, mais il est important de prendre
le rflexe de tester des fonctionnalits qui sont dterminantes pour notre application.
Voyons maintenant comment nous pourrions tester une mthode avec lapproche TDD.
Pour rappel, lors dune approche TDD, le but est de pouvoir faire un dveloppement

partir des cas de tests pralablement tablis par la personne qui exprime le besoin ou
suivant les spcifications fonctionnelles.
Imaginons que nous voulions tester une mthode qui calcule la factorielle dun nombre.
Nous savons que la factorielle de 0 vaut 1, la factorielle de 1 vaut 1. Commenons par
crire les tests :
static void Main(string[] args)
{
int valeur = 0;
int resultat = Factorielle(valeur);
if (resultat != 1)
Console.WriteLine("Le test a rat");
valeur = 1;
resultat = Factorielle(valeur);
if (resultat != 1)
Console.WriteLine("Le test a rat");
}

Le code ne compile pas ! Forcment, nous navons pas encore cr la mthode


Factorielle. Cest la premire tape. La suite de la mthode est de faire en sorte que le
test compile, mais il chouera puisque la mthode nest pas encore implmente :
public static int Factorielle(int a)
{
throw new NotImplementedException();
}

Il faudra ensuite crire le code minimal qui servira faire passer nos deux tests. Cela peuttre :
public static int Factorielle(int a)
{
return 1;
}

Si nous excutons nos tests, nous voyons que cette mthode est fonctionnelle car ils
passent tous. La suite de la mthode consiste refactoriser le code, loptimiser. Ici, il ny
a rien faire tellement cest simple.
On se rend compte par contre quon na pas couvert normment de cas de tests, juste des
tests avec 0 et 1 cest un peu lger Nous savons que la factorielle de 2 vaut 2, la
factorielle de 3 vaut 6, la factorielle de 4 vaut 24, Continuons crire des tests. (Il faut
bien sr garder les anciens tests afin dtre sr quon couvre un maximum de cas) :
static void Main(string[] args)
{
int valeur = 0;
int resultat = Factorielle(valeur);
if (resultat != 1)
Console.WriteLine("Le test a rat");
valeur = 1;
resultat = Factorielle(valeur);
if (resultat != 1)
Console.WriteLine("Le test a rat");
valeur = 2;
resultat = Factorielle(valeur);
if (resultat != 2)
Console.WriteLine("Le test a rat");
valeur = 3;
resultat = Factorielle(valeur);
if (resultat != 6)
Console.WriteLine("Le test a rat");

valeur = 4;
resultat = Factorielle(valeur);
if (resultat != 24)
Console.WriteLine("Le test a rat");
}

Et nous pouvons crire une mthode Factorielle qui fait passer ces tests :
public static int Factorielle(int a)
{
if (a == 2)
return 2;
if (a == 3)
return 6;
if (a == 4)
return 24;
return 1;
}

Lanons les tests, nous voyons que tout est OK. Cependant, nous nallons pas faire des if
en dclinant tous les cas possible, il faut donc repasser par ltape damlioration et de
refactorisation du code, afin dviter les redondances de code, damliorer les algorithmes,
etc. Cette opration devient sans risque puisque le test est l pour nous assurer que la
modification que lon vient de faire est sans rgression, si le test passe toujours bien sr
Nous voyons que nous pouvons amliorer le code en utilisant la vraie formule de la
factorielle :
public static int Factorielle(int a)
{
int total = 1;
for (int i = 1 ; i <= a ; i ++)
{
total *= i;
}
return total;
}

Ce qui permet dillustrer que par exemple la factorielle de 5 est gale 1*2*3*4*5.
Relanons nos tests, ils passent tous. Parfait. Nous sommes donc certains que notre
changement de code na pas altr la fonctionnalit car les tests continuent de passer.
On peut mme rajouter des tests pour le plaisir, comme la factorielle de 10, histoire
davoir quelque chose dun peu plus grand :
valeur = 10;
resultat = Factorielle(valeur);
if (resultat != 3628800)
Console.WriteLine("Le test a rat");

Est-ce que cette mthode est optimisable ? Srement.


Est-ce quil y a un risque optimiser cette mthode ? Aucun ! En effet, nos tests nous
garantissent que sils continuent passer, alors une optimisation nentraine pas de
rgression dans la fonctionnalit.
On sait par exemple quil y a un autre moyen pour calculer une factorielle. Par exemple,
pour calculer la factorielle de 5, il suffit de multiplier 5 par la factorielle de 4. Pour
calculer la factorielle de 4, il faut multiplier 4 par la factorielle de 3, et ainsi de suite
jusqu arriver 1 Bref, pour obtenir une factorielle on peut se servir du rsultat de la
factorielle du nombre prcdent. Ce qui peut scrire :
public static int Factorielle(int a)
{
if (a <= 1)
return 1;
return a * Factorielle(a - 1);

Ici la mthode Factorielle est une mthode rcursive, cest--dire quelle sappelle ellemme. Cela nous permet de dindiquer que la factorielle dun nombre correspond ce
nombre multipli par la factorielle du nombre prcdent. Bien sr, il faut sarrter un
moment dans la rcursion. On sarrte ici quand on atteint le chiffre 1.
Pour sassurer que cette factorielle fonctionne bien, il suffit de relancer les tests. Tout est
OK, cest parfait !
Voil donc un exemple de TDD. Bien sr, la mthode est ici pousse au maximum pour
que vous compreniez lintrt de cette pratique. On peut gagner du temps en partant
directement sur la bonne implmentation. Mais vous verrez quil y a toujours des premiers
essais qui satisfont les tests mais quil sera possible damliorer rgulirement notre code.
Ceci devient possible grce aux tests qui nous assurent que tout continue bien
fonctionner.
La pratique du TDD dpend de la faon dont le dveloppeur apprhende sa philosophie de
dveloppement. Elle est prsente ici pour sensibiliser ce dernier cette pratique mais son
utilisation nest pas du tout obligatoire.
Voil pour les tests basiques. Cependant, utiliser une application console pour faire ses
tests, ce nest pas trs pratique, vous en conviendrez. Nous avons besoin doutils !

Le framework de test
Un framework de test est aux tests ce que lIDE est au dveloppement. Il fournit un
environnement structur permettant lexcution de test et des mthodes pour aider au
dveloppement de ceux-ci.
Il existe plusieurs frameworks de test. Microsoft dispose de son framework, mstest, qui est
disponible dans les versions payantes de Visual Studio. Son intrt est quil est fortement
intgr lIDE. Son dfaut est quil ne fonctionne pas avec les versions gratuites de
lenvironnement de dveloppement. Comme nous sommes partis dans ce tutoriel avec la
version gratuite, Visual C# Express, nous nallons pas pouvoir utiliser mstest.
Par contre, il existe dautres framework de test, gratuits, comme le trs connu NUnit.
NUnit est la version .NET du framework XUnit, qui se dcline pour plusieurs
environnements, avec par exemple PHPUnit pour le langage PHP, JUnit, pour java, etc.
Premire chose faire, installer NUnit, pour cela, rendez-vous cet emplacement pour le
tlcharger : http://www.NUnit.org/?p=download. La version que jutilise dans ce tutoriel
est la version 2.5.10.
Dmarrez linstallation :

Linstallation est en anglais, mais assez facile suivre. Cliquez sur Next pour aller
lcran suivant.
Aprs avoir accept la licence, vous pouvez choisir linstallation classique :

la fin de linstallation, nous pouvons voir que tout sest bien pass :

Une fois le framework de test install, nous pouvons crer un nouveau projet qui
contiendra une fonctionnalit tester. Je lappelle MaBibliothequeATester. Dune manire
gnrale, nous allons surtout tester des assemblys avec NUnit. Ici, je cre donc un projet
de type bibliothque de classes. Ce projet ne sera donc pas excutable, car il ne sagit pas
dune application console.
Et dedans, je vais pouvoir crer une classe utilitaire, disons Math, qui contiendra notre
fameuse mthode de calcul de factoriel :
public static class Math
{
public static int Factorielle(int a)
{
if (a <= 1)
return 1;
return a * Factorielle(a - 1);
}
}

Puis ajoutons un nouveau projet de type bibliothque de classes o nous allons mettre nos
tests unitaires, appelons le MathTests.Unit. Ce nest pas une norme absolue, mais je vous
conseille de suffixer vos projets de test avec .Unit, ce qui permet de les identifier
facilement.
Les tests doivent se mettre dans une classe spciale. Ici aussi, pas de rgle de nommage
obligatoire, mais il est intressant davoir une norme pour facilement sy retrouver. Je
vous propose de nommer les classes de tests en commenant par le nom de la classe que
lon doit tester, suivie du mot Tests. Ce qui donne : MathTests.
Pour tre reconnue par le framework de test, la classe doit respecter un certain nombre de
contrainte. Elle doit dans un premier temps tre dcore de lattribut [TestFixture]. Il
sagit dun attribut qui permet NUnit de reconnaitre les classes qui contiennent des tests.
Cet attribut tant dans une assembly de NUnit, vous devez rajouter une rfrence
lassembly NUnit.framework :

et inclure lespace de nom adquat :


using NUnit.Framework;

Nous allons pouvoir crer des mthodes lintrieur de cette classe. De la mme faon,
une mthode pourra tre reconnue comme une mthode de test si elle est dcore de
lattribut [Test].
Ici aussi, il est intressant de suivre une rgle de nommage afin de pouvoir identifier
rapidement lintention de la mthode de test. Je vous propose le nommage suivant :
MethodeTestee_EtatInitial_EtatAttendu()

Par exemple, une mthode de test permettant de tester la factorielle pourrait sappeler :
[TestFixture]
public class MathTests
{
[Test]
public void Factorielle_AvecValeur3_Retourne6()
{
// test faire
}
}

Il existe plein dautres attributs que vous dcouvrirez ultrieurement. Il est temps de
passer lcriture du test et surtout la vrification du rsultat.
Pour cela, on utilise des mthodes de NUnit qui nous permette de vrifier par exemple
quune valeur est gale une autre attendue. Cela se fait grce la mthode
Assert.AreEqual() :
[Test]
public void Factorielle_AvecValeur3_Retourne6()
{
int valeur = 3;
int resultat = MaBibliothequeATester.Math.Factorielle(valeur);
Assert.AreEqual(6, resultat);
}

Elle permet de vrifier que la variable valeur vaut bien 6.


Rajoutons tant quon y est une mthode de test qui choue :

[Test]
public void Factorielle_AvecValeur10_Retourne1()
{
int valeur = 10;
int resultat = MaBibliothequeATester.Math.Factorielle(valeur);
Assert.AreEqual(1, resultat, "La valeur doit tre gale 1");
}

Il est important quune mthode de test ne soccupe de tester quun seul cas
dune unique fonctionnalit, comme illustr juste au dessus. La premire
mthode teste la fonctionnalit Factorielle pour le cas o la valeur vaut 3 et la
seconde soccupe du cas o la valeur vaut 10. Vous pouvez rajouter autant de
mthodes de tests que vous le souhaitez tant quelle sont dcore de lattribut
[Test].
Jen ai profit pour rajouter un message qui permettra dindiquer des informations
complmentaires si le test choue.
Compilons maintenant notre projet et rendez-vous dans le rpertoire dinstallation de
NUnit (par dfaut : C:\Program Files\NUnit 2.5.10\bin\net-2.0) et lancez lapplication
NUnit.exe :

La premire chose faire est de crer un nouveau projet :

Appelez-le ProjetTest par exemple. Il faut ensuite ajouter une assembly de test, allez dans
Project > Add Assembly :

Et allez pointer lassembly de tests, savoir MathTests.Unit.dll.


NUnit analyse lassembly et fait apparaitre la liste des tests qui composent notre assembly
(en se basant sur les attributs TestFixture et Test) :

Nous pouvons prsent lancer les tests en cliquant sur Run, et nous obtenons :

Ce qui nous permet de voir rapidement quil y a un test qui passe (icne verte) et un test

qui choue (icne rouge). Forcment, notre test ntait pas bon, il faut le rcrire. Nous
voyons galement quil nous indique que le rsultat attendu tait 1 alors que le rsultat
obtenu est de 3628800. Nous pouvons galement voir le message que nous avons
demand dafficher en cas derreur.
Le souci avec NUnit, cest qu partir du moment o il a charg la dll pour lancer les tests,
il nest plus possible de faire des modifications, car toute tentative de compilation
provoquera une erreur o il sera mentionn quil ne peut pas faire de modifications car le
fichier est dj utilis ailleurs. Ce qui est vrai. Ce qui va nous obliger fermer NUnit et
le r-ouvrir.
noter que dans les versions payantes de Visual Studio, nous avons la possibilit de
configurer NUnit en tant quoutil externe, ce que nous ne pouvons pas faire avec la
version gratuite. Il va falloir faire avec Tristesse de la gratuit
Nous pouvons cependant un peu tricher en dfinissant un vnement de post-compilation,
qui consiste lancer NUnit automatiquement. Pour cela, allez dans les proprits du
projet, onglet vnement de builds et tapez la commande suivante :
C:\Program Files\NUnit 2.5.10\bin\net-2.0\NUnit.exe $(TargetFileName)

Ici, nous indiquons quaprs la compilation, il va lancer le programme NUnit.exe en


prenant en paramtre le rsultat de la compilation, reprsent par la variable interne de
Visual C# Express : $(TargetFileName).
Par contre, cela veut dire que NUnit va se lancer chaque compilation, ce qui nest peuttre pas le but recherch Il faudra galement fermer NUnit avant de pouvoir faire quoi
que soit dautre.
noter que maintenant que nous savons faire de lintrospection sur les mthodes et les
attributs dune classe, nous devrions tre capables de crer une petite application qui
excute les tests automatiquement
Pour en finir avec NUnit, notons quil y a beaucoup de mthodes permettant de vrifier si
un rsultat est correct. Regardons les assertions suivantes :

bool b = true;
Assert.IsTrue(b);
string s = null;
Assert.IsNull(s);
int i = 10;
Assert.Greater(i, 6);

Elles parlent delles-mmes. La premire permet de vrifier quune condition est vraie. La
deuxime permet de vrifier la nullit dune variable. La dernire permet de vrifier que la
variable est bien suprieure une autre. noter quelles ont chacune leur pendant
(IsFalse, IsNotNull, Less). En regardant la compltion automatique, vous dcouvrirez
dautres mthodes de vrification, mais celles-ci sont globalement suffisantes.
Nous pouvons galement utiliser une syntaxe un peu plus parlante comme :
Assert.That(i, Is.EqualTo(10));

Mais cette syntaxe est peut-tre un peu plus parlante aux anglophones
Il est galement possible dutiliser un attribut pour vrifier quune mthode lve bien une
exception, par exemple :
[Test]
[ExpectedException(typeof(FormatException))]
public void ToInt32_AvecChaineNonNumerique_LeveUneException()
{
Convert.ToInt32("abc");
}

Dans ce cas, le test passe si la mthode lve bien une FormatException.


Avant de terminer, prsentons deux attributs supplmentaires : les attributs SetUp et
TearDown.
Ils permettent de dcorer des mthodes qui seront appeles respectivement avant chaque
test et aprs chaque test. Cest lendroit idal pour factoriser des initialisations ou des
nettoyages dont dpendent tous les tests.
[TestFixture]
public class MathTests
{
[SetUp]
public void InitialisationDesTests()
{
// rajouter les initialisations
}
[Test]
public void Factorielle_AvecValeur3_Retourne6()
{
// test faire
}
[TearDown]
public void NettoyageDesTests()
{
// nettoyer les variables, ...
}
}

Il existe plein dautres choses utiles dire sur NUnit, comme la description des autres
attributs, ce que je ne vais pas faire ici. Nhsitez pas aller voir sur internet des
informations plus pousses pour approfondir votre maitrise des tests.

Le framework de simulacre

Un framework de simulacre fournit un moyen de tester une mthode en lisolant du reste


du systme. Imaginons par exemple une mthode qui permet de rcuprer la mto du
jour, en allant la lire dans une base de donnes.
Nous avons ici un problme car lorsque nous excutons le test le lundi, il pleut. Quand
nous excutons le test le mardi, il fait beau, etc.
Nous avons une information qui varie au cours du temps. Il est donc difficile de tester
automatiquement que la mthode arrive bien construire la mto du jour partir de ces
informations, vu quelles varient.
Le but de ces frameworks est de pouvoir bouchonner le code dont notre dveloppement
dpend afin de pouvoir le tester unitairement, sans dpendance et isol du reste du
systme.
Cela veut dire que dans notre test, nous allons remplacer la lecture en base de donnes par
une fausse mthode qui renvoie toujours quil fait beau. Cependant, ceci doit se faire sans
modifier notre application, sinon cela na pas dintrt. Voil quoi servent ces framework
de simulacres.
Il en existe plusieurs, plus ou moins complexe. Citons par exemple Moq (prononcez
moque-you ) ou encore Moles (il y en a plein dautres).
Lintrt de Moq est quil est simple daccs, nous allons le prsenter rapidement. Il
permet de faire des choses simples et facilement. Tandis que Moles est un peu plus volu
mais plus complexe prendre en main. Vous y reviendrez sans doute ultrieurement.
Pour le tlcharger, rendez-vous sur : http://code.google.com/p/moq/downloads/list
Pas de systme dinstallation volu, il y aura juste une dll rfrencer. Ajoutez donc la
rfrence la dll moq.dll qui se trouve dans le sous-rpertoire NET40.
Ensuite, pour pouvoir bouchonner facilement une classe, elle doit implmenter une
interface. Imaginons la classe daccs aux donnes suivante :
public class Dal : IDal
{
public Meteo ObtenirLaMeteoDuJour()
{
// ici, c'est le code pour lire en base de donnes
// mais finalement, peu importe ce qu'on y met vu qu'on va bouchonner la mthode
throw new NotImplementedException();
}
}

Qui implmente linterface suivante :


public interface IDal
{
Meteo ObtenirLaMeteoDuJour();
}

Avec lobjet Meteo suivant :


public class Meteo
{
public double Temperature { get; set; }
public Temps Temps { get; set; }
}

Et lnumration Temps suivante :


public enum Temps
{
Soleil,
Pluie
}

Nous pourrons crire un test qui bouchonne lappel la mthode ObtenirLaMeteoDuJour,


qui doit normalement aller lire en base de donnes, pour renvoyer un objet la place. Pour
bien montrer ce fonctionnement, jai fait en sorte que la mthode lve une exception,
comme a, si on passe dedans a sera tout de suite visible.
La mthode de test classique devrait tre :
[Test]
public void ObtenirLaMeteoDuJour_AvecUnBouchon_RetourneSoleil()
{
IDal dal = new Dal();
Meteo meteoDuJour = dal.ObtenirLaMeteoDuJour();
Assert.AreEqual(25, meteoDuJour.Temperature);
Assert.AreEqual(Temps.Soleil, meteoDuJour.Temps);
}

Si nous excutons le test, nous aurons une exception.


Utilisons maintenant Moq pour bouchonner cet appel et le remplacer par ce que lon veut :
[Test]
public void ObtenirLaMeteoDuJour_AvecUnBouchon_RetourneSoleil()
{
Meteo fausseMeteo = new Meteo { Temperature = 25, Temps = Temps.Soleil };
Mock<IDal> mock = new Mock<IDal>();
mock.Setup(dal => dal.ObtenirLaMeteoDuJour()).Returns(fausseMeteo);
IDal fausseDal = mock.Object;
Meteo meteoDuJour = fausseDal.ObtenirLaMeteoDuJour();
Assert.AreEqual(25, meteoDuJour.Temperature);
Assert.AreEqual(Temps.Soleil, meteoDuJour.Temps);
}

On utilise lobjet gnrique Mock pour crer un faux objet du type de notre interface. On
utilise la mthode Setup travers une expression lambda pour indiquer que la mthode
ObtenirLaMeteoDuJour retournera en fait un faux objet mto. Cela se fait tout
naturellement en utilisant la mthode Returns(). Lavantage de ces constructions est que la
syntaxe claire parle delle-mme partir du moment o on connait les expressions
lambdas.
On obtient ensuite une instance de notre objet grce la proprit Object et cest ce faux
objet que nous pourrons comparer nos valeurs.
Bien sr, ici, ce test na pas grand intrt. Mais il faut le voir un niveau plus gnral.
Imaginons que nous ayons besoin de tester la fonctionnalit qui met en forme cet objet
mto rcupr de la base de donnes ou bien lalgorithme qui nous permet de faire des
statistiques sur ces donnes mtos L, nous sommes srs de pouvoir nous baser sur une
valeur connue de la dpendance la base de donnes. Cela permettra galement de
dcliner tous les cas possibles en changeant la valeur du bouchon et de faire les tests les
plus exhaustifs possibles.
Nous pouvons faire la mme chose avec les proprits. Imaginons la classe suivante dont
la proprit valeur retourne un nombre alatoire :
public interface IGenerateur
{
int Valeur { get; }
}
public class Generateur : IGenerateur
{
private Random r;
public Generateur()
{
r = new Random();
}

public int Valeur


{
get
{
return r.Next(0, 100);
}
}
}

Nous pourrions avoir besoin de bouchonner cette proprit pour quelle renvoie un
nombre connu lavance. Cela se fera de la mme faon :
Mock<IGenerateur> mock = new Mock<IGenerateur>();
mock.Setup(generateur => generateur.Valeur).Returns(5);
Assert.AreEqual(5, mock.Object.Valeur);

Ici, la proprit Valeur renverra toujours 5 en se moquant bien du gnrateur de nombre


alatoire
Je marrte l pour laperu de ce framework de simulacre. Nous avons pu voir quil
pouvait facilement bouchonner des dpendances nous permettant de faciliter la mise en
place de nos tests unitaires. Rappelez-vous, pour quun test soit efficace, il doit pouvoir se
concentrer sur un point prcis du code sans tre embt par les dpendances ventuelles
qui peuvent perturber ltat du test un instant t.
En rsum

Les tests unitaires sont un moyen efficace de tester des bouts de code dans une
application afin de garantir son bon fonctionnement.
Ils sont un filet de scurit permettant de faire des oprations de maintenance, de
refactoring ou doptimisation sur le code.
Les frameworks de tests unitaires sont en gnral accompagns doutils permettant de
superviser le bon droulement des tests et la couverture de tests.

Chapitre 39

Rsum des diffrents types dapplications pouvant


tre dveloppes avec le C# (indit)

Vous savez quoi ? Avec le C# on peut crer autre chose que des applications consoles !!
Dingue non ? Des applications avec des boutons et des menus, ou des sites web et mme
des jeux.
Ces bonnes nouvelles sonnent la fin de nos applications console toutes noires et toutes
tristes mais grce a, cest le dbut dune toute nouvelle aventure.
Dans ce chapitre, je vais vous indiquer rapidement ce quon peut faire dautre avec le C#.
Vous retrouverez en tlchargement web une version plus complte de chaque chapitre.
Nous y trouverons une introduction un peu plus dtaille mais notez quand mme que
chaque paragraphe de ce chapitre ncessite un livre entier pour tre correctement trait.
Aussi, je vous y montrerai simplement ce que lon peut faire, histoire davoir un premier
point de dpart et de pouvoir dcider vers quoi orienter vos prochaines lectures.
Jespre que vous me pardonnerez par avance de ce trop bref aperu mais je souhaite
nanmoins piquer votre curiosit pour vous donner envie daller explorer tous ces
nouveaux mondes qui souvrent vous.
Il y aura surement des notions que vous ne comprendrez pas compltement en fonction
des thmes abords. Il faudra aller jeter un il complmentaire sur internet ou attendre un
prochaine livre .

Crer une application Windows avec WPF


Les applications Windows sont ce quon appelle des applications clients lourds. Elles
sexcutent directement depuis notre systme dexploitation. Nous pouvons crer toutes
sortes dapplications, comme un traitement de texte, une calculatrice, etc.
Nous avons dj cr une application Windows travers notre projet console, sauf que
nous tions rapidement limits. Avec WPF, nous allons pouvoir crer des applications
graphiques avec des boutons, des menus, Bref, tout ce qui compose une application
habituelle.
WPF signifie Windows Presentation Foundation. Il sagit dune bibliothque permettant
de raliser des applications graphiques. Ces applications sont dites vnementielles car
elles ragissent des vnements (clic sur un bouton, redimensionnement de la fentre,
saisie de texte, etc.)

Pour construire des applications WPF, nous aurons besoin de deux langages. Un langage
de prsentation qui va permettre de dcrire le contenu de notre fentre : le
XAML(prononcez xamelle) et du C# qui va permettre de faire tout le code mtier.
Dans le chapitre en ligne, nous verrons comment raliser une petite application du style
Hello World qui ressemblera :

Puis nous verrons un premier aperu des notions de liaison de donnes, appel binding
.

Crer une application web avec ASP.NET


ASP.NET cest la plateforme de Microsoft pour raliser des applications web. Cest un
peu comme PHP, sauf que, vous vous en doutez, ASP.NET sappuie massivement sur le
framework .NET. Et tout comme WPF, il sagit de bibliothques qui vont permettre de
raliser facilement son site web.
Dans le chapitre en ligne nous dcouvrirons quil existe deux ASP.NET : lASP.NET
WebForms et lASP.NET MVC.
ASP.NET WebForms cest tout un mcanisme qui permet de faciliter la cration dune
application web en faisant comme si ctait une application Windows. Cest--dire que le
framework soccupe de grer toute la persistance dinformations entre les diffrents tats
des pages alors que le protocole http est sans tat. Il permet aussi de travailler avec une
approche vnementielle, comme une application Windows. Le premier but dASP.NET
WebForms tait de faire en sorte que les personnes qui avaient dj fait du dveloppement
Windows (avec des langages comme le Visual Basic ou autre) puisse facilement faire du
dveloppement web, dans un contexte qui leur serait familier.
ASP.NET MVC est plus rcent et offre une approche o le dveloppeur doit bien connaitre
tous les mcanismes du web. Il offre galement une plus grande maitrise sur le rendu du
site web. Enfin, il intgre par dfaut tous les mcanismes prouvs du fameux patron de
conception (design pattern) MVC.
On ne peut pas dire quASP.NET WebForms soit mieux ou moins bien quASP.NET
MVC. Il sagit de deux faons diffrentes de crer des sites web. Chacune a ses avantages
et ses inconvnients. Par contre, les deux se basent sur un socle commun qui est le cur
dASP.NET.
Dans les deux formes dASP.NET, nous raliserons une petite application web du genre
Hello world :

Crer une application client riche avec Silverlight


Nous avons vu quil existait des applications clientes lourdes, comme les applications
console ou les applications WPF. Nous avons galement vu des applications web, avec
ASP. NET WebForms ou ASP.NET MVC. Il existe quelque chose entre les deux, ce sont
les applications dites client riche . Ce sont des applications qui ressemblent des
applications lourdes, mais qui sexcutent lintrieur dun navigateur internet plutt que
directement au niveau du systme dexploitation.
Vous connaissez surement le trs clbre flash , trs populaire grce la multitude de
jeux disponibles sur internet. Microsoft possde galement des bibliothques permettant
de raliser des applications clients riches : Silverlight.
Une application cliente riche sexcute donc directement dans un navigateur internet,
comme Internet Explorer, Firefox ou Chrome. Ces applications sexcutent dans un plugin
du navigateur. Pour excuter des applications flash ou des applications Silverlight, le
navigateur devra possder le plugin adquat. Du fait quelles sexcutent dans un
navigateur, ces applications ont quelques restrictions. Elles ne peuvent par dfaut pas
accder au contenu du disque dur de lutilisateur, ce qui est finalement plutt pas mal pour
une application disponible directement sur internet. Elles sexcutent uniquement dans la
zone mmoire du navigateur, une espce de bac sable dont on ne peut pas schapper et
o il est impossible daccder aux ressources directes de lordinateur sur lequel sexcute
lapplication, au contraire des applications WPF par exemple.
Ces applications clientes riches ressemblent normment aux applications clientes lourdes
avec quelques restrictions. Silverlight est donc une espce de WPF allg qui ne garde que
lessentiel de lessentiel. Nous serons donc capables de raliser des applications
sexcutant dans notre navigateur grce au C# et au XAML.
Dans le chapitre en ligne, nous allons encore raliser une petite application type Hello
World qui ressemblera :

Mais comme il sagit essentiellement de XAML que nous aurons vu dans la partie sur
WPF, nous nous attarderons un peu plus sur une autre faon de prsenter les donnes.

Le graphisme et les jeux avec XNA


Et oui, il est aussi possible de raliser des jeux avec le C#. Mme si tout ce quon a vu
auparavant permet la ralisation dapplications de gestions, Microsoft dispose aussi de
tout ce quil faut pour raliser des jeux, grce XNA.
XNA est un ensemble de bibliothques permettant de crer des applications graphiques
mais cest galement un outil permettant dintgrer des contenus facilement dans des jeux
(comme des images ou des musiques). Avec XNA, il est possible de faire des jeux qui
fonctionnent sous Windows mais galement sous Xbox ou sur les tlphones Windows.
Voyons rapidement comment fonctionne XNA.
Dans le chapitre en ligne, nous allons voir comment faire bouger une balle lcran :

Crer une application mobile avec Windows Phone 7


Le C# nous permet galement de dvelopper des applications pour tlphones mobiles
quips du systme dexploitation Windows Phone. Cest un point trs important car il est
possible de mutualiser beaucoup de choses que nous avons apprises pour les transposer
dans le monde en plein essor des smartphones.
Ce qui est intressant avec les applications Windows Phone cest que nous rutilisons le
savoir que nous avons pu acqurir dans les autres types dapplications C#, car pour
raliser des applications de gestion, nous allons utiliser Silverlight et pour raliser des
jeux, nous utiliserons XNA.
Dans le chapitre en ligne, nous nallons pour une fois pas refaire le traditionnel Hello
World. Nous allons dcouvrir lmulateur. Puis, comme nous aurons faire avec du C# et
du XAML, nous montrerons simplement une spcificit li lorientation du tlphone :

Puis nous verrons avec XNA comment crire du texte lcran avec une police
particulire :

Crer un service web avec WCF


Avec le C# il est galement trs facile de crer des services web. Un service web permet
en gnral daccder des fonctionnalits depuis nimporte o, travers internet. Citons
par exemple les services web dAmazon qui nous permettent de rcuprer des
informations sur des livres, ou encore des services web qui permettent dobtenir la mto
du jour. Bref, cest un moyen de communication entre applications htrognes
potentiellement situes des emplacements physiques trs loigns.
En imaginant que nous ayons galement besoin dexposer des mthodes lextrieur, pour
quun fournisseur vienne consulter ltat de nos commandes ou quun client puisse suivre

lavance de la sienne , nous allons devoir crer un service web.


Le framework .NET dispose de tout un framework pour cela qui sappelle WCF :
Windows Communication Foundation.
Un service web est une espce dapplication web qui rpond des requtes permettant
dappeler une mthode avec des paramtres et de recevoir en rponse le retour de la
mthode. Lintrt dun service web est quil est indpendant de la technologie. Mme si
on crit un service web avec du C#, il doit tre appelable par du java ou du PHP.
Dans le chapitre en ligne, nous allons montrer comment crer un service web qui sera
appel par un client de test et par une application console :

Le C# et le framework .NET permettent de raliser des applications de toutes sortes


Diffrents types dapplications peuvent tre dveloppes grce diffrentes versions
express de Visual Studio.

You might also like