Article de reference

Syntaxe C Sharp

( Learn how and when to remove this message ) Cet article décrit la syntaxe du langage de programmation C# . Les fonctionnalités décrites sont compatibles avec .NET Framework et...

(Learn how and when to remove this message)

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 dans le code . Il peut contenir des lettres, des chiffres et des traits de soulignement ( _), et est sensible à la casse ( FOOest différent de foo). Le langage impose les restrictions suivantes aux noms d'identifiants :

  • Ils ne peuvent pas commencer par un chiffre ;
  • Ils ne peuvent pas commencer par un symbole, à moins qu’il ne s’agisse d’un mot-clé ;
  • Ils ne peuvent pas contenir plus de 511 caractères .

Les noms d'identifiant peuvent être préfixés par un signe @ ( @), mais cela n'a aucune importance ; @nameest le même identifiant que name.

Microsoft a publié des conventions de dénomination pour les identifiants en C#, qui recommandent l'utilisation de PascalCase pour les noms de types et de la plupart des membres de type, et de camelCase pour les variables et pour les champs privés ou internes. Cependant, ces conventions de dénomination ne sont pas appliquées dans le langage.

Mots clés

Les mots-clés sont des mots réservés prédéfinis avec une signification syntaxique particulière. Le langage possède 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 et qu'il est identique à un mot-clé réservé, il peut être préfixé par un signe at pour le distinguer. Par exemple, @outest interprété comme un identifiant, tandis que outcomme un mot-clé. Cette syntaxe facilite la réutilisation du code .NET écrit dans d'autres langages.

Les mots-clés C# suivants sont des mots réservés :

  • abstract
  • as
  • base
  • bool
  • break
  • byte
  • case
  • catch
  • char
  • checked
  • class
  • const
  • continue
  • decimal
  • default
  • delegate
  • do
  • double
  • else
  • enum
  • event
  • explicit
  • extern
  • false
  • finally
  • fixed
  • float
  • for
  • foreach
  • goto
  • if
  • implicit
  • in
  • int
  • interface
  • internal
  • is
  • lock
  • long
  • namespace
  • new
  • null
  • object
  • operator
  • out
  • override
  • params
  • private
  • protected
  • public
  • readonly
  • ref
  • return
  • sbyte
  • sealed
  • short
  • sizeof
  • stackalloc
  • static
  • string
  • struct
  • switch
  • this
  • throw
  • true
  • try
  • typeof
  • uint
  • ulong
  • unchecked
  • unsafe
  • ushort
  • using
  • virtual
  • void
  • volatile
  • while

Un mot-clé contextuel est utilisé pour fournir une signification spécifique dans le code, mais ce n'est pas un mot réservé en C#. Certains mots-clés contextuels, tels que partialet where, ont des significations spéciales dans plusieurs contextes. Les mots-clés C# suivants sont contextuels :

  • add
  • allows
  • alias
  • and
  • ascending
  • args
  • async
  • await
  • by
  • descending
  • dynamic
  • equals
  • from
  • get
  • global
  • group
  • init
  • into
  • join
  • let
  • managed
  • nameof
  • nint
  • not
  • notnull
  • nuint
  • on
  • or
  • orderby
  • partial
  • record
  • remove
  • required
  • select
  • set
  • unmanaged
  • value
  • var
  • when
  • where
  • with
  • yield

Littéraux

Séparateurs de chiffres

À partir de C# 7.0, le symbole de soulignement peut être utilisé pour séparer les chiffres dans les valeurs numériques à des fins de lisibilité. Le compilateur ignore ces traits de soulignement.

int bin = 0 b1101_0010_1011_0100 ; int hex = 0 x2F_BB_4A_F1 ; int dec = 1 _000_500_954 ; double real = 1 _500 . 200 _2e - 1 _000 ;

En général, il ne peut être placé 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) ou à côté du spécificateur de type ( 10_f).

Variables

Les variables sont des identifiants associés à des valeurs. Elles sont déclarées en écrivant le type et le nom de la variable, et sont éventuellement initialisées dans la même instruction.

Déclarer

int myInt ; // Déclaration d'une variable non initialisée appelée 'myInt', de type 'int'

Affectation

int myInt ; // Déclaration d'une variable non initialisée myInt = 35 ; // Affectation d'une valeur à la variable

Initialiser

int myInt = 35 ; // Déclaration et initialisation de la variable

Plusieurs variables du même type peuvent être déclarées et initialisées dans une seule instruction.

int a , b ; // Déclaration de plusieurs variables du même type
int a = 2 , b = 3 ; // Déclaration et initialisation de plusieurs variables du même 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 de remplacer le spécificateur de type d'une déclaration de variable 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 mesChars = new char [] { 'A' , 'Ö' }; // ou char[] mesChars = new char[] {'A', 'Ö'};
var myNums = new Liste < int > (); // ou Liste<int> myNums = new Liste<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é comme préfixe, la valeur doit être donnée lors de la déclaration. Après cela, elle est verrouillée et ne peut pas changer. Elles peuvent être déclarées dans le contexte comme un champ ou une variable locale. Les constantes sont implicitement statiques.

constante double PI = 3,14 ;

Ceci montre les deux utilisations du mot-clé.

classe publique Foo { const privée double X = 3 ;
public Foo () { const int y = 2 ; } }

readonly

Le readonlymot-clé fait une chose similaire aux champs. Comme les champs marqués comme tels, constils ne peuvent pas changer une fois initialisés. La différence est que l'on peut choisir de les initialiser dans un constructeur, ou à une valeur qui n'est pas connue avant l'exécution. Cela ne fonctionne que sur les champs. readonlyLes champs peuvent être soit des membres d'une instance, soit des membres de classe statiques.

Blocs de code

Les accolades sont utilisées pour indiquer un bloc de code et une nouvelle portée . Les membres de classe et le corps d'une méthode sont des exemples de ce qui peut se trouver à l'intérieur de ces accolades dans divers contextes. {...}

À l’intérieur des corps de méthode, les accolades peuvent être utilisées pour créer de nouvelles portées :

