Programmation 2 — 420-2A6-VI (Hiver 2026)

Programmation 2 — 420-2A6-VI (Hiver 2026)

Notes de cours complètes — Prof. Tousignant Simon

Préalable : Prog 1 (420-1D6-VI)
Donne accès à : Prog 3, Jeux 2D, Jeux 3D, Applications mobiles, Nano-ordinateurs
Langage : C# | IDE : Visual Studio Community 2022 | Versioning : Git + GitHub Classroom


⚠️ Règles importantes du cours

  • IA et générateurs de code strictement interdits (ChatGPT, Copilot, etc.) → exclusion immédiate de la période
  • Cellulaires interdits dans le local (pauses incluses)
  • 3 exclusions = démarches administratives avec l'API
  • Paquets VS autorisés uniquement :
    • Outils de développement d'application de bureau .NET
    • Outils de développement .NET Framework 4.7.2
    • C# et Visual Basic
    • (Interdits : IntelliCode, IntelliSense avancé, LiveShare)

Convention Git

  • 1 commit par fonctionnalité terminée et testée
  • Nom de compte GitHub : cgpvicto-[numéro_DA]
  • Plateforme : GitHub Classroom (groupes 00001 et 00002)

MODULE 1 — Types, tableaux et structures de contrôle

Types de base en C

Entiers

Type Octets Min Max
short 2 -32 768 32 767
ushort 2 0 65 535
int 4 -2 147 483 648 2 147 483 647
uint 4 0 4 294 967 295
long 8 -9 223 372 036 854 775 808 9 223 372 036 854 775 807
ulong 8 0 18 446 744 073 709 551 615

Virgule flottante

Type Octets Précision
float 4 ~6-9 chiffres
double 8 ~15-17 chiffres
decimal 16 28-29 chiffres

Autres types courants

Type Octets Notes
bool 1 true / false
char 2 1 caractère Unicode
string variable Jusqu'à 2 147 483 647 caractères
byte 1 0 à 255
sbyte 1 -128 à 127

Tableaux

// Unidimensionnel
int[] nombres = { 1, 2, 3 };

// Multidimensionnel (matrice)
int[,] grille = new int[3, 3];

// Tableau de tableaux (jagged array)
int[][] tableau = new int[3][];

Structures de contrôle

// if / else if / else
if (condition) { } else if (condition2) { } else { }

// for
for (int i = 0; i < 10; i++) { }

// while
while (condition) { }

// do while (toujours exécuté au moins 1 fois)
do { } while (condition);

// foreach (lecture seule — ne modifie pas la structure)
foreach (int val in tableau) { }

Compilation et arguments de ligne de commande

Lors de la compilation, Visual Studio produit un exécutable dans [solution]/[projet]/bin/debug/.

public static void Main(string[] args)
{
    foreach (string parametre in args)
    {
        Console.WriteLine(parametre);
    }
}

Note : Les paramètres avec espaces doivent être entre guillemets dans l'invite de commande.
Pour simuler des arguments dans VS : Déboguer → Propriétés de déboguage → Arguments de la ligne de commande

Ce que le prof peut vous faire coder ici : tris (bulle, sélection, insertion), méthodes d'échange (Interchanger), validation des paramètres Main, application console avec paramètres typés.


MODULE 2 — Solution, projets et Git

Structure d'une solution Visual Studio

Une solution peut contenir plusieurs projets : - Projet bibliothèque de classes (.dll) : contient les classes métier - Projet application console : point d'entrée - Projet de tests MSTest : tests automatisés

Pour importer un projet existant : File → Open → Project/Solution (puis pointer vers le .sln ou .csproj).

Git — Commandes essentielles

git init                          # Initialiser un dépôt
git add .                         # Ajouter tous les fichiers
git commit -m "Description"       # Créer un commit
git status                        # État du dépôt
git log                           # Historique des commits
git push                          # Envoyer sur GitHub
git pull                          # Récupérer depuis GitHub
git clone [url]                   # Cloner un dépôt

Important : Toujours utiliser un fichier .gitignore pour C# (ignorer les dossiers bin/, obj/, .vs/, etc.)


MODULE 3 — Déboguage et trace

Debug.WriteLine

