Cours C# Complet 💜

Les 12 chapitres essentiels — de la syntaxe de base à la POO avancée avec .NET, exemples et quiz

12
Chapitres
100+
Exemples de code
C# 12
Version
A1→C2
Niveaux

CHAPITRE 01

Introduction et premier programme

💜 Hello World
// C# classique (avant .NET 6)
using System;

namespace MonApp

// C# moderne — top-level statements (.NET 6+)
Console.WriteLine(« Bonjour le monde ! »);

Top-level statements (.NET 6+) : Plus besoin de classe Main ni de namespace. Le compilateur les génère automatiquement. Idéal pour les scripts et petits programmes. C'est le style recommandé pour commencer.

⚙️ Créer et exécuter un projet
# Installer le SDK .NET
# https://dotnet.microsoft.com/download

# Créer un projet console
dotnet new console -n MonApp
cd MonApp

# Exécuter
dotnet run

# Compiler (produit un .dll ou .exe)
dotnet build

🏗️ C# en résumé
Caractéristique C#
Typage Statique et fort — vérifié à la compilation
Paradigme Orienté objet + fonctionnel
Runtime .NET (CLR — Common Language Runtime)
Portabilité Cross-platform depuis .NET Core / .NET 5+
Usage Web (ASP.NET), desktop (WPF, MAUI), jeux (Unity), cloud (Azure)
Convention PascalCase pour les méthodes/propriétés, camelCase pour les variables locales

C# vs Java : Syntaxe très similaire, mais C# évolue plus vite (propriétés, LINQ, async/await natif, pattern matching, records). Si vous connaissez JavaScript ou TypeScript, vous retrouverez beaucoup de concepts communs.

🧠 Quiz
Quel est le point d'entrée d'un programme C# classique ?
static void Main(string[] args) — Depuis .NET 6, les top-level statements permettent de s'en passer.

CHAPITRE 02

Variables et types de données

📦 Types valeur (Value Types)
Type Taille Plage Exemple
byte 1 octet 0 à 255 byte b = 42;
int 4 octets ±2.1 milliards int n = 42;
long 8 octets ±9.2 × 10¹⁸ long l = 42L;
float 4 octets ~7 chiffres float f = 3.14f;
double 8 octets ~15 chiffres double d = 3.14;
decimal 16 octets ~28 chiffres decimal m = 3.14m;
bool 1 bit true / false bool b = true;
char 2 octets Un caractère Unicode char c = 'A';

decimal vs double : Utilisez decimal pour les calculs financiers (pas d'erreurs d'arrondi). double pour les calculs scientifiques (plus rapide). Suffixes obligatoires : f (float), m (decimal), L (long).

📝 String et var
// String — type référence, immuable
string nom = « Alice »;

// Interpolation (recommandée)
string salut = $ »Bonjour , vous avez ans »;

// Concaténation
string s = « Hello » +  » World »;

// Verbatim string (pas d'échappement)
string path = @ »C:\Users\Alice\docs »;

// Raw string literal (C# 11+)
string json = «  » »

«  » »;

// Méthodes string
nom.Length // 5
nom.ToUpper() // « ALICE »
nom.ToLower() // « alice »
nom.Contains(« lic ») // true
nom.StartsWith(« A ») // true
nom.Substring(0, 3) // « Ali »
nom.Replace(« A », « a ») // « alice »
nom.Trim() // Supprime espaces
nom.Split(',') // string[]
string.IsNullOrEmpty(s) // null ou «  »
string.IsNullOrWhiteSpace(s) // null, «  » ou espaces

// var — inférence de type (le compilateur déduit)
var age = 25; // int
var liste = new List<string>(); // List

❓ Nullable types
// Les types valeur ne peuvent pas être null par défaut
int age = null; // ❌ Erreur
int? age = null; // ✅ Nullable int

// Vérifier et utiliser
if (age.HasValue)

// ?? — valeur par défaut si null
int result = age ?? 0;

// ??= — assigner si null
age ??= 18;

// ?. — null-conditional (évite NullReferenceException)
string? ville = user?.Address?.City;

🧠 Quiz
Quelle est la différence entre int et int? ?
int = ne peut pas être null (type valeur). int? = peut être null (Nullable). Utile quand une valeur peut être absente (ex: champ optionnel de BDD).

CHAPITRE 03

Opérateurs et expressions

⚖️ Opérateurs essentiels
// Arithmétique
+ * / % ++

// Division entière (comme Java)
7 / 2 // 3 (pas 3.5 !)
7.0 / 2 // 3.5

// Comparaison
== != < > <= >=

// Logique
&& || !

// Ternaire
string status = age >= 18 ? « majeur » : « mineur »;

// Null-coalescing (??) et null-conditional (?.)
string nom = input ?? « Inconnu »;
int? len = text?.Length;

// is — vérifier le type (pattern matching)
if (obj is string s)

// as — cast sûr (retourne null si impossible)
string? s = obj as string;

CHAPITRE 04

Structures conditionnelles

🔀 if / switch / switch expression
// if / else if / else
if (age < 13) else if (age < 18) else

