Vous êtes sur la page 1sur 811

__________________

Programmer dans .Net Framework avec le langage C# version 2.0

713 pages de cours + 94 pages dexercices corrigs

Une grande partie du contenu de ce livre pdf de cours d'initiation C#, est inclus dans un ouvrage papier de 1372 pages dit en Novembre 2004 par les ditions Berti Alger.

http://www.berti-editions.com

L'ouvrage est accompagn d'un CD-ROM contenant les assistants du package pdagogique.

Rm di Scala
Mise jour d'Aot 2006
Pour les mises jour et les autres cours pdf du mme auteur, consultez le site suivant : http://www.discala.net

SOMMAIRE
Types, oprateurs, instructions
Introduction ... . Les outils lmentaires .. Les lments de base ..... Les oprateurs + exemples .. Les instructions ... Les conditions .. Les itrations .. . Les ruptures de squence .. . Classes avec mthodes static .. P.3 P.4 P.12 P.21 P.35 P.40 P.46 P.50 P.54

Structures de donnes de base


Classe String ... Tableaux, matrices ... Collections, piles, files, listes .... P.72 P.80 P.95

C# est orient objet


Classes, objets et mthodes ... Polymorphisme d'objet .... Polymorphisme de mthode ...... Polymorphisme d'interfaces ... Classe de dlgation .... Traitement des exceptions .... Processus et multi-threading .... P.117 P.151 P.163 P.188 P.212 P.228 P.251

IHM avec C#
Programmation vnementielle ... Les vnements en C # . Proprits et indexeurs .... Fentres et ressources mmoires ... Contrles dans les formulaires ...... Exceptions compares Delphi et java .... Donnes simples flux et fichiers ... P.279 P.303 P.322 P.348 P.386 P.410 P.414

Elments principaux depuis la version C# 2.0


Les generics ... Les classes partielles .... Les mthodes anonymes ...... TAD de liste, pile, file et classes gnriques ... TAD darbre binaire et classes gnriques ....
Premier pas dans .Net avec C#2.0 - ( rv. 28.08.2006 )
- Rm di Scala

P.447 P.457 P.465 P.476 P.494


page

Principes des bases de donnes relationnelles .. ADO.Net et donnes relationnelles .... ADO.Net et SQL serveur 2005 ....

P.535 P.561 P.574

Programmation web avec ASP.net 2.0


Page web crite uniquement avec des contrles HTML simples P.617 Page web crite uniquement avec des contrles serveurs P.630 Communication entre pages web ...P.649 Page web et BD avec GridView. .P.660 Cookie dans une page web . ....P.678

Bibliographie .... Exercices ...


94 pages d'exercices avec solutions
Pour pouvoir sinitier C# avec ce cours et peu de frais dans un premier temps, il faut tlcharger gratuitement sur le site de Borland, lenvironnement Borland studio Delphi 2006 dition personnelle, ou aller sur le site de Microsoft et tlcharger gratuitement Visual C# express, ou bien utiliser la denire version de l'environnemnt open source Sharpdevelop qui fonctionne sous Net framework run-time..

Remerciements : (pour les corrections d'erreurs)


A tous ceux qui m'ont envoy un petit mail me signalant une erreur, une omission A mon pouse Dominique pour son soutien et sa patience qui me permettent de consacrer de nombreuses heures la construction du package et des cours inclus et surtout qui a eu la constance de relire entirement toutes les pages de la version initiale de l'ouvrage, alors que l'informatique n'est pas sa tasse de th. A michel Veuillerot ex-Manager europen Information Technology and Telecom Architecture and Delivery Services chez Eastman Kodak, qui a relu attentivement la version prcdente de louvrage et test tous les exemples.

Remerciements : (diffusion de la connaissance)


A l'universit de Tours qui supporte et donne accs la partie Internet du package pdagogique partir de sa rubrique "cours en ligne", partir duquel ce document a t labor. Au club des dveloppeurs francophones qui hberge gratuitement un site miroir du prcdent et qui recommande le package pdagogique ( http://rmdiscala.developpez.com/cours/ ) ses visiteurs dbutants.

Remerciements : (anticips)
Aux lecteurs qui trouveront ncessairement encore des erreurs, des oublis, et autres imperfections et qui voudront bien les signaler lauteur afin damliorer le cours, e-mail : csharplivre@discala.net

Site de consultation et de tlchargement des autres ouvrages en pdf ( Bases de l'informatique, Java 2 ) :

http://www.discala.net

Premier pas dans .Net avec C#2.0 - ( rv. 28.08.2006 )

- Rm di Scala

page

Introduction .Net

Une stratgie diffrente de rpartition de l'information et de son traitement est propose depuis 2001 par Microsoft, elle porte le nom de .NET (ou en anglais dot net). La conception de cette nouvelle architecture s'appuie sur quelques ides fondatrices que nous nonons ci-dessous :

Une disparition progressive des diffrences entre les applications et l'Internet, les serveurs ne fourniront plus seulement des pages HTML, mais des services des applications distantes. Les informations au lieu de rester concentres sur un seul serveur pourront tre rparties sur plusieurs machines qui proposeront chacune un service adapt aux informations qu'elles dtiennent. A la place d'une seule application, l'utilisateur aura accs une fdration d'applications distantes ou locales capables de cooprer entre elles pour divers usages de traitement. L'utilisateur n'aurait plus la ncessit d'acheter un logiciel, il louerait plutt les services d'une action spcifique. Le micro-ordinateur reste l'intermdiaire incontournable de cette stratgie, il dispose en plus de la capacit de terminal intelligent pour consulter et traiter les informations de l'utilisateur travers Internet o qu'elles se trouvent. Offrir aux dveloppeurs d'applications .NET un vaste ensemble de composants afin de faire de la programmation par composant unifie au sens des protocoles (comme lutilisation du protocole SOAP) et diversifie quant aux lieux o se trouvent les composants.

Afin de mettre en place cette nouvelle stratgie, microsoft procde par tapes. Les fondations de l'architecture .NET sont poses par l'introduction d'un environnement de dveloppement et d'excution des applications .NET. Cet environnement en version stabilise depuis 2002 avec une rvision majeure en 2005, porte la dnomination de .NET Framework, il est distribu gratuitement par microsoft sur toutes les versions de Windows (98, Me,..., Xp,...). L'outil Visual Studio .NET contient l'environnement RAD de dveloppement pour l'architecture .NET. Visual Studio .NET permet le dveloppement d'applications classiques Windows ou Internet.

Premier pas dans .Net avec C#2.0 - ( rv. 28.08.2006 )

- Rm di Scala

page

Les outils lmentaires

1. La plate forme .NET Framework


Elle comporte plusieurs couches les unes abstraites, les autres en code excutable :

Premier pas dans .Net avec C#2.0 - ( rv. 28.08.2006 )

- Rm di Scala

page

La premire couche CLS est compose des spcifications communes tous les langages qui veulent produire des applications .NET qui soient excutables dans cet environnement et les langages eux-mme. Le CLS est une sorte de sous-ensemble minimal de spcifications autorisant une interoprabilit complte entre tous les langages de .NET les rgles minimales (il y en a en fait 41 ) sont : Les langages de ..NET doivent savoir utiliser tous les composants du CLS Les langages de .NET peuvent construire de nouvelles classes, de nouveaux composants conformes au CLS

Le C# est le langage de base de .NET, il correspond une synthse entre Delphi et Java (le concepteur principal de .NET. et de C# est l'ancien chef de projet Turbo pascal puis Delphi de Borland). Afin de rendre Visual Basic interoprable sur .NET, il a t entirement reconstruit par microsoft et devient un langage orient objet dnomm VB.NET.

La seconde couche est un ensemble de composants graphiques disponibles dans Visual Studio .NET qui permettent de construire des interfaces homme-machine orientes Web (services Web) ou bien orientes applications classiques avec IHM.

Les donnes sont accdes dans le cas des services Web travers les protocoles qui sont des standards de l'industrie : HTTP, XML et SOAP.

La troisime couche est constitue d'une vaste librairie de plusieurs centaines de classes :

Toutes ces classes sont accessibles telles quelles tous les langages de .NET et cette librairie peut tre tendue par adjonction de nouvelles classes. Cette librairie a la mme fonction que la bibliothque des classes de Java.
Premier pas dans .Net avec C#2.0 - ( rv. 28.08.2006 ) page

- Rm di Scala

La librairie de classe de .NET Framework est organise en nom d'espace hierarchiss, exemple ci-dessous de quelques espaces de nom de la hirarchie System :

Un nom complet de classe comporte le "chemin" hirarchique de son espace de nom et se termine par le nom de la classe exemples : La classe DataSet qui se trouve dans l'espace de noms "System.Data.ADO" se dclare comme "System.Data.ADO.Dataset". La classe Console qui se trouve dans l'espace de noms "System" se dclare comme "System.Console".

La quatrime couche forme l'environnement d'excution commun (CLR ou Common Language Runtime) de tous les programmes s'excutant dans l'environnement .NET. Le CLR excute un bytecode crit dans un langage intermdiaire (MSIL ou MicroSoft Intermediate Language) Rappelons qu'un ordinateur ne sait excuter que des programmes crits en instructions machines comprhensibles par son processeur central. C# comme pascal, C etc... fait partie de la famille des langages volus (ou langages de haut niveau) qui ne sont pas comprhensibles immdiatement par le processeur de l'ordinateur. Il est donc ncesaire d'effectuer une "traduction" d'un programme crit en langage volu afin que le processeur puisse l'excuter. Les deux voies utilises pour excuter un programme volu sont la compilation ou l'interprtation :

Premier pas dans .Net avec C#2.0 - ( rv. 28.08.2006 )

- Rm di Scala

page

Un compilateur du langage X pour un processeur P, est un logiciel qui traduit un programme source crit en X en un programme cible crit en instructions machines excutables par le processeur P.

Un interprteur du langage X pour le processeur P, est un logiciel qui ne produit pas de programme cible mais qui effectue lui-mme immdiatement les oprations spcifies par le programme source.

Un compromis assurant la portabilit d'un langage : une pseudo-machine Lorsque le processeur P n'est pas une machine qui existe physiquement mais un logiciel simulant (ou interprtant) une machine on appelle cette machine pseudomachine ou p-machine. Le programme source est alors traduit par le compilateur en instructions de la pseudo-machine et se dnomme pseudo-code. La p-machine standard peut ainsi tre implante dans n'importe quel ordinateur physique travers un logiciel qui simule son comportement; un tel logiciel est appel interprteur de la p-machine.

La premire p-machine d'un langage volu a t construite pour le langage pascal assurant ainsi une large diffusion de ce langage et de sa version UCSD dans la mesure o le seul effort d'implementation pour un ordinateur donn tait d'crire l'interprteur de p-machine pascal, le reste de l'environnement de dveloppement (diteurs, compilateurs,...) tant crit en pascal tait fourni et fonctionnait ds que la p-machine tait oprationnelle sur la plate-forme cible. Donc dans le cas d'une p-machine le programme source est compil, mais le programme cible est excut par l'interprteur de la p-machine. Beaucoup de langages possdent pour une plate-forme fixe des interprteurs ou des compilateurs, moins possdent une p-machine, Java de Sun est l'un de ces langages. Tous les langages de la plateforme .NET fonctionnent selon ce principe, C# conu par microsoft en est le dernier, un programme C# compil en p-code, s'excute sur la p-machine virtuelle incluse dans le CLR. Nous dcrivons ci-dessous le mode opratoire en C#.

Compilation native
La compilation native consiste en la traduction du source C# (ventuellement pralablement traduit instantanment en code intermdiare) en langage binaire excutable sur la plate-forme concerne. Ce genre de compilation est quivalent n'importe quelle compilation d'un langage dpendant de la plate-forme, l'avantage est la rapidit d'excution des instructions machines par le processeur central. La stratgie de dveloppement multi-plateforme de .Net, fait que Microsoft ne fournit pas pour linstant, de compilateur C# natif, il faut aller voir sur le net les entreprises vendant ce type de produit.

Premier pas dans .Net avec C#2.0 - ( rv. 28.08.2006 )

- Rm di Scala

page

Programe source C# : xxx.cs Programe excutable sous Windows : xxx.exe (code natif processeur)

Bytecode ou langage intermdiaire


La compilation en bytecode (ou pseudo-code ou p-code ou code intermdiaire) est semblable l'ide du p-code de N.Wirth pour obtenir un portage multi plate-formes du pascal. Le compilateur C# de .NET Framework traduit le programme source xxx.cs en un code intermdiaire indpendant de toute machine physique et non excutable directement, le fichier obtenu se dnomme PE (portable executable) et prend la forme : xxx.exe. Seule une p-machine (dnomme machine virtuelle .NET) est capable d'excuter ce bytecode. Le bytecode est aussi dnomm MSIL. En fait le bytecode MSIL est pris en charge par le CLR et n'est pas interprt par celui-ci mais traduit en code natif du processeur et excut par le processeur sous contrle du CLR..

ATTENTION
Bien que se terminant par le suffixe exe, un programme issu d'une compilation sous .NET n'est pas un excutable en code natif, mais un bytecode en MSIL; ce qui veut dire que vous ne pourrez pas faire excuter directement sur un ordinateur qui n'aurait pas la machine virtuelle .NET, un programme PE "xxx.exe" ainsi construit.

Ci-dessous le schma d'un programme source Exemple.cs traduit par le compilateur C# sous .NET en un programme cible crit en bytecode nomm Exemple.exe

Premier pas dans .Net avec C#2.0 - ( rv. 28.08.2006 )

- Rm di Scala

page

Programe source C# : Exemple.cs Programe excutable sous .NET : Exemple.exe (code portable IL)

2. L'environnement d'excution du CLR


Rappelons que le CLR (Common Language Runtime) est un environnement complet d'excution semblable au JRE de Sun pour Java, il est indpendant de l'architecture machine sous-jacente. Le CLR prend en charge essentiellement : le chargement des classes, les vrifications de types, la gestion de la mmoire, des exceptions, de la scurit, la traduction la vole du code MSIL en code natif (compilateur interne JIT), travers le CTS (Common Type System) qui implmente le CLS (Common Language Specification), le CLR assure la scurit de compatibilit des types connus mais syntaxiquement diffrents selon les langages utiliss.

Une fois le programme source C# traduit en bytecode MSIL, la machine virtuelle du CLR se charge de l'excuter sur la machine physique travers son systme d'exploitation (Windows, Unix,...)

Premier pas dans .Net avec C#2.0 - ( rv. 28.08.2006 )

- Rm di Scala

page

Le CLR intgr dans l'environnement .NET est distribu gratuitement.

La compilation JIT progressive


L'interprtation et l'excution du bytecode ligne par ligne pourrait prendre beaucoup de temps et cela a t semble-t-il le souci de microsoft qui a adopt une stratgie d'optimisation de la vitesse d'excution du code MSIL en utilisant la technique Just-in-time. JIT (Just-in-time) est une technique de traduction dynamique durant l'interprtation. La machine virtuelle CLR contient un compilateur optimiseur qui recompile localement le bytecode MSIL afin de n'avoir plus qu' faire excuter des instructions machines de base. Le compilateur JIT du CLR compile une mthode en code natif ds qu'elle est appele dans le code MSIL, le processus recommence chaque fois qu'un appel de mthode a lieu sur une mthode non dj compile en code natif.

On peut mentalement considrer qu'avec cette technique vous obtenez un programme C# cible compil en deux passages : le premier passage est d l'utilisation du compilateur C# produisant excutable portable ( PE ) en bytecode MSIL, le second passage tant le compilateur JIT lui-mme qui optimise et traduit localement la vole et chaque appel de mthode, le bytecode MSIL en instructions du processeur de la plate-forme. Ce qui donne au bout d'un temps trs bref, un code totalement traduit en instruction du processeur de la plateforme, selon le schma ci-aprs :

Premier pas dans .Net avec C#2.0 - ( rv. 28.08.2006 )

- Rm di Scala

page

10

La compilation AOT
Toujours des fins d'optimisation de la vitesse d'excution du code MSIL, la technique AOT Ahead-Of-Time est employe dans les versions rcentes de .Net depuis 2005. AOT (ahead-of-time) est une technique de compilation locale de tout le bytecode MSIL avant excution (semblable la compilation native). Le compilateur AOT du CLR compile, avant une quelconque excution et en une seule fois, toutes les lignes de code MSIL et gnre des images dexcutables destination du CLR.

Premier pas dans .Net avec C#2.0 - ( rv. 28.08.2006 )

- Rm di Scala

page

11

Les lments de base

Tout est objet dans C#, en outre C# est un langage fortement typ. Comme en Delphi et en Java vous devez dclarer un objet C# ou une variable C# avec son type avant de l'utiliser. C# dispose de types valeurs intrinsques qui sont dfinis partir des types de base du CLS (Common Language Specification).

1. Les types valeurs du CLS dans .NET Framework

Struct
Les classes encapsulant les types lmentaires dans .NET Framework sont des classes de type valeur du genre structures. Dans le CLS une classe de type valeur est telle que les allocations d'objets de cette classe se font directement dans la pile et non dans le tas, il n'y a donc pas de rfrence pour un objet de type valeur et lorsqu'un objet de type valeur est pass comme paramtre il est pass par valeur. Dans .NET Framework les classes-structures de type valeur sont dclares comme structures et ne sont pas drivables, les classes de type rfrence sont dclares comme des classes classiques et sont drivables. Afin d'clairer le lecteur prenons par exemple un objet x instanci partir d'une classe de type rfrence et un objet y instanci partir d'un classe de type valeur contenant les mmes membres que la classe par rfrence. Ci-dessous le schma d'allocation de chacun des deux objets :

Premier pas dans .Net avec C#2.0 - ( rv. 28.08.2006 )

- Rm di Scala

page

12

En C# on aurait le genre de syntaxe suivant : Dclaration de classe-structure : struct StructAmoi { int b; void meth(int a){ b = 1000+a; } } Dclaration de classe : class ClassAmoi { int b; void meth(int a) { b = 1000+a; } } instanciation : ClassAmoi x = new ClassAmoi ( ) ; instanciation : StructAmoi y = new StructAmoi ( ) ;

Les classes-structures de type valeur peuvent comme les autres classes possder un constructeur explicite, qui comme pour toute classe C# doit porter le mme nom que celui de la classestructure. Exemple ci-desssous d'une classe-structure dnomme Menulang: public struct Menulang { public String MenuTexte; public String Filtre; public Menulang(String M, String s) { MenuTexte = M; Filtre = s; } } On instancie alors un objet de type valeur comme un objet de type rfrence. En reprenant l'exemple de la classe prcdente on instancie et on utilise un objet Rec : Menulang Rec = new Menulang ( Nomlang , FiltreLang ); Rec.MenuTexte = "Entrez" ; Rec.Filtre = "*.ent" ;

Classe-structure Boolean

intervalle de variation false , true

nombre de bits 1 bit

Premier pas dans .Net avec C#2.0 - ( rv. 28.08.2006 )

- Rm di Scala

page

13

SByte Byte Char Double Single Int16 Int32 Int64 UInt16 UInt32 UInt64 Decimal

octet sign -128 ... +127 octet non sign 0 ... 255 caractres unicode (valeurs de 0 65535) Virgule flottante double prcision ~ 15 dcimales Virgule flottante simple prcision ~ 7 dcimales entier sign court [ -215... +215-1 ] entier sign [ -231... +231-1 ] entier sign long [ -263... +263-1 ] entier non sign court 0216-1 entier non sign 0232 -1 entier non sign long 0264-1 reel = entier* 10n (au maximum 28 dcimales exactes)

8 bits 8 bits 16 bits 64 bits 32 bits 16 bits 32 bits 64 bits 16 bits 32 bits 64 bits 128 bits

Compatibilit des types de .NET Framework Le type System.Int32 est le type valeur entier sign sur 32 bits dans le CLS. Voici selon 4 langages de .NET Framework ( VB, C#, C++, J# ) la dclaration syntaxique du type Int32 : [Visual Basic] Public Structure Int32 Implements IComparable, IFormattable, IConvertible [C#] public struct Int32 : IComparable, IFormattable, IConvertible [C++] public __value struct Int32 : public IComparable, IFormattable, IConvertible [J#] public class Int32 extends System.ValueType implements System.IComparable, System.IFormattable, System.IConvertible

Les trois premires dclarations comportent syntaxiquement le mot clef struct ou Structure
Premier pas dans .Net avec C#2.0 - ( rv. 28.08.2006 )
- Rm di Scala

page

14

indiquant le mode de gestion par valeur donc sur la pile des objets de ce type. La dernire dclaration en J# compatible syntaxiquement avec Java, utilise une classe qui par contre gre ses objets par rfrence dans le tas. C'est le CLR qui va se charger de maintenir une cohrence interne entre ces diffrentes variantes; ici on peut raisonnablement supposer que grce au mcanisme d'embotage (Boxing) le CLR allouera un objet par rfrence encapsulant l'objet par valeur, mais cet objet encapsul sera marqu comme objet-valeur.

enum
Un type enum est un type valeur qui permet de dclarer un ensemble de constantes de base comme en pascal. En C#, chaque numration de type enum, possde un type sous-jacent, qui peut tre de n'importe quel type entier : byte, sbyte, short, ushort, int, uint, long ou ulong. Le type int est le type sous-jacent par dfaut des lments de l'numration. Par dfaut, le premier numrateur a la valeur 0, et l'numrateur de rang n a la valeur n-1. Soit par exemple un type numr jour :

enum jour { lundi, mardi, mercredi, jeudi, vendredi, samedi, dimanche} par dfaut : rang de lundi=0, rang de mardi=1, ... , rang de dimanche=6

1) Il est possible de dclarer classiquement une variable du type jour comme un objet de type jour, de l'instancier et de l'affecter :

jour unJour = new jour ( ); unJour = jour.lundi ; int rang = (int)unJour; // rang de la constante dans le type numr System.Console.WriteLine("unJour = "+unJour.ToString()+" , place = '+rang); Rsultat de ces 3 lignes de code affich sur la console : unJour = lundi , place = 0

2) Il est possible de dclarer d'une manire plus courte la mme variable du type jour et de l'affecter : jour unJour ; unJour = jour.lundi ; int rang = (int)unJour; System.Console.WriteLine("unJour = "+unJour.ToString()+" , place = '+rang); Rsultat de ces 3 lignes de code affich sur la console : unJour = lundi , place = 0
Premier pas dans .Net avec C#2.0 - ( rv. 28.08.2006 )

- Rm di Scala

page

15

Remarque C# accepte que des numrations aient des noms de constantes d'numrations identiques : enum jour { lundi, mardi, mercredi, jeudi, vendredi, samedi, dimanche} enum weekEnd { vendredi, samedi, dimanche}

Dans cette ventualit faire attention, la comparaison de deux variables de deux types diffrents, affectes chacune une valeur de constante identique dans les deux types, ne conduit pas l'galit de ces variables (c'est en fait le rang dans le type numr qui est test). L'exemple ci-dessous illustre cette remarque : enum jour { lundi, mardi, mercredi, jeudi, vendredi, samedi, dimanche} enum weekEnd { vendredi, samedi, dimanche} jour unJour ; weekEnd repos ; unJour = jour.samedi ; repos = weekEnd.samedi; if ( (jour)repos == unJour ) // il faut transtyper l'un des deux si l'on veut les comparer System.Console.WriteLine("Le mme jour"); else System.Console.WriteLine("Jours diffrents"); Rsultat de ces lignes de code affich sur la console : Jours diffrents

2. Syntaxe des types valeurs de C# et transtypage


Les types servent dterminer la nature du contenu d'une variable, du rsultat d'une opration, d'un retour de rsultat de fonction. Ci-dessous le tableau de correspondance syntaxique entre les types lmentaires du C# et les classes de .NET Framework (table appele aussi, table des alias) :

Types valeurs C# bool sbyte byte

Classe-structure de .NET Framework Boolean SByte Byte


- Rm di Scala

nombre de bits 1 bit 8 bits 8 bits


page

Premier pas dans .Net avec C#2.0 - ( rv. 28.08.2006 )

16

char double float short int long ushort uint ulong decimal

Char Double Single Int16 Int32 Int64 UInt16 UInt32 UInt64 Decimal

16 bits 64 bits 32 bits 16 bits 32 bits 64 bits 16 bits 32 bits 64 bits 128 bits

Rappelons qu'en C# toute variable qui sert de conteneur une valeur d'un type lmentaire prcis doit pralablement avoir t dclare sous ce type. Remarque importante

Une variable de type lmentaire en C# est (pour des raisons de compatibilit CLS) automatiquement un objet de type valeur (Par exemple une variable de type float peut tre considre comme un objet de classe Single).

Il est possible d'indiquer au compilateur le type d'une valeur numrique en utilisant un suffixe :

l ou L pour dsigner un entier du type long f ou F pour dsigner un rel du type float d ou D pour dsigner un rel du type double m ou M pour dsigner un rel du type decimal

Exemples :
45l ou 45L reprsente la valeur 45 en entier sign sur 64 bits. 45f ou 45F reprsente la valeur 45 en virgule flottante simple prcision sur 32 bits. 45d ou 45D reprsente la valeur 45 en virgule flottante double prcision sur 64 bits.
Premier pas dans .Net avec C#2.0 - ( rv. 28.08.2006 )
- Rm di Scala

page

17

5.27e-2f ou 5.27e-2F reprsente la valeur 0.0527 en virgule flottante simple prcision sur 32 bits.

Transtypage oprateur ( )
Les conversions de type en C# sont identiques pour les types numriques aux conversions utilises dans un langage fortement typ comme Delphi par exemple. Toutefois C# pratique la conversion implicite lorsque celle-ci est possible. Si vous voulez malgr tout, convertir explicitement une valeur immdiate ou une valeur contenue dans une variable il faut utiliser l'oprateur de transtypage not ( ). Nous nous sommes dj servi de la fonctionnalit de transtypage explicite au paragraphe prcdent dans l'instruction : int rang = (int)unJour; et dans l'instruction if ( (jour)repos == unJour )... Transtypage implicite en C# : int n = 1234; float x1 = n ; double x2 = n ; double x3 = x1 ; long p = n ; .....

Transtypage explicite en C# : int x; x = (int) y ; signifie que vous demandez de transtyper la valeur contenue dans la variable y en un entier sign 32 bits avant de la mettre dans la variable x. Tous les types lmentaires peuvent tre transtyps l'exception du type bool qui ne peut pas tre converti en un autre type (diffrence avec le C). Les conversions peuvent tre restrictives quant au rsultat; par exemple le transtypage du rel 5.27e-2 en entier ( x = (int)5.27e-2) mettra l'entier zro dans x.

3. Variables, valeurs, constantes en C#


Comme en Java, une variable C# peut contenir soit une valeur d'un type lmentaire, soit une rfrence un objet. Les variables jouent le mme rle que dans les langages de programmation classiques impratifs, leur visibilit est tudie dans le prochain chapitre. Les identificateurs de variables en C# se dcrivent comme ceux de tous les langages de programmation :
Premier pas dans .Net avec C#2.0 - ( rv. 28.08.2006 )
- Rm di Scala

page

18

Identificateur C# :

Attention C# fait une diffrence entre majuscules et minuscules, c'est dire que la variable BonJour n'est pas la mme que la variable bonjour ou encore la variable Bonjour. En plus des lettres, les caractres suivants sont autoriss pour construire une identificateur C# : "$" , "_" , "" et les lettres accentues.

Exemples de dclaration de variables : int Bonjour ; int Enumration_fin$; float Valeur ; char UnCar ; bool Test ; etc ... Exemples d'affectation de valeurs ces variables : Affectation Bonjour = 2587 ; Valeur = -123.5687 UnCar = 'K' ; Test = false ; Dclaration avec initialisation int Bonjour = 2587 ; float Valeur = -123.5687 char UnCar = 'K' ; bool Test = false ;

Exemple avec transtypage : int Valeur ; char car = '8' ; Valeur = (int)car - (int)'0'; fonctionnement de l'exemple : Lorsque la variable car est l'un des caractres '0', '1', ... ,'9', la variable Valeur est gale la valeur numrique associe (il s'agit d'une conversion car = '0' ---> Valeur = 0, car = '1' ---> Valeur = 1, ... , car = '9' ---> Valeur = 9).
Premier pas dans .Net avec C#2.0 - ( rv. 28.08.2006 )

- Rm di Scala

page

19

Les constantes en C#
C# dispose de deux mots clefs pour qualifier des variables dont le contenu ne peut pas tre modifi : const et readonly sont des qualificateurs de dclaration qui se rajoutent devant les autres qualificateurs de dclaration.. - Les constantes qualifies par const doivent tre initialises lors de leur dclaration. Une variable membre de classe ou une variable locale une mthode peut tre qualifie en constante const. Lorsque de telles variables sont dclares comme variables membre de classe, elles sont considres comme des variables de classe statiques : const int x ; // erreur , le compilateur n'accepte pas une constante non initialise. const int x = 1000 ; // x est dclare comme constante entire initialise 1000. x = 8 ; <------ provoquera une erreur de compilation interdisant la modification de la valeur de x.

- Les constantes qualifies par readonly sont uniquement des variables membre de classes, elles peuvent tre initialises dans le constructeur de la classe (et uniquement dans le constructeur) : readonly int x ; // correct. readonly int x = 100 ; // correct.

-Rappelons enfin pour mmoire les constantes de base d'un type numr ( cf. enum )

Base de reprsentation des entiers


C# peut reprsenter les entiers dans 2 bases de numration diffrentes : dcimale (base 10), hexadcimale (base 16). La dtermination de la base de reprsentation d'une valeur est d'ordre syntaxique grce un prfixe :

pas de prfixe ----> base = 10 dcimal. prfixe 0x ----> base = 16 hexadcimal

Premier pas dans .Net avec C#2.0 - ( rv. 28.08.2006 )

- Rm di Scala

page

20

Les oprateurs

1. Priorit d'oprateurs en C#
Les oprateurs du C# sont trs semblables ceux de Java et donc de C++, ils sont dtaills par famille, plus loin. Ils sont utiliss comme dans tous les langages impratifs pour manipuler, sparer, comparer ou stocker des valeurs. Les oprateurs ont soit un seul oprande, soit deux oprandes, il n'existe en C# qu'un seul oprateur trois oprandes (comme en Java) l'oprateur conditionnel " ? : ". Dans le tableau ci-dessous les oprateurs de C# sont classs par ordre de priorit croissante (0 est le plus haut niveau, 13 le plus bas niveau). Ceci sert lorsqu'une expression contient plusieurs oprateurs indiquer l'ordre dans lequel s'effectueront les oprations. Par exemple sur les entiers l'expression 2+3*4 vaut 14 car l'oprateur * est plus prioritaire que l'oprateur +, donc l'oprateur * est effectu en premier. Lorsqu'une expression contient des oprateurs de mme priorit alors C# effectue les valuations de gauche droite. Par exemple l'expression 12/3*2 vaut 8 car C# effectue le parenthsage automatique de gauche droite ((12/3)*2).

Tableau gnral de toutes les priorits


priorit 0 1 2 3 4 5 6 7 () ! * + << < == & ~ / >> <= > >= != is [ ] . new ++ -% tous les oprateurs de C#

Premier pas dans .Net avec C#2.0 - ( rv. 28.08.2006 )

- Rm di Scala

page

21

8 9 10 11 12 13

^ | && || ?: = *= /= %= += -= ^= &= <<= >>= >>>= |=

2. Les oprateurs arithmtiques en C#


Les oprateurs d'affectation seront mentionns plus loin comme cas particulier de l'instruction d'affectation.

oprateurs travaillant avec des oprandes valeur immdiate ou variable Oprateur priorit + * / % + 1 1 2 2 2 3 3 action signe positif signe ngatif multiplication division reste addition soustraction exemples +a; +(a-b); +7 (unaire) -a; -(a-b); -7 (unaire) 5*4; 12.7*(-8.31); 5*2.6 5 / 2; 5.0 / 2; 5.0 / 2.0 5 % 2; 5.0 %2; 5.0 % 2.0 a+b; -8.53 + 10; 2+3 a-b; -8.53 - 10; 2-3

Ces oprateurs sont binaires ( deux oprandes) excepts les oprateurs de signe positif ou ngatif. Ils travaillent tous avec des oprandes de types entiers ou rels. Le rsultat de l'opration est converti automatiquement en valeur du type des oprandes. L'oprateur " % " de reste n'est intressant que pour des calculs sur les entiers longs, courts, signs ou non signs : il renvoie le reste de la division euclidienne de 2 entiers.

Premier pas dans .Net avec C#2.0 - ( rv. 28.08.2006 )

- Rm di Scala

page

22

Exemples d'utilisation de l'oprateur de division selon les types des oprandes et du rsultat : programme C# int x = 5 , y ; float a , b = 5 ; y=x/2; y=b/2; y = b / 2.0 ; a=b/2; rsultat obtenu x = 5 , y =??? b = 5 , a =??? y = 2 // type int erreur de conversion : interdit erreur de conversion: interdit a = 2.5 // type float dclaration dclaration int x et int 2 rsultat : int conversion implicite impossible (float b --> int y) conversion implicite impossible (float b --> int y) float b et int 2 rsultat : float int x et int 2 rsultat : int conversion automatique int 2 --> float 2.0 int x et float 2f rsultat : float commentaire

a=x/2;

a = 2.0 // type float

a = x / 2f ;

a = 2.5 // type float

Pour l'instruction prcdente " y = b / 2 " engendrant une erreur de conversion voici deux corrections possibles utilisant le transtypage explicite : y = (int)b / 2 ; // b est converti en int avant la division qui s'effectue sur deux int. y = (int)(b / 2) ; // c'est le rsultat de la division qui est converti en int. oprateurs travaillant avec une unique variable comme oprande Oprateur priorit ++ 1 action exemples

post ou pr incrmentation : incrmente de 1 son oprande numrique : short, ++a; a++; (unaire) int, long, char, float, double. post ou pr dcrmentation : dcrmente de 1 son oprande numrique : short, --a; a--; (unaire) int, long, char, float, double.

--

L'objectif de ces oprateurs est l'optimisation de la vitesse d'excution du bytecode MSIL dans le CLR (cette optimisation n'est pas effective dans le version actuelle du MSIL) mais surtout la reprise syntaxique aise de code source Java et C++.

Premier pas dans .Net avec C#2.0 - ( rv. 28.08.2006 )

- Rm di Scala

page

23

post-incrmentation : k++
la valeur de k est d'abord utilise telle quelle dans l'instruction, puis elle est augmente de un la fin. Etudiez bien les exemples ci-aprs qui vont vous permettre de bien comprendre le fonctionnement de cet oprateur. Nous avons mis ct de l'instruction C# les rsultats des contenus des variables aprs excution de l'instruction de dclaration et de la post incrmentation. Exemple 1 : int k = 5 , n ; n = k++ ; n=5 k=6

Exemple 2 : int k = 5 , n ; n = k++ - k ; n = -1 k=6

Dans l'instruction k++ - k nous avons le calcul suivant : la valeur de k (k=5) est utilise comme premier oprande de la soustraction, puis elle est incrmente (k=6), la nouvelle valeur de k est maintenant utilise comme second oprande de la soustraction ce qui revient calculer n = 5-6 et donne n = -1 et k = 6. Exemple 3 : int k = 5 , n ; n = k - k++ ; n=0 k=6

Dans l'instruction k - k++ nous avons le calcul suivant : la valeur de k (k=5) est utilise comme premier oprande de la soustraction, le second oprande de la soustraction est k++ c'est la valeur actuelle de k qui est utilise (k=5) avant incrmentation de k, ce qui revient calculer n = 5-5 et donne n = 0 et k = 6. Exemple 4 : Utilisation de l'oprateur de post-incrmentation en combinaison avec un autre oprateur unaire. int nbr1, z , t , u , v ; nbr1 = 10 ; v = nbr1++ nbr1 = 10 ; z = ~ nbr1 ; nbr1 = 10 ; t = ~ nbr1 ++ ; nbr1 = 10 ; u = ~ (nbr1 ++) ; v = 10 z = -11 t = -11 u = -11 nbr1 = 11 nbr1 = 10 nbr1 = 11 nbr1 = 11

