Académique Documents
Professionnel Documents
Culture Documents
Csharp Part 1
Csharp Part 1
Rm di Scala
Mise jour doctobre 2007
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
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.293 P.317 P.338 P.366 P.405 P.429 P.433
Principes des bases de donnes relationnelles .. ADO.Net et donnes relationnelles .... ADO.Net et SQL serveur 2005 ....
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
Programmer objet .Net avec C#- ( rv.17.10.2007 )
- 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,...). La dernire version de .NET Framework est directement intgre Windows Vista. 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. Les versions gratuites soit "Express" de Microsoft, soit l'environnement complet enversion professionnelle de Visual Studio .NET, soit sharpDevelop de l'open source dmocratisent les outils de programmation.
- 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
- Rm di Scala
page
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. 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 :
- Rm di Scala
page
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" se dclare comme "System.Data.Dataset". La classe Console qui se trouve dans l'espace de noms "System" se dclare comme "System.Console". La classe DataAdapter qui se trouve dans l'espace de noms "System.Data.Common " se dclare comme "System.Data. Common.DataAdapter ".
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 : 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.
Programmer objet .Net avec C#- ( rv.17.10.2007 )
- Rm di Scala
page
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.
- Rm di Scala
page
Programe source C# : xxx.cs Programe excutable sous Windows : xxx.exe (code natif processeur)
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
- Rm di Scala
page
Programe source C# : Exemple.cs Programe excutable sous .NET : Exemple.exe (code portable IL)
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,...)
- Rm di Scala
page
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 :
- 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.
- Rm di Scala
page
11
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).
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 :
- 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
- 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
Programmer objet .Net avec C#- ( rv.17.10.2007 )
- 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
Programmer objet .Net avec C#- ( rv.17.10.2007 )
- 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
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.
Programmer objet .Net avec C#- ( rv.17.10.2007 )
- 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.
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).
Programmer objet .Net avec C#- ( rv.17.10.2007 )
- 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 )
- 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).
- Rm di Scala
page
21
8 9 10 11 12 13
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.
- 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 = x / 2f ;
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++.
- 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
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
- 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
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.
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
- 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...
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 ")
Programmer objet .Net avec C#- ( rv.17.10.2007 )
- Rm di Scala
page
26
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
Programmer objet .Net avec C#- ( rv.17.10.2007 )
- 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.
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) .
Programmer objet .Net avec C#- ( rv.17.10.2007 )
- Rm di Scala
page
28
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
- 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
~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
~ 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
~ 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).
- Rm di Scala
page
30
- Rm di Scala
page
31
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
- Rm di Scala
page
32
- Rm di Scala
page
33
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
- 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
Ici, nous expliquons les instructions C# en les comparant pascal-delphi. Voici la syntaxe d'une instruction en C#:
instruction :
instruction complte :
- 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; } } }
2 - l'affectation
C# est un langage de la famille des langages hybrides, il possde la notion d'instruction d'affectation.
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
- Rm di Scala
page
36
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;
i += 5 ;
page
37
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) ]
Au total, 12 instructions MSIL dont deux appels : call instance int32 exemple.WinForm::f(int32)
- Rm di Scala
page
38
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 :
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.
- Rm di Scala
page
39
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;
- 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):
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 :
- 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;
Instruction C#
Oprateur conditionnel :
c = a == 0 ? b : a+1 ;
une seule opration de stockage pour c : IL_0010: stloc.2
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)
- 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.
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
- 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
- Rm di Scala
page
44
Il est toujours possible d'utiliser des instructions if else imbriques pour reprsenter un switch avec break :
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.
- Rm di Scala
page
45
Sa smantique peut aussi tre explique l'aide d'une autre instruction C#, le while( ) :
Programmer objet .Net avec C#- ( rv.17.10.2007 )
- Rm di Scala
page
46
Exemple de boucle do...while Pascal-Delphi repeat InstrA ; InstrB ; ... until not Expr do { InstrA ; InstrB ; ... } while ( Expr ) C#
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.
- 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 }
(*)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 ; ... }
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
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.
- Rm di Scala
page
49
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)... ).
Programmer objet .Net avec C#- ( rv.17.10.2007 )
- 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).
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
page
51
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 }
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; }
- 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; }
- Rm di Scala
page
53
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)
- 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 !"); } }
- 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.
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.
- Rm di Scala
page
56
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 !
Syntaxe :
corps de fonction :
- 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
page
58
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.
- 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.
- Rm di Scala
page
60
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)
- 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.
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; }
- 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.
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) ;
En C# pour indiquer un passage par rsultat on prcde la dclaration du paramtre formel du mot
Programmer objet .Net avec C#- ( rv.17.10.2007 )
- 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.
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.
- Rm di Scala
page
64
params
Le mot clef params permet de spcifier que le nombre des paramtres formels dune mthode est variable. Ces paramtres sont obligatoirement dclars dans un tableau.
Contrainte et utilisation Le mot clef params doit tre le dernier de la liste des paramtres formels (aucune dclaration de paramtre aprs params nest recevable). Un seul mot clef params est autoris dans la liste des paramtres formels. Il peut y avoir des paramtres formels en nombre fixe, en sus des paramtres en nombre variable. Le passage ne peut seffectuer que par valeur (pas de ref, pas de out) La liste des paramtres effectifs passs par params peut tre vide.
class Exercice { public static void paramsVariable( int nbr, params int[ ] listeParam ) { Console.WriteLine("Premier paramtre : "+nbr); for (int i = 0 ; i < listeParam.Length; i++) Console.WriteLine("Paramtre suivant : "+listeParam[i]); } public static void Main( string[ ] x) { paramsVariable( 12 ); paramsVariable( 89, 2, 5, 9 ,8 ); Console.ReadLine(); } } Rsultats de lexcution :
Exemple :
- Rm di Scala
page
65
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.
- Rm di Scala
page
66
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.
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.
- Rm di Scala
page
67
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.
page
68
} 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) .
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
page
69
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 visibilit 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).
- Rm di Scala
page
70
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; } }
accept
- Rm di Scala
page
71
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; } }
- Rm di Scala
page
72
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.
- Rm di Scala
page
73
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.
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 !!
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";
- Rm di Scala
page
74
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)
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).
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.
- Rm di Scala
page
75
tCarac = "abcdefghijk".ToCharArray( );
- Rm di Scala
page
76
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");
- Rm di Scala
page
77
Aprs excution on obtient : s1=abcdef s2=abcdef s3=abcdef ch=abcdef s2=ch s2=s3 s3=ch s3 gal ch
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");
- Rm di Scala
page
78
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 :
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 ;
- Rm di Scala
page
79
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
Le compilateur enverra le message d'erreur suivant pour l'instruction l'instruction s1 = 'd' + 'e'; :
s1 = 'd' + c ; et pour
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.
- Rm di Scala
page
80
Tableaux et matrices
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
- Rm di Scala
81
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...)
- Rm di Scala
page
82
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];
- Rm di Scala
page
83
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};
- Rm di Scala
page
84
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}
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)
page
85
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];
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
table2 = table1;
- Rm di Scala
page
86
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 ;
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.
- Rm di Scala
page
87
- Rm di Scala
page
88
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
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.
- Rm di Scala
page
89
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 :
instancie un tableau d'int unidimensionnel 7 cases et renvoie sa rfrence qui est range dans la cellule de rang 0 du sous-tableau.
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.
- Rm di Scala
page
90
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 [ ] [ ].
- Rm di Scala
page
91
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.
page
92
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 :
- Rm di Scala
page
93
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( );
Dclaration avec dfinition explicite ncessaire de la taille, afin de pouvoir lancer la copie.
Dclaration de rfrence, puis clonage et transtypage. Dclaration avec dfinition explicite ncessaire de la taille, afin de pouvoir lancer la copie.
- Rm di Scala
page
94
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.
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.
- Rm di Scala
page
95
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 !).
- Rm di Scala
page
96
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.
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.
- Rm di Scala
page
97
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)
- Rm di Scala
page
98
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.
[ ]
- Rm di Scala
page
99
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
- Rm di Scala
page
100
Classe ArrayList
- Rm di Scala
page
101
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.
- Rm di Scala
page
102
Table de hachage
On dcide de dcouper l'ensemble des lments en N sous-ensembles (on fixe par exemple N=1000) et de dfinir une structure de donne statique de type table dont chaque cellule accde un sous-ensemble de la collection, cette table se dnomme table de hachage.
On construit une fonction de hachage que nous nommons Hashcode et qui chaque clef d'un lment de
Programmer objet .Net avec C#- ( rv.17.10.2007 )
- Rm di Scala
page
103
EnsElement fournit un entier compris dans l'intervalle [0 , N-1] : Hascode : EnsCl [0 , N-1]
Pour toute clef k EnsCl , l'entier Hashcode( k ) est appel la valeur de hachage ou code de hachage primaire de cette clef. Ce qui donne un TAD Dictionnaire enrichi : TAD Dictionnaire Utilise : EnsCl, EnsElement Champs : (a1,.....,an) suite finie dans EnsElement Oprations : Clef : EnsElement EnsCl Hashcode : EnsCl [0 , N-1] Ajouter : EnsElement x Collection Collection Supprimer : EnsCl x Collection Collection Rechercher : EnsCl x Collection EnsElement Fin TAD Dictionnaire
Collision primaire
La fonction Hashcode ( Clef ( element ) ) n'est pas bijective et mme pas injective, puisque plusieurs lments distincts peuvent avoir le mme code de hachage primaire. On dit qu'il y a collision entre deux lments elt_1 et elt_2 lorsqu'ils sont dans le mme compartiment (ils appartiennent au mme sous-ensemble) : Hashcode ( Clef ( elt_1 ) ) = Hashcode ( Clef ( elt_2 ) ) et Clef ( elt_1 ) Clef ( elt_2 ) Ce qui veut dire que la clef d'un lment est insuffisante pour accder aux informations sur l'lment, car la collision fait que plusieurs lments sont situs dans la mme zone associ au hashcode de cette clef, il est ncessaire de rsoudre le problme pos par cette collision.
- Rm di Scala
104
Caque cellule de la table de hachage contient uniquement le pointeur sur la tte de liste :
Les algorithmes ajouter, supprimer et rechercher un lment x, sont semblables ceux qui permettent de manipuler les listes chanes et sont proportionnels au pire, au nombre d'lments en collision. Ils sont prcds par un accs fonctionnel direct dans la table l'aide de Hashcode ( Clef ( x ) ).
Ajouter
Soit insrer l'lment x entre l'lment t et l'lment u dans le compartiment de collision contenant les lments a, t, u, b et q.
Etape n1 :
Etape n2 :
- Rm di Scala
page
105
Supprimer
Soit supprimer l'lment x entre l'lment t et l'lment u dans le compartiment de collision contenant les lments a, t, u, b et q.
Etape n1 :
Etape n2 :
Rechercher
Soit rechercher l'lment b dans le compartiment de collision contenant les lments a, t, u, b et q, par une recherche linaire classique.
- Rm di Scala
page
106
Reprsente une collection de paires cl/valeur qui sont organises en fonction du code de hachage de la cl, cette classe assure automatiquement la gestion des collisions.
- Rm di Scala
page
107
Informations Microsoft : Un objet Hashtable se compose de compartiments contenant les lments de la collection. Un compartiment est un sous-groupe virtuel d'lments dans le type Hashtable, ce qui rend les oprations de recherche et d'extraction plus simples et plus rapides que dans la plupart des collections. Chaque compartiment est associ un code de hachage, gnr l'aide d'une fonction de hachage et reposant sur la cl de l'lment. Chaque lment est une paire cl/valeur stocke dans un objet DictionaryEntry. Une cl ne peut pas tre rfrence Null (Nothing en Visual Basic), contrairement une valeur.
public struct DictionaryEntry Dfinit une paire cl/valeur du dictionnaire qui peut tre dfinie ou rcupre.
- Rm di Scala
page
108
Exercice :
Construire une classe ClasseHash drivant de Hashtable permettant de stocker des informations sur des objets de type Etudiant :
- Rm di Scala
page
109
Cette classe n'est pas utile pour la gestion d'une liste chane classique non range cause de son tri automatique selon les clefs.
- Rm di Scala
page
110
En revanche, si l'on stocke comme clef la valeur de Hashcode de l'lment, la recherche est amliore. La classe SortedList :
- Rm di Scala
page
111
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 ); Programmer objet .Net avec C#- ( rv.17.10.2007 ) 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
page
112
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)
[ ]
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 ); Programmer objet .Net avec C#- ( rv.17.10.2007 )
- Rm di Scala
page
113
//----> 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 ] );
Les trois boucles affichent dans l'ordre : Luc Jos Murielle Jean Claudie
- Rm di Scala
page
114
La 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).
- Rm di Scala
page
115
La 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 :
- Rm di Scala
page
116
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.
- Rm di Scala
page
117
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
- Rm di Scala
page
118
- Rm di Scala
page
119
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.
- Rm di Scala
page
120
classe DictionaryBase
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 { }
- Rm di Scala
page
121
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;
Programmer objet .Net avec C#- ( rv.17.10.2007 )
- Rm di Scala
page
122
} } // 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 :
- Rm di Scala
page
123