Debug.WriteLine permet d'afficher des messages dans la fenêtre Output de Visual Studio sans modifier l'interface utilisateur.

using System.Diagnostics;

Debug.WriteLine("Valeur de x : " + x);
Debug.WriteLine($"Objet créé : {monObjet.Nom}");
  • Les messages n'apparaissent qu'en mode Debug (pas en Release)
  • Utile pour tracer l'exécution pas à pas sans Console.WriteLine

MODULE 4 — Création de classes (POO)

Convention de nommage C

Élément Convention Exemple
Champ privé _camelCase avec underscore _nom, _dateNaissance
Propriété publique PascalCase Nom, DateNaissance
Méthode PascalCase CalculerAge()
Paramètre camelCase nom, dateNaissance
Classe PascalCase Etudiant, CompteBancaire
Interface IPascalCase IEquatable, IComparable

Structure d'une classe complète

/// <summary>
/// Représente un étudiant au cégep.
/// </summary>
public class Etudiant
{
    // 1. Champs privés (backing fields)
    private string _nom;
    private int _numeroDa;
    private string? _courriel; // nullable : peut être null

    // 2. Propriétés avec validation dans le set
    /// <summary>Obtient ou définit le nom de l'étudiant.</summary>
    public string Nom
    {
        get => _nom;
        set
        {
            if (string.IsNullOrWhiteSpace(value))
                throw new ArgumentException("Le nom ne peut pas être vide.");
            _nom = value;
        }
    }

    public int NumeroDa
    {
        get => _numeroDa;
        set
        {
            // Validation par regex (ex: 7 chiffres)
            if (!System.Text.RegularExpressions.Regex.IsMatch(value.ToString(), @"^\d{7}$"))
                throw new ArgumentException("Le numéro de DA doit contenir 7 chiffres.");
            _numeroDa = value;
        }
    }

    // 3. Constructeur — utiliser les Propriétés (pas les champs _)
    //    pour forcer l'exécution des validations
    /// <summary>
    /// Initialise un nouvel étudiant.
    /// </summary>
    /// <param name="nom">Nom de l'étudiant.</param>
    /// <param name="numeroDa">Numéro de DA (7 chiffres).</param>
    /// <exception cref="ArgumentException">Si le nom ou le DA est invalide.</exception>
    public Etudiant(string nom, int numeroDa)
    {
        Nom = nom;         // passe par le set → validation exécutée
        NumeroDa = numeroDa;
    }
}

Points clés à retenir

  • Toujours passer par la propriété dans le constructeur, pas par le champ _ directement
  • Documentation XML (<summary>) obligatoire sur les membres publics — alimente l'IntelliSense
  • Balise <exception> à ajouter manuellement sur les constructeurs et méthodes qui peuvent lancer
  • Pour renommer une classe proprement : Ctrl+R, Ctrl+R (renommage global dans tout le projet)
  • Types nullables : ajouter ? après le type (ex: string?) pour accepter null

Ce que le prof peut vous faire coder ici : classe Etudiant, Produit, CompteBancaire, GardeDonjon, Animal, etc. avec champs privés, propriétés validées, constructeurs documentés.


MODULE 5 — Les exceptions

Principe fondamental

Une exception signale une situation anormale qui interrompt le flux normal d'exécution. Elle remonte dans la pile d'appels jusqu'à un catch, ou fait planter le programme.

// Lever une exception
throw new ArgumentException("Message clair pour le développeur.");

// Intercepter une exception
try
{
    TraitementRisqué();
}
catch (ArgumentException ex)
{
    Console.WriteLine(ex.Message);
}
catch (Exception ex)
{
    // Attraper tout le reste (frontière applicative)
    Console.WriteLine("Erreur inattendue : " + ex.Message);
}
finally
{
    // Toujours exécuté (libération de ressources)
}

Exceptions standards du framework .NET

Exception Quand l'utiliser
ArgumentException Argument invalide (général)
ArgumentNullException Argument null non autorisé
ArgumentOutOfRangeException Valeur hors plage acceptable
InvalidOperationException L'état de l'objet ne permet pas cette opération
NotSupportedException Fonctionnalité non supportée
FormatException Format de données incorrect