// Switch expression (C# 8+) — concis, retourne une valeur
string label = day switch
;

// Pattern matching dans switch (C# 9+)
string category = age switch
;

Switch expression (=>) : Beaucoup plus concis que le switch classique. Pas de break, retourne une valeur, et le compilateur vérifie que tous les cas sont couverts. Similaire au switch de JavaScript mais en mieux.

CHAPITRE 05

Les boucles

🔁 for, foreach, while
// for classique
for (int i = 0; i < 5; i++)

// foreach — parcourir une collection
string[] fruits = ;
foreach (var fruit in fruits)

// while et do-while
while (i < 10)
do while (i < 10);

📊 Tableaux (Arrays)
// Déclaration
int[] nombres = ;
string[] noms = new string[3]; // Taille fixe

// Accès
nombres[0] // 1
nombres[^1] // 5 (dernier — Index from end)
nombres[1..3] // (Range — C# 8+)

// Propriétés et méthodes
nombres.Length // 5
Array.Sort(nombres);
Array.Reverse(nombres);
Array.IndexOf(nombres, 3);
Array.Exists(nombres, x => x > 3);

Arrays = taille fixe. Pour des collections dynamiques, utilisez List (chapitre 10). L'index ^1 (from end) et le range 1..3 sont des features modernes très pratiques.

CHAPITRE 06

POO : Classes et objets

🏛️ Définir une classe
public class Person

Propriétés ≠ champs. En C#, on utilise des propriétés (avec get/set) au lieu d'accéder directement aux champs. C'est la convention universelle — pas besoin de getters/setters manuels comme en Python.

