Initialisation des systèmes...

Baptiste.Dev
Retour aux notes
LangagesDébutantSérie : C# / .NET

C# : Les objets

Classes, constructeurs, propriétés, modificateurs d'accès et héritage - comment structurer le code en C#.

8 min de lecture
Mis à jour il y a 4 mois
c#csharpclassesobjetshéritagePOO

C# : Les objets

Jusqu'ici, on a manipulé des variables isolées et des méthodes en vrac. Mais dès qu'un programme grossit, il faut organiser le code. C'est là qu'intervient la programmation orientée objet (POO).

L'idée est simple : regrouper les données (ce qu'on décrit) et les comportements (ce qu'on fait avec) dans une même entité - un objet.


# Classes

Une classe, c'est un plan de fabrication. Elle décrit à quoi ressemble un objet et ce qu'il sait faire. L'objet, c'est le résultat concret construit à partir de ce plan.

class Voiture
{
    public string Marque;
    public string Modele;
    public int Annee;
}

Ici, Voiture est la classe (le plan). Pour créer un objet concret, on utilise le mot-clé new :

Voiture maVoiture = new Voiture();
maVoiture.Marque = "Peugeot";
maVoiture.Modele = "208";
maVoiture.Annee = 2022;

maVoiture est une instance de la classe Voiture. On peut en créer autant qu'on veut - chaque instance a ses propres valeurs.

Voiture autre = new Voiture();
autre.Marque = "Renault";
// maVoiture.Marque est toujours "Peugeot"

# Constructeurs

Créer un objet puis remplir ses champs un par un, c'est fastidieux et risqué - on peut oublier d'initialiser un champ. Le constructeur résout ça : c'est une méthode spéciale appelée automatiquement au moment du new.

Constructeur avec paramètres

class Voiture
{
    public string Marque;
    public string Modele;
    public int Annee;
 
    public Voiture(string marque, string modele, int annee)
    {
        Marque = marque;
        Modele = modele;
        Annee = annee;
    }
}
 
Voiture maVoiture = new Voiture("Peugeot", "208", 2022);

Plus besoin d'assigner chaque champ manuellement. Et si on oublie un argument, le compilateur refuse de compiler.

Le mot-clé this

Quand le nom du paramètre est identique au nom du champ, on utilise this pour lever l'ambiguïté. this fait référence à l'instance en cours de construction.

public Voiture(string marque, string modele, int annee)
{
    this.Marque = marque;   // this.Marque = le champ, marque = le paramètre
    this.Modele = modele;
    this.Annee = annee;
}

readonly - figer une valeur après la construction

Un champ readonly ne peut être assigné que dans le constructeur. Une fois l'objet construit, la valeur est figée pour toujours.

class Utilisateur
{
    public readonly DateTime DateInscription;
 
    public Utilisateur()
    {
        DateInscription = DateTime.Now;  // fixée à la création
    }
}
 
Utilisateur u = new Utilisateur();
// u.DateInscription = DateTime.Now;  // Erreur de compilation !

C'est la différence avec const : une const est fixée à la compilation (elle doit être connue à l'avance), un readonly est fixé à l'exécution (dans le constructeur).


# Propriétés

Dans l'exemple précédent, les champs sont public - n'importe qui peut les lire et les modifier directement. C'est un problème : rien n'empêche d'écrire maVoiture.Annee = -5.

Les propriétés remplacent les champs publics. Elles ressemblent à des champs mais passent par des accesseurs get et set, ce qui permet de contrôler l'accès.

Auto-properties

La forme la plus courante. Le compilateur génère un champ privé en coulisse.

class Voiture
{
    public string Marque { get; set; }
    public string Modele { get; set; }
    public int Annee { get; set; }
}

L'usage est identique à un champ : maVoiture.Marque = "Peugeot";. Mais on peut maintenant restreindre l'accès.

Propriétés en lecture seule

// get seul  -  la valeur est fixée dans le constructeur, jamais modifiable après
public string Vin { get; }
 
// init  -  modifiable uniquement à l'initialisation (C# 9+)
public string Couleur { get; init; }
Voiture v = new Voiture { Couleur = "Rouge" };  // OK
// v.Couleur = "Bleu";  // Erreur ! init = une seule fois

Propriété avec logique

Quand on a besoin de validation ou de calcul, on écrit le get/set manuellement :