Premier pas dans .Net avec C#2.0 - ( rv. 28.08.2006 )

- Rm di Scala

page

24

La notation " (~ nbr1) ++ " est refuse par C# comme pour Java. Remarquons que les expressions "~nbr1 ++ " et "~ (nbr1 ++)" produisent les mmes effets, ce qui est logique puisque lorsque deux oprateurs (ici ~ et ++ ) ont la mme priorit, l'valuation a lieu de gauche droite.

pr-incrmentation : ++k
la valeur de k est d'abord augmente de un ensuite utilise dans l'instruction. Exemple1 : int k = 5 , n ; n = ++k ; n=6 k=6

Exemple 2 : int k = 5 , n ; n = ++k - k ; n=0 k=6

Dans l'instruction ++k - k nous avons le calcul suivant : le premier oprande de la soustraction tant ++k c'est donc la valeur incrmente de k (k=6) qui est utilise, cette mme valeur sert de second oprande la soustraction ce qui revient calculer n = 6-6 et donne n = 0 et k = 6.

Exemple 3 : int k = 5 , n ; n = k - ++k ; n = -1 k=6

Dans l'instruction k - ++k nous avons le calcul suivant : le premier oprande de la soustraction est k (k=5), le second oprande de la soustraction est ++k, k est immdiatement incrment (k=6) et c'est sa nouvelle valeur incrmente qui est utilise, ce qui revient calculer n = 5-6 et donne n = 1 et k = 6.

post-dcrmentation : k-la valeur de k est d'abord utilise telle quelle dans l'instruction, puis elle est diminue de un la fin. Exemple1 : int k = 5 , n ; n = k-- ; n=5 k=4

Premier pas dans .Net avec C#2.0 - ( rv. 28.08.2006 )

- Rm di Scala

page

25

pr-dcrmentation : --k
la valeur de k est d'abord diminue de un, puis utilise avec sa nouvelle valeur. Exemple1 : int k = 5 , n ; n = --k ; n=4 k=4

Reprenez avec l'oprateur - - des exemples semblables ceux fournis pour l'oprateur ++ afin d'tudier le fonctionnement de cet oprateur (tudiez (- -k - k) et (k - - -k)).

3. Oprateurs de comparaison
Ces oprateurs employs dans une expression renvoient un rsultat de type boolen (false ou true). Nous en donnons la liste sans autre commentaire car ils sont strictement identiques tous les oprateurs classiques de comparaison de n'importe quel langage algorithmique (C, pascal, etc...). Ce sont des oprateurs deux oprandes. Oprateur priorit < <= > >= == != is 5 5 5 5 6 6 5 action strictement infrieur infrieur ou gal strictement suprieur suprieur ou gal gal diffrent Teste le type de l'objet exemples 5 < 2 ; x+1 < 3 ; y-2 < x*4 -5 <= 2 ; x+1 <= 3 ; etc... 5 > 2 ; x+1 > 3 ; etc... 5 >= 2 ; etc...

5 = = 2 ; x+1 = = 3 ; etc... 5 != 2 ; x+1 != 3 ; etc... X is int ; if ( x is Object ) etc...

4. Oprateurs boolens
Ce sont les oprateurs classiques de l'algbre de boole { { V, F }, ! , & , | } o { V, F } reprsente l'ensemble {Vrai,Faux}. Les connecteurs logiques ont pour syntaxe en C# : ! , & , |, ^:

& : { V, F } x { V, F } { V, F } (oprateur binaire qui se lit " et ") | : { V, F } x { V, F } { V, F } (oprateur binaire qui se lit " ou ") ^ : { V, F } x { V, F } { V, F } (oprateur binaire qui se lit " ou exclusif ") ! : { V, F } { V, F } (oprateur unaire qui se lit " non ")
Premier pas dans .Net avec C#2.0 - ( rv. 28.08.2006 )
- Rm di Scala

page

26

Table de vrit des oprateurs ( p et q tant des expressions boolennes)


p V V F F q V F V F ! p F F V V p & q V F F F P ^ q F V V F p | q V V V F

Remarque : p { V, F } , q { V, F } , p &q est toujours valu en entier ( p et q sont toujours valus). p { V, F } , q { V, F } , p |q est toujours valu en entier ( p et q sont toujours valus). C# dispose de 2 clones des oprateurs binaires & et | . Ce sont les oprateurs && et || qui se diffrentient de leurs originaux & et | par leur mode d'excution optimis (application de thormes de l'algbre de boole) : L'oprateur et optimis : && Thorme q { V, F } , F &q = F Donc si p est faux (p = F) , il est inutile d'valuer q car l'expression p &q est fausse (p &q = F), comme l'oprateur & value toujours l'expression q, C# des fins d'optimisation de la vitesse d'excution du bytecode MSIL dans le CLR , propose un oprateur et not && qui a la mme table de vrit que l'oprateur & mais qui applique ce thorme. p { V, F } , q { V, F } , p &&q = p &q Mais dans p&&q , q n'est valu que si p = V.

L'oprateur ou optimis : | | Thorme q { V, F } , V |q = V Donc si p est vrai (p = V), il est inutile d'valuer q car l'expression p |q est vraie (p |q = V), comme l'oprateur | value toujours l'expression q, C# des fins d'optimisation de la vitesse d'excution du bytecode dans la machine virtuelle C#, propose un oprateur ou not || qui
Premier pas dans .Net avec C#2.0 - ( rv. 28.08.2006 )
- Rm di Scala

page

27

applique ce thorme et qui a la mme table de vrit que l'oprateur | . p { V, F } , q { V, F } , p ||q = p |q Mais dans p||q , q n'est valu que si p = F.

En rsum: Oprateur priorit action non boolen et boolen complet ou boolen complet et boolen optimis ou boolen optimis exemples ! (5 < 2) ; !(x+1 < 3) ; etc... (5 = = 2) & (x+1 < 3) ; etc... (5 != 2) | (x+1 >= 3) ; etc... (5 = = 2) && (x+1 < 3) ; etc... (5 != 2) || (x+1 >= 3) ; etc...

! & | && ||

1 7 9 10 11

Nous allons voir ci-aprs une autre utilisation des oprateurs &et | sur des variables ou des valeurs immdiates en tant qu'oprateur bit-level.

5. Oprateurs bits level


Ce sont des oprateurs de bas niveau en C# dont les oprandes sont exclusivement l'un des types entiers ou caractre de C# (short, int, long, char, byte). Ils permettent de manipuler directement les bits du mot mmoire associ la donne. Oprateur priorit ~ << >> & ^ | 1 4 4 7 8 9 action complmente les bits dcalage gauche dcalage droite avec signe et boolen bit bit ou exclusif xor bit bit ou boolen bit bit exemples ~a; ~(a-b); ~7 (unaire) x << 3 ; (a+2) << k ; -5 << 2 ; x >> 3 ; (a+2) >> k ; -5 >> 2 ; x & 3 ; (a+2) & k ; -5 & 2 ; x ^ 3 ; (a+2) ^ k ; -5 ^ 2 ; x | 3 ; (a+2) | k ; -5 | 2 ;

Les tables de vrits des oprateurs "&", " | " et celle du ou exclusif " ^ " au niveau du bit sont identiques aux tables de verit boolennes ( seule la valeur des constantes V et F change, V est remplac par le bit 1 et F par le bit 0) .
Premier pas dans .Net avec C#2.0 - ( rv. 28.08.2006 )
- Rm di Scala

page

28

Table de vrit des oprateurs bit level p 1 1 0 0 q 1 0 1 0 ~p 0 0 1 1 p&q 1 0 0 0 p|q 1 1 1 0


p ^ q

0 1 1 0

Afin de bien comprendre ces oprateurs, le lecteur doit bien connatre les diffrents codages des entiers en machine (binaire pur, binaire sign, complment deux) car les entiers C# sont cods en complment deux et la manipulation bit bit ncessite une bonne comprhension de ce codage. Afin que le lecteur se familiarise bien avec ces oprateurs de bas niveau nous dtaillons un exemple pour chacun d'entre eux.

Les exemples en 3 instructions C# sur la mme mmoire : Rappel : int i = -14 ; soit reprsenter le nombre -14 dans la variable i de type int (entier sign sur 32 bits)

codage de |-14|= 14

complment 1

addition de 1

Le nombre entier -14 s'crit 1111..10010 en complment 2.

Premier pas dans .Net avec C#2.0 - ( rv. 28.08.2006 )

- Rm di Scala

page

29

Soient la dclaration C# suivante : int i = -14 , j ; Etudions les effets de chaque oprateur bit level sur cette mmoire i.

Etude de l'instruction : j = ~ i

j = ~ i ; // complmentation des bits de i

~i

Tous les bits 1 sont transforms en 0 et les bits 0 en 1, puis le rsultat est stock dans j qui contient la valeur 13 (car 000...01101 reprsente +13 en complment deux). Etude de l'instruction : j = i >> 2

j = i >> 2 ; // dcalage avec signe de 2 bits vers la droite

~ i >> 2
Tous les bits sont dcals de 2 positions vers la droite (vers le bit de poids faible), le bit de signe (ici 1) est recopi partir de la gauche ( partir du bit de poids fort) dans les emplacements librs (ici le bit 31 et le bit 30), puis le rsultat est stock dans j qui contient la valeur -4 (car 1111...11100 reprsente -4 en complment deux). Etude de l'instruction : j = i << 2

j = i << 2 ; // dcalage de 2 bits vers la gauche

~ i << 2
Tous les bits sont dcals de 2 positions vers la gauche (vers le bit de poids fort), des 0 sont introduits partir de la droite ( partir du bit de poids faible) dans les emplacements librs (ici le bit 0 et le bit 1), puis le rsultat est stock dans j contient la valeur -56 (car 11...1001000 reprsente -56 en complment deux).

Premier pas dans .Net avec C#2.0 - ( rv. 28.08.2006 )

- Rm di Scala

page

30

Exemples oprateurs arithmtiques


