Les bases du langage C#

1.1 Introduction

Nous traitons C# d'abord comme un langage de programmation classique. Nous aborderons les classes ultérieurement. Dans un
programme on trouve deux choses :
- des données
- les instructions qui les manipulent
On s'efforce généralement de séparer les données des instructions :
               +--------------------+
               ¦      DONNEES       ¦
               +--------------------¦
               ¦                    ¦
               ¦      INSTRUCTIONS  ¦
               ¦                    ¦
               +--------------------+

1.2 Les données de C#

C# utilise les types de données suivants:
1. les nombres entiers
2. les nombres réels
3. les nombres décimaux
4. les caractères et chaînes de caractères
5. les booléens
6. les objets

1.2.1 Les types de données prédéfinis :

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 découvre qu'il y a deux types possibles pour un entier sur 32 bits : int et Int32. Le type int est un type C#.

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 désigne la structure .NET System.Int32. De même, 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 différence fondamentale suivante :
• une variable de type Structure se manipule via sa valeur
• une variable de type Classe se manipule via son adresse (référence en langage objet).
Une structure comme une classe sont des types complexes ayant des attributs et des méthodes. Ainsi, on pourra écrire :
string nomDuType=3.GetType().FullName;
Ci-dessus le littéral 3 est par défaut de type C# int, donc de type .NET System.Int32. Cette structure a une méthode GetType() qui
rend un objet encapsulant les caractéristiques du type de données System.Int32. Parmi celles-ci, la propriété FullName rend le nom
complet du type. On voit donc que le littéral 3 est un objet plus complexe qu'il n'y paraît à première vue.

Voici un programme illustrant ces différents points :

Voici un programme illustrant ces différents 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 : déclaration 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
paramètre, en remplaçant chaque notation {i} par la valeur de l'expression parami.
• ligne 22 : le type string désignant une classe et non une structure, on ne peut utiliser l'opérateur sizeof.

Voici le résultat de l'exécution :
1. Type de ent[2] : [System.Int32,4]
2. Type de fl[10,5]: [System.Single,4]
3. Type de d[-4,6] : [System.Double,8]
4. Type de s[essai] : [System.String]
5. Type de ui[5] : [System.UInt32,4]
6. Type de l[1000] : [System.Int64,8]
7. Type de ul[1001] : [System.UInt64,8]
8. Type de b[5] : [System.Byte,1]
9. Type de sh[-4] : [System.Int16,2]
10.Type de ush[10] : [System.UInt16,2]
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 données littérales
entier int (32 bits)
145, -7, 0xFF (hexadécimal)
entier long (64 bits) - suffixe L
100000L
réel double
134.789, -45E-18 (-45 10
-18
)
réel float (suffixe F)
134.789F, -45E-18F (-45 10
-18
)
réel decimal (suffixe M) 100000M
caractère char
'A', 'b'
chaîne de caractères string
"aujourd'hui" "c:\\chap1\\paragraph3" @"c:\chap1\paragraph3"
booléen bool
true, false
date
new DateTime(1954,10,13) (an, mois, jour) pour le 13/10/1954
On notera les deux chaînes littérales : "c:\\chap1\\paragraph3" et @"c:\chap1\paragraph3". Dans les chaînes littérales, le caractère \
est interprété. Ainsi "\n" représente la marque de fin de ligne et non la succession des deux caractères \ et n. Si on voulait cette
succession, il faudrait écrire "\\n" où la séquence \\ est interprétée comme un seul caractère \. On pourrait écrire aussi @"\n"
pour avoir le même résultat. La syntaxe @"texte" demande que texte soit pris exactement comme il est écrit. On appelle parfois cela
une chaîne verbatim.
1.2.3 Déclaration des données
1.2.3.1 Rôle des déclarations
Un programme manipule des données caractérisées par un nom et un type. Ces données sont stockées en mémoire. Au moment de
la traduction du programme, le compilateur affecte à chaque donnée un emplacement en mémoire caractérisé par une adresse et
une taille. Il le fait en s'aidant des déclarations faites par le programmeur.
Par ailleurs celles-ci permettent au compilateur de détecter des erreurs de programmation. Ainsi l'opération
   x=x*2;
sera déclarée erronée si x est une chaîne de caractères par exemple.
1.2.3.2 Déclaration des constantes

