---
title: 'Les variables en C# : types, var, const et conversions'
description: 'Déclarer une variable en C#, choisir le bon type (int, decimal, bool, string…), comprendre la différence entre const et readonly, gérer le null et convertir une chaîne en nombre.'
pubDate: 2019-03-18
updatedDate: 2026-09-27
family: 'dotnet-csharp'
tags: ['CSharp']
heroImage: '/images/apprendre-le-csharp-les-variables.webp'
videoUrl: 'https://www.youtube.com/watch?v=8mxYnn-nkUk&list=PLkHw7J3J2iapWFUnQmVzsRCU5YxaAWHSY&index=1'
faq:
  - question: 'Quelle est la différence entre var et dynamic en C# ?'
    answer: "var est fortement typé : le compilateur déduit le type à partir de l'initialisation et ce type est fixé à la compilation — var i = 5; donne un int, exactement comme si tu l'avais écrit. dynamic repousse la résolution au moment de l'exécution, ce qui retire le contrôle du compilateur et les performances qui vont avec. var n'est pas un typage dynamique."
  - question: 'Quand utiliser const plutôt que readonly ?'
    answer: "const est une constante de compilation : sa valeur doit être connue à la compilation et elle est copiée dans les assemblies qui l'utilisent — si elle change, il faut tout recompiler. readonly est une constante d'exécution : elle peut être initialisée dans un constructeur, donc dépendre d'un calcul ou d'une configuration. Pour une valeur qui vient d'un fichier de configuration ou d'un DateTime, c'est readonly."
  - question: 'Pourquoi mon code ne compile pas quand je lis une variable locale non initialisée ?'
    answer: "Parce que les valeurs par défaut ne s'appliquent pas aux variables locales. Les types comme int valent 0 et les types référence null, mais uniquement pour les champs et les éléments de tableau non initialisés. Une variable locale doit recevoir une valeur avant d'être lue : le compilateur refuse le code plutôt que de te laisser lire une valeur indéfinie."
  - question: 'Comment convertir une chaîne en nombre sans faire planter le programme ?'
    answer: "Avec TryParse, qui renvoie un booléen et écrit le résultat dans une variable out, sans lever d'exception si la chaîne est invalide. int.Parse lève une FormatException, à entourer d'un try/catch, et Convert.ToInt32 peut lever en plus une OverflowException si le nombre dépasse la capacité du type. Pour une saisie utilisateur, c'est TryParse."
  - question: 'Les noms de variables sont-ils sensibles à la casse en C# ?'
    answer: "Oui, et c'est le compilateur qui l'impose : prix et Prix sont deux variables distinctes. Les conventions de casse, elles, ne sont pas vérifiées par le compilateur — mais elles sont claires et l'outillage s'y appuie : camelCase pour les variables locales et les paramètres, PascalCase pour les types, les membres publics et les constantes."
---

Une variable, c'est un emplacement de stockage nommé, avec un type. En C#, ce type est explicite — ou
déduit à la compilation quand tu écris `var` — et c'est lui qui détermine ce que la variable peut
contenir, la place qu'elle occupe en mémoire, et ce que le compilateur te laissera en faire.

## Déclarer et initialiser

La forme générale est `type nom;`, et l'initialisation se fait avec `=` :

```csharp
string pseudo;            // déclaration
int a, b, c;              // trois variables du même type

string message = "Bonjour";     // déclaration + initialisation
int x = 3, y = 2, z = x + y;
List<double> valeurs = new();   // new() ciblé par le type (C# 9+)
```

Le nom suit quelques règles vérifiées par le compilateur : il commence par une lettre ou un tiret bas, il
peut contenir des lettres Unicode et des chiffres, et il est **sensible à la casse** — `prix` et `Prix` sont
deux variables différentes. Pour utiliser un mot-clé comme nom, on le préfixe par `@` (`@if`).

