c# tutorial

457
APPRENTISSAGE DU LANGAGE C# 2008 et du Framework .NET 3.5 Serge Tahé - ISTIA - Université d'Angers Mai 2008 1

Upload: dahevos

Post on 04-Dec-2014

378 views

Category:

Documents


8 download

DESCRIPTION

Cours sur le C# par un membre de developez.com

TRANSCRIPT

APPRENTISSAGE DU LANGAGE C# 2008 et du Framework .NET 3.5Serge Tah - ISTIA - Universit d'Angers Mai 2008

1

IntroductionC# est un langage rcent. Il a t disponible en versions beta successives depuis lanne 2000 avant dtre officiellement disponible en fvrier 2002 en mme temps que la plate-forme .NET 1.0 de Microsoft laquelle il est li. C# ne peut fonctionner quavec cet environnement dexcution. Celui-ci rend disponible aux programmes qui sexcutent en son sein un ensemble trs important de classes. En premire approximation, on peut dire que la plate-forme .NET est un environnement dexcution analogue une machine virtuelle Java. On peut noter cependant deux diffrences : Java s'excute sur diffrents OS (windows, unix, macintosh) depuis ses dbuts. En 2002, la plate-forme .NET ne s'excutait que sur les machines Windows. Depuis quelques annes le projet Mono [http://www.mono-project.com] permet d'utiliser la plate-forme .NET sur des OS tels que Unix et Linux. La version actuelle de Mono (fvrier 2008) supporte .NET 1.1 et des lments de .NET 2.0. la plate-forme .NET permet l'excution de programmes crits en diffrents langages. Il suffit que le compilateur de ceux-ci sache produire du code IL (Intermediate Language), code excut par la machine virtuelle .NET. Toutes les classes de .NET sont disponibles aux langages compatibles .NET ce qui tend gommer les diffrences entre langages dans la mesure o les programmes utilisent largement ces classes. Le choix d'un langage .NET devient affaire de got plus que de performances.

En 2002, C# utilisait la plate-forme .NET 1.0. C# tait alors largement une copie de Java et .NET une bibliothque de classes trs proche de celle de la plate-forme de dveloppement Java. Dans le cadre de l'apprentissage du langage, on passait d'un environnement C# un environnement Java sans tre vraiment dpays. On trouvait mme des outils de conversion de code source d'un langage vers l'autre. Depuis, les choses ont volu. Chaque langage et chaque plate-forme de dveloppement a dsormais ses spcificits. Il n'est plus aussi immdiat de transfrer ses comptences d'un domaine l'autre. C# 3.0 et le framework .NET 3.5 amnent beaucoup de nouveauts. La plus importante est probablement LINQ (Language INtegrated Query) qui permet de requter de faon uniforme, d'une faon proche de celle du langage SQL, des squences d'objets provenant de structures en mmoire telles que les tableaux et les listes, de bases de donnes (SQL Server uniquement pour le moment - fvrier 2008) ou de fichiers XML. Ce document n'est pas un cours exhaustif. Par exemple, LINQ n'y est pas abord. Il est destin des personnes connaissant dj la programmation et qui veulent dcouvrir les bases de C#. Il est une rvision du document originel paru en 2002. Plusieurs livres m'ont aid crire ce cours : Pour la version 2002 : - Professional C# programming, Editions Wrox - C# et .NET, Grard Leblanc, Editions Eyrolles A l'poque j'avais trouv excellents ces deux ouvrages. Depuis Grard Leblanc a publi des versions mises jour dont la suivante : C# et .NET 2005, Grard Leblanc, Editions Eyrolles

Pour la rvision de 2008, j'ai utilis les sources suivantes : le document initial de 2002. Ce document issu d'un copier / coller de mon cours Java comportait la fois des erreurs de typographie et des erreurs plus srieuses telles que dire que les types primitifs comme System.Int32 taient des classes alors que ce sont des structures. Mea culpa... la documentation MSDN de Visual Studio Express 2008 le livre C# 3.0 in a Nutshell de Joseph et Ben Albahari aux ditions O'Reilly, l'un des meilleurs livres de programmation qu'il m'ait t donn de lire. Les codes source des exemples de ce document sont disponibles sur le site [http://tahe.developpez.com/dotnet/csharp/]. Serge Tah, mai 2008

2

0

Installation de Visual C# 2008

Fin janvier 2008, les versions Express de Visual Studio 2008 sont tlchargeables [2] l'adresse suivante [1] : [http://msdn2.microsoft.com/fr-fr/express/future/default(en-us).aspx] : 1

2

3

[1] : l'adresse de tlchargement [2] : l'onglet des tlchargements [3] : tlcharger C# 2008

L'installation de C# 2008 entranera celle d'autres lments :

le framework .NET 3.5 le SGBD SQL Server Compact 3.5 la documentation MSDN

Pour crer un premier programme avec C# 2008, on pourra procder comme suit, aprs avoir lanc C# :

1

2

4b

3 4

4c

[1] : prendre l'option File / New Project [2] : choisir une application de type Console [3] : donner un nom au projet - il va tre chang ci-dessous [4] : valider [4b] : le projet cr [4c] : Program.cs est le programme C# gnr par dfaut dans le projet.

Installation de Visual C# 2008

3

6 7 10 9 11

8

5

la 1re tape n'a pas demand o placer le projet. Si on ne fait rien, il sera sauvegard un endroit par dfaut qui ne nous conviendra probablement pas. L'option [5] permet de sauvegarder le projet dans un dossier prcis. on peut donner un nouveau nom au projet dans [6], prciser dans [7] son dossier. Pour cela on peut utiliser [8]. Ici, le projet sera au final dans le dossier [C:\temp\08-01-31\MyApplication1]. en cochant [9], on peut crer un dossier pour la solution nomme dans [10]. Si Solution1 est le nom de la solution : un dossier [C:\temp\08-01-31\Solution1] sera cr pour la solution Solution1 un dossier [C:\temp\08-01-31\Solution1\MyApplication1] sera cr pour le projet MyApplication1. Cette solution convient bien aux solutions composes de plusieurs projets. Chaque projet aura un sous-dossier dans le dossier de la solution. 1

3 2

en [1] : le dossier windows du projet MyApplication1 en [2] : son contenu en [3] : le projet dans l'explorateur de projets de Visual Studio

Modifions le code du fichier [Program.cs] [3] comme suit :1. 2. 3. 4. 5. 6. 7. 8. 9. using System; namespace ConsoleApplication1 { class Program { static void Main(string[] args) { Console.WriteLine("1er essai avec C# 2008"); } } }

ligne 3 : l'espace de noms de la classe dfinie ligne 4. Ainsi le nom complet de la classe, dfinie ligne 4, est-il ici ConsoleApplication1.Program. lignes 5-7 : la mthode statique Main qui est excute lorsqu'on demande l'excution d'une classe ligne 6 : un affichage cran

Le programme peut tre excut de la faon suivante :

Installation de Visual C# 2008

4

1. 2.

1er essai avec C# 2008 Appuyez sur une touche pour continuer...

2 1

[Ctrl-F5] pour excuter le projet, en [1] en [2], l'affichage console obtenu.

L'excution a rajout des fichiers au projet :

1

3 2

en [1], on fait afficher tous les fichiers du projet en [2] : le dossier [Release] contient l'excutable [MyApplication1.exe] du projet. en [3] : le dossier [Debug] qui contiendrait lui aussi un excutable [MyApplication1.exe] du projet si on avait excut le projet en mode [Debug] (touche F5 au lieu de Ctrl-F5). Ce n'est pas le mme excutable que celui obtenu en mode [Release]. Il contient des informations complmentaires permettant au processus de dbogage d'avoir lieu.

On peut rajouter un nouveau projet la solution courante :

2 1

4 3

[1] : clic droit sur la solution (pas le projet) / Add / New Project [2] : choix d'un type d'application [3] : le dossier propos par dfaut est celui contenant le dossier du projet dj existant [MyApplication1] [4] : donner un nom au nouveau projet

La solution a alors deux projets :

Installation de Visual C# 2008

5

1

2

[1] : le nouveau projet [2] : lorsque la solution est excute par (F5 ou Ctrl-F5), l'un des projets est excut. C'est celui qu'on dsigne par [2].

Un projet peut avoir plusieurs classes excutables (contenant une mthode Main). Dans ce cas, on doit prciser la classe excuter lorsque le projet est excut : 3

1

2 4

[1, 2] : on copie / colle le fichier [Program.cs] [3] : le rsultat du copier / coller [4,5] : on renomme les deux fichiers

5

La classe P1 (ligne 4) :1. 2. 3. 4. using System; namespace MyApplication2 { class P1 {

Installation de Visual C# 2008

6

5. 6. 7. 8.

} }

static void Main(string[] args) { }

La classe P2 (ligne 4) :1. 2. 3. 4. 5. 6. 7. 8. using System; namespace MyApplication2 { class P2 { static void Main(string[] args) { } } }

Le projet [MyApplication2] a maintenant deux classes ayant une mthode statique Main. Il faut indiquer au projet laquelle excuter :

4

5 3 2

1

en [1] : les proprits du projet [MyApplication2] en [2] : le choix de la classe excuter lorsque le projet est excut (F5 ou Ctrl-F5) en [3] : le type d'excutable produit - ici une application Console produira un fichier .exe. en [4] : le nom de l'excutable produit (sans le .exe) en [5] : l'espace de noms par dfaut. C'est celui qui sera gnr dans le code de chaque nouvelle classe ajoute au projet. Il peut alors tre chang directement dans le code, si besoin est.

Installation de Visual C# 2008

7

11.1

Les bases du langage C#Introduction

Nous traitons C# d'abord comme un langage de programmation classique. Nous aborderons les classes ultrieurement. Dans un programme on trouve deux choses : des donnes les instructions qui les manipulent

On s'efforce gnralement de sparer les donnes des instructions :+--------------------+ DONNEES +-------------------- INSTRUCTIONS +--------------------+

1.2

Les donnes de C#

C# utilise les types de donnes suivants: 1. 2. 3. 4. 5. 6. les nombres entiers les nombres rels les nombres dcimaux les caractres et chanes de caractres les boolens les objets

1.2.1

Les types de donnes prdfinisType .NET Donne reprsente caractre chane de caractres nombre entier .. .. .. .. .. .. .. nombre rel .. nombre dcimal .. rfrence d'objet Suffixe des valeurs littrales Codage Domaine de valeurs

Type C#

char string int uint long ulong sbyte byte short ushort float double decimal bool object

Char (S) String (C) Int32 (S) UInt32 (S) Int64 (S) UInt64 (S) Byte (S) Int16 (S) UInt16 (S) Single (S) Double (S) Decimal (S) Boolean (S) Object (C)

2 octets 4 octets 4 octets 8 octets

caractre Unicode (UTF-16) rfrence sur une squence de caractres Unicode

U L UL

F D M

[-231, 231-1] [2147483648, 2147483647] [0, 232-1] [0, 4294967295] [-263, 263 -1] [9223372036854775808, 9223372036854775807] 8 octets [0, 264 -1] [0, 18446744073709551615] 1 octet [-27 , 27 -1] [-128,+127] 1 octet [0 , 28 -1] [0,255] 2 octets [-215, 215-1] [-32768, 32767] 2 octets [0, 216-1] [0,65535] 4 octets [1.5 10-45, 3.4 10+38] en valeur absolue 8 octets [-1.7 10+308, 1.7 10+308] en valeur absolue 16 octets [1.0 10-28,7.9 10+28] en valeur absolue avec 28 chiffres significatifs 1 octet true, false rfrence d'objet

Ci-dessus, on a mis en face des types C#, leur type .NET quivalent avec le commentaire (S) si ce type est une structure et (C) si le type est une classe. On dcouvre qu'il y a deux types possibles pour un entier sur 32 bits : int et Int32. Le type int est un type C#.

Les bases du langage C#

8

