Accueil › Les fondations › Chapitre 2
Le vocabulaire de base
Les vingt mots qu'on emploie dans toutes les conversations, tous les tutoriels et tous les messages d'erreur — et qu'on n'ose plus demander au bout de six mois. Ce chapitre est fait pour être relu sans honte.
Le plus petit programme du monde
Console.WriteLine("Bonjour !");
Une ligne. Pas de classe, pas de Main, pas de cérémonie. C'est ce qu'on appelle les
instructions de haut niveau (top-level statements) : depuis C# 9, le compilateur écrit pour toi
l'enrobage. Si tu vois du code plus long dans un vieux tutoriel, c'est exactement la même chose :
internal class Program
{
private static void Main(string[] args)
{
Console.WriteLine("Bonjour !");
}
}
Main est le point d'entrée : la première méthode exécutée. Il y en a un seul par
programme exécutable.
Anatomie d'une ligne de code
Calculer(...)) produit une valeur.| Mot | Définition sans jargon | Exemple |
|---|---|---|
| Variable | Un nom donné à une valeur qu'on veut réutiliser | int age = 42; |
| Type | La nature de la valeur, et donc ce qu'on peut en faire | int, string, Client |
| Littéral | Une valeur écrite en dur dans le code | 42, "salut", true |
| Opérateur | Un symbole qui agit sur des valeurs | + - * / == && ?? |
| Expression | Tout ce qui produit une valeur | prix * 1.2m |
| Instruction | Tout ce qui fait quelque chose, terminé par ; | total = 12; |
| Bloc | Un groupe d'instructions entre accolades. Définit une portée | { … } |
| Mot-clé | Un mot réservé du langage | if, class, return |
| Identificateur | Un nom que tu choisis | total, Client |
var n'est pas « n'importe quoi »
var total = 12.5m; crée une variable de type decimal, définitivement. Le type est
déduit une fois, à la compilation ; il ne changera jamais. Ce n'est pas le var de JavaScript ni le
typage dynamique de Python. (Le vrai équivalent dynamique, dynamic, existe mais ne sert
quasiment jamais.)
Méthodes : des machines à transformer
void = elle ne renvoie rien.// ┌─ modificateur d'accès ┌─ nom ┌─ paramètres
// │ ┌─ type de retour │ │
public decimal CalculerTotal(decimal prix, int quantite)
{
return prix * quantite; // ce que la méthode rend
}
// Version courte, identique : « corps d'expression »
public decimal CalculerTotal2(decimal prix, int quantite) => prix * quantite;
// Ne renvoie rien : void
public void Journaliser(string message) => Console.WriteLine(message);
Paramètres : optionnels, nommés, surcharges
// valeur par défaut → l'appelant peut omettre le paramètre
public decimal Total(decimal prix, int quantite = 1, decimal remise = 0m)
=> prix * quantite * (1 - remise);
Total(10m); // 10
Total(10m, 3); // 30
Total(10m, remise: 0.5m); // 5 ← argument nommé : on saute quantite
Total(prix: 10m, quantite: 2); // 20 ← nommer rend l'appel lisible
Plusieurs méthodes peuvent porter le même nom si leurs paramètres diffèrent : c'est la surcharge. Le compilateur choisit selon les types passés.
Une valeur par défaut est recopiée dans le code appelant à la compilation. Si tu changes
remise = 0m en remise = 0.1m dans une bibliothèque, les projets déjà compilés qui
l'utilisent continuent d'envoyer 0m jusqu'à leur recompilation. Pour du code public partagé,
préfère une surcharge explicite.
Classe, objet, membres
La classe est le plan de la maison. L'objet est une maison construite. Un plan, trois cents maisons : même forme, adresses et habitants différents. On ne dort pas dans un plan.
public class CompteBancaire // le plan
{
private decimal _solde; // CHAMP : stockage interne, caché
public string Titulaire { get; } // PROPRIÉTÉ : lecture seule de l'extérieur
public decimal Solde => _solde; // PROPRIÉTÉ calculée
public CompteBancaire(string titulaire, decimal soldeInitial) // CONSTRUCTEUR
{
Titulaire = titulaire;
_solde = soldeInitial;
}
public void Deposer(decimal montant) // MÉTHODE
{
if (montant <= 0) throw new ArgumentException("Montant invalide");
_solde += montant;
}
}
// Utilisation : on INSTANCIE avec new
var compte = new CompteBancaire("Ada", 100m); // un OBJET
compte.Deposer(50m);
Console.WriteLine(compte.Solde); // 150
// compte._solde = 0; ← impossible : _solde est private
| Membre | Rôle | Convention |
|---|---|---|
| Champ | Stocker une donnée, en interne | private decimal _solde; — souligné + camelCase |
| Propriété | Exposer une donnée avec contrôle | public string Nom { get; set; } — PascalCase |
| Méthode | Agir | public void Deposer(...) — un verbe |
| Constructeur | Mettre l'objet dans un état valide dès sa naissance | même nom que la classe |
Écris public string Nom { get; set; } plutôt qu'un champ public. Une propriété se transforme
plus tard en calcul, en validation ou en journalisation sans casser le code qui l'utilise. Un champ
public rendu privé, lui, casse tout le monde.
Statique ou d'instance ?
var c1 = new CompteBancaire("Ada", 100m);
var c2 = new CompteBancaire("Bob", 50m);
c1.Deposer(10m); // n'affecte que c1
// chaque objet a SON soldeMath.Max(1, 2); // pas de new Math()
DateTime.Now; // partagé
CompteBancaire.TauxLivretA // un seul pour tousPratique, mais un état statique mutable est partagé par toute l'application, y compris entre requêtes web simultanées : source classique de bugs.
Namespace, assembly, projet : les poupées russes
namespace Boutique.Api.Commandes; // namespace « de fichier » (C# 10+) : pas d'accolades
using Boutique.Domaine; // « je veux pouvoir écrire Commande sans le préfixer »
using static System.Math; // importe les membres statiques : Max(...) directement
using Journal = Serilog.Log; // alias, en cas de conflit de noms
public class ServiceCommande { }
using n'inclut ni ne copie rien : c'est juste un raccourci d'écriture. Sans lui, il faudrait
écrire Boutique.Domaine.Commande à chaque fois.
<ImplicitUsings>enable</ImplicitUsings> ajoute automatiquement les using
les plus courants (System, System.Linq, System.Collections.Generic…),
d'où les fichiers sans aucun using visible. Tu peux compléter la liste une fois pour tout le
projet dans un fichier GlobalUsings.cs :
global using Boutique.Domaine;
global using Microsoft.EntityFrameworkCore;Les structures de contrôle en une page
// --- Condition ---
if (age >= 18) { /* ... */ }
else if (age >= 16) { /* ... */ }
else { /* ... */ }
var etiquette = age >= 18 ? "majeur" : "mineur"; // opérateur ternaire
// --- Aiguillage (switch expression, la forme moderne) ---
var tarif = categorie switch
{
"enfant" => 5m,
"senior" => 8m,
"adulte" => 12m,
_ => throw new ArgumentException("catégorie inconnue") // _ = tous les autres cas
};
// --- Boucles ---
for (int i = 0; i < 10; i++) { } // compteur maîtrisé
foreach (var client in clients) { } // parcourir une collection (le plus fréquent)
while (!termine) { } // tant que
do { } while (!termine); // au moins une fois
foreach (var c in clients)
{
if (c.Inactif) continue; // passe au suivant
if (c.Bloque) break; // sort de la boucle
}
foreach par défaut : plus lisible et impossible de se tromper d'indice. for
seulement quand tu as vraiment besoin de la position. Et souvent, ni l'un ni l'autre : LINQ dit la même chose
en une ligne (chapitre 7).
Les conventions de nommage (elles comptent)
| Élément | Convention | Exemple |
|---|---|---|
| Classe, record, struct, enum | PascalCase | ServiceCommande |
| Interface | PascalCase précédé de I | IEnvoyeurEmail |
| Méthode, propriété, événement | PascalCase | CalculerTotal, Solde |
| Paramètre, variable locale | camelCase | prixUnitaire |
| Champ privé | _ + camelCase | _solde |
| Constante | PascalCase (pas de MAJUSCULES en C#) | const int TailleMax = 100; |
| Méthode asynchrone | suffixe Async | ChargerAsync |
| Fichier | un type public par fichier, même nom | ServiceCommande.cs |
Ces règles ne sont pas de la coquetterie : tout l'écosystème les suit, donc un nom qui les respecte
se lit sans effort. dotnet format et les analyseurs les vérifient automatiquement.
Commentaires et documentation
// commentaire sur une ligne
/* commentaire
sur plusieurs lignes */
/// <summary>Calcule le total TTC d'une ligne de commande.</summary>
/// <param name="prix">Prix unitaire hors taxes.</param>
/// <returns>Le montant TTC arrondi au centime.</returns>
/// <exception cref="ArgumentOutOfRangeException">Si le prix est négatif.</exception>
public decimal TotalTtc(decimal prix, int quantite) { /* ... */ }
Les commentaires /// apparaissent dans l'autocomplétion de l'éditeur et alimentent la
documentation OpenAPI de tes API (voir
chapitre 14). C'est le seul type de commentaire qui « sort » du fichier.
// on incrémente i n'apporte rien : le code le dit déjà.
// TVA à 8,5 % : taux dérogatoire Corse, cf. ticket #1042 sauve la journée de celui qui relira
dans deux ans. Si tu as besoin d'un commentaire pour expliquer ce que fait le code, c'est souvent que
le code mérite d'être renommé ou découpé.
Lire un message d'erreur du compilateur
Les erreurs C# ont un code (CSxxxx) et sont, contrairement à leur réputation, très précises.
Voici celles que tu verras cent fois :
| Code | Message (traduit) | Cause réelle, en clair |
|---|---|---|
CS0103 | Le nom « x » n'existe pas dans ce contexte | Faute de frappe, ou variable déclarée dans un autre bloc (portée) |
CS0246 | Le type ou espace de noms « Client » est introuvable | using manquant, ou référence de projet/paquet absente |
CS0029 / CS1503 | Impossible de convertir « string » en « int » | Tu passes le mauvais type ; utilise int.TryParse |
CS0161 | Tous les chemins de code ne retournent pas de valeur | Un if sans else qui oublie de return |
CS8602 | Déréférencement possible d'une référence null | Le compilateur t'avertit d'un NullReferenceException probable (ch. 9) |
CS0120 | Une référence d'objet est nécessaire pour un membre non statique | Tu appelles une méthode d'instance depuis un contexte statique, sans objet |
CS1061 | « Client » ne contient pas de définition pour « Nom » | Nom de membre erroné, ou membre non public depuis l'extérieur |
Corrige toujours la première erreur de la liste et recompile : les suivantes sont souvent des conséquences en cascade. Et cherche le code exact (« CS0246 ») plutôt que la phrase : c'est plus efficace.
Vérifie que c'est passé
🎯 Quiz — 4 questions
1. Quelle est la différence entre un champ et une propriété ?
private. La propriété est
un guichet : elle peut valider, journaliser, calculer, ou devenir en lecture seule — sans casser les
appelants.2. using System.Text.Json; en haut d'un fichier…
.csproj (ou de la bibliothèque de base).3. Math.Max(3, 7) s'appelle sans new Math(). Pourquoi ?
static existe en un seul exemplaire, attaché au type. Pas
besoin d'instance — d'où l'absence de new.4. Le compilateur dit CS0103 : le nom « total » n'existe pas dans ce contexte, alors que tu
l'as bien déclaré. Que regardes-tu en premier ?
if ou une boucle meurt à l'accolade
fermante. Déclare-la avant le bloc si tu en as besoin après.Fiches de révision
a + b). Une instruction fait quelque chose et finit par ;.new transforme le plan en maison.internal.var ?private readonly string _nom; — souligné puis camelCase.Async : ChargerClientAsync. Convention universelle dans l'écosystème.- Variable = nom + type + valeur. Méthode = entrées → travail → sortie.
- Classe = plan, objet = exemplaire créé avec
new. - Champ privé pour stocker, propriété publique pour exposer.
- static = attaché au type, partagé ; méfiance s'il est modifiable.
- namespace = rangement de noms ; assembly = livrable et frontière
internal. - Respecte les conventions de nommage : tout l'écosystème les suit.