Les instructions de base de C# sont identiques syntaxiquement et sémantiquement à celles de Java, le lecteur qui connaît déjà le fonctionnement des instructions en Java peut ignorer ce chapitre et passer au chapitre suivant.
Les instructions de base de C# sont identiques syntaxiquement et sémantiquement
à celles de Java, le lecteur qui connaît déjà
le fonctionnement des instructions en Java peut ignorer ce chapitre.
1. instructions-bloc
Une large partie de la norme ANSI du langage C est reprise dans C#,
ainsi que la norme Delphi.
Dans ce sous-chapitre nous expliquons les instructions C# en les comparant
à pascal-delphi. Voici la syntaxe d'une instruction en C# :
1.2 instruction complète
:
Toutes les instructions se terminent donc en C# par un point-virgule
" ; "
1.3 bloc - instruction composée
:
L'élément syntaxique
est aussi dénommé bloc ou instruction composée
au sens de la visibilité des variables C# .
1.4 visibilité dans un bloc - instruction :
Exemple de déclarations licites et de visibilité dans 3 blocs instruction imbriqués :
int a, b = 12; { int x , y = 8 ; { int z =12; x = z ; a = x + 1 ; { int u = 1 ; y = u - b ; } } } |
![]() schéma d'imbrication des 3 blocs |
Nous examinons ci-dessous l'ensemble des instructions simples de C# .
C# est un langage de la famille des langages hybrides, il possède
la notion d'instruction d'affectation.
Le symbole d'affectation en C# est " = ", soit par exemple :
x = y ; // x doit obligatoirement être un identificateur de variable. |
L'affectation peut être utilisée dans une expression :
soient les instruction suivantes :
simulation d'exécution C# :
instruction | valeur de a | valeur de b |
int a , b = 56 ; | a = ??? | b = 56 |
a = (b = 12)+8 ; | a = 20 | b = 12 |
2.2 Raccourcis et opérateurs d'affectation
Soit op un opérateur appartenant à l'ensemble
des opérateurs suivant
{ +, - , * , / , % , << , >> , >>> , & , | , ^ }
Il est possible d'utiliser sur une seule variable le nouvel opérateur
op= construit avec l'opérateur op.
x op= y ; signifie en fait : x = x op y |
Il s'agit plus d'un raccourci syntaxique que d'un opérateur nouveau (sa traduction en MSIL est exactement la même : la traduction de a op= b devrait être plus courte en instructions p-code que a = a op b).
Ci-dessous le code MSIL engendré par i = i+5; et i +=5; est effectivement identique :
Code MSIL engendré pour l'instruction C# : i = i + 5 ;
IL_0077: ldloc.1Code MSIL engendré pour l'instruction C# : i += 5 ;
IL_0078: ldc.i4.5
IL_0079: add
IL_007a: stloc.1
IL_007b: ldloc.1soient les instruction suivantes :
IL_007c: ldc.i4.5
IL_007d: add
IL_007e: stloc.1
simulation d'exécution C# :
instruction | valeur de a | valeur de b |
int a , b = 56 ; | a = ??? | b = 56 |
a = -8 ; | a = -8 | b = 56 |
a += b ; | a = 48 | b = 56 |
b *= 3 ; | a = 48 | b = 168 |
Remarque :
1 . Code MSIL engendré pour l'nstruction C# : table[ f(i) ] = table[ f(i) ] +9IL_0086: ldloc.3 // adr(table)Au total, 12 instructions MSIL dont deux appels : call instance int32 exemple.WinForm::f(int32)
IL_0087: ldarg.0
IL_0088: ldloc.1 // adr(i)
IL_0089: call instance int32 exemple.WinForm::f(int32)
IL_008e: ldloc.3
IL_008f: ldarg.0
IL_0090: ldloc.1
IL_0091: call instance int32 exemple.WinForm::f(int32)
IL_0096: ldelem.i4
IL_0097: ldc.i4.s 9
IL_0099: add //table[ f(i) ] = table[ f(i) ] + 9 ;
IL_009a: stelem.i4
2 . Code MSIL engendré pour l'nstruction C# : table[ f(i) ] += 9
IL_009b: ldloc.3
IL_009c: dup
IL_009d: stloc.s CS$00000002$00000000
IL_009f: ldarg.0
IL_00a0: ldloc.1
IL_00a1: call instance int32 exemple.WinForm::f(int32)
IL_00a6: dup
IL_00a7: stloc.s CS$00000002$00000001
IL_00a9: ldloc.s CS$00000002$00000000
IL_00ab: ldloc.s CS$00000002$00000001
IL_00ad: ldelem.i4
IL_00ae: ldc.i4.s 9
IL_00b0: add
IL_00b1: stelem.i4
Au total, 14 instructions MSIL dont un seul appel : call instance int32 exemple.WinForm::f(int32)Dans l'exemple qui précède, il y a réellement gain sur le temps d'exécution de l'instruction table[ f(i) ] += 9, si le temps d'exécution de l'appel à f(i) à travers l'instruction MSIL < call instance int32 exemple.WinForm::f(int32) > , est significativement long devant les temps d'exécution des opérations ldloc et stloc.
En fait d'une manière générale en C# comme dans les autres langages, il est préférable d'adopter l'attitude prise en Delphi qui consiste à encourager la lisibilité du code en ne cherchant pas à écrire du code le plus court possible. Dans notre exemple précédent, la simplicité consisterait à utiliser une variable locale x et à stocker la valeur de f(i) dans cette variable :
table[ f(i) ] = table[ f(i) ] + 9 ;
x = table[ f(i) ] ;
table[ x ] = table[ x ] + 9 ;
Ces deux écritures étant équivalentes seulement si f(i) ne contient aucun effet de bord !
Info MSIL :
ldloc : Charge la variable locale à un index spécifique dans la pile d'évaluation.
stloc : Dépile la pile d'évaluation et la stocke dans la liste de variables locales à un index spécifié.
3.1 les instructions conditionnelles
Syntaxe :
Schématiquement les conditions sont de deux sortes :
La définition de l'instruction conditionnelle de C# est classiquement
celle des langages algorithmiques; comme en pascal l'expression doit
être de type booléen (différent du C), la notion d'instruction
a été définie plus haut.
Exemple d'utilisation du if..else (comparaison avec pascal)
Pascal-Delphi | C# |
var a , b , c : integer ; .... if b=0 then c := 1 else begin c := a / b; writeln("c = ",c); end; c := a*b ; if c <>0 then c:= c+b else c := a |
int a , b , c ; .... if ( b = = 0 ) c =1 ; else { c = a / b; System.Console.WriteLine("c = " + c); } if ((c = a*b) != 0) c += b; else c = a; |
Remarques :
Exemple de parenthésage du else pendant
Pascal-Delphi | C# |
if Expr1 then begin if Expr2 then InstrA end else InstrB |
if ( Expr1 ) { if ( Expr2 ) InstrA ; } else InstrB |
Il s'agit ici comme dans le cas des opérateurs d'affectation d'une sorte de raccourci entre l'opérateur conditionnel if...else et l'affectation. Le but étant encore d'optimiser le bytecode engendré.
Syntaxe :
Où expression est une expression renvoyant une valeur booléenne (le test), valeur sont des expressions genérales (variable, expression numérique, boolénne etc...) renvoyant une valeur de type T.
Sémantique :
Exemple :
Sémantique de l'exemple avec un if..else :
1 . Code MSIL engendré pour l'instruction C# : c = a = = 0 ? b :
a+1 ;
IL_0007: ldloc.0
IL_0008: brfalse.s IL_000f
IL_000a: ldloc.0
IL_000b: ldc.i4.1
IL_000c: add
IL_000d: br.s IL_0010
IL_000f: ldloc.1
IL_0010: stloc.2
une seule opération de stockage pour c :
IL_0010: stloc.2
2 . Code MSIL engendré pour l'instruction C# : if (a = = 0) c = b; else c = a+1 ;
IL_0011: ldloc.0
IL_0012: brtrue.s IL_0018
IL_0014: ldloc.1
IL_0015: stloc.2
IL_0016: br.s IL_001c
IL_0018: ldloc.0
IL_0019: ldc.i4.1
IL_001a: add
IL_001b: stloc.2
deux opérations de stockage pour c :
IL_0015: stloc.2
IL_001b: stloc.2
Le code MSIL engendré a la même structure classique de code
de test pour les deux instructions, la traduction de l'opérateur sera
légèrement plus rapide que celle de l'instructions car, il
n'y a pas besoin de stocker deux fois le résultat du test dans la
variable c (qui ici, est représentée par l'instruction MSIL
stloc.2)
question :
question : que fait ce morceau le programme ci-après
réponse : int a , b , c ; ....
c = a>b ? (b=a) : (a=b)
a,b,c contiennent après exécution le plus grand des deux entiers contenus au départ dans a et b.
Syntaxe :
Où expression est une expression renvoyant une valeur booléenne (le test de l'itération).
Sémantique :
Identique à celle du pascal (instruction algorithmique tantque
.. faire .. ftant) avec le même défaut de fermeture
de la boucle.
Exemple de boucle while
Pascal-Delphi | C# |
while Expr do Instr | while ( Expr ) Instr ; |
while Expr do begin InstrA ; InstrB ; ... end |
while ( Expr ) { InstrA ; InstrB ; ... } |
Syntaxe :
Où expression est une expression renvoyant une valeur booléenne (le test de l'itération).
Sémantique :
"do Instr while ( Expr )" fonctionne comme l'instruction algorithmique
répéter Instr jusquà
non Expr.
Sa sémantique peut aussi être expliquée à l'aide d'une autre instruction C# , le while( ) :
do Instr while ( Expr ) º Instr ; while ( Expr ) Instr |
Exemple de boucle do...while
Pascal-Delphi | C# |
repeat InstrA ; InstrB ; ... until not Expr |
do { InstrA ; InstrB ; ... } while ( Expr ) |
Syntaxe :
Sémantique :
Une boucle for contient 3 expressions for (Expr1 ; Expr2
; Expr3 ) Instr, d'une manière
générale chacune de ces expressions joue un rôle différent
dans l'instruction for. Une instruction for en C# (comme en Java) est
plus puissante et plus riche qu'une boucle for dans d'autres langages
algorithmiques. Nous donnons ci-après une sémantique minimale
:
"for (Expr1 ; Expr2 ; Expr3
) Instr" fonctionne au minimum comme l'instruction algorithmique pour...
fpour.
Sa sémantique peut aussi être approximativement(*) expliquée à l'aide d'une autre instruction C# while :
for (Expr1 ; Expr2 ; Expr3 ) Instr ; | Expr1 ; while ( Expr2 ) { Instr ; Expr3 } |
Exemples montrant la puissance du for
Pascal-Delphi | C# |
for i:=1 to 10 do begin InstrA ; InstrB ; ... end |
for ( i = 1;
i<=10; i++ ) { InstrA ; InstrB ; ... } |
i := 10; k := i; while (i>-450) do begin InstrA ; InstrB ; ... k := k+i; i := i-15; end |
for ( i = 10, k
= i ;i>-450 ; k += i , i -= 15) { InstrA ; InstrB ; ... } |
i := n; while i<>1 do if i mod 2 = 0 then i := i div 2 else i := i+1 |
for ( i = n ;i !=1 ; i % 2 == 0 ?
i /=2 : i++); // pas de corps de boucle ! |
Montrons exemple de boucle for dite boucle infinie :
for ( ; ; ); est équivalente à while (true); |
Voici une boucle ne possédant pas de variable de contrôle(f(x) est une fonction déjà déclarée) :
for (int n=0 ; Math.abs(x-y) < eps ; x = f(x) ); |
Terminons par une boucle for possédant deux variables de contrôle :
//inverse d'une suite de caractère
dans un tableau par permutation des deux extrêmes char [ ] Tablecar ={'a','b','c','d','e','f'} ; for ( i = 0 , j = 5 ; i<j ; i++ , j-- ) { char car ; car = Tablecar[i]; Tablecar[i ]= Tablecar[j]; Tablecar[j] = car; } |
Remarques récapitulatives sur la boucle for en C# :
Syntaxe :
Sémantique :
Une instruction break ne peut se situer qu'à l'intérieur
du corps d'instruction d'un bloc switch (le switch est traité
au paragraphe suivant), ou de l'une des trois itérations while,
do..while, for.
Lorsque break est présente dans l'une des trois itération
while, do..while, for :
Exemple d'utilisation du break dans un for :
(recherche séquentielle dans un tableau)
int [ ] table= {12,-5,7,8,-6,6,4,78,2};
int elt = 4; for ( i = 0 ; i<8 ; i++ ) if (elt= =table[i]) break ; if (i = = 8)System.out.println("valeur : "+elt+" pas trouvée."); else System.out.println("valeur : "+elt+" trouvée au rang :"+i); |
Explications |
- Si la valeur de la variable elt est présente dans le tableau table, l’expression (elt= =table[i]) est true et break est exécutée (arrêt de la boucle et exécution de if (i = = 8)... ).
- Après l'exécution de la boucle for, lorsque l'instruction if (i = = 8)... est exécutée, soit la boucle s'est exécutée complètement (recherche infructueuse), soit le break l'a arrêtée prématurément (elt est trouvé dans le tableau).
Syntaxe :
Sémantique :
Une instruction continue ne peut se situer qu'à l'intérieur
du corps d'instruction de l'une des trois itérations while,
do..while, for.
Lorsque continue est présente dans l'une des trois itération
while, do..while, for :
Exemple d'utilisation du continue dans un for :
Rappelons qu'un for s'écrit généralement
: for (Expr1 ; Expr2 ; Expr3 ) Instr L'instruction continue présente dans une telle boucle for s'effectue ainsi :
for ( i = 0, n = 0 ; i<8 ; i++ , k = 2*n ) { if ( ta[i] = = 0 ) continue ; tb[n] = ta[i]; n++; } |
Explications |
Le déroulement est alors le suivant :
et la boucle se poursuit en fonction de la valeur de la condition de rebouclage.
Cette boucle recopie dans le tableau d'entiers tb les valeurs non nulles du tableau d'entiers ta.
Attention
Nous avons déjà signalé plus haut que l'équivalence
suivante entre un for et un while
for (Expr1 ; Expr2 ; Expr3 ) Instr º | Expr1 ; while ( Expr2 ) { Instr ; Expr3 } |
Voyons ce qu'il en est en reprenant l'exemple précédent. Essayons d'écrire la boucle while qui lui serait équivalente selon la définition générale. Voici ce que l'on obtiendrait :
for ( i = 0, n = 0
; i<8 ; i++ , k = 2*n ) { if ( ta[i] = = 0 ) continue ; tb[n] = ta[i]; n++; } |
i = 0; n = 0 ; while ( i<8 ) { if ( ta[i] = = 0 ) continue ; tb[n] = ta[i]; n++; i++ ; k = 2*n; } |
Dans le while le continue réexécute la condition de rebouclage i<8 sans exécuter l'expression i++ ; k = 2*n; (nous avons d'ailleurs ici une boucle infinie). Une boucle while équivalente au for précédent pourrait être la suivante :
for ( i = 0, n =
0 ; i<8 ; i++ , k = 2*n ) { if ( ta[i] = = 0 ) continue ; tb[n] = ta[i]; n++; }
|
i = 0; n = 0 ; while ( i<8 ) { if ( ta[i] = = 0 ) { i++ ; k = 2*n; continue ; } tb[n] = ta[i]; n++; } |
Syntaxe :
bloc switch :
Sémantique :
• La partie expression d'une instruction switch doit être une expression ou une variable du type byte, char, int ou bien short.
• La partie expression d'un bloc switch doit être une constante ou une valeur immédiate du type byte, char, int ou bien short.
• switch <Epr1> s'appelle la partie sélection de l'instruction : il y a évaluation de <Epr1> puis selon la valeur obtenue le programme s'exécute en séquence à partir du case contenant la valeur immédiate égal. Il s'agit donc d'un déroutement du programme dès que <Epr1> est évaluée vers l'instruction étiquetée par le case <Epr1> associé.
Cette instruction en C#, contrairement à Java, est structurée
, elle doit obligatoirement être utilisée avec l'instruction
break afin de simuler le comportement de l'instruction structurée
case..of du pascal.
Exemple de switch..case..break
Pascal-Delphi | C# |
var x : char ; .... case x of 'a' : InstrA; 'b' : InstrB; else InstrElse end; |
char x ; .... switch (x) { case 'a' : InstrA ; break; case 'b' : InstrB ; break; default : InstrElse; } |
Dans ce cas le déroulement de l'instruction switch après déroutement vers le bon case, est interrompu par le break qui renvoie la suite après la fin du bloc switch. Une telle utilisation correspond à une utilisation de if...else imbriqués (donc utilisation structurée) mais devient plus lisible que les if ..else imbriqués, elle est donc fortement conseillée dans ce cas.
Exemples :
C# - source | Résultats de l'exécution |
int x = 10;
switch (x+1) |
>> case 11 |
int x = 11;
switch (x+1) |
>> case 12 |
Il est toujours possible d'utiliser des instructions if … else imbriquées
pour représenter un switch avec break.
Programmes équivalents switch et if...else :
C# - switch | C# - if...else |
int x = 10;
switch (x+1) |
int x = 10;
if (x+1= = 11)System.out.println(">> case 11");
|
Nous reviendrons plus loin sur les exceptions et l'instruction try..catch. Nous donnons ici la syntaxe d'une version simple et nous indiquons que sa sémantique est semblable à celle du bloc try..except de Delphi. Sachons pour l'instant que C# dispose d'un mécanisme puissant identique à Java, présent dans les langages robustes Ada, C++, Delphi pour intercepter et traiter les erreurs ou incidents survenant dans un bloc d'instruction.
L'instruction try...catch sert à traiter de tels incidents, elle dispose d'un bloc try (pour les instructions à protéger)et d'un ou plusieurs blocs catch (pour divers genres de traitement en cas d'incidents).
try {
BLOC à protéger
}
catch (TypeErreur1 Err1) {
TRAITEMENT d'une erreur
du type TypeErreur1
}
catch (TypeErreur2 Err2) {
TRAITEMENT d'une erreur
du type TypeErreur2
}
...
catch (TypeErreurX ErrX) {
TRAITEMENT d'une erreur
du type TypeErreurX
}
Ci-dessous une exemple permettant d'intercepter une erreur dans la division de l'entier x par l'entier y :
Exemple de try...catch
Pascal-Delphi | C# |
var x , y : integer; try x := 1; y := 0; Memo.lines.add("division ="+x div y); except on Err : EIntError do begin Memo.lines.add("Calcul impossible."); Memo.lines.add(Err.message); end end; |
int x , y ; try { x = 1; y = 0; System.Console.WriteLine( "division ="+x/y); } catch (ArithmeticException Err) { System.Console.WriteLine("Calcul impossible."); System.Console.WriteLine(Err.getMessage()); } |