Int32 est une structure appartenant l'espace de noms System. Son nom complet est ainsi System.Int32. Le type int est un alias C# qui dsigne la structure .NET System.Int32. De mme, le type C# string est un alias pour le type .NET System.String. System.String est une classe et non une structure. Les deux notions sont proches avec cependant la diffrence fondamentale suivante : une variable de type Structure se manipule via sa valeur une variable de type Classe se manipule via son adresse (rfrence en langage objet). Une structure comme une classe sont des types complexes ayant des attributs et des mthodes. Ainsi, on pourra crire :string nomDuType=3.GetType().FullName;

Ci-dessus le littral 3 est par dfaut de type C# int, donc de type .NET System.Int32. Cette structure a une mthode GetType() qui rend un objet encapsulant les caractristiques du type de donnes System.Int32. Parmi celles-ci, la proprit FullName rend le nom complet du type. On voit donc que le littral 3 est un objet plus complexe qu'il n'y parat premire vue. Voici un programme illustrant ces diffrents points :1. using System; 2. 3. namespace Chap1 { 4. class P00 { 5. static void Main(string[] args) { 6. // exemple 1 7. int ent = 2; 8. float fl = 10.5F; 9. double d = -4.6; 10. string s = "essai"; 11. uint ui = 5; 12. long l = 1000; 13. ulong ul = 1001; 14. byte octet = 5; 15. short sh = -4; 16. ushort ush = 10; 17. decimal dec = 10.67M; 18. bool b = true; 19. Console.WriteLine("Type de ent[{1}] : [{0},{2}]", ent.GetType().FullName, ent,sizeof(int)); 20. Console.WriteLine("Type de fl[{1}]: [{0},{2}]", fl.GetType().FullName, fl, sizeof(float)); 21. Console.WriteLine("Type de d[{1}] : [{0},{2}]", d.GetType().FullName, d, sizeof(double)); 22. Console.WriteLine("Type de s[{1}] : [{0}]", s.GetType().FullName, s); 23. Console.WriteLine("Type de ui[{1}] : [{0},{2}]", ui.GetType().FullName, ui, sizeof(uint)); 24. Console.WriteLine("Type de l[{1}] : [{0},{2}]", l.GetType().FullName, l, sizeof(long)); 25. Console.WriteLine("Type de ul[{1}] : [{0},{2}]", ul.GetType().FullName, ul, sizeof(ulong)); 26. Console.WriteLine("Type de b[{1}] : [{0},{2}]", octet.GetType().FullName, octet, sizeof(byte)); 27. Console.WriteLine("Type de sh[{1}] : [{0},{2}]", sh.GetType().FullName, sh, sizeof(short)); 28. Console.WriteLine("Type de ush[{1}] : [{0},{2}]", ush.GetType().FullName, ush, sizeof(ushort)); 29. Console.WriteLine("Type de dec[{1}] : [{0},{2}]", dec.GetType().FullName, dec, sizeof(decimal)); 30. Console.WriteLine("Type de b[{1}] : [{0},{2}]", b.GetType().FullName, b, sizeof(bool)); 31. } 32. } 33. }

ligne 7 : dclaration d'un entier ent ligne 19 : le type d'une variable v peut tre obtenue par v.GetType().FullName. La taille d'une structure S peut tre obtenue par sizeof(S). L'instruction Console.WriteLine("... {0} ... {1} ...",param0, param1, ...) crit l'cran, le texte qui est son premier paramtre, en remplaant chaque notation {i} par la valeur de l'expression parami. ligne 22 : le type string dsignant une classe et non une structure, on ne peut utiliser l'oprateur sizeof.

Voici le rsultat de l'excution :1. 2. 3. 4. 5. 6. 7. 8. 9. 10. Type Type Type Type Type Type Type Type Type Type de de de de de de de de de de ent[2] : [System.Int32,4] fl[10,5]: [System.Single,4] d[-4,6] : [System.Double,8] s[essai] : [System.String] ui[5] : [System.UInt32,4] l[1000] : [System.Int64,8] ul[1001] : [System.UInt64,8] b[5] : [System.Byte,1] sh[-4] : [System.Int16,2] ush[10] : [System.UInt16,2]

Les bases du langage C#

9

11. Type de dec[10,67] : [System.Decimal,16] 12. Type de b[True] : [System.Boolean,1]

L'affichage produit les types .NET et non les alias C#.

1.2.2

Notation des donnes littrales145, -7, 0xFF (hexadcimal) 100000L 134.789, -45E-18 (-45 10-18) 134.789F, -45E-18F (-45 10-18) 100000M 'A', 'b' "aujourd'hui" "c:\\chap1\\paragraph3" @"c:\chap1\paragraph3" true, false new DateTime(1954,10,13) (an, mois, jour) pour le 13/10/1954

entier int (32 bits) entier long (64 bits) - suffixe L rel double rel float (suffixe F) rel decimal (suffixe M) caractre char chane de caractres string boolen bool date

On notera les deux chanes littrales : "c:\\chap1\\paragraph3" et @"c:\chap1\paragraph3". Dans les chanes littrales, le caractre \ est interprt. Ainsi "\n" reprsente la marque de fin de ligne et non la succession des deux caractres \ et n. Si on voulait cette succession, il faudrait crire "\\n" o la squence \\ est interprte comme un seul caractre \. On pourrait crire aussi @"\n" pour avoir le mme rsultat. La syntaxe @"texte" demande que texte soit pris exactement comme il est crit. On appelle parfois cela une chane verbatim.

1.2.31.2.3.1

Dclaration des donnesRle des dclarations

Un programme manipule des donnes caractrises par un nom et un type. Ces donnes sont stockes en mmoire. Au moment de la traduction du programme, le compilateur affecte chaque donne un emplacement en mmoire caractris par une adresse et une taille. Il le fait en s'aidant des dclarations faites par le programmeur. Par ailleurs celles-ci permettent au compilateur de dtecter des erreurs de programmation. Ainsi l'opration x=x*2; sera dclare errone si x est une chane de caractres par exemple.

1.2.3.2

Dclaration des constantes

La syntaxe de dclaration d'une constante est la suivante :const type nom=valeur; //dfinit constante nom=valeur

Par exemple :const float myPI=3.141592F;

Pourquoi dclarer des constantes ? 1. La lecture du programme sera plus aise si l'on a donn la constante un nom significatif :const float taux_tva=0.186F;

2.

La modification du programme sera plus aise si la "constante" vient changer. Ainsi dans le cas prcdent, si le taux de tva passe 33%, la seule modification faire sera de modifier l'instruction dfinissant sa valeur :const float taux_tva=0.33F;

Si l'on avait utilis 0.186 explicitement dans le programme, ce serait alors de nombreuses instructions qu'il faudrait modifier.

1.2.3.3

Dclaration des variables

Une variable est identifie par un nom et se rapporte un type de donnes. C# fait la diffrence entre majuscules et minuscules. Ainsi les variables FIN et fin sont diffrentes.

Les bases du langage C#

10

Les variables peuvent tre initialises lors de leur dclaration. La syntaxe de dclaration d'une ou plusieurs variables est :Identificateur_de_type variable1[=valeur1],variable2=[valeur2],...;

o Identificateur_de_type est un type prdfini ou bien un type dfini par le programmeur. De faon facultative, une variable peut tre initialise en mme temps que dclare. On peut galement ne pas prciser le type exact d'une variable en utilisant le mot cl var en lieu et place de Identificateur_de_type :var variable1=valeur1,variable2=valeur2,...;

Le mot cl var ne veut pas dire que les variables n'ont pas un type prcis. La variable variablei a le type de la donne valeuri qui lui est affecte. L'initialisation est ici obligatoire afin que le compilateur puisse en dduire le type de la variable. Voici un exemple :1. using System; 2. 3. namespace Chap1 { 4. class P00 { 5. static void Main(string[] args) { 6. int i=2; 7. Console.WriteLine("Type de int i=2 : {0},{1}",i.GetType().Name,i.GetType().FullName); 8. var j = 3; 9. Console.WriteLine("Type de var j=3 : {0},{1}", j.GetType().Name, j.GetType().FullName); 10. var aujourdhui = DateTime.Now; 11. Console.WriteLine("Type de var aujourdhui : {0},{1}", aujourdhui.GetType().Name, aujourdhui.GetType().FullName); 12. } 13. } 14. }

ligne 6 : une donne type explicitement ligne 7 : (donne).GetType().Name est le nom court de (donne), (donne).GetType().FullName est le nom complet de (donne) ligne 8 : une donne type implicitement. Parce que 3 est de type int, j sera de type int. ligne 10 : une donne type implicitement. Parce que DateTime.Now est de type DateTime, aujourdhui sera de type DateTime.

A l'excution, on obtient le rsultat suivant :1. 2. 3. Type de int i=2 : Int32,System.Int32 Type de var j=3 : Int32,System.Int32 Type de var aujourdhui : DateTime,System.DateTime

Une variable type implicitement par le mot cl var ne peut pas ensuite changer de type. Ainsi, on ne pourrait crire aprs la ligne 10 du code, la ligne :var aujourdhui = "aujourd'hui";

Nous verrons ultrieurement qu'il est possible de dclarer un type " la vole" dans une expression. C'est alors un type anonyme, un type auquel l'utilisateur n'a pas donn de nom. C'est le compilateur qui donnera un nom ce nouveau type. Si une donne de type anonyme doit tre affecte une variable, la seule faon de dclarer celle-ci est d'utiliser le mot cl var.

1.2.4nombre chaine chane chane chane

Les conversions entre nombres et chanes de caractres-> -> -> -> -> chane int long double float

nombre.ToString() int.Parse(chaine) ou System.Int32.Parse long.Parse(chaine) ou System.Int64.Parse double.Parse(chane) ou System.Double.Parse(chane) float.Parse(chane) ou System.Float.Parse(chane)

La conversion d'une chane vers un nombre peut chouer si la chane ne reprsente pas un nombre valide. Il y a alors gnration d'une erreur fatale appele exception. Cette erreur peut tre gre par la clause try/catch suivante : try{

Les bases du langage C#

11

appel de la fonction susceptible de gnrer l'exception } catch (Exception e){ traiter l'exception e } instruction suivante Si la fonction ne gnre pas d'exception, on passe alors instruction suivante, sinon on passe dans le corps de la clause catch puis instruction suivante. Nous reviendrons ultrieurement sur la gestion des exceptions. Voici un programme prsentant quelques techniques de conversion entre nombres et chanes de caractres. Dans cet exemple la fonction affiche crit l'cran la valeur de son paramtre. Ainsi affiche(S) crit la valeur de S l'cran o S est de type string.1. using System; 2. 3. namespace Chap1 { 4. class P01 { 5. static void Main(string[] args) { 6. 7. // donnes 8. const int i = 10; 9. const long l = 100000; 10. const float f = 45.78F; 11. double d = -14.98; 12. 13. // nombre --> chane 14. affiche(i.ToString()); 15. affiche(l.ToString()); 16. affiche(f.ToString()); 17. affiche(d.ToString()); 18. 19. //boolean --> chane 20. const bool b = false; 21. affiche(b.ToString()); 22. 23. // chane --> int 24. int i1; 25. i1 = int.Parse("10"); 26. affiche(i1.ToString()); 27. try { 28. i1 = int.Parse("10.67"); 29. affiche(i1.ToString()); 30. } catch (Exception e) { 31. affiche("Erreur : " + e.Message); 32. } 33. 34. // chane --> long 35. long l1; 36. l1 = long.Parse("100"); 37. affiche(l1.ToString()); 38. try { 39. l1 = long.Parse("10.675"); 40. affiche(l1.ToString()); 41. } catch (Exception e) { 42. affiche("Erreur : " + e.Message); 43. } 44. 45. // chane --> double 46. double d1; 47. d1 = double.Parse("100,87"); 48. affiche(d1.ToString()); 49. try { 50. d1 = double.Parse("abcd"); 51. affiche(d1.ToString()); 52. } catch (Exception e) { 53. affiche("Erreur : " + e.Message); 54. } 55. 56. // chane --> float 57. float f1; 58. f1 = float.Parse("100,87"); 59. affiche(f1.ToString()); 60. try { 61. d1 = float.Parse("abcd"); 62. affiche(f1.ToString()); 63. } catch (Exception e) {

Les bases du langage C#

