Types de données en C#

Comme dans d'autres langages orientés objets, nous pouvons traiter deux grandes catégories de données : les types simples et les objets. En quelques mots, les types sont prédéfinis (un entier, un flottant, un caractère, etc.), mais personne ne peut prévoir l'ensemble des types dont nous pouvons avoir besoin pour notre application (des personnes, des automobiles, des adresses, etc.), et les types ne définissent pas de comportement associé (on nous dit ce que c'est, et c'est à nous de savoir ce que nous pouvons en faire). En plus de ces quelques types prédéfinis, nous avons à notre disposition un nombre illimité d'objets (certains sont prédéfinis, nous construirons les autres dont nous avons besoin) qui  définissent eux-mêmes ce que nous pouvons utiliser d'eux, et les comportements que nous devons en attendre.

Comme les types sont prédéfinis et qu'ils ne contiennent qu'une valeur (nous serons obligés par la suite de nuancer ceci pour les structures), le système d'exploitation connaît la quantité de mémoire qui lui sera nécessaire pour maintenir chaque variable d'un type donné. C'est pourquoi il peut maintenir les variables d'un type donné dans la pile (angl : stack), ce qui lui permet d'optimiser les accès en mémoire pour les variables typées. Par contre, le système d'exploitation ne peut prédire la taille mémoire nécessaire pour un objet, et il stockera ces derniers dans une zone mémoire spéciale (heap).

NB :

  • Le langage C# répond aux exigences de .NET, et la taille des types est donc définie indépendamment du système d'exploitation, au contraire des langages C et C++ qui étaient aussi fortement typés, mais dont la taille mémoire pour stocker un même type de variables dépendait du système d'exploitation.
  • Nous aurions pu aussi classer les variables en deux types : types valeurs et types référence.

Types valeur et référence

Les variables de type valeur ne peut contenir la valeur null (nous verrons par la suite ce qu'il en est de l'implémentation de nullable). Ces variables sont réparties en deux grandes catégories : Struct et Enumeration.
Les types simples sont en réalité des alias des types du framework .NET, ce qui nous permet d'invoquer leur constructeur par défaut.

Les variables de type valeur sont stockées dans la pile (stack) qui est une zone mémoire qui présente les avantages suivants :

  • Des mécanismes permettent d'accéder très rapidement aux informations de la pile.
  • Quand une valeur de la pile n'est plus utilisée, elle peut immédiatement être disponible.
  • Chaque valeur de la pile n'appartient qu'à un seul thread. Il n'est donc pas nécessaire de protéger les accès concurrentiels, cars ils sont impossibles.

Notes aux programmeurs Java

En C#, nous ne pouvons utiliser une variable tant qu'elle n'a pas été initialisée alors que c'est le cas avec les types primitifs de Java. C'est l'appel au constructeur par défaut (qui n'existe pas en Java pour lequel les types primitifs ne sont pas des objets) qui initialise la variable avec la valeur par défaut en fonction du type.

En Java, les arguments d'une méthode sont toujours passés par valeur (pour les objets, ce sont les valeurs des références qui sont passées et non les références elles-mêmes), alors qu'en C# nous pouvons passer les arguments par valeur ou par référence  : le mot clé out oblige la création de l'objet (qui ne peut donc avoir été initialisé auparavant) dans la méthode, alors que le mot clé ref oblige le passage par référence de l'objet (qui doit donc avoir été préalablement initialisé).

Les variables de type référence sont déclarées avec un des mots clés suivants : class, interface, delegate.

Les variables de type référence sont stockées dans le tas (heap) qui est une zone mémoire qui présente l'énorme avantage d'être nettement plus importante que celle allouée à la pile.

Remarques :

  • string est un type référence spécial qui le rapproche fort des types valeur (par exemple les opérateurs d'égalité utilisent le contenu de la chaîne de caractères au lieu de son emplacement mémoire). string est immuable (nous ne pouvons pas le modifier, même si nous en avons l'impression), mais nous verrons ceci plus tard.
  • object, tout comme string, est un type particulier qui n'est pas considéré comme un des types simples.

Les types entiers

byte
Une variable de type byte est codée sur un octet (8 bits), et permet des valeurs entières comprises entre 0 et 255 inclus.
Type correspondant dans le framework .NET : System.Byte
Valeur par défaut : 0.
sbyte
Une variable de type sbyte est codée sur un octet (8 bits), mais est signée(elle accepte des valeurs positives ou négatives). Le type sbyte permet des valeurs entières comprises entre -128 et 127 inclus.
Type correspondant dans le framework .NET : System.SByte.
Valeur par défaut : 0.
short
Une variable de type short est signée et codée sur deux octets (16 bits), et permet des valeurs entières comprises entre -32768 et 32767 inclus (de -215 à 215-1).
Type correspondant dans le framework .NET : System.Int16
Valeur par défaut : 0.
ushort
Une variable de type ushort est non signée et codée sur deux octets (16 bits), et permet des valeurs entières comprises entre 0 et 65535 inclus.
Type correspondant dans le framework .NET : System.UInt16
int
Une variable de type int est signée et codée sur 4 octets (32 bits), et permet des valeurs entières comprises entre -2147483648 et 2147483647 inclus (de -231 à 231-1).
Type correspondant dans le framework .NET : System.Int32
Valeur par défaut : 0.
uint
Une variable de type uint est non signée et codée sur 4 octets (32 bits), et permet des valeurs entières comprises entre 0 et 4294967295 inclus.
Type correspondant dans le framework .NET : System.UInt32
Suffixe : U ou u.
Valeur par défaut : 0.
long
Une variable de type long est signée et codée sur 8 octets (64 bits), et permet des valeurs entières comprises entre -9223372036854775808 et 9223372036854775807 inclus.
Type correspondant dans le framework .NET : System.Int64
Suffixe : L ou l (certains compilateurs peuvent générer un avertissement si nous utilisons la lettre "l" minuscule, car elle peut être confondue avec le chiffre "1").
Valeur par défaut : 0L.
ulong
Une variable de type ulong est non signée et codée sur 8 octets (64 bits), et permet des valeurs entières comprises entre 0 et 18446744073709551615 inclus.
Type correspondant dans le framework .NET : System.UInt64
Suffixe : UL.