class Voiture
{
    private int annee;
 
    public int Annee
    {
        get => annee;
        set
        {
            if (value < 1886)  // première voiture de l'histoire
                throw new ArgumentException("Année invalide");
            annee = value;
        }
    }
}

value est un mot-clé implicite dans le set - il contient la valeur qu'on essaie d'assigner.

Primary constructors (C# 12)

Maintenant que tu connais get, voici une syntaxe raccourcie introduite en C# 12 : les paramètres du constructeur se déclarent directement sur la classe. Plus concis, idéal pour les classes simples.

class Voiture(string marque, string modele, int annee)
{
    public string Marque { get; } = marque;
    public string Modele { get; } = modele;
    public int Annee { get; } = annee;
}

C'est équivalent à écrire une classe avec un constructeur classique et trois propriétés en lecture seule, mais en une fraction du code.


# Modificateurs d'accès

Les modificateurs d'accès contrôlent qui peut voir quoi. C'est le cœur de l'encapsulation : exposer le minimum nécessaire et cacher le reste.

ModificateurAccessible depuis
publicPartout
privateLa classe elle-même uniquement
protectedLa classe + ses classes enfants
internalLe même projet (assembly)
protected internalLe même projet OU les classes enfants

La règle est simple : tout est private par défaut, on ouvre au minimum nécessaire.

class CompteBancaire
{
    private decimal solde;           // personne ne touche au solde directement
    public string Titulaire { get; }  // tout le monde peut lire le nom
 
    public CompteBancaire(string titulaire, decimal soldeInitial)
    {
        Titulaire = titulaire;
        solde = soldeInitial;
    }
 
    public decimal GetSolde() => solde;  // lecture contrôlée
 
    public void Deposer(decimal montant)
    {
        if (montant <= 0)
            throw new ArgumentException("Le montant doit être positif");
        solde += montant;
    }
}

Le solde est private : on ne peut pas écrire compte.solde = 1000000; de l'extérieur. Il faut passer par Deposer() qui valide le montant. C'est ça, l'encapsulation.


# Héritage

L'héritage permet de créer une classe à partir d'une autre. La classe enfant récupère tout ce que la classe parent possède (champs, propriétés, méthodes) et peut ajouter ou modifier des comportements.

Syntaxe de base

En C#, on utilise : pour hériter :

class Vehicule
{
    public string Marque { get; set; }
    public int Annee { get; set; }
 
    public Vehicule(string marque, int annee)
    {
        Marque = marque;
        Annee = annee;
    }
 
    public virtual string Decrire()
    {
        return $"{Marque} ({Annee})";
    }
}
 
class Voiture : Vehicule
{
    public int NombrePortes { get; set; }
 
    public Voiture(string marque, int annee, int nombrePortes)
        : base(marque, annee)
    {
        NombrePortes = nombrePortes;
    }
 
    public override string Decrire()
    {
        return $"{Marque} ({Annee})  -  {NombrePortes} portes";
    }
}

Voiture hérite de Vehicule : elle a automatiquement Marque et Annee sans les redéclarer.

virtual et override

  • virtual sur la méthode parent = "cette méthode peut être redéfinie par un enfant"
  • override sur la méthode enfant = "je redéfinis le comportement hérité"

Sans virtual, la méthode ne peut pas être redéfinie. C'est une sécurité - on choisit explicitement ce qui est ouvert à la modification.

base - appeler le parent

Le mot-clé base permet d'appeler le constructeur ou une méthode du parent :

class VoitureElectrique : Voiture
{
    public int Autonomie { get; set; }
 
    public VoitureElectrique(string marque, int annee, int autonomie)
        : base(marque, annee, 4)  // appelle le constructeur de Voiture
    {
        Autonomie = autonomie;
    }
 
    public override string Decrire()
    {
        return $"{base.Decrire()}  -  {Autonomie} km d'autonomie";
    }
}

base.Decrire() appelle la version de Voiture, puis on ajoute l'autonomie. On réutilise le code du parent au lieu de le réécrire.

Limites

En C#, une classe ne peut hériter que d'une seule classe parent. Pas d'héritage multiple comme en C++. Si tu as besoin qu'une classe respecte plusieurs "contrats", c'est le rôle des interfaces - ce sera dans le prochain article.


# La suite

Tu sais maintenant structurer du code en objets. Les prochains articles de la série :