La syntaxe de déclaration d'une constante est la suivante :
const type nom=valeur;   //définit constante nom=valeur
Par exemple :
const float myPI=3.141592F;
Pourquoi déclarer des constantes ?
1. La lecture du programme sera plus aisée si l'on a donné à la constante un nom significatif :
const  float taux_tva=0.186F;
2. La modification du programme sera plus aisée si la "constante" vient à changer. Ainsi dans le cas précédent, si le taux de tva
passe à 33%, la seule modification à faire sera de modifier l'instruction définissant 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 Déclaration des variables
Une variable est identifiée par un nom et se rapporte à un type de données. C# fait la différence entre majuscules et minuscules.
Ainsi les variables FIN et fin sont différentes.

Les variables peuvent être initialisées lors de leur déclaration. La syntaxe de déclaration d'une ou plusieurs variables est :
 Identificateur_de_type variable1[=valeur1],variable2=[valeur2],...;
où Identificateur_de_type est un type prédéfini ou bien un type défini par le programmeur. De façon facultative, une variable peut être
initialisée en même temps que déclarée.
On peut également ne pas préciser 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 précis. La variable variablei a le type de la donnée valeuri qui lui est
affectée. L'initialisation est ici obligatoire afin que le compilateur puisse en déduire 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 donnée typée explicitement
• ligne 7 : (donnée).GetType().Name est le nom court de (donnée), (donnée).GetType().FullName est le nom complet de (donnée)
• ligne 8 : une donnée typée implicitement. Parce que 3 est de type int, j sera de type int.
• ligne 10 : une donnée typée implicitement. Parce que DateTime.Now est de type DateTime, aujourdhui sera de type DateTime.
A l'exécution, on obtient le résultat suivant :
1. Type de int i=2 : Int32,System.Int32
2. Type de var j=3 : Int32,System.Int32
3. Type de var aujourdhui : DateTime,System.DateTime
Une variable typée implicitement par le mot clé var ne peut pas ensuite changer de type. Ainsi, on ne pourrait écrire après la ligne 10
du code, la ligne :
var aujourdhui = "aujourd'hui";
Nous verrons ultérieurement qu'il est possible de déclarer un type "à la volée" 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 donnée de
type anonyme doit être affectée à une variable, la seule façon de déclarer celle-ci est d'utiliser le mot clé var.
1.2.4 Les conversions entre nombres et chaînes de caractères
nombre -> chaîne
 nombre.ToString()
chaine -> int
int.Parse(chaine) ou System.Int32.Parse
chaîne -> long
long.Parse(chaine) ou System.Int64.Parse
chaîne -> double
double.Parse(chaîne) ou System.Double.Parse(chaîne)
chaîne -> float
float.Parse(chaîne) ou System.Float.Parse(chaîne)
La conversion d'une chaîne vers un nombre peut échouer si la chaîne ne représente pas un nombre valide. Il y a alors génération
d'une erreur fatale appelée exception. Cette erreur peut être gérée par la clause try/catch suivante :

try{
appel de la fonction susceptible de générer l'exception
} catch (Exception e){
traiter l'exception e
}
instruction suivante
Si la fonction ne génère pas d'exception, on passe alors à instruction suivante, sinon on passe dans le corps de la clause catch puis à
instruction suivante. Nous reviendrons ultérieurement sur la gestion des exceptions. Voici un programme présentant quelques
techniques de conversion entre nombres et chaînes de caractères. Dans cet exemple la fonction affiche écrit à l'écran la valeur de son
paramètre. 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. // données
8. const int i = 10;
9. const long l = 100000;
10. const float f = 45.78F;
11. double d = -14.98;
12.
13. // nombre --> chaîne
14. affiche(i.ToString());
15. affiche(l.ToString());
16. affiche(f.ToString());
17. affiche(d.ToString());
18.
19. //boolean --> chaîne
20. const bool b = false;
21. affiche(b.ToString());
22.
23. // chaîne --> 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. // chaîne --> 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. // chaîne --> 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. // chaîne --> 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) {
 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 gère l'éventuelle exception  qui peut se produire. e.Message est le message d'erreur lié à l'exception e.
Les résultats obtenus sont les suivants :
1. S=10
2. S=100000
3. S=45,78
4. S=-14,98
5. S=False
6. S=10
7. S=Erreur : Input string was not in a correct format.
8. S=100
9. S=Erreur : Input string was not in a correct format.
10.S=100,87
11.S=Erreur : Input string was not in a correct format.
12.S=100,87
13.S=Erreur : Input string was not in a correct format.
On remarquera que les nombres réels sous forme de chaîne de caractères doivent utiliser la virgule et non le point décimal. Ainsi on
écrira
double d1=10.7;
mais
double d2=int.Parse("10,7");
1.2.5 Les tableaux de données
Un tableau C# est un objet permettant de rassembler sous un même identificateur des données de même type. Sa déclaration est la
suivante :
Type[] tableau[]=new Type[n]
n est le nombre de données que peut contenir le tableau. La syntaxe Tableau[i] désigne la donnée n° i où i appartient à l'intervalle
[0,n-1]. Toute référence à la donnée Tableau[i] où i n'appartient pas à l'intervalle [0,n-1] provoquera une exception. Un tableau peut
être initialisé en même temps que déclaré :
    int[] entiers=new int[] {0,10,20,30};
