Professional Documents
Culture Documents
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.
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
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.
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.
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.
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
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.
(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
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
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 !
Une fois lexcutable tlcharg, il ne reste plus qu le lancer et linstallation dmarre :
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 :
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#.
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
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
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
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 :
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 :
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
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.
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.
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,
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.
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;
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
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;
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.
Il est important de stocker des donnes dans des variables ayant le bon type.
Description
byte
Entier de 0 255
short
int
long
float
double
decimal Nombre dcimal convenant particulirement aux calculs financiers (en raison de ses nombres significatifs
aprs la virgule)
char
Reprsente un caractre
string
bool
Vous verrez plus loin quil existe encore dautres types dans le framework .NET et quon
peut galement construire les siens.
ou encore
int age1 = 20;
int age2 = 30;
int moyenne = (age1 + age2) / 2;
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);
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.
affichera :
Console.WriteLine(phrase);
Console.WriteLine("Passe\n\nla\nligne\n\n\n");
affichera :
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
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.
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");
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
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;
}
Chapitre 6
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.
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
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 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
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");
}
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);
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.
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 "";
}
Ainsi, si la variable prenom vaut inconnu , alors nous quittons la mthode Bonjour
Chapitre 8
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" };
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 :
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);
produira :
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.
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
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.
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" };
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.
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
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 :
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;
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.
et
Console.WriteLine(Environment.UserName);
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 que vous compilez nouveau, vous pouvez voir que ConformanceLevel est dsormais
soulign en rouge, signe quil y a un problme.
Ici, nous avons plusieurs onglets (selon la version de Visual Studio que vous possdez,
vous aurez peut-tre une prsentation lgrement diffrente).
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 :
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
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.
Traduction
Sunday
Dimanche
Monday
Lundi
Tuesday
Mardi
Wednesday Mercredi
Thursday
Jeudi
Friday
Vendredi
Saturday
Samedi
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);
}
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 :
(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]);
}
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 !";
}
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
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++;
}
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.
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
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 :
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;
}
m5 vaut 5
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 :
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
Chapitre 13
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.
Description
short
int
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;
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;
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;
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.
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
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);
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
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.
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);
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
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.
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.
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);
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.
CalculSommeIntersection() vous pourrez voir dans la pile des appels que vous tes dans
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 permet dexcuter son application pas pas et de suivre son volution.
Chapitre 16
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
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 :
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 :
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.
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
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 :
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
Chapitre 18
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.
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");
}
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
Chapitre 19
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 !
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,
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
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.
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
Chapitre 20
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#.
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);
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();
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 :
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 !");
}
}
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
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
protected
private
internal
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 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;
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);
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;
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;
}
}
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.
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; }
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
Chapitre 21
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");
}
}
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);
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;
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;
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")
{
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;
}
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
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#.
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.
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();
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);
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 !");
}
}
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;
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
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
ce qui donnera :
{
Console.WriteLine("Wouaf !");
}
}
public class Chat : Animal
{
public Chat(string prenomDuChat) : base(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 :
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 :
Ce qui donne :
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
{
}
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());
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("...");
}
}
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("...");
}
}
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
}
}
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 :
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;
}
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
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 :
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();
}
}
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
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
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.
Chapitre 23
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 !
Ce code affichera la chaine Les objets rfrencent la mme instance car effectivement,
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;
}
}
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; }
}
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 :
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);
}
{
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);
}
}
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");
}
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-
Comme prvu, il nest pas possible dinstancier un objet Animal. Si nous tentons
lopration :
Animal animal = new 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 :
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.
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.
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 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;
}
}
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);
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);
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
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;
}
}
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;
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.
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 .
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();
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 :
Chapitre 24
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.
Oprations :
+100
-50
-20
+20
+200
*******************************************
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
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>();
}
}
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
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
{
}
decouvertAutorise = decouvert;
}
}
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");
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;
}
}
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);
}
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;
}
}
}
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("^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^");
}
}
Ce qui donne :
Rsum du livret Toutbnef
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
Livret ToutBnf de
Solde : 1275,000
Taux : 0,275
Oprations :
+800
+200
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
{
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("########################################################");
}
}
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
Chapitre 25
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.
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 :
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.
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;
}
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.
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)
{
}
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;
}
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.
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.
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";
}
}
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()
{
}
}
Par contre, les autres formes des constructeurs sont possibles, comme :
public struct Personne
{
private int age;
public Personne(int agePersonne)
{
age = agePersonne;
}
}
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;
}
}
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
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.
{
personne.Age++;
}
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 !
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
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.
}
}
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.
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.
{
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.
Description
where T : struct
where T : class
where T : new()
where T1 : 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();
}
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 :
Si nous tentons dutiliser la mthode avec un type qui nimplmente pas linterface
IVolant, comme :
Voiture v = Creer<Voiture>();
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.
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;
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.
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
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);
}
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);
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 !
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;
{
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 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,
Chapitre 29
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
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);
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");
}
}
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
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.
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);
}
}
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
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.
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
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);
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 :
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)
{
}
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 });
}
}
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
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.
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 :
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;
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
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);
}
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
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 :
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");
}
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.
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")
{
}
}
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
Chapitre 32
TP vnements et mto
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
}
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++;
}
}
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.
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.
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
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.
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.
}
}
}
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.
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() :
Chapitre 34
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.
du mot-cl sealed :
public sealed class 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");
}
}
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;
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" };
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.
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");
}
}
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 :
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";
}
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 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.
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);
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);
Bref, ce choix, nous le retrouvons dans notre application si nous rcuprons la culture
courante :
Console.WriteLine(System.Threading.Thread.CurrentThread.CurrentCulture);
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);
}
Ici, dd sert afficher le jour, MM le mois et yyyy lanne sur quatre chiffre, ce qui donne :
La date est 25-12-2011
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
{0:d}
Dcimal
{0:e}
Notation scientifique
{0:f}
Virgule flottante
{0:g}
Gnral
{0:n}
{0:r}
Aller-retour
{0:x}
Hxadcimal
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
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
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()
{
// ...
}
}
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
{
}
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 :
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'.
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 :
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);
}
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));
}
}
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.
Chapitre 35
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 !
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 :
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.
Nous retrouvons notre fichier de configuration dans notre projet et nous pouvons le voir
dans lexplorateur de documents :
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 :
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]);
}
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);
}
}
Nous utiliserons les chaines de connexion dans le chapitre sur laccs aux donnes.
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");
Nous pouvons boucler sur les valeurs de la section en utilisant le code suivant :
NameValueSectionHandler
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");
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);
}
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 ?
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 :
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]
{
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");
}
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.
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
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);
}
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.
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
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; }
}
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 :
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);
}
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.
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; }
}
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());
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();
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);
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
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.
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
Oui
Oprations
ensemblistes
Oui
OfType, Where
Oui
Oprations de
quantificateur
Non
Oprations de
projection
Select, SelectMany
Oui
Partitionnement des
donnes
Oui
Oprations de jointure
Join, GroupJoin
Oui
Regroupement de
donnes
GroupBy, ToLookup
Oui
Oprations de
gnration
Oui
Oprations dgalit
SequenceEqual
Non
Oprations dlment
Non
Oprations de
concatnation
Concat
Oui
Oprations
dagrgation
Non
Chapitre 37
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 !
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.
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 :
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 :
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
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 :
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 :
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.
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 :
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 :
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#.
Nous indiquons ici que notre chaine de connexion va tre accessible par le nom
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
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 :
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;
}
}
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.
[]
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);
}
}
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
Chapitre 38
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.
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.
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");
}
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");
}
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");
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 :
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);
}
[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 :
Appelez-le ProjetTest par exemple. Il faut ensuite ajouter une assembly de test, allez dans
Project > Add Assembly :
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)
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");
}
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
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();
}
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);
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
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 .
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
.
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.
Puis nous verrons avec XNA comment crire du texte lcran avec une police
particulire :