La différence entre une structure et une classe change surtout la manière dont on conçoit ses types, ses copies et ses API. En pratique, la vraie réponse à struct vs class dépend du langage, et c’est justement ce point qui crée le plus de confusion. Ici, je découpe le sujet de façon utile: ce qui change en C++, ce qui change en C#, et la règle simple que j’utilise pour choisir sans me tromper.
Les différences qui comptent vraiment avant de choisir un type
- En C++, une structure et une classe sont presque identiques, avec surtout des défauts d’accès différents.
- En C#, une structure est un type valeur et une classe un type référence, ce qui change la copie et le comportement.
-
structconvient surtout aux petits types de données immuables. -
classconvient mieux aux objets avec identité, état mutable ou héritage. - Le plus mauvais réflexe consiste à choisir le type sur l’habitude, pas sur la sémantique du modèle.
La première question à se poser est le langage cible
Je commence toujours par là, parce que la comparaison n’a pas le même sens partout. En C++, la différence est surtout une question de visibilité par défaut; en C#, elle touche au modèle même du type, puisque l’un copie les données et l’autre copie une référence. Dit autrement, le mot-clé ne raconte pas la même histoire selon l’écosystème.
| Langage | struct |
class |
Ce que cela change vraiment |
|---|---|---|---|
| C++ | Membres publics par défaut | Membres privés par défaut | Le choix est surtout stylistique et expressif |
| C# | Type valeur | Type référence | La copie, l’identité et la mémoire ne se comportent pas pareil |
Je vois souvent des développeurs transférer leurs réflexes d’un langage à l’autre, puis s’étonner du résultat. C’est précisément pour éviter ce piège qu’il faut séparer les cas, au lieu d’imaginer une règle universelle. La suite commence donc par le cas le plus simple à clarifier: C++.
En C++, la différence est presque entièrement conventionnelle
Dans le langage C++, une struct et une class peuvent toutes les deux contenir des données, des méthodes, des constructeurs, des opérateurs, de l’héritage et du polymorphisme. La différence utile est courte: les membres d’une structure sont publics par défaut, ceux d’une classe sont privés par défaut. Le reste est pratiquement identique.
Je résume souvent C++ de cette façon: struct signale un type plus simple, souvent orienté données, alors que class signale plus volontiers un objet avec invariants et encapsulation. Ce n’est pas une contrainte technique stricte, mais un indice de lecture très fort pour une équipe. En revue de code, ce signal compte souvent plus que la syntaxe elle-même.
struct Point {
int x;
int y;
};
class Account {
int balance;
public:
Account(int initial) : balance(initial) {}
void deposit(int amount) { balance += amount; }
};Le premier type dit presque immédiatement "donnée simple". Le second dit "comportement et état à protéger". C’est une différence de design, pas une différence de pouvoir du langage. Et c’est justement cette subtilité qui mène naturellement au cas où le mot-clé change vraiment tout: C#.
En C#, la sémantique de valeur change vraiment la donne
En C#, une struct est un type valeur, tandis qu’une class est un type référence. La conséquence est immédiate: quand j’assigne une structure, je copie les données; quand j’assigne une classe, je copie la référence vers le même objet. C’est la différence la plus importante du sujet, bien plus que le mot-clé lui-même.
| Aspect | struct |
class |
|---|---|---|
| Sémantique | Valeur | Référence |
| Affectation | Copie les données | Copie la référence |
| Héritage | Pas d’héritage de classe ou de struct | Héritage possible |
| Usage naturel | Petit type de données, souvent immuable | Objet avec identité, comportement ou cycle de vie |
| Effet secondaire fréquent | Copie involontaire si le type grossit | Partage de l’état entre plusieurs variables |
public struct Point
{
public int X;
public int Y;
}
var a = new Point { X = 1, Y = 2 };
var b = a;
b.X = 10;
// a.X reste 1Ce petit exemple dit tout: la copie d’un struct isole les modifications. Avec une classe, ce même code modifierait l’objet partagé si la structure du programme suivait la logique de référence. Je préfère donc réserver les structs à des objets compacts, cohérents et idéalement immuables. Dans le code C# moderne, readonly struct ou parfois record struct sont souvent plus propres que des structs mutables bricolés à la main.
Il faut aussi garder un point technique en tête: lorsqu’un struct est traité comme un object ou via une interface, il peut être boxé, c’est-à-dire encapsulé dans un objet de référence. Ce n’est pas un détail académique; sur un chemin chaud, cela peut dégrader les performances et compliquer la lecture des allocations. C’est ce qui m’amène au vrai critère de choix.
Choisir le bon type revient surtout à décrire le bon comportement
Je ne pars pas de la performance brute. Je pars d’abord de la sémantique: est-ce que mon type représente une valeur ou un objet avec identité? Si la réponse est "valeur", la structure devient crédible. Si la réponse est "objet", la classe est presque toujours plus juste.
- Je choisis plutôt
structpour une coordonnée, une couleur, une mesure, un montant ou un petit résultat calculé. - Je choisis plutôt
classpour un utilisateur, une commande, un cache, un service, un contrôleur ou un agrégat métier. - Je garde
structpour des types courts, souvent immuables, et dont la copie reste bon marché. - Je bascule vers
classdès que le type grossit, doit être partagé, ou porte un état évolutif.
Une règle empirique que je garde en tête en C# est simple: si le type commence à devenir volumineux, la copie devient vite plus coûteuse que la référence. Le repère des 16 octets, souvent cité comme ordre de grandeur pour les petits types valeur, reste une heuristique utile, pas une loi gravée dans le marbre. Au-delà de ça, j’évalue aussi la lisibilité, parce qu’un type mal choisi finit presque toujours par coûter plus cher qu’un petit gain supposé.
Ce choix de base mène naturellement à une deuxième question: quelles erreurs font dérailler les projets, même quand la règle semble claire?
Les erreurs qui reviennent le plus souvent
Le premier piège, c’est de croire qu’un struct est forcément plus rapide. Ce n’est vrai que dans certaines conditions, et seulement si les copies restent légères et maîtrisées. Un struct mal conçu peut être plus coûteux qu’une classe, simplement parce qu’il est copié partout.
- Rendre un struct mutable crée des copies difficiles à suivre et des divergences silencieuses.
- Choisir struct pour “optimiser” trop tôt mène souvent à une API plus fragile sans bénéfice mesurable.
- Oublier le boxing peut introduire des allocations invisibles dans des chemins critiques.
- Utiliser class par habitude alors qu’un petit type valeur serait plus clair complique la logique de copie.
- Appliquer le raisonnement C++ à C# ou l’inverse provoque des erreurs de conception très classiques.
Je vois aussi souvent des structures utilisées comme de mini-objets métier alors qu’elles portent déjà trop de responsabilités. À ce stade, la structure devient un compromis bancal: elle semble légère, mais elle ne raconte plus correctement le comportement du domaine. C’est exactement là que la discipline de conception compte plus que le réflexe syntaxique.
Le test que j’applique avant de choisir
Quand j’hésite encore, je me pose trois questions très simples. Elles m’évitent la plupart des mauvais choix et elles fonctionnent aussi bien en C++ qu’en C#, à condition d’interpréter les résultats selon le langage.
- Deux instances avec les mêmes données doivent-elles être interchangeables? Si oui, la logique de valeur a du sens.
- Le type a-t-il une identité propre, un cycle de vie ou des changements d’état visibles ailleurs? Si oui, la classe est probablement plus juste.
- La copie fréquente de ce type reste-t-elle simple, claire et peu coûteuse? Si non, je reviens vers une classe ou je réduis le modèle.
Mon repère final est assez direct: je choisis le type qui décrit le mieux le comportement réel du modèle, pas celui qui semble le plus élégant sur le moment. En C++, cela revient souvent à penser en termes de lisibilité et d’intention. En C#, cela revient surtout à bien choisir entre valeur et référence. Si ce point est juste, le reste du design devient beaucoup plus stable.