ou plus simplement :
    int[] entiers={0,10,20,30};
Les tableaux ont une propriété Length qui est le nombre d'éléments du tableau.
Un tableau à deux dimensions pourra être déclaré 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] désigne l'élément j de la ligne i de tableau. Le tableau à
deux dimensions peut lui aussi être initialisé en même temps qu'il est déclaré :
    double[,] réels=new double[,] { {0.5, 1.7}, {8.4, -6}};
ou plus simplement :
    double[,] réels={ {0.5, 1.7}, {8.4, -6}};
Le nombre d'éléments dans chacune des dimensions peut être obtenue par la méthode  GetLength(i)  où i=0 représente la
dimension correspondant au 1er indice, i=1 la dimension correspondant au 2ième indice, …
Le nombre total de dimensions est obtenu avec la propriété Rank, le nombre total d'éléments avec la propriété Length.
Un tableau de tableaux est déclaré comme suit :
Type[][] tableau=new Type[n][];
La déclaration ci-dessus crée un tableau de n lignes. Chaque élément tableau[i] est une référence de tableau à une dimension. Ces
références  tableau[i] ne sont pas initialisées lors de la déclaration ci-dessus. Elles ont pour valeur la référence null.
L'exemple ci-dessous illustre la création d'un tableau de tableaux :
1. // un tableau de tableaux
2. string[][] noms = new string[3][];
3. for (int i = 0; i < noms.Length; i++) {
4. noms[i] = new string[i + 1];
5. }//for
6. // initialisation
7. for (int i = 0; i < noms.Length; i++) {
8. for (int j = 0; j < noms[i].Length; j++) {
9. noms[i][j] = "nom" + i + j;
10. }//for j
11. }//for i
• ligne 2 : un tableau noms de 3 éléments de type string[][]. Chaque élément est un pointeur de tableau (une référence d'objet)
dont les éléments sont de type string[].
• lignes 3-5 : les 3 éléments du tableau noms sont initialisés. Chacun "pointe" désormais sur un tableau d'éléments de type
string[]. noms[i][j] est l'élément j du tableau de type string [] référencé par noms[i].
• ligne 9 : initialisation de l'éélment noms[i][j] à l'intérieur d'une double boucle. Ici noms[i] est un tableau de i+1 éléments.
Comme noms[i] est un tableau, noms[i].Length est son nombre d'éléments.
Voici un exemple regroupant les trois types de tableaux que nous venons de présenter :
12.using System;
13.
14.namespace Chap1 {
15. // tableaux
16.
17. using System;
18.
19. // classe de test
20. public class P02 {
21. public static void Main() {
22. // un tableau à 1 dimension initialisé
23. int[] entiers = new int[] { 0, 10, 20, 30 };
24. for (int i = 0; i < entiers.Length; i++) {
25. Console.Out.WriteLine("entiers[{0}]={1}", i, entiers[i]);
26. }//for
27.
28. // un tableau à 2 dimensions initialisé
29. double[,] réels = new double[,] { { 0.5, 1.7 }, { 8.4, -6 } };
30. for (int i = 0; i < réels.GetLength(0); i++) {
31. for (int j = 0; j < réels.GetLength(1); j++) {
32. Console.Out.WriteLine("réels[{0},{1}]={2}", i, j, réels[i, j]);
33. }//for j
34. }//for i
35.
36. // un tableau de tableaux
37. string[][] noms = new string[3][];
38. for (int i = 0; i < noms.Length; i++) {
39. noms[i] = new string[i + 1];
40. }//for
41. // initialisation
42. for (int i = 0; i < noms.Length; i++) {
43. for (int j = 0; j < noms[i].Length; j++) {
44. noms[i][j] = "nom" + i + j;
45. }//for j

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'exécution, nous obtenons les résultats suivants :
1. entiers[0]=0
2. entiers[1]=10
3. entiers[2]=20
4. entiers[3]=30
5. réels[0,0]=0,5
6. réels[0,1]=1,7
7. réels[1,0]=8,4
8. réels[1,1]=-6
9. noms[0][0]=nom00
10.noms[1][0]=nom10
11.noms[1][1]=nom11
12.noms[2][0]=nom20
13.noms[2][1]=nom21
14.noms[2][2]=nom22


Published By Drupal french Study