Créer ses propres exceptions (exceptions métier)

public class SoldeInsuffisantException : Exception
{
    public SoldeInsuffisantException(string message) : base(message) { }
}

// Usage :
if (solde < montant)
    throw new SoldeInsuffisantException("Solde insuffisant pour ce retrait.");

Relancer une exception

catch (Exception ex)
{
    // Log...
    throw;      // ✅ Conserve la pile d'appels originale
    // throw ex; ❌ Réinitialise la pile d'appels (à éviter)
}

Règles d'or

  • Ne pas mettre try/catch partout — seulement aux frontières applicatives (UI, API)
  • Bas niveau → on laisse remonter l'exception
  • Frontière (UI/Vue) → on intercepte, on affiche, on log
  • Jamais masquer une exception sans raison valable
  • NullReferenceException, IndexOutOfRangeException, DivideByZeroException → souvent des bugs de conception, ne pas les cacher

Ce que le prof peut vous faire coder ici : classes avec validations qui lancent des exceptions, exceptions personnalisées (ex: DaInvalideException), try/catch dans un Main ou une interface.


MODULE 6 — Tests unitaires (MSTest)

Pourquoi tester ?

  • Valider le comportement d'une méthode isolément
  • Plus rapide et plus fiable que de tester manuellement via l'interface
  • Permet de détecter les régressions lors de modifications

Le patron AAA (obligatoire)

[TestMethod]
public void NomMethode_Contexte_ResultatAttendu()
{
    // ARRANGER — préparer les données
    var moteur = new MoteurPromotion();
    double prixInitial = 50;

    // AGIR — exécuter la méthode à tester
    double resultat = moteur.CalculerPrixFinal(prixInitial, 1, false);

    // AFFIRMER — vérifier le résultat
    Assert.AreEqual(prixInitial, resultat);
}

Les Assert disponibles

Méthode Utilité
Assert.AreEqual(attendu, actuel) Vérifie l'égalité
Assert.IsTrue(condition) Vérifie qu'une condition est vraie
Assert.IsNull(objet) Vérifie que l'objet est null
Assert.ThrowsException<T>(() => ...) Vérifie qu'une exception de type T est lancée
CollectionAssert.AreEqual(c1, c2) Mêmes éléments dans le même ordre
CollectionAssert.AreEquivalent(c1, c2) Mêmes éléments, ordre indifférent
CollectionAssert.Contains(col, item) Vérifie la présence d'un élément
StringAssert.Contains(str, sub) Vérifie qu'une chaîne contient une sous-chaîne

Tests avec plusieurs jeux de données — [DataRow]

[TestMethod]
[DataRow(1200, 1, false, 1080.0, "Rabais : Prix > 1000")]
[DataRow(100, 15, false, 90.0,  "Rabais : Quantité >= 10")]
[DataRow(100, 1,  true,  90.0,  "Rabais : Membre VIP")]
[DataRow(500, 2,  false, 500.0, "Aucun rabais")]
public void CalculerPrix_Combinaisons_RetournePrixAttendu(
    double prix, int qte, bool membre, double attendu, string scenario)
{
    var moteur = new MoteurPromotion();
    double actuel = moteur.CalculerPrixFinal(prix, qte, membre);
    Assert.AreEqual(attendu, actuel, $"Échec : {scenario}");
}

Tester les exceptions

[TestMethod]
public void CalculerPrix_PrixNegatif_LanceArgumentException()
{
    var moteur = new MoteurPromotion();

    var ex = Assert.ThrowsException<ArgumentException>(() =>
        moteur.CalculerPrixFinal(-10, 1, false)
    );

    StringAssert.Contains(ex.Message, "ne peut être négatif");
}

Ce que le prof peut vous faire coder ici : écrire des tests pour des classes avec validation (cas valide, cas invalide, cas limites), utiliser [DataRow] pour plusieurs scénarios, tester des listes avec CollectionAssert.


MODULE 7 — L'héritage

Concepts de base

L'héritage crée un lien "est un" entre une classe dérivée et une classe de base.

// Classe de base
public class Vehicule
{
    private string _marque;
    public string Marque { get => _marque; private set { /* validation */ _marque = value; } }