Remarques :
  • Nous pouvons aussi employer les suffixes suivants : Ul, ul, uL, LU, Lu, lu, lU.
  • Si nous utilisons U, u, L ou l, le type sera déterminé selon la taille de la valeur.
Valeur par défaut : 0.

Quand un littéral entier n'a aucun suffixe (une lettre qui suit la valeur et qui indique explicitement le type), le type utilisé est le premier des types dans lequel cette valeur peut être représentée. Nous pouvons considérer ceci comme une série de tamis avec des trous de plus en plus grands; quand le trou est suffisamment large pour laisser passer la variable, le type est celui du tamis.

Attention aux conversions implicites qui peuvent provoquer des erreurs de compilation.

  1. byte x=10, y=20;
  2. /*
  3. le code suivant provoque une erreur, car le résultat subit une conversion implicite en int,
  4. qui nécessite une place plus importante en mémoire qu'un byte, même pour une valeur identique,
  5. car elle est codée différemment.
  6. */
  7. byte z = x + y;
  8. /*
  9. Nous devons donc veiller à effectuer une conversion explicite (casting) pour que le résultat soit de type byte.
  10. */
  11. byte z = (byte)(x + y);

Types non signés

Parmi les types entiers qui nous sont présentés ci-dessus, les types non signés (ushort, uint, et ulong) ne sont pas conformes au CLS. Tant que nous utilisons des variables de ces types de manière privée jusqu'aux accesseurs (encapsulation totale) dans nos classes, nous n'avons pas de problèmes de compatibilité avec les autres langages. Mais nous pouvons nous attendre au pire dans le cas ou un autre langage qui ne supporte pas les types non signés tenterait d'accéder à une variable d'un type non signé auquel nous aurions laissé l'accès.

Type booléen

bool
Les valeurs admises sont false et true. Ces valeurs peuvent par exemple être obtenues par le résultat d'une expression.
Type correspondant dans le framework .NET : System.Boolean
Valeur par défaut : false.

Types réels

Au contraire des variables de type entier, les variables de type réel peuvent contenir des valeurs décimales.

Nous ne devons cependant pas perdre de vue que si les nombres entiers peuvent être représentés de manière exacte, la manière de traiter les types réels en informatique ne nous permet pas en général cette exactitude : un nombre réel est toujours représenté avec plus ou moins de précision. Cela est du au fait que nous travaillons sur des fractions pour obtenir la partie décimale d'un nombre réel. Par exemple, est obtenu par la formule suivante 3,875 : 3 + 1/2 + 1/4 + 1/8.

Une variable de type réel peut contenir une valeur contenue dans l'un des 5 états suivants :

  • En cas de nombre réel :
    • Zéro positif et zéro négatif.
    • Ensemble fini de valeurs différentes de zéro.
  • Dans le cas où la succession de bits 1 et 0 ne constitue pas un nombre réel :
    • Infini positif si nous avons par exemple ajouté 1 à la valeur du plus grand nombre positif autorisé.
    • Infini négatif si nous avons par exemple retiré 1 à la valeur du plus petit nombre négatif autorisé.
    • NaN (Not a Number) dans le cas où les bits ne permettent pas la représentation d'une valeur numérique.
float
Une variable de type float est codée sur 32 bits à virgule flottante en simple précision, et permet des valeurs entre ±1,5 * 10-45 à ±3,4 * 1028 avec une précision de 7 chiffres.
Type correspondant dans le framework .NET : System.Single
Suffixe : F ou f. Dans le cas où nous oublions de spécifier le suffixe, un casting implicite est fait vers le type double, ce qui générera une erreur de compilation.
double
Une variable de type double est codée sur 64 bits à virgule flottante en double précision, et permet des valeurs entre ±5.0 * 10-324 à ±1.7 * 10308 avec une précision de 15 à 16 chiffres.
Type correspondant dans le framework .NET : System.Double
Suffixe : D ou d.
Le type double est le type utilisé par défaut pour les réels.
decimal
Une variable de type decimal est codée sur 128 bits, et permet des valeurs entre ±1,0 * 10-28 à ±7,9 * 1028 avec une précision de 28 à 29 chiffres significatifs.
Type correspondant dans le framework .NET : System.Decimal
Suffixe : M ou m.
Le type decimal permet une plus grande précision que pour les variables de type virgule flottante (float ou double), mais au détriment d'une plage de valeurs maximales plus réduite. Dans le cas où nous oublions de spécifier le suffixe, un casting implicite est fait vers le type double, ce qui générera une erreur de compilation (sauf dans le cas d'un entier).

Type char

char
Une variable de type char est codée sur 16 bits dans le système Unicode et permet des valeurs uniques de type caractère.
Type correspondant dans le framework .NET : System.Char
Nous pouvons représenter les variables de type char sous la forme de littéraux de caractères, d'une séquence d'échappement hexadécimal ou de caractères Unicode, et nous pouvons convertir les codes de caractères en type intégral.

Document créé le 20/09/2006, dernière modification le 07/04/2023
Source du document imprimé : https://www.gaudry.be/csharp-types.html

L'infobrol est un site personnel dont le contenu n'engage que moi. Le texte est mis à disposition sous licence CreativeCommons(BY-NC-SA). Plus d'info sur les conditions d'utilisation et sur l'auteur.