⚡ Primary Constructors (C# 12)
// C# 12 — paramètres directement dans la déclaration de classe
public class Person(string name, int age)
🔒 Modificateurs d'accès
Modificateur Portée
public Accessible partout
private Classe uniquement (défaut pour les membres)
protected Classe + sous-classes
internal Même assembly (projet)
protected internal Même assembly OU sous-classes
private protected Même assembly ET sous-classes
⚡ static et required
public class Counter

// required (C# 11) — oblige l'initialisation
public class Config

var config = new Config ;

🧠 Quiz
Différence entre get; set; et get; init; ?
set = modifiable n'importe quand. init = assignable uniquement à la construction (object initializer). Après, c'est en lecture seule. Parfait pour l'immutabilité.

CHAPITRE 07

Héritage et polymorphisme

🔗 Héritage
public class Animal

public class Dog : Animal

// Polymorphisme
Animal a = new Dog(« Rex », « Labrador »);
a.Speak(); // « Rex : Wouf ! » — appelle la version Dog

📦 Records (C# 9+)
// Record = classe de données immuable (comme Java 16 ou TypeScript)
public record Point(double X, double Y);

var p = new Point(3, 4);
p.X // 3
Console.WriteLine(p); // « Point  »

// Comparaison par valeur (pas par référence)
var p2 = new Point(3, 4);
p == p2 // true (compare les valeurs)

// Copie avec modification (with expression)
var p3 = p with ; // Point

// record struct (C# 10) — type valeur
public readonly record struct Color(byte R, byte G, byte B);

Records = le standard pour les données. Ils génèrent automatiquement Equals, GetHashCode, ToString, et le déconstructeur. Préférez les records aux classes quand vous modélisez des données.

🧠 Quiz
Différence entre class et record en C# ?
class = comparaison par référence, mutable. record = comparaison par valeur, immuable par défaut, avec ToString/Equals auto-générés et l'expression with pour copier.

CHAPITRE 08

Interfaces et classes abstraites

📋 Interfaces
public interface ISerializable

public interface IValidatable

// Implémentation multiple
public class User : ISerializable, IValidatable

Convention C# : Les interfaces commencent par I (ISerializable, IDisposable, IEnumerable). C'est une convention forte de l'écosystème .NET.

🎭 Abstract class vs Interface
public abstract class Shape

public class Circle(double radius) : Shape

Interface Abstract class
Héritage Multiple Simple uniquement
Constructeur
Champs
Usage « Peut faire » (contrat) « Est un » (base commune)

CHAPITRE 09

Gestion des exceptions

🛡️ try / catch / finally
try

catch (DivideByZeroException ex)

catch (Exception ex) // Attrape tout le reste

finally

// Lever une exception
public void SetAge(int age)

// Exception personnalisée
public class InsufficientFundsException : Exception

🔥 Exceptions courantes
Exception Cause
NullReferenceException Accès à un objet null
ArgumentException Argument invalide
ArgumentNullException Argument null
InvalidOperationException Opération invalide pour l'état courant
IndexOutOfRangeException Index hors limites
FormatException int.Parse(« abc »)
IOException Erreur lecture/écriture

CHAPITRE 10

Collections et Generics

📋 List, Dictionary, HashSet
// List — le plus utilisé (comme ArrayList en Java)
var noms = new List<string> ;
noms.Add(« Charlie »);
noms.Remove(« Bob »);
noms.Contains(« Alice »); // true
noms.Count // 2
noms[0] // « Alice »

// Dictionary
var ages = new Dictionary<string, int>
;
ages[« Alice »] // 25
ages.TryGetValue(« Eve », out var age); // false, age = 0
ages.ContainsKey(« Bob »); // true

// HashSet — éléments uniques
var ids = new HashSet<int> ; //

// Queue et Stack
var queue = new Queue<string>(); // FIFO
var stack = new Stack<string>(); // LIFO

🔄 Generics
// Méthode générique
public T Max<T>(T a, T b) where T : IComparable<T>
=> a.CompareTo(b) > 0 ? a : b;

// Classe générique
public class Result<T>

// Contraintes where
// where T : class — type référence
// where T : struct — type valeur
// where T : new() — constructeur sans paramètre
// where T : IComparable — implémente l'interface
// where T : BaseClass — hérite de BaseClass

🧠 Quiz
Quelle collection utiliser pour des paires clé/valeur ?
Dictionary. Accès O(1) par clé. Utilisez TryGetValue au lieu de l'indexeur direct pour éviter les KeyNotFoundException.

CHAPITRE 11

LINQ et Lambdas

⚡ Expressions lambda
// Lambda = fonction anonyme
Func<int, int> double_ = x => x * 2;
Func<int, int, int> add = (a, b) => a + b;
Action<string> log = msg => Console.WriteLine(msg);

// Func = retourne une valeur
// Action = void (pas de retour)
// Predicate = retourne bool

🌊 LINQ (Language Integrated Query)
var nombres = new List<int> ;

// Where — filtrer
var pairs = nombres.Where(n => n % 2 == 0); //

// Select — transformer
var doubles = nombres.Select(n => n * 2); //

// Chaînage complet
var result = nombres
.Where(n => n > 3)
.Select(n => n * 2)
.OrderByDescending(n => n)
.Take(3)
.ToList(); //

// Opérations utiles
nombres.First() // 1
nombres.FirstOrDefault() // 1 (ou 0 si vide)
nombres.Last() // 10
nombres.Any(n => n > 5) // true
nombres.All(n => n > 0) // true
nombres.Count() // 10
nombres.Sum() // 55
nombres.Average() // 5.5
nombres.Min() // 1
nombres.Max() // 10
nombres.Distinct() // Dédupliquer
nombres.GroupBy(n => n % 2) // Grouper
nombres.ToDictionary(n => n, n => n * 2) // Dict

LINQ = le super-pouvoir de C#. Similaire aux Streams Java ou à filter/map de JavaScript, mais intégré directement dans le langage. Where = filter, Select = map.

🧠 Quiz
Différence entre Select et Where en LINQ ?
Select = transforme chaque élément (équivalent de map). Where = filtre les éléments (équivalent de filter).

CHAPITRE 12

Async/Await et C# moderne

⚡ async / await
// Méthode asynchrone — retourne Task
public async Task<string> FetchDataAsync(string url)

// void async = Task (pas de retour)
public async Task SaveAsync(string data)

// Parallèle
var task1 = FetchDataAsync(« url1 »);
var task2 = FetchDataAsync(« url2 »);
var results = await Task.WhenAll(task1, task2);

Convention : Toute méthode async se termine par Async (FetchDataAsync, SaveAsync). Utilisez toujours await — ne bloquez jamais avec .Result ou .Wait().

📄 Fichiers
// Lire
string content = await File.ReadAllTextAsync(« file.txt »);
string[] lines = await File.ReadAllLinesAsync(« file.txt »);

// Écrire
await File.WriteAllTextAsync(« out.txt », « Contenu »);
await File.AppendAllTextAsync(« log.txt », « Nouvelle ligne\n »);

// Vérifier
File.Exists(« file.txt »);
Directory.Exists(« folder »);

🔮 Pattern Matching avancé (C# 9-12)
// Type pattern
string Describe(object obj) => obj switch
;

// Property pattern
string GetDiscount(User user) => user switch
;

// List pattern (C# 11)
int[] arr = ;
if (arr is [1, .., 3])

✅ Bonnes pratiques

✅ À FAIRE
var quand le type est évident
• Propriétés auto (get; set;)
Records pour les données
LINQ pour les collections
async/await pour l'I/O
Switch expressions
Pattern matching
Nullable reference types activé
using pour IDisposable

❌ À ÉVITER
.Result et .Wait() (deadlocks)
catch (Exception) vide
public fields (utiliser propriétés)
• string concat en boucle (StringBuilder)
goto
dynamic sauf nécessité
• Ignorer les warnings nullable
Thread direct (utiliser Task)

🧠 Quiz
Pourquoi utiliser await au lieu de .Result ?
.Result bloque le thread courant et peut causer des deadlocks. await libère le thread pendant l'attente, permettant au programme de rester réactif.
Qu'est-ce que LINQ en une phrase ?
LINQ = des requêtes intégrées au langage pour filtrer, transformer et agréger n'importe quelle collection avec une syntaxe fluide (Where, Select, OrderBy, etc.).

Cours C# Complet — Des bases à C# 12 / .NET 8

Référence : Microsoft C# Docs | dotnet.microsoft.com