Quand dois-je utiliser une structure au lieu d’une classe?

MSDN indique que vous devez utiliser des structures lorsque vous avez besoin d’objects légers. Y a-t-il d’autres scénarios où une structure est préférable à une classe?

Certaines personnes pourraient avoir oublié que:

  1. Les structures peuvent avoir des méthodes.
  2. Les structures ne peuvent pas être héritées.

Je comprends les différences techniques entre les structures et les classes, je ne sais pas trop quand utiliser une structure.

MSDN a la réponse: Choisir entre les classes et les structures .

Fondamentalement, cette page vous donne une liste de contrôle de 4 éléments et vous dit d’utiliser une classe à moins que votre type ne réponde à tous les critères.

Ne définissez une structure que si le type présente toutes les caractéristiques suivantes:

  • Il représente logiquement une valeur unique, similaire aux types primitifs (entier, double, etc.).
  • Il a une taille d’instance inférieure à 16 octets.
  • C’est immuable.
  • Il ne sera pas nécessaire de l’encadrer fréquemment.

Je suis surpris de n’avoir lu aucune des réponses précédentes, ce que je considère comme l’aspect le plus crucial:

J’utilise des structures quand je veux un type sans identité. Par exemple un point 3D:

 public struct ThreeDimensionalPoint { public readonly int X, Y, Z; public ThreeDimensionalPoint(int x, int y, int z) { this.X = x; this.Y = y; this.Z = z; } public override ssortingng ToSsortingng() { return "(X=" + this.X + ", Y=" + this.Y + ", Z=" + this.Z + ")"; } public override int GetHashCode() { return (this.X + 2) ^ (this.Y + 2) ^ (this.Z + 2); } public override bool Equals(object obj) { if (!(obj is ThreeDimensionalPoint)) return false; ThreeDimensionalPoint other = (ThreeDimensionalPoint)obj; return this == other; } public static bool operator ==(ThreeDimensionalPoint p1, ThreeDimensionalPoint p2) { return p1.X == p2.X && p1.Y == p2.Y && p1.Z == p2.Z; } public static bool operator !=(ThreeDimensionalPoint p1, ThreeDimensionalPoint p2) { return !(p1 == p2); } } 

Si vous avez deux instances de cette structure, vous ne vous souciez pas de savoir si elles sont un seul morceau de données en mémoire ou deux. Vous vous souciez simplement de la ou des valeur (s) qu’ils détiennent.

Bill Wagner a un chapitre à ce sujet dans son livre “c # efficace” ( http://www.amazon.com/Effective-Specific-Ways-Improve-Your/dp/0321245660 ). Il conclut en utilisant le principe suivant:

  1. La principale responsabilité du type de stockage de données est-elle?
  2. Son interface publique est-elle entièrement définie par des propriétés qui accèdent ou modifient ses membres de données?
  3. Êtes-vous sûr que votre type n’aura jamais de sous-classes?
  4. Êtes-vous sûr que votre type ne sera jamais traité de manière polymorphe?

Si vous répondez «oui» aux 4 questions: utilisez une structure. Sinon, utilisez une classe.

Utilisez une structure lorsque vous souhaitez utiliser une sémantique de type valeur au lieu du type de référence. Les structures sont copiées par valeur, alors faites attention!

Voir aussi les questions précédentes, par exemple

Quelle est la différence entre struct et class dans .NET?

J’utiliserais des structures quand:

  1. un object est supposé être en lecture seule (chaque fois que vous passez / assignez une structure, il est copié). Les objects en lecture seule sont parfaits pour le traitement multithread car ils ne nécessitent pas de locking dans la plupart des cas.

  2. un object est petit et court. Dans un tel cas, il y a de fortes chances que l’object soit alloué sur la stack, ce qui est beaucoup plus efficace que de le placer sur le tas géré. De plus, la mémoire allouée par l’object sera libérée dès qu’elle sort de son cadre. En d’autres termes, le ramasse-miettes fonctionne moins bien et la mémoire est utilisée plus efficacement.

Utilisez une classe si:

  • Son identité est importante. Les structures sont copiées implicitement lorsqu’elles sont transmises par valeur dans une méthode.
  • Il aura une grande empreinte mémoire.
  • Ses champs nécessitent des initialiseurs.
  • Vous devez hériter d’une classe de base.
  • Vous avez besoin d’un comportement polymorphe.

Utilisez une structure si:

  • Il agira comme un type primitif (int, long, octet, etc.).
  • Il doit avoir une faible empreinte mémoire.
  • Vous appelez une méthode P / Invoke qui nécessite qu’une structure soit transmise par valeur.
  • Vous devez réduire l’impact de la récupération de place sur les performances des applications.
  • Ses champs doivent être initialisés uniquement à leurs valeurs par défaut. Cette valeur serait zéro pour les types numériques, false pour les types booléens et null pour les types de référence.
    • Notez que dans C # 6.0, les structures peuvent avoir un constructeur par défaut qui peut être utilisé pour initialiser les champs de la structure à des valeurs autres que celles par défaut.
  • Vous n’avez pas besoin d’hériter d’une classe de base (autre que ValueType, à partir de laquelle toutes les structures héritent).
  • Vous n’avez pas besoin d’un comportement polymorphe.