Les conventions de casse, elles, ne sont **pas** imposées par le compilateur, seulement par la
[documentation de l'équipe .NET](https://learn.microsoft.com/en-us/dotnet/csharp/fundamentals/coding-style/identifier-names) :

```csharp
const int MaxTentatives = 3;   // PascalCase pour une constante
int compteur = 0;              // camelCase pour une variable locale
class CompteBancaire { }       // PascalCase pour un type
```

## Les types à connaître

La documentation range les types prédéfinis en deux familles
([types intégrés](https://learn.microsoft.com/en-us/dotnet/csharp/language-reference/builtin-types/built-in-types)) :
les **types valeur** (`bool`, `char`, `int`, `long`, `double`, `decimal`…) qui contiennent directement
leur donnée, et les **types référence** (`object`, `string`, `delegate`, `dynamic`) qui pointent vers un
objet. Chaque mot-clé est un alias du type .NET : `int` = `System.Int32`, `string` = `System.String`.

Les entiers, avec leurs plages réelles ([doc](https://learn.microsoft.com/en-us/dotnet/csharp/language-reference/builtin-types/integral-numeric-types)) :

| Mot-clé            | Plage                                              | Taille  |
| ------------------ | -------------------------------------------------- | ------- |
| `sbyte` / `byte`   | −128 à 127 / 0 à 255                               | 8 bits  |
| `short` / `ushort` | −32 768 à 32 767 / 0 à 65 535                      | 16 bits |
| `int` / `uint`     | −2 147 483 648 à 2 147 483 647 / 0 à 4 294 967 295 | 32 bits |
| `long` / `ulong`   | jusqu'à ±9,2 × 10¹⁸                                | 64 bits |

`int` est le type entier par défaut : `var n = 5;` donne un `int`. Les littéraux acceptent des suffixes —
`u` pour non signé, `L` pour `long` (majuscule, sinon le `l` se confond avec un 1) — et des formes
hexadécimales (`0x2A`) ou binaires (`0b_0010_1010`).

Les nombres à virgule demandent plus d'attention, parce que le choix a des conséquences
([doc](https://learn.microsoft.com/en-us/dotnet/csharp/language-reference/builtin-types/floating-point-numeric-types)) :

```csharp
double d = 3.934_001;     // sans suffixe : double
float f = 5.4f;
decimal prix = 3000.5m;   // m pour decimal
```

Point crucial : `12.3` est un **`double`**, jamais un `decimal`. Et pour l'argent, `decimal` est le type
adapté — `0.1` y est représenté exactement, alors qu'aucun `double` ne le représente sans erreur
d'arrondi. Attention, `decimal` ne se mélange pas avec `float` ou `double` sans conversion explicite.

Enfin deux types simples : `bool` (`true` ou `false` uniquement, `false` par défaut) et `char`, qui
représente une **unité de code UTF-16** sur 16 bits — pas un caractère Unicode complet dans tous les cas.

Côté types référence, `string` mérite une remarque qui surprend les débutants : c'est un type
**référence**, mais les opérateurs `==` et `!=` comparent le **contenu**, pas les références. Et une
chaîne est **immuable** : `b += "ello"` ne modifie pas la chaîne, il en crée une nouvelle.

## var : du confort, pas du typage dynamique

`var` demande au compilateur de déduire le type depuis l'initialisation
([doc](https://learn.microsoft.com/en-us/dotnet/csharp/programming-guide/classes-and-structs/implicitly-typed-local-variables)) :

```csharp
var i = 5;                      // compilé comme int
var s = "Hello";                // compilé comme string
var liste = new List<int>();    // compilé comme List<int>
Console.WriteLine(i.GetType()); // System.Int32
```

La variable reste **fortement typée** : le type est fixé à la compilation, et rien ne changera ensuite.
Quatre restrictions à retenir : `var` n'est utilisable que sur une variable locale déclarée **et**
initialisée dans la même instruction, il est interdit avec `null` (le type serait indéduit), interdit sur
un champ de classe, et obligatoire pour les types anonymes. À ne pas confondre avec `dynamic`, qui
repousse la résolution au runtime.

## Valeurs par défaut, et la règle des variables locales

| Type                      | Valeur par défaut    |
| ------------------------- | -------------------- |
| Type référence            | `null`               |
| Entier ou flottant        | `0`                  |
| `bool`                    | `false`              |
| `char`                    | `'\0'` (U+0000)      |
| `enum`                    | `(E)0`               |
| `struct`                  | champs à leur défaut |
| Type valeur nullable `T?` | `null`               |

Piège classique : ces valeurs concernent les **champs** et les éléments de tableau, pas les variables
locales. Une variable locale doit être affectée avant d'être lue, sinon le compilateur refuse le code
([valeurs par défaut](https://learn.microsoft.com/en-us/dotnet/csharp/language-reference/builtin-types/default-values)).

## const ou readonly ?

`const` est une constante de **compilation** : initialisée à la déclaration, sa valeur doit être connue du
compilateur, et elle est propagée dans les assemblies appelants — si tu la changes, il faut recompiler
ceux qui l'utilisent ([const](https://learn.microsoft.com/en-us/dotnet/csharp/language-reference/keywords/const)).

`readonly` est une constante d'**exécution** : elle peut être initialisée dans un constructeur, donc
dépendre d'un calcul ou d'une configuration ([readonly](https://learn.microsoft.com/en-us/dotnet/csharp/language-reference/keywords/readonly)).

```csharp
const string Plateforme = ".NET";
public static readonly uint Horodatage = (uint)DateTime.Now.Ticks;
```

Nuance à ne pas rater : sur un type référence, `readonly` empêche de **remplacer** la référence, pas de
modifier le contenu de l'objet pointé.

## Le null et le point d'interrogation

Un type valeur peut accepter `null` avec `T?`, ce qui correspond à `System.Nullable<T>` :

```csharp
int? age = null;
if (age.HasValue) Console.WriteLine(age.Value);
Console.WriteLine(age?.ToString());   // null-conditional : aucune exception
int total = age ?? 0;                 // coalescence nulle
```

Trois usages distincts du `?` qu'il ne faut pas confondre : `int?` est un type nullable, `age?.ToString()`
est l'opérateur null-conditional, et `age ?? 0` est la coalescence nulle. Et pour les types référence, la
version nullable (`string?`) fait partie d'une analyse statique **désactivée par défaut**, que l'on active
au niveau du projet.

## Convertir une chaîne en nombre

Trois méthodes, trois comportements ([doc](https://learn.microsoft.com/en-us/dotnet/csharp/programming-guide/types/how-to-convert-a-string-to-a-number)) :

```csharp
int.TryParse(saisie, out int nombre);   // renvoie un booléen, ne lève rien
int m = int.Parse("11");                // lève FormatException si la chaîne est invalide
int n = Convert.ToInt32("42");          // peut lever OverflowException en plus
```

`TryParse` est la bonne réponse pour une saisie utilisateur : pas d'exception à gérer, un simple `if`.
À savoir : `Parse` et `TryParse` tolèrent les espaces en début et en fin de chaîne, mais tout espace **à
l'intérieur** est une erreur — et une chaîne vide échoue aussi.

C'est déjà beaucoup pour un premier contact avec les variables, mais ces six notions — type, `var`,
défaut, `const`, `null`, conversion — sont celles qui reviennent à chaque fichier C# que tu écriras. La
suite naturelle, ce sont les structures de contrôle : j'ai détaillé
[les instructions conditionnelles](/blog/apprendre-le-csharp-les-instructions-conditionnelles/) dans le
même esprit.

Dans la vidéo, je reprends les variables pas à pas, avec les erreurs de compilation les plus fréquentes du
débutant.

## Questions fréquentes

**Quelle est la différence entre var et dynamic en C# ?**

var est fortement typé : le compilateur déduit le type à partir de l'initialisation et ce type est fixé à la compilation — var i = 5; donne un int, exactement comme si tu l'avais écrit. dynamic repousse la résolution au moment de l'exécution, ce qui retire le contrôle du compilateur et les performances qui vont avec. var n'est pas un typage dynamique.

**Quand utiliser const plutôt que readonly ?**

const est une constante de compilation : sa valeur doit être connue à la compilation et elle est copiée dans les assemblies qui l'utilisent — si elle change, il faut tout recompiler. readonly est une constante d'exécution : elle peut être initialisée dans un constructeur, donc dépendre d'un calcul ou d'une configuration. Pour une valeur qui vient d'un fichier de configuration ou d'un DateTime, c'est readonly.

**Pourquoi mon code ne compile pas quand je lis une variable locale non initialisée ?**

Parce que les valeurs par défaut ne s'appliquent pas aux variables locales. Les types comme int valent 0 et les types référence null, mais uniquement pour les champs et les éléments de tableau non initialisés. Une variable locale doit recevoir une valeur avant d'être lue : le compilateur refuse le code plutôt que de te laisser lire une valeur indéfinie.

**Comment convertir une chaîne en nombre sans faire planter le programme ?**

Avec TryParse, qui renvoie un booléen et écrit le résultat dans une variable out, sans lever d'exception si la chaîne est invalide. int.Parse lève une FormatException, à entourer d'un try/catch, et Convert.ToInt32 peut lever en plus une OverflowException si le nombre dépasse la capacité du type. Pour une saisie utilisateur, c'est TryParse.

**Les noms de variables sont-ils sensibles à la casse en C# ?**

Oui, et c'est le compilateur qui l'impose : prix et Prix sont deux variables distinctes. Les conventions de casse, elles, ne sont pas vérifiées par le compilateur — mais elles sont claires et l'outillage s'y appuie : camelCase pour les variables locales et les paramètres, PascalCase pour les types, les membres publics et les constantes.