using System; namespace CsAlgorithmique { class AppliOperat_Arithme { static void Main(string[ ] args) { int x = 4, y = 8, z = 3, t = 7, calcul ; calcul = x * y - z + t ; System.Console.WriteLine(" x * y - z + t = "+calcul); calcul = x * y - (z + t) ; System.Console.WriteLine(" x * y - (z + t) = "+calcul); calcul = x * y % z + t ; System.Console.WriteLine(" x * y % z + t = "+calcul); calcul = (( x * y) % z ) + t ; System.Console.WriteLine("(( x * y) % z ) + t = "+calcul); calcul = x * y % ( z + t ) ; System.Console.WriteLine(" x * y % ( z + t ) = "+calcul); calcul = x *(y % ( z + t )); System.Console.WriteLine(" x *( y % ( z + t)) = "+calcul); } }

Rsultats d'excution de ce progamme : x * y - z + t = 36 x * y - (z + t) = 22 x*y%z+t=9 (( x * y) % z ) + t = 9 x*y%(z+t)=2 x *( y % ( z + t)) = 32

Premier pas dans .Net avec C#2.0 - ( rv. 28.08.2006 )

- Rm di Scala

page

31

Exemples oprateurs bit level


using System; namespace CsAlgorithmique { class AppliOperat_BitBoole { static void Main(String[ ] args) { int x, y, z ,t, calcul=0 ; x = 4; // 00000100 y = -5; // 11111011 z = 3; // 00000011 t = 7; // 00000111 calcul = x & y ; System.Console.WriteLine(" x & y = "+calcul); calcul = x & z ; System.Console.WriteLine(" x & z = "+calcul); calcul = x & t ; System.Console.WriteLine(" x & t = "+calcul); calcul = y & z ; System.Console.WriteLine(" y & z = "+calcul); calcul = x | y ; System.Console.WriteLine(" x | y = "+calcul); calcul = x | z ; System.Console.WriteLine(" x | z = "+calcul); calcul = x | t ; System.Console.WriteLine(" x | t = "+calcul); calcul = y | z ; System.Console.WriteLine(" y | z = "+calcul); calcul = z ^ t ; System.Console.WriteLine(" z ^ t = "+calcul); System.Console.WriteLine(" ~x = "+~x+", ~y = "+~y+", ~z = "+~z+", ~t = "+~t); } } }

Rsultats d'excution de ce progamme : x&y =0 x&z =0 x&t =4 y&z =3 x | y = -1 x|z =7 x|t =7 y | z = -5 z^t =4 ~x = -5, ~y = 4, ~z = -4, ~t = -8

Premier pas dans .Net avec C#2.0 - ( rv. 28.08.2006 )

- Rm di Scala

page

32

Exemples oprateurs bit level - Dcalages


using System; namespace CsAlgorithmique { class AppliOperat_BitDecalage { static void Main(String[ ] args) { int x,y, calcul = 0 ; x = -14; // 1...11110010 y = x; calcul = x >>2; // 1...11111100 System.Console.WriteLine(" x >> 2 = "+calcul); calcul = y <<2 ; // 1...11001000 System.Console.WriteLine(" y <<2 = "+calcul); uint x1,y1, calcul1 = 0 ; x1 = 14; // 0...001110 y1 = x1; calcul1 = x1 >> 2; // 0...000011 System.Console.WriteLine(" x1 >> 2 = "+calcul1); calcul1 = y1 <<2 ; // 0...00111000 System.Console.WriteLine(" y1 <<2 = "+calcul1); } } }

Rsultats d'excution de ce progamme : x >> 2 = -4 y <<2 = -56 x1 >> 2 = 3 y1 <<2 = 56

Premier pas dans .Net avec C#2.0 - ( rv. 28.08.2006 )

- Rm di Scala

page

33

Exemples oprateurs boolens


using System; namespace CsAlgorithmique { class AppliOperat_Boole { static void Main(String[ ] args) { int x = 4, y = 8, z = 3, t = 7, calcul=0 ; bool bool1 ; bool1 = x < y; System.Console.WriteLine(" x < y = "+bool1); bool1 = (x < y) & (z == t) ; System.Console.WriteLine(" (x < y) & (z = = t) = "+bool1); bool1 = (x < y) | (z == t) ; System.Console.WriteLine(" (x < y) | (z = = t) = "+bool1); bool1 = (x < y) && (z == t) ; System.Console.WriteLine(" (x < y) && (z = = t) = "+bool1); bool1 = (x < y) || (z == t) ; System.Console.WriteLine(" (x < y) || (z = = t) = "+bool1); bool1 = (x < y) || ((calcul=z) == t) ; System.Console.WriteLine(" (x < y) || ((calcul=z) == t) = "+bool1+" ** calcul = "+calcul); bool1 = (x < y) | ((calcul=z) == t) ; System.Console.WriteLine(" (x < y) | ((calcul=z) == t) = "+bool1+" ** calcul = "+calcul); System.Console.Read(); } } }

Rsultats d'excution de ce progamme : x < y = true (x < y) & (z = = t) = false (x < y) | (z = = t) = true (x < y) && (z = = t) = false (x < y) || (z = = t) = true (x < y) || ((calcul=z) == t) = true ** calcul = 0 (x < y) | ((calcul=z) == t) = true ** calcul = 3

Premier pas dans .Net avec C#2.0 - ( rv. 28.08.2006 )

- Rm di Scala

page

34

Les instructions

Les instructions de base de C# sont identiques syntaxiquement et smantiquement celles de Java, le lecteur qui connat dj le fonctionnement des instructions en Java peut ignorer ces chapitres

1 - les instructions de bloc


Une large partie de la norme ANSI du langage C est reprise dans C# , ainsi que la norme Delphi. Les commentaires sur une ligne dbutent par //.... comme en Delphi Les commentaires sur plusieurs lignes sont encadrs par /* ... */

Ici, nous expliquons les instructions C# en les comparant pascal-delphi. Voici la syntaxe d'une instruction en C#:

instruction :

instruction complte :

Toutes les instructions se terminent donc en C# par un point-virgule " ; "

bloc - instruction compose :


L'lment syntaxique

Premier pas dans .Net avec C#2.0 - ( rv. 28.08.2006 )

- Rm di Scala

page

35

est aussi dnomm bloc ou instruction compose au sens de la visibilit des variables C#.

visibilit dans un bloc - instruction : Exemple de dclarations licites et de visibilit dans 3 blocs instruction imbriqus : int a, b = 12; { int x , y = 8 ; { int z =12; x=z; a=x+1; { int u = 1 ; y=u-b; } } }

schma d'imbrication des 3 blocs

Nous examinons ci-dessous l'ensemble des instructions simples de C#.

2 - l'affectation
C# est un langage de la famille des langages hybrides, il possde la notion d'instruction d'affectation.

Le symbole d'affectation en C# est " = ", soit par exemple :

x=y;
// x doit obligatoirement tre un identificateur de variable.

Affectation simple
L'affectation peut tre utilise dans une expression : soient les instruction suivantes : int a , b = 56 ; a = (b = 12)+8 ; // b prend une nouvelle valeur dans l'expression a = b = c = d =8 ; // affectation multiple

Premier pas dans .Net avec C#2.0 - ( rv. 28.08.2006 )

- Rm di Scala

page

36

simulation d'excution C# : instruction int a , b = 56 ; a = (b = 12)+8 ; valeur de a a = ??? a = 20 valeur de b b = 56 b = 12

3 - Raccourcis et oprateurs d'affectation


Soit op un oprateur appartenant l'ensemble des oprateurs suivant { +, - , * , / , % , << , >> , >>> , & , | , ^ }, Il est possible d'utiliser sur une seule variable le nouvel oprateur op= construit avec l'oprateur op.

x op= y ;

signifie en fait : x = x op y

Il s'agit plus d'un raccourci syntaxique que d'un oprateur nouveau (sa traduction en MSIL est exactement la mme : la traduction de a op= b devrait tre plus courte en instructions pcode que a = a op b). Ci-dessous le code MSIL engendr par i = i+5; et i +=5; est effectivement identique : Code MSIL engendr
IL_0077: IL_0078: IL_0079: IL_007a: ldloc.1 ldc.i4.5 add stloc.1

Instruction C#

i=i+5;

IL_007b: IL_007c: IL_007d: IL_007e:

ldloc.1 ldc.i4.5 add stloc.1

i += 5 ;

Soient les instruction suivantes : int a , b = 56 ; a = -8 ; a += b ; // quivalent : a = a + b b *= 3 ; // quivalent : b = b * 3


Premier pas dans .Net avec C#2.0 - ( rv. 28.08.2006 )
- Rm di Scala

page

37

simulation d'excution C# : instruction int a , b = 56 ; a = -8 ; a += b ; b *= 3 ; valeur de a a = ??? a = -8 a = 48 a = 48 valeur de b b = 56 b = 56 b = 56 b = 168

Remarques : Cas d'une optimisation intressante dans l'instruction suivante : table[ f(a) ] = table[ f(a) ] + x ; // o f(a) est un appel la fonction f qui serait longue calculer. Si l'on rcrit l'instruction prcdente avec l'oprateur += : table[ f(a) ] += x ; // l'appel f(a) n'est effectu qu'une seule fois

Ci-dessous le code MSIL engendr par "table[ f(i) ] = table[ f(i) ] +9 ;" et "table[ f(i) ] += 9 ;" n'est pas le mme : Code MSIL engendr Instruction C#

table[ f(i) ] = table[ f(i) ] + 9 ;


IL_0086: IL_0087: IL_0088: IL_0089: IL_008e: IL_008f: IL_0090: IL_0091: IL_0096: IL_0097: IL_0099: IL_009a: ldloc.3 // adr(table) ldarg.0 ldloc.1 // adr(i) call instance int32 exemple.WinForm::f(int32) ldloc.3 ldarg.0 ldloc.1 call instance int32 exemple.WinForm::f(int32) ldelem.i4 ldc.i4.s 9 add stelem.i4 table[ f(i) ]

table[ f(i) ]

table[ f(i) ] = table[ f(i) ] + 9 ; (suite)

Au total, 12 instructions MSIL dont deux appels : call instance int32 exemple.WinForm::f(int32)

Premier pas dans .Net avec C#2.0 - ( rv. 28.08.2006 )

- Rm di Scala

page

38

Code MSIL engendr

Instruction C#

table[ f(i) ] += 9 ;
IL_009b: IL_009c: IL_009d: IL_009f: IL_00a0: IL_00a1: IL_00a6: IL_00a7: IL_00a9: IL_00ab: IL_00ad: IL_00ae: IL_00b0: IL_00b1: ldloc.3 dup stloc.s CS$00000002$00000000 ldarg.0 ldloc.1 call instance int32 exemple.WinForm::f(int32) dup stloc.s CS$00000002$00000001 ldloc.s CS$00000002$00000000 ldloc.s CS$00000002$00000001 ldelem.i4 ldc.i4.s 9 add stelem.i4 table[ f(i) ]

+=

table[ f(i) ] += 9 ; Au total, 14 instructions MSIL dont un seul appel : call instance int32 exemple.WinForm::f(int32)

Dans l'exemple qui prcde, il y a rellement gain sur le temps d'excution de l'instruction table[ f(i) ] += 9, si le temps d'excution de l'appel f(i) travers l'instruction MSIL < call instance int32 exemple.WinForm::f(int32) > , est significativement long devant les temps d'excution des oprations ldloc et stloc. En fait d'une manire gnrale en C# comme dans les autres langages, il est prfrable d'adopter l'attitude prise en Delphi qui consiste encourager la lisibilit du code en ne cherchant pas crire du code le plus court possible. Dans notre exemple prcdent, la simplicit consisterait utiliser une variable locale x et stocker la valeur de f(i) dans cette variable :

table[ f(i) ] = table[ f(i) ] + 9 ;

x = f(i) ; table[ x ] = table[ x ] + 9 ;

Ces deux critures tant quivalentes seulement si f(i) ne contient aucun effet de bord ! Info MSIL : ldloc : Charge la variable locale un index spcifique dans la pile d'valuation. stloc : Dpile la pile d'valuation et la stocke dans la liste de variables locales un index spcifi.

Premier pas dans .Net avec C#2.0 - ( rv. 28.08.2006 )

- Rm di Scala

page

39

Les instructions conditionnelles

1 - l'instruction conditionnelle

Syntaxe :

Schmatiquement les conditions sont de deux sortes : if ( Expr ) Instr ; if ( Expr ) Instr ; else Instr ;

La dfinition de l'instruction conditionnelle de C# est classiquement celle des langages algorithmiques; comme en pascal l'expression doit tre de type boolen (diffrent du C), la notion d'instruction a t dfinie plus haut.

Exemple d'utilisation du if..else (comparaison avec Delphi) Pascal-Delphi var a , b , c : integer ; .... if b=0 then c := 1 else begin c := a / b; writeln("c = ",c); end; c := a*b ; if c <>0 then c:= c+b else c := a C# int a , b , c ; .... if ( b = = 0 ) c =1 ; else { c = a / b; System.Console.WriteLine ("c = " + c); } if ((c = a*b) != 0) c += b; else c = a;

Premier pas dans .Net avec C#2.0 - ( rv. 28.08.2006 )

- Rm di Scala

page

40

Remarques : L'instruction " if ((c = a*b) != 0) c +=b; else c = a; " contient une affectation intgre dans le test afin de vous montrer les possibilits de C# : la valeur de a*b est range dans c avant d'effectuer le test sur c. Comme Delphi, C# contient le manque de fermeture des instructions conditionnelles ce qui engendre le classique problme du dandling else d'algol, c'est le compilateur qui rsout l'ambigut par rattachement du else au dernier if rencontr (valuation par la gauche. L'instruction suivante est ambigu : if ( Expr1 ) if ( Expr2 ) InstrA ; else InstrB ; Le compilateur rsoud l'ambigit de cette instruction ainsi (rattachement du else au dernier if):

if ( Expr1 ) if ( Expr2 ) InstrA ; else InstrB ;


Comme en pascal, si l'on veut que l'instruction else InstrB ; soit rattache au premier if, il est ncessaire de parenthser (introduire un bloc) le second if :

Exemple de parenthsage du else pendant Pascal-Delphi C#

if Expr1 then begin if Expr2 then InstrA end else InstrB

if ( Expr1 ) { if ( Expr2 ) InstrA ; } else InstrB

2 - l'oprateur conditionnel
Il s'agit ici comme dans le cas des oprateurs d'affectation d'une sorte de raccourci entre l'oprateur conditionnel if...else et l'affectation. Le but tant encore d'optimiser le MSIL engendr.

Syntaxe :

Premier pas dans .Net avec C#2.0 - ( rv. 28.08.2006 )

- Rm di Scala

page

41

O expression renvoie une valeur boolenne (le test), les deux termes valeur sont des expressions gnrales (variable, expression numrique, boolnne etc...) renvoyant une valeur de type quelconque. Smantique : Exemple : int a,b,c ; c = a = = 0 ? b : a+1 ; Si l'expression est true l'oprateur renvoie la premire valeur, (dans l'exemple c vaut la valeur de b) Si l'expression est false l'oprateur renvoie la seconde valeur (dans l'exemple c vaut la valeur de a+1). Smantique de l'exemple avec un if..else : if (a = = 0) c = b; else c = a+1;

Code MSIL engendr


IL_0007: IL_0008: IL_000a: IL_000b: IL_000c: IL_000d: IL_000f: IL_0010: ldloc.0 brfalse.s IL_000f ldloc.0 ldc.i4.1 add br.s IL_0010 ldloc.1 stloc.2

Instruction C#

Oprateur conditionnel :

c = a == 0 ? b : a+1 ;
une seule opration de stockage pour c : IL_0010: stloc.2

IL_0011: IL_0012: IL_0014: IL_0015: IL_0016: IL_0018: IL_0019: IL_001a: IL_001b:

ldloc.0 brtrue.s IL_0018 ldloc.1 stloc.2 br.s IL_001c ldloc.0 ldc.i4.1 add stloc.2

Instruction conditionnelle :

if (a = = 0) c = b; else c = a+1;
deux oprations de stockage pour c : IL_0015: stloc.2 IL_001b: stloc.2

Le code MSIL engendr a la mme structure classique de code de test pour les deux instructions, la traduction de l'oprateur sera lgrement plus rapide que celle de l'instructions car, il n'y a pas besoin de stocker deux fois le rsultat du test dans la variable c (qui ici, est reprsente par l'instruction MSIL stloc.2)

Premier pas dans .Net avec C#2.0 - ( rv. 28.08.2006 )

- Rm di Scala

page

42

Question : utiliser l'oprateur conditionnel pour calculer le plus grand de deux entiers. rponse : int a , b , c ; ... c = a>b ? a : b ;

Question : que fait ce morceau le programme ci-aprs ? int a , b , c ; .... c = a>b ? (b=a) : (a=b) ; rponse : a,b,c contiennent aprs excution le plus grand des deux entiers contenus au dpart dans a et b.

3 - l'oprateur switch...case Syntaxe :


switch :

bloc switch :

Smantique : La partie expression d'une instruction switch doit tre une expression ou une variable du type byte, char, int, short, string ou bien enum. La partie expression d'un bloc switch doit tre une constante ou une valeur immdiate du type byte, char, int, short, string ou bien enum. switch <Epr1> s'appelle la partie slection de l'instruction : il y a valuation de <Epr1> puis selon la valeur obtenue le programme s'excute en squence partir du case contenant la valeur immdiate gale. Il s'agit donc d'un droutement du programme, ds que <Epr1> est value, vers l'instruction tiquete par le case <Epr1> associ.
page

Premier pas dans .Net avec C#2.0 - ( rv. 28.08.2006 )

- Rm di Scala

43

Cette instruction en C#, contrairement Java, est structure , elle doit obligatoirement tre utilise avec l'instruction break afin de simuler le comportement de l'instruction structure case..of du pascal. Exemple de switch..case..break Pascal-Delphi var x : char ; .... case x of 'a' : InstrA; 'b' : InstrB; else InstrElse end; C# char x ; .... switch (x) { case 'a' : InstrA ; break; case 'b' : InstrB ; break; default : InstrElse; break; }

Dans ce cas le droulement de l'instruction switch aprs droutement vers le bon case, est interrompu par le break qui renvoie la suite de l'excution aprs la fin du bloc switch. Une telle utilisation correspond une utilisation de if...else imbriqus (donc une utilisation structure) mais devient plus lisible que les if ..else imbriqus, elle est donc fortement conseille dans ce cas. Exemples : C# - source
int x = 10; switch (x+1) { case 11 : System.Console.WriteLine (">> case 11"); break; case 12 : System.Console.WriteLine (">> case 12"); break; default : System.Console.WriteLine (">> default")"); break; } int x = 11; switch (x+1) { case 11 : System.Console.WriteLine (">> case 11"); break; case 12 : System.Console.WriteLine (">> case 12"); break; default : System.Console.WriteLine (">> default")"); break; }

Rsultats de l'excution

>> case 11

>> case 12

Premier pas dans .Net avec C#2.0 - ( rv. 28.08.2006 )

- Rm di Scala

page

44

Il est toujours possible d'utiliser des instructions if else imbriques pour reprsenter un switch avec break :

Programmes quivalents switch et if...else : C# - switch


int x = 10; switch (x+1) { case 11 : System.Console.WriteLine (">> case 11"); break; case 12 : System.Console.WriteLine (">> case 12"); break; default : System.Console.WriteLine (">> default"); break; } int x = 10; if (x+1= = 11) System.Console.WriteLine (">> case 11"); else if (x+1= = 12) System.Console.WriteLine (">> case 12"); else System.Console.WriteLine (">> default");

C# - if...else

Bien que la syntaxe du switch break soit plus contraignante que celle du caseof de Delphi, le fait que cette instruction apporte commme le caseof une structuration du code, conduit une amlioration du code et augmente sa lisibilit. Lorsque cela est possible, il est donc conseill de l'utiliser d'une manire gnrale comme alternative des if...thenelse imbriqus.

Premier pas dans .Net avec C#2.0 - ( rv. 28.08.2006 )

- Rm di Scala

page

45

Les instructions itratives

1 - l'instruction while Syntaxe :


O expression est une expression renvoyant une valeur boolenne (le test de l'itration). Smantique : Identique celle du pascal (instruction algorithmique tantque .. faire .. ftant) avec le mme dfaut de fermeture de la boucle. Exemple de boucle while Pascal-Delphi while Expr do Instr while Expr do begin InstrA ; InstrB ; ... end C# while ( Expr ) Instr ; while ( Expr ) { InstrA ; InstrB ; ... }

2 - l'instruction do ... while Syntaxe :


O expression est une expression renvoyant une valeur boolenne (le test de l'itration). Smantique : L'instruction "do Instr while ( Expr )" fonctionne comme l'instruction algorithmique rpter Instr jusqu non Expr.

Sa smantique peut aussi tre explique l'aide d'une autre instruction C#, le while( ) :
Premier pas dans .Net avec C#2.0 - ( rv. 28.08.2006 )
- Rm di Scala

page

46

do Instr while ( Expr ) <=> Instr ; while ( Expr ) Instr

Exemple de boucle do...while Pascal-Delphi repeat InstrA ; InstrB ; ... until not Expr do { InstrA ; InstrB ; ... } while ( Expr ) C#

3 - l'instruction for(...) Syntaxe :

Smantique : Une boucle for contient 3 expressions for (Expr1 ; Expr2 ; Expr3 ) Instr, d'une manire gnrale chacune de ces expressions joue un rle diffrent dans l'instruction for. Une instruction for en C# (comme en C) est plus puissante et plus riche qu'une boucle for dans d'autres langages algorithmiques. Nous donnons ci-aprs une smantique minimale : Expr1 sert initialiser une ou plusieurs variables (dont ventuellement la variable de contrle de la boucle) sous forme d'une liste d'instructions d'initialisation spares par des virgules. Expr2 sert donner la condition de rebouclage sous la fome d'une expression renvoyant une valeur boolenne (le test de l'itration). Expr3 sert ractualiser les variables (dont ventuellement la variable de contrle de la boucle)sous forme d'une liste d'instructions spares par des virgules.

Premier pas dans .Net avec C#2.0 - ( rv. 28.08.2006 )

- Rm di Scala

page

47

L'instruction "for (Expr1 ; Expr2 ; Expr3 ) Instr" fonctionne au minimum comme l'instruction algorithmique pour... fpour, elle est toutefois plus puissante que cette dernire. Sa smantique peut aussi tre approximativement(*) explique l'aide d'une autre instruction C# while : Expr1 ; while ( Expr2 ) { Instr ; Expr3 }

for (Expr1 ; Expr2 ; Expr3 ) Instr

(*)Nous verrons au paragraphe consacr l'instruction continue que si l'instruction for contient un continue cette dfinition smantique n'est pas valide. Exemples montrant la puissance du for Pascal-Delphi for i:=1 to 10 do begin InstrA ; InstrB ; ... end i := 10; k := i; while (i>-450) do begin InstrA ; InstrB ; ... k := k+i; i := i-15; end i := n; while i<>1 do if i mod 2 = 0 then i := i div 2 else i := i+1 C# for ( i = 1; i<=10; i++ ) { InstrA ; InstrB ; ... }

for ( i = 10, k = i ;i>-450 ; k += i , i -= 15) { InstrA ; InstrB ; ... }

int i, j ; for ( i = n, j ;i !=1 ; j = i % 2 == 0 ? i /=2 : i++); // pas de corps de boucle !

Le premier exemple montre une boucle for classique avec la variable de contrle "i" (indice de boucle), sa borne initiale "i=1" et sa borne finale "10", le pas d'incrmentation squentiel tant de 1. Le second exemple montre une boucle toujours contrle par une variable "i", mais dont le pas de dcrmentation squentiel est de -15. Le troisme exemple montre une boucle aussi contrle par une variable "i", mais dont la variation n'est pas squentielle puisque la valeur de i est modifie selon sa parit ( i % 2 == 0 ? i /=2 : i++).
- Rm di Scala

Premier pas dans .Net avec C#2.0 - ( rv. 28.08.2006 )

page

48

Voici un exemple de boucle for dite boucle infinie : for ( ; ; ); est quivalente while (true);

Voici une boucle ne possdant pas de variable de contrle(f(x) est une fonction dj dclare) : for (int n=0 ; Math.abs(x-y) < eps ; x = f(x) );

Terminons par une boucle for possdant deux variables de contrle : //inverse d'une suite de caractre dans un tableau par permutation des deux extrmes char [ ] Tablecar ={'a','b','c','d','e','f'} ; for ( i = 0 , j = 5 ; i<j ; i++ , j-- ) { char car ; car = Tablecar[i]; Tablecar[i ]= Tablecar[j]; Tablecar[j] = car; } dans cette dernire boucle ce sont les variations de i et de j qui contrlent la boucle.

Remarques rcapitulatives sur la boucle for en C# : rien n'oblige incrmenter ou dcrmenter la variable de contrle, rien n'oblige avoir une instruction excuter (corps de boucle), rien n'oblige avoir une variable de contrle, rien n'oblige n'avoir qu'une seule variable de contrle.

Premier pas dans .Net avec C#2.0 - ( rv. 28.08.2006 )

- Rm di Scala

page

49

Les instructions de rupture de squence

1 - l'instruction d'interruption break Syntaxe :

Smantique : Une instruction break ne peut se situer qu' l'intrieur du corps d'instruction d'un bloc switch ou de l'une des trois itrations while, do..while, for.

Lorsque break est prsente dans l'une des trois itrations while, do..while, for : break interrompt l'excution de la boucle dans laquelle elle se trouve, l'excution se poursuit aprs le corps d'instruction. Exemple d'utilisation du break dans un for : (recherche squentielle dans un tableau) int [ ] table = {12,-5,7,8,-6,6,4,78}; int elt = 4; for ( i = 0 ; i<8 ; i++ ) if (elt= =table[i]) break ; if (i = = 8)System.Console.WriteLine("valeur : "+elt+" pas trouve."); else System. Console.WriteLine("valeur : "+elt+" trouve au rang :"+i);

Explications Si la valeur de la variable elt est prsente dans le tableau table, lexpression (elt= =table[i]) est true et break est excute (arrt de la boucle et excution de if (i = = 8)... ).
Premier pas dans .Net avec C#2.0 - ( rv. 28.08.2006 )
- Rm di Scala

page

50

Aprs l'excution de la boucle for, lorsque l'instruction if (i = = 8)... est excute, soit la boucle s'est excute compltement (recherche infructueuse), soit le break l'a arrte prmaturment (elt est trouv dans le tableau).

2 - l'instruction de rebouclage continue Syntaxe :

Smantique : Une instruction continue ne peut se situer qu' l'intrieur du corps d'instruction de l'une des trois itrations while, do..while, for. Lorsque continue est prsente dans l'une des trois itrations while, do..while, for : Si continue n'est pas suivi d'une tiquette elle interrompt l'excution de la squence des instructions situes aprs elle, l'excution se poursuit par rebouclage de la boucle. Elle agit comme si l'on venait d'excuter la dernire instruction du corps de la boucle. Si continue est suivi d'une tiquette elle fonctionne comme un goto (utilisation dconseille en programmation moderne, c'est pourquoi nous n'en dirons pas plus !)

Exemple d'utilisation du continue dans un for : int [ ] ta = {12,-5,7,8,-6,6,4,78}, tb = new int[8]; for ( i = 0, n = 0 ; i<8 ; i++ , k = 2*n ) { if ( ta[i] = = 0 ) continue ; tb[n] = ta[i]; n++; }

Explications Rappelons qu'un for s'crit gnralement : for (Expr1 ; Expr2 ; Expr3 ) Instr L'instruction continue prsente dans une telle boucle for s'effectue ainsi : excution immdiate de Expr3 ensuite, excution de Expr2
- Rm di Scala

Premier pas dans .Net avec C#2.0 - ( rv. 28.08.2006 )

page

51

rexcution du corps de boucle.

Si l'expression ( ta[i] = = 0 ) est true, la suite du corps des instructions de la boucle (tb[n] = ta[i]; n++;) n'est pas excute et il y a rebouclage du for . Le droulement est alors le suivant : i++ , k = 2*n en premier , puis la condition de rebouclage : i<8

et la boucle se poursuit en fonction de la valeur de la condition de rebouclage. Cette boucle recopie dans le tableau d'entiers tb les valeurs non nulles du tableau d'entiers ta.

Attention
Nous avons dj signal plus haut que l'quivalence suivante entre un for et un while Expr1 ; while ( Expr2 ) { Instr ; Expr3 }

for (Expr1 ; Expr2 ; Expr3 ) Instr

valide dans le cas gnral, tait mise en dfaut si le corps d'instruction contenait un continue.

Voyons ce qu'il en est en reprenant l'exemple prcdent. Essayons d'crire la boucle while qui lui serait quivalente selon la dfinition gnrale. Voici ce que l'on obtiendrait : i = 0; n = 0 ; while ( i<8 ) { if ( ta[i] = = 0 ) continue ; tb[n] = ta[i]; n++; i++ ; k = 2*n; }

for ( i = 0, n = 0 ; i<8 ; i++ , k = 2*n ) { if ( ta[i] = = 0 ) continue ; tb[n] = ta[i]; n++; }

Premier pas dans .Net avec C#2.0 - ( rv. 28.08.2006 )

- Rm di Scala

page

52

Dans le while le continue rexcute la condition de rebouclage i<8 sans excuter l'expression i++ ; k = 2*n; (nous avons d'ailleurs ici une boucle infinie).

Une boucle while strictement quivalente au for prcdent pourrait tre la suivante : i = 0; n = 0 ; while ( i<8 ) { if ( ta[i] = = 0 ) { i++ ; k = 2*n; continue ; } tb[n] = ta[i]; n++; i++ ; k = 2*n; }

for ( i = 0, n = 0 ; i<8 ; i++ , k = 2*n ) { if ( ta[i] = = 0 ) continue ; tb[n] = ta[i]; n++; }

Premier pas dans .Net avec C#2.0 - ( rv. 28.08.2006 )

- Rm di Scala

page

53

Classes avec mthodes static

Une classe suffit Les mthodes sont des fonctions Transmission des paramtres en C# (plus riche qu'en Visibilit des variables
Avant d'utiliser les possibilits offertes par les classes et les objets en C#, apprenons utiliser et excuter des applications simples C# ne ncessitant pas la construction de nouveaux objets, ni de navigateur pour s'excuter Comme C# est un langage entirement orient objet, un programme C# est compos de plusieurs classes, nous nous limiterons une seule classe.

java)

1 - Une classe suffit


On peut trs grossirement assimiler un programme C# ne possdant qu'une seule classe, un programme principal classique d'un langage de programmation algorithmique. Une classe minimale commence obligatoirement par le mot class suivi de l'identificateur de la classe puis du corps d'implmentation de la classe dnomm bloc de classe. Le bloc de classe est parenths par deux accolades "{" et "}".

Syntaxe d'une classe excutable


Exemple1 de classe minimale : class Exemple1 { } Cette classe ne fait rien et ne produit rien. En fait, une classe quelconque peut s'excuter toute seule condition qu'elle possde dans ses dclarations internes la mthode Main (avec ou sans paramtres) qui sert lancer l'excution de la classe (fonctionnement semblable au lancement dun programme principal).

Premier pas dans .Net avec C#2.0 - ( rv. 28.08.2006 )

- Rm di Scala

page

54

Exemple2 de squelette d'une classe minimale excutable : class Exemple2 { static void Main( ) // sans paramtres { // c'est ici que vous crivez votre programme principal } }

Exemple3 trivial d'une classe minimale excutable : class Exemple3 { static void Main(string[ ] args) // avec paramtres { System.Console.WriteLine("Bonjour !"); } }

Exemples d'applications une seule classe


Nous reprenons deux exemples de programme utilisant la boucle for, dj donns au chapitre sur les instructions, cette fois-ci nous les rcrirons sous la forme d'une application excutable. Exemple1 class Application1 { static void Main(string[ ] args) { /* inverse d'une suite de caractre dans un tableau par permutation des deux extrmes */ char [ ] Tablecar ={'a','b','c','d','e','f'} ; int i, j ; System.Console.WriteLine("tableau avant : " + new string(Tablecar)); for ( i = 0 , j = 5 ; i<j ; i++ , j-- ) { char car ; car = Tablecar[i]; Tablecar[i ]= Tablecar[j]; Tablecar[j] = car; } System.Console.WriteLine("tableau aprs : " + new string(Tablecar)); } } L'instruction "new string(Tablecar)" sert uniquement pour l'affichage, elle cre une string partir du tableau de char.

Premier pas dans .Net avec C#2.0 - ( rv. 28.08.2006 )

- Rm di Scala

page

55

Contrairement java il n'est pas ncessaire en C#, de sauvegarder la classe dans un fichier qui porte le mme nom, tout nom de fichier est accept condition que le suffixe soit cs, ici "AppliExo1.cs". Lorsque l'on demande la compilation (production du bytecode) de ce fichier source "AppliExo1.cs" le fichier cible produit en bytecode MSIL se dnomme "AppliExo1.exe", il est alors prt tre excut par la machine virtuelle du CLR.

Le rsultat de l'excution de ce programme est le suivant : tableau avant : abcdef tableau aprs : fedcba

Exemple2 class Application2 { static void Main(string[ ] args) { // recherche squentielle dans un tableau int [ ] table= {12,-5,7,8,-6,6,4,78}; int elt = 4, i ; for ( i = 0 ; i<8 ; i++ ) if (elt= =table[i]) break ; if (i = = 8) System.Console.WriteLine("valeur : "+elt+" pas trouve."); else System.Console.WriteLine ("valeur : "+elt+" trouve au rang :"+i); } }

Aprs avoir sauvegard la classe dans un fichier xxx.cs, ici dans notre exemple "AppliExo2.cs", la compilation de ce fichier "AppliExo2.cs" produit le fichier "AppliExo2.exe" prt tre excut par la machine virtuelle du CLR.

Le rsultat de l'excution de ce programme est le suivant : valeur : 4 trouve au rang : 6

Conseil de travail : Reprenez tous les exemples simples du chapitre sur les instructions de boucle et le switch en les intgrant dans une seule classe (comme nous venons de le faire avec les deux exemples prcdents) et excutez votre programme.

Premier pas dans .Net avec C#2.0 - ( rv. 28.08.2006 )

- Rm di Scala

page

56

2 - Les mthodes sont des fonctions


Les mthodes ou fonctions reprsentent une encapsulation des instructions qui dterminent le fonctionnement d'une classe. Sans mthodes pour agir, une classe ne fait rien de particulier, dans ce cas elle ne fait que contenir des attributs.

Mthode lmentaire de classe


Bien que C# distingue deux sortes de mthodes : les mthodes de classe et les mthodes d'instance, pour l'instant dans cette premire partie nous dcidons titre pdagogique et simplificateur de n'utiliser que les mthodes de classe, le chapitre sur C# et la programmation oriente objet apportera les complments adquats.

Une mthode de classe commence obligatoirement par le mot clef

static.

Donc par la suite dans ce document lorsque nous emploierons le mot mthode sans autre adjectif, il s'agira d'une mthode de classe, comme nos applications ne possdent qu'une seule classe, nous pouvons assimiler ces mthodes aux fonctions de l'application et ainsi retrouver une utilisation classique de C# en mode application. Attention, il est impossible en C# de dclarer une mthode l'intrieur d'une autre mthode comme en pascal; toutes les mthodes sont au mme niveau de dclaration : ce sont les mthodes de la classe !

Dclaration d'une mthode


La notion de fonction en C# est semblable celle de Java, elle comporte une en-tte avec des paramtres formels et un corps de fonction ou de mthode qui contient les instructions de la mthode qui seront excuts lors de son appel. La dclaration et l'implmentation doivent tre conscutives comme l'indique la syntaxe ci-dessous :

Syntaxe :

corps de fonction :

Premier pas dans .Net avec C#2.0 - ( rv. 28.08.2006 )

- Rm di Scala

page

57

Nous dnommons en-tte de fonction la partie suivante : <qualificateurs><type du rsultat><nom de fonction> (<liste paramtres formels>) Smantique : Les qualificateurs sont des mots clefs permettant de modifier la visibilit ou le fonctionnement d'une mthode, nous n'en utiliserons pour l'instant qu'un seul : le mot clef static permettant de dsigner la mthode qu'il qualifie comme une mthode de classe dans la classe o elle est dclare. Une mthode n'est pas ncessairement qualifie donc ce mot clef peut tre omis. Une mthode peut renvoyer un rsultat d'un type C# quelconque en particulier d'un des types lmentaires (int, byte, short, long, bool, double, float, char...) et nous verrons plus loin qu'elle peut renvoyer un rsultat de type objet comme en Delphi. Ce mot clef ne doit pas tre omis. Il existe en C# comme en C une criture fonctionnelle correspondant aux procdures des langages procduraux : on utilise une fonction qui ne renvoie aucun rsultat. L'approche est inverse celle du pascal o la procdure est le bloc fonctionnel de base et la fonction n'en est qu'un cas particulier. En C# la fonction (ou mthode) est le seul bloc fonctionnel de base et la procdure n'est qu'un cas particulier de fonction dont le retour est de type void. La liste des paramtres formels est semblable la partie dclaration de variables en C# (sans initialisation automatique). La liste peut tre vide. Le corps de fonction est identique au bloc instruction C# dj dfini auparavant. Le corps de fonction peut tre vide (la mthode ne reprsente alors aucun intrt).

Exemples d'en-tte de mthodes sans paramtres en C# int calculer( ){.....} bool tester( ){.....} void uncalcul( ){.....} renvoie un entier de type int renvoie un entier de type bool procdure ne renvoyant rien

Exemples d'en-tte de mthodes avec paramtres en C# int calculer(byte a, byte b, int x ) {.....} bool tester( int k) {.....} void uncalcul(int x, int y, int z ) {.....} fonction 3 paramtres fonction 1 paramtre procdure 3 paramtres

Appel d'une mthode


L'appel de mthode en C# s'effectue trs classiquement avec des paramtres effectifs dont le nombre doit obligatoirement tre le mme que celui des paramtres formels et le type doit tre soit le mme, soit un type compatible ne ncessitant pas de transtypage.
Premier pas dans .Net avec C#2.0 - ( rv. 28.08.2006 )
- Rm di Scala

page

58

Exemple d'appel de mthode-procdure sans paramtres en C#


class Application3 { static void Main(string[ ] args) { afficher( ); } static void afficher( ) { System.Console.WriteLine("Bonjour"); } }

Appel de la mthode afficher

Exemple d'appel de mthode-procdure avec paramtres de mme type en C#


class Application4 { static void Main(string[ ] args) { // recherche squentielle dans un tableau int [ ] table= {12,-5,7,8,-6,6,4,78}; long elt = 4; int i ; for ( i = 0 ; i<=8 ; i++ ) if (elt == table[i]) break ; afficher(i,elt); } static void afficher (int rang , long val) { if (rang == 8) System.Console.WriteLine ("valeur : "+ val+" pas trouve."); else System.Console.WriteLine ("valeur : "+ val +" trouve au rang :"+ rang); }

Appel de la mthode afficher Afficher (i , elt ); Les deux paramtres effectifs "i" et "elt" sont du mme type que le paramtre formel associ. - Le paramtre effectif "i" est associ au paramtre formel rang. - Le paramtre effectif "elt" est associ au paramtre formel val.

3 - Transmission des paramtres


Rappelons tout d'abord quelques principes de base : Dans tous les langages possdant la notion de sous-programme (ou fonction ou procdure), il se pose une question savoir : quoi servent les paramtres formels ? Les paramtres formels dcrits lors de la dclaration d'un sous-programme ne sont que des variables muettes servant expliquer le fonctionnement du sous-programme sur des futures variables lorsque le sousprogramme s'excutera effectivement.

Premier pas dans .Net avec C#2.0 - ( rv. 28.08.2006 )

- Rm di Scala

page

59

La dmarche en informatique est semblable celle qui, en mathmatiques, consiste crire la fonction f(x) = 3*x - 7, dans laquelle x est une variable muette indiquant comment f est calcule : en informatique elle joue le rle du paramtre formel. Lorsque l'on veut obtenir une valeur effective de la fonction mathmatique f, par exemple pour x=2, on crit f(2) et l'on calcule f(2)=3*2 - 7 = -1. En informatique on "passera" un paramtre effectif dont la valeur vaut 2 la fonction. D'une manire gnrale, en informatique, il y a un sous-programme appelant et un sous-programme appel par le sous-programme appelant.

Compatibilit des types des paramtres


Resituons la compatibilit des types entier et rel en C#. Un moyen mnmotechnique pour retenir cette compatibilit est indiqu dans les figures cidessous, par la taille en nombre dcroissant de bits de chaque type que l'on peut mmoriser sous la forme "qui peut le plus peut le moins" ou bien un type n bits accueillera un sous-type p bits, si p est infrieur n.

Compatibilit ascendante des types de variables en C#


(conversion implicite sans transtypage obligatoire) Les types entiers signs : Les types entiers non signs :

Premier pas dans .Net avec C#2.0 - ( rv. 28.08.2006 )

- Rm di Scala

page

60

Exemple d'appel de la mme mthode-procdure avec paramtres de type compatibles en C#


class Application5 { static void Main(string[ ] args) { // recherche squentielle dans un tableau int [ ] table= {12,-5,7,8,-6,6,4,78}; sbyte elt = 4; short i ; for ( i = 0 ; i<8 ; i++ ) if (elt= =table[i]) break ; afficher(i,elt); } static void afficher (int rang , long val) { if (rang == 8) System.Console.WriteLine ("valeur : "+ val+" pas trouve."); else System.Console.WriteLine ("valeur : "+ val +" trouve au rang :"+ rang); } }

Appel de la mthode afficher afficher(i,elt); Les deux paramtres effectifs "i" et "elt" sont d'un type compatible avec celui du paramtre formel associ. - Le paramtre effectif "i" est associ au paramtre formel rang.(short = entier sign sur 16 bits et int = entier sign sur 32 bits) - Le paramtre effectif "elt" est associ au paramtre formel val.(sbyte = entier sign sur 8 bits et long = entier sign sur 64 bits)

Les trois modes principaux de transmission des paramtres sont :

passage par valeur passage par rfrence passage par rsultat


Un paramtre effectif transmis au sous-programme appel est en fait un moyen dutiliser ou daccder une information appartenant au bloc appelant (le bloc appel peut tre le mme que le bloc appelant, il sagit alors de rcursivit).

Premier pas dans .Net avec C#2.0 - ( rv. 28.08.2006 )

- Rm di Scala

page

61

La question technique qui se pose en C# comme dans tout langage de programmation est de connatre le fonctionnement du passage des paramtres :

En C#, ces trois modes de transmission (ou de passage) des paramtres (trs semblables Delphi) sont implants.

3.1 - Les paramtres C# passs par valeur


Le passage par valeur est valable pour tous les types lmentaires (int, byte, short, long, bool, double, float, char) et les objets.

En C# tous les paramtres sont passs par dfaut par valeur (lorsque le paramtre est un objet, c'est en fait la rfrence de l'objet qui est passe par valeur). Pour ce qui est de la vision algorithmique de C#, le passage par valeur permet une variable d'tre passe comme paramtre d'entre.
static int methode1(int a , char b) { //....... return a+b; }

Premier pas dans .Net avec C#2.0 - ( rv. 28.08.2006 )

- Rm di Scala

page

62

Cette mthode possde 2 paramtres a et b en entre passs par valeur et renvoie un rsultat de type int.

3.2 - Les paramtres C# passs par rfrence


Le passage par rfrence est valable pour tous les types de C#.

En C# pour indiquer un passage par rfrence on prcde la dclaration du paramtre formel du mot clef ref :
static int methode1(int a , ref char b) { //....... return a+b; }

Lors de l'appel d'un paramtre pass par rfrence, le mot clef ref doit obligatoirement prcder le paramtre effectif qui doit obligatoirement avoir t initialis auparavant : int x = 10, y = '$', z = 30; z = methode1(x, ref y) ;

3.3 - Les paramtres C# passs par rsultat


Le passage par rsultat est valable pour tous les types de C#.

En C# pour indiquer un passage par rsultat on prcde la dclaration du paramtre formel du mot
Premier pas dans .Net avec C#2.0 - ( rv. 28.08.2006 )
- Rm di Scala

page

63

out :
static int methode1(int a , out char b) { //....... return a+b; }

Lors de l'appel d'un paramtre pass par rsultat, le mot clef out doit obligatoirement prcder le paramtre effectif qui n'a pas besoin d'avoir t initialis : int x = 10, y , z = 30; z = methode1(x, out y) ;

Remarque : Le choix de passage selon les types limine les inconvnients ds l'encombrement mmoire et la lenteur de recopie de la valeur du paramtre par exemple dans un passage par valeur, car nous verrons plus loin que les tableaux en C# sont des objets et que leur structure est passe par rfrence.

3.4 - Les retours de rsultats de mthode type fonction


Les mthodes de type fonction en C#, peuvent renvoyer un rsultat de n'importe quel type et acceptent des paramtres de tout type. Une mthode- fonction ne peut renvoyer qu'un seul rsultat comme en Java, mais l'utilisation des passages de paramtres par rfrence ou par rsultat, permet aussi d'utiliser les paramtres de la fonction comme des variables de rsultats comme en Delphi. En C# comme en Java le retour de rsultat est pass grce au mot clef return plac n'importe o dans le corps de la mthode.

Syntaxe :

L'expression lorsqu'elle est prsente est quelconque mais doit tre obligatoirement du mme type que le type du rsultat dclar dans l'en-tte de fonction (ou d'un type compatible). Lorsque le return est rencontr il y a arrt de l'excution du code de la mthode et retour du rsultat dans le bloc appelant.

Premier pas dans .Net avec C#2.0 - ( rv. 28.08.2006 )

- Rm di Scala

page

64

4 - Visibilits des variables


Le principe de base est que les variables en C# sont visibles (donc utilisables) dans le bloc dans lequel elles ont t dfinies.

Visibilit de bloc
C# est un langage structure de blocs ( comme pascal et C ) dont le principe gnral de visibilit est : Toute variable dclare dans un bloc est visible dans ce bloc et dans tous les blocs imbriqus dans ce bloc.

En C# les blocs sont constitus par : les classes, les mthodes, les instructions composes, les corps de boucles, les try...catch

Le masquage des variables n'existe que pour les variables dclares dans des mthodes : Il est interdit de redfinir une variable dj dclare dans une mthode soit : comme paramtre de la mthode, comme variable locale la mthode, dans un bloc inclus dans la mthode. Il est possible de redfinir une variable dj dclare dans une classe.

Variables dans une classe, dans une mthode


Les variables dfinies (dclares, et/ou initialises) dans une classe sont accessibles toutes les mthodes de la classe, la visibilit peut tre modifie par les qualificateurs public ou private que nous verrons au chapitre C# et POO.

Premier pas dans .Net avec C#2.0 - ( rv. 28.08.2006 )

- Rm di Scala

page

65

Exemple de visibilit dans une classe


La variable "a" dfinie dans int a =10; : class ExempleVisible1 { int a = 10; int g (int x ) { return 3*x-a; } int f (int x, int a ) { return 3*x-a; } } - Est une variable de la classe ExempleVisible. - Elle est visible dans la mthode g et dans la mthode f. C'est elle qui est utilise dans la mthode g pour valuer l'expression 3*x-a. - Dans la mthode f, elle est masque par le paramtre du mme nom qui est utilis pour valuer l'expression 3*x-a.

Contrairement ce que nous avions signal plus haut nous n'avons pas prsent un exemple fonctionnant sur des mthodes de classes (qui doivent obligatoirement tre prcdes du mot clef static), mais sur des mthodes d'instances dont nous verrons le sens plus loin en POO.

Remarquons avant de prsenter le mme exemple cette fois-ci sur des mthodes de classes, que quelque soit le genre de mthode la visibilit des variables est identique.

Exemple identique sur des mthodes de classe


La variable "a" dfinie dans static int a =10; : class ExempleVisible2 { static int a = 10; static int g (int x ) { return 3*x-a; } static int f (int x, int a ) { return 3*x-a; } } - Est une variable de la classe ExempleVisible. - Elle est visible dans la mthode g et dans la mthode f. C'est elle qui est utilise dans la mthode g pour valuer l'expression 3*x-a. - Dans la mthode f, elle est masque par le paramtre du mme nom qui est utilis pour valuer l'expression 3*x-a.

Les variables dfinies dans une mthode (de classe ou d'instance) suivent les rgles classiques de la visibilit du bloc dans lequel elles sont dfinies : Elles sont visibles dans toute la mthode et dans tous les blocs imbriqus dans cette mthode et seulement ce niveau (les autres mthodes de la classe ne les voient pas), c'est pourquoi on emploie aussi le terme de variables locales la mthode.

Premier pas dans .Net avec C#2.0 - ( rv. 28.08.2006 )

- Rm di Scala

page

66

Reprenons l'exemple prcdent en adjoignant des variables locales aux deux mthodes f et g. Exemple de variables locales
class ExempleVisible3 { static int a = 10; static int g (int x ) { char car = 't'; long a = 123456; .... return 3*x-a; } static int f (int x, int a ) { char car ='u'; .... return 3*x-a; } } La variable de classe "a" dfinie dans static int a = 10; est masque dans les deux mthodes f et g. Dans la mthode g, c'est la variable locale long a = 123456 qui masque la variable de classe static int a. char car ='t'; est une variable locale la mthode g. - Dans la mthode f, char car ='u'; est une variable locale la mthode f, le paramtre int a masque la variable de classe static int a. Les variables locales char car n'existent que dans la mthode o elles sont dfinies, les variables "car" de f et celle de g n'ont aucun rapport entre elles, bien que portant le mme nom.

Variables dans un bloc autre qu'une classe ou une mthode


Les variables dfinies dans des blocs du genre instructions composes, boucles, trycatch ne sont visibles que dans le bloc et ses sous-blocs imbriqus, dans lequel elles sont dfinies. Toutefois attention aux redfinitions de variables locales. Les blocs du genre instructions composes, boucles, trycatch ne sont utiliss qu' l'intrieur du corps d'une mthode (ce sont les actions qui dirigent le fonctionnement de la mthode), les variables dfinies dans de tels blocs sont automatiquement considres par C# comme des variables locales la mthode. Tout en respectant l'intrieur d'une mthode le principe de visibilit de bloc, C# n'accepte pas le masquage de variable l'intrieur des blocs imbriqus. Nous donnons des exemples de cette visibilit : Exemple correct de variables locales class ExempleVisible4 { static int a = 10, b = 2; static int f (int x ) { char car = 't'; for (int i = 0; i < 5 ; i++) {int a=7; if (a < 7) {int b = 8; b = 5-a+i*b; } else b = 5-a+i*b; La variable de classe "a" dfinie dans static int a =
- Rm di Scala

Premier pas dans .Net avec C#2.0 - ( rv. 28.08.2006 )

page

67

} return 3*x-a+b; } }

10; est masque dans la mthode f dans le bloc imbriqu for. La variable de classe "b" dfinie dans static int b = 2; est masque dans la mthode f dans le bloc imbriqu if. Dans l'instruction { int b = 8; b = 5-a+i*b; } , c'est la variable b interne ce bloc qui est utilise car elle masque la variable b de la classe. Dans l'instruction else b = 5-a+i*b; , c'est la variable b de la classe qui est utilise (car la variable int b = 8 n'est plus visible ici) .

Exemple de variables locales gnrant une erreur

class ExempleVisible5 { static int a = 10, b = 2; static int f (int x ) { char car = 't'; for (int i = 0; i < 5 ; i++) {int a=7; if (a < 7) {int b = 8, a = b = 5-a+i*b; } else b = 5-a+i*b; }

9;

Toutes les remarques prcdentes restent valides puisque l'exemple ci-contre est quasiment identique au prcdent. Nous avons seulement rajout dans le bloc if la dfinition d'une nouvelle variable interne a ce bloc. C# produit une erreur de compilation int b = 8, a = 9; sur la variable a, en indiquant que c'est une redfinition de
- Rm di Scala

Premier pas dans .Net avec C#2.0 - ( rv. 28.08.2006 )

page

68

return 3*x-a+b; } }

variable l'intrieur de la mthode f, car nous avions dj dfini une variable a ({ int a=7;...) dans le bloc englobant for {...}.

Remarquons que le principe de visiblit des variables adopt en C# est identique au principe inclus dans tous les langages structures de bloc y compris pour le masquage, s'y rajoute en C# comme en Java, uniquement l'interdiction de la redfinition l'intrieur d'une mme mthode (semblable en fait, l'interdiction de redclaration sous le mme nom, de variables locales un bloc).

Premier pas dans .Net avec C#2.0 - ( rv. 28.08.2006 )

- Rm di Scala

page

69

Spcificits du compilateur C#
Le compilateur C# n'accepte pas que l'on utilise une variable de classe qui a t redfinie dans un bloc interne une mthode, sans la qualifier par la notation uniforme aussi appele opration de rsolution de porte. Les 4 exemples ci-dessous situent le discours : class ClA1 {
Variables de classe Variables de classe

static int a = 10, b = 2; static int f (int x ) { char car = 't'; for (int i = 0; i < 5 ; i++) {int a=7; 'a' redfinie if (a < 7) { b = 5-a+i*b; Refus 'a' : conflit } else b = 10-a+i*b; } return 3*x-a+b; erreur return 3*x- ClA1.a+b; } }
Variables de classe accept

class ClA2 { static int a = 10, b = 2; ' b' redfinie static int f (int x ) { char car = 't'; int b = 8; for (int i = 0; i < 5 ; i++) {int a=7; ' a' redfinie if (a < 7) { b = 5-a+i*b; Refus 'a' : conflit } else b = 10-a+i*b; } return 3*x-a+b; erreur return 3*x- ClA2.a+b; } accept }
Variables de classe

class ClA3 { static int a = 10, b = 2; static int f (int x ) { char car = 't'; ' a' redfinie for (int i = 0; i < 5 ; i++) {int a=7; ' b' redfinie if (a < 7) { int b = 8; b = 5-a+i*b; Refus 'b' : conflit } else b = 10-a+i*b; else ClA3.b = 10-a+i* ClA3.b; } accept
Refus 'a' et 'b' : conflit

class ClA4 { static int a = 10, b = 2; static int f (int x ) { char car = 't'; for (int i = 0; i < 5 ; i++) {int a=7; int b = 8; if (a < 7) ' a' et ' b' redfinies { b = 5-a+i*b; } else b = 10-a+i*b; } return 3*x-a+b; Refus 'a' et 'b' : conflit return 3*x- ClA4.a+ ClA4.b; } }

return 3*x-a+b; return 3*x- ClA3.a+ ClA3.b; } }


accept

accept

Premier pas dans .Net avec C#2.0 - ( rv. 28.08.2006 )

- Rm di Scala

page

70

Observez les utilisations et les redfinitions correctes des variables "static int a = 10, int b = 2;" dclares comme variables de classe et redfinies dans diffrents blocs de la classe (lorsqu'il y a un conflit signal par le compilateur sur une instruction nous avons mis tout de suite aprs le code correct accept par le compilateur) L o le compilateur C# dtecte un conflit potentiel, il suffit alors de qualifier la variable grce l'oprateur de rsolution de porte, comme par exemple ClasseA.b pour indiquer au compilateur la variable que nous voulons utiliser.

Comparaison C# , java : la mme classe gauche passe en Java mais fournit 6 conflits en C#

En Java
class ClA5 { static int a = 10, b = 2; static int f (int x ) { char car = 't'; for (int i = 0; i < 5 ; i++) { if (a < 7) { int b = 8 , a = 7; b = 5-a+i*b; } else b = 10-a+i* b; } return 3*x- a+ b; } }

Conflits levs en C#
class ClA5 { static int a = 10, b = 2; static int f (int x ) Evite le conflit { char car = 't'; for (int i = 0; i < 5 ; i++) { if (ClA5.a < 7) { int b = 8 , a = 7; b = 5-a+i*b; } else ClA5.b = 10-ClA5.a+i*ClA5.b; } return 3*x- ClA5.a+ ClA5.b; } }

' a' et ' b' redfinies

Premier pas dans .Net avec C#2.0 - ( rv. 28.08.2006 )

- Rm di Scala

page

71

Les chanes de caractres string

La classe string
Le type de donnes String (chane de caractre) est une classe de type rfrence dans l'espace de noms System de .NET Framework. Donc une chane de type string est un objet qui n'est utilisable qu' travers les mthodes de la classe string. Le type string est un alias du type System.String dans .NET

Un littral de chane est une suite de caractres entre guillemets : " abcdef " est un exemple de littral de String. Toutefois un objet string de C# est immuable (son contenu ne change pas) Etant donn que cette classe est trs utilise les variables de type string bnficient d'un statut d'utilisation aussi souple que celui des autres types lmentaires par valeurs. On peut les considrer comme des listes de caractres Unicode numrots de 0 n-1 (si n figure le nombre de caractres de la chane). Il est possible d'accder chaque caractre de la chane en la considrant comme un tableau de caractres en lecture seule.

Accs une chane string


Dclaration d'une variable String Dclaration d'une variable String avec initialisation On accde la longueur d'une chane par la proprite : int Length String str1; String str1 = " abcdef "; Instancie un nouvel objet : "abcdef "

String str1 = "abcdef"; int longueur; longueur = str1.Length ; // ici longueur = 6

Premier pas dans .Net avec C#2.0 - ( rv. 28.08.2006 )

- Rm di Scala

page

72

On accde un caractre de rang fix d'une chane par l'oprateur [ ] : (la chane est lue comme un tableau de char) Il est possible d'accder en lecture seulement chaque caractres d'une chane, mais qu'il est impossible de modifier un caractre directement dans une chane.

String ch1 = "abcdefghijk"; Reprsentation interne de l'objet ch1 de type string :

char car = ch1[4] ; // ici la variable car contient la lettre 'e'

Remarque En fait l'oprateur [ ] est un indexeur de la classe string (cf. chapitre indexeurs en C#), et il est en lecture seule : public char this [ int index ] { get ; } Ce qui signifie au stade actuel de comprhension de C#, qu'il est possible d'accder en lecture seulement chaque caractre d'une chane, mais qu'il est impossible de modifier un caractre grce l'indexeur. char car = ch1[7] ; // l'indexeur renvoie le caractre 'h' dans la variable car. ch1[5] = car ; // Erreur de compilation : l'criture dans l'indexeur est interdite !! ch1[5] = 'x' ; // Erreur de compilation : l'criture dans l'indexeur est interdite !!

Oprations de base sur une chane string


Le type string possde des mthodes d'insertion, modification et suppression : mthodes Insert, Copy, Concat,...

Position d'une sous-chane l'intrieur d'une chane donne : Mthode surcharge 6 fois : int IndexOf ( ) Ci-contre une utilisation de la surcharge : int IndexOf ( string ssch) qui renvoie l'indice de la premire occurrence du string ssch contenue dans la chane scanne.

Recherche de la position de ssch dans ch1 : String ch1 = " abcdef " , ssch="cde";

int rang ; rang = ch1.IndexOf ( ssch ); // ici la variable rang vaut 2

Premier pas dans .Net avec C#2.0 - ( rv. 28.08.2006 )

- Rm di Scala

page

73

Concatnation de deux chanes Un oprateur ou une mthode Oprateur : + sur les chanes ou Mthode static surcharge 8 fois : String Concat() Les deux critures ci-dessous sont donc quivalentes en C# : str3 = str1+str2 str3 = str1.Concat(str2)

String str1,str2,str3; str1="bon"; str2="jour"; str3=str1+str2;

Insertion d'une chane dans une autre chane

Soit :

Appel de la mthode Insert de la chane ch1 afin de construire une nouvelle chane ch2 qui est une copie de ch1 dans laquelle on a insr une sous-chane, ch1 n'a pas chang (immuabilit).

Puis invocation de la mthode Insert sur ch1 :

ch2 est une copie de ch1 dans laquelle on a insrer la sous-chane "..x..". Attention : Les mthodes d'insertion, suppression, etcne modifient pas la chane objet qui invoque la mthode mais renvoient un autre objet de chane diffrent, obtenu aprs action de la mthode sur l'objet initial.

Premier pas dans .Net avec C#2.0 - ( rv. 28.08.2006 )

- Rm di Scala

page

74

Convertir une chane string en tableau de caractres


Si l'on souhaite se servir d'une string comme un tableau de char, il faut utiliser la mthode ToCharArray qui convertit la chane en un tableau de caractres contenant tous les caractres de la chane. Soient les lignes de programme suivantes : string str1 = "abcdef" ; char [ ] tCarac ; tCarac = str1.ToCharArray( ) ; tCarac = "abcdefghijk".ToCharArray( ); Illustrons ces lignes par des schmas de rfrences :

string str1 = "abcdef" ;

char [ ] tCarac ; tCarac = str1.ToCharArray( ) ;

tCarac = "abcdefghijk".ToCharArray( );

Premier pas dans .Net avec C#2.0 - ( rv. 28.08.2006 )

- Rm di Scala

page

75

Oprateurs d'galit et d'ingalit de string


L'oprateur d'galit = = , dtermine si deux objets string spcifis ont la mme valeur, il se comporte comme sur des lments de type de base (int, char,...) string a , b ; (a = = b ) renvoie true si la valeur de a est la mme que la valeur de b ; sinon il renvoie false. public static bool operator == ( string a, string b ); Cet oprateur est surcharg et donc il compare les valeurs effectives des chanes et non leur rfrences, il fonctionne comme la mthode public bool Equals( string value ) de la classe string, qui teste l'galit de valeur de deux chanes. Voici un morceau de programme qui permet de tester l'oprateur d'galit = = et la mthode Equals : string s1,s2,s3,ch; ch = "abcdef"; s1 = ch; s2 = "abcdef"; s3 = new string("abcdef".ToCharArray( ));

System.Console.WriteLine("s1="+s1); System.Console.WriteLine("s2="+s2); System.Console.WriteLine("s3="+s3); System.Console.WriteLine("ch="+ch); if( s2 == ch )System.Console.WriteLine("s2=ch"); else System.Console.WriteLine("s2<>ch"); if( s2 == s3 )System.Console.WriteLine("s2=s3"); else System.Console.WriteLine("s2<>s3"); if( s3 == ch )System.Console.WriteLine("s3=ch"); else System.Console.WriteLine("s3<>ch"); if( s3.Equals(ch) )System.Console.WriteLine("s3 gal ch"); else System.Console.WriteLine("s3 diffrent de ch");

Premier pas dans .Net avec C#2.0 - ( rv. 28.08.2006 )

- Rm di Scala

page

76

Aprs excution on obtient : s1=abcdef s2=abcdef s3=abcdef ch=abcdef s2=ch s2=s3 s3=ch s3 gal ch

POUR LES HABITUES DE JAVA : ATTENTION


L'oprateur d'galit == en Java (Jdk1.4.2) n'est pas surcharg, il ne fonctionne pas totalement de la mme faon que l'oprateur == en C#, car il ne compare que les rfrences. Donc des programmes en apparence syntaxiquement identiques dans les deux langages, peuvent produire des rsultats d'excution diffrents :

Programme Java
String ch; ch = "abcdef" ; String s2,s1="abc" ; s2 = s1+"def"; //-- tests d'galit avec l'oprateur = = if( s2 == "abcdef" ) System.out.println ("s2==abcdef"); else System.out.println ("s2<>abcdef"); if( s2 == ch ) System.out.println ("s2==ch"); else System.out.println ("s2<>ch");

Programme C#
string ch; ch = "abcdef" ; string s2,s1="abc" ; s2 = s1+"def"; //-- tests d'galit avec l'oprateur = = if( s2 == "abcdef" ) System.Console.WriteLine ("s2==abcdef"); else System.Console.WriteLine ("s2<>abcdef"); if( s2 == ch ) System.Console.WriteLine ("s2==ch"); else System.Console.WriteLine ("s2<>ch");

Rsultats d'excution du code Java : s2<>abcdef s2<>ch

Rsultats d'excution du code C# : s2==abcdef s2==ch

Rapport entre string et char


Une chane string contient des lments de base de type char, comment passe-t-on de l'un l'autre type ?

Premier pas dans .Net avec C#2.0 - ( rv. 28.08.2006 )

- Rm di Scala

page

77

1) On ne peut pas considrer un char comme un cas particulier de string, le transtypage suivant est refus comme en Java : char car = 'r'; string s; s = (string)car; Il faut utiliser l'une des surcharges de la mthode de conversion ToString de la classe Convert : System.Object |__System.Convert mthode de classe static :

public static string ToString( char c );


Le code suivant est correct, il permet de stocker un caractre char dans une string : char car = 'r'; string s; s = Convert.ToString (car);

Remarque : La classe Convert contient un grand nombre de mthodes de conversion de types. Microsoft indique que cette classe : "constitue une faon, indpendante du langage, d'effectuer les conversions et est disponible pour tous les langages qui ciblent le Common Language Runtime. Alors que divers langages peuvent recourir diffrentes techniques pour la conversion des types de donnes, la classe Convert assure que toutes les conversions communes sont disponibles dans un format gnrique."

2) On peut concatner avec l'oprateur +, des char une chane string dj existante et affecter le rsultat une String : string s1 , s2 ="abc" ; char c = 'e' ; s1 = s2 + 'd' ; s1 = s2 + c ;

Premier pas dans .Net avec C#2.0 - ( rv. 28.08.2006 )

- Rm di Scala

page

78

Toutes les critures prcdentes sont licites et acceptes par le compilateur C#, il n'en est pas de mme pour les critures ci-aprs :

Les critures suivantes seront refuses : String s1 , s2 ="abc" ; char c = 'e' ; s1 = 'd' + c ; // types incompatibles

Ecritures correctes associes : String s1 , s2 ="abc" ; char c = 'e' ; s1 = "d" + Convert.ToString ( c) ;

s1 = 'd' + 'e'; // types incompatibles

s1 = "d" + "e"; s1 = "d" + 'e'; s1 = 'd' + "e";

Le compilateur enverra le message d'erreur suivant pour l'instruction l'instruction s1 = 'd' + 'e'; :

s1 = 'd' + c ; et pour

[C# Erreur] : Impossible de convertir implicitement le type 'int' en 'string'

Car il faut qu'au moins un des deux oprandes de l'oprateur + soit du type string : Le littral 'e' est de type char, Le littral "e" est de type string (chane ne contenant qu'un seul caractre)

Pour plus d'information sur toutes les mthodes de la classe string consulter la documentation de .Net framework.

Premier pas dans .Net avec C#2.0 - ( rv. 28.08.2006 )

- Rm di Scala

page

79

Tableaux et matrices

Gnralits sur les tableaux


Ds que l'on travaille avec de nombreuses donnes homognes ( de mme type) la premire structure de base permettant le regroupement de ces donnes est le tableau. C# comme tous les langages algorithmiques propose cette structure au programmeur. Comme pour les string et pour des raisons d'efficacit dans l'encombrement mmoire, les tableaux sont grs par C# , comme des objets de type rfrence (donc sur le tas), leur type hrite de la classe abstraite System.Array.. Les tableaux C# sont indexs uniquement par des entiers (char, int, long,) et sur un intervalle fixe partir de zro. Un tableau C# peut avoir de une ou plusieurs dimensions, nous avons donc les varits suivantes de tableaux dans le CLR :

Tableaux une dimension. Tableaux plusieurs dimensions (matrices,) comme en Delphi. Tableaux dchiquets comme en Java.

Chaque dimension d'un tableau en C# est dfinie par une valeur ou longueur, qui est un nombre ou une variable N entier (char, int, long,) dont la valeur est suprieur ou gal zro. Lorsqu'une dimension a une longueur N, l'indice associ varie dans l'intervalle [ 0 , N 1 ].

Tableau uni-dimensionnel
Ci-dessous un tableau 'tab' une dimension, de n+1 cellules numrotes de 0 n :

Les tableaux C# contiennent comme en Delphi, des objets de types quelconques de C# (type rfrence ou type valeur). Il n'y a pas de mot clef spcifique pour la classe tableaux, mais l'oprateur symbolique [ ] indique qu'une variable de type fix est un tableau. La taille d'un tableau doit obligatoirement avoir t dfinie avant que C# accepte que vous l'utilisiez !
page

Premier pas dans .Net avec C#2.0 - ( rv. 28.08.2006 )

- Rm di Scala

80

Remarque : Les tableaux de C# sont des objets d'une classe dnomme Array qui est la classe de base d'implmentation des tableaux dans le CLR de .NET framework (localisation : System.Array) Cette classe n'est pas drivable pour l'utilisateur : "public abstract class Array : ICloneable, IList, ICollection, IEnumerable". C'est en fait le compilateur qui est autoris implmenter une classe physique de tableau. Il faut utiliser les tableaux selon la dmarche ci-dessous en sachant que l'on dispose en plus des proprits et des mthodes de la classe Array si ncessaire (longueur, tri, etc...)

Dclaration d'une variable de tableau, rfrence seule:


int [ ] table1; char [ ] table2; float [ ] table3; ... string [ ] tableStr; Voici une image de ce qui est prsent en mmoire centrale aprs ces dclarations :

Dclaration d'une variable de tableau avec dfinition explicite de taille :


int [ ] table1 = new int [5]; char [ ] table2 = new char [5]; float [ ] table3 = new float [5]; ... string [ ] tableStr = new String [5]; Voici une image de ce qui est prsent en mmoire centrale aprs ces dclarations :

Premier pas dans .Net avec C#2.0 - ( rv. 28.08.2006 )

- Rm di Scala

page

81

Chaque variable de refrence pointe vers un tableau de longueur 5, chaque cellule d'un tableau contient la valeur null. Le mot clef new correspond la cration d'un nouvel objet (un nouveau tableau) dont la taille est fixe par la valeur indique entre les crochets. Ici 4 tableaux sont cres et prts tre utiliss : table1 contiendra 5 entiers 32 bits, table2 contiendra 5 caractres, table3 contiendra 5 rels en simple prcision et tableStr contiendra 5 chanes de type string. On peut aussi dclarer un tableau sous la forme de deux instructions : une instruction de dclaration et une instruction de dfinition de taille avec le mot clef new, la seconde pouvant tre mise n'importe o dans le corps d'instruction, mais elle doit tre utilise avant toute manipulation du tableau. Cette dernire instruction de dfinition peut tre rpte plusieurs fois dans le programme, il s'agira alors chaque fois de la cration d'un nouvel objet (donc un nouveau tableau), l'ancien tant dtruit et dsallou automatiquement par le ramasse-miettes (garbage collector) de C#. int [ ] table1; char [ ] table2; float [ ] table3; string [ ] tableStr; .... table1 = new int [5]; table2 = new char [12]; table3 = new float [8]; tableStr = new string [9];

Dclaration et initialisation avec dfinition implicite de taille :


int [ ] table1 = {17,-9,4,3,57}; char [ ] table2 = {'a','j','k','m','z'}; float [ ] table3 = {-15.7f, 75, -22.03f, 3 ,57 }; string [ ] tableStr = {"chat","chien","souris","rat","vache"}; Dans cette ventualit C# cre le tableau, calcule sa taille et l'initialise avec les valeurs fournies. Voici une image de ce qui est prsent en mmoire centrale aprs ces instructions :

Premier pas dans .Net avec C#2.0 - ( rv. 28.08.2006 )

- Rm di Scala

page

82

Il existe en C# un attribut de la classe abstraite mre Array, qui contient la taille d'un tableau uni-dimensionnel, quelque soit son type, c'est la proprit Length en lecture seule. Exemple :
int [ ] table1 = {17,-9, 4, 3, 57}; int taille; taille = table1.Length; // taille = 5

Attention
Il est possible de dclarer une rfrence de tableau, puis de l'initialiser aprs uniquement ainsi :
int [ ] table1 ; // cre une rfrence table1 de type tableau de type int table1 = new int {17,-9, 4, 3, 57}; // instancie un tableau de taille 5 lments refrenc par table1

table1 = new int {5, 0, 8}; // instancie un autre tableau de taille 3 lments refrenc par table1

table1 = new int {1, 2, 3, 4}; // instancie un autre tableau de taille 3 lments refrenc par table1

Les cellules du tableau prcdent sont perdues ds que l'on instancie table1 vers un nouveau tableau, leurs emplacements en mmoire centrale sont rendus au systme de gestion de la mmoire (Garbage Collector). L'criture ci-dessous engendre une erreur la compilation :
int [ ] table1 ; // cre une rfrence table1 de type tableau de type int table1 = {17,-9,4,3,57}; // ERREUR de compilation, correction : int [ ] table1 = {17,-9,4,3,57};

Premier pas dans .Net avec C#2.0 - ( rv. 28.08.2006 )

- Rm di Scala

page

83

Utiliser un tableau
Un tableau en C# comme dans les autres langages algorithmiques s'utilise travers une cellule de ce tableau repre par un indice obligatoirement de type entier ou un char considr comme un entier (byte, short, int, long ou char). Le premier lment d'un tableau est numrot 0, le dernier Length-1. On peut ranger des valeurs ou des expressions du type gnral du tableau dans une cellule du tableau. Exemple avec un tableau de type int :
int [ ] table1 = new int [5]; // dans une instruction d'affectation: table1[0] = -458; table1[4] = 5891; table1[5] = 72; <--- est une erreur de dpassement de la taille ! (valeur entre 0 et 4) // dans une instruction de boucle: for (int i = 0 ; i<= table1.Length-1; i++) table1[i] = 3*i-1; // aprs la boucle: table1 = {-1,2,5,8,11}

Mme exemple avec un tableau de type char :


char [ ] table2 = new char [7]; table2[0] = '?' ; table2[4] = 'a' ; table2[14] = '#' ; <--- est une erreur de dpassement de la taille for (int i = 0 ; i<= table2.Length-1; i++) table2[i] =(char)('a'+i); // aprs la boucle: table2 = {'a', 'b', 'c' ,'d', 'e', 'f'}

Remarque : Dans une classe excutable la mthode Main reoit en paramtre un tableau de string nomm args qui correspond en fait aux ventuels paramtres de l'application elle-mme: static void Main(string [ ] args)

Affectation de tableaux, recopie de tableaux uni-dimensionnels


Un tableau est une refrence pointant vers un ensemble de cellules en mmoire centrale (c'est un
Premier pas dans .Net avec C#2.0 - ( rv. 28.08.2006 )
- Rm di Scala

page

84

objet), il est tout fait possible d'effectuer une affectation entre deux variables de tableaux de mme type mais de taille gale ou diffrente, il ne s'agit alors pas d'une recopie de tableaux : ce sont en fait les pointeurs qui sont recopis l'un dans l'autre par l'affectation et non pas les tableaux eux-mmes.

Instruction int [ ]table1 = new int [9]; int [ ]table2 = new int [6];

Image en mmoire centrale

table2 = table1

Aprs l'affectation : table1 et table2 pointent vers le mme ensemble de cellules (le mme tableau) Toute modification sur table1 ou table2 a lieu sur les mmes cellules :
Instructions Image en mmoire centrale

int [ ]table1 = new int [9];

int [ ]table2 = new int [6];

for(int k = 0; k<=8; k++) table1(k) = k * 10 ; for(int k = 0; k<=5; k++) table2(k) = k ;

table2 = table1;

table1 et table2 maintenant se refrent la mme entit.

Premier pas dans .Net avec C#2.0 - ( rv. 28.08.2006 )

- Rm di Scala

page

85

table2(4) = -1;

Donc table1(4) vaut aussi "-1", car il pointe vers le mme tableau qui ne se trouve qu'en un seul exemplaire en mmoire centrale.

Si l'on veut recopier un tableau uni-dimensionnel et en avoir deux exemplaires en mmoire, l'affectation ne le permet pas, il faut alors utiliser la mthode CopyTo() de la classe Array en lui passant comme paramtre le tableau de destination et en dmarrant l'index de copie la valeur 0. En reprenant les tableaux prcdents : Instruction int [ ]table1 = new int [9]; int [ ]table2 = new int [6];
for(int k = 0; k<=8; k++) table1(k) = k * 10; for(int k = 0; k<=5; k++) table2(k) = k ;

Image en mmoire centrale

table1.CopyTo(table2,0);

Cette instruction cre un nouveau tableau point par table2 qui l'exacte copie de celui point par table1. table2(4) = -1;

La modification de la cellule de rang 4 dans table2 n'a aucune influence sur table1 puisque ce sont deux entits bien distinctes.

Premier pas dans .Net avec C#2.0 - ( rv. 28.08.2006 )

- Rm di Scala

page

86

Les matrices et les tableaux multi-dimensionnels


Les tableaux C# peuvent avoir plusieurs dimensions, ceux qui ont deux dimensions sont dnomms matrices (vocabulaire scientifique). Tous ce que nous allons dire sur les matrices s'tend ipso facto aux tableaux de dimensions trois, quatre et plus. Ce sont aussi des objets et ils se comportent comme les tableaux une dimension tant au niveau des dclarations qu'au niveau des utilisations. La dclaration s'effectue avec un oprateur crochet et des virgules, exemples d'une syntaxe de dclaration d'un tableau trois dimension : [ , , ] . Leur structuration est semblable celle des tableaux Delphi-pascal.

Dclaration d'une matrice, rfrence seule:


int [ , ] table1; char [ , ] table2; float [ , ] table3; ... string [ , ] tableStr;

Dclaration d'une matrice avec dfinition explicite de taille :


int [ , ] table1 = new int [5, 2]; char [ , ] table2 = new char [9,4]; float [ , ] table3 = new float [2;8]; ... string [ , ] tableStr = new String [3,9];

Exemple d'criture de matrices de type int :


int [ , ] table1 = new int [2 , 3 ];// deux lignes de dimension 3 chacunes // dans une instruction d'affectation: table1[ 0 , 0 ] = -458; table1[ 2 , 5 ] = -3; <--- est une erreur de dpassement ! (valeur entre 0 et 1) table1[ 1 , 4 ] = 83; <--- est une erreur de dpassement ! (valeur entre 0 et 2) // dans une instruction de boucle: for (int i = 0 ; i<= 2; i++) table1[1 , i ] = 3*i-1; // avec initialisation d'une variable dans la dclaration : int n ; int [ , ] table1 = new int [4 , n =3 ];// quatre lignes de dimension 3 chacunes

Premier pas dans .Net avec C#2.0 - ( rv. 28.08.2006 )

- Rm di Scala

page

87

L'attribut Length en lecture seule, de la classe abstraite mre Array, contient en fait la taille d'un tableau en nombre total de cellules qui constituent le tableau (nous avons vu dans le cas uni-dimensionnel que cette valeur correspond la taille de la dimension du tableau). Dans le cas d'un tableau multi-dimensionnel Length correspond au produit des tailles de chaque dimension d'indice : int [ , ] table1 = new int [5, 2]; char [ , , ] table2 = new char [9,4,5]; float [ , , , ] table3 = new float [2,8,3,4]; table1.Length = 5 x 2 = 10 table2.Length = 9 x 4 x 5 = 180 table3.Length = 2 x 8 x 3 x 4= 192

Tableaux dchiquets ou en escalier


Leur structuration est strictement semblable celle des tableaux Java, en fait en C# un tableau dchiquet est compos de plusieurs tableaux unidimensionnels de taille variable. La dclaration s'effectue avec des oprateurs crochets [ ] [ ] : autant de crochets que de dimensions. C# autorise comme Java, des tailles diffrentes pour chacun des sous-tableaux. Ci-dessous le schma d'un tableau T trois dimensions en escalier :

Ce schma montre bien qu'un tel tableau T est constitu de tableaux unidimensionnels, les tableaux composs de cases blanches contiennent des pointeurs (rfrences). Chaque case blanche est une rfrence vers un autre tableau unidimensionnel, seules les cases grises contiennent les informations utiles de la structure de donnes : les lments de mme type du tableau T.

Premier pas dans .Net avec C#2.0 - ( rv. 28.08.2006 )

- Rm di Scala

page

88

Pour fixer les ides figurons la syntaxe des dclarations en C# d'un tableau d'lments de type int nomm myArray bi-dimensionnel en escalier :
int n = 10; int [ ][ ] myArray = new int [n][ ]; myArray[0] = new int[7]; myArray[1] = new int[9]; myArray[2] = new int[3]; myArray[3] = new int[4]; myArray[n-2] = new int[2]; myArray[n-1] = new int[8];

Ce tableau comporte 7+9+3+4+.+2+8 cellules utiles au stockage de donnes de type int, on peut le considrer comme une succession de tableaux d'int unidimensionnels (le premier ayant 7 cellules, le second ayant 9 cellules, etc) . Les dclarations suivantes :
int n = 10; int [ ][ ] myArray = new int [n][ ];

dfinissent un sous-tableau de 10 pointeurs qui vont chacun pointer vers un tableau unidimensionnel qu'il faut instancier :

myArray[0] = new int [7];

instancie un tableau d'int unidimensionnel 7 cases et renvoie sa rfrence qui est range dans la cellule de rang 0 du sous-tableau.

myArray[1] = new int [9];

instancie un tableau d'int unidimensionnel 9 cases et renvoie sa rfrence qui est range dans la cellule de rang 1 du sous-tableau.

Etc.

Attention
Dans le cas d'un tableau dchiquet, le champ Length de la classe Array, contient la taille du sous-tableau unidimensionnel associ la rfrence.

Premier pas dans .Net avec C#2.0 - ( rv. 28.08.2006 )

- Rm di Scala

page

89

Soit la dclaration : int [ ][ ] myArray = new int [n][ ]; myArray est une rfrence vers un sous-tableau de pointeurs. myArray.Length vaut 10 (taille du sous-tableau point)

Soit la dclaration : myArray[0] = new int [7]; MyArray [0] est une rfrence vers un sous-tableau de cellules d'lments de type int. myArray[0].Length vaut 7 (taille du sous-tableau point) Soit la dclaration : myArray[1] = new int [9]; MyArray [1] est une rfrence vers un sous-tableau de cellules d'lments de type int. myArray[1].Length vaut 9 (taille du sous-tableau point)

C# initialise les tableaux par dfaut 0 pour les int, byte, ... et null pour les objets. On peut simuler une matrice avec un tableau dchiquet dont tous les sous-tableaux ont exactement la mme dimension. Voici une figuration d'une matrice n+1 lignes et p+1 colonnes avec un tableau en escalier :
- Contrairement Java qui l'accepte, le code cidessous ne sera pas compil par C# : int [ ][ ] table = new int [n+1][p+1]; - Il est ncessaire de crer manuellement tous les sous-tableaux : int [ ][ ] table = new int [n+1][ ]; for (int i=0; i<n+1; i++) table[i] = new int [p+1];

Conseil
L'exemple prcdent montre l'vidence que si l'on souhaite rellement utiliser des matrices en C#, il est plus simple d'utiliser la notion de tableau multi-dimensionnel [ , ] que celle de tableau en escalier [ ] [ ].

Premier pas dans .Net avec C#2.0 - ( rv. 28.08.2006 )

- Rm di Scala

page

90

Egalit et ingalit de tableaux


L'oprateur d'galit = = appliqu au tableau de n'importe quel type, dtermine si deux objets spcifis ont la mme rfrence, il se comporte dans ce cas comme la mthode Equals de la classe Object qui ne teste que l'galit de rfrence int [ ] a , b ; (a = = b ) renvoie true si la rfrence a est la mme que la rfrence b ; sinon il renvoie false. Le morceau de code ci-dessous cr deux tableaux de char t1 et t2, puis teste leur galit avec l'oprateur = = et la mthode Equals : char[ ] t1="abcdef".ToCharArray(); char[ ] t2="abcdef".ToCharArray();

if(t1==t2)System.Console.WriteLine("t1=t2"); else System.Console.WriteLine("t1<>t2"); if(t1.Equals(t2))System.Console.WriteLine("t1 gal t2"); else System.Console.WriteLine("t1 diffrent de t2"); Aprs excution on obtient : t1<>t2 t1 diffrent de t2 Ces deux objets (les tableaux) sont diffrents (leurs rfrences pointent vers des blocs diffrents)bien que le contenu de chaque objet soit le mme.

Recopie de tableaux quelconques


Comme les tableaux sont des objets, l'affectation de rfrences de deux tableaux distincts donne les mmes rsultats que pour d'autres objets : les deux rfrences de tableaux pointent vers le mme objet. Donc une affectation d'un tableau dans un autre t1 = t2 ne provoque pas la recopie des lments du tableau t2 dans celui de t1. Si l'on souhaite que t1 soit une copie identique de t2, tout en conservant le tableau t2 et sa rfrence distincte il faut utiliser l'une des deux mthodes suivante de la classe abstraite mre Array : public virtual object Clone( ) : mthode qui renvoie une rfrence sur une nouvelle instance de
Premier pas dans .Net avec C#2.0 - ( rv. 28.08.2006 )
- Rm di Scala

page

91

tableau contenant les mmes lments que l'objet de tableau qui l'invoque. (il ne reste plus qu' transtyper la rfrence retourne puisque clone renvoie un type object)

public static void Copy ( Array t1 , Array t2, int long) : mthode de classe qui copie dans un tableau t2 dj existant et dj instanci, long lments du tableau t1 depuis son premier lment (si l'on veut une copie complte du tableau t1 dans t2, il suffit que long reprsente le nombre total d'lments soit long = t1.Length).

Attention

Dans le cas o le tableau t1 contient des rfrences qui pointent vers des objets :

la recopie dans un autre tableau travers les mthode Clone ou Copy ne recopie que les rfrences, mais pas les objets points, voici un "clone" du tableau t1 de la figure prcdente dans le tableau t2 :

Premier pas dans .Net avec C#2.0 - ( rv. 28.08.2006 )

- Rm di Scala

page

92

Si l'on veut que le clonage (la recopie) soit plus complte et comprenne aussi les objets points, il suffit de construire une telle mthode car malheureusement la classe abstraite Array n'est pas implantable par l'utilisateur mais seulement par le compilateur et nous ne pouvons pas redfinir la mthode virtuelle Clone).

Code source d'utilisation de ces deux mthodes sur un tableau unidimensionnel et sur une matrice : //-- tableau une dimension :
Dclaration avec dfinition explicite de taille. Dclaration de rfrence, puis clonage et transtypage.

int[ ] t = new int[10]; for (int i=0; i<10; i++) t[i]=10*i; int[ ] tab; tab = ( int[ ] )t.Clone( );

int[ ] table = new int[10]; Array.Copy ( t , table , t.Length );

Dclaration avec dfinition explicite ncessaire de la taille, afin de pouvoir lancer la copie.

//-- tableau deux dimensions :


int [ , ] matr = new int [3 , 2]; for (int i = 0; i<3; i++) for (int j = 0; j<2; j++) matr [i , j] = i+j; int [ , ] matrtab; matrtab=( int [ , ] )matr.Clone( ); int [ , ] matrtable = new int[3,2]; Array.Copy ( matr , matrtable , matr.Length );

Dclaration de rfrence, puis clonage et transtypage. Dclaration avec dfinition explicite ncessaire de la taille, afin de pouvoir lancer la copie.

Parcours itratifs de tableaux - foreachin


Les instructions itratives for( ), while, dowhile prcdemment vues permettent le parcours d'un tableau lment par lment travers l'indice de tableau. Il existe une instruction d'itration spcifique foreachin qui numre les lments d'une collection, en excutant un ensemble d'actions pour chaque lment de la collection. Syntaxe

Premier pas dans .Net avec C#2.0 - ( rv. 28.08.2006 )

- Rm di Scala

page

93

La classe Array est en fait un type de collection car elle implmente l'interface ICollection : public abstract class Array : ICloneable, IList, ICollection, IEnumerable Donc tout objet de cette classe (un tableau) est susceptible d'tre parcouru par un instruction foreachin. Mais les lments ainsi parcourus ne peuvent tre utiliss qu'en lecture, ils ne peuvent pas tre modifis, ce qui limite d'une faon importante la porte de l'utilisation d'un foreachin.

foreachin dans un tableau uni-dimensionnel


Dans un tableau T une dimension de taille long, les lments sont parcourus dans l'ordre croissant de l'index en commenant par la borne infrieure 0 et en terminant par la borne suprieure long-1 (rappel : long = T.Length ). Dans l'exemple ci-aprs o un tableau uni-dimensionnel table est instanci et rempli il y a quivalence de parcours du tableau table entre l'instruction for de gauche et l'instruction foreach de droite :
int [ ] table = new int [ 10 ]; . Remplissage du tableau for (int i=0; i<10; i++) System.Console.WriteLine ( table[i] ); foreach ( int val in table) System.Console.WriteLine ( val );

foreachin dans un tableau multi-dimensionnel

Lorsque T est un tableau multi-dimensionnel microsoft indique : les lments sont parcourus de manire que les indices de la dimension la plus droite soient augments en premier, suivis de ceux de la dimension immdiatement gauche, et ainsi de suite en continuant vers la gauche. Dans l'exemple ci-aprs o une matrice table est instancie et remplie il y a quivalence de parcours de la matrice table, entre l'instruction for de gauche et l'instruction foreach de droite (fonctionnement identique pour les autres types de tableaux multi-dimensionnels et en escalier) :
int [ , ] table = new int [ 3 , 2 ]; . Remplissage de la matrice for (int i=0; i<3; i++) for (int j=0; j<2; i++) System.Console.WriteLine ( table[ i , j ] ); foreach ( int val in table) System.Console.WriteLine ( val );

Avantage : la simplicit d'criture, toujours la mme quelle que soit le type du tableau. Inconvnient : on ne peut qu'numrer en lecture les lments d'un tableau.

Premier pas dans .Net avec C#2.0 - ( rv. 28.08.2006 )

- Rm di Scala

page

94

Collections - Piles - Files - Listes

Gnralits sur les collections


Ce chapitre est aborder si le lecteur est familiaris avec les concepts objets, dans le cas contraire le lecteur sautera ce chapitre pour y revenir ultrieurement.

L'espace de noms System.Collections contient des interfaces et des classes qui permettent de manipuler des collections d'objets. Plus prcisment, les donnes structures classiques que l'on utilise en informatique comme les listes, les piles, les files d'attente, sont reprsentes dans .Net framework par des classes directement utilisables du namespace System.Collections. Quatre interfaces de cet espace de noms jouent un rle fondamental : IEnumerable, IEnumerator, ICollection et IList selon les diagrammes d'hritage et d'agrgation suivants :

interface IEnumerable

interface IEnumerator

interface ICollection

interface IList

IEnumerable :
contient une seule mthode qui renvoie un numrateur (objet de type IEnumerator) qui peut itrer sur les lment d'une collection (c'est une sorte de pointeur qui avance dans la collection, comme un pointeur de fichier se dplace sur les enregistrements du fichier) : public IEnumerator GetEnumerator( );

IEnumerator :
Proprits public object Current {get;} Obtient l'lment en cours point actuellement par l'numrateur dans la
collection.

Mthodes public bool MoveNext( ); Dplace l'numrateur d'un lment il pointe maintenant vers l'lment
suivant dans la collection (renvoie false si l'numrateur est aprs le dernier lment de la collection sinon renvoie true).

public void Reset( ); Dplace l'numrateur au dbut de la collection, avant le premier lment (donc si
l'on effectue un Current on obtiendra la valeur null, car aprs un Reset( ), l'numrateur ne pointe pas devant le premier lment de la collection mais avant ce premier lment !).

Premier pas dans .Net avec C#2.0 - ( rv. 28.08.2006 )

- Rm di Scala

page

95

ICollection :
Proprits public int Count {get;} public bool IsSynchronized {get;}
Fournit le nombre d'lments contenus dans ICollection. Fournit un boolen indiquant si l'accs ICollection est synchronis (les lments de ICollection sont protgs de l'accs simultans de plusieurs threads diffrents). Fournit un objet qui peut tre utilis pour synchroniser (verrouiller ou dverrouiller) l'accs ICollection.

public object SyncRoot {get;}

Mthode public void CopyTo ( Array table, int index)

Copie les lments de ICollection dans un objet de type Array (table), commenant un index fix.

IList :
Proprits public bool IsFixedSize {get;} : indique si IList est de taille fixe. public bool IsReadOnly {get;} : indique si IList est en lecture seule.
Les classes implmentant l'interface IList sont indexables par l'indexeur [ ].

Mthodes (classique de gestion de liste) public int Add( object elt ); public void Clear( ); public bool Contains( object elt ); public int IndexOf( object elt );
Ajoute l'lment elt IList. Supprime tous les lments de IList. Indique si IList contient l'lment elt en son sein. Indique le rang de l'lment elt dans IList. Insre l'lment elt dans IList la position

public void Insert( int rang , object elt ); spcifie par rang. public void Remove( object elt ); public void RemoveAt (int rang);

Supprime la premire occurrence de l'objet elt de IList. Supprime l'lment de IList dont le rang est spcifi.

Ces quatre interfaces C# servent de contrat d'implmentation de nombreuses classes de structures de donnes, nous en tudions quelques unes sur le plan pratique dans la suite du document.

Premier pas dans .Net avec C#2.0 - ( rv. 28.08.2006 )

- Rm di Scala

page

96

Les tableaux dynamiques : classe ArrayList


Un tabeau Array une dimension, lorsque sa taille a t fixe soit par une dfinition explicite, soit par une dfinition implicite, ne peut plus changer de taille, c'est donc un objet de taille statique.
char [ ] TableCar ; TableCar = new char[8]; //dfinition de la taille et cration d'un nouvel objet tableau 8 cellules TableCar[0] = 'a'; TableCar[1] = '#'; ... TableCar[7] = '?';

Si l'on rajoute l'instruction suivante aux prcdentes < TableCar = new char[10]; > il y a cration d'un nouveau tableau de mme nom et de taille 10, l'ancien tableau 8 cellules est alors dtruit. Nous ne redimensionnons pas le tableau, mais en fait nous crons un nouvel objet utilisant la mme variable de rfrence TableCar que le prcdent, toutefois la rfrence TableCar pointe vers le nouveau bloc mmoire :

Ce qui nous donne aprs excution de la liste des instructions ci-dessous, un tableau TabCar ne contenant plus rien :
char [ ] TableCar ; TableCar = new char[8]; TableCar[0] = 'a'; TableCar[1] = '#'; ... TableCar[7] = '?'; TableCar = new char[10];

Comment faire pour "agrandir" un tableau pendant l'excution


Il faut dclarer un nouveau tableau t2 plus grand, puis recopier l'ancien dans le nouveau, par exemple en utilisant la mthode public static void Copy ( Array t1 , Array t2, int long)

Premier pas dans .Net avec C#2.0 - ( rv. 28.08.2006 )

- Rm di Scala

page

97

Il est possible d'viter cette faon de faire en utilisant une classe de vecteur (tableau unidimensionnel dynamique) qui est en fait une liste dynamique gre comme un tableau. La classe concerne se dnomme System.Collections.ArrayList, elle hrite de la classe object et implmente les interfaces IList, ICollection, IEnumerable, ICloneable ( public class ArrayList : IList, ICollection, IEnumerable, ICloneable;) Les principales mthodes permettant de manipuler les lments d'un ArrayList sont :
public virtual int Add( object value ); public virtual void Insert(int index, object value); public virtual void Clear(); public virtual void Remove(object obj); Ajoute l'objet value la fin de ArrayList. Insre un lment dans ArrayList l'index spcifi. Supprime tous les lments de ArrayList. Supprime la premire occurrence d'un objet spcifique de ArrayList. Trie les lments dans l'intgralit de ArrayList l'aide de l'implmentation IComparable de chaque lment (algorithme QuickSort). Accs en lecture et en criture un lment quelconque de rang i du tableau par Table[i] PROPRIETE public virtual int Count { get ;} Vaut le nombre d'lments contenus dans ArrayList, proprit en lecture seulement.. Proprit indexeur de la classe, on l'utilise comme un oprateur tab[ i ] accde l'lment de rang i.

public virtual void Sort( ); ArrayList Table; Table[i] = ....;

[ ]

Schma interne dun ArrayList :


O n est le nombre d'lments prsent dans l'ArrayList ( n = this.Count ). L'accs un lment d'un objet de type ArrayList s'effectue essentiellement grce lindexeur (R/W) : ArrayList liste ; object obj; . int k = liste.Add( obj ); object x = liste[k] ;

liste[k] = new object() ;

Premier pas dans .Net avec C#2.0 - ( rv. 28.08.2006 )

- Rm di Scala

page

98

Un objet de classe ArrayList peut "grandir" automatiquement d'un certain nombre de cellules pendant l'excution, c'est le programmeur qui peut fixer la valeur d'augmentation du nombre de cellules supplmentaires ds que la capacit maximale en cours est dpasse. Dans le cas o la valeur d'augmentation n'est pas fixe, c'est la machine virtuelle du CLR qui procde une augmentation par dfaut. Vous pouvez utiliser le type ArrayList avec n'importe quel type d'objet puisqu'un ArrayList contient des lments de type drivs d'object (ils peuvent tre tous de types diffrents et le vecteur est de type htrogne).

Voici un exemple simple de vecteur de chanes utilisant quelques unes des mthodes prcdentes :
static void afficheVector (ArrayList vect) //affiche un vecteur de string { System.Console.WriteLine( "Vecteur taille = " + vect.Count ); for ( int i = 0; i<= vect.Count-1; i++ ) System.Console.WriteLine( "Vecteur[" + i + "]=" + (string)vect[ i ] ); } static void VectorInitialiser ( ) // initialisation du vecteur de string { ArrayList table = new ArrayList( ); string str = "val:"; for ( int i = 0; i<=5; i++ ) table.Add(str + i.ToString( ) ); afficheVector(table); } Voici le rsultat de l'excution de la mthodeVectorInitialiser : Vecteur taille = 6 Vecteur [0] = val:0 Vecteur [1] = val:1 Vecteur [2] = val:2 Vecteur [3] = val:3 Vecteur [4] = val:4 Vecteur [5] = val:5

Premier pas dans .Net avec C#2.0 - ( rv. 28.08.2006 )

- Rm di Scala

page

99

Classe ArrayList

Premier pas dans .Net avec C#2.0 - ( rv. 28.08.2006 )

- Rm di Scala

page

100

Les listes chanes : classe ArrayList


Rappelons qu'une liste linaire (ou liste chane) est un ensemble ordonn d'lments de mme type (structure de donne homogne) auxquels on accde squentiellement. Les oprations minimales effectues sur une liste chane sont l'insertion, la modification et la suppression d'un lment quelconque de la liste. Les listes peuvent tre uni-directionnelles, elles sont alors parcourues squentiellement dans un seul sens :

ou bien bi-directionnelles dans lesquelles chaque lment possde deux liens de chanage, l'un sur l'lment qui le suit, l'autre sur l'lment qui le prcde, le parcours s'effectuant en suivant l'un ou l'autre sens de chanage :

La classe ArrayList peut servir une implmentation de la liste chane uni ou bi-directionnelle; un ArrayList contient des lments de type drivs d'Object, la liste peut donc tre htrogne, cf exercice sur les listes chanes.

Premier pas dans .Net avec C#2.0 - ( rv. 28.08.2006 )

- Rm di Scala

page

101

Liste clefs tries : classe SortedList


Si l'on souhaite grer une liste trie par clef, il est possible d'utiliser la classe SortedList (localisation : System.Collections.SortedList ). Cette classe reprsente une collection de paires valeur-cl tries par les cls toutes diffrentes et accessibles par cl et par index : il s'agit donc d'une liste d'identifiants et de valeur associe cet identifiant, par exemple une liste de personne dont l'identifiant (la clef) est un entier et la valeur associe des informations sur cette personne sont stockes dans une structure (le nom, l'ge, le genre, ...). Les interfaces implmentes par SortedList
Interface ICollection ICloneable IEnumerable IDictionary Description Contrat pour la taille, l'numration et la synchronisation d'une collection. Contrat d'une seulemthode pour cloner (recopier entirement) un objet : object clone( ) Contrat pour l'numration d'une collection. Reprsente une collection sous forme de couple (clef, valeur).

Cette classe n'est pas utile pour la gestion d'une liste chane classique non range cause de son tri automatique selon les clefs.

Premier pas dans .Net avec C#2.0 - ( rv. 28.08.2006 )

- Rm di Scala

page

102

En revanche, si l'on stocke comme clef la valeur de Hashcode de l'lment, la recherche est amliore. La classe SortedList :

Structure utilise par SortedList


Structure DictionaryEntry Description Dfini un unique couple (clef, valeur) qui peut tre stock ou retrouv. public struct DictionaryEntry

Premier pas dans .Net avec C#2.0 - ( rv. 28.08.2006 )

- Rm di Scala

page

103

Schma interne dun SortedList :


( un SortedList = 2 tables Keys et Values lies entre elles implantant un IDictionnary ) O n est le nombre d'lments prsent dans le SortedList ( n = this.Count ). 1) Accs un lment essentiellement grce sa clef et lindexeur associ : SortedList liste ; . liste.Add( objKey, objValue) ; .... object x = liste[objKey ] ; .... liste[objKey ] = new object( ) ; 2) Le couple ( une cellule de Keys , la cellule associe dans Values ) peut aussi tre considr comme un DictionaryEntry = (Key ,Value), le SortedList peut alors tre considr aussi comme une collection d'objets de type DictionaryEntry :

Dans ce cas l'accs aux lments d'un SortedList peut aussi s'effectuer avec une boucle foreach sur chaque DictionaryEntry contenu dans le SortedList.

foreach (DictionaryEntry couple in this ) { couple.Key.... (accs la clef) couple.Value....(accs la valeur associe la clef) }

Les principales mthodes permettant de manipuler les lments d'un SortedList sont :
public virtual void Add( object key, object value ); public virtual void CopyTo( Array array, int arrayIndex ); Ajoute un lment avec la cl key et la valeur value spcifies dans le SortedList. Copie les lments du SortedList dans une instance Array array unidimensionnelle l'index arrayIndex spcifi (valeur de l'index dans array o la copie
- Rm di Scala

Premier pas dans .Net avec C#2.0 - ( rv. 28.08.2006 )

page

104

commence). public virtual void Clear( ); public virtual object GetByIndex( int index ); public virtual object GetKey( int index ); public virtual int IndexOfValue( object value ); Supprime tous les lments de SortedList. Obtient la valeur l'index spcifi de la liste SortedList. Obtient la cl l'index spcifi de SortedList. Retourne l'index de base zro de la premire occurrence de la valeur value spcifie dans SortedList. Retourne l'index de base zro de la cl key spcifie dans SortedList. Supprime de SortedList l'lment ayant la cl key spcifie. Supprime l'lment au niveau de l'index spcifi de SortedList. Obtient un objet de liste IList en lecture seule contenant toutes les valeurs tries dans le mme ordre que dans le SortedList. PROPRIETE public virtual int Count { get ; } Vaut le nombre d'lments contenus dans SortedList, proprit en lecture seulement. Proprit indexeur de la classe, on l'utilise comme un oprateur tab[ i ] accde l'lment dont la clef vaut i. Obtient dans un objet de ICollection les valeurs dans SortedList. (les lments de ICollection sont tous tris dans le mme ordre que les valeurs du SortedList) Obtient dans un objet de ICollection les cls dans SortedList. (les lments de ICollection sont tous tris dans le mme ordre que les clefs du SortedList)

public virtual int IndexOfKey( object key );

public virtual void Remove( object key );

public virtual void RemoveAt( int index );

public virtual IList GetValueList ( );

[ ]

public virtual ICollection Values { get; }

public virtual ICollection Keys { get; }

Exemple d'utilisation d'un SortedList :

SortedList Liste = new SortedList ( ); Liste.Add(100,"Jean"); Liste.Add(45,"Murielle"); Liste.Add(201,"Claudie"); Liste.Add(35,"Jos"); Liste.Add(28,"Luc"); //----> Balayage complet de la Liste par index : for (int i=0; i<Liste.Count; i++) System.Console.WriteLine( (string)Liste.GetByIndex(i) ); //----> Balayage complet de la collection des valeurs : foreach(string s in Liste.Values) System.Console.WriteLine( s ); Premier pas dans .Net avec C#2.0 - ( rv. 28.08.2006 )
- Rm di Scala

page

105

//----> Balayage complet de la collection des clefs : foreach(object k in Liste.Keys) System.Console.WriteLine( Liste[k] ); //----> Balayage complet de l'objet IList retourn : for (int i = 0; i < Liste.GetValueList( ).Count; i++) System.Console.WriteLine( Liste.GetValueList( ) [ i ] );

Soit la reprsentation suivante (attention la confusion entre clef et index) :

Les trois boucles affichent dans l'ordre : Luc Jos Murielle Jean Claudie

Piles Lifo, files Fifo : classes Stack et Queue


Ces deux classes font partie du namespace System.Collections : System.Collections.Stack System.Collections.Queue Les interfaces implmentes par Stack
Interface ICollection ICloneable IEnumerable Description Contrat pour la taille, l'numration et la synchronisation d'une collection. Contrat d'une seulemthode pour cloner (recopier entirement) un objet : object clone( ) Contrat pour l'numration d'une collection.

Premier pas dans .Net avec C#2.0 - ( rv. 28.08.2006 )

- Rm di Scala

page

106

La classe Stack :

Schma interne dun objet LIFO de classe Stack :

La classe "public class Stack : ICollection, IEnumerable, ICloneable" reprsente une pile Lifo :
public virtual object Peek ( ); public virtual object Pop( ); public virtual void Push( object elt ); public virtual object [ ] ToArray( ); Renvoie la rfrence de l'objet situ au sommet de la pile. Dpile la pile (l'objet au sommet est enlev et renvoy) Empile un objet au sommet de la pile. Recopie toute la pile dans un tableau d'objet depuis le sommet jusqu'au fond de la pile (dans l'ordre du dpilement).

Les interfaces implmentes par Queue


Interface ICollection ICloneable IEnumerable Description Contrat pour la taille, l'numration et la synchronisation d'une collection. Contrat d'une seulemthode pour cloner (recopier entirement) un objet : object clone( ) Contrat pour l'numration d'une collection.

Premier pas dans .Net avec C#2.0 - ( rv. 28.08.2006 )

- Rm di Scala

page

107

La classe Queue :

Schma interne dun objet FIFO de classe Queue :

La classe "public class Queue : ICollection, IEnumerable, ICloneable" reprsente une file Fifo :
public virtual object Peek ( ); public virtual object Dequeue( ); public virtual void Enqueue ( object elt ); public virtual object [ ] ToArray( ); Renvoie la rfrence de l'objet situ au sommet de la file. L'objet au dbut de la file est enlev et renvoy. Ajoute un objet la fin de la file. Recopie toute la file dans un tableau d'objet depuis le dbut de la fifo jusqu' la fin de la file.

Exemple d'utilisation d'une Lifo de type Stack Construisons une pile de string possdant une mthode getArray permettant d'empiler immdiatement dans la pile tout un tableau de string. Le programme ci-dessous rempli avec les chanes du tableau t1 grce la mthode getArray, la pile Lifo construite. On tente ensuite de rcuprer le contenu de la pile sous forme d'un tableau de chane t2 (opration inverse) en utilisant la mthode ToArray. Le compilateur signale une erreur :

Premier pas dans .Net avec C#2.0 - ( rv. 28.08.2006 )

- Rm di Scala

page

108

class Lifo : Stack { public virtual void getArray(string[ ] t) { foreach(string s in t) this.Push (s); } } Impossible de convertir implicitement le type 'object[ ]' en 'string[ ]' .

class Class { static void Main ( string[ ] args ) { Lifo piLifo = new Lifo ( ); string [ ] t1 = {"aaa","bbb","ccc","ddd","eee","fff","fin"}; string [ ] t2 ; piLifo.getArray(t1) ; t2 = piLifo.ToArray( ) ; foreach ( string s in t2 ) System.Console.WriteLine(s) ; }

En effet la mthode ToArray renvoie un tableau d'object et non un tableau de string. On pourrait penser transtyper explicitement :

t2 = ( string [ ] ) piLifo.ToArray( ) ;

en ce cas C# ragit comme Java, en acceptant la compilation, mais en gnrant une exception de cast invalide, car il est en effet dangereux d'accepter le transtypage d'un tableau d'object en un tableau de quoique ce soit, car chaque object du tableau peut tre d'un type quelconque et tous les types peuvent tre diffrents ! Il nous faut donc construire une mthode ToArray qui effectue le transtypage de chaque cellule du tableau d'object et renvoie un tableau de string, or nous savons que la mthode de classe Array nomme Copy un tableau t1 vers un autre tableau t2 en effectuant ventuellement le transtypage des cellules : Array.Copy(t1 , t2 , t1.Length) Voici le code de la nouvelle mthode ToArray :
class Lifo : Stack { public virtual void getArray ( string[ ] t ) { foreach(string s in t) this.Push (s); } public new virtual string [ ] ToArray ( ){ string[ ] t = new string [this.Count]; Array.Copy( base.ToArray( ), t , this.Count ); return t ; } } Appel la mthode ToArray mre qui renvoie un object[ ] class Class { static void Main ( string[ ] args ) { Lifo piLifo = new Lifo ( ); string [ ] t1 = {"aaa","bbb","ccc","ddd","eee","fff","fin"}; string [ ] t2 ; piLifo.getArray(t1) ; fin t2 = piLifo.ToArray( ) ; fff foreach ( string s in t2 ) eee System.Console.WriteLine(s) ; ddd } ccc bbb aaa

Nous avons mis le qualificateur new car cette mthode masque la mthode mre de la classe Stack, nous avons maintenant une pile Lifo de string.

Premier pas dans .Net avec C#2.0 - ( rv. 28.08.2006 )

- Rm di Scala

page

109

Exemple d'utilisation d'une Fifo de type Queue

Construisons d'une manire identique la construction prcdente, une file de string possdant une mthode getArray permettant d'ajouter immdiatement dans la file tout un tableau de string et la mthode ToArray redfinie :

Nous livrons immdiatement le code source de cette classe et celui de la classe d'appel :
class Fifo : Queue { public virtual void getArray ( string[ ] t ) { foreach(string s in t) this. Enqueue (s); } public new virtual string [ ] ToArray ( ){ string[ ] t = new string [this.Count]; Array.Copy( base.ToArray( ), t , this.Count ); return t ; } class Class { static void Main ( string[ ] args ) { string [ ] t1 = {"aaa","bbb","ccc","ddd","eee","fff","fin"}; string [ ] t2 ; Fifo filFifo = new Fifo ( ); filFifo.getArray(t1); aaa t2 = filFifo.ToArray( ); bbb foreach (string s in t2) ccc System.Console.WriteLine(s); ddd System.Console.ReadLine(); eee } fff fin

Premier pas dans .Net avec C#2.0 - ( rv. 28.08.2006 )

- Rm di Scala

page

110

Classes de bases pour collections personnalises CollectionBase


Il existe une classe abstraite de gestion d'une collection d'objets nomme CollectionBase, elle est situe dans le namespace System.Collections. La classe CollectionBase :

Les interfaces implmentes par CollectionBase


Interface ICollection IEnumerable IList Description Contrat pour la taille, l'numration et la synchronisation d'une collection. Contrat pour l'numration d'une collection. Reprsente une collection dont chaque lment est accessible par un index.

Premier pas dans .Net avec C#2.0 - ( rv. 28.08.2006 )

- Rm di Scala

page

111

Schma interne dun objet de classe CollectionBase :

Cette structure peut tre atteinte soit comme un ArrayList, soit comme un IList.

Si l'on souhaite construire une collection personnalise bnficiant des fonctionnalits de base offertes par .Net, il faut hriter de la classe CollectionBase :
class MaCollection : CollectionBase { }

DictionaryBase
Il existe une classe abstraite de gestion d'une collection d'objets rangs sous forme de de dictionnaire (paire de valeur <clef,valeur>), nomme DictionaryBase, elle est situe dans le namespace System.Collections.

Les interfaces implmentes par DictionaryBase


Interface ICollection IEnumerable IDictionary Description Contrat pour la taille, l'numration et la synchronisation d'une collection. Contrat pour l'numration d'une collection. Reprsente une collection sous forme de couple (clef, valeur).

Premier pas dans .Net avec C#2.0 - ( rv. 28.08.2006 )

- Rm di Scala

page

112

classe DictionaryBase

Schma interne dun objet de classe DictionaryBase :


Les donnes sont atteignables travers la proprit Dictionary {get;}qui est un objet de type IDictionary :

Si l'on souhaite construire une Dictionnaire personnalis bnficiant des fonctionnalits de base offertes par .Net, il faut hriter de la classe DictionaryBase. class MonDictionnaire: DictionaryBase { }

Premier pas dans .Net avec C#2.0 - ( rv. 28.08.2006 )

- Rm di Scala

page

113

Accs aux donnes en lecture seulement directement : Comme pour le SortedList, le couple (une cellule de Keys , la cellule associe dans Values ) peut aussi tre considr comme un DictionaryEntry = (Key ,Value), un objet de classe MonDictionnaire peut alors tre considr aussi comme une collection d'objets de type DictionaryEntry :

Dans ce cas l'accs aux lments d'un MonDictionnaire peut s'effectuer avec une boucle foreach sur chaque DictionaryEntry contenu dans le MonDictionnaire. foreach (DictionaryEntry couple in this ) { couple.Key.... (accs la clef) couple.Value....(accs la valeur associe la clef) }

Accs aux donnes en lecture seulement travers le Dictionary : Comme cette classe abstraite met sa proprit Dictionary en accs protg (Protected ReadOnly Property Dictionary As IDictionary), il est bon dans la classe fille construite d'augmenter le niveau de visibilit de cette proprit en la rendant par exemple publique afin que les donnes soient accessibles aux objets, on peut alors accder aussi aux lments d'un objet de classe MonDictionnaire partir de sa proprit Dictionnary : foreach (DictionaryEntry couple in this. Dictionary ){ couple.Key.... (accs la clef) couple.Value....(accs la valeur associe la clef) } La proprit Dictionary permet plus de manipulations sur les donnes puisqu'elle possde les mthodes Add, Contains, Remove et Clear qui agissent directement sur les donnes de l'objet de classe MonDictionnaire.

Exemple de classe drive de DictionaryBase : class MonDictionnaire : DictionaryBase { // on rend Public la proprit protected Dictionary : public new IDictionary Dictionary { get { return base.Dictionary;
Premier pas dans .Net avec C#2.0 - ( rv. 28.08.2006 )
- Rm di Scala

page

114

} } // Afichage des donnes partir du Dictionary : public void afficherDico() { foreach (DictionaryEntry elt in this.Dictionary) { System.Console.WriteLine(" {0} : {1}", elt.Key, elt.Value); } } // Afichage des donnes partir de l'objet lui-mme : public void afficherMe() { foreach (DictionaryEntry elt in this) { System.Console.WriteLine(" {0}...{1}", elt.Key, elt.Value); } } } class Principale { public static void Main(string[] args) { MonDictionnaire dico = new MonDictionnaire(); dico.Dictionary.Add(10, "rmd1"); dico.Dictionary.Add(30, "rmd2"); dico.Dictionary.Add(20, "rmd3"); dico.Dictionary.Add(50, "rmd4"); dico.Dictionary.Add(40, "rmd5"); dico.afficherDico(); dico.afficherMe(); Console.ReadLine(); } }
Rsultats obtenus :

Premier pas dans .Net avec C#2.0 - ( rv. 28.08.2006 )

- Rm di Scala

page

115

Un langage trs orient objet

Classes, objets et mthodes Polymorphisme d'objets Polymorphisme de mthodes Polymorphisme d'interfaces Classe de dlgation Traitement d'exceptions Processus et multi-threading

Premier pas dans .Net avec C#2.0 - ( rv. 28.08.2006 )

- Rm di Scala

page

116

Classes, objets et mthodes

Plan gnral:

1. Les classes C# : des nouveaux types


1.1 Dclaration d'une classe 1.2 Une classe est un type en C# 1.3 Toutes les classes ont le mme anctre - hritage 1.4 Encapsulation des classes 1.5 Exemple de classe imbrique dans une autre classe 1.6 Exemple de classe inclue dans un mme espace de noms 1.7 Mthodes abstraites 1.8 Classe abstraite, Interface

2. Les objets : des rfrences ou des valeurs


2.1 Modle de la rfrence 2.2 Les constructeurs d'objets rfrence ou valeurs 2.3 Utilisation du constructeur d'objet par dfaut 2.4 Utilisation d'un constructeur d'objet personnalis 2.5 Le mot clef this- cas de la rfrence seulement

3. Variables et mthodes
3.1 Variables dans une classe en gnral 3.2 Variables et mthodes d'instance 3.3 Variables et mthodes de classe - static 3.4 Bilan et exemple d'utilisation

Premier pas dans .Net avec C#2.0 - ( rv. 28.08.2006 )

- Rm di Scala

page

117

Introduction
Nous proposons des comparaisons entre les syntaxes de C# et Delphi et/ou Java, lorsque les dfinitions sont semblables. Tableau des limitations des niveaux de visibilit fourni par microsoft :

Modification de visibilit Rappelons les classiques modificateurs de visibilit des variables et des mthodes dans les langages orients objets, dont C# dispose : Les mots clef (modularit public-priv)
par dfaut (aucun mot clef) Les variables et les mthodes d'une classe non prcdes d'un mot clef sont private et ne sont visibles que dans la classe seulement. Les variables et les mthodes d'une classe prcdes du mot clef public sont visibles par toutes les classes de tous les modules. Les variables et les mthodes d'une classe prcdes du mot clef private ne sont visibles que dans la classe seulement. Les variables et les mthodes d'une classe prcdes du mot clef protected sont visibles par toutes les classes drives de cette classe. Les variables et les mthodes d'une classe prcdes du mot clef internal sont visibles par toutes les classes
- Rm di Scala

public

private

protected internal

Premier pas dans .Net avec C#2.0 - ( rv. 28.08.2006 )

page

118

inclues dans le mme assembly.

Les attributs d'accessibilit public, private, protected sont identiques ceux de Delphi et Java, pour les classes nous donnons ci-dessous des informations sur leur utilisation. L'attribut internal joue peu prs le rle (au niveau de l'assembly) des classes Java dclares sans mot clef dans le mme package, ou des classes Delphi dclares dans la mme unit (classes amies). Toutefois pour des raisons de scurit C# ne possde pas la notion de classe amie.

1. Les classes : des nouveaux types


Rappelons un point fondamental dj indiqu : tout programme C# contient une ou plusieurs classes prcdes ou non d'une dclaration d'utilisation dautres classes contenues dans des bibliothques (clause using) ou dans un package complet compos de nombreuses classes. La notion de module en C# est reprsente par l'espace de noms (clause namespace) semblable au package Java, en C# vous pouvez omettre de spcifier un namespace, par dfaut les classes dclares le sont automatiquement dans un espace 'sans nom' (gnralement qualifi de global) et tout identificateur de classe dclar dans cet espace global sans nom est disponible pour tre utilis dans un espace de noms nomm. Contrairement Java, en C# les classes non qualifies par un modificateur de visibilit (dclares sans rien devant) sont internal. Delphi
Unit Biblio; interface // les dclarations des classes implementation // les implmentations des classes end. si pas de nom de package alors automatiquement dans : package java.lang; } si pas de nom d'espace de noms alors automatiquement dans l'espace global.

Java
package Biblio;

C#
namespace Biblio { // les dclarations et implmentation des classes

// les dclarations et implmentation des classes

1.1 Dclaration d'une classe En C#, nous n'avons pas comme en Delphi, une partie dclaration de la classe et une partie implmentation spares l'une de l'autre. La classe avec ses attributs et ses mthodes sont dclars et implments un seul endroit comme en Java.
Premier pas dans .Net avec C#2.0 - ( rv. 28.08.2006 ) page

- Rm di Scala

119

Delphi
interface uses biblio; type Exemple = class x : real; y : integer; function F1(a,b:integer): real; procedure P2; end; implementation function F1(a,b:integer): real; begin ...........code de F1 end; procedure P2; begin ...........code de P2 end; end. import biblio;

Java
using biblio;

C#

class Exemple { float x; int y; float F1(int a, int b) { ...........code de F1 } void P2( ) { ...........code de P2 } }

namespace Machin { class Exemple { float x; int y; float F1(int a, int b) { ...........code de F1 } void P2( ) { ...........code de P2 } } }

1.2 Une classe est un type en C# Comme en Delphi et Java, une classe C# peut tre considre comme un nouveau type dans le programme et donc des variables d'objets peuvent tre dclares selon ce nouveau "type". Une dclaration de programme comprenant 3 classes : Delphi
interface type Un = class ... end; Deux = class ... end; Appli3Classes = class x : Un; y : Deux; public procedure main; end; implementation procedure Appli3Classes.main; var x : Un; y : Deux; begin ... end; end.

Java
class Appli3Classes { Un x; Deux y; public static void main(String [ ] arg) { Un x; Deux y; ... } } class Un { ... } class Deux { ... }

C#
class Appli3Classes { Un x; Deux y; static void Main(String [ ] arg) { Un x; Deux y; ... } } class Un { ... } class Deux { ... }

Premier pas dans .Net avec C#2.0 - ( rv. 28.08.2006 )

- Rm di Scala

page

120

1.3 Toutes les classes ont le mme anctre - hritage Comme en Delphi et en Java, toutes les classes C# drivent automatiquement d'une seule et mme classe anctre : la classe Object. En C# le mot-clef pour indiquer la drivation (hritage) partir d'une autre classe est le symbole deux points ':', lorsqu'il est omis c'est donc que la classe hrite automatiquement de la classe Object : Les deux dclarations de classe ci-dessous sont quivalentes : Delphi
type Exemple = class ( TObject ) ...... end; type Exemple = class ...... end;

Java
class Exemple extends Object { ....... } class Exemple { ....... }

C#
class Exemple : Object { ....... } class Exemple { ....... }

L'hritage en C# est tout fait classiquement de l'hritage simple comme en Delphi et en Java. Une classe fille qui drive d'une seule classe mre, hrite de sa classe mre toutes ses mthodes et tous ses champs. En C# la syntaxe de l'hritage fait intervenir le symbole clef ':', comme dans "class Exemple : Object". Une dclaration du type : class ClasseFille : ClasseMere { } signifie que la classe ClasseFille dispose de tous les attributs et de toutes les mthodes de la classe ClasseMere. Comparaison hritage : Delphi
type ClasseMere = class // champs de ClasseMere // mthodes de ClasseMere end; ClasseFille = class ( ClasseMere )
// hrite des champs de ClasseMere // hrite des mthodes de ClasseMere

Java
class ClasseMere { // champs de ClasseMere // mthodes de ClasseMere } class ClasseFille extends ClasseMere {
// hrite des champs de ClasseMere // hrite des mthodes de ClasseMere

C#
class ClasseMere { // champs de ClasseMere // mthodes de ClasseMere } class ClasseFille : ClasseMere {
// hrite des champs de ClasseMere // hrite des mthodes de ClasseMere

end;

Bien entendu une classe fille peut dfinir de nouveaux champs et de nouvelles mthodes qui lui sont propres.

Premier pas dans .Net avec C#2.0 - ( rv. 28.08.2006 )

- Rm di Scala

page

121

1.4 Encapsulation des classes La visibilit et la protection des classes en Delphi est apporte par le module Unit o toutes les classes sont visibles dans le module en entier et ds que la unit est utilise les classes sont visibles partout. Il n'y a pas de possibilit d'imbriquer une classe dans une autre. En C#, nous avons la possibilit d'imbriquer des classes dans d'autres classes (classes internes), par consquent la visibilit de bloc s'applique aussi aux classes. Remarque La notion de classe interne de C# (qui n'existe pas en Delphi) est sensiblement diffrente de celle de Java (pas de classe locale et pas de classe anonyme mais des mthodes anonymes), elle correspond la notion de classe membre de Java.

Mots clefs pour la protection des classes et leur visibilit : Une classe C# peut se voir attribuer un modificateur de comportement sous la forme d'un mot clef devant la dclaration de classe. Par dfaut si aucun mot clef n'est indiqu la classe est visible dans tout le namespace dans lequel elle est dfinie. Il y a 4 qualificateurs possibles pour modifier le comportement de visibilit d'une classe selon sa position (imbrique ou non) : public, private, protected, internal (dnomms modificateurs d'accs) et abstract (qualificateur d'abstraction pouvant tre associ l'un des 3 autres modificateurs d'accs). On rappelle que sans qualificateur public, private, internal ou protected, une classe C# est automatiquement internal. Le nom du fichier source dans lequel plusieurs classes C# sont stockes n'a aucun rapport avec le nom d'une des classes dclares dans le texte source, il est laiss au libre choix du dveloppeur et peut ventuellement tre celui d'une classe du namespace etc...

Tableau des possibilits fourni par microsoft :

Attention Par dfaut dans une classe tous les membres sans qualificateur de visibilit (classes internes
Premier pas dans .Net avec C#2.0 - ( rv. 28.08.2006 )
- Rm di Scala

page

122

inclues) sont private.

C# mot clef abstract : abstract class ApplicationClasse1 { ... } mot clef public : public class ApplicationClasse2 { ... } mot clef protected : protected class ApplicationClasse3 { ... } mot clef internal : internal class ApplicationClasse4 { ... } mot clef private : private class ApplicationClasse5 { ... } pas de mot clef : class ApplicationClasse6 { ... } - sauf si c'est une classe interne

Explication classe abstraite non instanciable. Aucun objet ne peut tre cr. classe visible par n'importe quel programme d'un autre namespace classe visible seulement par toutes les autres classes hritant de la classe conteneur de cette classe.

classe visible seulement par toutes les autres classes du mme assembly. classe visible seulement par toutes les autres classes du mme namespace o elle est dfinie.

qualifie internal -si c'est une classe interne elle est alors qualifie private.

Nous remarquons donc qu'une classe ds qu'elle est dclare dans lespace de noms est toujours visible dans tout l'assembly, que le mot clef internal soit prsent ou non. Les mots clefs abstract et protected n'ont de l'influence que pour l'hritage. Remarque La notion de classe sealed en C# correspond strictement la notion de classe final de Java : ce sont des classes non hritables.

Nous tudions ci-aprs la visibilit des classes prcdentes dans deux contextes diffrents.

1.5 Exemple de classes imbriques dans une autre classe Dans le premier contexte, ces six classes sont utilises en tant intgres (imbriques) une classe publique. La classe ApplicationClasses : C# Explication

Premier pas dans .Net avec C#2.0 - ( rv. 28.08.2006 )

- Rm di Scala

page

123

namespace Exemple { public class ApplicationClasses { abstract class ApplicationClasse1 { ... } public class ApplicationClasse2 { ... } protected class ApplicationClasse3 { ... } internal class ApplicationClasse4 { ... } private class ApplicationClasse5 { ... } class ApplicationClasse6 { ... } } }

Ces 6 "sous-classes" sont visibles ou non, partir de l'accs la classe englobante "ApplicationClasses", elles peuvent donc tre utilises dans tout programme qui utilise la classe "ApplicationClasses".

Par dfaut la classe ApplicationClasse6 est ici private.

Un programme utilisant la classe ApplicationClasses : C# Explication

Le programme de gauche "class AppliTestClasses" utilise la classe prcdente ApplicationClasses et ses sous-classes. La notation uniforme de chemin de classe est standard.

namespace Exemple { class AppliTestClasses { ApplicationClasses.ApplicationClasse2 a2; } }

Seule la classe interne ApplicationClasse2 est visible et permet d'instancier un objet.

1.6 Mme exemple de classes non imbriques situes dans le mme espace de noms Dans ce second exemple, ces mmes 6 classes sont utilises en tant incluses dans le mme namespace.
Premier pas dans .Net avec C#2.0 - ( rv. 28.08.2006 )
- Rm di Scala

page

124

C#

Explication

Une classe dans un espace de nom ne peut pas tre qualifie protected ou private (si elle est imbrique comme ci-haut cela est possible): Les classes ApplicationClasse3 et ApplicationClasse5 ne peuvent donc pas faire partie du mme namespace Exemple. La classe ApplicationClasse1 est ici abstraite et public, donc visible. La classe ApplicationClasse2 est public, donc visible. La classe ApplicationClasse4 n'est visible que dans le mme assembly. Par dfaut la classe ApplicationClasse6 est ici public, donc visible.

namespace Exemple { abstract class ApplicationClasse1 { ... } public class ApplicationClasse2 { ... } protected class ApplicationClasse3 { ... } internal class ApplicationClasse4 { ... } private class ApplicationClasse5 { ... } class ApplicationClasse6 { ... } }

Un programme AppliTestClasses utilisant ces 4 classes : C# dans deux namespace diffrents Explication

Le programme de gauche "class AppliTestClasses" utilise les classes qui composent le namespace Exemple.

Cette classe AppliTestClasses est dfinie dans un autre namespace dnomm Essai qui est suppos ne pas faire partie du mme assembly que le namespace Exemple. Elle ne voit donc pas la classe ApplicationClasse4 (visible dans le mme assembly seulement).

using Exemple; namespace Essai { class AppliTestClasses{ ApplicationClasse2 a2; ApplicationClasse6 a6; } }

Si l'on veut instancier des objets, seules les classes ApplicationClasse2 et ApplicationClasse6 sont de bonnes candidates, car la classe ApplicationClasse1 bien qu'elle soit visible est abstraite.

Premier pas dans .Net avec C#2.0 - ( rv. 28.08.2006 )

- Rm di Scala

page

125

C# dans le mme namespace

Explication

Le programme de gauche "class AppliTestClasses" utilise les classes qui composent le namespace Exemple.

La classe AppliTestClasses est dfinie dans le mme namespace Exemple que les 4 autres classes. Toutes les classes du mme namespace sont visibles entre elles. namespace Exemple { class AppliTestClasses{ ApplicationClasse1 a1; ApplicationClasse2 a2; ApplicationClasse4 a4; ApplicationClasse6 a6; } } Ici toutes les 4 classes sont visibles pour la classe AppliTestClasses.

Remarque pratique : Selon sa situation imbrique ou non imbrique, une classe peut ou ne peut pas tre qualifie par les divers modificateurs de visibilit. En cas de doute le compilateur fournit un diagnostique clair, comme ci-dessous : [C# Erreur] Class.cs(nn): Les lments namespace ne peuvent pas tre dclars explicitement comme private, protected ou protected internal.

1.7 Mthodes abstraites Le mot clef abstract est utilis pour reprsenter une classe ou une mthode abstraite. Quel est l'intrt de cette notion ? Avoir des modles gnriques permettant de dfinir ultrieurement des actions spcifiques.

Une mthode dclare en abstract dans une classe mre : N'a pas de corps de mthode. N'est pas excutable.
- Rm di Scala

Premier pas dans .Net avec C#2.0 - ( rv. 28.08.2006 )

page

126

Doit obligatoirement tre redfinie dans une classe fille.

Une mthode abstraite n'est qu'une signature de mthode sans implmentation dans la classe. Exemple de mthode abstraite : class Etre_Vivant { } La classe Etre_Vivant est une classe mre gnrale pour les tres vivants sur la plante, chaque catgorie d'tre vivant peut tre reprsente par une classe drive (classe fille de cette classe) : class Serpent : Etre_Vivant { } class Oiseau : Etre_Vivant { } class Homme : Etre_Vivant { } Tous ces tres se dplacent d'une manire gnrale, donc une mthode SeDeplacer est commune toutes les classes drives, toutefois chaque espce excute cette action d'une manire diffrente et donc on ne peut pas dire que se dplacer est une notion concrte mais une notion abstraite que chaque sous-classe prcisera concrtement. En C#, les mthodes abstraites sont automatiquement virtuelles, elles ne peuvent tre dclares que public ou protected, enfin elles doivent tre redfinies avec le qualificateur override. Cidessous deux dclarations possibles pour le dplacement des tres vivants :

abstract class Etre_Vivant { public abstract void SeDeplacer( ); } class Serpent : Etre_Vivant { public override void SeDeplacer( ) { //.....en rampant } } class Oiseau : Etre_Vivant { public override void SeDeplacer( ) { //.....en volant } } class Homme : Etre_Vivant { public override void SeDeplacer( ) { //.....en marchant } }

abstract class Etre_Vivant { protected abstract void SeDeplacer( ); } class Serpent : Etre_Vivant { protected override void SeDeplacer( ) { //.....en rampant } } class Oiseau : Etre_Vivant { protected override void SeDeplacer( ) { //.....en volant } } class Homme : Etre_Vivant { protected override void SeDeplacer( ) { //.....en marchant } }

Comparaison de dclaration d'abstraction de mthode en Delphi et C# : Delphi


type Etre_Vivant = class

C#
abstract class Etre_Vivant { public abstract void SeDeplacer( );
- Rm di Scala

Premier pas dans .Net avec C#2.0 - ( rv. 28.08.2006 )

page

127

procedure SeDeplacer;virtual; abstract ; end; Serpent = class ( Etre_Vivant ) procedure SeDeplacer; override; end; Oiseau = class ( Etre_Vivant ) procedure SeDeplacer; override; end; Homme = class ( Etre_Vivant ) procedure SeDeplacer; override; end;

} class Serpent : Etre_Vivant { public override void SeDeplacer( ) { //.....en rampant } } class Oiseau : Etre_Vivant { public override void SeDeplacer( ) { //.....en volant } } class Homme : Etre_Vivant { public override void SeDeplacer( ) { //.....en marchant } }

En C# une mthode abstraite est une mthode virtuelle nayant pas dimplmentation dans la classe o elle est dclare. Son implmentation est dlgue une classe drive. Les mthodes abstraites doivent tre dclares en spcifiant la directive abstract .

1.8 Classe abstraite, Interface Classe abstraite Comme nous venons de le voir dans l'exemple prcdent, une classe C# peut tre prcde du mot clef abstract, ce qui signifie alors que cette classe est abstraite, nous avons les contraintes de dfinition suivantes pour une classe abstraite en C# : Si une classe contient au moins une mthode abstract, elle doit imprativement tre dclare en classe abstract elle-mme. C'est ce que nous avons crit au paragraphe prcdent pour la classe Etre_Vivant que nous avons dclare abstract parce qu'elle contenait la mthode abstraite SeDeplacer. Une classe abstract ne peut pas tre instancie directement, seule une classe drive (sous-classe) qui redfinit obligatoirement toutes les mthodes abstract de la classe mre peut tre instancie. Consquence du paragraphe prcdent, une classe drive qui redfinit toutes les mthodes abstract de la classe mre sauf une (ou plus d'une) ne peut pas tre instancie et subit la mme rgle que la classe mre : elle contient au moins une mthode abstraite donc elle est aussi une classe abstraite et doit donc tre dclare en abstract. Une classe abstract peut contenir des mthodes non abstraites et donc implantes dans la classe. Une classe abstract peut mme ne pas contenir du tout de mthodes abstraites, dans ce cas une classe fille n'a pas la ncessit de redfinir les mthodes de la classe mre pour tre instancie. Delphi contrairement C# et Java, ne possde pas ce jour le modle de la classe abstraite, seule la version Delphi2005 pour le Net Framework possde les mmes caractristiques que C#. Interface Lorsqu'une classe est dclare en abstract et que toutes ses mthodes sont dclares en abstract, on appelle en C# une telle classe une Interface.

Premier pas dans .Net avec C#2.0 - ( rv. 28.08.2006 )

- Rm di Scala

page

128

Rappel classes abstraites-interfaces

Les interfaces ressemblent aux classes abstraites sur un seul point : elles contiennent des membres expliquant certains comportements sans les implmenter. Les classes abstraites et les interfaces se diffrencient principalement par le fait qu'une classe peut implmenter un nombre quelconque d'interfaces, alors qu'une classe abstraite ne peut hriter que d'une seule classe abstraite ou non.

Vocabulaire et concepts :

Une interface est un contrat, elle peut contenir des proprits, des mthodes et des vnements mais ne doit contenir aucun champ ou attribut. Une interface ne peut pas contenir des mthodes dj implmentes. Une interface doit contenir des mthodes non implmentes. Une interface est hritable. On peut contsruire une hirarchie d'interfaces. Pour pouvoir construire un objet partir d'une interface, il faut dfinir une classe non abstraite implmentant toutes les mthodes de l'interface.

Une classe peut implmenter plusieurs interfaces. Dans ce cas nous avons une excellente alternative l'hritage multiple. Lorsque l'on cre une interface, on fournit un ensemble de dfinitions et de comportements qui ne devraient plus tre modifis. Cette attitude de constance dans les dfinitions, protge les applications crites pour utiliser cette interface. Les variables de types interface respectent les mmes rgles de transtypage que les variables de types classe. Les objets de type classe clA peuvent tre transtyps et refrencs par des variables d'interface IntfA dans la mesure o la classe clA implmente linterface IntfA. (cf. polymorphisme d'objet)

Si vous voulez utiliser la notion d'interface pour fournir un polymorphisme une famille de classes, elles doivent toutes implmenter cette interface, comme dans l'exemple ci-dessous. Exemple : l'interface Vhicule dfinissant 3 mthodes (abstraites) Demarrer, RpartirPassagers de
Premier pas dans .Net avec C#2.0 - ( rv. 28.08.2006 )
- Rm di Scala

page

129

rpartition des passagers bord du vhicule (fonction de la forme, du nombre de places, du personnel charg de s'occuper de faire fonctionner le vhicule...), et PriodicitMaintenance renvoyant la priodicit de la maintenance obligatoire du vhicule (fonction du nombre de km ou miles parcourus, du nombre d'heures d'activits,...) Soit l'interface Vhicule dfinissant ces 3 mthodes : interface IVehicule{ void Demarrer( ); void RpartirPassager( ); void PriodicitMaintenance( ); } Soient les deux classes Vhicule terrestre et Vhicule marin, qui implmentent partiellement chacune l'interface Vhicule , ainsi que trois classes voiture, voilier et croiseur hritant de ces deux classes :

Les trois mthodes de l'interface Vhicule sont abstraites et publiques par dfinition. Les classes Vhicule terrestre et Vhicule marin sont abstraites, car la mthode abstraite Demarrer de l'interface Vhicule n'est pas implmente elle reste comme "modle" aux futures classes. C'est dans les classes voiture, voilier et croiseur que l'on implmente le comportement prcis du genre de dmarrage.

Dans cette vision de la hirarchie on a suppos que les classes abstraites Vhicule terrestre et Vhicule marin savent comment rpartir leur ventuels passagers et quand effectuer une maintenance du vhicule. Les classes voiture, voilier et croiseur , n'ont plus qu' implmenter chacune son propre comportement de dmarrage.

Une interface C# peut tre qualifie par un des 4 modificateur public, protected, internal, private.
Premier pas dans .Net avec C#2.0 - ( rv. 28.08.2006 )
- Rm di Scala

page

130

Contrairement Java, une classe abstraite C# qui implmente une interface doit obligatoirement dclarer toutes les mthodes de l'interface, celles qui ne sont pas implmentes dans la classe abstraite doivent tre dclares abstract. C'est le cas dans l'exemple ci-dessous pour la mthode abstraite Demarrer , nous proposons deux critures possibles pour cette hirarchie de classe : C# mthode abstraite sans corps
interface IVehicule{ void Demarrer( ); void RpartirPassager( ); void PriodicitMaintenance( ); } abstract class Terrestre : IVehicule { public abstract void Demarrer( ); public virtual void RpartirPassager( ){...} public virtual void PriodicitMaintenance( ){...} } class Voiture : Terrestre { override public void Demarrer( ){...} } abstract class Marin : IVehicule { public abstract void Demarrer( ); public virtual void RpartirPassager( ){...} public virtual void PriodicitMaintenance( ){...} } class Voilier : Marin { override public void Demarrer( ){...} } class Croiseur : Marin { override public void Demarrer( ){...} }

C# mthode virtuelle corps vide


interface IVehicule{ void Demarrer( ); void RpartirPassager( ); void PriodicitMaintenance( ); } abstract class Terrestre : IVehicule { public virtual void Demarrer( ){} public virtual void RpartirPassager( ){...} public virtual void PriodicitMaintenance( ){...} } class Voiture : Terrestre { override public void Demarrer( ){...} } abstract class Marin : IVehicule { public virtual void Demarrer( ) {} public virtual void RpartirPassager( ){...} public virtual void PriodicitMaintenance( ){...} } class Voilier : Marin { override public void Demarrer( ){...} } class Croiseur : Marin { override public void Demarrer( ){...} }

Les mthodes RpartirPassagers, PriodicitMaintenance et Demarrer sont implantes en virtual , soit comme des mthodes liaison dynamique, afin de laisser la possibilit pour des classes enfants de redfinir ces mthodes. Remarque : Attention le qualificateur new ne peut masquer que des membres non abstract. Un membre abstract doit imprativement tre redfini (implment) par le qualificateur override, car ce genre de membre est implicitement virtual en C#.

Premier pas dans .Net avec C#2.0 - ( rv. 28.08.2006 )

- Rm di Scala

page

131

Soit titre de comparaison, les deux mmes critures en Java de ces classes :
Java , mthode abstraite sans corps

Java mthode virtuelle corps vide

Premier pas dans .Net avec C#2.0 - ( rv. 28.08.2006 )

- Rm di Scala

page

132

2. Les objets : des rfrences ou des valeurs


Les classes sont des descripteurs d'objets, les objets sont les agents effectifs et "vivants" implantant les actions d'un programme. Les objets dans un programme ont une vie propre : Ils naissent (ils sont crs ou allous). Ils agissent (ils s'envoient des messages grce leurs mthodes). Ils meurent (ils sont dsallous, automatiquement en C#).

C'est dans le segment de mmoire du CLR de .NetFramework que s'effectuent l'allocation et la dsallocation d'objets. Objets type valeur Les classes encapsulant les types lmentaires dans .NET Framework sont des classes de type valeur du genre structures. Dans le CLS une classe de type valeur est telle que les allocations d'objets de cette classe se font directement dans la pile et non dans le tas, il n'y a donc pas de rfrence pour un objet de type valeur et lorsqu'un objet de type valeur est pass comme paramtre il est pass par valeur. Dans .NET Framework les classes-structures de type valeur sont dclares comme structures et ne sont pas drivables Objets type rfrence Le principe d'allocation et de reprsentation des objets type rfrence en C# est identique celui de Delphi il s'agit de la rfrence, qui est une encapsulation de la notion de pointeur. Dans .NET Framework les classes de type rfrence sont dclares comme des classes classiques et sont drivables. Afin d'clairer le lecteur prenons par exemple un objet x instanci partir d'une classe de type rfrence et un objet y instanci partir d'un classe de type valeur contenant les mmes membres que la classe par rfrence. Ci-dessous le schma d'allocation de chacun des deux catgories d'objets :

Pour les types valeurs, la gestion mmoire des objets est classiquement celle de la pile dynamique, un tel objet se comporte comme une variable locale de la mthode dans laquelle il est instanci et ne ncessite pas de gestion supplmentaire. Seuls les objets type rfrence instancis sur le tas, ncessitent une gestion mmoire spciale que nous dtaillons ci-aprs (dans un
Premier pas dans .Net avec C#2.0 - ( rv. 28.08.2006 )
- Rm di Scala

page

133

programme C# les types rfrences du dveloppeur reprsentent prs de 99% des objets du programme). 2.1 Modle de la rfrence en C# Rappelons que dans le modle de la rfrence chaque objet (reprsent par un identificateur de variable) est caractris par un couple (rfrence, bloc de donnes). Comme en Delphi, C# dcompose l'instanciation (allocation) d'un objet en deux tapes : La dclaration d'identificateur de variable type qui contiendra la rfrence, la cration de la structure de donnes elle-mme (bloc objet de donnes) avec new.

Delphi type Un = class ...... end; // la dclaration : var x , y : Un; .... // la cration : x := Un.create ; y := Un.create ; class Un { ... } // la dclaration : Un x , y ; .... // la cration : x = new Un( ); y = new Un( );

C#

Aprs excution du pseudo-programme prcdent, les variables x et y contiennent chacune une rfrence (adresse mmoire) vers un bloc objet diffrent:

Un programme C# est fait pour tre excut par l'environnement CLR de .NetFramework. Deux objets C# seront instancis dans le CLR de la manire suivante :

Premier pas dans .Net avec C#2.0 - ( rv. 28.08.2006 )

- Rm di Scala

page

134

Attitude rapprocher pour comparaison, celle dont Delphi gre les objets dans une pile d'excution de type LIFO et un tas :

Attention l'utilisation de l'affectation entre variables d'objets dans le modle de reprsentation par rfrence. L'affectation x = y ne recopie pas le bloc objet de donnes de y dans celui de x, mais seulement la rfrence (l'adresse) de y dans la rfrence de x. Visualisons cette remarque importante : Situation au dpart, avant affectation

Situation aprs l'affectation " x = y "

En C#, la dsallocation tant automatique, le bloc de donnes objet qui tait rfrenc par y avant
Premier pas dans .Net avec C#2.0 - ( rv. 28.08.2006 )
- Rm di Scala

page

135

l'affectation, n'est pas perdu, car le garbage collector se charge de restituer la mmoire libre au segment de mmoire du CLR

2.2 Les constructeurs d'objets rfrences ou valeurs Un constructeur est une mthode spciale d'une classe dont la seule fonction est d'instancier un objet (crer le bloc de donnes). Comme en Delphi une classe C# peut possder plusieurs constructeurs, il est possible de pratiquer des initialisations d'attributs dans un constructeur. Comme toutes les mthodes, un constructeur peut avoir ou ne pas avoir de paramtres formels. Si vous ne dclarez pas de constructeur spcifique pour une classe, par dfaut C# attribue automatiquement un constructeur sans paramtres formels, portant le mme nom que la classe. A la diffrence de Delphi o le nom du constructeur est quelconque, en C# le( ou les) constructeur doit obligatoirement porter le mme nom que la classe (majuscules et minuscules comprises). Un constructeur d'objet d'une classe n'a d'intrt que s'il est visible par tous les programmes qui veulent instancier des objets de cette classe, c'est pourquoi l'on mettra toujours le mot clef public devant la dclaration du constructeur. Un constructeur est une mthode spciale dont la fonction est de crer des objets, dans son en-tte il n'a pas de type de retour et le mot clef void n'est pas non plus utilis !

Soit une classe dnomme Un dans laquelle, comme nous l'avons fait jusqu' prsent nous n'indiquons aucun constructeur spcifique : class Un { int a; } Automatiquement C# attribue un constructeur public cette classe public Un ( ). C'est comme si C# avait introduit dans votre classe votre insu , une nouvelle mthode dnomme Un. Cette mthode "cache" n'a aucun paramtre et aucune instruction dans son corps. Ci-dessous un exemple de programme C# correct illustrant ce qui se passe : class Un { public Un ( ) { } int a; }

Vous pouvez programmer et personnaliser vos propres constructeurs. Une classe C# peut contenir plusieurs constructeurs dont les en-ttes diffrent uniquement par la liste des paramtres formels.

Premier pas dans .Net avec C#2.0 - ( rv. 28.08.2006 )

- Rm di Scala

page

136

Exemple de constructeur avec instructions : C# class Un { public Un ( ) { a = 100 } int a; } Explication

Le constructeur public Un sert ici initialiser 100 la valeur de l'attribut "int a" de chaque objet qui sera instanci.

Exemple de constructeur avec paramtre : C# class Un { public Un (int b ) { a = b; } int a; } Explication Le constructeur public Un sert ici initialiser la valeur de l'attribut "int a" de chaque objet qui sera instanci. Le paramtre int b contient cette valeur.

Exemple avec plusieurs constructeurs : C# class Un { public Un (int b ) { a = b; } public Un ( ) { a = 100; } public Un (float b ) { a = (int)b; } int a; } Explication

La classe Un possde 3 constructeurs servant initialiser chacun d'une manire diffrente le seul attribut int a.

Il est possible de rappeller un constructeur de la classe dans un autre constructeur, pour cela C# utilise comme Java le mot clef this, avec une syntaxe diffrente : Exemple avec un appel un constructeur de la mme classe: C# class Un { int a; public Un (int b ) { a = b; } public Un ( ) Explication La classe Un possde 3 constructeurs servant initialiser chacun d'une manire diffrente le seul attribut int a.

Soit le dernier constructeur :


- Rm di Scala

Premier pas dans .Net avec C#2.0 - ( rv. 28.08.2006 )

page

137

{ a = 100; } public Un (float b ) { a = (int)b; } public Un (int x , float y ) : this(y) { a += 100; } }

public Un (int x , float y ) : this(y) { a += 100; } Ce constructeur appelle tout d'abord le constructeur Un (float y ) par l'intermdiaire de this(y), puis il excute le corps de mthode soit : a += 100; Ce qui revient calculer : a = (int)y + 100 ;

Comparaison Delphi - C# pour la dclaration de constructeurs Delphi class Un { int a; public Un ( ) { a = 100; } public Un (int b ) { a = b; } public Un (float b ) { a = (int)b; } public Un (int x , float y ) : this(y) { a += 100; } } C#

Un = class a : integer; public constructor creer; overload; constructor creer (b:integer); overload; constructor creer (b:real); overload; end; implementation constructor Un.creer; begin a := 100 end; constructor Un.creer(b:integer); begin a := b end; constructor Un.creer(b:real); begin a := trunc(b) end; constructor Un.creer(x:integer; y:real); begin self.creer(y); a := a+100; end;

En Delphi un constructeur a un nom quelconque, tous les constructeurs peuvent avoir des noms diffrents ou le mme nom comme en C#.

2.3 Utilisation du constructeur d'objet automatique (par dfaut) Le constructeur d'objet par dfaut de toute classe C# qu'elle soit de type valeur ou de type rfrence, comme nous l'avons signal plus haut est une mthode spciale sans paramtre, l'appel
Premier pas dans .Net avec C#2.0 - ( rv. 28.08.2006 )
- Rm di Scala

page

138

cette mthode spciale afin de construire un nouvel objet rpond une syntaxe spcifique par utilisation du mot clef new. Syntaxe Pour un constructeur sans paramtres formels, l'instruction d'instanciation d'un nouvel objet partir d'un identificateur de variable dclare selon un type de classe, s'crit syntaxiquement ainsi :

Exemple : (deux faons quivalentes de crer un objet x de classe Un) Un x ; x = new Un( );

Un x = new Un( )

Cette instruction cre dans le segment de mmoire, un nouvel objet de classe Un dont la rfrence (l'adresse) est mise dans la variable x, si x est de type rfrence, ou bien l'objet est directement cr dans la pile et mis dans la variable x, si x est de type valeur.

Soit Un une classe de type rfrence et Deux une autre classe de type valeur, ci-dessous une image des rsulats de l'instanciation d'un objet de chacune de ces deux classes : Un x = new Un( ) ; Deux y = new Deux ( ) ;

Dans l'exemple ci-dessous, nous utilisons le constructeur par dfaut de la classe Un , pour crer deux objets dans une autre classe : class Un { ... }
Premier pas dans .Net avec C#2.0 - ( rv. 28.08.2006 )

- Rm di Scala

page

139

class UnAutre { // la dclaration : Un x , y ; .... // la cration : x = new Un( ); y = new Un( ); } Un programme de 2 classes, illustrant l'affectation de rfrences : C#
class AppliClassesReferences { public static void Main(String [ ] arg) { Un x,y ; x = new Un( ); y = new Un( ); System.Console.WriteLine("x.a="+x.a); System.Console.WriteLine"y.a="+y.a); y = x; x.a =12; System.Console.WriteLine("x.a="+x.a); System.Console.WriteLine("y.a="+y.a); } } class Un { int a=10; }

Explication
Ce programme C# contient deux classes : class AppliClassesReferences et class Un La classe AppliClassesReferences est une classe excutable car elle contient la mthode main. C'est donc cette mthode qui agira ds l'excution du programme.

Dtaillons les instructions


Un x,y ; x = new Un( ); y = new Un( ); System.Console.WriteLine("x.a="+x.a); System.Console.WriteLine("y.a="+y.a);

Que se passe-t-il l'excution ?


Instanciation de 2 objets diffrents x et y de type Un. Affichage de : x.a = 10 y.a = 10 La rfrence de y est remplace par celle de x dans la variable y (y pointe donc vers le mme bloc que x). On change la valeur de l'attribut a de x, et l'on demande d'afficher les attributs de x et de y : x.a = 12 y.a = 12 Comme y pointe vers x, y et x sont maintenant le mme objet sous deux noms diffrents !

y = x;

x.a =12; System.Console.WriteLine("x.a="+x.a); System.Console.WriteLine("y.a="+y.a);

2.4 Utilisation d'un constructeur d'objet personnalis L'utilisation d'un constructeur personnalis d'une classe est semblable celle du constructeur par
Premier pas dans .Net avec C#2.0 - ( rv. 28.08.2006 )
- Rm di Scala

page

140

dfaut de la classe. La seule diffrence se trouve lors de l'instanciation : il faut fournir des paramtres effectifs lors de l'appel au constructeur. Syntaxe

Exemple avec plusieurs constructeurs : une classe C# class Un { int a ; public Un (int b ) { a=b; } public Un ( ) { a = 100 ; } public Un (float b ) { a = (int)b ; } public Un (int x , float y ) : this(y) { a += 100; } } Des objets crs Un obj1 = newUn( ); Un obj2 = new Un( 15 ); int k = 14; Un obj3 = new Un( k ); Un obj4 = new Un( 3.25f ); float r = -5.6; Un obj5 = new Un( r ); int x = 20; float y = -0.02; Un obj6 = new Un( x , y );

2.5 Le mot clef this - cas de la rfrence seulement Il est possible de dnommer dans les instructions d'une mthode de classe, un futur objet qui sera instanci plus tard. Le paramtre ou (mot clef) this est implicitement prsent dans chaque objet instanci et il contient la rfrence l'objet actuel. Il joue exactement le mme rle que le mot clef self en Delphi. Nous avons dj vu une de ses utilisations dans le cas des constructeurs. C# class Un { public Un ( ) { a = 100; } int a; } C# quivalent class Un { public Un ( ) { this.a = 100; } int a; }

Dans le programme de droite le mot clef this fait rfrence l'objet lui-mme, ce qui dans ce cas est superflu puisque la variable int a est un champ de l'objet.
Premier pas dans .Net avec C#2.0 - ( rv. 28.08.2006 )
- Rm di Scala

page

141

Montrons deux exemples d'utilisation pratique de this. Cas o l'objet est pass comme un paramtre dans une de ses mthodes : C#
class Un { public Un ( ) { a = 100; } public void methode1(Un x) { System.Console.WriteLine("champ a = " + x.a); } public void methode2( int b ) { a += b; methode1(this); } int a; }

Explications
La methode1(Un x) reoit un objet de type Exemple en paramtre et imprime son champ int a. La methode2( int b ) reoit un entier int b qu'elle additionne au champ int a de l'objet, puis elle appelle la mthode1 avec comme paramtre l'objet lui-mme.

Comparaison Delphi - C# sur cet exemple (similitude complte) Delphi


Un = class a : integer; public constructor creer; procedure methode1( x:Un ); procedure methode2 ( b:integer ); end; implementation constructor Un.creer; begin a := 100 end; procedure Un.methode1( x:Un ); begin showmessage( 'champ a ='+inttostr(x.a) ) end; procedure Un.methode2 ( b:integer ); begin a := a+b; methode1(self) end;

C#
class Un { public Un ( ) { a = 100; } public void methode1(Un x) { System.Console.WriteLine("champ a ="+x.a); } public void methode2( int b ) { a += b; methode1(this); } int a; }

Cas o le this sert outrepasser le masquage de visibilit : C#


class Un { int a; public void methode1(float a) { a = this.a + 7 ; } }

Explications
La methode1(float a) possde un paramtre float a dont le nom masque le nom du champ int a. Si nous voulons malgr tout accder au champ de l'objet, l'objet tant rfrenc par this, "this.a" est donc le champ int a de l'objet lui-mme.

Premier pas dans .Net avec C#2.0 - ( rv. 28.08.2006 )

- Rm di Scala

page

142

Comparaison Delphi - C# sur ce second exemple (similitude complte aussi) Delphi Un = class a : integer; public procedure methode( a:real ); end; implementation procedure Un.methode( a:real );begin a = self.a + 7 ; end; C# class Un { int a; public void methode(float a) { a = this.a + 7 ; } }

3. Variables et mthodes
Nous examinons dans ce paragraphe comment C# utilise les variables et les mthodes l'intrieur d'une classe. Il est possible de modifier des variables et des mthodes d'une classe ceci sera examine plus loin. En C#, les champs et les mthodes sont classs en deux catgories : Variables et mthodes de classe Variables et mthodes d'instance

3.1 Variables dans une classe en gnral Rappelons qu'en C#, nous pouvons dclarer dans un bloc (for, try,...) de nouvelles variables la condition qu'elles n'existent pas dj dans le corps de la mthode o elles sont dclares. Nous les dnommerons : variables locales de mthode. Exemple de variables locales de mthode : class Exemple { void calcul ( int x, int y ) {int a = 100; for ( int i = 1; i<10; i++ ) {char carlu;
System.Console.Write("Entrez un caractre : "); carlu = (char)System.Console.Read( );

La dfinition int a = 100; est locale la mthode en gnral La dfinition int i = 1; est locale la boucle for.

int b =15; a =.... .....

Les dfinitions char carlu et int b sont locales au corps de la boucle for.

Premier pas dans .Net avec C#2.0 - ( rv. 28.08.2006 )

- Rm di Scala

page

143

} } }

C# ne connat pas la notion de variable globale au sens habituel donn cette dnomination, dans la mesure o toute variable ne peut tre dfinie qu' l'intrieur d'une classe, ou d'une mthode inclue dans une classe. Donc part les variables locales de mthode dfinies dans une mthode, C# reconnat une autre catgorie de variables, les variables dfinies dans une classe mais pas l'intrieur d'une mthode spcifique. Nous les dnommerons : attributs de classes parce que ces variables peuvent tre de deux catgories. Exemple de attributs de classe : class AppliVariableClasse { float r ; void calcul ( int x, int y ) { .... } int x =100; int valeur ( char x ) { ..... } long y; } Les attributs de classe peuvent tre soit de la catgorie des variables de classe, soit de la catgorie des variables d'instance. 3.2 Variables et mthodes d'instance C# se comporte comme un langage orient objet classique vis vis de ses variables et de ses mthodes. A chaque instanciation d'un nouvel objet d'une classe donne, la machine CLR enregistre le p-code des mthodes de la classe dans la zone de stockage des mthodes, elle alloue dans le segment de mmoire autant d'emplacements mmoire pour les variables que d'objet crs. C# dnomme cette catgorie les variables et les mthodes d'instance. une classe C# class AppliInstance { int x ; int y ; } Instanciation de 3 objets AppliInstance obj1 = new AppliInstance( ); AppliInstance obj2 = new AppliInstance( ); AppliInstance obj3 = new AppliInstance( ); Les variables float r , long y et int x sont des attributs de classe (ici en fait plus prcisment, des variables d'instance). La position de la dclaration de ces variables n'a aucune importance. Elles sont visibles dans tout le bloc classe (c'est dire visibles par toutes les mthodes de la classe).

Conseil : regroupez les variables de classe au dbut de la classe afin de mieux les grer.

Premier pas dans .Net avec C#2.0 - ( rv. 28.08.2006 )

- Rm di Scala

page

144

Segment de mmoire associ ces 3 objets si la classe AppliInstance est de type rfrence :

Segment de mmoire associ ces 3 objets si la classe AppliInstance tait de type valeur (pour mmoire):

struct AppliIn stance { int x ; int y ; }

Un programme C# 2 classes illustrant l'exemple prcdent (classe rfrence): Programme C# excutable class AppliInstance { public int x = -58 ; public int y = 20 ; } class Utilise
Premier pas dans .Net avec C#2.0 - ( rv. 28.08.2006 )
- Rm di Scala

page

145

{ public static void Main( ) { AppliInstance obj1 = new AppliInstance( ); AppliInstance obj2 = new AppliInstance( ); AppliInstance obj3 = new AppliInstance( ); System.Console.WriteLine( "obj1.x = " + obj1.x ); } }

3.3 Variables et mthodes de classe - static Variable de classe On identifie une variable ou une mthode de classe en prcdant sa dclaration du mot clef static. Nous avons dj pris la majorit de nos exemples simples avec de tels composants. Voici deux dclarations de variables de classe : static int x ; static int a = 5; Une variable de classe est accessible comme une variable d'instance(selon sa visibilit), mais aussi sans avoir instancier un objet de la classe, uniquement en rfrenant la variable par le nom de la classe dans la notation de chemin uniforme d'objet. une classe C# class AppliInstance { static int x ; int y ; } Instanciation de 3 objets AppliInstance obj1 = new AppliInstance( ); AppliInstance obj2 = newAppliInstance( ); AppliInstance obj3 = newAppliInstance( );

Voici une image du segment de mmoire associ ces 3 objets :

Exemple de variables de classe :


Premier pas dans .Net avec C#2.0 - ( rv. 28.08.2006 ) page

- Rm di Scala

146

class ApplistaticVar { public static int x =15 ; } class UtiliseApplistaticVar { int a ; void f( ) { a = ApplistaticVar.x ; ..... } }

La dfinition "static int x =15 ;" cre une variable de la classe ApplistaticVar, nomme x. L'instruction "a = ApplistaticVar.x ;" utilise la variable x comme variable de classe ApplistaticVar sans avoir instanci un objet de cette classe.

Nous pouvons utiliser la classe Math ( public sealed class Math ) qui contient des constantes et des fonctions mathmatiques courantes : public static const double E; // la constante e reprsente la base du logarithme nprien. public static const double PI; // la constante pi reprsente le rapport de la circonfrence d'un cercle son diamtre.

Mthode de classe Une mthode de classe est une mthode dont l'implmentation est la mme pour tous les objets de la classe, en fait la diffrence avec une mthode d'instance a lieu sur la catgorie des variables sur lesquelles ces mthodes agissent. De par leur dfinition les mthodes de classe ne peuvent travailler qu'avec des variables de classe, alors que les mthodes d'instances peuvent utiliser les deux catgories de variables. Un programme correct illustrant le discours : C#
class Exemple { public static int x ; int y ; public void f1(int a) { x = a; y = a; } public static void g1(int a) { x = a; } } class Utilise { public static void Main( ) { Exemple obj = new Exemple( ); obj.f1(10); System.Console.WriteLine("<f1(10)>obj.x="+ Exemple.x); Exemple.g1(50); System.Console.WriteLine("<g1(50)>obj.x="+ Exemple.x); } }

Explications
public void f1(int a) { x = a; //accs la variable de classe y = a ; //accs la variable d'instance } public static void g1(int a) { x = a; //accs la variable de classe y = a ; //engendrerait un erreur de compilation : accs une variable non static interdit ! } La mthode f1 accde toutes les variables de la classe Exemple, la mthode g1 n'accde qu'aux variables de classe (static et public). Aprs excution on obtient : <f1(10)>obj.x = 10 <g1(50)>obj.x = 50

Premier pas dans .Net avec C#2.0 - ( rv. 28.08.2006 )

- Rm di Scala

page

147

Rsum pratique sur les membres de classe en C#


1) - Les mthodes et les variables de classe sont prcdes obligatoirement du mot clef static. Elles jouent un rle semblable celui qui est attribu aux variables et aux sousroutines globales dans un langage impratif classique.

C# class Exemple1 { int a = 5; static int b = 19; void m1( ){...} static void m2( ) {...} }

Explications La variable a dans int a = 5; est une variable d'instance. La variable b dans static int b = 19; est une variable de classe. La mthode m2 dans static void m2( ) {...} est une mthode de classe.

2) - Pour utiliser une variable x1 ou une mthode meth1 de la classe Classe1, il suffit de d'crire Classe1.x1 ou bien Classe1.meth1.

C# class Exemple2 { public static int b = 19; public static void m2( ) {...} } class UtiliseExemple { Exemple2.b = 53; Exemple2.m2( ); ... }

Explications Dans la classe Exemple2, b est une variable de classe, m2 une mthode de classe. La classe UtiliseExemple fait appel la mthode m2 directement avec le nom de la classe, il en est de mme avec le champ b de la classe Exemple2

3) - Une variable de classe (prcde du mot clef static) est partage par tous les objets de la mme classe.

C#
class AppliStatic { public static int x = -58 ; public int y = 20 ; ... } class Utilise { public static void Main(String [ ] arg) { AppliStatic obj1 = new AppliStatic( );

Explications
Dans la classe AppliStatic x est une variable de classe, et y une variable d'instance. La classe Utilise cre 3 objets (obj1,obj2,obj3) de classe AppliStatic. L'instruction obj1.y = 100; est un accs au champ y de l'instance obj1. Ce n'est que le champ y de cet objet qui est modifi,les champs y des objets obj2 et
- Rm di Scala

Premier pas dans .Net avec C#2.0 - ( rv. 28.08.2006 )

page

148

AppliStatic obj2 = new AppliStatic( ); AppliStatic obj3 = new AppliStatic( ); obj1.y = 100; AppliStatic.x = 101; System.Console.WriteLine("obj1.x="+ AppliStatic.x); System.Console.WriteLine("obj1.y="+obj1.y); System.Console.WriteLine("obj2.x="+ AppliStatic.x); System.Console.WriteLine("obj2.y="+obj2.y); System.Console.WriteLine("obj3.x="+ AppliStatic.x); System.Console.WriteLine("obj3.y="+obj3.y); AppliStatic.x = 99; System.Console.WriteLine("AppliStatic.x=" + AppliStatic.x); } }

obj3 restent inchangs Il ny a quune seule manire d'accder la variable static x, comme pour toute variable de classe, en utilisant le nom de la classe : AppliStatic.x = 101; Dans ce cas cette variable x est modifie globalement et donc tous les champs x de tous les objets obj1, obj2 et obj3 prennent la nouvelle valeur.

Au dbut lors de la cration des 3 objets, chacun des champs x vaut -58 et chacun des champs y vaut 20, l'affichage par System.Console.WriteLine(...) donne les rsultats suivants qui dmontrent le partage de la variable x par tous les objets. Aprs excution : obj1.x = 101 obj1.y = 100 obj2.x = 101 obj2.y = 20 obj3.x = 101 obj3.y = 20 AppliStatic.x = 99

4) - Une mthode de classe (prcde du mot clef static) ne peut utiliser que des variables de classe (prcdes du mot clef static) et jamais des variables d'instance.Une mthode d'instance peut accder aux deux catgories de variables

5) - Une mthode de classe (prcde du mot clef static) ne peut appeler (invoquer) que des mthodes de classe (prcdes du mot clef static).

C#
class AppliStatic { public static int x = -58 ; public int y = 20 ; public void f1(int a) { AppliStatic.x = a; y=6; } } class Utilise { static void f2(int a) { AppliStatic.x = a;

Explications
Nous reprenons l'exemple prcdent en ajoutant la classe AppliStatic une mthode interne f1 : public void f1(int a) { AppliStatic.x = a; y=6; } Cette mthode accde la variable de classe comme un champ d'objet. Nous rajoutons la classe Utilise, un mthode static (mthode de classe) note f2: static void f2(int a) { AppliStatic.x = a; } Cette mthode accde elle aussi la variable de
- Rm di Scala

Premier pas dans .Net avec C#2.0 - ( rv. 28.08.2006 )

page

149

} public static void Main( ) { AppliStatic obj1 = new AppliStatic( ); AppliStatic obj2 = new AppliStatic( ); AppliStatic obj3 = new AppliStatic( ); obj1.y = 100; obj1.x = 101; AppliStatic.x = 99; f2(101); obj1.f1(102); } }

classe parce qu c'est une mthode static. Nous avons donc trois manires d'accder la variable static x, : soit directement comme une variable de classe proprement dite : AppliStatic.x = 99; soit indirectement par une mthode d'instance sur son champ : obj1.f1(102); soit indirectement par une mthode static (de classe) : f2(101);

Comme la mthode Main est static, elle peut invoquer la mthode f2 qui est aussi static.

Au paragraphe prcdent, nous avons indiqu que C# ne connaissait pas la notion de variable globale stricto sensu, mais en fait une variable static peut jouer le rle d'un variable globale pour un ensemble d'objets instancis partir de la mme classe.

Attention :
Contrairement Java qui autorise laccs une variable static partir dun nom dobjet la place du nom de la classe de dfinition, le compilateur C# impose une cohrence dans les dclarations, en refusant cette possibilit.

Depuis la version 2.0, une classe peut tre static, dans ce cas elle correspond un module de bibliothque de membres (mthodes et variables) obligatoirement tous static.
static class AppliStatic { public static int x = -58 ; public static int y = 20 ; public static void f1(int a) { x = a; y=6; } }

Premier pas dans .Net avec C#2.0 - ( rv. 28.08.2006 )

- Rm di Scala

page

150

Polymorphisme d'objet en

Plan gnral:

Rappel des notions de base Polymorphisme d'objet en C# : dfinitions


1.1 Instanciation et utilisation dans le mme type 1.2 Polymorphisme d'objet implicite 1.3 Polymorphisme d'objet explicite par transtypage 1.4 Utilisation pratique du polymorphisme d'objet 1.5 Instanciation dans un type ascendant impossible

Premier pas dans .Net avec C#2.0 - ( rv. 28.08.2006 )

- Rm di Scala

page

151

Le polymorphisme en C#
Rappel utile sur les notions de bases Il existe un concept essentiel en POO dsignant la capacit d'une hirarchie de classes fournir diffrentes implmentations de mthodes portant le mme nom et par corollaire la capacit qu'ont des objets enfants de modifier les comportements hrits de leur parents. Ce concept d'adaptation diffrentes "situations" se dnomme le polymorphisme qui peut tre implment de diffrentes manires.

Polymorphisme d'objet C'est une interchangeabilit entre variables d'objets de classes de la mme hirarchie sous certaines conditions, que dnommons le polymorphisme d'objet.

Polymorphisme par hritage de mthode Lorsqu'une classe enfant hrite d'une classe mre, des mthodes supplmentaires nouvelles peuvent tre implmentes dans la classe enfant mais aussi des mthodes des parents peuvent tre substitues pour obtenir des implmentations diffrentes.

Polymorphisme par hritage de classes abstraites Une classe abstraite est une classe qui ne peut pas s'instancier elle-mme ; elle doit tre hrite. Certains membres de la classe peuvent ne pas tre implments, et c'est la classe qui hrite de fournir cette implmentation.

Polymorphisme par implmentation d'interfaces Une interface dcrit la signature complte des membres qu'une classe doit implmenter, mais elle laisse l'implmentation de tous ces membres la charge de la classe d'implmentation de l'interface.

Polymorphisme d'objet en C# Soit une classe Mere et une classe Fille hritant de la classe Mere :

Premier pas dans .Net avec C#2.0 - ( rv. 28.08.2006 )

- Rm di Scala

page

152

Les objets peuvent avoir des comportements polymorphes (s'adapter et se comporter diffrement selon leur utilisation) licites et des comportements polymorphes dangereux selon les langages. Dans un langage dont le modle objet est la rfrence (un objet est un couple : rfrence, bloc mmoire) comme C#, il y a dcouplage entre les actions statiques du compilateur et les actions dynamiques du systme d'excution, le compilateur protge statiquement des actions dynamiques sur les objets une fois crs. C'est la dclaration et l'utilisation des variables de rfrences qui autorise ou non les actions licites grce la compilation. Supposons que nous ayons dclar deux variables de rfrence, l'une de classe Mere, l'autre de classe Fille, une question qui se pose est la suivante : au cours du programme quel genre d'affectation et d'instanciation est-on autoris effectuer sur chacune de ces variables dans un programme C#.
En C# : public class Mere { ..... } public class Fille : Mere { ..... }

L'hritage permet une variabilit entre variables d'objets de classes de la mme hirarchie, c'est cette variabilit que dnommons le polymorphisme d'objet.

Nous envisageons toutes les situations possibles et les valuons, les exemples explicatifs sont crits en C# (lorsqu'il y a discordance avec java ou Delphi autres langages, celle-ci est mentionne explicitement), il existe 3 possibilits diffrentes illustres par le schma cidessous.

Premier pas dans .Net avec C#2.0 - ( rv. 28.08.2006 )

- Rm di Scala

page

153

L'instanciation et l'utilisation de rfrences dans le mme type L'affectation de rfrences : polymorphisme implicite L'affectation de rfrences : polymorphisme par transtypage d'objet La dernire de ces possibilits pose un problme d'excution lorsqu'elle mal employe !

1.1 instanciation dans le type initial et utilisation dans le mme type


Il s'agit ici d'une utilisation la plus classique qui soit, dans laquelle une variable de rfrence d'objet est utilise dans son type de dfinition initial (valable dans tous les LOO)

En C# : Mere x , u ; Fille y , w ; ..... x = new Mere( ) ; // instanciation dans le type initial u = x ; // affectation de rfrences du mme type y = new Fille( ) ; // instanciation dans le type initial v = y ; // affectation de rfrences du mme type

Premier pas dans .Net avec C#2.0 - ( rv. 28.08.2006 )

- Rm di Scala

page

154

1.2 Polymorphisme d'objet implicite

En C# : Mere x ; Fille ObjF = new Fille( ) ; x = ObjF; // affectation de rfrences du type descendant implicite

Nous pouvons en effet dire que x peut se rfrer implicitement tout objet de classe Mere ou de toute classe hritant de la classe Mere.

fig - 1

fig - 2

Premier pas dans .Net avec C#2.0 - ( rv. 28.08.2006 )

- Rm di Scala

page

155

Dans la figure fig-1 ci-dessus, une hirarchie de classes decendant toutes de la classe Mere, dans fig-2 ci-contre le schma montre une rfrence de type Mere qui peut 'pointer' vers n'importe quel objet de classe descendante (polymorphisme d'objet). D'une faon gnrale vous pourrez toujours crire des affectations entre deux rfrences d'objets :
En C# : Classe1 x ; Classe2 y ; .......... x=y; si et seulement si Classe2 est une classe descendante de Classe1.

Exemple pratique tir du schma prcdent 1) Le polymorphisme d'objet est typiquement fait pour reprsenter des situations pratiques figures ci-dessous :

Une hirarchie de classe de vhicules descendant toutes de la classe mre Vehicule, on peut noncer le fait suivant : Un vhicule peut tre de plusieurs sortes : soit un croiseur, soit une voiture, soit un vhicule terrestre etc... Traduit en termes informatiques, si l'on dclare une rfrence de type vhicule (vehicule x) elle pourra pointer vers n'importe quel objet d'une des classe filles de la classe vehicule.
En C# : public class Vehicule { .......... } public class terrestre : Vehicule{ .......... } public class voiture : terrestre { .......... } public class marin : Vehicule { .......... } public class voilier : marin { .......... } public class croiseur : marin { .......... }

Premier pas dans .Net avec C#2.0 - ( rv. 28.08.2006 )

- Rm di Scala

page

156

Mettons en oeuvre la dfinition du polymorphisme implicite : Polymorphisme implicite = cration d'objet de classe descendante rfrenc par une variable parent Ajoutons 2 classes la hirarchie des vhicules :

Partons de la situation pratique suivante : on cre un vhicule du type voiture , on cre une voiture de type berline , enfin on cre un break de type break

Traduit en termes informatiques : nous dclarons 3 rfrences x, y et z de type vehicule, voiture et break et nous crons 3 objets de classe voiture, berline et break. Comme il est possible de crer directement un objet de classe descendante partir d'une rfrence de classe mre, nous proposons les instanciations suivantes : on cre une voiture rfrence par la variable de classe vehicule, on cre une berline rfrence par la variable de classe voiture, enfin on cre un break rfrenc par la variable de classe break.

Premier pas dans .Net avec C#2.0 - ( rv. 28.08.2006 )

- Rm di Scala

page

157

En C# : public class berline : voiture { .......... } public class break : voiture { .......... }

public class Fabriquer { Vehicule x = new voiture ( ) ; voiture y = new berline ( ) ; break z = new break ( ); .......... }

1.3 Polymorphisme d'objet explicite par transtypage


La situation informatique est la suivante : on dclare une variable x de type Mere, on dclare une variable y de type Fille hritant de Mere, on instancie la variable x dans le type descendant Fille (polymorphisme implicite).

Il est alors possible de faire "pointer" la variable y (de type Fille) vers l'objet (de type Fille) auquel se rfre x en effectuant une affectation de rfrences : y = x ne sera pas accepte directement car statiquement les variables x et y ne sont pas du mme type, il faut indiquer au compilateur que l'on souhaite temporairement changer le type de la variable x afin de pouvoir effectuer l'affectation. Cette opration de changement temporaire, se dnomme le transtypage ( note en C# : y = (Fille)x ) :

Premier pas dans .Net avec C#2.0 - ( rv. 28.08.2006 )

- Rm di Scala

page

158

En C# : Mere x ; Fille y ; Fille ObjF = new Fille( ) ; x = ObjF ; // x pointe vers un objet de type Fille y = (Fille) x ; // transtypage et affectation de rfrences du type ascendant explicite compatible dynamiquement.

Attention
La validit du transtypage n'est pas vrifie statiquement par le compilateur, donc si votre variable de rfrence pointe vers un objet qui n'a pas la mme nature que l'oprateur de transtypage, c'est lors de l'excution qu'il y aura production d'un message d'erreur indiquant le transtypage impossible. Il est donc impratif de tester l'appartenance la bonne classe de l'objet transtyper avant de le transtyper, les langages C#, Delphi et Java disposent d'un oprateur permettant de tester cette appartenance ou plutt l'appartenance une hirarchie de classes (oprateur is en C#). L'oprateur as est un oprateur de transtypage de rfrence d'objet semblable l'oprateur ( ). L'oprateur as fournit la valeur null en cas d'chec de conversion alors que l'oprateur ( ) lve une exception.

Premier pas dans .Net avec C#2.0 - ( rv. 28.08.2006 )

- Rm di Scala

page

159

En C# : Mere x ; Fille y ; x = new Mere( ) ; // instanciation dans le type initial { affectation de rfrences du type ascendant explicite mais dangereuse si x est uniquement Mere : } y = (Fille)x ; <--- erreur lors de l'excution ici {affectation accepte statiquement mais refuse dynamiquement, car x pointe vers un objet de type Mere }

En reprenant l'exemple pratique prcdant de la hirarchie des vhicules :

Puisque x pointe vers un objet de type voiture toute variable de rfrence acceptera de pointer vers cet objet, en particulier la variable voiture aprs transtypage de la rfrence de x. En C# l'affectation s'crirait par application de l'oprateur de transtypage :

y = ( voiture ) x;

Pour pallier cet inconvnient de programmation pouvant lever des exceptions lors de l'excution, C# offre au dveloppeur la possibilit de tester l'appartenance d'un objet rfrenc par une variable quelconque une classe ou plutt une hirarchie de classe ; en C# cet oprateur se dnote is : L'oprateur "is" de C# est identique celui de Delphi : L'oprateur is, qui effectue une vrification de type dynamique, est utilis pour vrifier quelle est effectivement la classe d'un objet l'excution. L'expression : objet is classeT renvoie True si objet est une instance de la classe dsigne par classeT ou de l'un de ses descendants, et False sinon. Si objet a la valeur nil, le rsultat est False.

Premier pas dans .Net avec C#2.0 - ( rv. 28.08.2006 )

- Rm di Scala

page

160

En C# : Mere x ; Fille y ; x = new Mere( ) ; // instanciation dans le type initial if ( x is Fille) // test d'appartenance de l'objet rfrenc par x la bonne classe y = (Fille)x ;

1.4 Utilisation pratique du polymorphisme d'objet


Le polymorphisme d'objet associ au transtypage est trs utile dans les paramtres des mthodes.

Lorsque vous dclarez une mthode meth avec un paramtre formel x de type ClasseT : void meth ( ClasseT x ); { ........ } Vous pouvez utiliser lors de l'appel de la mthode meth n'importe quel paramtre effectif de ClasseT ou bien d'une quelconque classe descendant de ClasseT et ensuite l'intrieur de la procdure vous transtypez le paramtre. Cet aspect est utilis en particulier en C# lors de la cration de gestionnaires d'vnements communs plusieurs composants :
private void meth1(object sender, System.EventArgs e) { if (sender is System.Windows.Forms.TextBox) (sender as TextBox).Text="Fin"; else if (sender is System.Windows.Forms.Label) (sender as Label).Text="ok"; // ou encore : if (sender is System.Windows.Forms.TextBox) ( (TextBox)sender ).Text="Fin"; else if (sender is System.Windows.Forms.Label) ( (Label)sender ).Text="ok";

} Autre exemple avec une mthode meth2 personnelle sur la hirarchie des vhicules :
private void meth2 ( vehicule Sender ); { if (Sender is voiture) ((voiture)Sender). ....... ; else if (Sender is voilier) ((voilier)Sender). ....... ; ............ }

Premier pas dans .Net avec C#2.0 - ( rv. 28.08.2006 )

- Rm di Scala

page

161

instanciation dans un type ascendant ( impossible en C#)


Il s'agit ici d'une utilisation non licite qui n'est pas commune tous les langages LOO. Le compilateur C# comme le compilateur Java, refuse ce type de cration d'objet, les compilateurs C++ et Delphi acceptent ce genre d'instanciation en laissant au programmeur le soin de se dbrouiller avec les problmes de cohrence lorsqu'ils apparatront.

Premier pas dans .Net avec C#2.0 - ( rv. 28.08.2006 )

- Rm di Scala

page

162

Polymorphisme de mthode en

Plan gnral:

1. Le polymophisme de mthodes en C#
Rappel des notions de base 1.1 Surcharge et redfinition en C# 1.2 Liaison statique et masquage en C# 1.3 Liaison dynamique en C# 1.4 Comment opre le compilateur Rsum pratique

2. Accs la super classe en C#


2.1 Le mot clef base 2.2 Initialiseur de constructeur this et base 2.3 Comparaison C#, Delphi et Java sur un exemple 2.4 Traitement d'un exercice complet 2.5 Destructeur-finaliseur

Premier pas dans .Net avec C#2.0 - ( rv. 28.08.2006 )

- Rm di Scala

page

163

1. Le polymorphisme de mthode en C#
Nous avons vu au chapitre prcdent le polymorphisme d'objet, les mthodes peuvent tre elles aussi polymorphes. Nous avons vu comment Delphi mettait en oeuvre le polymorphisme d'objet et de mthode, nous voyons ici comment C# hrite une bonne part de Delphi pour ses comportements et de la souplesse de Java pour l'criture. Rappel de base

Polymorphisme par hritage de mthode


Lorsqu'une classe enfant hrite d'une classe mre, des mthodes supplmentaires nouvelles peuvent tre implmentes dans la classe enfant, mais aussi des mthodes des parents substitues pour obtenir des implmentations diffrentes. L'objectif vis en terme de qualiti du logiciel est la rutilisabilit en particulier lorsque l'on ralise une mme opration sur des lments diffrents : opration = ouvrir ( ) ouvrir une fentre de texte, ouvrir un fichier, ouvrir une image etc ...

Surcharge et redfinition avec C#


En informatique ce vocable s'applique aux mthodes selon leur degr d'adaptabilit, nous distinguons alors deux dnominations :
le polymorphisme statique ou la surcharge de mthode le polymorphisme dynamique ou la redfinition de mthode ou encore la surcharge hrite.

1.1 Surcharge La surcharge de mthode (polymorphisme statique de mthode) est une fonctionnalit classique des langages trs volus et en particulier des langages orients objet dont C# fait partie; elle consiste dans le fait qu'une classe peut disposer de plusieurs mthodes ayant le mme nom, mais avec des paramtres formels diffrents ou ventuellement un type de retour diffrent. On dit alors que ces mthodes n'ont pas la mme signature

On rappelle que la signature d'une mthode est forme par l'en-tte de la mthode avec ses paramtres formels et leur type. Nous avons dj utilis cette fonctionnalit prcdement dans le paragraphe sur les constructeurs, o la classe Un disposait de quatre constructeurs surchargs (quatre signatures diffrentes du constructeur) :
class Un { int a; public Un ( ) { a = 100; } public Un (int b ) Premier pas dans .Net avec C#2.0 - ( rv. 28.08.2006 )
- Rm di Scala

page

164

{ a = b; } public Un (float b ) { a = (int)b; } public Un (int x , float y ) : this(y) { a += 100; } }

Mais cette surcharge est possible aussi pour n'importe quelle mthode de la classe autre que le constructeur.

Ci-contre deux exemplaires surchargs de la mthode ouvrir dans la classe PortesEtFenetres :

Le compilateur n'prouve aucune difficult lorsqu'il rencontre un appel l'une des versions surcharge d'une mthode, il cherche dans la dclaration de toutes les surcharges celle dont la signature (la dclaration des paramtres formels) concide avec les paramtres effectifs de l'appel. Remarque : Le polymorphisme statique (ou surcharge) de C# est syntaxiquement semblable celui de Java.

Programme C# excutable
class Un { public int a; public Un (int b ) { a = b; } public void f ( ) { a *=10; } public void f ( int x ) { a +=10*x; } public int f ( int x, char y ) { a = x+(int)y; return a; } } class AppliSurcharge { public static void Main(String [ ] arg) { Un obj = new Un(15); System.Console.WriteLine("<cration> a ="+obj.a); obj.f( ); System.Console.WriteLine("<obj.f()> a ="+obj.a); obj.f(2); System.Console.WriteLine("<obj.f()> a ="+obj.a); obj.f(50,'a'); System.Console.WriteLine("<obj.f()> a ="+obj.a); } }

Explications
La mthode f de la classe Un est surcharge trois fois : public void f ( ) { a *=10; } public void f ( int x ) { a +=10*x; } public int f ( int x, char y ) { a = x+(int)y; return a; } La mthode f de la classe Un peut donc tre appele par un objet instanci de cette classe sous l'une quelconque des trois formes : obj.f( ); pas de paramtre => choix : void f ( ) obj.f(2); paramtre int => choix : void f ( int x ) obj.f(50,'a'); deux paramtres, un int un char => choix : int f ( int x, char y )

Comparaison Delphi - C# sur la surcharge :


Premier pas dans .Net avec C#2.0 - ( rv. 28.08.2006 )
- Rm di Scala

page

165

Delphi
Un = class a : integer; public constructor methode( b : integer ); procedure f;overload; procedure f(x:integer);overload; function f(x:integer;y:char):integer;overload; end; implementation constructor Un.methode( b : integer ); begin a:=b end; procedure Un.f; begin a:=a*10; end; procedure Un.f(x:integer); begin a:=a+10*x end; function Un.f(x:integer;y:char):integer; begin a:=x+ord(y); result:= a end; procedure Main; var obj:Un; begin obj:=Un.methode(15); obj.f; Memo1.Lines.Add('obj.f='+inttostr(obj.a)); obj.f(2); Memo1.Lines.Add('obj.f(2)='+inttostr(obj.a)); obj.f(50,'a'); Memo1.Lines.Add('obj.f(50,''a'')='+inttostr(obj.a)); end; class Un { public int a; public Un (int b ) { a = b; }

C#

public void f ( ) { a *=10; } public void f ( int x ) { a +=10*x; } public int f ( int x, char y ) { a = x+(int)y; return a; } }

class AppliSurcharge { public static void Main(String [ ] arg) { Un obj = new Un(15); System.Console.WriteLine("<cration> a ="+obj.a); obj.f( ); System.Console.WriteLine("<obj.f()> a ="+obj.a); obj.f(2); System.Console.WriteLine("<obj.f()> a ="+obj.a); obj.f(50,'a'); System.Console.WriteLine("<obj.f()> a ="+obj.a); } }

Redfinition
La redfinition de mthode (ou polymorphisme dynamique) est spcifique aux langages orients objet. Elle est mise en oeuvre lors de l'hritage d'une classe mre vers une classe fille dans le cas d'une mthode ayant la mme signature dans les deux classes. Dans ce cas les actions des l'appel de la mthode, dpendent du code inhrent chaque version de la mthode (celle de la classe mre, ou bien celle de la classe fille). Dans l'exemple ci-dessous, nous supposons que dans la classe PortesEtFenetres la mthode ouvrir(fenetre) explique le mode opratoire gnral d'ouverture d'une fentre, il est clair que dans les deux classes descendantes l'on doit "redfinir" le mode opratoire selon que l'on est en prsence d'une fentre la franaise, ou d'une fentre l'anglaise :

Premier pas dans .Net avec C#2.0 - ( rv. 28.08.2006 )

- Rm di Scala

page

166

Redfinition et rpartition des mthodes en C#


La redfinition de mthode peut tre selon les langages :

prcoce
et/ou

tardive

Ces deux actions sont diffrentes selon que le compilateur du langage met en place la laison du code de la mthode immdiatement lors de la compilation (liaison statique ou prcoce) ou bien lorsque le code est li lors de l'excution (laison dynamique ou tardive). Ce phnomne se dnomme la rpartition des mthodes. Le terme de rpartition fait rfrence la faon dont un programme dtermine o il doit rechercher une mthode lorsqu'il rencontre un appel cette mthode. Le code qui appelle une mthode ressemble un appel classique de mthode. Mais les classes ont des faons diffrentes de rpartir les mthodes.

Le langage C# supporte d'une manire identique Delphi, ces deux modes de liaison du code, la liaison statique tant comme en Delphi le mode par dfaut. Le dveloppeur Java sera plus dcontenanc sur ce sujet, car la liaison statique en Java n'existe que pour les methodes de classe static, de plus la liaison du code par dfaut est dynamique en Java.

Donc en C# comme en Delphi , des mots clefs comme virtual et override sont ncessaires pour la redfinition de mthode, ils sont utiliss strictement de la mme manire qu'en Delphi.

Premier pas dans .Net avec C#2.0 - ( rv. 28.08.2006 )

- Rm di Scala

page

167

1.2 Liaison statique et masquage en C# Toute mthode C# qui n'est prcde d'aucun des deux qualificateurs virtual ou override est liaison statique. Le compilateur dtermine l'adresse exacte de la mthode et lie la mthode au moment de la compilation. L'avantage principal des mthodes statiques est que leur rpartition est trs rapide. Comme le compilateur peut dterminer l'adresse exacte de la mthode, il la lie directement (les mthodes virtuelles, au contraire, utilisent un moyen indirect pour rcuprer l'adresse des mthodes l'excution, moyen qui ncessite plus de temps). Une mthode statique ne change pas lorsqu'elle est transmise en hritage une autre classe. Si vous dclarez une classe qui inclut une mthode statique, puis en drivez une nouvelle classe, la classe drive partage exactement la mme mthode situe la mme adresse. Cela signifie qu'il est impossible de redfinir les mthodes statiques; une mthode statique fait toujours exactement la mme chose, quelque soit la classe dans laquelle elle est appele. Si vous dclarez dans une classe drive une mthode ayant le mme nom qu'une mthode statique de la classe anctre, la nouvelle mthode remplace simplement (on dit aussi masque) la mthode hrite dans la classe drive. Comparaison masquage en Delphi et C# : Delphi
type ClasseMere = class x : integer; procedure f (a:integer); end; ClasseFille = class ( ClasseMere ) y : integer; procedure f (a:integer);//masquage end; implementation procedure ClasseMere.f (a:integer); begin... end; procedure ClasseFille.f (a:integer); begin... end;

C#
public class ClasseMere { public int x = 10; public void f ( int a) { x +=a; } } public class ClasseFille : ClasseMere { int y = 20; public void f ( int a) //masquage { x +=a*10+y; } }

Remarque importante : L'exprience montre que les tudiants comprennent immdiatement le masquage lorsque le polymorphisme d'objet n'est pas prsent. Ci-dessous un exemple de classe UtiliseMereFille qui instancie et utilise dans le mme type un objet de classe ClasseMere et un objet de classe ClasseFille :

Premier pas dans .Net avec C#2.0 - ( rv. 28.08.2006 )

- Rm di Scala

page

168

Lors de la compilation de l'instruction M.meth(10), c'est le code de la mthode meth de la classe ClasseMere qui est li avec comme paramtre par valeur 10; ce qui donnera la valeur 11 au champ x de l'objet M. Lors de la compilation de l'instruction F.meth(10), c'est le code de la mthode meth de la classe ClasseFille qui masque celui de la classe parent et qui est donc li avec comme paramtre par valeur 10; ce qui donnera la valeur 101 au champ x de l'objet F.

Pour bien comprendre toute la porte du masquage statique et les risques de mauvaises interprtations, il faut tudier le mme exemple lgrement modifi en incluant le cas du polymorphisme d'objet, plus prcisment le polymorphisme d'objet implicite. Dans l'exemple prcdent nous instancions la variable ClasseMere M en un objet de classe ClasseFille (polymorphisme implicite d'objet) soient les instructions ClasseMere M ; M = new ClasseFille ( ) ; Une erreur courante est de croire que dans ces conditions, dans l'instruction M.meth(10) c'est la mthode meth(int a) de la classe ClasseFille (en particulier si l'on ne connat que Java qui ne pratique pas le masquage) :

Premier pas dans .Net avec C#2.0 - ( rv. 28.08.2006 )

- Rm di Scala

page

169

Que fait alors le compilateur C# dans ce cas ? : il ralise une liaison statique : Lors de la compilation de l'instruction M.meth(10), c'est le code de la mthode meth(int a) de la classe ClasseMere qui est li, car la rfrence M a t dclare de type ClasseMere et peu importe dans quelle classe elle a t instancie ( avec comme paramtre par valeur 10; ce qui donnera la valeur 11 au champ x de l'objet M). Lors de la compilation de l'instruction F.meth(10), c'est le code de la mthode meth de la classe ClasseFille comme dans l'exemple prcdent (avec comme paramtre par valeur 10; ce qui donnera la valeur 101 au champ x de l'objet F).

Voici la bonne configuration de laison effectue lors de la compilation :

Premier pas dans .Net avec C#2.0 - ( rv. 28.08.2006 )

- Rm di Scala

page

170

Afin que le programmeur soit bien conscient d'un effet de masquage d'une mthode hrite par une mthode locale, le compilateur C# envoie, comme le compilateur Delphi, un message d'avertissement indiquant une possibilit de manque de cohrence smantique ou un masquage. S'il s'agit d'un masquage voulu, le petit plus apport par le langage C# est la proposition que vous fait le compilateur de l'utilisation optionnelle du mot clef new qualifiant la nouvelle mthode masquant la mthode parent. Cette criture amliore la lisibilit du programme et permet de se rendre compte que l'on travaille avec une liaison statique. Ci-dessous deux critures quivalentes du masquage de la mthode meth de la classe ClasseMere : masquage C#
public class ClasseMere { int x = 10; public void meth ( int a) //liaison statique { x +=a; } } public class ClasseFille : ClasseMere { public void meth ( int a) //masquage { x +=a*10+y; } }

masquage C# avec new


public class ClasseMere { int x = 10; public void meth ( int a) //liaison statique { x +=a; } } public class ClasseFille : ClasseMere { public new void meth ( int a) //masquage { x +=a*10+y; } }

Premier pas dans .Net avec C#2.0 - ( rv. 28.08.2006 )

- Rm di Scala

page

171

L'exemple ci-dessous rcapitule les notions de masquage et de surcharge en C# : public class ClasseMere { int x = 1; public void meth1 ( int a) { x += a ; } public void meth1 ( int a , int b) { x += a*b ; } } public class ClasseFille : ClasseMere { public new void meth1 ( int a) { x += a*100 ; } public void meth1 ( int a , int b , int c) { x += a*b*c ; } } public class UtiliseMereFille { public static void Main (string [ ] args) { ClasseMere M ; ClasseFille F ; M = new ClasseFille ( ) ; F = new ClasseFille ( ) ; M.meth1 (10) ; <--- meth1(int a) de ClasseMere M.meth1 (10,5) ; <--- meth1(int a, int b) de ClasseMere M.meth1 (10,5,2) ; <--- erreur! n'existe pas dans ClasseMere . F.meth1 (10) ; <--- meth1(int a) de ClasseFille F.meth1 (10,5) ; <--- meth1(int a, int b) de ClasseFille F.meth1 (10,5,2) ; <--- meth1(int a, int b, int c) de ClasseFille } } 1.3 Liaison dynamique (ou redfinition) en C# Dans l'exemple ci-dessous la classe ClasseFille qui hrite de la classe ClasseMere, redfini la mthode f de sa classe mre : Comparaison redfinition Delphi et C# : Delphi
type ClasseMere = class x : integer; procedure f (a:integer);virtual;//autorisation procedure g(a,b:integer); end; ClasseFille = class ( ClasseMere ) y : integer; procedure f (a:integer);override;//redfinition procedure g1(a,b:integer); end; implementation procedure ClasseMere.f (a:integer); begin... end; procedure ClasseMere.g(a,b:integer); begin... Premier pas dans .Net avec C#2.0 - ( rv. 28.08.2006 ) class ClasseMere { public int x = 10; public virtual void f ( int a) { x +=a; } void g ( int a, int b) { x +=a*b; } } class ClasseFille : ClasseMere { int y = 20; public override void f ( int a) //redfinition { x +=a; } void g1 (int a, int b) //nouvelle mthode { ...... } }
- Rm di Scala

C#

page

172

end; procedure ClasseFille.f (a:integer); begin... end; procedure ClasseFille.g1(a,b:integer); begin... end;

Comme delphi, C# peut combiner la surcharge et la redfinition sur une mme mthode, c'est pourquoi nous pouvons parler de surcharge hrite : C#
class ClasseMere { public int x = 10; public virtual void f ( int a) { x +=a; } public virtual void g ( int a, int b) { x +=a*b; } } class ClasseFille : ClasseMere { int y = 20; public override void f ( int a) //redfinition { x +=a; } public virtual void g (char b) //surcharge de g { x +=b*y; } }

1.4 Comment opre le compilateur C# C'est le compilateur C# qui fait tout le travail de recherche de la bonne mthode. Prenons un objet obj de classe Classe1, lorsque le compilateur C# trouve une instruction du genre "obj.method1(paramtres effectifs);", sa dmarche d'analyse est semblable celle du compilateur Delphi, il cherche dans l'ordre suivant : Y-a-t-il dans Classe1, une mthode qui se nomme method1 ayant une signature identique aux paramtres effectifs ? si oui c'est la mthode ayant cette signature qui est appele, si non le compilateur remonte dans la hierarchie des classes mres de Classe1 en posant la mme question rcursivement jusqu' ce qu'il termine sur la classe Object. Si aucune mthode ayant cette signature n'est trouve il signale une erreur.

Soit partir de l'exemple prcdent les instructions suivantes : ClasseFille obj = new ClasseFille( ); obj.g(-3,8);
Premier pas dans .Net avec C#2.0 - ( rv. 28.08.2006 )
- Rm di Scala

page

173

obj.g('h'); Le compilateur C# applique la dmarche d'analyse dcrite, l'instruction "obj.g(-3,8);". Ne trouvant pas dans ClasseFille de mthode ayant la bonne signature (signature = deux entiers) , le compilateur remonte dans la classe mre ClasseMere et trouve une mthode " void g ( int a, int b) " de la classe ClasseMere ayant la bonne signature (signature = deux entiers), il procde alors l'appel de cette mthode sur les paramtres effectifs (-3,8). Dans le cas de l'instruction obj.g('h'); , le compilateur trouve immdiatement dans ClasseFille la mthode " void g (char b) " ayant la bonne signature, c'est donc elle qui est appele sur le paramtre effectif 'h'. Le compilateur consulte les mta-donnes (informations de description) de l'assemblage en cours ( applicationXXX.exe ), plus particulirement les mtadonnes de type qui sont stockes au fur et mesure dans de nombreuses tables. Nous figurons ci-dessous deux tables de dfinition importantes relativement au polymorphisme de mthode MethodDef et TypeDef utilises par le compilateur.

Premier pas dans .Net avec C#2.0 - ( rv. 28.08.2006 )

- Rm di Scala

page

174

Rsum pratique sur le polymorphisme en C#


La surcharge (polymorphisme statique) consiste proposer diffrentes signatures de la mme mthode. La redfinition (polymorphisme dynamique) ne se produit que dans l'hritage d'une classe, par redfinition (liaison dynamique) de la mthode mre avec une mthode fille (ayant ou n'ayant pas la mme signature). Le masquage ne se produit que dans l'hritage d'une classe, par redfinition (liaison statique) de la mthode mre par une mthode fille (ayant la mme signature). Toute mthode est considre liaison statique sauf si vous la dclarez autrement.

2. Accs la super classe en C#


2.1 Le mot clef ' base ' Nous venons de voir que le compilateur s'arrte ds qu'il trouve une mthode ayant la bonne signature dans la hirarchie des classes, il est des cas o nous voudrions accder une mthode de la classe mre alors que celle-ci est redfinie dans la classe fille. C'est un problme analogue l'utilisation du this lors du masquage d'un attribut. classe mre
class

classe fille class ClasseB : ClasseA { public new void meth01 ( ) { attrA = 1000 ; } public void meth02 ( ) { meth01 ( ); } }

ClasseA

{ public int attrA ; private int attrXA ; public void meth01 ( ) { attrA = 57 ; } }

La mthode meth02 ( ) invoque la mthode meth01 ( ) de la classe ClasseB. Il est impossible de faire directement appel la mthode meth01 ( ) de la classe mre ClasseA car celle-ci est masque dans la classe fille.

Premier pas dans .Net avec C#2.0 - ( rv. 28.08.2006 )

- Rm di Scala

page

175

Il existe en C# un mcanisme dclench par un mot clef qui permet d'accder la classe mre (classe immdiatement au dessus): ce mot est base. Le mot clef base est utilis pour accder tous les membres visibles de la classe mre partir d'une classe fille drive directement de cette classe mre ( la super-classe en Java).

Ce mot clef base est trs semblable au mot clef inherited de Delphi qui joue le mme rle sur les mthodes et les proprits (il est en fait plus proche du mot clef super de Java car il ne remonte qu' la classe mre), il permet l'appel d'une mthode de la classe de base qui a t substitue (masque ou redfinie) par une autre mthode dans la classe fille. Exemple :

Remarques : Le fait d'utiliser le mot clef base partir d'une mthode statique constitue une erreur. base est utile pour spcifier un constructeur de classe mre lors de la cration d'instances de la classe fille.

Nous dveloppons ci-dessous l'utilisation du mot clef base afin d'initialiser un constructeur.

Premier pas dans .Net avec C#2.0 - ( rv. 28.08.2006 )

- Rm di Scala

page

176

2.2 Initialiseur de constructeur this et base Semblablement Delphi et Java, tous les constructeurs d'instance C# autorisent l'appel d'un autre constructeur d'instance immdiatement avant le corps du constructeur, cet appel est dnomm l'initialiseur du constructeur, en Delphi cet appel doit tre explicite, en C# et en Java cet appel peut tre implicite. Rappelons que comme en Java o dans toute classe ne contenant aucun constructeur, en C# un constructeur sans paramtres par dfaut est implicitement dfini :

vous crivez votre code comme ceci : class ClasseA { public int attrA ; public string attrStrA ; }

il est complt implicitement ainsi : class ClasseA { public int attrA ; public string attrStrA ; public ClasseA ( ) { } }

Remarque : Lors de l'hritage d'une classe fille, diffremment Delphi et Java, si un constructeur d'instance C# de la classe fille ne fait pas figurer explicitement d'initialiseur de constructeur, c'est qu'en fait un initialiseur de constructeur ayant la forme base( ) lui a t fourni implicitement.

Soit par exemple une classe ClasseA possdant 2 constructeurs :


class ClasseA { public int attrA ; public string attrStrA ; public ClasseA ( ) { /* premier constructeur */ attrA = 57 ; } public ClasseA ( string s ) { /* second constructeur */ attrStrA = s +"...1..." ; } }

Soit par suite une classe fille ClasseB drivant de ClasseA possdant elle aussi 2 constructeurs, les deux dclarations ci-dessous sont quivalentes : Initialiseur implicite class ClasseB : ClasseA { /* premier constructeur */ public ClasseB ( ) { attrStrA = "..." ; Initialiseur explicite quivalent class ClasseB : ClasseA { /* premier constructeur */ public ClasseB ( ) : base( ) { attrStrA = "..." ;
- Rm di Scala

Premier pas dans .Net avec C#2.0 - ( rv. 28.08.2006 )

page

177

} /* second constructeur */ public ClasseB ( string s ) { attrStrA = s ; } }

} /* second constructeur */ public ClasseB ( string s ) : base( ) { attrStrA = s ; } }

Dans les deux cas le corps du constructeur de la classe fille est initialis par un premier appel au constructeur de la classe mre ( ), en l'occurrence << public ClasseA ( ) ... /* premier constructeur */ >> Remarque : De mme pour une classe fille, C# comme Java, tout constructeur de la classe fille appelle implicitement et automatiquement le constructeur par dfaut (celui sans paramtres) de la classe mre.

Exemple : vous crivez votre code comme ceci : class ClasseA { public int attrA ; public string attrStrA ; } class ClasseB : ClasseA { } } Le constructeur de ClasseA sans paramtres est implicitement dclar par le compilateur. class ClasseB : ClasseA { public ClasseB ( ): base( ) { } } il est complt implicitement ainsi : class ClasseA { public int attrA ; public string attrStrA ; public ClasseA ( ) { }

Si la classe mre ne possde pas de constructeur par dfaut, le compilateur engendre un message d'erreur : vous crivez votre code comme ceci : class ClasseA { public int attrA ; public string attrStrA ; public ClasseA ( int a ) { } class ClasseB : ClasseA { public ClasseB ( ) { //..... }
Premier pas dans .Net avec C#2.0 - ( rv. 28.08.2006 )

il est complt implicitement ainsi : class ClasseA { public int attrA ; public string attrStrA ; public ClasseA ( int a ) { } } class ClasseB : ClasseA { public ClasseB ( ): base( ) { // ....
- Rm di Scala

page

178

} }
La classe de base ClasseA ne comporte qu'un seul constructeur explicite un paramtre. Le constructeur sans paramtres n'existe que si vous le dclarez explicitement, ou bien si la classe ne possde pas de constructeur explicitement dclar.

}
L'initialiseur implicite base( ) renvoie le compilateur chercher dans la classe de base un constructeur sans paramtres. Or il n'existe pas dans la classe de base (ClasseA) de constructeur par dfaut sans paramtres. Donc la tentative choue !

Le message d'erreur sur la ligne " public ClasseB ( ) { ", est le suivant : [C# Erreur] Class.cs(54): Aucune surcharge pour la mthode 'ClasseA' ne prend d'arguments '0' Remarques : Donc sans initialiseur explicite, tout objet de classe fille ClasseB est minima et par dfaut, instanci comme un objet de classe de base ClasseA. Lorsque l'on veut invoquer dans un constructeur d'une classe donne un autre constructeur de cette mme classe tant donn que tous les constructeurs ont le mme nom, il faut utiliser le mot clef this comme nom d'appel.

Exemple : Reprenons la mme classe ClasseA possdant 2 constructeurs et la classe ClasseB drivant de ClasseA, nous marquons les actions des constructeurs par une chane indiquant le numro du constructeur invoqu ainsi que sa classe : class ClasseA { public int attrA ; public string attrStrA ; public ClasseA ( ) { /* premier constructeur */ attrA = 57 ; } public ClasseA ( string s ) { /* second constructeur */ attrStrA = s +"...classeA1..." ; } } Ci-dessous la ClasseB crite de deux faons quivalentes : avec initialiseurs implicites-explicites
class ClasseB : ClasseA { /* premier constructeur */ public ClasseB ( ) { Premier pas dans .Net avec C#2.0 - ( rv. 28.08.2006 )

avec initialiseurs explicites quivalents


class ClasseB : ClasseA { /* premier constructeur */ public ClasseB ( ) : base( ) {
- Rm di Scala

page

179

attrA = 100+attrA ; } /* second constructeur */ public ClasseB ( string s ) { attrStrA = attrStrA +s+"...classeB2..." ; } /* troisime constructeur */ public ClasseB ( int x , string ch ) : this( ch ) { attrStrA = attrStrA+"...classeB3..." ; } /* quatrime constructeur */ public ClasseB ( char x , string ch ) : base( ch ) { attrStrA = attrStrA+"...classeB4..." ; } } }

attrA = 100+attrA ; /* second constructeur */ public ClasseB ( string s ): base( ) { attrStrA = attrStrA +s+"...classeB2..." ; } /* troisime constructeur */ public ClasseA ( int x , string ch ) : this( ch ) { attrStrA = attrStrA+"...classeB3..." ; } /* quatrime constructeur */ public ClasseB ( char x , string ch ) : base( ch ) { attrStrA = attrStrA+"...classeB4..." ; } }

Crons quatre objets de ClasseB, chacun avec l'un des 4 constructeurs de la ClasseB : class MaClass { static void Main(string[] args) { int x=68; ClasseB ObjetB= new ClasseB( ); System.Console.WriteLine(ObjetB.attrA); ObjetB= new ClasseB(x,"aaa"); System.Console.WriteLine(ObjetB.attrStrA); ObjetB= new ClasseB((char)x,"bbb"); System.Console.WriteLine(ObjetB.attrStrA); ObjetB= new ClasseB("ccc"); System.Console.WriteLine(ObjetB.attrStrA); System.Console.ReadLine(); } } Voici le rsultat console de l'excution de ce programme :

Explications :
public ClasseB ( ) { attrA = 100+attrA ; } ClasseB ObjetB= new ClasseB( ); System.Console.WriteLine(ObjetB.attrA); C# slectionne la signature du premier constructeur de la ClasseB (le constructeur sans paramtres). C# appelle d'abord implicitement le constructeur sans paramtre de la classe mre ( : base( ) )

Premier pas dans .Net avec C#2.0 - ( rv. 28.08.2006 )

- Rm di Scala

page

180

public ClasseA ( ) { attrA = 57 ; } Le champ attrA vaut 57, puis C# excute le corps du constructeur : attrA = 100+attrA ; attrA vaut 100+57 = 157 public ClasseB ( string s ) { attrStrA = attrStrA +s+"...classeB2..." ; } public ClasseB ( int x , string ch ) : this( ch ) { attrStrA = attrStrA+"...classeB3..." ; } ObjetB= new ClasseB(x,"aaa"); System.Console.WriteLine(ObjetB.attrStrA); C# slectionne la signature du troisime constructeur de la ClasseB (le constructeur avec paramtres : int x , string ch). C# appelle d'abord explicitement le constructeur local de la classeB avec un paramtre de type string ( le second constructeur de la ClasseB ) s = "aaa" ; public ClasseB ( string s ) { attrStrA = attrStrA +s+"...classeB2..." ; } Le champ attrStrA vaut "aaa...classeB2...", puis C# excute le corps du constructeur : attrStrA = attrStrA+"...classeB3..." ; attrStrA vaut "aaa...classeB2......classeB3..." public ClasseB ( char x , string ch ) : base( ch ) { attrStrA = attrStrA+"...classeB4..." ; } ObjetB= new ClasseB((char)x,"bbb"); System.Console.WriteLine(ObjetB.attrStrA); C# slectionne la signature du quatrime constructeur de la ClasseB (le constructeur avec paramtres : char x , string ch). C# appelle d'abord explicitement le constructeur de la classe mre (de base) classeA avec un paramtre de type string ( ici le second constructeur de la ClasseA ) s = "bbb" ; public ClasseA ( string s ) { attrStrA = s +"...classeA1..." ; } Le champ attrStrA vaut "bbb...classeA1..." puis C# excute le corps du constructeur : attrStrA = attrStrA+"...classeB4..." ; attrStrA vaut "bbb...classeA1......classeB4..."

La dernire instanciation : ObjetB= new ClasseB("ccc"); est strictement identique la premire mais avec appel au second constructeur.

2.3 Comparaison de construction C#, Delphi et Java Exemple classe mre : C#


Premier pas dans .Net avec C#2.0 - ( rv. 28.08.2006 )
- Rm di Scala

Java
page

181

class ClasseA { public int attrA ; public string attrStrA ; public ClasseA ( ) { attrA = 57 ; } public ClasseA ( string s ) { attrStrA = s +"...classeA1..." ; } }

class ClasseA { public int attrA ; public String attrStrA = "" ; public ClasseA ( ) { attrA = 57 ; } public ClasseA ( String s ) { attrStrA = s +"...classeA1..." ; } }

C#
class ClasseA { public int attrA ; public string attrStrA ; public ClasseA ( ) { attrA = 57 ; } public ClasseA ( string s ) { attrStrA = s +"...classeA1..." ; } }

Delphi
ClasseA = class public attrA : integer ; attrStrA: string ; constructor Creer;overload; constructor Creer(s:string); overload; end; constructor ClasseA.Creer begin attrA := 57 ; end; constructor ClasseA.Creer(s:string); begin attrStrA := s +'...classeA1...' ; end;

Exemple classe fille : C# class ClasseB : ClasseA { /* premier constructeur */ public ClasseB ( ) { attrA = 100+attrA ; } /* second constructeur */ public ClasseB ( string s ) { attrStrA = attrStrA +s+"...classeB2..." ; } Java class ClasseB extends ClasseA { /* premier constructeur */ public ClasseB ( ) { super( ) ; attrA = 100+attrA ; } /* second constructeur */ public ClasseB ( String s ) { super( ) ; attrStrA = attrStrA +s+"...classeB2..." ; } /* troisime constructeur */ public ClasseB ( int x , String ch ) { this( ch ) ; attrStrA = attrStrA+"...classeB3..." ;
- Rm di Scala

/* troisime constructeur */ public ClasseB ( int x , string ch ) : this( ch ) { attrStrA = attrStrA+"...classeB3..." ; }


Premier pas dans .Net avec C#2.0 - ( rv. 28.08.2006 )

page

182

} /* quatrime constructeur */ public ClasseB ( char x , string ch ) : base( ch ) { attrStrA = attrStrA+"...classeB4..." ; } } /* quatrime constructeur */ public ClasseB ( char x , String ch ) { super( ch ) ; attrStrA = attrStrA+"...classeB4..." ; } }

C#
class ClasseB : ClasseA {

Delphi
ClasseB = class( ClasseA ) public constructor Creer;overload; constructor Creer(s:string); overload; constructor Creer(x:integer;ch:string); overload; constructor Creer(x:char;ch:string); overload; end; /* premier constructeur */ constructor ClasseB.Creer; begin inherited ; attrA := 100+attrA ; end; /* second constructeur */ constructor ClasseB.Creer(s:string); begin inherited Creer ; attrStrA := attrStrA +s+'...classeB2...' ; end; /* troisime constructeur */ constructor ClasseB.Creer(x:integer;ch:string); begin Creer( ch ) ; attrStrA := attrStrA+'...classeB3...' ; end; /* quatrime constructeur */ constructor ClasseB.Creer(x:integer;ch:string); begin inherited Creer( ch ) ; attrStrA := attrStrA+'...classeB4...' ; end;

/* premier constructeur */ public ClasseB ( ) { attrA = 100+attrA ; }

/* second constructeur */ public ClasseB ( string s ) { attrStrA = attrStrA +s+"...classeB2..." ; }

/* troisime constructeur */ public ClasseB ( int x , string ch ) : this( ch ) { attrStrA = attrStrA+"...classeB3..." ; }

/* quatrime constructeur */ public ClasseB ( char x , string ch ) : base( ch ) { attrStrA = attrStrA+"...classeB4..." ; } }

2.4 Traitement d'un exercice complet soit une hirarchie de classe de vhicules :

Premier pas dans .Net avec C#2.0 - ( rv. 28.08.2006 )

- Rm di Scala

page

183

syntaxe de base : class Vehicule { } class Terrestre :Vehicule { } class Marin :Vehicule { } class Voiture : Terrestre { } class Voilier : Marin { } class Croiseur : Marin { }

Supposons que la classe Vhicule contienne 3 mthodes, qu'elle n'implmente pas la mthode Demarrer qui est alors abstraite, qu'elle fournit et implante vide la mthode "RpartirPassagers" de rpartition des passagers bord du vhicule, qu'elle fournit aussi et implante vide une mthode "PriodicitMaintenance" renvoyant la priodicit de la maintenance obligatoire du vhicule.

La classe Vhicule est abstraite : car la mthode Demarrer est abstraite et sert de "modle" aux futures classes drivant de Vhicule. Supposons que l'on implmente le comportement prcis du genre de dmarrage dans les classes Voiture , Voilier et Croiseur .

Dans cette hirarchie, les classes Terrestre et Marin hritent de la classe Vehicule, mais
Premier pas dans .Net avec C#2.0 - ( rv. 28.08.2006 )
- Rm di Scala

page

184

n'implmentent pas la mthode abstraite Dmarrer, ce sont donc par construction des classes abstraites elles aussi. Elles implantent chacune la mthode "RpartirPassagers" (fonction de la forme, du nombre de places, du personnel charg de s'occuper de faire fonctionner le vhicule...) et la mthode "PriodicitMaintenance" (fonction du nombre de km ou miles parcourus, du nombre d'heures d'activits,...) Les classes Voiture , Voilier et Croiseur savent par hritage direct comment rpartir leur ventuels passagers et quand effectuer une maintenance, chacune d'elle implmente son propre comportement de dmarrage.

Quelques implantations en C#
Une implmentation de la classe Voiture avec des mthodes non virtuelles (Version-1) :
abstract class Vehicule { public abstract void Demarrer( ); public void RpartirPassagers( ){} public void PriodicitMaintenance( ){} } La mthode Demarrer de la classe Vehicule est abstraite et donc automatiquement virtuelle ( liaison dynamique). Les mthodes RpartirPassagers et PriodicitMaintenance sont concrtes mais avec un corps vide. Ces deux mthodes sont non virtuelles ( liaison statique) abstract class Terrestre : Vehicule { public new void RpartirPassagers( ){ //...} public new void PriodicitMaintenance( ){ //...} } La classe Terrestre est abstraite car elle n'implmente pas la mthode abstraite Demarrer. Les deux mthodes dclares dans la classe Terrestre masquent chacune la mthode du mme nom de la classe Vehicule (d'o l'utilisation du mot clef new)

class Voiture : Terrestre { public override void Demarrer( ){ //...} }

La classe Voiture est la seule tre instanciable car toutes ses mthodes sont concrtes : Elle hrite des 2 mthodes implmentes de la classe Terrestre et elle implante (redfinition avec override) la mthode abstraite de l'anctre.

La mme implmentation de la classe Voiture avec des mthodes virtuelles (Version-2):


abstract class Vehicule { public abstract void Demarrer( ); public virtual void RpartirPassagers( ){} public virtual void PriodicitMaintenance( ){} } La mthode Demarrer de la classe Vehicule est abstraite et donc automatiquement virtuelle ( liaison dynamique). Les mthodesRpartirPassagers et PriodicitMaintenance sont concrtes mais avec un corps vide. Ces deux mthodes sont maintenant virtuelles ( liaison dynamique) abstract class Terrestre : Vehicule { public override void RpartirPassagers( ){ //...} public override void PriodicitMaintenance( ){ //...} La classe Terrestre est abstraite car elle n'implmente pas la mthode abstraite Demarrer. Les deux mthodes dclares dans la classe Terrestre redfinissent chacune la mthode du mme nom de la
- Rm di Scala

Premier pas dans .Net avec C#2.0 - ( rv. 28.08.2006 )

page

185

classe Vehicule (d'o l'utilisation du mot clef override)

class Voiture : Terrestre { public override void Demarrer( ){ //...} }

La classe Voiture est la seule tre instanciable car toutes ses mthodes sont concrtes : Elle hrite des 2 mthodes implmentes de la classe Terrestre et elle implante (redfinition avec override) la mthode abstraite de l'anctre.

Supposons que les mthodes non virtuelles RpartirPassagers et PriodicitMaintenance sont implantes compltement dans la classe Vehicule, puis reprenons la classe Terrestre en masquant ces deux mthodes : abstract class Vehicule { public abstract void Demarrer( ); public void RpartirPassagers( ){ //....} public void PriodicitMaintenance( ){ //....} } abstract class Terrestre : Vehicule { public new void RpartirPassagers( ){ //...} public new void PriodicitMaintenance( ){ //...} } Question Nous voulons qu'un vhicule Terrestre rpartisse ses passagers ainsi : 1) d'abord comme tous les objets de classe Vehicule, 2) ensuite qu'il rajoute un comportement qui lui est propre Rponse La mthode RpartirPassagers est non virtuelle, elle masque la mthode mre du mme nom, si nous voulons accder au comportement de base d'un vhicule, il nous faut utiliser le mot clef base permettant d'accder aux membres de la classe mre : abstract class Terrestre : Vehicule { public new void RpartirPassagers( ){ base.RpartirPassagers( ); //... 1; comportement du parent //... 2 comportement propre } public new void PriodicitMaintenance( ){ //...} }

Il est conseill au lecteur de reprendre le mme schma et d'implanter l'identique les autres classe de la hirarchie pour la branche des vhicules Marin.

Premier pas dans .Net avec C#2.0 - ( rv. 28.08.2006 )

- Rm di Scala

page

186

2.5 Destructeur Finaliseur Les classes de C# peuvent possder un finaliseur (syntaxe semblable au destructeur de C++) mais comme la gestion de la mmoire est automatiquement assure par le CLR, l'usage d'un finaliseur n'est pas le mme que celui qui en est fait avec Delphi ou C++ dans lesquels la rcupration de la mmoire est la charge du dveloppeur. Les structs ne peuvent pas possder de finaliseur (ou destructeur) Proprits et usage des finaliseurs avec C# : Une classe C# ne peut possder qu'un seul finaliseur. Un finaliseur ne peut pas tre redfini ni surcharg. Un finaliseur n'a pas de paramtre. Un destructeur est appel automatiquement par le CLR lors de la destruction effective de l'objet par le CLR, il ne peut pas tre appel directement.

De ces proprits il dcoule qu'un destructeur peut tre utilise par le dveloppeur pour librer certaines ressources non gres par le CLR (fermeture de fichier, fermeture de connexion,...) lorsque le programme ne les utilise plus. Exemple :
class classeB : classeA { // constructeur : public classeB( ) { .. } // destructeur : ~classeB( ) { Console.WriteLine("finalizeur de la classeB "); } }

A chaque fois qu'un objet de classeA est dtruit par le CLR, le message "finalizeur de la classeB"
apparatra sur la console. Il est entendu qu'il ne faut pas se servir de la programmation des destructeurs (finaliseurs) pour grer un programme d'une manire synchrone puisque le programme n'a aucun contrle sur la faon dont les objets sont librs par le CLR qui utilise son propre algorithme de garbage collection et de restitution de mmoire. Enfin, un destructeur appelle automatiquement le destructeur de la classe mre et ainsi de suite rcursivement jusqu' la classe object. Dans l'exemple prcdent, le destructeur de la classeB, aprs criture du message "finalizeur de la classeB" , appelle le destructeur de la classeA.

Premier pas dans .Net avec C#2.0 - ( rv. 28.08.2006 )

- Rm di Scala

page

187

Polymorphisme et interfaces en

Plan gnral:

Rappels sur la notion d'interface 1.Concepts et vocabulaire d'interface en C# les interfaces peuvent constituer des hirarchies et hriter entre elles la construction d'un objet necessite une classe implmentant l'interface les implmentations d'un membre d'interface sont en gnral public les implmentations explicites d'un membre d'interface sont spciales 1.1 Spcification d'un exemple complet 1.1.A Une classe abstraite 1.1.B Une interface 1.1.C Une simulation d'hritage multiple 1.1.D Encore une classe abstraite, mais plus concrte 1.1.E Une classe concrte 1.2 Implantation en C# de l'exemple 1.2.A La classe abstraite 1.2.B L'interface 1.2.C La simulation d'hritage multiple 1.2.D La nouvelle classe abstraite 1.2.E La classe concrte 2. Analyse du code de liaison de la solution prcdente 2.1 Le code de la classe Vehicule 2.2 Le code de l'interface IVehicule 2.3 Le code de la classe UnVehicule 2.4 Le code de la classe Terrestre 2.5 Le code de la classe Voiture 3. Cohrence de C# entre les notions de classe et d'interface une classe peut implmenter plusieures interfaces les interfaces et les classes respectent les mmes rgles de polymorphisme les conflits de noms dans les interfaces

Premier pas dans .Net avec C#2.0 - ( rv. 28.08.2006 )

- Rm di Scala

page

188

Rappels essentiels sur la notion d'interface


Les interfaces ressemblent aux classes abstraites : elles contiennent des membres spcifiant certains comportements sans les implmenter. Les classes abstraites et les interfaces se diffrencient principalement par le fait qu'une classe peut implmenter un nombre quelconque d'interfaces, alors qu'une classe abstraite ne peut hriter que d'une seule classe abstraite ou non. Une interface peut servir reprsenter des comportements d'hritage multiple.

Quelques conseils gnraux prodigus par des dveloppeurs professionnels (microsoft, Borland, Sun) :
Les interfaces bien conues sont plutt petites et indpendantes les unes des autres. Un trop grand nombre de fonctions rend l'interface peu maniable. Si une modification s'avre ncessaire, une nouvelle interface doit tre cre. Si la fonctionnalit que vous crez peut tre utile de nombreux objets diffrents, faites appel une interface. Si vous crez des fonctionnalits sous la forme de petits morceaux concis, faites appel aux interfaces. L'utilisation d'interfaces permet d'envisager une conception qui spare la manire d'utiliser une classe de la manire dont elle est implmente. Deux classes peuvent partager la mme interface sans descendre ncessairement de la mme classe de base.

1. Vocabulaire et concepts en C#

Une interface C# est un contrat, elle peut contenir des proprits, des mthodes , des vnements ou des indexeurs, mais ne doit contenir aucun champ ou attribut. Une interface ne peut pas contenir des mthodes dj implmentes. Une interface ne contient que des signatures (proprits, mthodes ). Tous les membres d'une interface sont automatiquement public. Une interface est hritable. On peut construire une hirarchie d'interfaces. Pour pouvoir construire un objet partir d'une interface, il faut dfinir une classe non abstraite implmentant tous les membres de l'interface.

Les interfaces peuvent constituer des hirarchies et hriter entre elles soient l'interface IinterfA et l'interface IinterfB hritant de IinterfA . On pourra employer aussi le vocable d'tendre au sens o l'interface drive IinterfB "tend" le contrat (augmente le nombre de membres contractuels) de l'interface IinterfA . Dans tous les cas il faut une classe pour implmenter ces contrats :

Premier pas dans .Net avec C#2.0 - ( rv. 28.08.2006 )

- Rm di Scala

page

189

code C# : interface IinterfA { event TypEvent1 OnTruc ; // vnement char Prop1 // proprit { get ; set ; } void meth1 ( ); // mthode int meth2 ( ); // mthode char meth3 ( int x ); // mthode } interface IinterfB : IinterfA { event TypEvent2 OnChose ; // vnement string Prop2 // proprit { get ; } void meth4 ( ); // mthode }

La construction d'un objet ncessite une classe implmentant l'interface La classe ClasseY doit implmenter tous les 8 membres provenant de l'hritage des interfaces : les 2 vnements OnTruc et Onchose, les 2 proprits Prop1 et Prop2, et enfin les 4 mthodes meth1, ... , meth4 . La classe ClasseY est une classe concrte (instanciable), un objet de cette classe possde en particulier tous les membres de l'interface IinterfB (et donc IinterfA car IinterfB hrite de IinterfA)
class ClasseY : ClasseX , IinterfB { // ... implmente tous les membres de InterfB } // construction et utilisation d'un objet : ClasseY Obj = new ClasseY( ) ; Obj.Prop1 = 'a' ; // proprit hrite de InterfA string s = Obj.prop2 ; // proprit hrite de InterfB Obj.meth1( ) ; // mthode hrite de InterfA etc ...

Si, ClasseY n'implmente par exemple que 7 membres sur les 8 alors C# considre que c'est une classe abstraite et vous devez la dclarer abstract :
abstract class ClasseY : ClasseX , IinterfB { // ...n' implmente que certains membres de InterfB } class ClasseZ : ClasseY { // ... implmente le reste des membres de InterfB } // ... construction d'un objet : ClasseZ Obj = new ClasseZ( ) ; Premier pas dans .Net avec C#2.0 - ( rv. 28.08.2006 )
- Rm di Scala

page

190

Obj.Prop1 = 'a' ; // proprit hrite de InterfA string s = Obj.prop2 ; // proprit hrite de InterfB Obj.meth1( ) ; // mthode hrite de InterfA etc ...

Les implmentations des membres d'une interface sont en gnral public Par dfaut sans dclaration explicite, les membres (indexeurs, proprits, vnements, mthodes) d'une interface ne ncessitent pas de qualificateurs de visibilit car ils sont automatiquement dclars par C# comme tant de visibilit public, contrairement une classe ou par dfaut les membres sont du niveau assembly. Ce qui signifie que toute classe qui implmente un membre de l'interface doit obligatoirement le qualifier de public sous peine d'avoir un message d'erreur du compilateur, dans cette ventualit le membre devient un membre d'instance. Comme la signature de la mthode n'est qu'un contrat, le mode de liaison du membre n'est pas fix; la classe qui implmente le membre peut alors choisir de l'implmenter soit en liaison statique, soit en liaison dynamique. Soient une interface IinterfA et une classe ClasseX hritant directement de la classe Object et implmentant cette interface, ci-dessous les deux seules implmentations possibles d'une mthode avec un rappel sur les redfinitions possibles dans des classes descendantes :
interface IinterfA { void meth1 ( ); // mthode de l'interface }

Implmentation en liaison prcoce


class ClasseX : IinterfA { public void meth1 ( ){ ... } }

Implmentation en liaison tardive


class ClasseX : IinterfA { public virtual void meth1 ( ){ ... } }

meth1 devient une mthode d'instance

Redfinitions possibles
class ClasseY : ClasseX { public new void meth1 ( ){ ... } masque statiquement celle de ClasseX } class ClasseZ : ClasseX { public new virtual void meth1 ( ){ ... } masque dynamiquement celle de ClasseX }

Redfinitions possibles
class ClasseY : ClasseX { public new void meth1 ( ){ ... } masque statiquement celle de ClasseX } class ClasseZ : ClasseX { public new virtual void meth1 ( ){ ... } masque dynamiquement celle de ClasseX } class ClasseT : ClasseX { public override void meth1 ( ){ ... } redfinit dynamiquement celle de ClasseX }

Les implmentations explicites des membres d'une interface sont spciales Une classe qui implmente une interface peut aussi implmenter de faon explicite un membre de cette interface. Lorsqu'un membre est implment de faon explicite (le nom du membre
Premier pas dans .Net avec C#2.0 - ( rv. 28.08.2006 )
- Rm di Scala

page

191

est prfix par le nom de l'interface : InterfaceXxx.NomDuMembre ), il n'est pas accessible via une rfrence de classe, il est alors invisible tout objet instanci partir de la classe o il est dfini. Un membre implment de faon explicite n'est donc pas un membre d'instance. Pour utiliser un membre d'interface implment de manire explicite, il faut utiliser une rfrence sur cette interface et non une rfrence de classe; il devient visible uniquement travers une rfrence sur l'interface. Nous reprenons le mme tableau de diffrentes implmentations de la mthode void meth1 ( ) en ajoutant une nouvelle mthode void meth2 ( int x ) que nous implmentons explicitement dans les classes drives : interface IinterfA { void meth2 ( int x ); // mthode de l'interface void meth1 ( ); // mthode de l'interface } Nous implmentons l'interface IinterfA dans la classe ClasseX : 1) nous implmentons explicitement void meth2 ( int x ), 2) nous implmentons void meth1 ( ) en mthode virtuelle.

interface IinterfA { void meth2 ( int x ); // mthode de l'interface void meth1 ( ); // mthode de l'interface } class ClasseX : IinterfA { void IinterfA.meth2 ( int x ){ ... } public virtual void meth1 ( ){ ... } } Comprenons bien que la classe ClasseX ne possde pas cet instant une mthode d'instance qui se nommerait meth2, par exemple si dans la mthode virtuelle meth1 nous utilisons le paramtre implicite this qui est une rfrence la future instance, l'audit de code de C#Builder nous renvoie 7 mthodes comme visibles (6 provenant de la classe mre Object et une seule provenant de ClasseX), la mthode IinterfA.meth2 n'est pas visible :

Premier pas dans .Net avec C#2.0 - ( rv. 28.08.2006 )

- Rm di Scala

page

192

Lorsque l'on instancie effectivement un objet de classe ClasseX, cet objet ne voit comme mthode provenant de ClasseX que la mthode meth1 :

La mthode meth2 implmente explicitement en IinterfA.meth2 devient visible uniquement si l'on utilise une rfrence sur l'interface IinterfA, l'exemple ci-dessous montre qu'alors les deux mthodes meth1 et meth2 sont visibles :

Premier pas dans .Net avec C#2.0 - ( rv. 28.08.2006 )

- Rm di Scala

page

193

L'audit de code de Visual C# fournit plus de prcision directement :

Nous voyons bien que la mthode est qualifie avec sa signature dans IinterfA, voyons dans l'exemple ci-dessous que nous pouvons dclarer une mthode d'instance ayant la mme signature que la mthode explicite, voir mme de surcharger cette mthode d'instance sans que le compilateur C# n'y voit de conflit car la mthode explicite n'est pas rang dans la table des mthodes d'instances de la classe : class ClasseX : IinterfA { void IinterfA.meth2 ( int x ){ ... } //mthode de IinterfA implmente explicitement public virtual void meth2 ( int x ){ ... } //mthode de la ClasseX surcharge public virtual void meth2 ( char c ){ ... } //mthode de la ClasseX surcharge public virtual void meth1 ( ){ ... } //mthode de IinterfA implmente virtuellement }

Premier pas dans .Net avec C#2.0 - ( rv. 28.08.2006 )

- Rm di Scala

page

194

La rfrence Obj1 peut appeller les deux surcharges de la mthode d'instance meth2 de la classe ClasseX :

La rfrence Obj2 sur IinterfA fonctionne comme nous l'avons montr plus haut, elle ne peut voir de la mthode meth2 que son implmentation explicite :

Cette fonctionnalit d'implmentation explicite spcifique C# peut tre utilise dans au moins deux cas utiles au dveloppeur : Lorsque vous voulez qu'un membre (une mthode par exemple) implment d'une interface soit priv dans une classe pour toutes les instances de classes qui en driveront, l'implmentation explicite vous permet de rendre ce membre (cette mthode) inaccessible tout objet. Lors d'un conflit de noms si deux interfaces possdent un membre ayant la mme signature et que votre classe implmente les deux interfaces.

1.1 Spcification d'un exemple complet Utilisons la notion d'interface pour fournir un polymorphisme une hirarchie de classe de vhicules fonde sur une interface. : Soit au dpart une classe abstraite Vehicule et une interface IVehicule.

1.1.A) Une classe abstraite La classe abstraite Vehicule contient trois mthodes :
Premier pas dans .Net avec C#2.0 - ( rv. 28.08.2006 )
- Rm di Scala

page

195

La mthode Dmarrer qui est abstraite. La mthode RpartirPassagers de rpartition des passagers bord du vhicule, implante avec un corps vide. La mthode PriodicitMaintenance renvoyant la priodicit de la maintenance obligatoire du vhicule, implante avec un corps vide.

1.1.B) Une interface Afin d'utiliser les possibilits de C#, l'interface IVehicule propose un contrat d'implmentation pour un vnement, un indexeur, une proprit et une mthode :

L'vnement OnStart est de type dlgu (on construit un type dlgu Starting( ) spcifique pour lui) et se dclenchera au dmarrage du futur vhicule, L'indexeur this [int ] est de type string et permettra d'accder une liste indice d'informations sur le futur vhicule, La proprit TypeEngin est en lecture et criture et concerne le type du futur vhicule dans la marque, La mthode Stopper( ) indique comment le futur vhicule s'immobilisera.

1.1.C) Une simulation d'hritage multiple Nous souhaitons construire une classe abstraite UnVehicule qui "hrite" la fois des fonctionnalits de la classe Vehicule et de celles de l'interface IVehicule. Il nous suffit en C# de faire hriter la classe UnVehicule de la classe Vehicule , puis que la classe UnVehicule implmente les propositions de contrat de l'interface IVehicule :

Premier pas dans .Net avec C#2.0 - ( rv. 28.08.2006 )

- Rm di Scala

page

196

1.1.D) Encore une classe abstraite, mais plus "concrte" Nous voulons maintenant proposer une spcialisation du vhicule en crant une classe abstraite Terrestre , base des futurs vhicules terrestres. Cette classe implantera de faon explicite la mthode RpartirPassagers de rpartition des passagers et la mthode PriodicitMaintenance renvoyant la priodicit de la maintenance. Cette classe Terrestre reste abstraite car elle ne fournit pas l'implmentation de la mthode Demarrer :

1.1.E) Une classe concrte Nous finissons notre hirarchie par une classe Voiture qui descend de la classe Terrestre , qui implante la mthode Demarrer( ) et qui redfinie la mthode Stopper( ) :

Premier pas dans .Net avec C#2.0 - ( rv. 28.08.2006 )

- Rm di Scala

page

197

Ce qui nous donne le schma d'hritage total suivant :

1.2 Implantation en C# de l'exemple Nous proposons ci-dessous pour chaque classe ou interface une implmenation en C#. 1.2.A) La classe abstraite Vehicule

Premier pas dans .Net avec C#2.0 - ( rv. 28.08.2006 )

- Rm di Scala

page

198

abstract class Vehicule // classe abstraite mre { public abstract void Demarrer ( ); // mthode abstraite public void RpartirPassagers ( ) { } // implantation de mthode avec corps vide public void PriodicitMaintenance ( ) { } // implantation de mthode avec corps vide }

1.2.B) L'interface IVehicule

public delegate void Starting ( );

// declaration de type dlgu

interface IVehicule { event Starting OnStart ; // dclaration d'vnement du type dlgu : Starting string this [ int index] // dclaration d'indexeur { get ; set ; } string TypeEngin // dclaration de proprit { get ; set ; } void Stopper ( ); // dclaration de mthode }

1.2.C) La classe UnVehicule

Premier pas dans .Net avec C#2.0 - ( rv. 28.08.2006 )

- Rm di Scala

page

199

abstract class UnVehicule : Vehicule , IVehicule // hrite de la classe mre et implmente l'interface { private string nom = ""; private string [ ] ArrayInfos = new string [10] ; public event Starting OnStart ; protected void LancerEvent () { if( OnStart != null) OnStart (); } public string this [ int index] // implantation Indexeur { get { return ArrayInfos[index] ; } set { ArrayInfos[index] = value ; } } public string TypeEngin // implantation proprit { get { return nom ; } set { nom = value ; } } public virtual void Stopper ( ) { } // implantation de mthode avec corps vide }

1.2.D) La classe Terrestre

Premier pas dans .Net avec C#2.0 - ( rv. 28.08.2006 )

- Rm di Scala

page

200

abstract class Terrestre : UnVehicule { public new void RpartirPassagers ( ) { //...implantation de mthode } public new void PriodicitMaintenance ( ) { //...implantation de mthode } }

1.2.E) La classe Voiture

class Voiture : Terrestre { public override void Demarrer ( ) { LancerEvent ( ); } public override void Stopper ( ) { //... } }

Premier pas dans .Net avec C#2.0 - ( rv. 28.08.2006 )

- Rm di Scala

page

201

2. Analyse du code de liaison de la solution prcdente

Nous nous interessons au mode de liaison des membres du genre : mthodes, proprits, indexeurs et vnements. Rappelons au lecteur que la liaison statique indique que le compilateur lie le code lors de la compilation, alors que dans le cas d'une liaison dynamique le code n'est choisi et li que lors de l'excution. 2.1 Le code de la classe Vehicule

abstract class Vehicule { public abstract void Demarrer ( ); // mthode abstraite public void RpartirPassagers ( ) { } // implantation de mthode avec corps vide public void PriodicitMaintenance ( ) { } // implantation de mthode avec corps vide }

Analyse : Sans qualification particulire une mthode est liaison statique : La mthode public void RpartirPassagers ( ) est donc laison statique. La mthode public void PriodicitMaintenance ( ) est donc laison statique. Une mthode qualifie abstract est implicitement virtuelle : La mthode public abstract void Demarrer ( ) est donc laison dynamique.

2.2 Le code de l'interface IVehicule

interface IVehicule { event Starting OnStart ; // dclaration d'vnement du type dlgu : Starting string this [ int index] // dclaration d'indexeur { get ; set ; } string TypeEngin // dclaration de proprit { get ; set ; } void Stopper ( ); // dclaration de mthode }

Premier pas dans .Net avec C#2.0 - ( rv. 28.08.2006 )

- Rm di Scala

page

202

Analyse : Une interface n'est qu'un contrat, les membres dclars comme signatures dans l'interface n'tant pas implmentes, la question de leur liaison ne se pose pas au niveau de l'interface, mais lors de l'implmentation dans une classe ultrieure : La mthode void Stopper ( ); pourra donc tre plus tard soit statique, so