12

64. affiche("Erreur : " + e.Message); 65. } 66. 67. }// fin main 68. 69. public static void affiche(string S) { 70. Console.Out.WriteLine("S={0}",S); 71. } 72. }// fin classe 73. }

Lignes 30-32, on gre l'ventuelle exception qui peut se produire. e.Message est le message d'erreur li l'exception e. Les rsultats obtenus sont les suivants :1. 2. 3. 4. 5. 6. 7. 8. 9. 10. 11. 12. 13. S=10 S=100000 S=45,78 S=-14,98 S=False S=10 S=Erreur S=100 S=Erreur S=100,87 S=Erreur S=100,87 S=Erreur

: Input string was not in a correct format. : Input string was not in a correct format. : Input string was not in a correct format. : Input string was not in a correct format.

On remarquera que les nombres rels sous forme de chane de caractres doivent utiliser la virgule et non le point dcimal. Ainsi on criradouble d1=10.7;

maisdouble d2=int.Parse("10,7");

1.2.5

Les tableaux de donnes

Un tableau C# est un objet permettant de rassembler sous un mme identificateur des donnes de mme type. Sa dclaration est la suivante : Type[] tableau[]=new Type[n] n est le nombre de donnes que peut contenir le tableau. La syntaxe Tableau[i] dsigne la donne n i o i appartient l'intervalle [0,n-1]. Toute rfrence la donne Tableau[i] o i n'appartient pas l'intervalle [0,n-1] provoquera une exception. Un tableau peut tre initialis en mme temps que dclar :int[] entiers=new int[] {0,10,20,30};

ou plus simplement :int[] entiers={0,10,20,30};

Les tableaux ont une proprit Length qui est le nombre d'lments du tableau. Un tableau deux dimensions pourra tre dclar comme suit : Type[,] tableau=new Type[n,m]; o n est le nombre de lignes, m le nombre de colonnes. La syntaxe Tableau[i,j] dsigne l'lment j de la ligne i de tableau. Le tableau deux dimensions peut lui aussi tre initialis en mme temps qu'il est dclar :double[,] rels=new double[,] { {0.5, 1.7}, {8.4, -6}};

ou plus simplement :

Les bases du langage C#

13

double[,] rels={ {0.5, 1.7}, {8.4, -6}};

Le nombre d'lments dans chacune des dimensions peut tre obtenue par la mthode GetLength(i) o i=0 reprsente la dimension correspondant au 1er indice, i=1 la dimension correspondant au 2ime indice, Le nombre total de dimensions est obtenu avec la proprit Rank, le nombre total d'lments avec la proprit Length. Un tableau de tableaux est dclar comme suit : Type[][] tableau=new Type[n][]; La dclaration ci-dessus cre un tableau de n lignes. Chaque lment tableau[i] est une rfrence de tableau une dimension. Ces rfrences tableau[i] ne sont pas initialises lors de la dclaration ci-dessus. Elles ont pour valeur la rfrence null. L'exemple ci-dessous illustre la cration d'un tableau de tableaux :1. 2. 3. 4. 5. 6. 7. 8. 9. 10. 11. // un tableau de tableaux string[][] noms = new string[3][]; for (int i = 0; i < noms.Length; i++) { noms[i] = new string[i + 1]; }//for // initialisation for (int i = 0; i < noms.Length; i++) { for (int j = 0; j < noms[i].Length; j++) { noms[i][j] = "nom" + i + j; }//for j }//for i

