C# est typé statiquement : chaque variable, champ et expression a un type connu à la compilation. Le langage possède un ensemble de types intégrés dotés de leurs propres mots-clés (int, double, string...). Chaque mot-clé est un alias d'un type du namespace System : int et System.Int32 sont le même type, tout comme string et System.String.
Les types intégrés
| Type | Type .NET | Taille | Plage ou précision | Littéral |
|---|---|---|---|---|
bool | Boolean | 1 octet | true ou false | true |
byte | Byte | 1 octet | 0 à 255 | 200 |
sbyte | SByte | 1 octet | -128 à 127 | -5 |
short | Int16 | 2 octets | -32 768 à 32 767 | 1200 |
ushort | UInt16 | 2 octets | 0 à 65 535 | 60000 |
int | Int32 | 4 octets | -2 147 483 648 à 2 147 483 647 | 42 |
uint | UInt32 | 4 octets | 0 à 4 294 967 295 | 42u |
long | Int64 | 8 octets | environ ±9,2 × 10^18 | 42L |
ulong | UInt64 | 8 octets | 0 à environ 1,8 × 10^19 | 42ul |
float | Single | 4 octets | environ ±3,4 × 10^38, ~6 à 9 chiffres | 2.5f |
double | Double | 8 octets | environ ±1,8 × 10^308, ~15 à 17 chiffres | 2.5 ou 2.5d |
decimal | Decimal | 16 octets | environ ±7,9 × 10^28, 28 à 29 chiffres | 2.5m |
char | Char | 2 octets | une unité de code UTF-16, de U+0000 à U+FFFF | 'A' |
string | String | référence | texte de longueur quelconque, immuable | "hello" |
object | Object | référence | la base de tous les types | new object() |
Un littéral entier sans suffixe est un int, ou le premier type parmi uint, long et ulong capable de le contenir s'il est trop grand pour un int, et un littéral avec un point décimal est un double. C'est pourquoi float f = 2.5; et decimal d = 2.5; ne compilent pas : le littéral est un double, et il n'existe pas de conversion implicite de double vers float ou decimal. Ajoutez le suffixe : 2.5f, 2.5m.
Chaque type numérique expose ses limites sous forme de constantes :
Sortie :
byte: 0 to 255
short: -32768 to 32767
int: -2147483648 to 2147483647
long: -9223372036854775808 to 9223372036854775807
decimal max: 79228162514264337593543950335
double max: 1.798E+308
sizes: int 4, long 8, double 8, char 2
255 161 3000000000
En pratique, la plupart du code utilise quatre de ces types : int pour les compteurs et les index, long pour les identifiants et les grands totaux, double pour les mesures et les calculs, et decimal pour l'argent.
decimal ou double
double (et float) stockent les nombres en binaire. La plupart des fractions décimales, dont 0,1, n'ont pas de forme binaire exacte : elles sont stockées comme la valeur représentable la plus proche, et de petites erreurs s'accumulent. decimal stocke un entier en base 10 plus une échelle, donc 0,1 vaut exactement 0,1.
Sortie :
False
True
0.30000000000000004
0.3
double sum == 1.0: False
decimal sum == 1.0: True
Total: 59.97
Split three ways: 19.99
Utilisez decimal pour l'argent, les prix, les taxes et tout ce qu'une personne additionnera à la main et comparera. Il conserve les zéros de fin de ses entrées (1.50m s'affiche 1.50) et a 28 à 29 chiffres significatifs.
Utilisez double pour les mesures physiques, les statistiques, la géométrie et les jeux. Il est plusieurs fois plus rapide, utilise moitié moins de mémoire, et sa plage est énorme. Ne comparez jamais deux doubles calculés avec == ; comparez leur différence à une tolérance : Math.Abs(x - y) < 1e-9.
decimal n'a rien de magique : 1m / 3m donne toujours 0.3333333333333333333333333333, et l'arithmétique double ne lève pas d'exception sur une division par zéro (elle produit l'infini, ou NaN pour 0.0 / 0.0), alors que la division par zéro en decimal et en entier lève une DivideByZeroException.
Dépassement d'entier et checked
Une opération entière qui dépasse la plage du type boucle par défaut, silencieusement. Le compilateur détecte le dépassement dans les expressions constantes, mais pas quand les valeurs viennent de variables :
Sortie :
-2147483648
OverflowException
-1294967296
2147483648
checked(...) ou un bloc checked { ... } transforme le dépassement en OverflowException. Vous pouvez aussi l'activer pour tout le projet avec <CheckForOverflowUnderflow>true</CheckForOverflowUnderflow>. Écrire int x = int.MaxValue + 1; avec des constantes est une erreur de compilation (CS0220), car le compilateur voit le dépassement.
La dernière ligne montre la correction de la plupart des bugs de dépassement : convertir vers un type plus large avant le calcul. (long)(max + 1) serait faux, puisque l'addition a déjà bouclé au moment où la conversion s'exécute.
Types valeur et types référence
Chaque type C# est soit un type valeur, soit un type référence, et cette différence détermine ce que fait l'affectation.
- Types valeur : tous les types numériques,
bool,char,DateTime, les enums et chaquestruct. La variable contient la donnée ; l'affectation la copie. - Types référence :
string, les tableaux,objectet chaqueclass. La variable contient une référence vers un objet ; l'affectation copie la référence, donc les deux variables voient le même objet.
Sortie :
a=10 b=99
first[0]=99
s1=cat s2=cats
string est un type référence qui se comporte en pratique comme une valeur : il est immuable, et == compare le texte, pas les références. Plus de détails sur les types valeur définis par l'utilisateur dans structs.
char et bool
Un char est une unité de code UTF-16, écrite entre apostrophes. C'est un type numérique en interne, il se convertit donc en int et accepte l'arithmétique :
Sortie :
66
C
True
True
7
b
True
bool a exactement deux valeurs et ne se convertit pas vers ou depuis des nombres : if (count) est une erreur de compilation, pas un test de non-nullité. Écrivez if (count != 0). Les caractères situés hors du plan multilingue de base, comme la plupart des emoji, occupent deux valeurs char (une paire de substitution), donc la Length d'une string compte des unités UTF-16, pas des caractères visibles.
Valeurs par défaut
Les champs, les éléments de tableau et default(T) reçoivent la valeur par défaut du type : zéro pour les nombres, false pour bool, '\0' pour char et null pour les types référence. Les variables locales n'ont pas de valeur par défaut et doivent être affectées avant utilisation.
Sortie :
0
0
False
True
0001-01-01
0,0,0
Depuis C# 7.1, vous pouvez écrire le littéral default sans le type quand le compilateur peut le déduire (int x = default;). Un type valeur ne peut pas valoir null ; c'est à cela que sert int? (un type valeur nullable), voir types nullables.
Entiers de taille native et grands entiers
Deux autres types numériques existent pour des cas particuliers. nint et nuint (C# 9) sont des entiers de la taille d'un pointeur, 32 ou 64 bits selon la plateforme, utilisés dans l'interopérabilité et le code bas niveau. System.Numerics.BigInteger contient des entiers de taille quelconque, au prix de la vitesse :
using System.Numerics;
BigInteger factorial = 1;
for (int i = 2; i <= 30; i++) factorial *= i;
// 265252859812191058636308480000000
Questions fréquentes
Quels sont les types de données en C# ?
Les types intégrés sont les entiers (sbyte, byte, short, ushort, int, uint, long, ulong), les types à virgule flottante float et double, decimal pour l'arithmétique décimale exacte, char pour un caractère UTF-16, bool, et les types référence string et object. Chaque mot-clé est un alias d'un type .NET : int est System.Int32, string est System.String.
Faut-il utiliser decimal ou double pour l'argent en C# ?
Utilisez decimal. Un double stocke des fractions binaires, donc des valeurs comme 0,1 ne peuvent pas être représentées exactement et les sommes dérivent (0.1 + 0.2 == 0.3 vaut false). Un decimal stocke des chiffres en base 10, conserve 28 à 29 chiffres significatifs et donne les résultats qu'attendent les comptables. double est plus rapide et a une plage bien plus grande : utilisez-le pour la science, le graphisme et les mesures.
Quelle est la valeur maximale d'un int en C# ?
int.MaxValue vaut 2 147 483 647 et int.MinValue vaut -2 147 483 648 (un entier signé sur 32 bits). Ajouter 1 à int.MaxValue revient silencieusement à int.MinValue, sauf si le code s'exécute dans un contexte checked, où une OverflowException est levée. Utilisez long (jusqu'à environ 9,2 trillions) pour les valeurs plus grandes.
Quelle est la différence entre float et double en C# ?
float occupe 32 bits avec environ 6 à 9 chiffres significatifs ; double occupe 64 bits avec environ 15 à 17. Les littéraux à virgule flottante sont des double par défaut, donc un float a besoin du suffixe f : float speed = 2.5f;. Utilisez double sauf si la mémoire ou une API (comme le Vector3 de Unity) impose float.
Quelle est la différence entre types valeur et types référence en C# ?
Une variable de type valeur (int, double, bool, DateTime, toute struct) contient la donnée elle-même, et l'affectation la copie. Une variable de type référence (string, tableaux, toute class) contient une référence vers un objet sur le tas, et l'affectation copie la référence, si bien que deux variables peuvent pointer vers le même objet.