J’ai toujours utilisé une structure quand je voulais regrouper quelques valeurs pour renvoyer des éléments d’un appel de méthode, mais je n’aurai pas besoin de l’utiliser pour rien après avoir lu ces valeurs. Juste pour garder les choses propres. J’ai tendance à voir les choses dans une structure comme “jetable” et les choses dans une classe comme plus utiles et “fonctionnelles”

Si une entité doit être immuable, la question de savoir s’il faut utiliser une structure ou une classe sera généralement une question de performances plutôt que de sémantique. Sur un système 32/64 bits, les références de classe nécessitent 4/8 octets à stocker, quelle que soit la quantité d’informations dans la classe; copier une référence de classe nécessite la copie de 4/8 octets. D’autre part, chaque instance de classe distincte aura 8/16 octets de surcharge en plus des informations qu’elle contient et du coût en mémoire des références à celle-ci. Supposons que l’on veuille un tableau de 500 entités, chacune contenant quatre entiers de 32 bits. Si l’entité est un type de structure, le tableau nécessitera 8 000 octets, que les 500 entités soient toutes identiques, toutes différentes ou entre les deux. Si l’entité est un type de classe, le tableau de 500 références prendra 4 000 octets. Si toutes ces références pointent vers des objects différents, les objects nécessiteraient 24 octets supplémentaires chacun (12 000 octets pour tous les 500), soit un total de 16 000 octets, soit le double du coût de stockage d’un type de structure. D’autre part, du code créé une instance d’object, puis copié une référence aux 500 emplacements de tableau, le coût total serait de 24 octets pour cette instance et de 4 000 pour le tableau, soit un total de 4 024 octets. Une économie majeure. Peu de situations fonctionneraient aussi bien que la dernière, mais dans certains cas, il serait peut-être possible de copier des références sur suffisamment de slots pour créer un tel partage.

Si l’entité est supposée être mutable, la question de savoir s’il faut utiliser une classe ou une structure est à certains égards plus facile. Supposons que “Thing” soit une structure, soit une classe ayant un champ entier appelé x, et que l’on effectue le code suivant:

   Chose t1, t2;
   ...
   t2 = t1;
   t2.x = 5;

Veut-on que cette dernière déclaration affecte t1.x?

Si Thing est un type de classe, t1 et t2 seront équivalents, ce qui signifie que t1.x et t2.x seront également équivalents. Ainsi, la seconde déclaration affectera t1.x. Si Thing est un type de structure, t1 et t2 seront des instances différentes, ce qui signifie que t1.x et t2.x feront référence à différents entiers. Ainsi, la deuxième déclaration n’affectera pas t1.x.

Les structures mutables et les classes mutables ont des comportements fondamentalement différents, bien que .net ait quelques bizarreries dans la gestion des mutations de structure. Si on veut un comportement de type valeur (ce qui signifie que “t2 = t1” copiera les données de t1 à t2 en laissant t1 et t2 comme instances distinctes), et si l’on peut vivre avec les bizarreries dans la gestion des types de valeur par .net, utilisez une structure. Si on veut une sémantique de type valeur, mais les bizarreries de .net conduiraient à une sémantique de type valeur cassée dans une application, utiliserait une classe et marmonnerait.

En plus des excellentes réponses ci-dessus:

Les structures sont des types de valeur.

Ils ne peuvent jamais être réglés sur Rien .

Définir une structure = Nothing, définira tous ses types de valeurs sur leurs valeurs par défaut.

lorsque vous n’avez pas vraiment besoin de comportement, mais que vous avez besoin de plus de structure qu’un simple tableau ou dictionnaire.

Suivi C’est comme ça que je pense aux structures en général. Je sais qu’ils peuvent avoir des méthodes, mais j’aime garder cette distinction mentale globale.

Comme le dit @Simon, les structures fournissent une sémantique de type “valeur”, donc si vous avez besoin d’un comportement similaire à un type de données intégré, utilisez une structure. Comme les structures sont transmises par copie, vous voulez vous assurer qu’elles sont de petite taille, environ 16 octets.

Hmm …

Je n’utiliserais pas la récupération de place comme argument pour / contre l’utilisation de structures par rapport aux classes. Le tas géré fonctionne comme une stack – la création d’un object ne fait que le placer en haut du tas, ce qui est presque aussi rapide que l’allocation sur la stack. De plus, si un object est de courte durée et ne survit pas à un cycle GC, la désallocation est libre car le GC ne fonctionne qu’avec la mémoire encore accessible. (Recherche MSDN, il y a une série d’articles sur la gestion de la mémoire .NET, je suis juste trop paresseux pour aller les chercher).

La plupart du temps, j’utilise une structure, je finis par me cogner dessus, car je découvre plus tard que la sémantique de référence aurait rendu les choses un peu plus simples.

Quoi qu’il en soit, ces quatre points dans l’article MSDN publié ci-dessus semblent être une bonne directive.

Les structures sont sur la stack, pas sur le tas, donc elles sont sûres et doivent être utilisées lors de l’implémentation du modèle d’object de transfert, vous ne voulez jamais utiliser d’objects sur le tas, elles sont volatiles. Ceci est un cas de base pour l’utilisation d’une structure, je suis surpris par les réponses ici,

Je pense que la meilleure réponse est simplement d’utiliser struct lorsque ce dont vous avez besoin est une collection de propriétés, class lorsque c’est un ensemble de propriétés AND comportements.