ligne 2 : un tableau noms de 3 lments de type string[][]. Chaque lment est un pointeur de tableau (une rfrence d'objet) dont les lments sont de type string[]. lignes 3-5 : les 3 lments du tableau noms sont initialiss. Chacun "pointe" dsormais sur un tableau d'lments de type string[]. noms[i][j] est l'lment j du tableau de type string [] rfrenc par noms[i]. ligne 9 : initialisation de l'lment noms[i][j] l'intrieur d'une double boucle. Ici noms[i] est un tableau de i+1 lments. Comme noms[i] est un tableau, noms[i].Length est son nombre d'lments.

Voici un exemple regroupant les trois types de tableaux que nous venons de prsenter :12. 13. 14. 15. 16. 17. 18. 19. 20. 21. 22. 23. 24. 25. 26. 27. 28. 29. 30. 31. 32. 33. 34. 35. 36. 37. 38. 39. 40. 41. 42. 43. 44. 45. using System; namespace Chap1 { // tableaux using System; // classe de test public class P02 { public static void Main() { // un tableau 1 dimension initialis int[] entiers = new int[] { 0, 10, 20, 30 }; for (int i = 0; i < entiers.Length; i++) { Console.Out.WriteLine("entiers[{0}]={1}", i, entiers[i]); }//for // un tableau 2 dimensions initialis double[,] rels = new double[,] { { 0.5, 1.7 }, { 8.4, -6 } }; for (int i = 0; i < rels.GetLength(0); i++) { for (int j = 0; j < rels.GetLength(1); j++) { Console.Out.WriteLine("rels[{0},{1}]={2}", i, j, rels[i, j]); }//for j }//for i // un tableau de tableaux string[][] noms = new string[3][]; for (int i = 0; i < noms.Length; i++) { noms[i] = new string[i + 1]; }//for // initialisation for (int i = 0; i < noms.Length; i++) { for (int j = 0; j < noms[i].Length; j++) { noms[i][j] = "nom" + i + j; }//for j

Les bases du langage C#

14

46. }//for i 47. // affichage 48. for (int i = 0; i < noms.Length; i++) { 49. for (int j = 0; j < noms[i].Length; j++) { 50. Console.Out.WriteLine("noms[{0}][{1}]={2}", i, j, noms[i][j]); 51. }//for j 52. }//for i 53. }//Main 54. }//class 55. }//namespace

A l'excution, nous obtenons les rsultats suivants :1. 2. 3. 4. 5. 6. 7. 8. 9. 10. 11. 12. 13. 14. entiers[0]=0 entiers[1]=10 entiers[2]=20 entiers[3]=30 rels[0,0]=0,5 rels[0,1]=1,7 rels[1,0]=8,4 rels[1,1]=-6 noms[0][0]=nom00 noms[1][0]=nom10 noms[1][1]=nom11 noms[2][0]=nom20 noms[2][1]=nom21 noms[2][2]=nom22

1.3

Les instructions lmentaires de C#

On distingue 1 2 les instructions lmentaires excutes par l'ordinateur. les instructions de contrle du droulement du programme.

Les instructions lmentaires apparaissent clairement lorsqu'on considre la structure d'un micro-ordinateur et de ses priphriques.U. C MEMOIRE ECRAN +-------------------+ +-------+ 2 3 +-----------+ 1 ----+------+-> CLAVIER +-----------+--------+--> +-----------+ +-------------------+ +-------+

1. lecture d'informations provenant du clavier 2. traitement d'informations 3. criture d'informations l'cran

1.3.1

Ecriture sur cran

Il existe diffrentes instructions d'criture l'cran :Console.Out.WriteLine(expression) Console.WriteLine(expression) Console.Error.WriteLine (expression)

o expression est tout type de donne qui puisse tre converti en chane de caractres pour tre affich l'cran. Tous les objets de C# ou .NET ont une mthode ToString() qui est utilise pour faire cette conversion. La classe System.Console donne accs aux oprations d'criture cran (Write, WriteLine). La classe Console a deux proprits Out et Error qui sont des flux d'criture de type TextWriter : Console.WriteLine() est quivalent Console.Out.WriteLine() et crit sur le flux Out associ habituellement l'cran. Console.Error.WriteLine() crit sur le flux Error, habituellement associ lui aussi l'cran.

Les bases du langage C#

15

Les flux Out et Error peuvent tre redirigs vers des fichiers texte au moment de l'excution du programme comme nous le verrons prochainement.

1.3.2

Lecture de donnes tapes au clavier

Le flux de donnes provenant du clavier est dsign par l'objet Console.In de type TextReader. Ce type d'objets permet de lire une ligne de texte avec la mthode ReadLine :string ligne=Console.In.ReadLine();

La classe Console offre une mthode ReadLine associe par dfaut au flux In. On peut donc crire crire :string ligne=Console.ReadLine();

La ligne tape au clavier est range dans la variable ligne et peut ensuite tre exploite par le programme. Le flux In peut tre redirig vers un fichier, comme les flux Out et Error.

1.3.3

Exemple d'entres-sorties

Voici un court programme d'illustration des oprations d'entres-sorties clavier/cran :1. 2. 3. 4. 5. 6. 7. 8. 9. 10. 11. 12. 13. 14. 15. 16. 17. 18. 19. 20. 21. 22. using System; namespace Chap1 { // classe de test public class P03 { public static void Main() { // criture sur le flux Out object obj = new object(); Console.Out.WriteLine(obj); // criture sur le flux Error int i = 10; Console.Error.WriteLine("i=" + i); // lecture d'une ligne saisie au clavier Console.Write("Tapez une ligne : "); string ligne = Console.ReadLine(); Console.WriteLine("ligne={0}", ligne); }//fin main }//fin classe }

ligne 9 : obj est une rfrence d'objet ligne 10 : obj est crit l'cran. Par dfaut, c'est la mthode obj.ToString() qui est appele. ligne 14 : on peut aussi crire : Le 1er paramtre "i={0}" est le format d'affichage, les autres paramtres, les expressions afficher. Les lments {n} sont des paramtres "positionnels". A l'excution, le paramtre {n} est remplac par la valeur de l'expression n n.Console.Error.WriteLine("i={0}",i);

Le rsultat de l'excution est le suivant :1. 2. 3. 4. System.Object i=10 Tapez une ligne : je suis l ligne=je suis l

ligne 1 : l'affichage produit par la ligne 10 du code. La mthode obj.ToString() a affich le nom du type de la variable obj : System.Object. Le type object est un alias C# du type .NET System.Object.

1.3.4

Redirection des E/S

Il existe sous DOS et UNIX trois priphriques standard appels : 1. priphrique d'entre standard - dsigne par dfaut le clavier et porte le n 0

Les bases du langage C#

16

2. 3.

priphrique de sortie standard - dsigne par dfaut l'cran et porte le n 1 priphrique d'erreur standard - dsigne par dfaut l'cran et porte le n 2

En C#, le flux d'criture Console.Out crit sur le priphrique 1, le flux d'criture Console.Error crit sur le priphrique 2 et le flux de lecture Console.In lit les donnes provenant du priphrique 0. Lorsqu'on lance un programme sous Dos ou Unix, on peut fixer quels seront les priphriques 0, 1 et 2 pour le programme excut. Considrons la ligne de commande suivante :pg arg1 arg2 .. argn

Derrire les arguments argi du programme pg, on peut rediriger les priphriques d'E/S standard vers des fichiers:0out.txt 1>>out.txt 2>error.txt 2>>error.txt 1>out.txt 2>error.txt

le flux d'entre standard n 0 est redirig vers le fichier in.txt. Dans le programme le flux Console.In prendra donc ses donnes dans le fichier in.txt. redirige la sortie n 1 vers le fichier out.txt. Cela entrane que dans le programme le flux Console.Out crira ses donnes dans le fichier out.txt idem, mais les donnes crites sont ajoutes au contenu actuel du fichier out.txt. redirige la sortie n 2 vers le fichier error.txt. Cela entrane que dans le programme le flux Console.Error crira ses donnes dans le fichier error.txt idem, mais les donnes crites sont ajoutes au contenu actuel du fichier error.txt. Les priphriques 1 et 2 sont tous les deux redirigs vers des fichiers

On notera que pour rediriger les flux d'E/S du programme pg vers des fichiers, le programme pg n'a pas besoin d'tre modifi. C'est le systme d'exploitation qui fixe la nature des priphriques 0,1 et 2. Considrons le programme suivant :1. 2. 3. 4. 5. 6. 7. 8. 9. 10. 11. 12. 13. 14. 15. 16. using System; namespace Chap1 { // redirections public class P04 { public static void Main(string[] args) { // lecture flux In string data = Console.In.ReadLine(); // criture flux Out Console.Out.WriteLine("criture dans flux Out : " + data); // criture flux Error Console.Error.WriteLine("criture dans flux Error : " + data); }//Main }//classe }

Gnrons l'excutable de ce code source :1. 2. 3. C:\data\travail\2007-2008\c# 2008\poly\Chap1\04\bin\Release>dir 29/01/2008 15:01 4 608 04.exe 29/01/2008 15:01 11 776 04.pdb

1

2

en [1] : l'excutable est cr par clic droit sur le projet / Build en [2] : dans une fentre Dos, l'excutable 04.exe a t cr dans le dossier bin/Release du projet.

Emettons les commandes suivantes dans la fentre Dos [2] :1. 2. 3. 4. 5. 6. ...\04\bin\Release>echo test >in.txt ...\04\bin\Release>more in.txt test ...\04\bin\Release>04 0out.txt 2>err.txt ...\04\bin\Release>more out.txt criture dans flux Out : test

Les bases du langage C#

17

7. 8.

...\04\bin\Release>more err.txt criture dans flux Error : test

ligne 1 : on met la chane test dans le fichier in.txt lignes 2-3 : on affiche le contenu du fichier in.txt pour vrification ligne 4 : excution du programme 04.exe. Le flux In est redirig vers le fichier in.txt, le flux Out vers le fichier out.txt, le flux Error vers le fichier err.txt. L'excution ne provoque aucun affichage. lignes 5-6 : contenu du fichier out.txt. Ce contenu nous montre que : le fichier in.txt a t lu l'affichage cran a t redirig vers out.txt lignes 7-8 : vrification analogue pour le fichier err.txt

On voit clairement que les flux Out et In n'crivent pas sur les mmes priphriques puisqu'on a pu les rediriger sparment.

1.3.5

Affectation de la valeur d'une expression une variable

On s'intresse ici l'opration variable=expression; L'expression peut tre de type : arithmtique, relationnelle, boolenne, caractres

1.3.5.1

Interprtation de l'opration d'affectation

L'opration variable=expression; est elle-mme une expression dont l'valuation se droule de la faon suivante :

la partie droite de l'affectation est value : le rsultat est une valeur V. la valeur V est affecte la variable la valeur V est aussi la valeur de l'affectation vue cette fois en tant qu'expression.

C'est ainsi que l'opration V1=V2=expression est lgale. A cause de la priorit, c'est l'oprateur = le plus droite qui va tre valu. On a donc V1=(V2=expression) L'expression V2=expression est value et a pour valeur V. L'valuation de cette expression a provoqu l'affectation de V V2. L'oprateur = suivant est alors valu sous la forme : V1=V La valeur de cette expression est encore V. Son valuation provoque l'affectation de V V1. Ainsi donc, l'opration V1=V2=expression est une expression dont l'valuation 1 2 provoque l'affectation de la valeur de expression aux variables V1 et V2 rend comme rsultat la valeur de expression.

On peut gnraliser une expression du type : V1=V2=....=Vn=expression

1.3.5.2

Expression arithmtique

Les oprateurs des expressions arithmtiques sont les suivants : + addition

Les bases du langage C#

18

* / %

soustraction multiplication division : le rsultat est le quotient exact si l'un au moins des oprandes est rel. Si les deux oprandes sont entiers le rsultat est le quotient entier. Ainsi 5/2 -> 2 et 5.0/2 ->2.5. division : le rsultat est le reste quelque soit la nature des oprandes, le quotient tant lui entier. C'est donc l'opration modulo.

Il existe diverses fonctions mathmatiques. En voici quelques-unes :double double double double double double double double Sqrt(double x) Cos(double x) Sin(double x) Tan(double x) Pow(double x,double y) Exp(double x) Log(double x) Abs(double x)

racine carre Cosinus Sinus Tangente x la puissance y (x>0) Exponentielle Logarithme nprien valeur absolue

etc... Toutes ces fonctions sont dfinies dans une classe C# appele Math. Lorsqu'on les utilise, il faut les prfixer avec le nom de la classe o elles sont dfinies. Ainsi on crira :double x, y=4; x=Math.Sqrt(y);

La dfinition complte de la classe Math est la suivante :

Les bases du langage C#

19

1.3.5.3

Priorits dans l'valuation des expressions arithmtiques

La priorit des oprateurs lors de l'valuation d'une expression arithmtique est la suivante (du plus prioritaire au moins prioritaire) : [fonctions], [ ( )],[ *, /, %], [+, -] Les oprateurs d'un mme bloc [ ] ont mme priorit.

1.3.5.4

Expressions relationnelles

Les oprateurs sont les suivants : = priorits des oprateurs 1. >, >=, chane n2 -1 si chane n1 < chane n2 Ligne 8, la variable egal aura la valeur true si les deux chanes sont gales, false sinon. Ligne 10, on utilise les oprateurs == et != pour vrifier l'galit ou non de deux chanes. Les rsultats de l'excution :1. 2. i=-1, egal=False chien==chaine1:False,chien!=chaine2:False

1.3.5.5

Expressions boolennes

Les oprateurs utilisables sont AND (&&) OR(||) NOT (!). Le rsultat d'une expression boolenne est un boolen. priorits des oprateurs : 1. ! 2. && 3. ||double x = 3.5; bool valide = x > 2 && x < 4;

Les oprateurs relationnels ont priorit sur les oprateurs && et ||.

1.3.5.6

Traitement de bits

Les oprateurs

Les bases du langage C#

21

Soient i et j deux entiers.in i & j i | j ~i i^j

dcale i de n bits sur la gauche. Les bits entrants sont des zros. dcale i de n bits sur la droite. Si i est un entier sign (signed char, int, long) le bit de signe est prserv. fait le ET logique de i et j bit bit. fait le OU logique de i et j bit bit. complmente i 1 fait le OU EXCLUSIF de i et j

Soit le code suivant :1. 2. 3. 4. short i = 100, j = -13; ushort k = 0xF123; Console.WriteLine("i=0x{0:x4}, j=0x{1:x4}, k=0x{2:x4}", i,j,k); Console.WriteLine("i4=0x{1:x4},k>>4=0x{2:x4},i&j=0x{3:x4},i| j=0x{4:x4},~i=0x{5:x4},j2=0x{7:x4}", i > 4, k >> 4, (short)(i & j), (short)(i | j), (short)(~i), (short)(j > 2));

le format {0:x4} affiche le paramtre n 0 au format hexadcimal (x) avec 4 caractres (4).

Les rsultats de l'excution sont les suivants :1. 2. i=0x0064, j=0xfff3, k=0xf123 i4=0x0006,k>>4=0x0f12,i&j=0x0060,i|j=0xfff7,~i=0xff9b,j2=0xfffc

1.3.5.7

Combinaison d'oprateurs

a=a+b peut s'crire a+=b a=a-b peut s'crire a-=b Il en est de mme avec les oprateurs /, %,* ,, &, |, ^. Ainsi a=a/2; peut s'crire a/=2;

1.3.5.8

Oprateurs d'incrmentation et de dcrmentation

La notation variable++ signifie variable=variable+1 ou encore variable+=1 La notation variable-- signifie variable=variable-1 ou encore variable-=1.

1.3.5.9

L'oprateur ternaire ?

L'expressionexpr_cond ? expr1:expr2

est value de la faon suivante : 1 2 3 l'expression expr_cond est value. C'est une expression conditionnelle valeur vrai ou faux Si elle est vraie, la valeur de l'expression est celle de expr1 et expr2 n'est pas value. Si elle est fausse, c'est l'inverse qui se produit : la valeur de l'expression est celle de expr2 et expr1 n'est pas value.

L'opration i=(j>4 ? j+1:j-1); affectera la variable i : j+1 si j>4, j-1 sinon. C'est la mme chose que d'crire if(j>4) i=j+1; else i=j-1; mais c'est plus concis.

1.3.5.10

Priorit gnrale des oprateursgd dg dg gd gd gd gd gd gd gd

() [] fonction ! ~ ++ -new (type) oprateurs cast * / % + > < >= instanceof == != & ^

Les bases du langage C#

22

| && || ? : = += -= etc. .

gd gd gd dg dg

gd indique qu'a priorit gale, c'est la priorit gauche-droite qui est observe. Cela signifie que lorsque dans une expression, l'on a des oprateurs de mme priorit, c'est l'oprateur le plus gauche dans l'expression qui est valu en premier. dg indique une priorit droite-gauche.

1.3.5.11

Les changements de type

Il est possible, dans une expression, de changer momentanment le codage d'une valeur. On appelle cela changer le type d'une donne ou en anglais type casting. La syntaxe du changement du type d'une valeur dans une expression est la suivante: (type) valeur La valeur prend alors le type indiqu. Cela entrane un changement de codage de la valeur.1. 2. 3. 4. 5. 6. 7. 8. 9. 10. 11. 12. using System; namespace Chap1 { class P06 { static void Main(string[] args) { int i = 3, j = 4; float f1=i/j; float f2=(float)i/j; Console.WriteLine("f1={0}, f2={1}",f1,f2); } } }

ligne 7, f1 aura la valeur 0.0. La division 3/4 est une division entire puisque les deux oprandes sont de type int. ligne 8, (float)i est la valeur de i transforme en float. Maintenant, on a une division entre un rel de type float et un entier de type int. C'est la division entre nombres rels qui est alors faite. La valeur de j sera elle galement transforme en type float, puis la division des deux rels sera faite. f2 aura alors la valeur 0,75.

Voici les rsultats de l'excution :f1=0, f2=0,75

Dans l'opration (float)i : i est une valeur code de faon exacte sur 2 octets (float) i est la mme valeur code de faon approche en rel sur 4 octets Il y a donc transcodage de la valeur de i. Ce transcodage n'a lieu que le temps d'un calcul, la variable i conservant toujours son type int.

1.41.4.1

Les instructions de contrle du droulement du programmeArrt

La mthode Exit dfinie dans la classe Environment permet d'arrter l'excution d'un programme.syntaxe void Exit(int status) action arrte le processus en cours et rend la valeur status au processus pre

Exit provoque la fin du processus en cours et rend la main au processus appelant. La valeur de status peut tre utilise par celui-ci. Sous DOS, cette variable status est rendue dans la variable systme ERRORLEVEL dont la valeur peut tre teste dans un fichier batch. Sous Unix, avec l'interprteur de commandes Shell Bourne, c'est la variable $? qui rcupre la valeur de status.Environment.Exit(0);

Les bases du langage C#

23

arrtera l'excution du programme avec une valeur d'tat 0.

1.4.2

Structure de choix simpleif (condition) {actions_condition_vraie;} else {actions_condition_fausse;}

syntaxe :

notes:

la condition est entoure de parenthses. chaque action est termine par point-virgule. les accolades ne sont pas termines par point-virgule. les accolades ne sont ncessaires que s'il y a plus d'une action. la clause else peut tre absente. il n'y a pas de clause then.

L'quivalent algorithmique de cette structure est la structure si .. alors sinon : si condition alors actions_condition_vraie sinon actions_condition_fausse finsi exempleif (x>0) { nx=nx+1;sx=sx+x;} else dx=dx-x;

On peut imbriquer les structures de choix :if(condition1) if (condition2) {......} else //condition2 {......} else //condition1 {.......}

Se pose parfois le problme suivant :1. 2. 3. 4. 5. 6. 7. 8. 9. 10. 11. 12. 13. using System; namespace Chap1 { class P07 { static void Main(string[] args) { int n = 5; if (n > 1) if (n > 6) Console.Out.WriteLine(">6"); else Console.Out.WriteLine("6"); } else Console.Out.WriteLine(" limites[i]) i++; // l'impt int impots = (int)(coeffR[i] * revenu - coeffN[i] * nbParts); // on affiche le rsultat Console.WriteLine("Impt payer : {0} euros", impots);

}

lignes 7-9 : les valeurs numriques sont suffixes par M (Money) pour qu'elles soient de type decimal. ligne 16 : Console.ReadLine() rend la chane C1 tape au clavier C1.Trim() enlve les espaces de dbut et fin de C1 - rend une chane C2 C2.ToLower() rend la chane C3 qui est la chane C2 transforme en minuscules. ligne 21 : le boolen marie reoit la valeur true ou false de la relation reponse=="o" ligne 29 : la chane tape au clavier est transforme en type int. Si la transformation choue, une exception est lance. ligne 30 : le boolen OK reoit la valeur true ou false de la relation nbEnfants>=0 lignes 55-56 : on ne peut crire simplement nbEnfants/2. Si nbEnfants tait gal 3, on aurait 3/2, une division entire qui donnerait 1 et non 1.5. Aussi, crit-on (decimal)nbEnfants pour rendre rel l'un des oprandes de la division et avoir ainsi une division entre rels.

Voici des exemples d'excution :Etes-vous mari(e) (O/N) ? o Nombre d'enfants : 2 Salaire annuel : 60000 Impt payer : 4282 euros Etes-vous mari(e) (O/N) ? oui Rponse incorrecte. Recommencez Etes-vous mari(e) (O/N) ? o Nombre d'enfants : trois Rponse incorrecte. Recommencez Nombre d'enfants : 3 Salaire annuel : 60000 euros Rponse incorrecte. Recommencez Salaire annuel : 60000 Impt payer : 2959 euros

1.7

Arguments du programme principal

Les bases du langage C#

32

La fonction principale Main peut admettre comme paramtre un tableau de chanes : String[] (ou string[]). Ce tableau contient les arguments de la ligne de commande utilise pour lancer l'application. Ainsi si on lance le programme P avec la commande (Dos) suivante : P arg0 arg1 argn et si la fonction Main est dclare comme suit :public static void Main(string[] args)

on aura args[0]="arg0", args[1]="arg1" Voici un exemple :1. 2. 3. 4. 5. 6. 7. 8. 9. 10. 11. 12. 13. using System; namespace Chap1 { class P10 { static void Main(string[] args) { // on liste les paramtres reus Console.WriteLine("Il y a " + args.Length + " arguments"); for (int i = 0; i < args.Length; i++) { Console.Out.WriteLine("arguments[" + i + "]=" + args[i]); } } } }

Pour passer des arguments au code excut, on procdera comme suit :

1 3 2

en [1] : clic droit sur le projet / Properties en [2] : onglet [Debug] en [3] : mettre les arguments

L'excution donne les rsultats suivants :1. 2. 3. 4. 5. Il y a 4 arguments arguments[0]=a0 arguments[1]=a1 arguments[2]=a2 arguments[3]=a3

On notera que la signaturepublic static void Main()

est valide si la fonction Main n'attend pas de paramtres.

1.8

Les numrations

Une numration est un type de donnes dont le domaine de valeurs est un ensemble de constantes entires. Considrons un programme qui a grer des mentions un examen. Il y en aurait cinq : Passable,AssezBien,Bien,TrsBien, Excellent. On pourrait alors dfinir une numration pour ces cinq constantes :

Les bases du langage C#

33

enum Mentions { Passable, AssezBien, Bien, TrsBien, Excellent };

De faon interne, ces cinq constantes sont codes par des entiers conscutifs commenant par 0 pour la premire constante, 1 pour la suivante, etc... Une variable peut tre dclare comme prenant ces valeurs dans l'numration :1. 2. // une variable qui prend ses valeurs dans l'numration Mentions Mentions maMention = Mentions.Passable;

On peut comparer une variable aux diffrentes valeurs possibles de l'numration :1. 2. 3. if (maMention == Mentions.Passable) { Console.WriteLine("Peut mieux faire"); }

On peut obtenir toutes les valeurs de l'numration :1. 2. 3. 4. // liste des mentions sous forme de chanes foreach (Mentions m in Enum.GetValues(maMention.GetType())) { Console.WriteLine(m);

}

De la mme faon que le type simple int est quivalent la structure System.Int32, le type simple enum est quivalent la structure System.Enum. Cette structure a une mthode statique GetValues qui permet d'obtenir toutes les valeurs d'un type numr que l'on passe en paramtre. Celui-ci doit tre un objet de type Type qui est une classe d'informations sur le type d'une donne. Le type d'une variable v est obtenu par v.GetType(). Le type d'un type T est obtenu par typeof(T). Donc ici maMention.GetType() donne l'objet Type de l'numration Mentions et Enum.GetValues(maMention.GetType()) la liste des valeurs de l'numration Mentions. Si on crit maintenant1. 2. 3. 4. //liste des mentions sous forme d'entiers foreach (int m in Enum.GetValues(typeof(Mentions))) { Console.WriteLine(m); }

Ligne 2, la variable de boucle est de type entier. On obtient alors la liste des valeurs de l'numration sous forme d'entiers. L'objet de type System.Type correspondant au type de donnes Mentions est obtenu par typeof(Mentions). On aurait pu crire comme prcdemment, maMention.GetType(). Le programme suivant met en lumire ce qui vient d'tre crit :1. 2. 3. 4. 5. 6. 7. 8. 9. 10. 11. 12. 13. 14. 15. 16. 17. 18. 19. 20. 21. 22. 23. 24. 25. using System; namespace Chap1 { class P11 { enum Mentions { Passable, AssezBien, Bien, TrsBien, Excellent }; static void Main(string[] args) { // une variable qui prend ses valeurs dans l'numration Mentions Mentions maMention = Mentions.Passable; // affichage valeur variable Console.WriteLine("mention=" + maMention); // test avec valeur de l'numration if (maMention == Mentions.Passable) { Console.WriteLine("Peut mieux faire"); } // liste des mentions sous forme de chanes foreach (Mentions m in Enum.GetValues(maMention.GetType())) { Console.WriteLine(m); } //liste des mentions sous forme d'entiers foreach (int m in Enum.GetValues(typeof(Mentions))) { Console.WriteLine(m); } } } }

Les rsultats d'excution sont les suivants :1. mention=Passable

Les bases du langage C#

34

2. 3. 4. 5. 6. 7. 8. 9. 10. 11. 12.

Peut mieux faire Passable AssezBien Bien TrsBien Excellent 0 1 2 3 4

1.9

Passage de paramtres une fonction

Nous nous intressons ici au mode de passage des paramtres d'une fonction. Considrons la fonction statique suivante :1. 2. 3. 4. private static void ChangeInt(int a) { a = 30; Console.WriteLine("Paramtre formel a=" + a); }

Dans la dfinition de la fonction, ligne1, a est appel un paramtre formel. Il n'est l que pour les besoins de la dfinition de la fonction changeInt. Il aurait tout aussi bien pu s'appeler b. Considrons maintenant une utilisation de cette fonction :1. 2. 3. 4. 5. public static void Main() { int age = 20; ChangeInt(age); Console.WriteLine("Paramtre effectif age=" + age);

}

Ici dans l'instruction de la ligne 3, ChangeInt(age), age est le paramtre effectif qui va transmettre sa valeur au paramtre formel a. Nous nous intressons la faon dont un paramtre formel rcupre la valeur d'un paramtre effectif.

1.9.1

Passage par valeur

L'exemple suivant nous montre que les paramtres d'une fonction sont par dfaut passs par valeur, c'est dire que la valeur du paramtre effectif est recopie dans le paramtre formel correspondant. On a deux entits distinctes. Si la fonction modifie le paramtre formel, le paramtre effectif n'est lui en rien modifi.1. 2. 3. 4. 5. 6. 7. 8. 9. 10. 11. 12. 13. 14. 15. using System; namespace Chap1 { class P12 { public static void Main() { int age = 20; ChangeInt(age); Console.WriteLine("Paramtre effectif age=" + age); } private static void ChangeInt(int a) { a = 30; Console.WriteLine("Paramtre formel a=" + a); } } }

Les rsultats obtenus sont les suivants :1. 2. Paramtre formel a=30 Paramtre effectif age=20

La valeur 20 du paramtre effectif age a t recopie dans le paramtre formel a (ligne 10). Celui-ci a t ensuite modifi (ligne 11). Le paramtre effectif est lui rest inchang. Ce mode de passage convient aux paramtres d'entre d'une fonction.

1.9.2

Passage par rfrence

Les bases du langage C#

35

Dans un passage par rfrence, le paramtre effectif et le paramtre formel sont une seule et mme entit. Si la fonction modifie le paramtre formel, le paramtre effectif est lui aussi modifi. En C#, ils doivent tre tous deux prcds du mot cl ref : Voici un exemple :1. 2. 3. 4. 5. 6. 7. 8. 9. 10. 11. 12. 13. 14. 15. 16. using System; namespace Chap1 { class P12 { public static void Main() { // exemple 2 int age2 = 20; ChangeInt2(ref age2); Console.WriteLine("Paramtre effectif age2=" + age2); } private static void ChangeInt2(ref int a2) { a2 = 30; Console.WriteLine("Paramtre formel a2=" + a2); } } }

et les rsultats d'excution :1. 2. Paramtre formel a2=30 Paramtre effectif age2=30

Le paramtre effectif a suivi la modification du paramtre formel. Ce mode de passage convient aux paramtres de sortie d'une fonction.

1.9.3

Passage par rfrence avec le mot cl out

Considrons l'exemple prcdent dans lequel la variable age2 ne serait pas initialise avant l'appel la fonction changeInt :1. 2. 3. 4. 5. 6. 7. 8. 9. 10. 11. 12. 13. 14. 15. 16. using System; namespace Chap1 { class P12 { public static void Main() { // exemple 2 int age2; ChangeInt2(ref age2); Console.WriteLine("Paramtre effectif age2=" + age2); } private static void ChangeInt2(ref int a2) { a2 = 30; Console.WriteLine("Paramtre formel a2=" + a2); } } }

Lorsqu'on compile ce programme, on a une erreur :Use of unassigned local variable 'age2'

On peut contourner l'obstacle en affectant une valeur initiale age2. On peut aussi remplacer le mot cl ref par le mot cl out. On exprime alors que la paramtre est uniquement un paramtre de sortie et n'a donc pas besoin de valeur initiale :1. using System; 2. 3. namespace Chap1 { 4. class P12 { 5. public static void Main() { 6. // exemple 3 7. int age3; 8. ChangeInt3(out age3); 9. Console.WriteLine("Paramtre effectif age3=" + age3); 10. } 11. private static void ChangeInt3(out int a3) {

Les bases du langage C#

36

12. 13. 14. 15. } 16. }

}

a3 = 30; Console.WriteLine("Paramtre formel a3=" + a3);

Les rsultats de l'excution sont les suivants :1. 2. Paramtre formel a3=30 Paramtre effectif age3=30

Les bases du langage C#

37

22.1

Classes, Stuctures, InterfacesL' objet par l'exempleGnralits

2.1.1

Nous abordons maintenant, par l'exemple, la programmation objet. Un objet est une entit qui contient des donnes qui dfinissent son tat (on les appelle des champs, attributs, ...) et des fonctions (on les appelle des mthodes). Un objet est cr selon un modle qu'on appelle une classe :public class C1{ Type1 p1; Type2 p2; Type3 m3(){ } Type4 m4(){ } } // champ p1 // champ p2 // mthode m3 // mthode m4

A partir de la classe C1 prcdente, on peut crer de nombreux objets O1, O2, Tous auront les champs p1, p2, et les mthodes m3, m4, Mais ils auront des valeurs diffrentes pour leurs champs pi ayant ainsi chacun un tat qui leur est propre. Si o1 est un objet de type C1, o1.p1 dsigne la proprit p1 de o1 et o1.m1 la mthode m1 de O1. Considrons un premier modle d'objet : la classe Personne.

2.1.2

Cration du projet C#

Dans les exemples prcdents, nous n'avions dans un projet qu'un unique fichier source : Program.cs. A partir de maintenant, nous pourrons avoir plusieurs fichiers source dans un mme projet. Nous montrons comment procder.

2 1

5

3

4

En [1], crez un nouveau projet. En [2], choisissez une Application Console. En [3], laissez la valeur par dfaut. En [4], validez. En [5], le projet qui a t gnr. Le contenu de Program.cs est le suivant :1. 2. 3. 4. 5. 6. 7. 8. using using using using System; System.Collections.Generic; System.Linq; System.Text;

namespace ConsoleApplication1 { class Program { static void Main(string[] args) {

Classes, Stuctures, Interfaces

38

9. 10. } 11. }

}

Sauvegardons le projet cr : 3 2 4 5 6

1

En [1], l'option de sauvegarde. En [2], dsignez le dossier o sauvegarder le projet. En [3], donnez un nom au projet. En [5], indiquez que vous voulez crer une solution. Une solution est un ensemble de projets. En [4], donnez le nom de la solution. En [6], validez la sauvegarde.

1 2

En [1], le projet sauvegard. En [2], ajoutez un nouvel lment au projet. 4 1

2

3

En [1], indiquez que vous voulez ajouter une classe. En [2], le nom de la classe. En [3], validez les informations. En [4], le projet [01] a un nouveau fichier source Personne.cs :1. 2. 3. 4. 5. 6. using using using using System; System.Collections.Generic; System.Linq; System.Text;

namespace ConsoleApplication1 {

Classes, Stuctures, Interfaces

39

7. 8. 9.

class Personne { } }

On modifie l'espace de noms de chacun des fichiers source en Chap2 et on supprime l'importation des espaces de noms inutiles :1. 2. 3. 4. 5. 6. 1. 2. 3. 4. 5. 6. 7. 8. using System; namespace Chap2 { class Personne { } } using System; namespace Chap2 { class Program { static void Main(string[] args) { } } }

2.1.31. 2. 3. 4. 5. 6. 7. 8. 9. 10. 11. 12. 13. 14. 15. 16. 17. 18. 19. 20. 21. 22. 23.

Dfinition de la classe Personneusing System; namespace Chap2 { public class Personne { // attributs private string prenom; private string nom; private int age; // mthode public void Initialise(string P, string N, int age) { this.prenom = P; this.nom = N; this.age = age; } // mthode public void Identifie() { Console.WriteLine("[{0}, {1}, {2}]", prenom, nom, age); }

La dfinition de la classe Personne dans le fichier source [Personne.cs] sera la suivante :

} }

Nous avons ici la dfinition d'une classe, donc d'un type de donnes. Lorsqu'on va crer des variables de ce type, on les appellera des objets ou des instances de classe. Une classe est donc un moule partir duquel sont construits des objets. Les membres ou champs d'une classe peuvent tre des donnes (attributs), des mthodes (fonctions), des proprits. Les proprits sont des mthodes particulires servant connatre ou fixer la valeur d'attributs de l'objet. Ces champs peuvent tre accompagns de l'un des trois mots cls suivants :priv public protg

Un champ priv (private) n'est accessible que par les seules mthodes internes de la classe Un champ public (public) est accessible par toute mthode dfinie ou non au sein de la classe Un champ protg (protected) n'est accessible que par les seules mthodes internes de la classe ou d'un objet driv (voir ultrieurement le concept d'hritage).

En gnral, les donnes d'une classe sont dclares prives alors que ses mthodes et proprits sont dclares publiques. Cela signifie que l'utilisateur d'un objet (le programmeur)

n'aura pas accs directement aux donnes prives de l'objet pourra faire appel aux mthodes publiques de l'objet et notamment celles qui donneront accs ses donnes prives.

Classes, Stuctures, Interfaces

40

La syntaxe de dclaration d'une classe C est la suivante :public class C{ private donne ou mthode ou proprit prive; public donne ou mthode ou proprit publique; protected donne ou mthode ou proprit protge; }

L'ordre de dclaration des attributs private, protected et public est quelconque.

2.1.4

La mthode Initialise

Revenons notre classe Personne dclare comme :1. 2. 3. 4. 5. 6. 7. 8. 9. 10. 11. 12. 13. 14. 15. 16. 17. 18. 19. 20. 21. 22. 23. using System; namespace Chap2 { public class Personne { // attributs private string prenom; private string nom; private int age; // mthode public void Initialise(string p, string n, int age) { this.prenom = p; this.nom = n; this.age = age; } // mthode public void Identifie() { Console.WriteLine("[{0}, {1}, {2}]", prenom, nom, age); }

} }

Quel est le rle de la mthode Initialise ? Parce que nom, prenom et age sont des donnes prives de la classe Personne, les instructions :Personne p1; p1.prenom="Jean"; p1.nom="Dupont"; p1.age=30;

sont illgales. Il nous faut initialiser un objet de type Personne via une mthode publique. C'est le rle de la mthode Initialise. On crira :Personne p1; p1.Initialise("Jean","Dupont",30);

L'criture p1.Initialise est lgale car Initialise est d'accs public.

2.1.5

L'oprateur new

La squence d'instructionsPersonne p1; p1.Initialise("Jean","Dupont",30);

est incorrecte. L'instructionPersonne p1;

dclare p1 comme une rfrence un objet de type Personne. Cet objet n'existe pas encore et donc p1 n'est pas initialis. C'est comme si on crivait :Personne p1=null;

Classes, Stuctures, Interfaces

41

o on indique explicitement avec le mot cl null que la variable p1 ne rfrence encore aucun objet. Lorsqu'on crit ensuitep1.Initialise("Jean","Dupont",30);

on fait appel la mthode Initialise de l'objet rfrenc par p1. Or cet objet n'existe pas encore et le compilateur signalera l'erreur. Pour que p1 rfrence un objet, il faut crire :Personne p1=new Personne();

Cela a pour effet de crer un objet de type Personne non encore initialis : les attributs nom et prenom qui sont des rfrences d'objets de type String auront la valeur null, et age la valeur 0. Il y a donc une initialisation par dfaut. Maintenant que p1 rfrence un objet, l'instruction d'initialisation de cet objetp1.Initialise("Jean","Dupont",30);

est valide.

2.1.6

Le mot cl this

Regardons le code de la mthode initialise :1. 2. 3. 4. 5. public void Initialise(string p, string n, int age) { this.prenom = p; this.nom = n; this.age = age;

}

L'instruction this.prenom=p signifie que l'attribut prenom de l'objet courant (this) reoit la valeur p. Le mot cl this dsigne l'objet courant : celui dans lequel se trouve la mthode excute. Comment le connat-on ? Regardons comment se fait l'initialisation de l'objet rfrenc par p1 dans le programme appelant :p1.Initialise("Jean","Dupont",30);

C'est la mthode Initialise de l'objet p1 qui est appele. Lorsque dans cette mthode, on rfrence l'objet this, on rfrence en fait l'objet p1. La mthode Initialise aurait aussi pu tre crite comme suit :1. 2. 3. 4. 5. public void Initialise(string p, string n, int age) { prenom = p; nom = n; this.age = age; }

Lorsqu'une mthode d'un objet rfrence un attribut A de cet objet, l'criture this.A est implicite. On doit l'utiliser explicitement lorsqu'il y a conflit d'identificateurs. C'est le cas de l'instruction :this.age=age;

o age dsigne un attribut de l'objet courant ainsi que le paramtre age reu par la mthode. Il faut alors lever l'ambigut en dsignant l'attribut age par this.age.

2.1.7

Un programme de test

Voici un court programme de test. Celui-ci est crit dans le fichier source [Program.cs] :1. 2. 3. 4. 5. 6. 7. 8. 9. 10. 11. using System; namespace Chap2 { class P01 { static void Main() { Personne p1 = new Personne(); p1.Initialise("Jean", "Dupont", 30); p1.Identifie(); } } }

Classes, Stuctures, Interfaces

42

Avant d'excuter le projet [01], il peut tre ncessaire de prciser le fichier source excuter :

1

Dans les proprits du projet [01], on indique en [1] la classe excuter. Les rsultats obtenus l'excution sont les suivants :[Jean, Dupont, 30]

2.1.8

Une autre mthode Initialise

Considrons toujours la classe Personne et rajoutons-lui la mthode suivante :1. 2. 3. 4. 5. public void Initialise(Personne p) { prenom = p.prenom; nom = p.nom; age = p.age;

}

On a maintenant deux mthodes portant le nom Initialise : c'est lgal tant qu'elles admettent des paramtres diffrents. C'est le cas ici. Le paramtre est maintenant une rfrence p une personne. Les attributs de la personne p sont alors affects l'objet courant (this). On remarquera que la mthode Initialise a un accs direct aux attributs de l'objet p bien que ceux-ci soient de type private. C'est toujours vrai : un objet o1 d'une classe C a toujours accs aux attributs des objets de la mme classe C. Voici un test de la nouvelle classe Personne :1. 2. 3. 4. 5. 6. 7. 8. 9. 10. 11. 12. 13. 14. using System; namespace Chap2 { class Program { static void Main() { Personne p1 = new Personne(); p1.Initialise("Jean", "Dupont", 30); p1.Identifie(); Personne p2 = new Personne(); p2.Initialise(p1); p2.Identifie(); } } }

et ses rsultats :1. 2. [Jean, Dupont, 30] [Jean, Dupont, 30]

2.1.9

Constructeurs de la classe Personne

Un constructeur est une mthode qui porte le nom de la classe et qui est appele lors de la cration de l'objet. On s'en sert gnralement pour l'initialiser. C'est une mthode qui peut accepter des arguments mais qui ne rend aucun rsultat. Son prototype ou sa dfinition ne sont prcds d'aucun type (pas mme void). Si une classe C a un constructeur acceptant n arguments argi, la dclaration et l'initialisation d'un objet de cette classe pourra se faire sous la forme :

Classes, Stuctures, Interfaces

43

C objet =new C(arg1,arg2, ... argn); ou C objet; objet=new C(arg1,arg2, ... argn);

Lorsqu'une classe C a un ou plusieurs constructeurs, l'un de ces constructeurs doit tre obligatoirement utilis pour crer un objet de cette classe. Si une classe C n'a aucun constructeur, elle en a un par dfaut qui est le constructeur sans paramtres : public C(). Les attributs de l'objet sont alors initialiss avec des valeurs par dfaut. C'est ce qui s'est pass lorsque dans les programmes prcdents, o on avait crit :Personne p1; p1=new Personne();

Crons deux constructeurs notre classe Personne :1. 2. 3. 4. 5. 6. 7. 8. 9. 10. 11. 12. 13. 14. 15. 16. 17. 18. 19. 20. 21. 22. 23. 24. 25. 26. 27. 28. 29. 30. 31. 32. 33. using System; namespace Chap2 { public class Personne { // attributs private string prenom; private string nom; private int age; // constructeurs public Personne(String p, String n, int age) { Initialise(p, n, age); } public Personne(Personne P) { Initialise(P); } // mthode public void Initialise(string p, string n, int age) { } ... public void Initialise(Personne p) { } // mthode public void Identifie() { Console.WriteLine("[{0}, {1}, {2}]", prenom, nom, age); } } }

...

Nos deux constructeurs se contentent de faire appel aux mthodes Initialise tudies prcdemment. On rappelle que lorsque dans un constructeur, on trouve la notation Initialise(p) par exemple, le compilateur traduit par this.Initialise(p). Dans le constructeur, la mthode Initialise est donc appele pour travailler sur l'objet rfrenc par this, c'est dire l'objet courant, celui qui est en cours de construction. Voici un court programme de test :1. 2. 3. 4. 5. 6. 7. 8. 9. 10. 11. 12. 13. using System; namespace Chap2 { class Program { static void Main() { Personne p1 = new Personne("Jean", "Dupont", 30); p1.Identifie(); Personne p2 = new Personne(p1); p2.Identifie(); } } }

Classes, Stuctures, Interfaces

44

et les rsultats obtenus :[Jean, Dupont, 30] [Jean, Dupont, 30]

2.1.10

Les rfrences d'objets

Nous utilisons toujours la mme classe Personne. Le programme de test devient le suivant :1. using System; 2. 3. namespace Chap2 { 4. class Program2 { 5. static void Main() { 6. // p1 7. Personne p1 = new Personne("Jean", "Dupont", 30); 8. Console.Write("p1="); p1.Identifie(); 9. // p2 rfrence le mme objet que p1 10. Personne p2 = p1; 11. Console.Write("p2="); p2.Identifie(); 12. // p3 rfrence un objet qui sera une copie de l'objet rfrenc par p1 13. Personne p3 = new Personne(p1); 14. Console.Write("p3="); p3.Identifie(); 15. // on change l'tat de l'objet rfrenc par p1 16. p1.Initialise("Micheline", "Benot", 67); 17. Console.Write("p1="); p1.Identifie(); 18. // comme p2=p1, l'objet rfrenc par p2 a du changer d'tat 19. Console.Write("p2="); p2.Identifie(); 20. // comme p3 ne rfrence pas le mme objet que p1, l'objet rfrenc par p3 n'a pas du changer 21. Console.Write("p3="); p3.Identifie(); 22. } 23. } 24. }

Les rsultats obtenus sont les suivants :1. 2. 3. 4. 5. 6. p1=[Jean, Dupont, 30] p2=[Jean, Dupont, 30] p3=[Jean, Dupont, 30] p1=[Micheline, Benot, 67] p2=[Micheline, Benot, 67] p3=[Jean, Dupont, 30]

Lorsqu'on dclare la variable p1 parPersonne p1=new Personne("Jean","Dupont",30);

p1 rfrence l'objet Personne("Jean","Dupont",30) mais n'est pas l'objet lui-mme. En C, on dirait que c'est un pointeur, c.a.d. l'adresse de l'objet cr. Si on crit ensuite :p1=null;

Ce n'est pas l'objet Personne("Jean","Dupont",30) qui est modifi, c'est la rfrence p1 qui change de valeur. L'objet Personne("Jean","Dupont",30) sera "perdu" s'il n'est rfrenc par aucune autre variable. Lorsqu'on crit :Personne p2=p1;

on initialise le pointeur p2 : il "pointe" sur le mme objet (il dsigne le mme objet) que le pointeur p1. Ainsi si on modifie l'objet "point" (ou rfrenc) par p1, on modifie aussi celui rfrenc par p2. Lorsqu'on crit :Personne p3=new Personne(p1);

il y a cration d'un nouvel objet Personne. Ce nouvel objet sera rfrenc par p3. Si on modifie l'objet "point" (ou rfrenc) par p1, on ne modifie en rien celui rfrenc par p3. C'est ce que montrent les rsultats obtenus.

Classes, Stuctures, Interfaces

45

2.1.11

Passage de paramtres de type rfrence d'objet

Dans le chapitre prcdent, nous avons tudi les modes de passage des paramtres d'une fonction lorsque ceux-ci reprsentaient un type C# simple reprsent par une structure .NET. Voyons ce qui se passe lorsque la paramtre est une rfrence d'objet :1. 2. 3. 4. 5. 6. 7. 8. using System; using System.Text;

namespace Chap1 { class P12 { public static void Main() { // exemple 4 StringBuilder sb0 = new StringBuilder("essai0"), sb1 = new StringBuilder("essai1"), sb2 = new StringBuilder("essai2"), sb3; 9. Console.WriteLine("Dans fonction appelante avant appel : sb0={0}, sb1={1}, sb2={2}", sb0,sb1, sb2); 10. ChangeStringBuilder(sb0, sb1, ref sb2, out sb3); 11. Console.WriteLine("Dans fonction appelante aprs appel : sb0={0}, sb1={1}, sb2={2}, sb3={3}", sb0, sb1, sb2, sb3); 12. 13. } 14. 15. private static void ChangeStringBuilder(StringBuilder sbf0, StringBuilder sbf1, ref StringBuilder sbf2, out StringBuilder sbf3) { 16. Console.WriteLine("Dbut fonction appele : sbf0={0}, sbf1={1}, sbf2={2}", sbf0,sbf1, sbf2); 17. sbf0.Append("*****"); 18. sbf1 = new StringBuilder("essai1*****"); 19. sbf2 = new StringBuilder("essai2*****"); 20. sbf3 = new StringBuilder("essai3*****"); 21. Console.WriteLine("Fin fonction appele : sbf0={0}, sbf1={1}, sbf2={2}, sbf3={3}", sbf0, sbf1, sbf2, sbf3); 22. } 23. } 24. }

ligne 8 : dfinit 3 objets de type StringBuilder. Un objet StringBuilder est proche d'un objet string. Lorsqu'on manipule un objet string, on obtient en retour un nouvel objet string. Ainsi dans la squence de code :string s="une chane"; s=s.ToUpperCase();

1. 2.

La ligne 1 cre un objet string en mmoire et s est son adresse. Ligne 2, s.ToUpperCase() cre un autre objet string en mmoire. Ainsi entre les lignes 1 et 2, s a chang de valeur (il pointe sur le nouvel objet). La classe StringBuilder elle, permet de transformer une chane sans qu'un second objet soit cr. C'est l'exemple donn plus haut :

ligne 8 : 4 rfrences [sb0, sb1, sb2, sb3] des objets de type StringBuilder ligne 10 : sont passes la mthode ChangeStringBuilder avec des modes diffrents : sb0, sb1 avec le mode par dfaut, sb2 avec le mot cl ref, sb3 avec le mot cl out. lignes 15-22 : une mthode qui a les paramtres formels [sbf0, sbf1, sbf2, sbf3]. Les relations entre paramtres formels formels sbfi et effectifs sbi sont les suivantes : sbf0 et sb0 sont, au dmarrage de la mthode, deux rfrences distinctes qui pointent sur le mme objet (passage par valeur des adresses) idem pour sbf1 et sb1 sbf2 et sb2 sont, au dmarrage de la mthode, une mme rfrence sur le mme objet (mot cl ref) sbf3 et sb3 sont, aprs excution de la mthode, une mme rfrence sur le mme objet (mot cl out)

Les rsultats obtenus sont les suivants :1. 2. 3. 4. Dans fonction appelante avant appel : sb0=essai0, sb1=essai1, sb2=essai2 Dbut fonction appele : sbf0=essai0, sbf1=essai1, sbf2=essai2 Fin fonction appele : sbf0=essai0*****, sbf1=essai1*****, sbf2=essai2*****, sbf3=essai3***** Dans fonction appelante aprs appel : sb0=essai0*****, sb1=essai1, sb2=essai2*****, sb3=essai3*****

Explications :

sb0 et sbf0 sont deux rfrences distinctes sur le mme objet. Celui-ci a t modifi via sbf0 - ligne 3. Cette modification peut tre vue via sb0 - ligne 4.

Classes, Stuctures, Interfaces

46

sb1 et sbf1 sont deux rfrences distinctes sur le mme objet. sbf1 voit sa valeur modifie dans la mthode et pointe dsormais sur un nouvel objet - ligne 3. Cela ne change en rien la valeur de sb1 qui continue pointer sur le mme objet ligne 4. sb2 et sbf2 sont une mme rfrence sur le mme objet. sbf2 voit sa valeur modifie dans la mthode et pointe dsormais sur un nouvel objet - ligne 3. Comme sbf2 et sb2 sont une seule et mme entit, la valeur de sb2 a t galement modifie et sb2 pointe sur le mme objet que sbf2 - lignes 3 et 4. avant appel de la mthode, sb3 n'avait pas de valeur. Aprs la mthode, sb3 reoit la valeur de sbf3. On a donc deux rfrences sur le mme objet - lignes 3 et 4

2.1.12

Les objets temporaires

Dans une expression, on peut faire appel explicitement au constructeur d'un objet : celui-ci est construit, mais nous n'y avons pas accs (pour le modifier par exemple). Cet objet temporaire est construit pour les besoins d'valuation de l'expression puis abandonn. L'espace mmoire qu'il occupait sera automatiquement rcupr ultrieurement par un programme appel "ramassemiettes" dont le rle est de rcuprer l'espace mmoire occup par des objets qui ne sont plus rfrencs par des donnes du programme. Considrons le nouveau programme de test suivant :1. 2. 3. 4. 5. 6. 7. 8. 9. using System; namespace Chap2 { class Program { static void Main() { new Personne(new Personne("Jean", "Dupont", 30)).Identifie(); } } }

et modifions les constructeurs de la classe Personne afin qu'ils affichent un message :1. 2. 3. 4. 5. 6. 7. 8. 9. // constructeurs public Personne(String p, String n, int age) { Console.WriteLine("Constructeur Personne(string, string, int)"); Initialise(p, n, age); } public Personne(Personne P) { Console.Out.WriteLine("Constructeur Personne(Personne)"); Initialise(P); }

Nous obtenons les rsultats suivants :1. 2. 3. Constructeur Personne(string, string, int) Constructeur Personne(Personne) [Jean, Dupont, 30]

montrant la construction successive des deux objets temporaires.

2.1.13

Mthodes de lecture et d'criture des attributs privs

Nous rajoutons la classe Personne les mthodes ncessaires pour lire ou modifier l'tat des attributs des objets :1. using System; 2. 3. namespace Chap2 { 4. public class Personne { 5. // attributs 6. private string prenom; 7. private string nom; 8. private int age; 9. 10. // constructeurs 11. public Personne(String p, String n, int age) { 12. Console.WriteLine("Constructeur Personne(string, string, int)"); 13. Initialise(p, n, age); 14. } 15. public Personne(Personne p) {

Classes, Stuctures, Interfaces

47

16. 17. 18. 19. 20. 21. 22. 23. 24. 25. 26. 27. 28. 29. 30. 31. 32. 33. 34. 35. 36. 37. 38. 39. 40. 41. 42. 43. 44. 45. 46. 47. 48. 49. 50. 51. 52. 53. 54. 55. 56. 57. 58. 59. } 60. 61. }

}

Console.Out.WriteLine("Constructeur Personne(Personne)"); Initialise(p);

// mthode public void Initialise(string p, string n, int age) { this.prenom = p; this.nom = n; this.age = age; } public void Initialise(Personne p) { prenom = p.prenom; nom = p.nom; age = p.age; } // accesseurs public String GetPrenom() { return prenom; } public String GetNom() { return nom; } public int GetAge() { return age; } //modifieurs public void SetPrenom(String P) { this.prenom = P; } public void SetNom(String N) { this.nom = N; } public void SetAge(int age) { this.age = age; } // mthode public void Identifie() { Console.WriteLine("[{0}, {1}, {2}]", prenom, nom, age); }

Nous testons la nouvelle classe avec le programme suivant :1. 2. 3. 4. 5. 6. 7. 8. 9. 10. 11. 12. using System; namespace Chap2 { class Program { static void Main(string[] args) { Personne p = new Personne("Jean", "Michelin", 34); Console.Out.WriteLine("p=(" + p.GetPrenom() + "," + p.GetNom() + "," + p.GetAge() + ")"); p.SetAge(56); Console.Out.WriteLine("p=(" + p.GetPrenom() + "," + p.GetNom() + "," + p.GetAge() + ")"); } } }

et nous obtenons les rsultats :1. 2. 3. Constructeur Personne(string, string, int) p=(Jean,Michelin,34) p=(Jean,Michelin,56)

2.1.14

Les proprits

Il existe une autre faon d'avoir accs aux attributs d'une classe, c'est de crer des proprits. Celles-ci nous permettent de manipuler des attributs privs comme s'ils taient publics.

Classes, Stuctures, Interfaces

48

Considrons la classe Personne suivante o les accesseurs et modifieurs prcdents ont t remplacs par des proprits en lecture et criture :1. 2. 3. 4. 5. 6. 7. 8. 9. 10. 11. 12. 13. 14. 15. 16. 17. 18. 19. 20. 21. 22. 23. 24. 25. 26. 27. 28. 29. 30. 31. 32. 33. 34. 35. 36. 37. 38. 39. 40. 41. 42. 43. 44. 45. 46. 47. 48. 49. 50. 51. 52. 53. 54. 55. 56. 57. 58. 59. 60. 61. 62. 63. 64. 65. 66. 67. 68. 69. 70. 71. 72. using System; namespace Chap2 { public class Personne { // attributs private string prenom; private string nom; private int age; // constructeurs public Personne(String p, String n, int age) { Initialise(p, n, age); } public Personne(Personne p) { Initialise(p); } // mthode public void Initialise(string p, string n, int age) { this.prenom = p; this.nom = n; this.age = age; } public void Initialise(Personne p) { prenom = p.prenom; nom = p.nom; age = p.age; } // proprits public string Prenom { get { return prenom; } set { // prnom valide ? if (value == null || value.Trim().Length == 0) { throw new Exception("prnom (" + value + ") invalide"); } else { prenom = value; } }//if }//prenom public string Nom { get { return nom; } set { // nom valide ? if (value == null || value.Trim().Length == 0) { throw new Exception("nom (" + value + ") invalide"); } else { nom = value; } }//if }//nom public int Age { get { return age; } set { // age valide ? if (value >= 0) { age = value; } else throw new Exception("ge (" + value + ") invalide"); }//if }//age // mthode public void Identifie() { Console.WriteLine("[{0}, {1}, {2}]", prenom, nom, age); }

} }

Classes, Stuctures, Interfaces

49

Une proprit permet de lire (get) ou de fixer (set) la valeur d'un attribut. Une proprit est dclare comme suit : public Type Proprit{ get {...} set {...} } o Type doit tre le type de l'attribut gr par la proprit. Elle peut avoir deux mthodes appeles get et set. La mthode get est habituellement charge de rendre la valeur de l'attribut qu'elle gre (elle pourrait rendre autre chose, rien ne l'empche). La mthode set reoit un paramtre appel value qu'elle affecte normalement l'attribut qu'elle gre. Elle peut en profiter pour faire des vrifications sur la validit de la valeur reue et ventuellement lancer un exception si la valeur se rvle invalide. C'est ce qui est fait ici. Comment ces mthodes get et set sont-elles appeles ? Considrons le programme de test suivant :1. 2. 3. 4. 5. 6. 7. 8. 9. 10. 11. 12. 13. 14. 15. 16. 17. using System; namespace Chap2 { class Program { static void Main(string[] args) { Personne p = new Personne("Jean", "Michelin", 34); Console.Out.WriteLine("p=(" + p.Prenom + "," + p.Nom + "," + p.Age + ")"); p.Age = 56; Console.Out.WriteLine("p=(" + p.Prenom + "," + p.Nom + "," + p.Age + ")"); try { p.Age = -4; } catch (Exception ex) { Console.Error.WriteLine(ex.Message); }//try-catch } } }

Dans l'instructionConsole.Out.WriteLine("p=(" + p.Prenom + "," + p.Nom + "," + p.Age + ")");

on cherche avoir les valeurs des proprits Prenom, Nom et Age de la personne p. C'est la mthode get de ces proprits qui est alors appele et qui rend la valeur de l'attribut qu'elles grent. Dans l'instruction on veut fixer la valeur de la proprit Age. C'est alors la mthode set de cette proprit qui est alors appele. Elle recevra 56 dans son paramtre value. Une proprit P d'une classe C qui ne dfinirait que la mthode get est dite en lecture seule. Si c est un objet de classe C, l'opration c.P=valeur sera alors refuse par le compilateur. L'excution du programme de test prcdent donne les rsultats suivants :1. 2. 3. p=(Jean,Michelin,34) p=(Jean,Michelin,56) ge (-4) invalide p.Age=56;

Les proprits nous permettent donc de manipuler des attributs privs comme s'ils taient publics. Une autre caractristique des proprits est qu'elles peuvent tre utilises conjointement avec un constructeur selon la syntaxe suivante :Classe objet=new Classe (...) {Proprit1=val1, Proprit2=val2, ...}

Cette syntaxe est quivalente au code suivant :1. 2. 3. 4. Classe objet=new Classe(...); objet.Proprit1=val1; objet.Proprit2=val2; ...

L'ordre des proprits n'importe pas. Voici un exemple.

Classes, Stuctures, Interfaces

50

La classe Personne se voit ajouter un nouveau constructeur sans paramtres :1. 2. } public Personne() {

Le constructeur n'initialise pas les membres de l'objet. C'est ce qu'on appelle le constructeur par dfaut. C'est lui qui est utilis lorsque la classe ne dfinit aucun constructeur. Le code suivant cre et initialise (ligne 6) une nouvelle Personne avec la syntaxe prsente prcdemment :1. 2. 3. 4. 5. 6. 7. 8. 9. 10. using System; namespace Chap2 { class Program { static void Main(string[] args) { Personne p2 = new Personne { Age = 7, Prenom = "Arthur", Nom = "Martin" }; Console.WriteLine("p2=({0},{1},{2})", p2.Prenom, p2.Nom, p2.Age); } } }

Ligne 6 ci-dessus, c'est le constructeur sans paramtres Personne() qui est utilis. Dans ce cas particulier, on aurait pu aussi crirePersonne p2 = new Personne() { Age = 7, Prenom = "Arthur", Nom = "Martin" };

mais les parenthses du constructeur Personne() sans paramtres ne sont pas obligatoires dans cette syntaxe. Les rsultats de l'excution sont les suivants :p2=(Arthur,Martin,7)

Dans beaucoup de cas, les mthodes get et set d'une proprit se contentent de lire et crire un champ priv sans autre traitement. On peut alors, dans ce scnario, utiliser une proprit automatique dclare comme suit : public Type Proprit{ get ; set ; } Le champ priv associ la proprit n'est pas dclar. Il est automatiquement gnr par le compilateur. On y accde que via sa proprit. Ainsi, au lieu d'crire :private string prenom; ... // proprit associe public string Prenom { get { return prenom; } set { // prnom valide ? if (value == null || value.Trim().Length == 0) { throw new Exception("prnom (" + value + ") invalide"); } else { prenom = value; } }//if }//prenom

on pourra crire :public string Prenom {get; set;}

sans dclarer le champ priv prenom. La diffrence entre les deux proprits prcdentes est que la premire vrifie la validit du prnom dans le set, alors que la deuxime ne fait aucune vrification. Utiliser la proprit automatique Prenom revient dclarer un champ Prenom public :public string Prenom;

On peut se demander s'il y a une diffrence entre les deux dclarations. Dclarer public un champ d'une classe est dconseill. Cela rompt avec le concept d'encapsulation de l'tat d'un objet, tat qui doit tre priv et expos par des mthodes publiques.

Classes, Stuctures, Interfaces

51

Si la proprit automatique est dclare virtuelle, elle peut alors tre redfinie dans une classe fille :1. 2. 3. 1. 2. 3. class Class1 { public virtual string Prop { get; set; } } class Class2 : Class1 { public override string Prop { get { return base.Prop; } set {... } } }

Ligne 2 ci-dessus, la classe fille Class2 peut mettre dans le set, du code vrifiant la validit de la valeur affecte la proprit automatique base.Prop de la classe mre Class1.

2.1.15

Les mthodes et attributs de classe

Supposons qu'on veuille compter le nombre d'objets Personne cres dans une application. On peut soi-mme grer un compteur mais on risque d'oublier les objets temporaires qui sont crs ici ou l. Il semblerait plus sr d'inclure dans les constructeurs de la classe Personne, une instruction incrmentant un compteur. Le problme est de passer une rfrence de ce compteur afin que le constructeur puisse l'incrmenter : il faut leur passer un nouveau paramtre. On peut aussi inclure le compteur dans la dfinition de la classe. Comme c'est un attribut de la classe elle-mme et non celui d'une instance particulire de cette classe, on le dclare diffremment avec le mot cl static :private static long nbPersonnes;

Pour le rfrencer, on crit Personne.nbPersonnes pour montrer que c'est un attribut de la classe Personne elle-mme. Ici, nous avons cr un attribut priv auquel on n'aura pas accs directement en-dehors de la classe. On cre donc une proprit publique pour donner accs l'attribut de classe nbPersonnes. Pour rendre la valeur de nbPersonnes la mthode get de cette proprit n'a pas besoin d'un objet Personne particulier : en effet nbPersonnes est l'attribut de toute une classe. Aussi a-t-on besoin d'une proprit dclare elleaussi static :1. 2. 3. public static long NbPersonnes { get { return nbPersonnes; }

}

qui de l'extrieur sera appele avec la syntaxe Personne.NbPersonnes. Voici un exemple. La classe Personne devient la suivante :1. 2. 3. 4. 5. 6. 7. 8. 9. 10. 11. 12. 13. 14. 15. 16. 17. 18. 19. 20. 21. 22. 23. 24. 25. 26. 27. 28. using System; namespace Chap2 { public class Personne { // attributs de classe private static long nbPersonnes; public static long NbPersonnes { get { return nbPersonnes; } } // attributs d'instance private string prenom; private string nom; private int age; // constructeurs public Personne(String p, String n, int age) { Initialise(p, n, age); nbPersonnes++; } public Personne(Personne p) { Initialise(p); nbPersonnes++; } ... }

Classes, Stuctures, Interfaces

52

Lignes 20 et 24, les constructeurs incrmentent le champ statique de la ligne 7. Avec le programme suivant :1. 2. 3. 4. 5. 6. 7. 8. 9. 10. 11. 12. using System; namespace Chap2 { class Program { static void Main(string[] args) { Personne p1 = new Personne("Jean", "Dupont", 30); Personne p2 = new Personne(p1); new Personne(p1); Console.WriteLine("Nombre de personnes cres : " + Personne.NbPersonnes); } } }

on obtient les rsultats suivants :Nombre de personnes cres : 3

2.1.16

Un tableau de personnes

Un objet est une donne comme une autre et ce titre plusieurs objets peuvent tre rassembls dans un tableau :1. 2. 3. 4. 5. 6. 7. 8. 9. 10. 11. 12. 13. 14. 15. 16. 17. using System; namespace Chap2 { class Program { static void Main(string[] args) { // un tableau de personnes Personne[] amis = new Personne[3]; amis[0] = new Personne("Jean", "Dupont", 30); amis[1] = new Personne("Sylvie", "Vartan", 52); amis[2] = new Personne("Neil", "Armstrong", 66); // affichage foreach (Personne ami in amis) { ami.Identifie(); } } } }

ligne 7 : cre un tableau de 3 lments de type Personne. Ces 3 lments sont initialiss ici avec la valeur null, c.a.d. qu'ils ne rfrencent aucun objet. De nouveau, par abus de langage, on parle de tableau d'objets alors que ce n'est qu'un tableau de rfrences d'objets. La cration du tableau d'objets, qui est un objet lui-mme (prsence de new) ne cre aucun objet du type de ses lments : il faut le faire ensuite. lignes 8-10 : cration des 3 objets de type Personne lignes 12-14 : affichage du contenu du tableau amis

On obtient les rsultats suivants :1. 2. 3. [Jean, Dupont, 30] [Sylvie, Vartan, 52] [Neil, Armstrong, 66]

2.22.2.1

L'hritage par l'exempleGnralits

Nous abordons ici la notion d'hritage. Le but de l'hritage est de "personnaliser" une classe existante pour qu'elle satisfasse nos besoins. Supposons qu'on veuille crer une classe Enseignant : un enseignant est une personne particulire. Il a des attributs qu'une autre personne n'aura pas : la matire qu'il enseigne par exemple. Mais il a aussi les attributs de toute personne : prnom, nom et ge. Un enseignant fait donc pleinement partie de la classe Personne mais a des attributs supplmentaires. Plutt que d'crire une

Classes, Stuctures, Interfaces

53

classe Enseignant partir de rien, on prfrerait reprendre l'acquis de la classe Personne qu'on adapterait au caractre particulier des enseignants. C'est le concept d'hritage qui nous permet cela. Pour exprimer que la classe Enseignant hrite des proprits de la classe Personne, on crira :public class Enseignant : Personne

Personne est appele la classe parent (ou mre) et Enseignant la classe drive (ou fille). Un objet Enseignant a toutes les qualits d'un objet Personne : il a les mmes attributs et les mmes mthodes. Ces attributs et mthodes de la classe parent ne sont pas rptes dans la dfinition de la classe fille : on se contente d'indiquer les attributs et mthodes rajouts par la classe fille : Nous supposons que la classe Personne est dfinie comme suit :1. using System; 2. 3. namespace Chap2 { 4. public class Personne { 5. 6. // attributs de classe 7. private static long nbPersonnes; 8. public static long NbPersonnes { 9. get { return nbPersonnes; } 10. } 11. 12. // attributs d'instance 13. private string prenom; 14