Chapitre 125 minIntermédiaire
Les bases de C# pour la POO
Par Josselyn C., prof de BTS SIO. Mis à jour le .
À la fin de ce chapitre, tu sauras
- installer .NET 10 et créer une application console ;
- choisir le bon type, notamment
decimalpour l'argent ; - écrire conditions, boucles et méthodes en C# ;
- mettre en forme un texte avec l'interpolation ;
- manipuler une valeur qui peut être nulle sans planter.
5 exercices corrigés
C# (prononcé « C sharp ») est un langage de programmation de Microsoft, typé et compilé, qui tourne sur la plateforme .NET sous Windows, macOS et Linux. Ce chapitre installe ton environnement et te donne les bases du langage dont tu as besoin pour la suite du cours : types, variables, conditions, boucles, méthodes, interpolation de chaînes et types nullables. Si tu viens de JavaScript ou de l'algorithmique, tu retrouveras les mêmes idées avec une syntaxe plus stricte.
Pourquoi apprendre la POO en C#
La programmation orientée objet s'apprend dans n'importe quel langage objet : Java, C#, PHP, Python, TypeScript. Ce cours a choisi C# pour trois raisons.
- Le typage strict t'aide à apprendre. Chaque variable a un type déclaré ; le compilateur refuse de ranger un texte dans un nombre ou d'appeler une méthode qui n'existe pas. Les erreurs de conception apparaissent avant l'exécution, avec un message qui dit où chercher.
- Tout le vocabulaire de la POO existe en vrai.
class,private,abstract,interface,virtual,override: ce que les sujets d'examen écrivent en UML ou en Java se traduit mot pour mot. - C'est un langage d'entreprise. Applications de bureau (WinForms, WPF), API web (ASP.NET Core), jeux (Unity), applications mobiles (.NET MAUI) : tu le retrouveras en stage et en alternance.
Java, que tu croiseras dans les sujets d'E7, est le cousin de C# : même famille de syntaxe, mêmes concepts. Le chapitre 16 donne les équivalences pour passer de l'un à l'autre.
Installer .NET 10 et ton éditeur
Le langage C# s'exécute sur .NET, la plateforme gratuite et libre de Microsoft. Il te faut le SDK (Software Development Kit), qui contient le compilateur, les bibliothèques et la commande dotnet.
| Version | Type | Sortie | Fin du support |
|---|---|---|---|
| .NET 8 | LTS (support long) | novembre 2023 | 10 novembre 2026 |
| .NET 10 (C# 14) | LTS (support long) | 11 novembre 2025 | 14 novembre 2028 |
| .NET 11 (C# 15) | STS (support court, 2 ans) | novembre 2026 | novembre 2028 |
Le cours utilise .NET 10, la version à support long : c'est celle que les entreprises installent et que tu peux garder pendant tes deux ans de BTS. Télécharge le SDK sur le site officiel dotnet.microsoft.com, puis vérifie l'installation dans un terminal :
dotnet --version
# 10.0.203 (le dernier nombre change à chaque mise à jour de sécurité)
Pour écrire ton code, trois éditeurs gratuits pour un usage étudiant :
| Éditeur | Système | Remarque |
|---|---|---|
| Visual Studio 2026 Community | Windows | L'environnement complet de Microsoft, gratuit pour les étudiants et les particuliers. Indispensable pour WinForms |
| Visual Studio Code + extension C# Dev Kit | Windows, macOS, Linux | Léger ; l'extension ajoute l'explorateur de solutions et le lancement des tests |
| JetBrains Rider | Windows, macOS, Linux | Gratuit pour un usage non commercial (études, projets personnels) |
Créer et lancer un projet console
Une application console affiche du texte dans un terminal : c'est le meilleur terrain pour apprendre la POO, sans interface graphique pour te distraire. Crée le projet du fil rouge :
dotnet new console -n GestParc.Console
cd GestParc.Console
dotnet run
# Hello, World!
La commande crée un dossier avec deux fichiers utiles :
<!-- GestParc.Console.csproj : la description du projet -->
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>net10.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
</PropertyGroup>
</Project>
// Program.cs : le point d'entrée
Console.WriteLine("Hello, World!");
Program.cs ne contient ni classe ni méthode Main : depuis C# 9, les instructions de niveau supérieur (top-level statements) te laissent écrire directement le code à exécuter. Le compilateur génère la méthode Main pour toi. ImplicitUsings importe les espaces de noms courants (System, System.Collections.Generic, System.Linq…), et Nullable active le contrôle des valeurs nulles, expliqué plus bas.
Dans Visual Studio, tu obtiens la même chose avec Créer un projet → Application console ; dans VS Code, avec la palette de commandes (.NET: New Project). Le bouton ▶ ou F5 lance le programme.
Variables et types
En C#, chaque variable a un type fixé à la déclaration. Le compilateur vérifie que tu n'y ranges que des valeurs de ce type.
string numInventaire = "PC-0007";
int anneeAchat = 2023;
decimal prixAchat = 849.90m;
bool enStock = false;
DateOnly dateAchat = new DateOnly(2023, 3, 14);
var marque = "Dell"; // le compilateur déduit le type string
Console.WriteLine($"{numInventaire} ({marque}) acheté le {dateAchat:dd/MM/yyyy} pour {prixAchat:C}");
Console.WriteLine($"En stock : {enStock}, âge : {2026 - anneeAchat} ans");
PC-0007 (Dell) acheté le 14/03/2023 pour 849,90 €
En stock : False, âge : 3 ans
Les types que tu utiliseras tout le temps :
| Type | Contenu | Exemple | Valeur par défaut |
|---|---|---|---|
int | Entier de −2 147 483 648 à 2 147 483 647 | int duree = 45; | 0 |
long | Entier sur 64 bits | long octets = 5_000_000_000; | 0 |
double | Nombre à virgule flottante, environ 15 chiffres significatifs | double ratio = 0.75; | 0 |
decimal | Nombre décimal exact, 28 chiffres : pour l'argent | decimal prix = 849.90m; | 0 |
bool | true ou false | bool actif = true; | false |
char | Un caractère, entre apostrophes | char lettre = 'A'; | '\0' |
string | Une chaîne de caractères, entre guillemets | string nom = "Lambert"; | null |
DateOnly | Une date sans heure | new DateOnly(2026, 11, 2) | 01/01/0001 |
DateTime | Une date et une heure | new DateTime(2026, 11, 2, 9, 15, 0) | 01/01/0001 00:00:00 |
var demande au compilateur de déduire le type à partir de la valeur. La variable reste typée : var marque = "Dell"; déclare un string, et marque = 12; sera refusé. Utilise var quand le type saute aux yeux (var poste = new Poste(...)), écris le type quand il aide le lecteur.
const déclare une constante connue à la compilation : const int DureeMaxMin = 480;. Par convention, les variables locales s'écrivent en camelCase et les constantes, les classes, les méthodes et les propriétés en PascalCase.
Opérateurs et conversions
Les opérateurs arithmétiques sont ceux de l'algorithmique, avec un piège : la division de deux entiers est une division entière.
Console.WriteLine(7 / 2); // 3 : deux int, division entière
Console.WriteLine(7 / 2.0); // 3,5 : un des deux est un double
Console.WriteLine(7 % 2); // 1 : le reste (modulo)
| Opérateurs | Rôle |
|---|---|
+ - * / % | Calcul ; + concatène aussi deux chaînes |
== != < > <= >= | Comparaison, résultat bool |
&& || ! | Et, ou, non logiques (évaluation paresseuse : a && b ne calcule pas b si a est faux) |
+= -= *= /= ++ -- | Raccourcis : total += d; équivaut à total = total + d; |
? : | Condition en une expression : age >= 5 ? "ancien" : "récent" |
Pour convertir un texte en nombre, deux méthodes : int.Parse("12") lève une exception si le texte n'est pas un nombre, int.TryParse renvoie false sans planter. Avec une saisie de l'utilisateur, c'est toujours TryParse :
string saisie = "12a";
if (int.TryParse(saisie, out int nombre))
{
Console.WriteLine($"Nombre : {nombre}");
}
else
{
Console.WriteLine($"« {saisie} » n'est pas un nombre entier");
}
« 12a » n'est pas un nombre entier
Entre deux types numériques, la conversion est implicite quand elle ne perd rien (int vers long ou double) et doit être explicite, avec un cast, quand elle peut perdre de l'information : int heures = (int)3.9; donne 3, la partie décimale est tronquée.
Chaînes et interpolation
L'interpolation est la façon moderne de construire un texte : un $ devant les guillemets, des expressions entre accolades. Après un deux-points, tu ajoutes un format.
string nom = "Diallo";
decimal prix = 1234.5m;
Console.WriteLine($"{prix:C} | {prix:N2} | {prix}");
Console.WriteLine($"[{nom,10}] [{nom,-10}] [{3.14159:0.00}] [{42:000}]");
1 234,50 € | 1 234,50 | 1234,5
[ Diallo] [Diallo ] [3,14] [042]
| Format | Effet | Exemple |
|---|---|---|
:C | Monnaie de la culture | 849,90 € |
:N2 | Nombre à deux décimales, avec séparateur de milliers | 1 234,50 |
:0.00 ou :000 | Gabarit personnalisé | 3,14, 042 |
:P0 | Pourcentage | 20 % |
:dd/MM/yyyy | Date | 14/03/2023 |
,10 ou ,-10 | Largeur 10, alignée à droite ou à gauche | utile pour les tableaux en console |
Une chaîne est immuable : nom.ToUpper() renvoie une nouvelle chaîne et ne modifie pas nom. Les méthodes que tu utiliseras le plus : Trim(), ToUpper(), ToLower(), Contains(), StartsWith(), Replace(), Split(), Substring(), la propriété Length, et string.IsNullOrWhiteSpace(s) pour tester une saisie vide.
Conditions : if et switch
int ageAns = 2026 - anneeAchat;
if (ageAns >= 5)
{
Console.WriteLine("À renouveler");
}
else if (ageAns >= 3)
{
Console.WriteLine("À surveiller");
}
else
{
Console.WriteLine("Récent");
}
À surveiller
L'expression switch associe une valeur à chaque cas et renvoie un résultat. Le cas _ attrape tout le reste ; s'il manque, le compilateur avertit que des cas ne sont pas traités.
string priorite = "haute";
int delaiHeures = priorite switch
{
"critique" => 4,
"haute" => 8,
"normale" => 24,
"basse" => 72,
_ => throw new ArgumentException($"Priorité inconnue : {priorite}")
};
Console.WriteLine($"Délai pour une priorité {priorite} : {delaiHeures} h");
Délai pour une priorité haute : 8 h
L'ancienne instruction switch (priorite) { case "haute": … break; } existe toujours ; tu la croiseras dans du code existant et dans les sujets. Chaque case doit se terminer par break ou return.
Boucles : for, foreach et while
string[] postes = ["PC-0001", "PC-0002", "PC-0003"];
for (int i = 0; i < postes.Length; i++)
{
Console.WriteLine($"{i + 1}. {postes[i]}");
}
int[] durees = [30, 45, 20, 90];
int total = 0;
foreach (int d in durees)
{
total += d;
}
Console.WriteLine($"Total : {total} min, soit {total / 60} h {total % 60} min");
1. PC-0001
2. PC-0002
3. PC-0003
Total : 185 min, soit 3 h 5 min
forquand tu as besoin de l'indice ;foreachpour parcourir tous les éléments d'un tableau ou d'une collection, sans indice : c'est la boucle que tu écriras le plus en POO ;while (condition) { … }quand le nombre de tours dépend d'une condition, par exemple redemander une saisie tant qu'elle est invalide ;do { … } while (condition);fait au moins un tour.
break sort de la boucle, continue passe directement au tour suivant.
Les crochets ["PC-0001", "PC-0002"] sont une expression de collection (C# 12). Tu verras aussi l'ancienne écriture new string[] { "PC-0001", "PC-0002" }. Un tableau a une taille fixe (postes.Length) ; pour une liste qui grandit, tu utiliseras List<T> au chapitre 6.
Méthodes
Une méthode est un bloc de code nommé, qui reçoit des paramètres et peut renvoyer une valeur. Sa signature indique le type de retour (void s'il n'y en a pas), le nom et la liste des paramètres typés.
Console.WriteLine(ValeurNette(849.90m, 3, 4));
Console.WriteLine(FormaterDuree(185));
Console.WriteLine(FormaterDuree(45));
static decimal ValeurNette(decimal prix, int ageAns, int dureeAns)
{
if (ageAns >= dureeAns)
{
return 0m;
}
return Math.Round(prix * (dureeAns - ageAns) / dureeAns, 2, MidpointRounding.AwayFromZero);
}
static string FormaterDuree(int minutes)
{
int h = minutes / 60;
int m = minutes % 60;
return h > 0 ? $"{h} h {m:00}" : $"{m} min";
}
212,48
3 h 05
45 min
Dans Program.cs, les méthodes se déclarent après les instructions de niveau supérieur, avec le mot-clé static. À partir du chapitre suivant, elles vivront dans des classes. Quelques possibilités utiles :
- Paramètre facultatif :
static string Formater(int minutes, bool court = false); l'appelFormater(45)prendfalse. - Argument nommé :
Formater(45, court: true)rend l'appel lisible. - Paramètre
out: la méthode renvoie une valeur supplémentaire, commeint.TryParse(saisie, out int nombre). - Corps d'expression : une méthode d'une seule expression s'écrit avec
=>:static int Carre(int x) => x * x;.
Les types nullables
null signifie « pas de valeur ». C'est la source d'erreur la plus courante en programmation objet : appeler une méthode sur null provoque une NullReferenceException à l'exécution. C# t'aide de deux façons.
Pour les types valeur (int, decimal, bool, DateOnly…), un ? autorise null : int? etage = null; signifie « étage inconnu », ce qui est différent de « rez-de-chaussée » (0). La propriété HasValue dit si une valeur est présente.
Pour les types référence (string et toutes tes classes), le réglage <Nullable>enable</Nullable> du projet change les règles : string signifie « jamais null », string? signifie « peut être null ». Le compilateur avertit (CS8600, CS8602) quand tu risques de manipuler un null sans l'avoir vérifié.
string? telephone = null;
Console.WriteLine(telephone ?? "non renseigné"); // ?? : valeur de repli si null
Console.WriteLine(telephone?.Length); // ?. : null au lieu de planter
int? etage = null;
Console.WriteLine(etage.HasValue ? $"Étage {etage.Value}" : "Pas de bureau");
etage ??= 0; // ??= : affecte seulement si null
Console.WriteLine(etage);
non renseigné
Pas de bureau
0
La deuxième ligne affichée est vide : telephone?.Length vaut null, et Console.WriteLine(null) écrit une ligne vide.
Lire une saisie au clavier
Console.ReadLine() renvoie un string? : null si l'entrée est fermée. Une saisie fiable se fait dans une boucle qui redemande tant que la valeur est invalide.
int duree;
Console.Write("Durée de l'intervention (min) : ");
while (!int.TryParse(Console.ReadLine(), out duree) || duree <= 0)
{
Console.Write("Saisis un nombre entier positif : ");
}
Console.WriteLine($"Durée enregistrée : {FormaterDuree(duree)}");
De JavaScript à C# : les équivalences
| Besoin | JavaScript | C# |
|---|---|---|
| Déclarer une variable | let n = 5; | int n = 5; ou var n = 5; |
| Constante | const TVA = 0.2; | const decimal Tva = 0.2m; |
| Afficher | console.log(x) | Console.WriteLine(x) |
| Interpoler | `Total : ${t}` | $"Total : {t}" |
| Égalité | === | == (les types sont déjà vérifiés) |
| Tableau | [1, 2, 3] | int[] t = [1, 2, 3]; |
| Taille | t.length | t.Length (tableau), liste.Count (liste) |
| Parcourir | for (const x of t) | foreach (var x in t) |
| Fonction | function f(a) { … } | static int F(int a) { … } |
| Fonction fléchée | x => x * 2 | x => x * 2 (une lambda, même syntaxe) |
| Valeur absente | null ou undefined | null, seulement pour un type nullable |
| Repli | a ?? b, a?.b | a ?? b, a?.b (identiques) |
Exercices
Exercice 1 sur 5 Prédire les affichages Réussi À revoir
Sans exécuter, donne ce qu'affiche chaque ligne.
Console.WriteLine(7 / 2);
Console.WriteLine(7 / 2.0);
Console.WriteLine(7 % 2);
Console.WriteLine(0.1 + 0.2);
Console.WriteLine(0.1m + 0.2m);
Voir la réponseMasquer la réponse de l'exercice 1
3
3,5
1
0,30000000000000004
0,3
7 / 2 divise deux int : le résultat est l'entier 3. Dès qu'un opérande est un double (2.0), la division est décimale. % donne le reste. La quatrième ligne montre l'imprécision du double, qui stocke les nombres en binaire ; le decimal calcule en base 10 et donne 0,3 exactement.
Tu avais trouvé ?
Exercice 2 sur 5 Facturer une intervention Réussi À revoir
Sorbier Logistique refacture certaines interventions aux filiales, au quart d'heure entamé : 50 minutes sont facturées comme 60 minutes. Écris la méthode static decimal CoutIntervention(int minutes, decimal tauxHoraire) qui renvoie le montant dû, et 0 pour une durée nulle ou négative. Donne le résultat pour 50 min à 60 €/h, 61 min à 60 €/h et 15 min à 48 €/h.
Voir la réponseMasquer la réponse de l'exercice 2
static decimal CoutIntervention(int minutes, decimal tauxHoraire)
{
if (minutes <= 0)
{
return 0m;
}
int quarts = (minutes + 14) / 15; // quart d'heure entamé = quart d'heure dû
return quarts * tauxHoraire / 4;
}
- 50 min :
(50 + 14) / 15= 4 quarts d'heure, 4 × 60 / 4 = 60 €. - 61 min :
(61 + 14) / 15= 5 quarts d'heure, 5 × 60 / 4 = 75 €. - 15 min : 1 quart d'heure, 48 / 4 = 12 €.
L'astuce (minutes + 14) / 15 arrondit au quart d'heure supérieur grâce à la division entière. Math.Ceiling(minutes / 15.0) marche aussi, mais renvoie un double qu'il faut reconvertir.
Tu avais trouvé ?
Exercice 3 sur 5 Les valeurs nulles Réussi À revoir
Donne l'affichage de ce programme.
string? tel = null;
Console.WriteLine(tel?.Length ?? 0);
int? etage = null;
Console.WriteLine((etage + 1).HasValue);
etage ??= 2;
etage ??= 5;
Console.WriteLine(etage);
Voir la réponseMasquer la réponse de l'exercice 3
0
False
2
tel?.Length vaut null puisque tel est nul, et ?? 0 remplace ce null par 0. Un calcul avec un int? nul donne null : etage + 1 n'a pas de valeur. Le premier ??= affecte 2 car etage est nul ; le second ne fait rien, etage ayant maintenant une valeur.
Tu avais trouvé ?
Exercice 4 sur 5 Dérouler une boucle Réussi À revoir
Que vaut total à la fin ?
int total = 0;
for (int i = 1; i <= 10; i++)
{
if (i % 3 == 0) { continue; }
if (i > 8) { break; }
total += i;
}
Voir la réponseMasquer la réponse de l'exercice 4
total vaut 27. Les multiples de 3 (3, 6, 9) sont sautés par continue, avant le test du break. On ajoute donc 1, 2, 4, 5, 7 et 8 ; à i = 9, le continue passe au tour suivant ; à i = 10, i > 8 déclenche le break. 1 + 2 + 4 + 5 + 7 + 8 = 27.
Tu avais trouvé ?
Exercice 5 sur 5 Corriger les erreurs de compilation Réussi À revoir
Ce code contient quatre erreurs ou avertissements. Trouve-les et corrige-les.
decimal prix = 849.90;
int quantite = "3";
string? commentaire = null;
Console.WriteLine(commentaire.Length);
if (quantite = 3) { Console.WriteLine("ok"); }
Voir la réponseMasquer la réponse de l'exercice 5
decimal prix = 849.90;: erreur CS0664, un littéral à virgule est undouble. Écris849.90m.int quantite = "3";: erreur CS0029, un texte n'est pas un entier. Écrisint quantite = 3;, ouint.Parse("3")si la valeur vient d'un texte.commentaire.Length: avertissement CS8602, déréférencement d'une référence peut-être nulle ; ici, c'est uneNullReferenceExceptioncertaine. Écriscommentaire?.Length ?? 0.if (quantite = 3): erreur CS0029, une affectationintn'est pas unbool. Écrisif (quantite == 3).
Tu avais trouvé ?