    public Vehicule(string marque) { Marque = marque; }

    // "virtual" = permission de redéfinir dans les sous-classes
    public virtual void Klaxonner() => Console.WriteLine("Bip bip !");
}

// Classe dérivée — ": Vehicule" crée le lien "est un"
public class Camion : Vehicule
{
    // "base(marque)" appelle le constructeur du parent
    public Camion(string marque) : base(marque) { }

    // "override" = on redéfinit le comportement
    public override void Klaxonner() => Console.WriteLine("POUÊÊÊT !");
}

Résumé des mots-clés

Mot-clé Rôle
: (après le nom de classe) Établit le lien "est un"
virtual Donne la permission de redéfinir
override Redéfinit le comportement hérité
base(...) Appelle le constructeur/membre du parent
protected Accessible dans la classe ET ses dérivées, mais pas à l'extérieur
sealed (sur classe) Interdit de créer des sous-classes
sealed (sur méthode) Interdit de redéfinir plus bas dans la hiérarchie

Le polymorphisme

// L'étiquette est "Vehicule" mais la boîte peut être n'importe quelle sous-classe
Vehicule v = new Camion("Ford");
v.Klaxonner(); // Appelle la méthode de CAMION, pas de Vehicule → polymorphisme

// Impossible : l'étiquette "Vehicule" ne connaît pas NbEssieux (prop de Camion)
// Console.WriteLine(v.NbEssieux); ❌

// Pour accéder : caster explicitement
Camion c = (Camion)v;
Console.WriteLine(c.NbEssieux); // ✅

Collection polymorphe

List<Vehicule> garage = new List<Vehicule>();
garage.Add(new Voiture("Toyota", 4));
garage.Add(new Camion("Ford"));
garage.Add(new Moto("Honda"));

foreach (Vehicule v in garage)
{
    v.Klaxonner(); // Chaque objet réagit selon sa propre implémentation
}

protected — accès hiérarchique

public class Vehicule
{
    // get public (tout le monde peut lire)
    // set protected (seules les classes dérivées peuvent modifier)
    public bool EstEnMarche { get; protected set; }
}

public class Voiture : Vehicule
{
    public void TomberEnPanne() { EstEnMarche = false; } // ✅ autorisé
}

// En dehors : maVoiture.EstEnMarche = false; ❌ erreur

Ce que le prof peut vous faire coder ici : hiérarchie Animal → Chien/Chat/Oiseau, Employe → Salarie/Contractuel, Vehicule → Voiture/Camion/Moto, liste polymorphe, redéfinition de méthodes.


MODULE 8 — Les interfaces

Principe

Une interface est un contrat : elle garantit qu'une classe implémente certaines méthodes/propriétés, sans imposer de lien hiérarchique.

public interface IImprimable
{
    void Imprimer();
}

// Document ET Photo n'ont aucun lien, mais partagent le contrat
public class Document : IImprimable { public void Imprimer() { ... } }
public class Photo   : IImprimable { public void Imprimer() { ... } }

IEquatable et IComparable — Les 4 cas obligatoires

Pour qu'une classe fonctionne correctement avec les tris et recherches .NET, il faut implémenter les 4 cas suivants :

public class Etudiant : IEquatable<Etudiant>, IComparable<Etudiant>, IComparable
{
    private string _nom;
    private double _moyenne;

    // CAS 1 — IEquatable<T> : égalité basée sur les données
    public bool Equals(Etudiant other)
    {
        if (other is null) return false;
        return _nom == other._nom && _moyenne == other._moyenne;
    }

    // CAS 2 — Override de Equals(object) : relance le CAS 1
    public override bool Equals(object obj)
    {
        Etudiant temp = obj as Etudiant;
        if (temp is null) return false;
        return Equals(temp);
    }

    // CAS 3 — IComparable<T> : ordre de tri
    public int CompareTo(Etudiant other)
    {
        if (other is null) return 1; // on est "plus grand" que null
        return _moyenne.CompareTo(other._moyenne);
    }

    // CAS 4 — IComparable (objet) : compatibilité, relance le CAS 3
    public int CompareTo(object obj)
    {
        Etudiant temp = obj as Etudiant;
        if (temp is null) return 1;
        return CompareTo(temp);
    }

