Remarque
L’accès à cette page nécessite une autorisation. Vous pouvez essayer de vous connecter ou de modifier des répertoires.
L’accès à cette page nécessite une autorisation. Vous pouvez essayer de modifier des répertoires.
C# est un langage fortement typé. Chaque variable et constante a un type, tout comme chaque expression qui se résout à une valeur. C# utilise principalement un système de type normative. Un système de type normative utilise des noms pour identifier chaque type. En C#, les types struct, class, interface, y compris les types record, sont tous identifiés par leur nom. Chaque déclaration de méthode spécifie un nom, un type et une nature (valeur, référence ou sortie) pour chaque paramètre et pour la valeur de retour. La bibliothèque de classes .NET définit des types numériques intégrés et des types complexes qui représentent une grande variété de constructions. Ces constructions incluent le système de fichiers, les connexions réseau, les collections et les tableaux d’objets et de dates. Un programme C# classique utilise des types de la bibliothèque de classes et des types définis par l’utilisateur qui modélisent les concepts spécifiques au domaine de problème du programme.
C# prend également en charge les types structurels, tels que les tuples et les types anonymes. Les types structurels sont définis par les noms et les types de chaque membre et l’ordre des membres dans une expression. Les types structurels n’ont pas de noms uniques.
Les informations stockées dans un type peuvent inclure les éléments suivants :
- Espace de stockage requis par une variable du type.
- Valeurs maximales et minimales qu’elle peut représenter.
- Les membres (méthodes, champs, événements, et ainsi de suite) qu’il contient.
- Type de base dont le type est hérité.
- Interfaces qu’il implémente.
- Opérations autorisées.
Le compilateur utilise des informations de type pour vous assurer que toutes les opérations effectuées dans votre code sont sécurisées de type. Par exemple, si vous déclarez une variable de type int, le compilateur vous permet d’utiliser la variable en plus et en soustraction. Si vous essayez d’effectuer ces mêmes opérations sur une variable de type bool, le compilateur génère une erreur, comme illustré dans l’exemple suivant :
int a = 5;
int b = a + 2; //OK
bool test = true;
// Error. Operator '+' cannot be applied to operands of type 'int' and 'bool'.
int c = a + test;
Remarque
Les développeurs C et C++, veuillez noter qu’en C#, bool n’est pas convertible en int.
Le compilateur incorpore les informations de type dans le fichier exécutable en tant que métadonnées. Le Common Language Runtime (CLR) utilise ces métadonnées au moment de l’exécution pour garantir davantage la sécurité du type lorsqu’elle alloue et récupère de la mémoire.
Spécification de types dans les déclarations de variables
Lorsque vous déclarez une variable ou une constante dans un programme, vous devez spécifier son type ou utiliser le var mot clé pour permettre au compilateur de déduire le type. L’exemple suivant montre certaines déclarations de variables qui utilisent à la fois des types numériques intégrés et des types complexes définis par l’utilisateur :
// Declaration only:
float temperature;
string name;
MyClass myClass;
// Declaration with initializers (four examples):
char firstLetter = 'C';
var limit = 3;
int[] source = [0, 1, 2, 3, 4, 5];
var query = from item in source
where item <= limit
select item;
Vous spécifiez les types de paramètres de méthode et les valeurs de retour dans la déclaration de méthode. La signature suivante montre une méthode qui nécessite un int argument d’entrée et retourne une chaîne :
public string GetName(int ID)
{
if (ID < names.Length)
return names[ID];
else
return String.Empty;
}
private string[] names = ["Spencer", "Sally", "Doug"];
Après avoir déclaré une variable, vous ne pouvez pas la déclarer avec un nouveau type et vous ne pouvez pas affecter une valeur incompatible avec son type déclaré. Par exemple, vous ne pouvez pas déclarer un int et ensuite lui attribuer une valeur booléenne de true. Toutefois, vous pouvez convertir des valeurs en d’autres types, par exemple quand vous les affectez à de nouvelles variables ou les transmettre en tant qu’arguments de méthode. Le compilateur effectue automatiquement une conversion de type qui n’entraîne pas de perte de données. Une conversion susceptible d’entraîner une perte de données nécessite un cast dans le code source.
Pour plus d’informations, consultez Cast et conversions de types.
Types intégrés
C# fournit un ensemble standard de types intégrés. Ces types représentent des entiers, des valeurs à virgule flottante, des expressions booléennes, des caractères de texte, des valeurs décimales et d’autres types de données. Le langage inclut également des types intégrés string et object. Vous pouvez utiliser ces types dans n’importe quel programme C#. Pour obtenir la liste complète des types intégrés, consultez les types intégrés.
Types personnalisés
Créez des types de structure en utilisant des tuples pour stocker les membres de données associés. Ces types fournissent une structure qui contient plusieurs membres. Les tuples ont un comportement limité. Il s’agit d’un conteneur pour les valeurs. Il s’agit des types les plus simples que vous pouvez créer. Vous pouvez décider ultérieurement d’avoir besoin d’un comportement. Dans ce cas, vous pouvez convertir un tuple en un struct ou class.
Utilisez les constructions struct, class, interface, enum et record pour créer vos propres types personnalisés. La bibliothèque de classes .NET elle-même est une collection de types personnalisés que vous pouvez utiliser dans vos propres applications. Par défaut, les types les plus fréquemment utilisés dans la bibliothèque de classes sont disponibles dans n’importe quel programme C#. Vous rendez d’autres types disponibles en ajoutant explicitement une référence de package au package qui les fournit. Une fois que le compilateur a une référence au package, vous pouvez déclarer des variables et des constantes des types déclarés dans les assemblys de ce package dans le code source.
L’une des premières décisions que vous prenez lors de la définition d’un type consiste à décider quelle construction utiliser pour votre type. La liste suivante permet de prendre cette décision initiale. Certains choix se chevauchent. Dans la plupart des scénarios, plusieurs options sont un choix raisonnable.
- Si le type de données ne fait pas partie de votre domaine d’application et n’inclut pas de comportement, utilisez un type structurel.
- Si la taille de stockage des données est petite, pas plus de 64 octets, choisissez un
structourecord struct. - Si le type est immuable, ou si vous souhaitez une mutation non destructeur, choisissez un
structourecord struct. - Si votre type doit avoir une sémantique de valeur pour l’égalité, choisissez un
record classourecord struct. - Si le type est principalement destiné au stockage de données, avec un comportement minimal, choisissez un
record classourecord struct. - Si le type fait partie d’une hiérarchie d’héritage, choisissez un
record classou unclass. - Si le type utilise le polymorphisme, choisissez un
class. - Si l’objectif principal est le comportement, choisissez un
class.
Vous pouvez également choisir un interface pour modéliser un contrat : il s'agit d'un comportement décrit par les membres qui peuvent être implémentés par des types non liés. Les interfaces sont abstraites et déclarent des membres qui doivent être implémentés par tous les types class ou struct qui héritent de cette interface.
Le système de type commun
Le système de type commun prend en charge le principe de l’héritage. Les types peuvent dériver d’autres types, appelés types de base. Le type dérivé hérite (avec certaines restrictions) des méthodes, des propriétés et d’autres membres du type de base. Le type de base peut à son tour dériver d’un autre type, auquel cas le type dérivé hérite des membres des deux types de base dans sa hiérarchie d’héritage.
Tous les types, y compris les types numériques intégrés tels que System.Int32 (mot clé C# : int), dérivent finalement d’un type de base unique, qui est System.Object (mot clé C# : object). Cette hiérarchie de types unifié est appelée Common Type System (CTS). Pour plus d’informations sur l’héritage en C#, consultez Héritage.
Chaque type du CTS est défini comme un type valeur ou un type référence. Ces types incluent tous les types personnalisés dans la bibliothèque de classes .NET et vos propres types définis par l’utilisateur :
- Les types que vous définissez à l’aide des mots clés
structourecord structsont des types de valeur. Tous les types numériques intégrés sontstructs. - Les types que vous définissez à l’aide du
class,record classourecorddes mots clés sont des types de référence.
Les types de référence et les types valeur ont des règles de compilation et un comportement d’exécution différent.
Remarque
Les types les plus couramment utilisés sont tous organisés dans le namespace System. Toutefois, l’espace de noms dans lequel un type est contenu n’a aucune relation avec s’il s’agit d’un type valeur ou d’un type référence.
Les classes et les structs sont deux des constructions de base du système de type commun dans .NET. Chaque construction est essentiellement une structure de données qui encapsule un ensemble de données et de comportements qui appartiennent ensemble en tant qu’unité logique. Les données et les comportements sont les membres de la classe, du struct ou de l’enregistrement. Les membres incluent ses méthodes, ses propriétés, ses événements, et ainsi de suite, comme indiqué plus loin dans cet article.
Une déclaration de classe, de struct ou d’enregistrement est semblable à un blueprint que vous utilisez pour créer des instances ou des objets au moment de l’exécution. Si vous définissez une classe, un struct ou un enregistrement nommé Person, Person est le nom du type. Si vous déclarez et initialisez une variable p de type Person, p est considéré comme un objet ou une instance de Person. Vous pouvez créer plusieurs instances du même Person type, et chaque instance peut avoir des valeurs différentes dans ses propriétés et ses champs.
Une classe est un type référence. Lorsque vous créez un objet du type, la variable à laquelle vous affectez l’objet contient uniquement une référence à cette mémoire. Lorsque vous affectez la référence d’objet à une nouvelle variable, la nouvelle variable fait référence à l’objet d’origine. Les modifications que vous apportez via une variable sont reflétées dans l’autre variable, car elles font tous les deux référence aux mêmes données.
Un struct est un type valeur. Lorsque vous créez un struct, la variable à laquelle vous affectez le struct contient les données réelles du struct. Lorsque vous affectez le struct à une nouvelle variable, il est copié. La nouvelle variable et la variable d’origine contiennent donc deux copies distinctes des mêmes données. Les modifications apportées à une copie n’affectent pas l’autre copie.
Les types d’enregistrements peuvent être des types référence (record class) ou des types valeur (record struct). Les types d’enregistrements contiennent des méthodes qui prennent en charge l’égalité des valeurs.
En général, utilisez des classes pour modéliser un comportement plus complexe. Les classes stockent généralement les données que vous modifiez après la création d’un objet de classe. Les structs sont mieux adaptés aux petites structures de données. Les structs stockent généralement les données que vous ne modifiez pas après la création du struct. Les types d’enregistrements sont des structures de données avec des membres synthétisés supplémentaires du compilateur. Les enregistrements stockent généralement les données que vous ne modifiez pas après la création de l’objet.
Types de valeur
Les types valeur dérivent de System.ValueType, qui dérive de System.Object. Les types qui dérivent de System.ValueType ont un comportement spécial dans le CLR. Les variables de type valeur contiennent directement leurs valeurs. La mémoire d'une structure est allouée directement dans le contexte où la variable est déclarée. Vous pouvez déclarer des types record struct qui sont des types valeur et inclure les membres synthétisés pour les enregistrements.
Deux catégories de types valeur existent : struct et enum.
Les types numériques intégrés sont des structs et ont des champs et des méthodes auxquels vous pouvez accéder :
// constant field on type byte.
byte b = byte.MaxValue;
Toutefois, vous déclarez et affectez des valeurs comme si elles sont des types non agrégés simples :
byte num = 0xA;
int i = 5;
char c = 'Z';
Les types valeur sont scellés . Vous ne pouvez pas dériver un type à partir d’un type valeur, tel que System.Int32. Vous ne pouvez pas définir de struct pour hériter d’une classe ou d’un struct défini par l’utilisateur, car un struct ne peut hériter que de System.ValueType. Toutefois, un struct peut implémenter une ou plusieurs interfaces. Vous pouvez convertir un type de struct en n’importe quel type d’interface qu’il implémente. Ce cast entraîne une opération de boxing pour envelopper le struct dans un objet de type référence sur le tas managé. Les opérations de boxing se produisent quand vous passez un type valeur à une méthode qui prend un System.Object ou tout type d’interface comme paramètre d’entrée. Pour plus d’informations, consultez Boxing et Unboxing.
Utilisez le mot clé struct pour créer vos propres types de valeurs personnalisées. En règle générale, un struct est utilisé comme conteneur pour un petit ensemble de variables associées, comme illustré dans l’exemple suivant :
public struct Coords(int x, int y)
{
public int X { get; init; } = x;
public int Y { get; init; } = y;
}
Pour plus d’informations sur les structs, consultez Types de structure. Pour plus d’informations sur les types valeur, consultez Types Valeur.
L’autre catégorie de types valeur est enum. Une énumération définit un ensemble de constantes intégrales nommées. Par exemple, l’énumération System.IO.FileMode dans la bibliothèque de classes .NET contient un ensemble d’entiers de constantes nommées qui spécifient comment un fichier doit être ouvert. Elle est définie comme indiqué dans l’exemple suivant :
public enum FileMode
{
CreateNew = 1,
Create = 2,
Open = 3,
OpenOrCreate = 4,
Truncate = 5,
Append = 6,
}
La constante System.IO.FileMode.Create a la valeur 2. Toutefois, le nom est beaucoup plus significatif pour les humains lisant le code source, et pour cette raison, il est préférable d’utiliser des énumérations plutôt que des nombres littéraux constants. Pour plus d’informations, consultez System.IO.FileMode.
Toutes les énumérations héritent de System.Enum, qui hérite de System.ValueType. Toutes les règles qui s’appliquent aux structs s’appliquent également aux énumérations. Pour plus d’informations sur les énumérations, consultez Types d’énumération.
Types de référence
Type que vous définissez en tant que class, record class, record, delegate, tableau ou interface est un reference type.
Lorsque vous déclarez une variable d’un reference type, elle contient la valeur null jusqu’à ce que vous l’affectiez avec une instance de ce type ou créez-en une à l’aide de l’opérateur new . L’exemple suivant illustre la création et l’affectation d’une classe :
MyClass myClass = new();
MyClass myClass2 = myClass;
Vous ne pouvez pas instancier directement un interface à l’aide de l’opérateur new . Au lieu de cela, créez et affectez une instance d’une classe qui implémente l’interface. Prenons l’exemple suivant :
MyClass myClass = new();
// Declare and assign using an existing value.
IMyInterface myInterface = myClass;
// Or create and assign a value in a single statement.
IMyInterface myInterface2 = new MyClass();
Lorsque vous créez l’objet, le système alloue de la mémoire sur le tas managé. La variable contient uniquement une référence à l’emplacement de l’objet. Les types sur le tas managé nécessitent une surcharge à la fois quand ils sont alloués et quand ils sont récupérés. Garbage collection est la fonctionnalité de gestion automatique de la mémoire du CLR, qui effectue la récupération. Toutefois, le ramasse-miettes est également hautement optimisé et, dans la plupart des scénarios, il ne pose pas de problème de performance. Pour plus d’informations sur le ramasse-miettes, consultez Gestion automatique de la mémoire.
Tous les tableaux sont des types de référence, même si leurs éléments sont des types de valeur. Les tableaux dérivent implicitement de la System.Array classe. Vous déclarez et utilisez-les à l’aide de la syntaxe simplifiée que fournit C#, comme illustré dans l’exemple suivant :
// Declare and initialize an array of integers.
int[] nums = [1, 2, 3, 4, 5];
// Access an instance property of System.Array.
int len = nums.Length;
Les types de référence prennent entièrement en charge l’héritage. Lorsque vous créez une classe, vous pouvez hériter de toute autre interface ou classe qui n’est pas définie comme scellée. D’autres classes peuvent hériter de votre classe et remplacer vos méthodes virtuelles. Pour plus d’informations sur la création de vos propres classes, consultez Classes, structs et enregistrements. Pour plus d’informations sur l’héritage et les méthodes virtuelles, consultez Héritage.
Types de valeurs littérales
En C#, le compilateur affecte un type à des valeurs littérales. Vous pouvez spécifier la façon dont un littéral numérique doit être tapé en ajoutant une lettre à la fin du nombre. Par exemple, pour spécifier que la valeur 4.56 doit être traitée comme un float, ajoutez un « f » ou « F » après le nombre : 4.56f. Si vous n’ajoutez pas de lettre, le compilateur déduit un type pour le littéral. Pour plus d’informations sur les types que vous pouvez spécifier avec des suffixes de lettre, consultez types numériques intégraux et types numériques à virgule flottante.
Étant donné que les littéraux sont typés et que tous les types dérivent en fin de compte de System.Object, vous pouvez écrire et compiler du code comme dans l'exemple suivant :
string s = "The answer is " + 5.ToString();
// Outputs: "The answer is 5"
Console.WriteLine(s);
Type type = 12345.GetType();
// Outputs: "System.Int32"
Console.WriteLine(type);
Types génériques
Déclarez un type avec un ou plusieurs paramètres de type qui agissent en tant qu’espaces réservés pour le type réel (type concret). Le code client fournit le type concret lorsqu’il crée une instance du type. Ces types sont appelés types génériques. Par exemple, le type System.Collections.Generic.List<T> .NET a un paramètre de type que par convention est nommé T. Lorsque vous créez une instance du type, vous spécifiez le type des objets que la liste contient, tels que string:
List<string> stringList = new List<string>();
stringList.Add("String example");
// compile time error adding a type other than a string:
stringList.Add(4);
L’utilisation du paramètre de type permet de réutiliser la même classe pour contenir n’importe quel type d’élément, sans avoir à convertir chaque élément en objet. Les classes de collection générique sont des collections fortement typées , car le compilateur connaît le type spécifique des éléments de la collection et peut déclencher une erreur au moment de la compilation si, par exemple, vous essayez d’ajouter un entier à l’objet dans l’exemple stringList précédent. Pour plus d’informations, consultez Génériques.
Tuples et types anonymes
La création d’un type pour des ensembles simples de valeurs associées peut être peu pratique si vous n’avez pas l’intention de stocker ou de transmettre ces valeurs à l’aide d’API publiques. Vous pouvez créer des tuples ou des types anonymes à cet effet. Pour plus d’informations, consultez tuples et types anonymes.
Types valeur Nullable
Les types valeur ordinaires ne peuvent pas avoir la valeur null. Toutefois, vous pouvez créer des types de valeurs nullables en ajoutant un ? après le type. Par exemple, int? est un int type qui peut également avoir la valeur null. Les types valeur nullable sont des instances du type de struct générique System.Nullable<T>. Les types de valeurs nullables sont particulièrement utiles lorsque vous transmettez des données vers et depuis des bases de données dans lesquelles des valeurs numériques peuvent être null. Pour plus d'informations, consultez types de valeur nullables.
Déclarations de type implicites
Tapez implicitement une variable locale (mais pas des membres de classe) à l’aide du var mot clé. La variable reçoit toujours un type au moment de la compilation, mais le compilateur fournit le type. Pour plus d’informations, consultez Variables locales implicitement typées.
Type de compilation et type d’exécution
Une variable peut avoir différents types de temps de compilation et de temps d'exécution. Le type de compilation est le type déclaré ou différé de la variable dans le code source. Le type d’exécution est le type de l’instance référencée par cette variable. Souvent, ces deux types sont identiques, comme dans l’exemple suivant :
string message = "This is a string of characters";
Dans d’autres cas, le type de compilation est différent, comme illustré dans les deux exemples suivants :
object anotherMessage = "This is another string of characters";
IEnumerable<char> someCharacters = "abcdefghijklmnopqrstuvwxyz";
Dans les deux exemples précédents, le type d’exécution est un string. Le type au moment de la compilation est object dans la première ligne et IEnumerable<char> dans la deuxième.
Si les deux types sont différents pour une variable, il est important de comprendre quand le type de compilation et le type d’exécution s’appliquent. Le type de compilation détermine toutes les actions effectuées par le compilateur. Ces actions du compilateur incluent la résolution des appels de méthode, la résolution de surcharge et les casts implicites et explicites disponibles. Le type d’exécution détermine toutes les actions résolues au moment de l’exécution. Ces actions d’exécution incluent l'envoi des appels de méthode virtuelle, l'évaluation des expressions is et switch, ainsi que d’autres API de test de type. Pour mieux comprendre comment votre code interagit avec les types, reconnaissez l’action qui s’applique à quel type.
Sections connexes
Pour plus d’informations, consultez les articles suivants :
Spécification du langage C#
Pour plus d'informations, voir la spécification du langage C#. La spécification du langage est la source de référence pour la syntaxe C# et son utilisation.