void FaireQuelqueChose () { int a ;
{ 
int b ; a = 1 ; }
a = 2 ; b = 3 ; // Échouera car la variable est déclarée dans une portée interne. }

Structure du programme

L'application AC# se compose de classes et de leurs membres. Les classes et autres types existent dans des espaces de noms mais peuvent également être imbriqués dans d'autres classes.

Méthode principale

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 d'une application C# est la Mainméthode. Il ne peut y avoir qu'une seule déclaration de cette méthode, et il s'agit d'une méthode statique dans une classe. Elle renvoie généralement voiddes arguments de ligne de commande et les reçoit sous forme de tableau de chaînes.

static void Main ( string [] args ) // string[] args peut être omis si le programme n'a pas d'arguments de ligne de commande. { }

La méthode principale est également autorisée à renvoyer une valeur entière si elle est spécifiée.

statique int Main ( chaîne [] args ) { renvoie 0 ; }

Main asynchrone

Il s'agit d'une fonctionnalité de C# 7.1.

Les tâches asynchrones peuvent être attendues dans la Mainméthode en déclarant le type de retour de la méthode comme Task.

tâche statique asynchrone principale ( chaîne [] args ) { wait 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 des classes.

Cela a été introduit pour rendre C# moins verbeux et donc plus accessible aux débutants pour démarrer.

en utilisant le système ;
Console . WriteLine ( "Bonjour tout le monde !" );

Les types sont déclarés après les instructions et seront automatiquement disponibles à partir des instructions situées au-dessus d'eux.

Espaces de noms

Les espaces de noms font partie d'un nom de type et sont utilisés pour regrouper et/ou distinguer les entités nommées des autres.

System . IO . DirectoryInfo // DirectoryInfo se trouve dans l'espace de noms System.IO

Un espace de noms est défini comme ceci :

espace de noms FooNamespace { // Membres }

Un espace de noms peut être placé à l'intérieur d'un espace de noms comme ceci :

espace de noms FooNamespace { // Membres espace de noms BarNamespace { // Membres } }

Cela peut également être fait comme ceci :

espace de noms FooNamspace { // Membres }
espace de noms FooNamspace.BarNamespace { // Membres }

Dans C# 10 et versions ultérieures, les espaces de noms peuvent également être définis à l'aide de déclarations de portée de fichier en procédant comme suit :

espace de noms FooNamespace ; // les crochets sont omis ici au profit d'un point-virgule.

usingdirectif

La usingdirective charge un espace de noms spécifique à partir d'un assemblage référencé. Elle est généralement placée en haut (ou en-tête) d'un fichier de code, mais elle peut être placée ailleurs si nécessaire, par exemple à l'intérieur des classes.

en utilisant System ; en utilisant System.Collections ;

La directive peut également être utilisée pour définir un autre nom pour un espace de noms ou un type existant. Cela est parfois utile lorsque les noms sont trop longs et moins lisibles.

en utilisant Net = System . Net ; en utilisant DirInfo = System . IO . DirectoryInfo ;

usingstaticdirectif

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.usingstatic

en utilisant la console système statique ;
WriteLine ( "Bonjour le monde !" );

Opérateurs

Surcharge de l'opérateur

Certains des opérateurs existants peuvent être surchargés en écrivant une méthode de surcharge.

opérateur public static Foo + ( Foo foo , Bar bar ) { renvoie un nouveau Foo ( foo . Value + bar . Value ); }

Voici les opérateurs surchargeables :

  • 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 conversion ( ) ne peuvent pas être surchargés, mais il est possible de définir des opérateurs de conversion.()
  • L'opérateur d'indexation de tableau ( [ ]) n'est pas surchargé, mais il est possible de définir de nouveaux indexeurs.

Opérateurs de conversion

L'opérateur de conversion n'est pas surchargé, mais on peut é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 la conversion sans spécifier l'opérateur de conversion ( ) et l'opérateur explicite nécessite son utilisation. ()

Opérateur de conversion implicite

classe Foo { public int Value ;
opérateur implicite statique public Foo ( int valeur ) { return new Foo ( valeur ); } } // Conversion implicite Foo foo = 2 ;

Opérateur de conversion explicite

classe Foo { public int Value ;
opérateur explicite statique public Foo ( int valeur ) { return new Foo ( valeur ); } } // Conversion explicite Foo foo = ( Foo ) 2 ;

asopérateur

L' asopérateur va tenter d'effectuer un cast silencieux vers un type donné. Il renverra l'objet en tant que nouveau type si possible, et sinon renverra null.

Stream stream = Fichier . Open ( @"C:\Temp\data.dat" ); FileStream fstream = stream as FileStream ; // Renverra un objet.
Chaîne str = flux comme chaîne ; // Renverra null.

Opérateur de coalescence nul

Il s'agit d'une fonctionnalité de C# 2.0 .

Ce qui suit:

retourner ifNotNullValue ?? elseValue ;

est l'abréviation de :

retourner ifNotNullValue != null ? ifNotNullValue : elseValue ;

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 l'affectation de fusion nulle , telle que

variable ??= sinonValeur ;

est équivalent à

si ( la variable est nulle ) variable = elseValue ;

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 flux du programme à travers des conditions données.

ifdéclaration

L' ifinstruction est saisie lorsque la condition donnée est vraie. Les instructions de cas sur une seule ligne ne nécessitent pas de parenthèses en bloc, bien que cela soit généralement préféré par convention.

Déclaration simple en une seule ligne :

si ( i == 3 ) ... ;

Multi-ligne avec else-block (sans aucune accolade) :

si ( i == 2 ) ... sinon ...

Conventions de codage recommandées pour une instruction if.

si ( i == 3 ) { ... } sinon si ( i == 2 ) { ... } sinon { ... }

switchdé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 donc le mot-clé breakest généralement utilisé pour terminer un cas. Une incondition returndans une section de cas peut également être utilisée pour terminer un cas. Voir aussi comment gotoune instruction 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' : // Une section switch peut avoir plusieurs étiquettes de cas. case 'D' : ... break ; default : ... break ; }

Structures d'itération

Les instructions d'itération sont des instructions qui sont exécutées de manière répétée lorsqu'une condition donnée est évaluée comme vraie.

whileboucle

tant que ( i == vrai ) { ... }

do ... whileboucle

faire 
{
} 
tandis que ( i == vrai );

forboucle

La forboucle se compose de trois parties : déclaration , condition et expression de compteur . Chacune d'entre elles peut être omise car elles sont facultatives.

pour ( 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 ++ ; }

foreachboucle

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 sur lesquels itérer.

Chaque élément de la collection donnée sera renvoyé et accessible dans le contexte du bloc de code. Une fois le bloc exécuté, l'élément suivant sera renvoyé jusqu'à ce qu'il ne reste plus d'éléments.

foreach ( int i dans intList ) { ... }

Déclarations de saut

Les instructions de saut sont héritées du langage C/C++ et, par la suite, des langages assembleurs. Elles représentent simplement les instructions de saut d'un langage assembleur qui contrôle le déroulement d'un programme.

Étiquettes et gotodéclaration

Les étiquettes sont des points donnés dans le code auxquels on peut accéder en utilisant l' gotoinstruction.

début : 
....... aller au début ;

Notez que l'étiquette n'a pas besoin d'être positionnée après l' gotoinstruction ; elle peut être avant celle-ci dans le fichier source.

L' gotoinstruction peut être utilisée dans switchdes instructions 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 : // La compilation échouera ici car les cas ne peuvent pas échouer en C#. Console.WriteLine ( " Case 4" ); goto default ; // C'est la bonne façon de passer au cas suivant. case 5 : // Plusieurs étiquettes pour le même code sont OK case 6 : default : Console.WriteLine ( "Default" ); break ; // Même default ne doit pas atteindre le point final }

breakdéclaration

L' breakinstruction sort de la boucle ou switchde l'instruction la plus proche. L'exécution continue dans l'instruction suivant l'instruction terminée, le cas échéant.

int e = 10 ; for ( int i = 0 ; i < e ; i ++ ) { while ( true ) { break ; } // S'arrêtera à ce point. }

continuedéclaration

L' continueinstruction interrompt l'itération en cours de l'instruction de contrôle actuelle et commence l'itération suivante.

int ch ; while (( ch = Console . Read ()) != - 1 ) { if ( ch == ' ' ) continue ; // Ignore le reste de la boucle while
// Reste de la boucle while 
... }

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 de C++.

La bibliothèque de classes de base possède 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 :

lancer une nouvelle NotImplementedException ();

try...catch...finallydéclarations

Les exceptions sont gérées au sein de blocs. try...catch

try 
{ 
// Instructions qui peuvent générer des exceptions ... } catch ( Exception ex ) { // Exception interceptée et gérée ici ... } finally { // Instructions toujours exécutées après les blocs try/catch ... }

Les instructions contenues dans le trybloc sont exécutées et si l'une d'entre elles génère 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 générée est exécuté.

Si aucun catchbloc ne correspond au type de l'exception levée, l'exécution du bloc (ou de la méthode) externe contenant l' try ... catchinstruction est interrompue et l'exception est transmise vers le haut et en dehors du bloc ou de la méthode contenant. L'exception est propagée vers le haut dans la pile d'appels jusqu'à ce qu'un catchbloc 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'aucun bloc correspondant ne soit trouvé, l'ensemble du programme est terminé et une description textuelle de l'exception est écrite dans le flux de sortie standard. Main()catch

Les instructions contenues dans le finallybloc sont toujours exécutées après les blocs tryet catch, qu'une exception ait été levée ou non. Ces blocs sont utiles pour fournir un code de nettoyage.

Soit un catchbloc, soit un finallybloc, soit les deux, doivent suivre le trybloc.

Types

C# est un langage typé statiquement comme C et C++. Cela signifie que chaque variable et constante obtient un type fixe lors de sa déclaration. Il existe deux types : les types valeur et les types référence .

Types de valeur

Les instances de types de valeur résident sur la pile, c'est-à-dire qu'elles sont liées à leurs variables. Si l'on déclare une variable pour un type de 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 structures sont des types de valeur définis par l'utilisateur qui sont déclarés à l'aide structdu mot-clé . Elles sont très similaires aux classes mais conviennent mieux aux types légers. Certaines différences syntaxiques importantes entre une classe et une structure sont présentées plus loin dans cet article.

structure Foo { ... }

Les types de données primitifs sont tous des structures.

Types prédéfinis

Ce sont les types de données primitifs.

Remarque : string ( ) n’est pas une structure et n’est pas un type primitif. System.String

Énumérations

Les types énumérés (déclarés avec enum) sont des valeurs nommées représentant des valeurs entières.

enum Saison { Hiver = 0 , Printemps = 1 , Été = 2 , Automne = 3 , Automne = Autumn // L'automne s'appelle Fall en anglais américain. }

Les variables d'énumération sont initialisées par défaut à zéro. Elles peuvent être assignées ou initialisées aux valeurs nommées définies par le type d'énumération.

Saison saison ; saison = Saison . Printemps ;

Les variables de type enum sont des valeurs entières. L'addition et la soustraction entre variables du même type sont autorisées sans conversion spécifique, mais la multiplication et la division sont un peu plus risquées et nécessitent une conversion explicite. Les conversions sont également requises pour convertir des variables enum vers et depuis des types entiers. Cependant, la conversion ne génèrera pas d'exception si la valeur n'est pas spécifiée par la définition de type.

season = ( Season ) 2 ; // convertit 2 en une valeur d'énumération de type Season. season = season + 1 ; // Ajoute 1 à la valeur. season = season + season2 ; // Ajout des valeurs de deux variables d'énumération. int value = ( int ) season ; // Conversion de la valeur d'énumération en valeur entière.
saison ++ ; // Season.Spring (1) devient Season.Summer (2). saison -- ; // Season.Summer (2) devient Season.Spring (1).

Les valeurs peuvent être combinées à l'aide de l'opérateur OU au niveau du bit |.

Couleur myColors = Couleur . Vert | Couleur . Jaune | Couleur . Bleu ;

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 attribué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érence, l'objet est marqué comme un déchet. Le ramasse-miettes le récupérera et le détruira bientôt.

Une variable de référence est nulle lorsqu'elle ne fait référence à aucun objet.

Tableaux

Un type de tableau est un type de 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 en Java. System.Array

Un tableau en C# est ce qu'on appellerait un tableau dynamique en C++.

int [] nombres = nouveau int [ 2 ]; nombres [ 0 ] = 2 ; nombres [ 1 ] = 5 ; int x = nombres [ 0 ];
Initialiseurs

Les initialiseurs de tableaux fournissent une syntaxe pratique pour l'initialisation des tableaux.

// Syntaxe longue 
int [] numbers = new int [ 5 ]{ 20 , 1 , 42 , 15 , 34 }; // Syntaxe courte int [] numbers2 = { 20 , 1 , 42 , 15 , 34 }; // Syntaxe inférée var numbers3 = new [] { 20 , 1 , 42 , 15 , 34 };
Tableaux multidimensionnels

Les tableaux peuvent avoir plus d'une dimension, par exemple 2 dimensions pour représenter une grille.

int [,] nombres = nouveau int [ 3 , 3 ]; nombres [ 1 , 2 ] = 2 ;
int [,] nombres2 = nouveau int [ 3 , 3 ] { { 2 , 3 , 2 }, { 1 , 2 , 6 }, { 2 , 4 , 5 } };

Voir aussi

Cours

Les classes sont des types de référence définis par l'utilisateur et autodescriptifs. Essentiellement, tous les types du .NET Framework sont des classes, y compris les structures et les é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 n'importe quel descendant de la classe.

Cordes

La classe, ou simplement , représente une séquence immuable de caractères Unicode ( ). System.Stringstringchar

Les actions effectuées sur une chaîne renverront toujours une nouvelle chaîne.

chaîne texte = "Bonjour tout le monde !" ; chaîne substr = texte . Substring ( 0 , 5 ); chaîne [] parts = texte . Split ( nouveau char []{ ' ' });

La classe peut être utilisée lorsqu'une « chaîne » mutable est souhaitée. System.StringBuilder

var 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 d'un type d'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 .

classe Programme { // Type de délégué : délégué int Operation ( int a , int b );
statique int Add ( int i1 , int i2 ) { retour i1 + i2 ; }
statique int Sub ( int i1 , int i2 ) { retour i1 - i2 ; }
static void Main () { // Instanciez le délégué et assignez-lui la méthode. Opération op = Add ;
// Appelez la méthode vers laquelle pointe le délégué. 
int result1 = op ( 2 , 3 ); // 5
op = Sub ; int résultat2 = op ( 10 , 2 ); // 8 } }

Initialisation du délégué avec une méthode anonyme.

addition = délégué ( int a , int b ) { retour 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 des pointeurs de méthode à un identifiant. Cela peut donc être considéré comme une extension des délégués . Ils sont généralement utilisés comme déclencheurs dans le développement d'interface utilisateur. La forme utilisée dans C# et le reste de l' infrastructure de langage commun est basée sur celle du Visual Basic classique .

délégué void MouseEventHandler ( objet expéditeur , MouseEventArgs e );
classe publique Button : System . Windows . Controls . Control { événement privé MouseEventHandler _onClick ;
/* Fonction de déclenchement imaginaire */ 
void Click () { _onClick ( this , new MouseEventArgs ( data )); } }

Un événement nécessite un gestionnaire d'événements associé 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 d'é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 de la CLI.

Une fois déclaré dans sa classe, le seul moyen d'invoquer l'événement est depuis 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é.

classe publique MainWindow : System . Windows . Controls . Window { private Button _button1 ;
public MainWindow () { _button1 = new Button ( ); _button1.Text = " Cliquez sur moi !" ;
/* S'abonner à l'événement */ 
_button1 . ClickEvent += Button1_OnClick ;
/* Syntaxe alternative considérée comme ancienne : 
 _button1.MouseClick += new MouseEventHandler(Button1_OnClick); */ 
}
protected void Button1_OnClick ( objet expéditeur , MouseEventArgs e ) { MessageBox . Show ( "Clic !" ); } }

L'implémentation d'événements personnalisés est également possible :

Gestionnaire d'événements privé _clickHandles = ( s , e ) => { };
événement public EventHandler Cliquez sur { ajouter { // Du code à exécuter lorsque le gestionnaire est ajouté... ...
_clickHandles += value ; } remove { // Du code à exécuter lorsque le gestionnaire est supprimé... ...
_clickHandles -= valeur ; } }

Voir aussi

Types nullables

Il s'agit d'une fonctionnalité de C# 2.0 .

Les types Nullable ont été introduits dans C# 2.0 dans un premier temps pour permettre aux types de valeur d'être nuls (utile lorsque l'on travaille 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# possède et autorise les pointeurs vers des types sélectionnés (certains primitifs, énumérations, chaînes, pointeurs et même tableaux et structures s'ils ne contiennent que des types qui peuvent être pointés ) dans un contexte non sécurisé : méthodes et blocs 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 unsafedes blocs.

static void Main ( string [] args ) { non sécurisé { int a = 2 ; int * b = &a a ;
Console . WriteLine ( "Adresse de a : {0}. Valeur : {1}" , ( int ) &a a , a ); Console . WriteLine ( "Adresse de b : {0}. Valeur : {1}. Valeur de *b : {2}" , ( int ) & b , ( int ) b , * b );
// Affichera quelque chose comme : 
// Adresse de a : 71953600. Valeur : 2 // Adresse de b : 71953596. Valeur : 71953600. Valeur de *b : 2 } }

Les structures doivent uniquement être des structures pures sans membres d'un type de référence géré, par exemple une chaîne ou toute autre classe.

public struct MyStruct { public char Caractère ; public int Entier ; }
public struct MyContainerStruct { public byte Byte ; public MyStruct MyStruct ; }

Utilisé:

MonConteneurStruct x ; MonConteneurStruct * ptr = & x ;
valeur d'octet = ptr -> Octet ;

Dynamique

Il s'agit d'une fonctionnalité de C# 4.0 et de .NET Framework 4.0 .

Le type dynamicest une fonctionnalité qui permet une recherche dynamique à l'exécution en C# de manière statique. Dynamic désigne une variable avec un objet dont le type est résolu à l'exécution, par opposition à la compilation, comme cela se fait normalement.

Cette fonctionnalité tire parti du Dynamic Language Runtime (DLR) et a été conçue spécifiquement dans le but d'interopérer avec des langages typés dynamiquement comme IronPython et IronRuby (implémentations de Python et Ruby pour .NET).

Le support dynamique facilite également l'interopérabilité avec les objets COM .

dynamic x = new Foo (); x . DoSomething (); // Sera compilé et résolu à l'exécution. Une exception sera levée si elle n'est pas valide.

Types anonymes

Il s'agit d'une fonctionnalité de C# 3.0 .

Les types anonymes sont des classes sans nom générées par le compilateur. Elles sont uniquement consommables et pourtant très utiles dans un scénario comme celui où l'on a une requête LINQ qui renvoie un objet selectet où l'on veut simplement renvoyer des valeurs spécifiques. Ensuite, définissez 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 }; // Le nom du type n'est connu que du compilateur. var mary = new { Name = "Mary" , Age = 22 }; // Même type que l'expression ci-dessus

Emballage et déballage

La boxe est l'opération de conversion d'une valeur d'un type de valeur en une valeur d'un type de 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 de référence (précédemment encadré) en une valeur d'un type de valeur. Le déballage en C# nécessite un cast de type explicite.

Exemple:

int foo = 42 ; // Type de valeur. objet bar = foo ; // foo est encadré dans bar. int foo2 = ( int ) bar ; // Déballé pour revenir au type de valeur.

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-ci dans le code.

Classe d'objet

Tous les types, même les types de 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. Certaines d'entre elles sont et peuvent être remplacées. System.Objectvirtual

Les classes héritent directement ou indirectement via une autre classe de base. System.Object

Membres
Certains des membres de la classe Object :

  • Equals- Prend en charge les comparaisons entre 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 numéro 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. Elle renvoie généralement le nom du type.

Cours

Les classes sont des éléments fondamentaux d'un langage orienté objet tel que C#. Elles servent de modèle pour les objets. Elles contiennent des membres qui stockent et manipulent des données de manière réaliste.

Différences entre les classes et les structures

Bien que les classes et les structures soient similaires dans la manière dont elles sont déclarées et utilisées, il existe quelques 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. Elle contient directement la valeur. Les classes sont différentes car la mémoire est allouée sous forme d'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 diffèrent des classes de plusieurs autres manières. Par exemple, bien que les deux offrent un constructeur implicite par défaut qui ne prend aucun argument, on ne peut pas le redéfinir pour les structures. Définir explicitement un constructeur paramétré différemment supprimera le constructeur implicite par défaut dans les classes, mais pas dans les structures. Tous les champs d'une structure doivent être initialisés dans ces types de constructeurs. Les structures n'ont pas de finaliseurs et ne peuvent pas hériter d'une autre classe comme le font les classes. Implicitement, elles sont scellées et héritent de (qui hérite de ). Les structures sont plus adaptées aux petites quantités de données. System.ValueTypeSystem.Object

Voici un bref résumé des différences :

Déclaration

Une classe est déclarée comme ceci :

classe Foo { // Déclarations de membres }
Cours partiel
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.

// Classe partielle File1.cs Foo { ... }
// Classe partielle File2.cs Foo { ... }

La raison habituelle de l'utilisation de classes partielles est de diviser une classe en une partie maintenue par le programmeur et une partie maintenue par l'outil, c'est-à-dire qu'un code est automatiquement généré par un outil de conception d'interface utilisateur ou quelque chose de similaire.

Initialisation

Avant de pouvoir utiliser les membres de la classe, il faut initialiser la variable avec une référence à un objet. Pour le créer, il faut appeler le constructeur approprié en utilisant le newmot-clé. Il porte le même nom que la classe.

var foo = nouveau Foo ();

Pour les structures, il est facultatif d'appeler explicitement un constructeur car celui par défaut est appelé automatiquement. Il suffit de le déclarer et il est initialisé avec des valeurs standards.

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.

var personne = nouvelle personne { Nom = "John Doe" , Âge = 39 };
// Égal à 
var 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 fournissent 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.

var liste = nouvelle liste < int > { 2 , 5 , 6 , 6 };
// Égal à 
var 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.

chaîne foo = "Bonjour" ; chaîne fooUpper = foo . ToUpper ();

Accéder à un membre de classe statique
Les membres statiques sont accessibles en utilisant le nom de la classe ou un autre type.

int r = chaîne . Compare ( foo , fooUpper );

Accéder à un membre via un pointeur
Dans le 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 types. Il existe notamment un sous-groupe contenant les modificateurs d'accès.

Modificateurs de classe
  • abstract- Spécifie qu'une classe sert uniquement de classe de base. Elle doit être implémentée dans une classe héritière. Une condition préalable pour permettre à la classe d'avoir des méthodes abstraites.
  • sealed- Spécifie qu'une classe ne peut pas être héritée.
Modificateurs des membres de la classe
  • abstract- Déclare une méthode disponible dans toutes les classes dérivées non abstraites.
  • 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 une substitution d'un membre virtuel ou une implémentation d'un membre d'une classe abstraite.
  • readonly- Déclare un champ auquel des valeurs ne peuvent être attribuées 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 déclaration de méthode ou 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 optimisant de faire des suppositions sur la persistance de la valeur actuelle du champ.
staticmodificateur

Le staticmodificateur indique qu'un membre appartient à la classe et non à un objet spécifique. Les classes marquées static ne peuvent contenir que des membres static. Les membres static sont parfois appelés membres de classe car ils s'appliquent à la classe dans son ensemble et non à ses instances.

public class Foo { public static void Something () { ... } } // Appel de la méthode de classe. Foo . Something ();
Modificateurs d'accès

Les modificateurs d'accès , ou modificateurs d'héritage , définissent l'accessibilité des classes, des méthodes et des autres membres. Un élément 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 lorsqu'ils sont hérités. 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 implicites internalet les membres sont implicites privates'ils n'ont pas de modificateur d'accès.

classe publique Foo { public int Do () { renvoie 0 ; }
classe publique Bar {
} 
}

Ce tableau définit où les modificateurs d'accès peuvent être utilisés.

Constructeurs

Un constructeur est une méthode spéciale qui est appelée automatiquement lors de la création d'un objet. Son but est d'initialiser les membres de l'objet. Les constructeurs ont le même nom que la classe et ne renvoient rien de manière explicite. Implicitement, ils renverront l'objet nouvellement créé lorsqu'ils seront appelés via l' newopérateur. Ils peuvent prendre des paramètres comme n'importe quelle autre méthode. Le constructeur sans paramètre est spécial car il peut être spécifié comme une contrainte nécessaire pour un paramètre de type générique.

classe Foo { Foo () { ... } }

Les constructeurs peuvent être public, private, protectedou internal.

Destructeur

Le destructeur est appelé lorsque l'objet est récupéré par le ramasse-miettes pour effectuer un nettoyage manuel. Il existe une méthode de destructeur par défaut appelée finalizequi peut être remplacée en en déclarant une.

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.

classe 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 possède une valeur de retour, un nom et généralement des paramètres initialisés lorsqu'elle est appelée avec des arguments. Elle peut appartenir à une instance d'une classe ou être un membre statique.

classe Foo { int Bar ( int a , int b ) { renvoie a % b ; } }

Une méthode est appelée en utilisant .une notation sur une variable spécifique, ou comme dans le cas des méthodes statiques, le nom d'un type.

Foo foo = nouveau Foo (); int r = foo . Bar ( 7 , 2 );
Console . WriteLine ( r );
refet outparamètres

Il est possible de faire passer explicitement des arguments par référence lors de l'appel d'une méthode avec des paramètres précédés de mots-clés refou out. Ces pointeurs managés sont pratiques lors du passage de variables que l'on souhaite 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é assigné dans la méthode au moment où la méthode retourne. refpeut ou non assigner une nouvelle valeur, mais la variable de paramètre doit être initialisée avant d'appeler la fonction.

void PassRef ( ref int x ) { si ( x == 2 ) x = 10 ; } int Z = 7 ; PassRef ( ref Z );
void PassOut ( out int x ) { x = 2 ; } int Q ; PassOut ( out Q );
Paramètres optionnels
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 dans C++. Par exemple :

void Incrément ( ref int x , int dx = 1 ) { x += dx ; }
int x = 0 ; Increment ( ref x ); // dx prend la valeur par défaut de 1 Increment ( ref x , 2 ); // dx prend la valeur 2

De plus, pour compléter les paramètres optionnels, il est possible de spécifier explicitement les noms des paramètres dans les appels de méthode, ce qui permet de passer de manière sélective n'importe quel sous-ensemble donné de paramètres optionnels pour une méthode. La seule restriction est que les paramètres nommés doivent être placés après les paramètres sans nom. 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 ( chaîne nom , FileMode mode = FileMode . Open , FileAccess access = FileAccess . Read ) { ... }
OpenFile ( "file.txt" ); // utiliser les valeurs par défaut pour "mode" et "access" OpenFile ( "file.txt" , mode : FileMode . Create ); // utiliser la valeur par défaut pour "access" OpenFile ( "file.txt" , access : FileAccess . Read ); // utiliser la valeur par défaut pour "mode" OpenFile ( name : "file.txt" , access : FileAccess . Read , mode : FileMode . Create ); // nommer tous les paramètres pour une meilleure lisibilité, // et utiliser un ordre différent de celui de la déclaration de méthode

Les paramètres optionnels facilitent l'interopérabilité avec COM. Auparavant, C# devait transmettre tous les paramètres de la méthode du composant COM, même ceux qui sont facultatifs. Par exemple :

objet fileName = "Test.docx" ; objet manquant = 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 ) ; console . writeline ( " Fichier enregistré avec succès " ) ;

Avec la prise en charge des paramètres facultatifs, le code peut être raccourci comme suit

doc . SaveAs ( ref nom_fichier );
extern

Une fonctionnalité 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")] 
statique externe double Pow ( double a , double b );

Champs

Les champs, ou variables d'instance , peuvent être déclarés à l'intérieur du corps de la classe pour stocker des données.

classe Foo { double foo ; }

Les champs peuvent être initialisés directement lors de leur déclaration (sauf s'ils sont déclarés dans struct).

classe Foo { double foo = 2.3 ; }

Modificateurs pour les champs :

  • const- Rend le champ constant.
  • private- Rend le champ privé (par défaut).
  • protected- Rend le champ protégé.
  • public- Rend le champ public.
  • readonly- Permet au champ d'être initialisé une seule fois dans un constructeur.
  • static- Fait du champ un membre statique, c'est-à-dire une variable de classe .

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.

classe publique Personne { chaîne privée _name ;
chaîne Nom { obtenir { renvoyer _name ; } définir { _name = valeur ; } } }
// Utilisation d'une propriété 
var 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- Rend 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 l'implémentation automatique des propriétés. 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 Largeur { obtenir ; privé ensemble ; }

Indexeurs

Les indexeurs ajoutent des capacités d'indexation de type tableau aux objets. Ils sont implémentés de manière similaire aux propriétés.

classe interne IntList { privé int [] _items ;
int ceci [ int index ] { obtenir { retourner _items [ index ]; } définir { _items [ index ] = valeur ; } } }
// Utilisation d'un indexeur 
var 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.

classe A { }
classe 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.

classe Opération { public virtual int Do () { return 0 ; } }
classe 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 clé newpeut être utilisé. La méthode utilisée sera choisie par le type de la variable au lieu du type réel de l'objet.

classe Opération { public int Do () { return 0 ; } }
classe NewOperation : Opération { public new double Do () { return 4.0 ; } }

Ceci démontre le cas :

var opération = new NewOperation ();
// Appellera "double Do()" dans NewOperation 
double d = operation . Do ();
Opération opération_ = opération ;
// Appellera "int Do()" dans l'opération 
int i = operation_ . Do ();
abstract

Les classes abstraites sont des classes qui servent uniquement de modèles et il n'est pas possible d'initialiser un objet de ce type. Sinon, elles sont comme une classe ordinaire.

Il peut également 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 toute classe non abstraite qui hérite du membre.

classe abstraite Mammifère { public abstract void Walk (); }
classe Humain : Mammifère { public override void Walk () {
}
... 
}
sealed

Le sealedmodificateur peut être combiné avec les autres comme modificateur facultatif pour les classes afin de les rendre non héritables, ou pour les méthodes afin d'interdire leur remplacement dans les classes dérivées.

classe interne scellée Foo { //... }
classe publique Bar { public virtual void Action () { //... } }
classe publique Baz : Bar { public sealed override void Action () { //... } }

Interfaces

Les interfaces sont des structures de données qui contiennent des définitions de membres et non une implémentation réelle. Elles sont utiles lorsque l'on souhaite définir un contrat entre des membres de types différents ayant des implémentations différentes. On peut déclarer des définitions pour des méthodes, des propriétés et des indexeurs. Les membres d'interface sont implicitement publics. Une interface peut être implémentée implicitement ou explicitement.

interface IBinaryOperation { double A { obtenir ; définir ; } double B { obtenir ; définir ; }
double Obtenir le résultat (); }

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 qu'une classe est dérivée 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.

classe publique Adder : IBinaryOperation { public double A { obtenir ; définir ; } public double B { obtenir ; définir ; }
public double GetResult () { renvoie A + B ; } }
classe publique Multiplicateur : IBinaryOperation { public double A { obtenir ; définir ; } public double B { obtenir ; définir ; }
public double GetResult () { renvoie A * B ; } }

Utilisé:

IBinaryOperation op = null ; double résultat ;
// Adder implémente l'interface IBinaryOperation.
op = nouvel additionneur (); op . UNE = 2 ; op . B = 3 ;
résultat = op . Obtenir le résultat (); // 5
// Multiplier implémente également l'interface.
op = nouveau multiplicateur (); op . A = 5 ; op . B = 4 ;
résultat = op . Obtenir le résultat (); // 20

Mise en œuvre explicite

Il est également possible d'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 traité comme le type d'interface.

classe publique Adder : IBinaryOperation { double IBinaryOperation . A { obtenir ; définir ; } double IBinaryOperation . B { obtenir ; définir ; }
double IBinaryOperation.GetResult ( ) { return (( IBinaryOperation ) this ) .A + ( ( IBinaryOperation ) this ) .B ; } }

Utilisé:

Additionneur ajouter = nouvel additionneur ();
// Ces membres ne sont pas accessibles : 
// add.A = 2 ; 
// add.B = 3 ; 
// double result = add.GetResult();
// Convertir en type d'interface pour y accéder : 
IBinaryOperation add2 = add ; add2 . A = 2 ; add2 . B = 3 ;
double résultat = add2 . GetResult ();

Remarque : les propriétés de la classe qui s'étend IBinaryOperationsont automatiquement implémentées 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.

classe MaClasse : 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. Voici 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 peut pas en avoir.
  • Une classe ou 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 (y compris 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 peut pas en avoir.
  • 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 de .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 précisent pas le type utilisé jusqu'à ce que la classe ou la méthode soit instanciée. Le principal avantage est que l'on peut utiliser des paramètres de type génériques pour créer des classes et des méthodes qui peuvent être utilisées sans encourir le coût des conversions d'exécution ou des opérations de boxing, comme illustré ici :

// Déclarer la classe générique.
classe publique GenericList < T > { void Add ( T input ) { } }
classe TestGenericList { classe privée ExampleClass { } static void Main () { // Déclarer une liste de type int. var list1 = new GenericList < int > ();
// Déclarer une liste de type chaîne. 
var list2 = new GenericList < string > ();
// Déclarer une liste de type ExampleClass. 
var list3 = new GenericList < ExampleClass > (); } }

Comparés aux modèles C++ , les génériques C# peuvent offrir une sécurité renforcée, 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 ; ils peuvent donc être inter-langages alors que les modèles C++ ne le peuvent pas. Ils prennent en charge certaines fonctionnalités qui ne sont pas prises en charge directement par les modèles C++, telles que les contraintes de type sur les paramètres génériques par l'utilisation d'interfaces. En revanche, C# ne prend pas en charge les paramètres génériques non typés.

Contrairement aux génériques de 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.

Utilisation des génériques

Classes génériques

Les classes et les structures peuvent être génériques.

classe publique Liste < T > { ... public void Ajouter ( T élément ) { ... } }
var liste = nouvelle Liste < int > (); liste . Ajouter ( 6 ); liste . Ajouter ( 2 );

Interfaces génériques

interface IEnumerable < T > { ... }

Délégués génériques

délégué 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 ; }
chaîne [] a = nouvelle chaîne [] { "a" , "b" , "c" }; chaîne [] b = nouvelle chaîne [] { "1" , "2" , "3" }; chaîne [] c = CombineArrays ( a , b );
double [] da = nouveau double [] { 1,2 , 2,17 , 3,141592 }; double [] db = nouveau double [] { 4,44 , 5,6 , 6,02 }; double [] dc = CombineArrays ( da , db );
// c est un tableau de chaînes contenant { "a", "b", "c", "1", "2", "3"} 
// dc est un tableau double contenant { 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 de type entre crochets angulaires . Lors de l'instanciation (ou de l'appel) d'un générique, on peut alors substituer un type concret au paramètre de type donné dans sa déclaration. Les paramètres de type peuvent être contraints par l'utilisation du mot-clé et d'une spécification de contrainte, l'une des six contraintes séparées par des virgules pouvant être utilisées : <>where

Covariance and contravariance

This is a feature of C# 4.0 and .NET Framework 4.0.

Generic interfaces and delegates can have their type parameters marked as covariant or contravariant, using keywords out and in, respectively. These declarations are then respected for type conversions, both implicit and explicit, and both compile-time and run-time. For example, the existing interface IEnumerable<T> has been redefined as follows:

interfaceIEnumerable<outT>
{
IEnumerator<T>GetEnumerator();
}

Therefore, any class that implements IEnumerable<Derived> for some class Derived is also considered to be compatible with IEnumerable<Base> for all classes and interfaces Base that Derived extends, directly, or indirectly. In practice, it makes it possible to write code such as:

voidPrintAll(IEnumerable<object>objects)
{
foreach(objectoinobjects)
{
System.Console.WriteLine(o);
}
}
IEnumerable<string>strings=newList<string>();
PrintAll(strings);// IEnumerable<string> is implicitly converted to IEnumerable<object>

For contravariance, the existing interface IComparer<T> has been redefined as follows:

publicinterfaceIComparer<inT>
{
intCompare(Tx,Ty);
}

Therefore, any class that implements IComparer<Base> for some class Base is also considered to be compatible with IComparer<Derived> for all classes and interfaces Derived that are extended from Base. It makes it possible to write code such as:

IComparer<object>objectComparer=GetComparer();
IComparer<string>stringComparer=objectComparer;

Enumerators

An enumerator is an iterator. Enumerators are typically obtained by calling the GetEnumerator() method of an object implementing the IEnumerable interface. Container classes typically implement this interface. However, the foreach statement in C# can operate on any object providing such a method, even if it doesn't implement IEnumerable. This interface was expanded into generic version in .NET 2.0.

The following shows a simple use of iterators in C# 2.0:

// explicit version
IEnumerator<MyType>iter=list.GetEnumerator();
while(iter.MoveNext())
Console.WriteLine(iter.Current);
// implicit version
foreach(MyTypevalueinlist)
Console.WriteLine(value);

Generator functionality

This is a feature of C# 2.0.

The .NET 2.0 Framework allowed C# to introduce an iterator that provides generator functionality, using a yieldreturn construct similar to yield in Python. With a yieldreturn, the function automatically keeps its state during the iteration.

// Method that takes an iterable input (possibly an array)
// and returns all even numbers.
publicstaticIEnumerable<int>GetEven(IEnumerable<int>numbers)
{
foreach(intiinnumbers)
{
if(i%2==0)
yieldreturni;
}
}
// using the method to output only even numbers from the array
staticvoidMain()
{
int[]numbers={1,2,3,4,5,6};
foreach(intiinGetEven(numbers))
Console.WriteLine(i);//outputs 2, 4 and 6
}

LINQ

This is a feature of C# 3.0 and .NET Framework 3.0.

LINQ, short for Language Integrated Queries, is a .NET Framework feature which simplifies the handling of data. Mainly it adds support that allows the querying of arrays, collections, and databases. It also introduces binders, which makes it easier to access to databases and their data.

Query syntax

The LINQ query syntax was introduced in C# 3.0 and lets one write SQL-like queries in C#.

varlist=newList<int>{2,7,1,3,9};
varresult=fromiinlist
wherei>1
selecti;

The statements are compiled into method calls, whereby almost only the names of the methods are specified. Which methods are ultimately used is determined by normal overload resolution. Thus, the end result of the translation is affected by what symbols are in scope.

What differs from SQL is that the from-statement comes first and not last as in SQL. This is because it seems more natural writing like this in C# and supports "Intellisense" (Code completion in the editor).

Anonymous methods

Anonymous methods, or in their present form more commonly referred to as "lambda expressions", is a feature which allows programmers to write inline closure-like functions in their code.

There are various ways to create anonymous methods. Prior to C# 3.0 there was limited support by using delegates.

Anonymous delegates

This is a feature of C# 2.0.

Anonymous delegates are functions pointers that hold anonymous methods. The purpose is to make it simpler to use delegates by simplifying the process of assigning the function. Instead of declaring a separate method in code the programmer can use the syntax to write the code inline and the compiler will then generate an anonymous function for it.

Func<int,int>f=delegate(intx){returnx*2;};

Lambda expressions

This is a feature of C# 3.0.

Lambda expressions provide a simple syntax for inline functions that are similar to closures. Functions with parameters infer the type of the parameters if other is not explicitly specified.

// [arguments] => [method-body]
// With parameters
n=>n==2
(a,b)=>a+b
(a,b)=>{a++;returna+b;}
// With explicitly typed parameters
(inta,intb)=>a+b
// No parameters
()=>return0
// Assigning lambda to delegate
Func<int,int,int>f=(a,b)=>a+b;

Multi-statement lambdas have bodies enclosed by braces and inside of them code can be written like in standard methods.

(a,b)=>{a++;returna+b;}

Lambda expressions can be passed as arguments directly in method calls similar to anonymous delegates but with a more aesthetic syntax.

varlist=stringList.Where(n=>n.Length>2);

Lambda expressions are essentially compiler-generated methods that are passed via delegates. These methods are reserved for the compiler only and can not be used in any other context.

Extension methods

This is a feature of C# 3.0.

Extension methods are a form of syntactic sugar providing the illusion of adding new methods to the existing class outside its definition. In practice, an extension method is a static method that is callable as if it were an instance method; the receiver of the call is bound to the first parameter of the method, decorated with keyword this:

publicstaticclassStringExtensions
{
publicstaticstringLeft(thisstrings,intn)
{
returns.Substring(0,n);
}
}
strings="foo";
s.Left(3);// same as StringExtensions.Left(s, 3);

Local functions

This is a feature of C# 7.0.

Local functions can be defined in the body of another method, constructor or property's getter and setter. Such functions have access to all variables in the enclosing scope, including parent method local variables. They are in scope for the entire method, regardless of whether they're invoked before or after their declaration. Access modifiers (public, private, protected) cannot be used with local functions. Also they do not support function overloading. It means there cannot be two local functions in the same method with the same name even if the signatures don't overlap. After a compilation, a local function is transformed into a private static method, but when defined it cannot be marked static.

In code example below, the Sum method is a local function inside Main method. So it can be used only inside its parent method Main:

staticvoidMain(string[]args)
{
intSum(intx,inty)
{
returnx+y;
}
Console.WriteLine(Sum(10,20));
Console.ReadKey();
}

Miscellaneous

Closure blocks

C# implements closure blocks by means of the using statement. The using statement accepts an expression which results in an object implementing IDisposable, and the compiler generates code that guarantees the object's disposal when the scope of the using-statement is exited. The using statement is syntactic sugar. It makes the code more readable than the equivalent try...finally block.

publicvoidFoo()
{
using(varbar=File.Open("Foo.txt"))
{
// do some work
thrownewException();
// bar will still get properly disposed.
}
}

Thread synchronization

C# provides the lock statement, which is yet another example of beneficial syntactic sugar. It works by marking a block of code as a critical section by mutual exclusion of access to a provided object. Like the using statement, it works by the compiler generating a try...finally block in its place.

privatestaticStreamWriter_writer;
publicvoidConcurrentMethod()
{
lock(_writer)
{
_writer.WriteLine("Line 1.");
_writer.WriteLine("Followed by line 2.");
}
}

Attributes

Attributes are entities of data that are stored as metadata in the compiled assembly. An attribute can be added to types and members like properties and methods. Attributes can be used for better maintenance of preprocessor directives.

[CompilerGenerated]
publicclass$AnonymousType$120
{
[CompilerGenerated]
publicstringName{get;set;}
}

The .NET Framework comes with predefined attributes that can be used. Some of them serve an important role at runtime while some are just for syntactic decoration in code like CompilerGenerated. It does only mark that it is a compiler-generated element. Programmer-defined attributes can also be created.

An attribute is essentially a class which inherits from the System.Attribute class. By convention, attribute classes end with "Attribute" in their name. This will not be required when using it.

publicclassEdibleAttribute:Attribute
{
publicEdibleAttribute():base()
{
}
publicEdibleAttribute(boolisNotPoisonous)
{
this.IsPoisonous=!isNotPoisonous;
}
publicboolIsPoisonous{get;set;}
}

Showing the attribute in use using the optional constructor parameters.

[Edible(true)]
publicclassPeach:Fruit
{
// Members if any
}

Preprocessor

C# features "preprocessor directives" (though it does not have an actual preprocessor) based on the C preprocessor that allow programmers to define symbols, but not macros. Conditionals such as #if, #endif, and #else are also provided.

Directives such as #region give hints to editors for code folding. The #region block must be terminated with a #endregion directive.

publicclassFoo
{
#region Constructors
publicFoo(){}
publicFoo(intfirstParam){}
#endregion
#region Methods
publicvoidIntBar(intfirstParam){}
publicvoidStrBar(stringfirstParam){}
publicvoidBoolBar(boolfirstParam){}
#endregion
}

Code comments

C# utilizes a double slash (//) to indicate the rest of the line is a comment.

publicclassFoo
{
// a comment
publicstaticvoidBar(intfirstParam){}// Also a comment
}

Multi-line comments can be indicated by a starting slash/asterisk (/*) and ending asterisk/forward slash (*/).

publicclassFoo
{
/* A multi-line
 comment */
publicstaticvoidBar(intfirstParam){}
}

Comments do not nest. These are two single comments:

// Can put /* */ */ */ /* /*
/* Can put /* /* /* but it ends with */

Single-line comments beginning with three slashes are used for XML documentation. This, however, is a convention used by Visual Studio and is not part of the language definition:

/// <summary>
/// This class is very classy.
/// </summary>

XML documentation comments

C#'s documentation comments are similar to Java's Javadoc, but based on XML. Two methods of documentation are currently supported by the C# compiler.

Single-line documentation comments, such as those commonly found in Visual Studio generated code, are indicated on a line beginning with ///.

publicclassFoo
{
/// <summary>A summary of the method.</summary>
/// <param name="firstParam">A description of the parameter.</param>
/// <remarks>Remarks about the method.</remarks>
publicstaticvoidBar(intfirstParam){}
}

Multi-line documentation comments, while defined in the version 1.0 language specification, were not supported until the .NET 1.1 release. These comments are designated by a starting forward slash/asterisk/asterisk (/**) and ending asterisk/forward slash (*/).

publicclassFoo
{
/** <summary>A summary of the method.</summary>
 * <param name="firstParam">A description of the parameter.</param>
 * <remarks>Remarks about the method.</remarks> */
publicstaticvoidBar(intfirstParam){}
}

There are some stringent criteria regarding white space and XML documentation when using the forward slash/asterisk/asterisk (/**) technique.

This code block:

/**
 * <summary>
 * A summary of the method.</summary>*/

produces a different XML comment than this code block:

/**
 * <summary>
 A summary of the method.</summary>*/

Syntax for documentation comments and their XML markup is defined in a non-normative annex of the ECMA C# standard. The same standard also defines rules for processing of such comments, and their transformation to a plain XML document with precise rules for mapping of Common Language Infrastructure (CLI) identifiers to their related documentation elements. This allows any C# integrated development environment (IDE) or other development tool to find documentation for any symbol in the code in a certain well-defined way.

Async-await syntax

This is a feature of C# 5.0 and .NET Framework 4.0.

As of .NET Framework 4 there is a task library that makes it easier to write parallel and multi-threaded applications through tasks.

C# 5.0 has native language support for asynchrony.

Consider this code that takes advantage of the task library directly:

publicstaticclassSomeAsyncCode
{
publicstaticTask<XDocument>GetContentAsync()
{
varhttpClient=newHttpClient();
returnhttpClient.GetStringAsync("https://www.contoso.com/").ContinueWith((task)=>{
stringresponseBodyAsText=task.Result;
returnXDocument.Parse(responseBodyAsText);
});
}
}
vart=SomeAsyncCode.GetContentAsync().ContinueWith((task)=>{
varxmlDocument=task.Result;
});
t.Start();

Here is the same logic written in the async-await syntax:

publicstaticclassSomeAsyncCode
{
publicstaticasyncTask<XDocument>GetContentAsync()
{
varhttpClient=newHttpClient();
stringresponseBodyAsText=awaithttpClient.GetStringAsync("https://www.contoso.com/");
returnXDocument.Parse(responseBodyAsText);
}
}
varxmlDocument=awaitSomeAsyncCode.GetContentAsync();
// The Task will be started on call with await.

Dialects

Spec#

Spec# is a dialect of C# that is developed in parallel with the standard implementation from Microsoft. It extends C# with specification language features and is a possible future feature to the C# language. It also adds syntax for the code contracts API that was introduced in .NET Framework 4.0. Spec# is being developed by Microsoft Research.

This sample shows two of the basic structures that are used when adding contracts to code.

staticvoidMain(string![]args)
requiresargs.Length>0
{
foreach(stringarginargs)
{
}
}
  • ! is used to make a reference type non-nullable, e.g. it is not possible to set the value to null. This in contrast of nullable types which allow value types to be set as null.
  • requires indicates a condition that must be followed in the code. In this case the length of args is not allowed to be zero or less.

Non-nullable types

Spec# extends C# with non-nullable types that simply checks so the variables of nullable types that has been set as non-nullable are not null. If is null then an exception will be thrown.

string!input

In use:

publicTest(string!input)
{
...
}

Preconditions

Preconditions are checked before a method is executed.

publicTest(inti)
requiresi>0;
{
this.i=i;
}

Postconditions

Postconditions are conditions that are ensured to be correct when a method has been executed.

publicvoidIncrement()
ensuresi>0;
{
i++;
}

Checked exceptions

Spec# adds checked exceptions like those in Java.

publicvoidDoSomething()
throwsSomeException;// SomeException : ICheckedException
{
...
}

Checked exceptions are problematic, because when a lower-level function adds a new exception type, the whole chain of methods using this method at some nested lower level must also change its contract. This violates the open/closed principle.

Plus d articles de Worldlex Wiki

Revenez a l index pour explorer davantage de pages sur l histoire, la science, la culture, la geographie et la societe en francais.

Explorer l index