Jusqu'ici, créer un personnage prend sept lignes : new Personnage() puis six affectations. Et rien ne garantit qu'on n'en oublie pas une. Le constructeur est une méthode spéciale qui s'exécute au moment du new et qui prend en charge ces initialisations. Résultat : un objet naît toujours dans un état valide, en une seule ligne.
Le new d'une classe est plus puissant que ce qu'on en a fait jusqu'à présent. Avec un constructeur, on peut donner à C# une recette précise pour fabriquer un objet — et garantir qu'aucun objet ne traîne avec des données manquantes ou absurdes.
À la fin de ce chapitre, tu seras capable de :
this(...) pour éviter la duplication.Reprends ton code d'instanciation du Chapitre 5 :
Personnage aragorn = new Personnage();
aragorn.Nom = "Aragorn";
aragorn.Classe = "Guerrier";
aragorn.PvMax = 100;
aragorn.Pv = 100;
aragorn.Attaque = 15;
aragorn.Defense = 10;
aragorn.Niveau = 5;
Sept lignes. Et il y a deux problèmes :
aragorn.PvMax = 100; casse silencieusement la validation des PV (puisque Pv se borne par PvMax qui vaut 0).L'idée du constructeur, c'est de regrouper toute cette initialisation à un seul endroit : la classe elle-même.
Un constructeur est une méthode spéciale qui :
Personnage pour la classe Personnage).void).new.Voici un constructeur basique pour Personnage :
class Personnage
{
// ... attributs et propriétés ...
public Personnage(string nom, string classe, int pvMax)
{
Nom = nom;
Classe = classe;
PvMax = pvMax;
Pv = pvMax; // on commence avec tous les PV
Niveau = 1;
}
}
Et l'utilisation devient :
Personnage aragorn = new Personnage("Aragorn", "Guerrier", 100);
Une seule ligne. L'objet est immédiatement utilisable, et il est impossible d'oublier une initialisation : le compilateur exige les trois paramètres.
🧠 Comment lire
new Personnage(...):
- C# alloue un nouvel objet
Personnageen mémoire.- Tous les attributs prennent leur valeur par défaut (
0,null,false…).- C# appelle le constructeur avec les arguments fournis.
- L'objet est renvoyé à l'appelant, prêt à l'emploi.
Si tu n'écris aucun constructeur dans ta classe, C# en fabrique un implicitement, sans paramètre et sans corps. C'est ce qui te permettait, jusqu'au Chapitre 5, d'écrire new Personnage() sans avoir rien défini.
// Classe sans constructeur écrit
class Voiture
{
public string Marque { get; set; }
}
// On peut quand même appeler :
Voiture v = new Voiture(); // ← constructeur par défaut implicite
Mais attention : dès que tu écris au moins un constructeur, C# n'ajoute plus le constructeur par défaut. Si tu veux conserver la possibilité d'instancier sans arguments, tu dois l'écrire explicitement.
class Personnage
{
public Personnage(string nom, string classe, int pvMax) { ... }
}
// Désormais :
Personnage hero = new Personnage(); // ❌ erreur de compilation
Personnage hero = new Personnage("Aragorn", "Guerrier", 100); // ✅
thisTu vois souvent les constructeurs écrits comme ceci, avec un paramètre qui porte le même nom que l'attribut privé :
class Voiture
{
private string marque;
public Voiture(string marque)
{
this.marque = marque;
// ^---- ^----
// attribut paramètre
}
}
Sans this., la ligne marque = marque; ne ferait que se réaffecter le paramètre à lui-même. Avec this., on précise qu'on veut écrire dans l'attribut de l'objet courant.
C'est exactement l'usage de this vu au Chapitre 5. Les constructeurs en sont le terrain de jeu le plus fréquent.
Une classe peut avoir plusieurs constructeurs, à condition qu'ils diffèrent par leurs paramètres (nombre ou type). Cette technique s'appelle la surcharge.
class Personnage
{
// Constructeur complet
public Personnage(string nom, string classe, int pvMax, int attaque, int defense)
{
Nom = nom;
Classe = classe;
PvMax = pvMax;
Pv = pvMax;
Attaque = attaque;
Defense = defense;
Niveau = 1;
}
// Constructeur simplifié (valeurs « moyennes » par défaut)
public Personnage(string nom, string classe)
{
Nom = nom;
Classe = classe;
PvMax = 50;
Pv = 50;
Attaque = 10;
Defense = 10;
Niveau = 1;
}
// Constructeur vide (PNJ générique)
public Personnage()
{
Nom = "Inconnu";
Classe = "Aventurier";
PvMax = 30;
Pv = 30;
Attaque = 5;
Defense = 5;
Niveau = 1;
}
}
C# choisit automatiquement le bon constructeur selon les arguments fournis :
Personnage aragorn = new Personnage("Aragorn", "Guerrier", 100, 15, 10); // → constructeur 1
Personnage paysan = new Personnage("Otto", "Paysan"); // → constructeur 2
Personnage inconnu = new Personnage(); // → constructeur 3
⚠️ Le piège de la duplication : regarde les trois constructeurs ci-dessus. Le travail d'affectation se répète. Si tu ajoutes un attribut, tu dois modifier les trois. La section suivante montre comment éviter ça.
: this(...)Pour éviter la duplication, un constructeur peut appeler un autre constructeur de la même classe avec la syntaxe : this(...).
class Personnage
{
// Constructeur principal — le seul qui contient la vraie logique
public Personnage(string nom, string classe, int pvMax, int attaque, int defense)
{
Nom = nom;
Classe = classe;
PvMax = pvMax;
Pv = pvMax;
Attaque = attaque;
Defense = defense;
Niveau = 1;
}
// Constructeur intermédiaire → délègue au principal
public Personnage(string nom, string classe)
: this(nom, classe, 50, 10, 10)
{ }
// Constructeur vide → délègue à l'intermédiaire
public Personnage()
: this("Inconnu", "Aventurier")
{ }
}
La syntaxe : this(...) se lit « avant d'exécuter mon corps, appelle ce constructeur-là avec ces arguments ». La logique d'affectation se trouve à un seul endroit (le constructeur principal). Les autres ne sont que des « raccourcis ».
C# autorise une autre approche, encore plus concise : donner des valeurs par défaut aux paramètres du constructeur.
public Personnage(string nom = "Inconnu",
string classe = "Aventurier",
int pvMax = 50,
int attaque = 10,
int defense = 10)
{
Nom = nom;
Classe = classe;
PvMax = pvMax;
Pv = pvMax;
Attaque = attaque;
Defense = defense;
Niveau = 1;
}
Avec un seul constructeur, on couvre tous les cas :
Personnage aragorn = new Personnage("Aragorn", "Guerrier", 100, 15, 10);
Personnage paysan = new Personnage("Otto", "Paysan");
Personnage inconnu = new Personnage();
Tu peux préciser seulement les paramètres qui t'intéressent, en les nommant :
Personnage tank = new Personnage(nom: "Boromir", pvMax: 150, defense: 20);
// classe, attaque restent à leurs valeurs par défaut
💡 Surcharge ou valeurs par défaut ? Les deux approches sont valides. La surcharge (avec
: this(...)) est plus explicite et plus utile quand les constructeurs ont des logiques très différentes. Les valeurs par défaut sont plus concises quand on a juste des paramètres facultatifs. Dans la pratique, les valeurs par défaut suffisent dans 90 % des cas.
Tu te souviens de la classe Inventaire du Chapitre 4 ? On y avait écrit :
private List<string> objets = new List<string>();
Cette initialisation à la déclaration est un raccourci. L'équivalent classique passe par le constructeur :
class Inventaire
{
private List<string> objets;
public Inventaire()
{
objets = new List<string>();
}
}
Les deux formes font exactement la même chose. La forme avec constructeur est plus visible et plus flexible : tu peux y mettre tout ce que tu veux au moment de la création, pas seulement initialiser des attributs (lecture d'un fichier, log de création, ouverture d'une connexion…).
Voici la classe Personnage finale, avec encapsulation, méthodes et constructeurs :
class Personnage
{
private int pv;
public string Nom { get; set; }
public string Classe { get; set; }
public int PvMax { get; set; }
public int Attaque { get; set; }
public int Defense { get; set; }
public int Niveau { get; set; }
public int Pv
{
get => pv;
set
{
if (value < 0) value = 0;
if (value > PvMax) value = PvMax;
pv = value;
}
}
public bool EstVivant => pv > 0;
// --- Constructeur principal ---
public Personnage(string nom, string classe, int pvMax,
int attaque = 10, int defense = 10, int niveau = 1)
{
Nom = nom;
Classe = classe;
PvMax = pvMax;
Pv = pvMax;
Attaque = attaque;
Defense = defense;
Niveau = niveau;
}
public void Attaquer(Personnage cible)
{
int degats = Attaque - cible.Defense;
if (degats < 0) degats = 0;
cible.Pv -= degats;
Console.WriteLine($"{Nom} attaque {cible.Nom} et inflige {degats} dégâts.");
}
public override string ToString()
{
string etat = EstVivant ? "vivant" : "mort";
return $"{Nom} ({Classe}) - Niv.{Niveau} - {Pv}/{PvMax} PV - {etat}";
}
}
Personnage aragorn = new Personnage("Aragorn", "Guerrier", 100, attaque: 15);
Personnage legolas = new Personnage("Legolas", "Archer", 80, attaque: 18, defense: 7);
Personnage paysan = new Personnage("Otto", "Paysan", 30);
Console.WriteLine(aragorn);
Console.WriteLine(legolas);
Console.WriteLine(paysan);
aragorn.Attaquer(legolas);
legolas.Attaquer(aragorn);
Console.WriteLine(aragorn);
Console.WriteLine(legolas);
Trois lignes pour créer trois personnages — au lieu de vingt-et-une. Et chaque personnage est garanti dans un état valide dès sa naissance.
Reprends ta classe Voiture. Ajoute un constructeur :
public Voiture(string marque, string couleur, int nombreDePortes = 5)
Refactore tout Program.cs pour utiliser ce constructeur. Vérifie que la version new Voiture() (sans paramètres) ne fonctionne plus — c'est voulu.
Bonus : ajoute un deuxième constructeur public Voiture(string marque) : this(marque, "Blanc") pour les voitures dont on connaît juste la marque.
Reprends ta classe CompteBancaire. Ajoute :
CompteBancaire c1 = new CompteBancaire("Marie Dupont", 1000);
CompteBancaire c2 = new CompteBancaire("Paul Martin"); // solde = 0
CompteBancaire c3 = new CompteBancaire("X", -500); // solde = 0 + avertissement
Pour la classe LampeConnectee, écris trois constructeurs chaînés avec : this(...) :
LampeConnectee(string couleur, int intensite, bool allumee) — le complet.LampeConnectee(string couleur) — délègue au précédent avec intensité 80 et allumée.LampeConnectee() — délègue au précédent avec couleur "Blanc".Test :
LampeConnectee l1 = new LampeConnectee("Rouge", 50, true);
LampeConnectee l2 = new LampeConnectee("Bleu");
LampeConnectee l3 = new LampeConnectee();
Ajoute à la classe Personnage une méthode statique GenererPnj() qui crée un personnage aléatoire :
Utilise la classe Random de C# (Random rng = new Random();).
Personnage pnj = Personnage.GenererPnj();
Console.WriteLine(pnj);
new Classe(args) exécute le constructeur correspondant et garantit l'état initial.this lève l'ambiguïté quand un paramètre porte le même nom qu'un attribut.: this(...) pour éviter la duplication.new — fini les sept lignes d'affectation.À ce stade, tu maîtrises la classe en C# : attributs, visibilité, propriétés, méthodes, constructeurs. Tu peux modéliser n'importe quelle entité simple. Les prochains chapitres ouvriront la dimension relationnelle : comment des objets en contiennent d'autres (composition), comment une classe peut hériter d'une autre (héritage), et comment plusieurs classes différentes peuvent partager un même comportement (polymorphisme).