    // Obligatoire si Equals est redéfini
    public override int GetHashCode() => HashCode.Combine(_nom, _moyenne);
}

Comparaison de chaînes → utiliser StringComparison.Ordinal

return string.Compare(_nom, other._nom, StringComparison.Ordinal);
// Rapide, prédictible, identique partout (comparaison binaire)

abstract, virtual, sealed, interface — Quand utiliser quoi ?

Besoin Solution
Classes liées par un concept commun + partage de code Classe abstract
Comportement par défaut modifiable Méthode virtual
Forcer une implémentation obligatoire dans les sous-classes Méthode abstract
Verrouiller une classe/méthode (plus de dérivation) sealed
Contrat entre classes sans lien hiérarchique interface
Héritage multiple interface (une classe ne peut hériter que d'une seule classe, mais implémenter plusieurs interfaces)
// Classe abstraite — ne peut pas être instanciée directement
public abstract class Animal
{
    public abstract void FaireDuBruit(); // OBLIGATION pour les sous-classes
    public virtual void Dormir() => Console.WriteLine("Zzz..."); // comportement par défaut modifiable
}

public class Chien : Animal
{
    public override void FaireDuBruit() => Console.WriteLine("Wouf !");
}

Ce que le prof peut vous faire coder ici : implémenter IEquatable/IComparable sur une classe, classe abstraite avec méthodes abstraites, comparaison et tri de listes d'objets.


MODULE 9 — Windows Forms (Interface graphique)

Création d'un projet WinForms

  1. Nouveau projet → Windows Forms App → .NET 10 → Langage C#
  2. Renommer Form1.csFormPrincipal.cs (clic droit → Renommer → accepter le renommage des références)

Concepts clés

Partial Class — Un formulaire est divisé en deux fichiers : - FormPrincipal.cs → votre code (événements, logique) - FormPrincipal.Designer.cs → code généré automatiquement par le concepteur visuel (ne jamais modifier manuellement)

InitializeComponent() — Méthode générée dans .Designer.cs, appelée dans le constructeur. Initialise tous les contrôles visuels.

Convention de nommage des contrôles

Ne jamais laisser les noms par défaut (button1, textBox1). Renommer avec le type complet + description :

Contrôle Exemple de nom
Button buttonValider, buttonEffacer
TextBox textBoxNom, textBoxPrix
Label labelNom, labelErreur
ComboBox comboBoxClients
ListBox listBoxProduits
DataGridView dataGridViewFactures

Propriétés importantes des contrôles

  • Name : identifiant utilisé dans le code (convention ci-dessus)
  • Text : texte affiché à l'écran (différent du Name)
  • Location : position (X, Y) par rapport au formulaire (Point)
  • Size : dimensions Width × Height (Size)
  • TabIndex : ordre de navigation à la touche Tab (commence à 0)

Événements

// Double-cliquer sur un bouton dans le designer génère automatiquement :
private void buttonValider_Click(object sender, EventArgs e)
{
    try
    {
        // Récupérer les valeurs de l'interface
        string nom = textBoxNom.Text;

        // Appeler le contrôleur (pas de logique métier dans la Vue !)
        _controller.AjouterEtudiant(nom);

        // Mettre à jour l'affichage
        listBoxEtudiants.DataSource = null;
        listBoxEtudiants.DataSource = _controller.ListeReadOnly;
    }
    catch (Exception ex)
    {
        MessageBox.Show(ex.Message, "Erreur", MessageBoxButtons.OK, MessageBoxIcon.Error);
    }
}

Ce que le prof peut vous faire coder ici : formulaire avec boutons, zones de texte, listes déroulantes, gestion des événements Click, affichage dynamique d'une liste, MessageBox pour les erreurs.


MODULE 10 — Architecture MVC

L'analogie du restaurant

Rôle MVC Analogie Dans le code
Modèle Recette + ingrédients Client.cs, Facture.cs — données brutes, validation
Contrôleur Chef de cuisine FactureController.cs — logique, manipulation des données
Vue Serveur Form.cs, Program.cs — interface, présentation, try/catch

Structure physique (plusieurs projets dans la solution)

MaApplication.Lib        ← Modèles + Contrôleurs (réutilisable)
MaApplication.Console    ← Vue console (référence Lib)
MaApplication.WinForms   ← Vue graphique (référence Lib)
MaApplication.Tests      ← Tests unitaires (référence Lib)

Avantage : si on veut changer l'interface (Console → WinForms → Mobile), on garde Lib intact et on change seulement l'habillage.

Protection des données — IReadOnlyList

public class FactureController
{
    private List<Facture> _historique = new List<Facture>(); // coffre-fort privé

    // La Vue reçoit une "photo" — elle peut lire mais pas modifier directement
    public IReadOnlyList<Facture> HistoriqueReadOnly => _historique.AsReadOnly();
}

Pourquoi ? Si on donnait la List directe à la Vue, elle pourrait la vider ou la modifier sans passer par le contrôleur. IReadOnlyList = lecture seule.

Workflow typique — Cycle d'une donnée

VUE : L'utilisateur clique "Ajouter"
  ↓
CONTRÔLEUR : reçoit l'action, vérifie que l'état est valide
  ↓ (si état invalide → throw InvalidOperationException)
MODÈLE : applique les règles métier, modifie les données
  ↓
CONTRÔLEUR : retourne le résultat à la Vue
  ↓
VUE : met à jour l'affichage

Le "Snapshot" dans les lignes de facture

// Quand on ajoute un item, on COPIE les valeurs au moment de l'achat
// (si le prix change demain, les anciennes factures restent correctes)
public class LigneFacture
{
    public LigneFacture(Item item)
    {
        Description = item.Description; // copie
        PrixUnitaire = item.Prix;       // copie du prix ACTUEL
    }
}

Méthodologie pour ajouter une fonctionnalité

  1. Modèle — les données sont-elles prêtes ?
  2. Contrôleur — ai-je une méthode pour cette action ?
  3. Test unitaire — ça marche sans ouvrir de fenêtre ?
  4. Vue — maintenant j'ajoute le bouton et le try/catch

Gestion des erreurs dans la Vue

// La Vue intercepte TOUTES les exceptions et les affiche proprement
try
{
    _factureCtrl.AjouterItem(itemSelectionne);
    // Mettre à jour l'affichage...
}
catch (InvalidOperationException ex)
{
    MessageBox.Show(ex.Message); // Aucune facture ouverte, etc.
}
catch (Exception ex)
{
    MessageBox.Show("Erreur inattendue : " + ex.Message);
}

Ce que le prof peut vous faire coder ici : projet MVC complet avec Lib + Vue (Console ou WinForms) + Tests, système de facturation, gestion d'étudiants ou de produits, séparation stricte des couches.


Résumé rapide — Mots-clés essentiels

Mot-clé / Concept Module En une phrase
private + _ 4 Champ interne caché
get / set avec validation 4 Propriété avec garde-fou
throw 5 Signaler une erreur
try / catch 5 Attraper une erreur
[TestMethod] 6 Déclarer un test
Assert.AreEqual 6 Vérifier un résultat
: (héritage) 7 Lien "est un"
virtual / override 7 Permettre / redéfinir un comportement
base() 7 Appeler le parent
protected 7 Visible dans la hiérarchie seulement
sealed 7 Verrouiller
abstract 8 Modèle incomplet, obligation d'override
interface 8 Contrat sans héritage
IReadOnlyList 10 Donner accès en lecture seule
MVC 10 Séparer données / logique / interface

Ressources

  • Notes de cours : https://tousignantsimon-p2.github.io
  • Visualisation des tris : https://visualgo.net/en/sorting
  • Exceptions .NET : https://learn.microsoft.com/fr-fr/dotnet/standard/exceptions/
  • Meilleures pratiques exceptions : https://learn.microsoft.com/fr-fr/dotnet/standard/exceptions/best-practices-for-exceptions
  • Cheminement complet du programme : https://informatique.apical.xyz/
Partager :
Retourner vers page d'accueil

Prêt à multiplier vos clics par 3 à 5 ?

Rejoignez 50 000+ créateurs qui utilisent Bio.ax pour booster leur présence digitale.

Créer mon Bio.ax gratuit