Syntaxe C Sharp - C Sharp syntax
Cet article décrit la syntaxe du langage de programmation C# . Les fonctionnalités décrites sont compatibles avec .NET Framework et Mono .
Notions de base
Identifiant
Un identifiant est le nom d'un élément du code . Il existe certaines conventions de nommage standard à suivre lors de la sélection de noms pour les éléments.
Un identifiant peut :
- commencer par un trait de soulignement : _
- contenir un trait de soulignement : _
- contenir un chiffre : 0123456789
- contiennent à la fois des lettres Unicode majuscules et minuscules . La casse est sensible ( FOO est différent de foo )
- commencer par un signe @ (mais c'est insignifiant ;
@namec'est le même identifiant quename).
Un identifiant ne peut pas :
- commencer par un chiffre
- commencer par un symbole, sauf s'il s'agit d'un mot-clé (cochez Mots-clés )
- contenir plus de 511 caractères
- contenir le signe @ après son premier caractère
Mots clés
Les mots - clés sont des mots réservés prédéfinis avec une signification syntaxique particulière. Le langage a deux types de mots-clés — contextuels et réservés. Les mots-clés réservés tels que falseou bytene peuvent être utilisés que comme mots-clés. Les mots-clés contextuels tels que whereou fromne sont traités comme des mots-clés que dans certaines situations. Si un identifiant est nécessaire qui serait le même qu'un mot-clé réservé, il peut être préfixé par le caractère @ pour le distinguer. Cela facilite la réutilisation du code .NET écrit dans d'autres langages.
| Mots-clés C#, mots réservés | |||
|---|---|---|---|
abstract
|
as
|
base
|
bool
|
break
|
by 2
|
byte
|
case
|
catch
|
char
|
checked
|
class
|
const
|
continue
|
decimal
|
default
|
delegate
|
do
|
double
|
descending 2
|
explicit
|
event
|
extern
|
else
|
enum
|
false
|
finally
|
fixed
|
float
|
for
|
foreach
|
from 2
|
goto
|
group 2
|
if
|
implicit
|
in
|
int
|
interface
|
internal
|
into 2
|
is
|
lock
|
long
|
new
|
null
|
namespace
|
object
|
operator
|
out
|
override
|
orderby 2
|
params
|
private
|
protected
|
public
|
readonly
|
ref
|
return
|
switch
|
struct
|
sbyte
|
sealed
|
short
|
sizeof
|
stackalloc
|
static
|
string
|
select 2
|
this
|
throw
|
true
|
try
|
typeof
|
uint
|
ulong
|
unchecked
|
unsafe
|
ushort
|
using
|
var 2
|
virtual
|
volatile
|
void
|
while
|
where 1 2
|
yield 1
|
|
| 1, 2 Ce sont des mots-clés contextuels ; ainsi (contrairement aux mots-clés réels), il est possible de définir des variables et des types en utilisant ces noms, mais ils agissent comme des mots-clés lorsqu'ils apparaissent à des positions spécifiques dans le code. Les mots-clés contextuels ont été introduits dans C# 2.0, et tous les mots-clés qui seront introduits dans le futur du langage seront contextuels. | |||
Utiliser un mot-clé comme identifiant :
string @out; // @out is an ordinary identifier, distinct from the 'out' keyword,
// which retains its special meaning
Littéraux
| Entiers | |
|---|---|
| décimal |
23456, [0..9]+
|
| hexadécimal |
0xF5, 0x[0..9, A..F, a..f]+
|
| binaire |
0b010110001101, 0b[0,1]+
|
| Valeurs à virgule flottante | |
| flotter |
23.5F, 23.5f; 1.72E3F, 1.72E3f, 1.72e3F, 1.72e3f
|
| double |
23.5, 23.5D, 23.5d; 1.72E3, 1.72E3D, ...
|
| décimal |
79228162514264337593543950335m, -0.0000000000000000000000000001m, ...
|
| Personnages | |
| carboniser |
'a', 'Z', '\u0231'
|
| Cordes | |
| chaîne de caractères |
"Hello, world""C:\\Windows\\", @"C:\Windows\"[les chaînes textuelles (précédées par @) peuvent inclure des caractères de saut de ligne et de retour chariot]
|
| Les caractères s'échappent dans les chaînes | |
| Caractère Unicode |
\u suivi du point de code unicode hexadécimal
|
| Caractère nul 1 |
\0
|
| Languette |
\t
|
| Retour arrière |
\b
|
| Retour chariot |
\r
|
| Flux de formulaire |
\f
|
| barre oblique inverse |
\\
|
| Simple citation |
\'
|
| Double citation |
\"
|
| Saut de ligne |
\n
|
| 1 Les chaînes en C# ne se terminent pas par null | |
Séparateurs de chiffres
- Il s'agit d'une fonctionnalité de C# 7.0.
Le symbole de soulignement sépare les chiffres dans les valeurs numériques à des fins de lisibilité. Le compilateur ignore ces traits de soulignement.
int bin = 0b1101_0010_1011_0100;
int hex = 0x2F_BB_4A_F1;
int dec = 1_000_500_954;
double real = 1_500.200_2e-1_000;
Généralement, il ne peut être mis qu'entre des caractères numériques. Il ne peut pas être placé au début ( _121) ou à la fin de la valeur ( 121_ou 121.05_), à côté de la décimale dans les valeurs à virgule flottante ( 10_.0), à côté du caractère exposant ( 1.1e_1) et à côté du spécificateur de type ( 10_f).
Variables
Les variables sont des identifiants associés à des valeurs. Ils sont déclarés en écrivant le type et le nom de la variable, et sont éventuellement initialisés dans la même instruction.
Déclarer
int myInt; // Declaring an uninitialized variable called 'myInt', of type 'int'
Attribution
int myInt; // Declaring an uninitialized variable
myInt = 35; // Assigning the variable a value
Initialiser
int myInt = 35; // Declaring and initializing the variable
Plusieurs variables du même type peuvent être déclarées et initialisées dans une seule instruction.
int a, b; // Declaring multiple variables of the same type
int a = 2, b = 3; // Declaring and initializing multiple variables of the same type
Inférence de type de variable locale
- Il s'agit d'une fonctionnalité de C# 3.0 .
C# 3.0 a introduit l'inférence de type, permettant au spécificateur de type d'une déclaration de variable d'être remplacé par le mot-clé var, si son type réel peut être déterminé de manière statique à partir de l'initialiseur. Cela réduit la répétition, en particulier pour les types avec plusieurs paramètres de type génériques , et adhère plus étroitement au principe DRY .
var myChars = new char[] {'A', 'Ö'}; // or char[] myChars = new char[] {'A', 'Ö'};
var myNums = new List<int>(); // or List<int> myNums = new List<int>();
Constantes
Les constantes sont des valeurs immuables.
const
Lors de la déclaration d'une variable locale ou d'un champ avec le constmot clé en préfixe, la valeur doit être donnée lors de sa déclaration. Après cela, il est verrouillé et ne peut pas changer. Ils peuvent être déclarés dans le contexte comme un champ ou une variable locale. Les constantes sont implicitement statiques.
const double PI = 3.14;
Cela montre les deux utilisations du mot-clé.
class Foo
{
const double X = 3;
Foo()
{
const int Y = 2;
}
}
readonly
Le readonlymot-clé fait une chose similaire aux champs. Comme les champs marqués comme constils ne peuvent pas changer une fois initialisés. La différence est que vous pouvez choisir de les initialiser dans un constructeur ou à une valeur qui n'est connue qu'au moment de l'exécution. Cela ne fonctionne que sur les champs. readonlyLes champs peuvent être des membres d'une instance ou des membres de classe statiques.
Blocs de code
Les accolades sont utilisées pour signifier un bloc de code et une nouvelle portée . Les membres de la classe et le corps d'une méthode sont des exemples de ce qui peut vivre à l'intérieur de ces accolades dans divers contextes.
{ ... }
À l'intérieur des corps de méthode, vous pouvez utiliser les accolades pour créer de nouvelles étendues comme ceci :
void doSomething()
{
int a;
{
int b;
a = 1;
}
a = 2;
b = 3; // Will fail because the variable is declared in an inner scope.
}
Structure du programme
L'application AC# se compose de classes et de leurs membres. Les classes et autres types existent dans les espaces de noms mais peuvent également être imbriqués dans d'autres classes.
Main méthode
Qu'il s'agisse d'une console ou d'une application d'interface graphique, le programme doit avoir un point d'entrée quelconque. Le point d'entrée de l'application C# est la Mainméthode. Il ne peut y en avoir qu'une, et c'est une méthode statique dans une classe. La méthode renvoie voidet reçoit généralement des arguments de ligne de commande sous la forme d'un tableau de chaînes.
static void Main(string[] args)
{
}
// OR Main method can be defined without parameters.
static void Main()
{
}
Une Mainméthode est également autorisée à renvoyer une valeur entière si elle est spécifiée.
static int Main(string[] args)
{
return 0;
}
Principal asynchrone
C'est une fonctionnalité de C# 7.1.
Les tâches asynchrones peuvent être attendues dans la Mainméthode en la déclarant de type de retour Task.
static async Task Main(string[] args)
{
await DoWorkAsync(42);
}
Toutes les combinaisons de Task, ou Task<int>,et avec ou sans le string[] argsparamètre sont prises en charge.
Déclarations de niveau supérieur
Il s'agit d'une fonctionnalité de C# 9.0.
Comme dans les langages de script, les instructions de niveau supérieur suppriment la cérémonie de devoir déclarer la Programclasse avec une Mainméthode.
Au lieu de cela, les instructions peuvent être écrites directement dans un fichier spécifique, et ce fichier sera le point d'entrée du programme. Le code dans d'autres fichiers devra toujours être défini dans les classes.
Cela a été introduit pour rendre le C# moins verbeux, et donc plus accessible pour les débutants.
using System;
Console.WriteLine("Hello World!");
Les types sont déclarés après les instructions et seront automatiquement disponibles à partir des instructions au-dessus d'eux.
Espaces de noms
Les espaces de noms font partie d'un nom de type et ils sont utilisés pour regrouper et/ou distinguer les entités nommées des autres.
System.IO.DirectoryInfo // DirectoryInfo is in the System.IO-namespace
Un espace de noms est défini comme ceci :
namespace FooNamespace
{
// Members
}
using directif
La usingdirective charge un espace de noms spécifique à partir d'un assembly référencé. Il est généralement placé en haut (ou en-tête) d'un fichier de code mais il peut être placé ailleurs si vous le souhaitez, par exemple à l'intérieur des classes.
using System;
using System.Collections;
La directive peut également être utilisée pour définir un autre nom pour un espace de noms ou un type existant. Ceci est parfois utile lorsque les noms sont trop longs et moins lisibles.
using Net = System.Net;
using DirInfo = System.IO.DirectoryInfo;
using static directif
La directive charge les membres statiques d'un type spécifié dans la portée actuelle, les rendant accessibles directement par le nom du membre.using static
using static System.Console;
WriteLine("Hello, World!");
Les opérateurs
| Catégorie d'opérateur | Les opérateurs |
|---|---|
| Arithmétique |
+, -, *, /,%
|
| Logique (booléen et au niveau du bit) |
&, |, ^, !, ~, &&, ||, true,false
|
| Concaténation de chaînes |
+
|
| Incrémenter, décrémenter |
++, --
|
| Décalage |
<<, >>
|
| Relationnel (conditionnel) |
==, !=, <, >, <=,>=
|
| Mission |
=, +=, -=, *=, /=, %=, &=, |=, ^=, <<=,>>=
|
| Accès membre |
., ?.,?[]
|
| Indexage |
[]
|
| Jeter |
()
|
| Conditionnel (ternaire) |
?:
|
| Concaténation et suppression des délégués |
+, -
|
| Création d'objets |
new
|
| Tapez les informations |
as, is, sizeof,typeof
|
| Contrôle des exceptions de débordement |
checked, unchecked
|
| Indirection et adresse |
*, ->, [],&
|
| Se fondre |
??
|
| Expression lambda |
=>
|
Surcharge de l'opérateur
Certains des opérateurs existants peuvent être surchargés en écrivant une méthode de surcharge.
public static Foo operator+(Foo foo, Bar bar)
{
return new Foo(foo.Value + bar.Value);
}
Voici les opérateurs surchargeables :
| Les opérateurs | |
|---|---|
+, -, !, ~, ++, --, true,false
|
Opérateurs unaires |
+, -, *, /, %, &, |, ^, <<, ,>>
|
Opérateurs binaires |
==, !=, <, >, <=,>=
|
Opérateurs de comparaison , doivent être surchargés par paires |
-
Les opérateurs d'affectation ( etc.) sont des combinaisons d'un opérateur binaire et de l'opérateur d'affectation ( ) et seront évalués à l'aide des opérateurs ordinaires, qui peuvent être surchargés.
+=, *== -
Les opérateurs de distribution ( ) ne peuvent pas être surchargés, mais vous pouvez définir des opérateurs de conversion.
( ) -
L'
[ ]opérateur d' indexation de tableau ( ) n'est pas surchargé, mais vous pouvez définir de nouveaux indexeurs.
Opérateurs de conversion
L'opérateur cast n'est pas surchargé mais vous pouvez écrire une méthode d'opérateur de conversion qui réside dans la classe cible. Les méthodes de conversion peuvent définir deux variétés d'opérateurs, les opérateurs de conversion implicites et explicites. L'opérateur implicite effectuera un transtypage sans spécifier avec l'opérateur de transtypage ( ) et l'opérateur explicite requiert son utilisation.
( )
Opérateur de conversion implicite
class Foo
{
public int Value;
public static implicit operator Foo(int value)
{
return new Foo(value);
}
}
// Implicit conversion
Foo foo = 2;
Opérateur de conversion explicite
class Foo
{
public int Value;
public static explicit operator Foo(int value)
{
return new Foo(value);
}
}
// Explicit conversion
Foo foo = (Foo)2;
as opérateur
L' asopérateur tentera de faire un transtypage silencieux vers un type donné. Il renverra l'objet en tant que nouveau type si possible, et sinon renverra null.
Stream stream = File.Open(@"C:\Temp\data.dat");
FileStream fstream = stream as FileStream; // Will return an object.
String str = stream as String; // Will return null.
Opérateur de coalescence nulle
- Il s'agit d'une fonctionnalité de C# 2.0 .
Ce qui suit:
return ifNotNullValue ?? otherwiseValue;
est un raccourci pour :
return ifNotNullValue != null ? ifNotNullValue : otherwiseValue;
Cela signifie que si le contenu de la variable ifNotNullValuen'est pas nul, ce contenu sera renvoyé, sinon le contenu de la variable otherwiseValueest renvoyé.
C# 8.0 introduit une affectation à fusion nulle , telle que
variable ??= otherwiseValue;
est équivalent à
if (variable is null) variable = otherwiseValue;
Structures de contrôle
C# hérite de la plupart des structures de contrôle de C/C++ et en ajoute également de nouvelles comme l' foreachinstruction.
Structures conditionnelles
Ces structures contrôlent le déroulement du programme dans des conditions données.
if déclaration
L' ifinstruction est entrée lorsque la condition donnée est vraie. Les instructions case d'une seule ligne ne nécessitent pas d'accolades, bien qu'elles soient généralement préférées par convention.
Déclaration simple d'une ligne :
if (i == 3) ... ;
Multi-ligne avec else-block (sans accolades):
if (i == 2)
...
else
...
Conventions de codage recommandées pour une instruction if.
if (i == 3)
{
...
}
else if (i == 2)
{
...
}
else
{
...
}
switch déclaration
La switchconstruction sert de filtre pour différentes valeurs. Chaque valeur mène à un "cas". Il n'est pas permis de passer à travers les sections de cas et, par conséquent, le mot break- clé est généralement utilisé pour mettre fin à un cas. Un inconditionnel returndans une section de cas peut également être utilisé pour mettre fin à un cas. Voir aussi comment l' gotoinstruction peut être utilisée pour passer d'un cas à l'autre. Cependant, de nombreux cas peuvent conduire au même code. Le cas par défaut gère tous les autres cas non gérés par la construction.
switch (ch)
{
case 'A':
statement;
...
break;
case 'B':
statement;
break;
case 'C': // A switch section can have multiple case labels.
case 'D':
...
break;
default:
...
break;
}
Structures d'itération
Les instructions d'itération sont des instructions qui sont exécutées à plusieurs reprises lorsqu'une condition donnée est évaluée comme vraie.
while boucle
while (i == true)
{
...
}
do ... while boucle
do
{
}
while (i == true);
for boucle
La forboucle se compose de trois parties : declaration , condition et counter expression . N'importe lequel d'entre eux peut être omis car ils sont facultatifs.
for (int i = 0; i < 10; i++)
{
...
}
Est équivalent à ce code représenté par une whileinstruction, sauf qu'ici la ivariable n'est pas locale à la boucle.
int i = 0;
while (i < 10)
{
//...
i++;
}
foreach boucle
L' foreachinstruction est dérivée de l' forinstruction et utilise un certain modèle décrit dans la spécification du langage C# afin d'obtenir et d'utiliser un énumérateur d'éléments à parcourir.
Chaque élément de la collection donnée sera renvoyé et accessible dans le contexte du bloc de code. Lorsque le bloc a été exécuté, l'élément suivant sera renvoyé jusqu'à ce qu'il ne reste plus d'éléments.
foreach (int i in intList)
{
...
}
Instructions de saut
Les instructions de saut sont héritées de C/C++ et, en fin de compte, des langages d'assemblage via celui-ci. Ils représentent simplement les instructions de saut d'un langage d'assemblage qui contrôle le flux d'un programme.
Étiquettes et gotodéclaration
Les étiquettes reçoivent des points dans le code auxquels on peut accéder à l'aide de l' gotoinstruction.
start:
.......
goto start;
Notez que l'étiquette n'a pas besoin d'être placée après l' gotoinstruction ; il peut être avant lui dans le fichier source.
L' gotoinstruction peut être utilisée dans des switchinstructions pour passer d'un cas à un autre ou pour passer d'un cas à l'autre.
switch(n)
{
case 1:
Console.WriteLine("Case 1");
break;
case 2:
Console.WriteLine("Case 2");
goto case 1;
case 3:
Console.WriteLine("Case 3");
case 4: // Compilation will fail here as cases cannot fall through in C#.
Console.WriteLine("Case 4");
goto default; // This is the correct way to fall through to the next case.
case 5: // Multiple labels for the same code are OK
case 6:
default:
Console.WriteLine("Default");
break; // Even default must not reach the end point
}
break déclaration
L' breakinstruction sort de la boucle ou de l' switchinstruction la plus proche . L'exécution se poursuit dans l'instruction après l'instruction terminée, le cas échéant.
int e = 10;
for (int i = 0; i < e; i++)
{
while (true)
{
break;
}
// Will break to this point.
}
continue déclaration
L' continueinstruction interrompt l'itération en cours de l'instruction de contrôle en cours et commence l'itération suivante.
int ch;
while ((ch = Console.Read()) != -1)
{
if (ch == ' ')
continue; // Skips the rest of the while-loop
// Rest of the while-loop
...
}
La whileboucle dans le code ci-dessus lit les caractères en appelant , en sautant les instructions dans le corps de la boucle si les caractères sont des espaces.
GetChar()
Gestion des exceptions
La méthode de gestion des exceptions d'exécution en C# est héritée de Java et C++.
La bibliothèque de classes de base a une classe appelée à partir de laquelle toutes les autres classes d'exception sont dérivées. Un -objet contient toutes les informations sur une exception spécifique ainsi que les exceptions internes qui ont été provoquées. Les programmeurs peuvent définir leurs propres exceptions en dérivant de la classe.
System.ExceptionExceptionException
Une exception peut être levée de cette façon :
throw new NotImplementedException();
try ... catch ... finally déclarations
Les exceptions sont gérées au sein de blocs.
try ... catch
try
{
// Statements which may throw exceptions
...
}
catch (Exception ex)
{
// Exception caught and handled here
...
}
finally
{
// Statements always executed after the try/catch blocks
...
}
Les instructions du trybloc sont exécutées et si l'une d'entre elles lève une exception, l'exécution du bloc est interrompue et l'exception est gérée par le catchbloc. Il peut y avoir plusieurs catchblocs, auquel cas le premier bloc avec une variable d'exception dont le type correspond au type de l'exception levée est exécuté.
Si aucun catchbloc ne correspond au type de l'exception levée, l'exécution du bloc externe (ou de la méthode) contenant l' instruction est interrompue et l'exception est transmise et en dehors du bloc ou de la méthode conteneur. L'exception est propagée vers le haut dans la pile d'appels jusqu'à ce qu'un bloc correspondant soit trouvé dans l'une des méthodes actuellement actives. Si l'exception se propage jusqu'à la méthode la plus élevée sans qu'un bloc correspondant soit trouvé, le programme entier est terminé et une description textuelle de l'exception est écrite dans le flux de sortie standard.
try ... catchcatchMain()catch
Les instructions dans le finallybloc sont toujours exécutées après les blocs tryet catch, qu'une exception ait été levée ou non. De tels blocs sont utiles pour fournir du code de nettoyage.
Un catchbloc, un finallybloc ou les deux doivent suivre le trybloc.
Les types
C# est un langage à typage statique comme le C et le C++. Cela signifie que chaque variable et constante obtient un type fixe lorsqu'elle est déclarée. Il existe deux types de types : les types valeur et les types référence .
Types de valeur
Les instances de types valeur résident sur la pile, c'est-à-dire qu'elles sont liées à leurs variables. Si vous déclarez une variable pour un type valeur, la mémoire est allouée directement. Si la variable sort de la portée, l'objet est détruit avec elle.
Structures
Les structures sont plus communément appelées structs . Les structs sont des types de valeur définis par l'utilisateur qui sont déclarés à l'aide du structmot - clé. Ils sont très similaires aux classes mais conviennent mieux aux types légers. Certaines différences syntaxiques importantes entre a classet a structsont présentées plus loin dans cet article .
struct Foo
{
...
}
Les types de données primitifs sont tous des structs.
Types prédéfinis
Ce sont les types de données primitifs.
| Types primitifs | |||||
|---|---|---|---|---|---|
| Tapez le nom | Équivalent BCL | Valeur | Varier | Taille | Valeur par défaut |
sbyte
|
System.SByte
|
entier | -128 à +127 | 8 bits (1 octet) |
0
|
short
|
System.Int16
|
entier | −32 768 à +32 767 | 16 bits (2 octets) |
0
|
int
|
System.Int32
|
entier | -2 147 483 648 à +2 147 483 647 | 32 bits (4 octets) |
0
|
long
|
System.Int64
|
entier | −9 223 372 036 854 775 808 à +9 223 372 036 854 775 807 |
64 bits (8 octets) |
0
|
byte
|
System.Byte
|
entier non signé | 0 à 255 | 8 bits (1 octet) |
0
|
ushort
|
System.UInt16
|
entier non signé | 0 à 65 535 | 16 bits (2 octets) |
0
|
uint
|
System.UInt32
|
entier non signé | 0 à 4 294 967 295 | 32 bits (4 octets) |
0
|
ulong
|
System.UInt64
|
entier non signé | 0 à 18 446 744 073 709 551 615 | 64 bits (8 octets) |
0
|
decimal
|
System.Decimal
|
nombre décimal signé | −79 228 162 514 264 337 593 543 950 335 à +79 228 162 514 264 337 593 543 950 335 |
128 bits (16 octets) |
0.0
|
float
|
System.Single
|
nombre à virgule flottante | ±1,401298E−45 à ±3,402823E+38 | 32 bits (4 octets) |
0.0
|
double
|
System.Double
|
nombre à virgule flottante | ±4.94065645841246E−324 à ±1.79769313486232E+308 |
64 bits (8 octets) |
0.0
|
bool
|
System.Boolean
|
booléen |
true ou false
|
8 bits (1 octet) |
false
|
char
|
System.Char
|
caractère Unicode unique |
'\u0000' par '\uFFFF'
|
16 bits (2 octets) |
'\u0000'
|
Remarque : string ( ) n'est pas une structure et n'est pas un type primitif.
System.String
Énumérations
Les types énumérés ( enums) sont des valeurs nommées représentant des valeurs entières.
enum Season
{
Winter = 0,
Spring = 1,
Summer = 2,
Autumn = 3,
Fall = Autumn // Autumn is called Fall in American English.
}
enumles variables sont initialisées par défaut à zéro. Ils peuvent être affectés ou initialisés aux valeurs nommées définies par le type d'énumération.
Season season;
season = Season.Spring;
enumles variables de type sont des valeurs entières. L'addition et la soustraction entre variables du même type sont autorisées sans transtypage spécifique, mais la multiplication et la division sont un peu plus risquées et nécessitent un transtypage explicite. Les conversions sont également nécessaires pour convertir des enumvariables vers et à partir de types entiers. Cependant, le cast ne lèvera pas d'exception si la valeur n'est pas spécifiée par la enumdéfinition de type.
season = (Season)2; // cast 2 to an enum-value of type Season.
season = season + 1; // Adds 1 to the value.
season = season + season2; // Adding the values of two enum variables.
int value = (int)season; // Casting enum-value to integer value.
season++; // Season.Spring (1) becomes Season.Summer (2).
season--; // Season.Summer (2) becomes Season.Spring (1).
Les valeurs peuvent être combinées à l'aide de l'opérateur OR au niveau du bit |.
Color myColors = Color.Green | Color.Yellow | Color.Blue;
Types de référence
Les variables créées pour les types de référence sont des références gérées typées. Lorsque le constructeur est appelé, un objet est créé sur le tas et une référence est affectée à la variable. Lorsqu'une variable d'un objet sort de la portée, la référence est rompue et lorsqu'il n'y a plus de références, l'objet est marqué comme poubelle. Le ramasse-miettes va alors bientôt le récupérer et le détruire.
Une variable de référence est nulllorsqu'elle ne fait référence à aucun objet.
Tableaux
Un type tableau est un type référence qui fait référence à un espace contenant un ou plusieurs éléments d'un certain type. Tous les types de tableau dérivent d'une classe de base commune, . Chaque élément est référencé par son index comme en C++ et Java.
System.Array
Un tableau en C# est ce qu'on appellerait un tableau dynamique en C++.
int[] numbers = new int[2];
numbers[0] = 2;
numbers[1] = 5;
int x = numbers[0];
Initialiseurs
Les initialiseurs de tableaux fournissent une syntaxe pratique pour l'initialisation des tableaux.
// Long syntax
int[] numbers = new int[5]{ 20, 1, 42, 15, 34 };
// Short syntax
int[] numbers2 = { 20, 1, 42, 15, 34 };
// Inferred syntax
var numbers3 = new[] { 20, 1, 42, 15, 34 };
Tableaux multidimensionnels
Les tableaux peuvent avoir plusieurs dimensions, par exemple 2 dimensions pour représenter une grille.
int[,] numbers = new int[3, 3];
numbers[1,2] = 2;
int[,] numbers2 = new int[3, 3] { {2, 3, 2}, {1, 2, 6}, {2, 4, 5} };
Voir également
Des classes
Les classes sont des types de référence auto-descriptifs définis par l'utilisateur. Essentiellement, tous les types du .NET Framework sont des classes, y compris des structs et des énumérations, qui sont des classes générées par le compilateur. Les membres de classe sont privatepar défaut, mais peuvent être déclarés comme publicétant visibles en dehors de la classe ou protectedcomme étant visibles par tous les descendants de la classe.
String classer
La classe, ou simplement , représente une séquence immuable de caractères Unicode ( ).
System.Stringstringchar
Les actions effectuées sur une chaîne renvoient toujours une nouvelle chaîne.
string text = "Hello World!";
string substr = text.Substring(0, 5);
string[] parts = text.Split(new char[]{ ' ' });
La classe peut être utilisée lorsqu'une "chaîne" mutable est souhaitée.
System.StringBuilder
StringBuilder sb = new StringBuilder();
sb.Append('H');
sb.Append("el");
sb.AppendLine("lo!");
Interface
Les interfaces sont des structures de données qui contiennent des définitions de membres sans implémentation réelle. Une variable de type interface est une référence à une instance d'une classe qui implémente cette interface. Voir #Interfaces .
Délégués
C# fournit des pointeurs de fonction orientés objet de type sécurisé sous la forme de délégués .
class Program
{
// Delegate type:
delegate int Operation(int a, int b);
static int Add(int i1, int i2)
{
return i1 + i2;
}
static int Sub(int i1, int i2)
{
return i1 - i2;
}
static void Main()
{
// Instantiate the delegate and assign the method to it.
Operation op = Add;
// Call the method that the delegate points to.
int result1 = op(2, 3); // 5
op = Sub;
int result2 = op(10, 2); // 8
}
}
Initialisation du délégué avec une méthode anonyme.
addition = delegate(int a, int b){ return a + b; };
Initialisation du délégué avec l'expression lambda.
addition = (a, b) => a + b;
Événements
Les événements sont des pointeurs qui peuvent pointer vers plusieurs méthodes. Plus précisément, ils lient les pointeurs de méthode à un identifiant. Cela peut donc être vu comme une extension aux délégués . Ils sont généralement utilisés comme déclencheurs dans le développement de l'interface utilisateur. Le formulaire utilisé en C# et le reste de Common Language Infrastructure est basé sur celui de Visual Basic classique .
delegate void MouseEventHandler(object sender, MouseEventArgs e);
public class Button : System.Windows.Controls.Control
{
event MouseEventHandler OnClick;
/* Imaginary trigger function */
void click()
{
this.OnClick(this, new MouseEventArgs(data));
}
}
Un événement nécessite un gestionnaire d'événements accompagné qui est créé à partir d'un délégué spécial qui, dans une bibliothèque spécifique à une plate-forme comme dans Windows Presentation Foundation et Windows Forms, prend généralement deux paramètres : l' expéditeur et les arguments de l' événement . Le type de l'objet argument d'événement dérive de la classe EventArgs qui fait partie de la bibliothèque de base CLI.
Une fois déclaré dans sa classe, le seul moyen d'invoquer l'événement est de l'intérieur du propriétaire. Une méthode d'écoute peut être implémentée à l'extérieur pour être déclenchée lorsque l'événement est déclenché.
public class MainWindow : System.Windows.Controls.Window
{
private Button button1;
public MainWindow()
{
button1 = new Button();
button1.Text = "Click me!";
/* Subscribe to the event */
button1.ClickEvent += button1_OnClick;
/* Alternate syntax that is considered old:
button1.MouseClick += new MouseEventHandler(button1_OnClick); */
}
protected void button1_OnClick(object sender, MouseEventArgs e)
{
MessageBox.Show("Clicked!");
}
}
La mise en œuvre d'événements personnalisés est également possible :
private EventHandler clickHandles = (s, e) => { };
public event EventHandler Click
{
add
{
// Some code to run when handler is added...
...
clickHandles += value;
}
remove
{
// Some code to run when handler is removed...
...
clickHandles -= value;
}
}
Voir également
Types Nullables
- Il s'agit d'une fonctionnalité de C# 2.0 .
Les types Nullable ont été introduits dans C# 2.0 tout d'abord pour permettre aux types valeur d'être null(utile lorsque vous travaillez avec une base de données).
int? n = 2;
n = null;
Console.WriteLine(n.HasValue);
En réalité, c'est la même chose que d'utiliser la structure.
Nullable<T>
Nullable<int> n = 2;
n = null;
Console.WriteLine(n.HasValue);
Pointeurs
C# a et autorise des pointeurs vers des types sélectionnés (certaines primitives, énumérations, chaînes, pointeurs et même des tableaux et des structures s'ils contiennent uniquement des types qui peuvent être pointés) dans un contexte non sécurisé : méthodes et bloc de code marqués unsafe. Ceux-ci sont syntaxiquement les mêmes que les pointeurs en C et C++. Cependant, la vérification à l'exécution est désactivée à l'intérieur des unsafeblocs.
static void Main(string[] args)
{
unsafe
{
int a = 2;
int* b = &a;
Console.WriteLine("Address of a: {0}. Value: {1}", (int)&a, a);
Console.WriteLine("Address of b: {0}. Value: {1}. Value of *b: {2}", (int)&b, (int)b, *b);
// Will output something like:
// Address of a: 71953600. Value: 2
// Address of b: 71953596. Value: 71953600. Value of *b: 2
}
}
Les structs doivent uniquement être des structs purs sans membres d'un type référence géré, par exemple une chaîne ou toute autre classe.
public struct MyStruct
{
public char Character;
public int Integer;
}
public struct MyContainerStruct
{
public byte Byte;
public MyStruct MyStruct;
}
Utilisé:
MyContainerStruct x;
MyContainerStruct* ptr = &x;
byte value = ptr->Byte;
Dynamique
- Il s'agit d'une fonctionnalité de C# 4.0 et .NET Framework 4.0 .
Le type dynamicest une fonctionnalité qui permet une recherche d'exécution dynamique vers C# de manière statique. Dynamique désigne une variable avec un objet dont le type est résolu à l'exécution, par opposition à la compilation, comme c'est normalement le cas.
Cette fonctionnalité tire parti de la Dynamic Language Runtime (DLR) et a été conçu spécifiquement dans le but de interoping avec typés dynamiquement des langues comme IronPython et IronRuby (Implémentations de Python et Ruby pour .NET).
La prise en charge dynamique facilite également l'interopérabilité avec les objets COM .
dynamic x = new Foo();
x.DoSomething(); // Will compile and resolved at runtime. An exception will be thrown if invalid.
Types anonymes
- Il s'agit d'une fonctionnalité de C# 3.0 .
Les types anonymes sont des classes sans nom qui sont générées par le compilateur. Ils ne sont que consommables et pourtant très utiles dans un scénario comme celui où vous avez une requête LINQ qui renvoie un objet selectet vous souhaitez simplement renvoyer des valeurs spécifiques. Ensuite, vous pouvez définir un type anonyme contenant des champs en lecture seule générés automatiquement pour les valeurs.
Lors de l'instanciation d'une autre déclaration de type anonyme avec la même signature, le type est automatiquement déduit par le compilateur.
var carl = new { Name = "Carl", Age = 35 }; // Name of the type is only known by the compiler.
var mary = new { Name = "Mary", Age = 22 }; // Same type as the expression above
Boxe et déballage
La boxe est l'opération de conversion d'une valeur d'un type valeur en une valeur d'un type référence correspondant. La boxe en C# est implicite.
Le déballage est l'opération de conversion d'une valeur d'un type référence (précédemment encadrée) en une valeur d'un type valeur. Le déballage en C# nécessite un transtypage de type explicite.
Exemple:
int foo = 42; // Value type.
object bar = foo; // foo is boxed to bar.
int foo2 = (int)bar; // Unboxed back to value type.
Programmation orientée objet (POO)
C# prend directement en charge la programmation orientée objet .
Objets
Un objet est créé avec le type comme modèle et est appelé une instance de ce type particulier.
En C#, les objets sont soit des références, soit des valeurs. Aucune autre distinction syntaxique n'est faite entre ceux du code.
object classer
Tous les types, même les types valeur sous leur forme encadrée, héritent implicitement de la classe, la classe de base ultime de tous les objets. Cette classe contient les méthodes les plus courantes partagées par tous les objets. Certains d'entre eux sont et peuvent être remplacés.
System.Objectvirtual
Les classes héritent directement ou indirectement via une autre classe de base.
System.Object
Membres
Quelques-uns des membres de la Objectclasse :
-
Equals- Prend en charge les comparaisons entre les objets. -
Finalize- Effectue des opérations de nettoyage avant qu'un objet ne soit automatiquement récupéré. (Destructeur par défaut) -
GetHashCode- Obtient le nombre correspondant à la valeur de l'objet pour prendre en charge l'utilisation d'une table de hachage. -
GetType- Obtient le Type de l'instance actuelle. -
ToString- Crée une chaîne de texte lisible par l'homme qui décrit une instance de la classe. Habituellement, il renvoie le nom du type.
Des classes
Les classes sont les bases d'un langage orienté objet tel que C#. Ils servent de modèle pour les objets. Ils contiennent des membres qui stockent et manipulent des données d'une manière réaliste.
Différences entre les classes et les structures
Bien que les classes et les structures soient similaires à la fois dans la façon dont elles sont déclarées et dans la façon dont elles sont utilisées, il existe des différences significatives. Les classes sont des types référence et les structures sont des types valeur. Une structure est allouée sur la pile lorsqu'elle est déclarée et la variable est liée à son adresse. Il contient directement la valeur. Les classes sont différentes car la mémoire est allouée en tant qu'objets sur le tas. Les variables sont plutôt des pointeurs gérés sur la pile qui pointent vers les objets. Ce sont des références.
Les structures demandent plus de travail que les classes. Par exemple, vous devez créer explicitement un constructeur par défaut qui ne prend aucun argument pour initialiser la structure et ses membres. Le compilateur en créera un par défaut pour les classes. Tous les champs et propriétés d'une structure doivent avoir été initialisés avant la création d'une instance. Les structures n'ont pas de finaliseur et ne peuvent pas hériter d'une autre classe comme les classes le font. Cependant, ils héritent de , qui hérite de . Les structures sont plus adaptées aux plus petites constructions de données.
System.ValueTypeSystem.Object
Voici un bref résumé des différences :
| Constructeur par défaut | Finaliseur | Initialisation du membre | Héritage | |
|---|---|---|---|---|
| Des classes | non requis (généré automatiquement 1 ) | Oui | non requis | oui (si la classe de base ne l'est pas sealed)
|
| Structures | requis (généré automatiquement 2 ) | non | obligatoire | non supporté |
|
1 Généré uniquement si aucun constructeur n'a été fourni 2 Toujours généré automatiquement et ne peut pas être écrit par le programmeur |
||||
Déclaration
Une classe est déclarée comme ceci :
class Foo
{
// Member declarations
}
Classe partielle
- Il s'agit d'une fonctionnalité de C# 2.0 .
Une classe partielle est une déclaration de classe dont le code est divisé en fichiers séparés. Les différentes parties d'une classe partielle doivent être marquées avec le mot-clé partial.
// File1.cs
partial class Foo
{
...
}
// File2.cs
partial class Foo
{
...
}
Initialisation
Avant de pouvoir utiliser les membres de la classe, vous devez initialiser la variable avec une référence à un objet. Pour le créer, vous appelez le constructeur approprié en utilisant le newmot - clé. Il porte le même nom que la classe.
Foo foo = new Foo();
Pour les structs, il est facultatif d'appeler explicitement un constructeur car celui par défaut est appelé automatiquement. Il vous suffit de le déclarer et il est initialisé avec des valeurs standard.
Initialiseurs d'objets
- Il s'agit d'une fonctionnalité de C# 3.0 .
Fournit un moyen plus pratique d'initialiser les champs publics et les propriétés d'un objet. Les appels de constructeur sont facultatifs lorsqu'il existe un constructeur par défaut.
Person person = new Person {
Name = "John Doe",
Age = 39
};
// Equal to
Person person = new Person();
person.Name = "John Doe";
person.Age = 39;
Initialiseurs de collection
- Il s'agit d'une fonctionnalité de C# 3.0 .
Les initialiseurs de collection donnent une syntaxe de type tableau pour initialiser les collections. Le compilateur générera simplement des appels à la méthode Add. Cela fonctionne pour les classes qui implémentent l'interface ICollection.
List<int> list = new List<int> {2, 5, 6, 6};
// Equal to
List<int> list = new List<int>();
list.Add(2);
list.Add(5);
list.Add(6);
list.Add(6);
Accéder aux membres
Les membres d'une instance et les membres statiques d'une classe sont accessibles à l'aide de l' .opérateur.
Accéder à un membre d'
instance Les membres d'instance sont accessibles via le nom d'une variable.
string foo = "Hello";
string fooUpper = foo.ToUpper();
Accès à un membre de classe statique
Les membres statiques sont accessibles en utilisant le nom de la classe ou un autre type.
int r = String.Compare(foo, fooUpper);
Accéder à un membre via un pointeur
Dans un code non sécurisé , les membres d'une valeur (type struct) référencés par un pointeur sont accessibles avec l' ->opérateur comme en C et C++.
POINT p;
p.X = 2;
p.Y = 6;
POINT* ptr = &p;
ptr->Y = 4;
Modificateurs
Les modificateurs sont des mots-clés utilisés pour modifier les déclarations de types et de membres de type. Plus particulièrement, il existe un sous-groupe contenant les modificateurs d'accès.
Modificateurs de classe
-
abstract- Spécifie qu'une classe sert uniquement de classe de base. Il doit être implémenté dans une classe héritante. -
sealed- Spécifie qu'une classe ne peut pas être héritée.
Modificateurs de membre de classe
-
const- Spécifie qu'une variable est une valeur constante qui doit être initialisée lorsqu'elle est déclarée. -
event- Déclare un événement. -
extern- Spécifie qu'une signature de méthode sans corps utilise une importation DLL. -
override- Spécifie qu'une déclaration de méthode ou de propriété est un remplacement d'un membre virtuel ou une implémentation d'un membre d'une classe abstraite. -
readonly- Déclare un champ auquel on ne peut affecter des valeurs que dans le cadre de la déclaration ou dans un constructeur de la même classe. -
unsafe- Spécifie un contexte non sécurisé, qui autorise l'utilisation de pointeurs. -
virtual- Spécifie qu'une méthode ou une déclaration de propriété peut être remplacée par une classe dérivée. -
volatile- Spécifie un champ qui peut être modifié par un processus externe et empêche un compilateur optimisateur de modifier l'utilisation du champ.
static modificateur
Le staticmodificateur indique qu'un membre appartient à la classe et non à un objet spécifique. Les classes marquées statiques ne peuvent contenir que des membres statiques. Les membres statiques sont parfois appelés membres de la classe car ils s'appliquent à la classe dans son ensemble et non à ses instances.
public class Foo
{
public static void Something()
{
...
}
}
// Calling the class method.
Foo.Something();
Modificateurs d'accès
Les modificateurs d'accès , ou modificateurs d'héritage , définissent l'accessibilité des classes, méthodes et autres membres. Quelque chose de marqué publicpeut être atteint de n'importe où. privateles membres ne sont accessibles que depuis l'intérieur de la classe dans laquelle ils sont déclarés et seront masqués lors de l'héritage. Les membres avec le protectedmodificateur seront private, mais accessibles lorsqu'ils sont hérités. internalles classes et les membres ne seront accessibles que depuis l'intérieur de l'assembly déclarant.
Les classes et les structures sont implicitement internalet les membres le sont implicitement privates'ils n'ont pas de modificateur d'accès.
public class Foo
{
public int Do()
{
return 0;
}
public class Bar
{
}
}
Ce tableau définit où les modificateurs d'accès peuvent être utilisés.
| Types non imbriqués | Membres (y compris les types imbriqués) | |
|---|---|---|
public
|
Oui | Oui |
protected internal
|
non | Oui |
protected
|
non | Oui |
internal
|
oui (par défaut) | Oui |
private protected
|
non | Oui |
private
|
non | oui (par défaut) |
Constructeurs
Un constructeur est une méthode spéciale qui est appelée automatiquement lorsqu'un objet est créé. Son but est d'initialiser les membres de l'objet. Les constructeurs ont le même nom que la classe et ne retournent rien. Ils peuvent prendre des paramètres comme n'importe quelle autre méthode.
class Foo
{
Foo()
{
...
}
}
Les constructeurs peuvent être public, private, protectedou internal.
Destructeur
Le destructeur est appelé lorsque l'objet est collecté par le ramasse-miettes pour effectuer un nettoyage manuel. Il existe une méthode destructrice par défaut appelée finalizequi peut être remplacée en déclarant la vôtre.
La syntaxe est similaire à celle des constructeurs. La différence est que le nom est précédé d'un ~ et qu'il ne peut contenir aucun paramètre. Il ne peut pas y avoir plus d'un destructeur.
class Foo
{
...
~Foo()
{
...
}
}
Les finaliseurs sont toujours private.
Méthodes
Comme en C et C++, il existe des fonctions qui regroupent du code réutilisable. La principale différence est que les fonctions, tout comme en Java, doivent résider à l'intérieur d'une classe. Une fonction est donc appelée une méthode . Une méthode a une valeur de retour, un nom et généralement des paramètres initialisés lorsqu'elle est appelée avec des arguments. Il peut appartenir à une instance d'une classe ou être un membre statique.
class Foo
{
int Bar(int a, int b)
{
return a%b;
}
}
Une méthode est appelée en utilisant la .notation sur une variable spécifique, ou comme dans le cas des méthodes statiques, le nom d'un type.
Foo foo = new Foo();
int r = foo.Bar(7, 2);
Console.WriteLine(r);
refet outparamètres
On peut explicitement faire passer des arguments par référence lors de l'appel d'une méthode avec des paramètres précédés de mots ref- clés ou out. Ces pointeurs managés sont utiles lorsque vous passez des variables que vous souhaitez modifier à l'intérieur de la méthode par référence. La principale différence entre les deux est qu'un outparamètre doit avoir été affecté dans la méthode au moment où la méthode revient, tandis que ref n'a pas besoin d'affecter de valeur.
void PassRef(ref int x)
{
if (x == 2)
x = 10;
}
int Z;
PassRef(ref Z);
void PassOut(out int x)
{
x = 2;
}
int Q;
PassOut(out Q);
Paramètres facultatifs
- Il s'agit d'une fonctionnalité de C# 4.0 .
C# 4.0 introduit des paramètres facultatifs avec des valeurs par défaut comme on le voit en C++. Par exemple:
void Increment(ref int x, int dx = 1)
{
x += dx;
}
int x = 0;
Increment(ref x); // dx takes the default value of 1
Increment(ref x, 2); // dx takes the value 2
De plus, pour compléter les paramètres facultatifs, il est possible de spécifier explicitement les noms de paramètres dans les appels de méthode, ce qui permet de passer sélectivement tout sous-ensemble donné de paramètres facultatifs pour une méthode. La seule restriction est que les paramètres nommés doivent être placés après les paramètres non nommés. Les noms de paramètres peuvent être spécifiés pour les paramètres facultatifs et obligatoires, et peuvent être utilisés pour améliorer la lisibilité ou réorganiser arbitrairement les arguments dans un appel. Par exemple:
Stream OpenFile(string name, FileMode mode = FileMode.Open,
FileAccess access = FileAccess.Read) { ... }
OpenFile("file.txt"); // use default values for both "mode" and "access"
OpenFile("file.txt", mode: FileMode.Create); // use default value for "access"
OpenFile("file.txt", access: FileAccess.Read); // use default value for "mode"
OpenFile(name: "file.txt", access: FileAccess.Read, mode: FileMode.Create);
// name all parameters for extra readability,
// and use order different from method declaration
Les paramètres facultatifs facilitent l'interopérabilité avec COM. Auparavant, C# devait passer tous les paramètres de la méthode du composant COM, même ceux qui sont facultatifs. Par exemple:
object fileName = "Test.docx";
object missing = System.Reflection.Missing.Value;
doc.SaveAs(ref fileName,
ref missing, ref missing, ref missing,
ref missing, ref missing, ref missing,
ref missing, ref missing, ref missing,
ref missing, ref missing, ref missing,
ref missing, ref missing, ref missing);
console.writeline("File saved successfully");
Avec la prise en charge des paramètres facultatifs, le code peut être raccourci comme
doc.SaveAs(ref fileName);
extern
Une caractéristique de C# est la possibilité d'appeler du code natif. Une signature de méthode est simplement déclarée sans corps et est marquée comme extern. L' DllImportattribut doit également être ajouté pour référencer le fichier DLL souhaité.
[DllImport("win32.dll")]
static extern double Pow(double a, double b);
Des champs
Les champs, ou variables de classe , peuvent être déclarés dans le corps de la classe pour stocker des données.
class Foo
{
double foo;
}
Les champs peuvent être initialisés directement lorsqu'ils sont déclarés (à moins qu'ils ne soient déclarés dans struct).
class Foo
{
double foo = 2.3;
}
Modificateurs pour les champs :
-
const- Rend le champ une constante. -
private- Rend le champ privé (par défaut). -
protected- Rend le champ protégé. -
public- Rend le terrain public. -
readonly- Permet au champ d'être initialisé une seule fois dans un constructeur. -
static- Rend le champ un membre statique.
Propriétés
Les propriétés apportent une syntaxe de type champ et les combinent avec la puissance des méthodes. Une propriété peut avoir deux accesseurs : getet set.
class Person
{
string name;
string Name
{
get { return name; }
set { name = value; }
}
}
// Using a property
Person person = new Person();
person.Name = "Robert";
Modificateurs pour les propriétés :
-
private- Rend la propriété privée (par défaut). -
protected- Rend la propriété protégée. -
public- Rend la propriété publique. -
static- Fait de la propriété un membre statique.
Modificateurs pour les accesseurs de propriété :
-
private- Rend l'accesseur privé. -
protected- Rend l'accesseur protégé. -
public- Rend l'accesseur public.
Les modificateurs par défaut des accesseurs sont hérités de la propriété. Notez que les modificateurs de l'accesseur ne peuvent être que égaux ou plus restrictifs que le modificateur de la propriété.
Propriétés automatiques
- Il s'agit d'une fonctionnalité de C# 3.0 .
Une fonctionnalité de C# 3.0 est les propriétés implémentées automatiquement. Vous définissez des accesseurs sans corps et le compilateur générera un champ de sauvegarde et le code nécessaire pour les accesseurs.
public double Width
{
get;
private set;
}
Indexeurs
Les indexeurs ajoutent des capacités d'indexation de type tableau aux objets. Ils sont implémentés d'une manière similaire aux propriétés.
class IntList
{
int[] items;
int this[int index]
{
get { return this.items[index]; }
set { this.items[index] = value; }
}
}
// Using an indexer
IntList list = new IntList();
list[2] = 2;
Héritage
Les classes en C# ne peuvent hériter que d'une seule classe. Une classe peut dériver de n'importe quelle classe qui n'est pas marquée comme sealed.
class A
{
}
class B : A
{
}
virtual
Les méthodes marquées virtualfournissent une implémentation, mais elles peuvent être remplacées par les héritiers en utilisant le overridemot - clé.
L'implémentation est choisie par le type réel de l'objet et non par le type de la variable.
class Operation
{
public virtual int Do()
{
return 0;
}
}
class NewOperation : Operation
{
public override int Do()
{
return 1;
}
}
new
Lors de la surcharge d'une méthode non virtuelle avec une autre signature, le mot new- clé peut être utilisé. La méthode utilisée sera choisie par le type de la variable au lieu du type réel de l'objet.
class Operation
{
public int Do()
{
return 0;
}
}
class NewOperation : Operation
{
public new double Do()
{
return 4.0;
}
}
Ceci démontre le cas :
NewOperation operation = new NewOperation();
// Will call "double Do()" in NewOperation
double d = operation.Do();
Operation operation_ = operation;
// Will call "int Do()" in Operation
int i = operation_.Do();
abstract
Les classes abstraites sont des classes qui servent uniquement de modèles et vous ne pouvez pas initialiser un objet de ce type. Sinon, c'est comme une classe ordinaire.
Il peut aussi y avoir des membres abstraits. Les membres abstraits sont des membres de classes abstraites qui n'ont aucune implémentation. Ils doivent être remplacés par la classe qui hérite du membre.
abstract class Mammal
{
public abstract void Walk();
}
class Human : Mammal
{
public override void Walk()
{
}
...
}
sealed
Le sealedmodificateur peut être combiné avec les autres en tant que modificateur facultatif pour les classes afin de les rendre inhéritables.
internal sealed class _FOO
{
}
Interfaces
Les interfaces sont des structures de données qui contiennent des définitions de membres et non une implémentation réelle. Ils sont utiles lorsque vous souhaitez définir un contrat entre des membres de différents types ayant des implémentations différentes. Vous pouvez déclarer des définitions pour les méthodes, les propriétés et les indexeurs. Les membres de l'interface sont implicitement publics. Une interface peut être implicitement ou explicitement implémentée.
interface IBinaryOperation
{
double A { get; set; }
double B { get; set; }
double GetResult();
}
Implémentation d'une interface
Une interface est implémentée par une classe ou étendue par une autre interface de la même manière que vous dérivez une classe d'une autre classe en utilisant la :notation.
Mise en œuvre implicite
Lors de l'implémentation implicite d'une interface, les membres de l'interface doivent être public.
public class Adder : IBinaryOperation
{
public double A { get; set; }
public double B { get; set; }
public double GetResult()
{
return A + B;
}
}
public class Multiplier : IBinaryOperation
{
public double A { get; set; }
public double B { get; set; }
public double GetResult()
{
return A*B;
}
}
Utilisé:
IBinaryOperation op = null;
double result;
// Adder implements the interface IBinaryOperation.
op = new Adder();
op.A = 2;
op.B = 3;
result = op.GetResult(); // 5
// Multiplier also implements the interface.
op = new Multiplier();
op.A = 5;
op.B = 4;
result = op.GetResult(); // 20
Implémentation explicite
Vous pouvez également implémenter explicitement des membres. Les membres de l'interface qui sont explicitement implémentés par une classe ne sont accessibles que lorsque l'objet est géré comme le type d'interface.
public class Adder : IBinaryOperation
{
double IBinaryOperation.A { get; set; }
double IBinaryOperation.B { get; set; }
double IBinaryOperation.GetResult()
{
return ((IBinaryOperation)this).A + ((IBinaryOperation)this).B;
}
}
Utilisé:
Adder add = new Adder();
// These members are not accessible:
// add.A = 2;
// add.B = 3;
// double result = add.GetResult();
// Cast to the interface type to access them:
IBinaryOperation add2 = add;
add2.A = 2;
add2.B = 3;
double result = add2.GetResult();
Remarque : les propriétés de la classe qui s'étend IBinaryOperationsont implémentées automatiquement par le compilateur et un champ de sauvegarde est automatiquement ajouté (voir #Propriétés automatiques ).
Extension de plusieurs interfaces
Les interfaces et les classes sont autorisées à étendre plusieurs interfaces.
class MyClass : IInterfaceA, IInterfaceB
{
...
}
Voici une interface qui étend deux interfaces.
interface IInterfaceC : IInterfaceA, IInterfaceB
{
...
}
Interfaces vs classes abstraites
Les interfaces et les classes abstraites sont similaires. Ce qui suit décrit quelques différences importantes :
- Une classe abstraite peut avoir des variables membres ainsi que des méthodes ou des propriétés non abstraites. Une interface ne le peut pas.
- Une classe ou une classe abstraite ne peut hériter que d'une seule classe ou classe abstraite.
- Une classe ou une classe abstraite peut implémenter une ou plusieurs interfaces.
- Une interface ne peut qu'étendre d'autres interfaces.
- Une classe abstraite peut avoir des méthodes et des propriétés non publiques (également abstraites). Une interface ne peut avoir que des membres publics.
- Une classe abstraite peut avoir des constantes, des méthodes statiques et des membres statiques. Une interface ne le peut pas.
- Une classe abstraite peut avoir des constructeurs. Une interface ne le peut pas.
Génériques
- Il s'agit d'une fonctionnalité de C# 2.0 et .NET Framework 2.0 .
Les génériques (ou types paramétrés, polymorphisme paramétrique ) utilisent des paramètres de type, qui permettent de concevoir des classes et des méthodes qui ne spécifient pas le type utilisé tant que la classe ou la méthode n'est pas instanciée. Le principal avantage est que l'on peut utiliser des paramètres de type générique pour créer des classes et des méthodes qui peuvent être utilisées sans encourir le coût des transtypages d'exécution ou des opérations de boxe, comme indiqué ici :
// Declare the generic class.
public class GenericList<T>
{
void Add(T input) { }
}
class TestGenericList
{
private class ExampleClass { }
static void Main()
{
// Declare a list of type int.
GenericList<int> list1 = new GenericList<int>();
// Declare a list of type string.
GenericList<string> list2 = new GenericList<string>();
// Declare a list of type ExampleClass.
GenericList<ExampleClass> list3 = new GenericList<ExampleClass>();
}
}
Par rapport aux modèles C++ , les génériques C# peuvent offrir une sécurité accrue, mais ont également des capacités quelque peu limitées. Par exemple, il n'est pas possible d'appeler des opérateurs arithmétiques sur un type générique C#. Contrairement aux modèles C++, les types paramétrés .NET sont instanciés au moment de l'exécution plutôt que par le compilateur ; par conséquent, ils peuvent être multilingues alors que les modèles C++ ne le peuvent pas. Ils prennent en charge certaines fonctionnalités non prises en charge directement par les modèles C++, telles que les contraintes de type sur les paramètres génériques à l'aide d'interfaces. En revanche, C# ne prend pas en charge les paramètres génériques non typés.
Contrairement aux génériques en Java, les génériques .NET utilisent la réification pour faire des types paramétrés des objets de première classe dans la machine virtuelle Common Language Infrastructure (CLI), ce qui permet des optimisations et la préservation des informations de type.
Utiliser des génériques
Cours génériques
Les classes et les structures peuvent être génériques.
public class List<T>
{
...
public void Add(T item)
{
...
}
}
List<int> list = new List<int>();
list.Add(6);
list.Add(2);
Interfaces génériques
interface IEnumerable<T>
{
...
}
Délégués génériques
delegate R Func<T1, T2, R>(T1 a1, T2 a2);
Méthodes génériques
public static T[] CombineArrays<T>(T[] a, T[] b)
{
T[] newArray = new T[a.Length + b.Length];
a.CopyTo(newArray, 0);
b.CopyTo(newArray, a.Length);
return newArray;
}
string[] a = new string[] { "a", "b", "c" };
string[] b = new string[] { "1", "2", "3" };
string[] c = CombineArrays(a, b);
double[] da = new double[] { 1.2, 2.17, 3.141592 };
double[] db = new double[] { 4.44, 5.6, 6.02 };
double[] dc = CombineArrays(da, db);
// c is a string array containing { "a", "b", "c", "1", "2", "3"}
// dc is a double array containing { 1.2, 2.17, 3.141592, 4.44, 5.6, 6.02}
Paramètres de type
Les paramètres de type sont des noms utilisés à la place des types concrets lors de la définition d'un nouveau générique. Ils peuvent être associés à des classes ou à des méthodes en plaçant le paramètre type entre crochets angulaires . Lors de l'instanciation (ou de l'appel) d'un générique, vous pouvez alors substituer un type concret au paramètre de type que vous avez donné dans sa déclaration. Les paramètres de type peuvent être contraints à l'aide du mot - clé et d'une spécification de contrainte, n'importe laquelle des six contraintes séparées par des virgules peut être utilisée :
< >where
| Contrainte | Explication |
|---|---|
where T : struct
|
le paramètre de type doit être un type valeur |
where T : class
|
le paramètre de type doit être un type de référence |
where T : new()
|
le paramètre de type doit avoir un constructeur sans paramètre (doit apparaître en dernier) |
where T : <base_class>
|
le paramètre de type doit hériter de <base_class>
|
where T : <interface>
|
le paramètre de type doit être, ou doit implémenter cette interface |
where T : U
|
contrainte de paramètre de type nu |
Covariance et contravariance
- Il s'agit d'une fonctionnalité de C# 4.0 et .NET Framework 4.0 .
Les interfaces génériques et les délégués peuvent avoir leurs paramètres de type marqués comme covariant ou contravariant , à l'aide des mots out- clés et in, respectivement. Ces déclarations sont ensuite respectées pour les conversions de types, à la fois implicites et explicites, et à la fois à la compilation et à l'exécution. Par exemple, l'interface existante a été redéfinie comme suit :
IEnumerable<T>
interface IEnumerable<out T>
{
IEnumerator<T> GetEnumerator();
}
Par conséquent, toute classe qui implémente pour une classe est également considérée comme compatible avec toutes les classes et interfaces qui s'étendent, directement ou indirectement. En pratique, il permet d'écrire du code tel que :
IEnumerable<Derived>DerivedIEnumerable<Base>BaseDerived
void PrintAll(IEnumerable<object> objects)
{
foreach (object o in objects)
{
System.Console.WriteLine(o);
}
}
IEnumerable<string> strings = new List<string>();
PrintAll(strings); // IEnumerable<string> is implicitly converted to IEnumerable<object>
Pour la contravariance, l'interface existante a été redéfinie comme suit :
IComparer<T>
public interface IComparer<in T>
{
int Compare(T x, T y);
}
Par conséquent, toute classe qui implémente une classe est également considérée comme compatible avec pour toutes les classes et interfaces qui sont étendues à partir de . Il permet d'écrire du code tel que :
IComparer<Base>BaseIComparer<Derived>DerivedBase
IComparer<object> objectComparer = GetComparer();
IComparer<string> stringComparer = objectComparer;
Énumérateurs
Un énumérateur est un itérateur. Les énumérateurs sont généralement obtenus en appelant la méthode d'un objet implémentant l' interface. Les classes de conteneur implémentent généralement cette interface. Cependant, l' instruction foreach en C# peut opérer sur n'importe quel objet fournissant une telle méthode, même si elle n'implémente pas . Cette interface a été étendue en version générique dans .NET 2.0 .
GetEnumerator()IEnumerableIEnumerable
Ce qui suit montre une utilisation simple des itérateurs en C# 2.0 :
// explicit version
IEnumerator<MyType> iter = list.GetEnumerator();
while (iter.MoveNext())
Console.WriteLine(iter.Current);
// implicit version
foreach (MyType value in list)
Console.WriteLine(value);
Fonctionnalité du générateur
- Il s'agit d'une fonctionnalité de C# 2.0 .
Le .NET 2.0 Framework a permis à C# d'introduire un itérateur qui fournit des fonctionnalités de générateur , en utilisant une construction similaire à celle de Python . Avec a , la fonction garde automatiquement son état pendant l'itération.
yield returnyieldyield return
// Method that takes an iterable input (possibly an array)
// and returns all even numbers.
public static IEnumerable<int> GetEven(IEnumerable<int> numbers)
{
foreach (int i in numbers)
{
if (i%2 == 0)
yield return i;
}
}
//using the method to output only even numbers from the array
static void Main()
{
int[] numbers = { 1, 2, 3, 4, 5, 6};
foreach (int i in GetEven(numbers))
Console.WriteLine(i); //outputs 2, 4 and 6
}
LINQ
- Il s'agit d'une fonctionnalité de C# 3.0 et .NET Framework 3.0 .
LINQ, abréviation de Language Integrated Queries, est une fonctionnalité .NET Framework qui simplifie la gestion des données. Il ajoute principalement un support qui vous permet d'interroger des tableaux, des collections et des bases de données. Il introduit également des classeurs, ce qui facilite l'accès aux bases de données et à leurs données.
Syntaxe de requête
La syntaxe de requête LINQ a été introduite dans C# 3.0 et vous permet d'écrire des requêtes de type SQL en C#.
var list = new List<int>{ 2, 7, 1, 3, 9 };
var result = from i in list
where i > 1
select i;
Les instructions sont compilées en appels de méthode, où presque seuls les noms des méthodes sont spécifiés. Les méthodes finalement utilisées sont déterminées par la résolution de surcharge normale. Ainsi, le résultat final de la traduction est affecté par les symboles qui sont dans la portée.
Ce qui diffère de SQL, c'est que l'instruction from vient en premier et non en dernier comme dans SQL. C'est parce qu'il semble plus naturel d'écrire comme celui-ci en C# et prend en charge "Intellisense" (Complétion de code dans l'éditeur).
Méthodes anonymes
Les méthodes anonymes , ou dans leur forme actuelle plus communément appelées "expressions lambda", sont une fonctionnalité qui vous permet d'écrire des fonctions de type fermeture en ligne dans votre code.
Il existe différentes manières de créer des méthodes anonymes. Avant C# 3.0, la prise en charge des délégués était limitée.
Délégués anonymes
- Il s'agit d'une fonctionnalité de C# 2.0 .
Les délégués anonymes sont des pointeurs de fonctions qui contiennent des méthodes anonymes. L'objectif est de simplifier l'utilisation des délégués en simplifiant le processus d'attribution de la fonction. Au lieu de déclarer une méthode distincte dans le code, le programmeur peut utiliser la syntaxe pour écrire le code en ligne et le compilateur générera ensuite une fonction anonyme pour celle-ci.
Func<int, int> f = delegate(int x) { return x*2; };
Expressions lambda
- Il s'agit d'une fonctionnalité de C# 3.0 .
Les expressions lambda fournissent une syntaxe simple pour les fonctions en ligne similaires aux fermetures. Les fonctions avec paramètres déduisent le type des paramètres si other n'est pas explicitement spécifié.
// [arguments] => [method-body]
// With parameters
n => n == 2
(a, b) => a + b
(a, b) => { a++; return a + b; }
// With explicitly typed parameters
(int a, int b) => a + b
// No parameters
() => return 0
// Assigning lambda to delegate
Func<int, int, int> f = (a, b) => a + b;
Les lambdas multi-instructions ont des corps entourés d'accolades et à l'intérieur d'eux, le code peut être écrit comme dans les méthodes standard.
(a, b) => { a++; return a + b; }
Les expressions lambda peuvent être transmises en tant qu'arguments directement dans les appels de méthode similaires aux délégués anonymes mais avec une syntaxe plus esthétique.
var list = stringList.Where(n => n.Length > 2);
Les expressions lambda sont essentiellement des méthodes générées par le compilateur qui sont transmises via des délégués. Ces méthodes sont réservées au compilateur uniquement et ne peuvent être utilisées dans aucun autre contexte.
Méthodes d'extension
- Il s'agit d'une fonctionnalité de C# 3.0 .
Les méthodes d'extension sont une forme de sucre syntaxique donnant l'illusion d'ajouter de nouvelles méthodes à la classe existante en dehors de sa définition. En pratique, une méthode d'extension est une méthode statique qui peut être appelée comme s'il s'agissait d'une méthode d'instance ; le destinataire de l'appel est lié au premier paramètre de la méthode, décoré du mot this- clé :
public static class StringExtensions
{
public static string Left(this string s, int n)
{
return s.Substring(0, n);
}
}
string s = "foo";
s.Left(3); // same as StringExtensions.Left(s, 3);
Voir également
Fonctions locales
- Il s'agit d'une fonctionnalité de C# 7.0.
Les fonctions locales peuvent être définies dans le corps d'un autre getter et setter de méthode, de constructeur ou de propriété. De telles fonctions ont accès à toutes les variables de la portée englobante, y compris les variables locales de la méthode parent. Ils sont dans la portée de l'ensemble de la méthode, qu'ils soient invoqués avant ou après leur déclaration. Les modificateurs d'accès (public, private, protected) ne peuvent pas être utilisés avec les fonctions locales. De plus, ils ne prennent pas en charge la surcharge de fonctions . Cela signifie qu'il ne peut pas y avoir deux fonctions locales dans la même méthode avec le même nom même si les signatures ne se chevauchent pas. Après une compilation, une fonction locale est transformée en une méthode statique privée, mais une fois définie, elle ne peut pas être marquée comme statique.
Dans l'exemple de code ci-dessous, la méthode Sum est une fonction locale à l'intérieur de la méthode Main. Il ne peut donc être utilisé qu'à l'intérieur de sa méthode parent Main :
static void Main(string[] args)
{
int Sum(int x, int y)
{
return x + y;
}
Console.WriteLine(Sum(10, 20));
Console.ReadKey();
}
Divers
Blocs de fermeture
C# implémente des blocs de fermeture au moyen de l' usinginstruction . L' usinginstruction accepte une expression qui aboutit à un objet implémentant IDisposable, et le compilateur génère du code qui garantit la usingsuppression de l'objet lorsque la portée de l' instruction est quittée. L' usingénoncé est du sucre syntaxique . Cela rend le code plus lisible que le bloc équivalent .
try ... finally
public void Foo()
{
using (var bar = File.Open("Foo.txt"))
{
// do some work
throw new Exception();
// bar will still get properly disposed.
}
}
Synchronisation des threads
C# fournit la lockdéclaration , qui est encore un autre exemple de sucre syntaxique bénéfique. Il fonctionne en marquant un bloc de code comme une section critique par exclusion mutuelle d'accès à un objet fourni. Comme l' usinginstruction, cela fonctionne par le compilateur générant un bloc à sa place.
try ... finally
private static StreamWriter _writer;
public void ConcurrentMethod()
{
lock (_writer)
{
_writer.WriteLine("Line 1.");
_writer.WriteLine("Followed by line 2.");
}
}
Les attributs
Les attributs sont des entités de données qui sont stockées en tant que métadonnées dans l'assembly compilé. Un attribut peut être ajouté aux types et aux membres comme les propriétés et les méthodes. Les attributs peuvent être utilisés pour une meilleure maintenance des directives du préprocesseur.
[CompilerGenerated]
public class $AnonymousType$120
{
[CompilerGenerated]
public string Name { get; set; }
}
Le .NET Framework est fourni avec des attributs prédéfinis qui peuvent être utilisés. Certains d'entre eux jouent un rôle important au moment de l'exécution tandis que d'autres servent uniquement à la décoration syntaxique dans le code comme CompilerGenerated. Il marque seulement qu'il s'agit d'un élément généré par le compilateur. Des attributs définis par le programmeur peuvent également être créés.
Un attribut est essentiellement une classe qui hérite de la classe. Par convention, les classes d'attributs se terminent par "Attribute" dans leur nom. Cela ne sera pas nécessaire lors de son utilisation.
System.Attribute
public class EdibleAttribute : Attribute
{
public EdibleAttribute() : base()
{
}
public EdibleAttribute(bool isNotPoisonous)
{
this.IsPoisonous = !isNotPoisonous;
}
public bool IsPoisonous { get; set; }
}
Affichage de l'attribut utilisé à l'aide des paramètres de constructeur facultatifs.
[Edible(true)]
public class Peach : Fruit
{
// Members if any
}
Préprocesseur
C# propose des "directives de préprocesseur" (bien qu'il n'ait pas de préprocesseur réel) basées sur le préprocesseur C qui permettent aux programmeurs de définir des symboles , mais pas des macros. Des conditions telles que #if, #endif, et #elsesont également fournies.
Des directives telles que #regiondonnent des conseils aux éditeurs pour le pliage de code . Le #regionbloc doit se terminer par une #endregiondirective.
public class Foo
{
#region Constructors
public Foo() {}
public Foo(int firstParam) {}
#endregion
#region Procedures
public void IntBar(int firstParam) {}
public void StrBar(string firstParam) {}
public void BoolBar(bool firstParam) {}
#endregion
}
Commentaires sur les codes
C# utilise une double barre oblique ( //) pour indiquer que le reste de la ligne est un commentaire.
public class Foo
{
// a comment
public static void Bar(int firstParam) {} // Also a comment
}
Les commentaires sur plusieurs lignes peuvent être indiqués par un slash/astérisque de début ( /*) et un astérisque/barre oblique de fin ( */).
public class Foo
{
/* A Multi-Line
comment */
public static void Bar(int firstParam) {}
}
Les commentaires ne s'emboîtent pas. Ce sont deux commentaires simples :
// Can put /* */ */ */ /* /*
/* Can put /* /* /* but it ends with */
Les commentaires sur une seule ligne commençant par trois barres obliques sont utilisés pour la documentation XML. Ceci, cependant, est une convention utilisée par Visual Studio et ne fait pas partie de la définition du langage :
/// <summary>
/// This class is very classy.
/// </summary>
Système de documentation XML
Le système de documentation de C# est similaire à Javadoc de Java , mais basé sur XML . Deux méthodes de documentation sont actuellement prises en charge par le compilateur C# .
Les commentaires de documentation sur une seule ligne, tels que ceux que l'on trouve couramment dans le code généré par Visual Studio , sont indiqués sur une ligne commençant par // /.
public class Foo
{
// / <summary>A summary of the method.</summary>
// / <param name="firstParam">A description of the parameter.</param>
// / <remarks>Remarks about the method.</remarks>
public static void Bar(int firstParam) {}
}
Les commentaires de documentation multilignes, bien que définis dans la spécification du langage de la version 1.0, n'étaient pas pris en charge jusqu'à la version .NET 1.1. Ces commentaires sont désignés par une barre oblique de départ/un astérisque/un astérisque ( /**) et un astérisque/une barre oblique de fin ( */).
public class Foo
{
/** <summary>A summary of the method.</summary>
* <param name="firstParam">A description of the parameter.</param>
* <remarks>Remarks about the method.</remarks> */
public static void Bar(int firstParam) {}
}
Il existe des critères stricts concernant les espaces blancs et la documentation XML lors de l'utilisation de la technique barre oblique/astérisque/astérisque ( /**).
Ce bloc de code :
/**
* <summary>
* A summary of the method.</summary>*/
produit un commentaire XML différent de ce bloc de code :
/**
* <summary>
A summary of the method.</summary>*/
La syntaxe des commentaires de documentation et de leur balisage XML est définie dans une annexe non normative de la norme ECMA C#. La même norme définit également des règles pour le traitement de ces commentaires et leur transformation en un document XML simple avec des règles précises pour le mappage des identifiants Common Language Infrastructure (CLI) à leurs éléments de documentation associés. Cela permet à n'importe quel environnement de développement intégré (IDE) C# ou à tout autre outil de développement de trouver la documentation pour n'importe quel symbole dans le code d'une certaine manière bien définie.
Syntaxe d'attente asynchrone
- Il s'agit d'une fonctionnalité de C# 5.0 et .NET Framework 4.0 .
À partir de .NET Framework 4, il existe une bibliothèque de tâches qui facilite l'écriture d'applications parallèles et multithread via des tâches.
C# 5.0 prend en charge l'asynchronie en langue native.
Considérez ce code qui profite directement de la bibliothèque de tâches :
public static class SomeAsyncCode
{
public static Task<XDocument> GetContentAsync()
{
HttpClient httpClient = new HttpClient();
return httpClient.GetStringAsync("www.contoso.com").ContinueWith((task) => {
string responseBodyAsText = task.Result;
return XDocument.Parse(responseBodyAsText);
});
}
}
var t = SomeAsyncCode.GetContentAsync().ContinueWith((task) => {
var xmlDocument = task.Result;
});
t.Start();
Voici la même logique écrite dans la syntaxe async-await :
public static class SomeAsyncCode
{
public static async Task<XDocument> GetContentAsync()
{
HttpClient httpClient = new HttpClient();
string responseBodyAsText = await httpClient.GetStringAsync("www.contoso.com");
return XDocument.Parse(responseBodyAsText);
}
}
var xmlDocument = await SomeAsyncCode.GetContentAsync();
// The Task will be started on call with await.
Dialectes
Spécification #
Spec# est un dialecte de C# qui est développé en parallèle avec l'implémentation standard de Microsoft. Il étend C# avec des fonctionnalités de langage de spécification et est une future fonctionnalité possible du langage C#. Il ajoute également une syntaxe pour l'API de contrats de code qui a été introduite dans .NET Framework 4.0 . Spec# est développé par Microsoft Research .
Cet exemple montre deux des structures de base utilisées lors de l'ajout de contrats à votre code.
static void Main(string![] args)
requires args.Length > 0
{
foreach(string arg in args)
{
}
}
-
!est utilisé pour rendre un type de référence non nullable, par exemple, vous ne pouvez pas définir la valeur surnull. Ceci contrairement aux types nullables qui permettent de définir les types de valeur commenull. -
requiresindique une condition qui doit être respectée dans le code. Dans ce cas, la longueur des arguments ne doit pas être égale ou inférieure à zéro.
Types non nullables
Spec# étend C# avec des types non nullables qui vérifient simplement que les variables des types nullables qui ont été définies comme non nullables ne sont pas null. Si est nullalors une exception sera levée.
string! input
Utilisé:
public Test(string! input)
{
...
}
Conditions préalables
Les préconditions sont vérifiées avant l'exécution d'une méthode.
public Test(int i)
requires i > 0;
{
this.i = i;
}
Postconditions
Les post-conditions sont des conditions dont on s'assure qu'elles sont correctes lorsqu'une méthode a été exécutée.
public void Increment()
ensures i > 0;
{
i++;
}
Exceptions vérifiées
Spec# ajoute des exceptions vérifiées comme celles de Java .
public void DoSomething()
throws SomeException; // SomeException : ICheckedException
{
...
}
Les exceptions vérifiées sont problématiques, car lorsqu'une fonction de niveau inférieur ajoute un nouveau type d'exception, toute la chaîne de méthodes utilisant cette méthode à un niveau inférieur imbriqué doit également modifier son contrat. Cela viole le principe ouvert/fermé .
Voir également
Les références
- Archer, Tom (2001). À l'intérieur de C# . Microsoft Press. ISBN 0-7356-1288-9.
- Bart de Smet sur Spec#