Visite guidée du langage C#
C# (prononcez « Si Sharp ») est un langage de programmation moderne, orienté objet et de type sécurisé. C# permet aux développeurs de créer de nombreux types d’applications sécurisées et robustes qui s’exécutent dans .NET. C# prend sa source dans la famille de langages C et sera immédiatement reconnaissable aux programmeurs en C, C++, Java et JavaScript. Cette visite guidée offre une vue d’ensemble des principaux composants du langage dans C# 8 et antérieur. Si vous voulez explorer le langage à travers des exemples interactifs, essayez les tutoriels de présentation de C#.
C# est un langage de programmation orienté objet, orienté composant. C# fournit des constructions de langage qui prennent directement en charge ces concepts, ce qui fait de C# un langage naturel pour créer et utiliser des composants logiciels. Depuis son apparition, C# a ajouté des fonctionnalités pour prendre en charge de nouvelles charges de travail et des pratiques de conception logicielle émergentes. À la base, C# est un langage orienté objet. Vous définissez des types et leur comportement.
Plusieurs fonctionnalités C# permettent de créer des applications robustes et durables. Garbage Collection récupère automatiquement la mémoire occupée par des objets inutilisés inaccessibles. Les types nullables protègent contre les variables qui ne font pas référence à des objets alloués. La gestion des exceptions offre une approche structurée et extensible de la détection et de la récupération des erreurs. Les expressions lambda prennent en charge les techniques de programmation fonctionnelle. La syntaxe LINQ (Language Integrated Query) crée un modèle commun pour utiliser les données de n’importe quelle source. La prise en charge du langage pour les opérations asynchrones fournit une syntaxe pour créer des systèmes distribués. C# a un système de type unifié. Tous les types C#, y compris les types primitifs tels que int
et double
, héritent d’un seul type object
racine. Tous les types partagent un ensemble d’opérations communes. Les valeurs de n’importe quel type peuvent être stockées, transportées et manipulées de manière cohérente. De plus, C# prend en charge à la fois les types référence et les types valeur définis par l’utilisateur. C# autorise l’allocation dynamique d’objets et le stockage in-line de structures légères. C# prend en charge les méthodes et les types génériques, qui offrent une cohérence des types et des performances accrues. C# fournit des itérateurs qui permettent aux implémenteurs de classes de collection de définir des comportements personnalisés pour le code client.
C# met l’accent sur la gestion de versions pour garantir que les programmes et les bibliothèques peuvent évoluer de manière compatible. Certains aspects de la conception de C# ont été directement influencés par la gestion de versions, notamment les modificateurs virtual
et override
distincts, les règles de résolution des surcharges de méthodes et la prise en charge des déclarations explicites de membres d’interface.
Architecture .NET
Les programmes C# s’exécutent sur .NET, un système d’exécution virtuel appelé common language runtime (CLR) et un ensemble de bibliothèques de classes. Le CLR est l’implémentation de Microsoft de la CLI (Common Language Infrastructure), qui est une norme internationale. La CLI est la base de la création d’environnements d’exécution et de développement où langages et bibliothèques fonctionnent ensemble de façon fluide.
Le code source écrit en C# est compilé dans un langage intermédiaire (IL) qui est conforme à la spécification CLI. Le code et les ressources IL, comme les bitmaps et les chaînes, sont stockés dans un assembly, en général avec une extension .dll. Un assembly contient un manifeste qui fournit des informations sur ses types, sa version et sa culture.
Lorsque le programme C# est exécuté, l’assembly est chargé dans le CLR. Le CLR effectue une compilation juste-à-temps (JIT, Just-In-Time) pour convertir le code IL en instructions machine natives. Le CLR fournit d’autres services liés au Garbage collection, à la gestion des exceptions et à la gestion des ressources. Le code exécuté par le CLR est parfois appelé « code managé ». Le « code non managé » est compilé dans le langage machine natif qui cible une plateforme spécifique.
L’interopérabilité des langages est une fonctionnalité clé de .NET. Le code IL produit par le compilateur C# est conforme à la spécification CTS (Common Type Specification). Le code IL généré à partir de C# peut interagir avec le code généré à partir des versions .NET de F#, Visual Basic et C++. Il existe plus de 20 autres langages conformes à la spécification CTS. Un seul assembly peut contenir plusieurs modules écrits dans différents langages .NET. Les types peuvent faire référence les uns aux autres comme s’ils avaient été écrits dans le même langage.
En plus des services d’exécution, .NET comprend également des bibliothèques complètes. Ces bibliothèques prennent en charge un grand nombre de charges de travail différentes. Elles sont organisées en espaces de noms qui fournissent un large éventail de fonctionnalités utiles. Les bibliothèques englobent tout, de l’entrée/la sortie des fichiers à la manipulation de chaînes et à l’analyse XML, en passant par les frameworks d’application web et les contrôles Windows Forms. Une application C# typique utilise beaucoup la bibliothèque de classes .NET pour gérer les tâches fastidieuses courantes.
Pour plus d’informations sur .NET, consultez Vue d’ensemble de .NET.
Hello World
Le programme « Hello, World » est souvent utilisé pour présenter un langage de programmation. Le voici en C# :
using System;
class Hello
{
static void Main()
{
Console.WriteLine("Hello, World");
}
}
Le programme « Hello, World » commence par une directive using
qui fait référence à l’espace de noms System
. Les espaces de noms représentent un moyen hiérarchique d’organiser les bibliothèques et les programmes C#. Les espaces de noms contiennent des types et d’autres espaces de noms ; par exemple, l’espace de noms System
contient plusieurs types, notamment la classe Console
référencée dans le programme, et d’autres espaces de noms, tels que IO
et Collections
. Une directive using
qui fait référence à un espace de noms donné permet l’utilisation non qualifiée des types membres de cet espace de noms. En raison de la directive using
, le programme peut utiliser Console.WriteLine
comme raccourci pour System.Console.WriteLine
.
La classe Hello
déclarée par le programme « Hello, World » a un membre unique, la méthode nommée Main
. La méthode Main
est déclarée avec le modificateur static
. Si les méthodes d’instance peuvent faire référence à une instance d’objet englobante particulière avec le mot clé this
, les méthodes statiques fonctionnent sans référence à un objet particulier. Par convention, une méthode statique nommée Main
sert de point d’entrée d’un programme C#.
La sortie du programme est générée par la méthode WriteLine
de la classe Console
dans l’espace de noms System
. Cette classe est fournie par les bibliothèques de classes standard, qui, par défaut, sont référencées automatiquement par le compilateur.
Types et variables
Un type définit la structure et le comportement de toutes les données dans C#. La déclaration d’un type peut inclure ses membres, le type de base, les interfaces qu’il implémente et les opérations autorisées pour ce type. Une variable est un nom qui fait référence à une instance d’un type spécifique.
Il existe deux genres de types en C# : les types référence et les types valeur. Les variables des types valeur contiennent directement leurs données. Les variables des types référence stockent des références à leurs données, connues sous le nom d’objets. Avec les types référence, deux variables peuvent faire référence au même objet et les opérations sur une variable peuvent affecter l’objet référencé par l’autre variable. Avec les types valeur, les variables ont chacune leur propre copie de données, et les opérations sur une variable ne peuvent pas affecter l’autre (sauf pour les variables de paramètre ref
et out
).
Un identificateur est un nom de variable. Un identificateur est une séquence de caractères Unicode sans espace blanc. Un identificateur peut être un mot réservé C#, s’il est préfixé par @
. L’utilisation d’un mot réservé comme identificateur peut être utile lors d’une interaction avec d’autres langages.
Les types valeur de C# sont divisés entre types simples, types enum, types struct, types valeur nullable et types valeur tuple. Les types référence de C# sont divisés entre types de classe, types d’interface, types de tableau et types de délégué.
La structure suivante offre une vue d’ensemble du système de types de C#.
- Types de valeur
- Types simples
- Intégral signé :
sbyte
,short
,int
,long
- Intégral non signé :
byte
,ushort
,uint
,ulong
- Caractères Unicode :
char
, qui représente une unité de code UTF-16 - Nombre binaire à virgule flottante IEEE :
float
,double
- Nombre décimal à virgule flottante haute précision :
decimal
- Booléen :
bool
, qui représente les valeurs booléennes. Les valeurs sonttrue
oufalse
- Intégral signé :
- Types d'enum
- Types définis par l’utilisateur de la forme
enum E {...}
Un typeenum
est un type distinct avec des constantes nommées. Chaque typeenum
a un type sous-jacent qui doit être un des huit types intégraux. L’ensemble de valeurs d’un typeenum
est le même que l’ensemble de valeurs du type sous-jacent.
- Types définis par l’utilisateur de la forme
- Types struct
- Types définis par l'utilisateur de la forme
struct S {...}
- Types définis par l'utilisateur de la forme
- Types valeur Nullable
- Extensions de tous les autres types de valeurs avec une valeur
null
- Extensions de tous les autres types de valeurs avec une valeur
- Types valeur tuple
- Types définis par l'utilisateur de la forme
(T1, T2, ...)
- Types définis par l'utilisateur de la forme
- Types simples
- Types référence
- Types de classes
- Classe de base fondamentale de tous les autres types :
object
- Chaînes Unicode :
string
, qui représente une séquence d’unités de code UTF-16 - Types définis par l'utilisateur de la forme
class C {...}
- Classe de base fondamentale de tous les autres types :
- Types d'interface
- Types définis par l'utilisateur de la forme
interface I {...}
- Types définis par l'utilisateur de la forme
- Types de tableaux
- Unidimensionnel, multidimensionnel et en escalier. Par exemple :
int[]
,int[,]
etint[][]
- Unidimensionnel, multidimensionnel et en escalier. Par exemple :
- Types délégués
- Types définis par l'utilisateur de la forme
delegate int D(...)
- Types définis par l'utilisateur de la forme
- Types de classes
Les programmes C# utilisent les déclarations de type pour créer de nouveaux types. Une déclaration de type spécifie le nom et les membres du nouveau type. Six catégories de types C# sont définissables par l’utilisateur : les types de classe, les types struct, les types d’interface, les types enum, les types de délégué et les types valeur tuple. Vous pouvez également déclarer des types record
, soit record struct
, soit record class
. Les types d’enregistrement (record) ont des membres synthétisés par le compilateur. Vous utilisez les enregistrements principalement pour stocker des valeurs, avec un comportement associé minimal.
- Un type
class
définit une structure de données qui contient des données membres (champs) et des fonctions membres (méthodes, propriétés, etc.). Les types de classes prennent en charge l’héritage unique et le polymorphisme, des mécanismes par lesquels les classes dérivées peuvent étendre et spécialiser les classes de base. - Un type
struct
est similaire à un type de classe dans la mesure où il représente une structure avec des membres de données et des membres de fonctions. Cependant, contrairement aux classes, les structs sont des types valeur et ne nécessitent généralement pas d’allocation de tas. Les types struct ne prennent pas en charge l’héritage spécifié par l’utilisateur, et tous les types struct héritent implicitement du typeobject
. - Un type
interface
définit un contrat en tant que jeu nommé de membres publics. Uneclass
ou unstruct
qui implémente uneinterface
doivent fournir les implémentations des membres de l’interface. Uneinterface
peut hériter de plusieurs interfaces de base, et uneclass
oustruct
peut implémenter plusieurs interfaces. - Un type
delegate
représente des références aux méthodes avec une liste de paramètres et un type de retour particuliers. Les délégués permettent de traiter les méthodes en tant qu’entités qui peuvent être affectées à des variables et passées comme paramètres. Les délégués sont semblables aux types de fonction fournis par les langages fonctionnels. Ils sont aussi similaires au concept de pointeurs de fonction que l’on trouve dans d’autres langages. Contrairement aux pointeurs de fonction, les délégués sont orientés objet et de type sécurisé.
Les types class
, struct
, interface
et delegate
prennent tous en charge les génériques, ce qui leur permet d’être paramétrés avec d’autres types.
C# prend en charge les tableaux unidimensionnels et multidimensionnels de tout type. Contrairement aux types mentionnés ci-dessus, les types de tableau n’ont pas à être déclarés avant de pouvoir être utilisés. Au lieu de cela, les types de tableaux sont construits en ajoutant des crochets à un nom de type. Par exemple, int[]
est un tableau unidimensionnel de int
, int[,]
est un tableau bidimensionnel de int
et int[][]
est un tableau unidimensionnel de tableaux unidimensionnels, ou un tableau « en escalier », de int
.
Les types nullables n’ont pas besoin de définition distincte. Pour chaque type T
non-nullable, il existe un type T?
nullable correspondant, qui peut contenir une valeur supplémentaire, null
. Par exemple, int?
est un type qui peut contenir n’importe quel entier 32 bits ou la valeur null
, tandis que string?
est un type qui peut contenir n’importe quelle string
ou la valeur null
.
Le système de types de C# est unifié afin qu’une valeur de n’importe quel type puisse être traitée comme object
. Chaque type dans C# dérive directement ou indirectement du type object
, et object
est la classe de base fondamentale de tous les types. Les valeurs des types référence sont considérées comme des objets simplement en affichant les valeurs en tant que type object
. Les valeurs des types valeur sont considérées comme des objets en effectuant des opérations de boxing et d’unboxing. Dans l’exemple suivant, une valeur int
est convertie en object
et à nouveau en int
.
int i = 123;
object o = i; // Boxing
int j = (int)o; // Unboxing
Quand une valeur d’un type valeur est affectée à une référence object
, une « box » est alloué pour contenir la valeur. Cette « box » est une instance d’un type référence et la valeur est copiée dans celle-ci. À l’inverse, lorsqu’une référence object
est castée en type valeur, un contrôle est effectué pour s’assurer que l’object
référencé est une « box » du type valeur correct. Si le contrôle réussit, la valeur dans la « box » est copiée dans le type valeur.
Le système de types unifié de C# signifie en effet que les types valeur sont traités comme références object
« à la demande ». En raison de l’unification, les bibliothèques à usage général qui se servent du type object
peuvent être utilisées avec tous les types qui dérivent de object
, y compris les types référence et les types valeur.
Il existe plusieurs types de variables en C#, y compris les champs, les éléments de tableau, les variables locales et les paramètres. Les variables représentent des emplacements de stockage. Chaque variable a un type qui détermine quelles valeurs peuvent y être stockées, comme illustré ci-dessous.
- Type de valeur n’acceptant pas Null
- Une valeur de ce type exact
- Types valeur Nullable
- Une valeur
null
ou une valeur de ce type exact
- Une valeur
- object
- Une référence
null
, une référence à un objet de tout type référence ou une référence à une valeur boxed de n’importe quel type valeur
- Une référence
- Type de classe
- Une référence
null
, une référence à une instance de ce type de classe ou une référence à une instance d’une classe dérivée de ce type de classe
- Une référence
- Type d'interface
- Une référence
null
, une référence à une instance d’un type de classe qui implémente ce type d’interface ou une référence à une valeur boxed d’un type valeur qui implémente ce type d’interface
- Une référence
- Type tableau
- Une référence
null
, une référence à une instance de ce type de tableau ou une instance d’un type de tableau compatible
- Une référence
- Type délégué
- Une référence
null
ou une référence à une instance d’un type délégué compatible
- Une référence
Structure du programme
Les concepts organisationnels clés en C# sont les programmes, les espaces de noms, les types, les membres et les assemblys. Les programmes déclarent des types qui contiennent des membres et peuvent être organisés en espaces de noms. Les classes, les structs et les interfaces sont des exemples de types. Les champs, méthodes, propriétés et événements sont des exemples de membres. Lorsque les programmes C# sont compilés, ils sont physiquement empaquetés dans des assemblys. Les assemblys ont généralement l’extension de fichier .exe
ou .dll
, selon qu’elles implémentent des applications ou des bibliothèques, respectivement.
Voici un petit exemple d’un assembly qui contient le code suivant :
namespace Acme.Collections;
public class Stack<T>
{
Entry _top;
public void Push(T data)
{
_top = new Entry(_top, data);
}
public T Pop()
{
if (_top == null)
{
throw new InvalidOperationException();
}
T result = _top.Data;
_top = _top.Next;
return result;
}
class Entry
{
public Entry Next { get; set; }
public T Data { get; set; }
public Entry(Entry next, T data)
{
Next = next;
Data = data;
}
}
}
Le nom qualifié complet de cette classe est Acme.Collections.Stack
. La classe contient plusieurs membres : un champ nommé _top
, deux méthodes nommées Push
et Pop
, et une classe imbriquée nommée Entry
. La classe Entry
contient trois membres en plus : une propriété nommée Next
, une propriété nommée Data
et un constructeur. Stack
est une classe générique. Il a un paramètre de type, T
, qui est remplacé par un type concret lorsqu’il est utilisé.
Un stack est une collection FILO (« first in - last out »). De nouveaux éléments sont ajoutés en haut du stack. Lorsqu’un élément est supprimé, il est supprimé du haut du stack. L’exemple précédent déclare le type Stack
qui définit le stockage et le comportement d’un stack. Vous pouvez déclarer une variable qui fait référence à une instance du type Stack
pour utiliser cette fonctionnalité.
Les assemblys contiennent le code exécutable sous forme d’instructions de langage intermédiaire (IL) et des informations symboliques sous la forme de métadonnées. Avant son exécution, le compilateur juste-à-temps (JIT) du Common Language Runtime .NET convertit le code IL d’un assembly en code spécifique au processeur.
Comme un assembly est une unité de fonctionnalité autodescriptive contenant du code et des métadonnées, les directives #include
et les fichiers d’en-tête ne sont pas nécessaires en C#. Les membres et types publics contenus dans un assembly particulier sont disponibles dans un programme C# par simple référence à cet assembly lors de la compilation du programme. Par exemple, ce programme utilise la classe Acme.Collections.Stack
à partir de l’assembly acme.dll
:
class Example
{
public static void Main()
{
var s = new Acme.Collections.Stack<int>();
s.Push(1); // stack contains 1
s.Push(10); // stack contains 1, 10
s.Push(100); // stack contains 1, 10, 100
Console.WriteLine(s.Pop()); // stack contains 1, 10
Console.WriteLine(s.Pop()); // stack contains 1
Console.WriteLine(s.Pop()); // stack is empty
}
}
Pour compiler ce programme, vous devez référencer l’assembly contenant la classe stack définie dans l’exemple précédent.
Les programmes C# peuvent être stockés dans plusieurs fichiers sources. Lorsqu’un programme C# est compilé, tous les fichiers sources sont traités ensemble et peuvent librement se référencer entre eux. Sur le plan conceptuel, c’est comme si tous les fichiers sources étaient concaténés dans un seul grand fichier avant d’être traités. Les déclarations anticipées ne sont jamais nécessaires en C#, car, à de rares exceptions près, l’ordre de déclaration n’a pas d’importance. C# ne limite pas un fichier source à la déclaration d’un seul type public et n’a pas besoin non plus que le nom du fichier source corresponde à un type déclaré dans ce fichier.
D’autres articles de cette visite guidée expliquent ces blocs organisationnels.