Les propriétés sont la version moderne et concise des getters/setters du Chapitre 3. Elles se comportent comme des attributs public pour le code appelant, mais elles cachent à l'intérieur tout le contrôle qu'on souhaite. C# en fait un usage si systématique qu'on ne voit presque jamais de GetX() / SetX() dans un vrai projet C#.
Tu as vu au Chapitre 3 que les getters/setters fonctionnent mais qu'ils sont lourds : 8 méthodes pour 4 attributs, et le code appelant écrit hero.GetPv() partout. C# propose une syntaxe dédiée — la propriété — qui fait exactement la même chose, en deux fois moins de lignes, avec un usage transparent.
À la fin de ce chapitre, tu seras capable de :
get et set.value dans un set.{ get; set; }).private set (lecture publique, écriture interne).get sans set).Reprends le getter/setter de Pv du chapitre précédent :
class Personnage
{
private int pv;
public int GetPv()
{
return pv;
}
public void SetPv(int valeur)
{
if (valeur < 0) valeur = 0;
if (valeur > pvMax) valeur = pvMax;
pv = valeur;
}
}
Voici la même logique, en C#, sous forme de propriété :
class Personnage
{
private int pv;
public int Pv
{
get
{
return pv;
}
set
{
if (value < 0) value = 0;
if (value > pvMax) value = pvMax;
pv = value;
}
}
}
Trois choses à remarquer :
public int Pv) suivi d'accolades qui contiennent deux blocs : get et set.set, le mot-clé value désigne la valeur qu'on essaie d'assigner. C'est l'équivalent du paramètre du setter classique.pv (minuscule) est conservé : c'est lui qui stocke réellement la donnée. La propriété Pv (majuscule) n'est qu'une interface qui l'expose proprement.🧠 Convention : attribut privé en camelCase (
pv), propriété publique en PascalCase (Pv). C'est la convention C# universelle, et c'est précisément pour distinguer ces deux compagnons qu'elle existe.
Program.csC'est là que la propriété fait toute la différence. Du point de vue de celui qui utilise la classe, une propriété s'utilise comme un attribut :
Personnage hero = new Personnage();
hero.Pv = 100; // appelle implicitement le set → stocke 100
hero.Pv = -9999; // appelle implicitement le set → stocke 0 (validation)
hero.Pv = 50000; // appelle implicitement le set → stocke pvMax (validation)
int actuels = hero.Pv; // appelle implicitement le get → renvoie la valeur
Console.WriteLine(hero.Pv);
Pas de Get, pas de Set, pas de parenthèses. La protection est invisible de l'extérieur. L'écriture est aussi simple qu'avec un attribut public — la sécurité est aussi solide qu'avec un setter classique.
Très souvent, on n'a aucune validation à faire : on veut juste un attribut accessible en lecture et en écriture. Pour ce cas, C# propose une forme raccourcie : la propriété auto-implémentée.
public string Nom { get; set; }
Une seule ligne. Plus d'attribut privé à déclarer — C# en crée un automatiquement, en coulisses. Cette ligne est exactement équivalente à :
private string _nom;
public string Nom
{
get { return _nom; }
set { _nom = value; }
}
⚠️ Mais attention : la forme raccourcie ne permet aucune validation. Dès que tu as besoin de refuser ou corriger une valeur, passe à la forme complète.
public string Classe { get; set; } // lecture + écriture publiques
public int Identifiant { get; private set; } // lecture publique, écriture interne uniquement
public bool EstActif { get; } // lecture seule (rare, voir propriétés calculées)
La version avec private set est très utile : depuis l'extérieur, on ne peut que lire (par exemple hero.Identifiant), mais à l'intérieur de la classe, on peut écrire (par exemple à la création de l'objet).
Une propriété calculée est une propriété qui n'a pas d'attribut privé dédié. Sa valeur est calculée à la volée à partir d'autres attributs. Elle n'a typiquement pas de set — on ne peut pas écrire dedans, seulement lire.
public bool EstVivant
{
get { return pv > 0; }
}
Aucun attribut privé estVivant ; à chaque appel, on évalue l'expression pv > 0.
hero.Pv = 0;
Console.WriteLine(hero.EstVivant); // False
hero.Pv = 50;
Console.WriteLine(hero.EstVivant); // True
🧠 Personne ne peut écrire
hero.EstVivant = true: sansset, la propriété est en lecture seule. La seule manière de changerEstVivant, c'est de modifierPv— ce qui est exactement ce qu'on veut. La cohérence est garantie par construction.
// Pourcentage de PV restants
public int PourcentagePv
{
get { return (int)((double)pv / pvMax * 100); }
}
// Barre de vie textuelle : "[████████░░]"
public string BarreDeVie
{
get
{
int nb = PourcentagePv / 10;
return "[" + new string('█', nb) + new string('░', 10 - nb) + "]";
}
}
=>Pour les propriétés calculées d'une seule ligne, C# accepte une forme avec flèche :
public bool EstVivant => pv > 0;
public int PourcentagePv => (int)((double)pv / pvMax * 100);
C'est strictement équivalent au bloc get { return ...; }, en beaucoup plus court.
Personnage au completVoici à quoi ressemble notre classe Personnage, encapsulée avec toutes les techniques vues ci-dessus :
class Personnage
{
// --- Attributs privés ---
private string nom;
private int pv;
private int pvMax;
private int niveau;
// --- Propriété avec validation : Nom ---
public string Nom
{
get { return nom; }
set
{
if (value == "" || value == null) value = "Inconnu";
nom = value;
}
}
// --- Propriété avec validation : Pv (bornée entre 0 et PvMax) ---
public int Pv
{
get { return pv; }
set
{
if (value < 0) value = 0;
if (value > pvMax) value = pvMax;
pv = value;
}
}
// --- Propriété avec validation : PvMax (minimum 1) ---
public int PvMax
{
get { return pvMax; }
set
{
if (value < 1) value = 1;
pvMax = value;
}
}
// --- Propriété auto-implémentée : Classe ---
public string Classe { get; set; }
// --- Propriété avec validation : Niveau (minimum 1) ---
public int Niveau
{
get { return niveau; }
set
{
if (value < 1) value = 1;
niveau = value;
}
}
// --- Propriété calculée : EstVivant ---
public bool EstVivant => pv > 0;
// --- Propriété calculée : barre de vie textuelle ---
public string BarreDeVie
{
get
{
int nb = pvMax == 0 ? 0 : pv * 10 / pvMax;
return "[" + new string('█', nb) + new string('░', 10 - nb) + "]";
}
}
}
Program.csPersonnage hero = new Personnage();
hero.Nom = "Aragorn";
hero.Classe = "Guerrier";
hero.PvMax = 100;
hero.Pv = 100;
hero.Niveau = 5;
Console.WriteLine($"{hero.Nom} ({hero.Classe}) — Niv.{hero.Niveau}");
Console.WriteLine($"PV : {hero.Pv}/{hero.PvMax} {hero.BarreDeVie}");
// Aragorn (Guerrier) — Niv.5
// PV : 100/100 [██████████]
hero.Pv = -9999; // ramené à 0
Console.WriteLine(hero.BarreDeVie);
// [░░░░░░░░░░]
Console.WriteLine(hero.EstVivant); // False
Compare le nombre de lignes avec le getter/setter du Chapitre 3 — et surtout compare la lisibilité du code appelant.
InventaireVoici un exemple complet qui combine propriétés calculées et liste interne protégée.
La classe Inventaire contient une liste d'objets appartenant à un personnage. La liste est private : aucun code à l'extérieur ne peut la modifier directement. On passe obligatoirement par les méthodes prévues.
class Inventaire
{
// La liste est privée et initialisée à la déclaration
// (pas besoin de constructeur — on verra cette syntaxe au Chap. 6)
private List<string> objets = new List<string>();
// Capacité maximale (auto-implémentée, modifiable de l'extérieur)
public int CapaciteMax { get; set; } = 10;
// Propriété calculée : nombre d'objets dans l'inventaire
public int Taille => objets.Count;
// Propriété calculée : est-ce que l'inventaire est plein ?
public bool EstPlein => objets.Count >= CapaciteMax;
// --- Méthodes métier ---
public void Ajouter(string objet)
{
if (EstPlein)
{
Console.WriteLine($"Inventaire plein ! Impossible d'ajouter {objet}");
return;
}
objets.Add(objet);
Console.WriteLine($"{objet} ajouté à l'inventaire.");
}
public void Retirer(string objet)
{
if (!objets.Contains(objet))
{
Console.WriteLine($"{objet} n'est pas dans l'inventaire.");
return;
}
objets.Remove(objet);
Console.WriteLine($"{objet} retiré de l'inventaire.");
}
public void Afficher()
{
if (Taille == 0)
{
Console.WriteLine("L'inventaire est vide.");
return;
}
Console.WriteLine($"Inventaire ({Taille}/{CapaciteMax}) :");
foreach (string o in objets)
{
Console.WriteLine($" - {o}");
}
}
}
Inventaire sac = new Inventaire();
sac.Ajouter("Épée longue");
sac.Ajouter("Potion de soin");
sac.Ajouter("Carte du donjon");
sac.Afficher();
// Inventaire (3/10) :
// - Épée longue
// - Potion de soin
// - Carte du donjon
sac.Retirer("Potion de soin");
sac.Retirer("Arc elfique"); // n'existe pas → message d'erreur
Console.WriteLine($"Objets restants : {sac.Taille}");
// Objets restants : 2
💡 Observe que
Taille,EstPleinetCapaciteMaxsont des propriétés, alors queAjouter,RetireretAffichersont des méthodes. La règle pour choisir : propriété pour exposer une donnée (lecture, écriture, calcul) ; méthode pour exécuter une action. On formalisera tout ça au Chapitre 5.
Voici comment les trois chapitres s'enchaînent :
CHAP. 1 CHAP. 2 CHAP. 3 CHAP. 4
────────────── ────────────── ────────────── ──────────────
public int Pv; → private int pv; → private int pv; → private int pv;
public int GetPv() public int Pv
{ return pv; } {
get { return pv; }
public void SetPv(int v) set
{ {
if (v < 0) v = 0; if (value < 0)
pv = v; value = 0;
} pv = value;
}
}
hero.Pv = -9999; ❌ inaccessible hero.SetPv(-9999); hero.Pv = -9999;
(accepté, dégâts) (compile error) (stocke 0 silenc.) (stocke 0 silenc.)
CompteBancaireReprends ta classe CompteBancaire du Chapitre 3 (ex. 2). Convertis tes getters/setters en propriétés.
Titulaire : propriété avec validation (refuser une chaîne vide).Solde : propriété en lecture seule depuis l'extérieur (public double Solde { get; private set; }). L'écriture passe uniquement par Deposer et Retirer.Test : essaie compte.Solde = 1_000_000; depuis Program.cs — Rider doit refuser de compiler.
Crée une classe LampeConnectee avec :
Couleur (string, propriété auto-implémentée).Intensite (int, propriété avec validation : bornée entre 0 et 100).EstAllumee (bool, propriété auto-implémentée).Description : propriété calculée qui renvoie une chaîne du genre :
"Lampe rouge — intensité 80% — allumée"
ou "Lampe blanche — éteinte" si la lampe n'est pas allumée.Crée une classe Produit avec :
Nom, Prix, Quantite (propriétés avec validations adaptées : prix ≥ 0, quantité ≥ 0).ValeurStock : propriété calculée = Prix × Quantite.EnRupture : propriété calculée = true si la quantité est à 0.Bonus : ajoute une méthode Vendre(int nombre) qui diminue la quantité de nombre unités, en refusant si le stock est insuffisant.
set, le mot-clé value représente la valeur que l'appelant cherche à affecter.get { ... } / set { ... }.{ get; set; } pour les propriétés sans validation — C# crée l'attribut privé tout seul.private set = lecture publique, écriture interne uniquement.set.On a vu les données d'une classe en profondeur : attributs privés, propriétés, propriétés calculées. Il reste l'autre moitié — les méthodes. On les a utilisées sans jamais les formaliser : type de retour, paramètres, le mot-clé this, la méthode magique ToString(). C'est l'objet du chapitre suivant.