Structs – exemples réels?

Il existe un certain nombre de questions sur SO traitant des différences entre Structs et Classes en C #, et quand utiliser l’une ou l’autre. (La seule phrase répond: utilisez des structures si vous avez besoin de sémantique de valeur.) Il existe de nombreuses directives sur la manière de choisir l’une ou l’autre, dont la plupart se résument à: utiliser une classe à moins une structure

Tout cela a du sens pour moi.

Cependant, je n’arrive pas à trouver d’exemples réels de personnes utilisant des structures dans un système. Je suis (semi-) nouveau dans C #, et j’ai du mal à imaginer une situation concrète où les structs sont vraiment le bon choix (du moins, je n’en ai pas encore rencontré.)

Donc, je me tourne vers le monde du cerveau SO. Quels sont les cas où vous avez réellement utilisé une structure dans un système où une classe n’aurait pas fonctionné?

Eh bien, une classe travaillerait toujours pour cela, mais un exemple auquel je pourrais penser est quelque chose comme un point. En supposant que ce soit une valeur x et y, vous pouvez utiliser une structure.

 struct Point { int x; int y; } 

Dans mon esprit, je préférerais avoir une représentation plus simple d’une paire d’entiers que de définir une utilisation d’une classe avec des instanciations lorsque l’entité réelle n’a pas vraiment beaucoup (ou pas du tout) de comportement.

J’ai utilisé une structure pour représenter une géolocalisation

 struct LatLng { public decimal Lattitude { get; set; } public decimal Longitude { get; set; } } 

cela représente une entité unique, par exemple, je peux append 2 LatLng ensemble ou effectuer d’autres opérations sur cette entité unique.

MSDN-struct

Le type de structure convient pour représenter des objects légers tels que Point, Rectangle et Color. Bien qu’il soit possible de représenter un point en tant que classe, une structure est plus efficace dans certains scénarios. Par exemple, si vous déclarez un tableau de 1000 objects Point, vous allouerez de la mémoire supplémentaire pour référencer chaque object. Dans ce cas, la structure est moins chère.

De plus, si vous regardez les types primitifs Int32, decimal, double ..etc, vous remarquerez qu’ils sont tous des structures , ce qui leur permet d’être des types de valeur tout en leur permettant d’implémenter certaines interfaces cruciales.

Les structures sont également généralement utilisées dans les systèmes de graphisme / rendu. La création de points / vecteurs comporte de nombreux avantages.

Rico Mariani a publié un excellent questionnaire sur la programmation basée sur la valeur . Il a discuté de nombreuses raisons pour préférer des structures dans des situations spécifiques, et l’a expliqué en détail dans son post des résultats du quiz .

Une structure Money est probablement l’une des plus courantes, mais le numéro de téléphone ou l’adresse sont également courants.

 public struct Money { public ssortingng Currency { get; set; } public double Amount { get; set; } } public struct PhoneNumber { public int Extension { get; set; } public int RegionCode { get; set; } //... etc. } public struct FullName { public ssortingng FirstName { get; set; } public ssortingng MiddleName { get; set; } public ssortingng LastName { get; set; } } 

Gardez à l’esprit que, dans .NET, vos structures ne devraient pas avoir une empreinte mémoire supérieure à 16 octets, car si elles deviennent plus grandes, le CLR doit allouer de la mémoire supplémentaire.

En outre, comme les structs ‘live’ sur la stack (et non le tas comme le font les types de référence), vous pouvez envisager d’utiliser des structures si vous devez instancier un grand nombre d’objects de même type.

L’exemple par excellence est le nullable types frameworks, tel que int? . Celles-ci utilisent des structures afin de conserver la sémantique de la valeur d’un int, tout en permettant de les rendre nulles sans les enchaîner et en les transformant en types de référence.

Vous utiliseriez une structure lorsque vous ne voulez pas passer quelque chose par référence. Supposons que vous ayez une collection de données ou un object que vous souhaitez transmettre par valeur (c.-à-d. Tout ce que vous lui transmettez fonctionne avec sa propre copie unique et non une référence à la version d’origine), alors une structure est le bon type pour utilisation.

Ils fournissent une implémentation par défaut pour Object.GetHashCode (). Vous pouvez donc utiliser une structure au lieu d’une classe lorsque l’object est une collection simple de types non référencés que vous souhaitez utiliser comme clés d’un dictionnaire.

Ils sont également utiles pour les scénarios de mise en réseau PInvoke / interop ou bas niveau dans lesquels vous souhaitez un contrôle précis de la disposition binary d’une structure de données. (aller à http://www.pinvoke.net pour beaucoup de code d’interopérabilité qui nécessite des structs)

Mais vraiment, je ne les utilise jamais moi-même. Ne transpire pas sans les utiliser.

Fondamentalement, j’essaie de ne pas les utiliser. Je trouve qu’ils confondent les autres développeurs de l’équipe et ne valent donc pas la peine. Je n’ai trouvé qu’un cas d’utilisation, un type personnalisé de type Enum, nous utilisons un générateur de code pour produire à partir de XML.

La clé pour moi est de définir si je veux garder une référence au même object.

Ce qui fait que lorsque struct fait partie d’une autre entité, mais fait entité elle-même.

Dans l’exemple ci-dessus avec LatLong qui fait parfaitement le sens, par exemple. Vous devez copier les valeurs d’un object vers un autre, sans continuer à référencer le même object.

J’utilise souvent des structures pour représenter un type de valeur de modèle de domaine qui pourrait être représenté comme un enum, mais nécessite un nombre illimité arbitraire de valeurs discrètes, ou je souhaite qu’il ait un comportement (méthodes) supplémentaire que vous ne pouvez pas append à une énumération … Par exemple, dans un projet récent, de nombreux éléments de données étaient associés à un mois de calendrier spécifique plutôt qu’à une date. J’ai donc créé une structure CalendarMonth dotée de méthodes:

  • CalendarMonth Parse statique (DateTime inValue);
  • CalendarMonth Parse statique (chaîne inValue);

et la méthode TryParse (),

  • bool statique TryParse (chaîne inValue, out CalendarMonth outVal);

Et propriétés

  • int Month {get; ensemble; }
  • int Année {get; ensemble; }
  • DateTime StartMonthLocal {get; ensemble; }
  • DateTime StartMonthUTC {get; ensemble; }
  • DateTime EndMonthLocal {get; ensemble; }
  • DateTime EndMonthUTC {get; ensemble; }

etc.

Je ne suis généralement pas concerné par la «densité de données» dans mes applications professionnelles. Je vais généralement toujours utiliser une classe sauf si je veux spécifiquement la sémantique de la valeur

cela signifie que je suis en train de visiter une situation où je veux comparer deux de ces choses et je veux qu’elles se présentent comme si elles avaient la même valeur. Avec les classes, c’est en fait plus de travail car j’ai besoin de remplacer ==,! =, Equals, et GetHashcode, ce qui, même si cela est fait pour moi, est un code inutile.

Donc, dans mon esprit, utilisez toujours des classes, sauf si vous savez que vous voulez que ces choses soient comparées par valeur (dans ce cas, la valeur du composant)

Je suppose donc que vous n’avez jamais utilisé DateTime (une structure).

Je ne peux pas croire que personne n’a mentionné XNA : dans XNA, presque tout est une struct . Alors quand tu le fais

 Masortingx rotation = Masortingx.CreateRotationZ(Math.PiOver2); 

Vous créez vraiment un type de valeur.

En effet, contrairement à la programmation d’applications, un décrochage de quelques millisecondes pendant que le ramasse-miettes tourne n’est pas acceptable (nous n’obtenons que 16,6 ms pour restituer l’ensemble de la trame!). ne doit pas courir autant.

Cela est particulièrement vrai sur la XBox 360, où la qualité du CPG est loin d’être la même que sur le PC – même une moyenne d’une allocation par image peut tuer les performances!

J’ai travaillé dans des institutions financières où des exigences de mise en cache et de latence à grande échelle ont été atteintes en utilisant des structures. Fondamentalement, les structures peuvent épargner le garbage collector de beaucoup de travail.

Voir ces exemples:

http://00sharp.wordpress.com/2013/07/03/a-case-for-the-struct/ http://00sharp.wordpress.com/2013/07/04/a-case-for-the- structpart-2 /

Fondamentalement, j’utilise Struct pour modéliser des données géomésortingques et mathématiques, ou lorsque je veux une structure de données basée sur la valeur.

La seule fois où j’ai utilisé une structure, c’était quand je construisais une structure de fraction:

 public struct Fraction { public int Numerator {get;set;} public int Denominator {get; set;} //it then had a bunch of Fraction methods like Reduce, Add, Subtract etc... } 

Je pensais que cela représentait une valeur, tout comme les types de valeur intégrés, et par conséquent, le coder serait plus naturel si elle se comportait comme un type de valeur.

Je pense que le Framework .Net est assez réel. Voir la liste sous “Structures”:

Espace de noms système

Dans certaines situations critiques en termes de performances, une structure (un type de valeur et donc allouée à partir de la stack) peut être meilleure qu’une classe (un type de référence et donc alloué à partir du tas). Le billet de Joe Duffy ” Un lecteur / enregistreur de texte à rotation unique ” montre une application réelle.

StorageCapacity est l’un de ceux que j’ai créés par le passé. Il représentait 0 octets à N exaoctets (aurait pu être plus élevé pour le yottabyte, mais exa semblait suffisant à l’époque). La structure avait un sens depuis que je travaillais pour une société de gestion de stockage. Vous penseriez que c’était assez simple: une structure avec un StorageUnit (enum) et une quantité (j’ai utilisé décimal). Mais lorsque vous ajoutez des conversions, des opérateurs et des classes pour prendre en charge le formatage, l’parsing, etc.

L’abstraction était utile pour vous permettre de prendre n’importe quelle capacité de stockage et de la représenter sous forme d’octets, kilo-octets, etc. sans avoir à multiplier ou diviser par 1024 plusieurs fois.

J’ai déjà donné mes raisons d’utiliser des structures déjà ailleurs ( Quand utiliser struct en C # ), et j’ai utilisé des structures pour ces raisons dans des projets réels:

Je choisirais d’utiliser des structures pour des raisons de performances si je devais stocker un grand nombre du même type d’élément dans un tableau, ce qui peut arriver dans le traitement des images.

Il faut utiliser des structures pour transmettre des données structurées entre C # et C ++.

À moins d’avoir une très bonne raison de les utiliser, j’essaie de les éviter.

Je sais que certaines personnes aiment les utiliser pour implémenter la sémantique des valeurs mais je trouve que ce comportement est si différent du comportement d’affectation “normal” des classes (en C #) qu’on rencontre des bogues difficiles à suivre que l’object auquel on assignait ou avait ce comportement parce qu’il était implémenté comme une structure au lieu d’une classe. (Cela m’est arrivé plus d’une fois, alors je donne cet avertissement car j’ai été brûlé par l’utilisation injuste de structures C #.)

Je ne suis pas certain de son utilité, mais j’ai découvert aujourd’hui que même si vous ne pouvez pas avoir d’intialiseurs de champs d’instance dans des structures, vous pouvez le faire dans les classes.

Le code suivant donnera donc des erreurs de compilation, mais si vous modifiez le “struct” en “class”, il comstack.

  public struct ServiceType { public bool backEnd { get; set; } public bool frontEnd { get; set; } public ssortingng[] backEndServices = { "Service1", "Service2" }; public ssortingng[] frontEndServices = { "Service3", "Service4" }; } 

Une structure en C # n’est ni plus ni moins qu’une série de variables collées avec du ruban adhésif. Si l’on veut que chaque variable d’un type particulier représente un groupe de variables indépendantes mais liées (telles que les coordonnées d’un point) collées avec du ruban adhésif, il est souvent préférable d’utiliser une structure de champ exposé qu’une classe, que “groupe” signifie deux ou vingt. Notez que, bien que les conseils de struct-versus-class de Microsoft conviennent aux types de données qui encapsulent une seule valeur, ils doivent être considérés comme inapplicables aux types dont le but est d’encapsuler des valeurs indépendantes mais liées. Plus les variables sont indépendantes, plus les avantages de l’utilisation d’une structure de champ exposé sont importants.

Si l’on souhaite utiliser une classe pour encapsuler un ensemble de variables indépendantes, il y a deux manières de le faire, dont aucune n’est très pratique. On peut utiliser une classe immuable, auquel cas tout emplacement de stockage non nul de ce type de classe encapsulera les valeurs détenues par l’instance identifiée par ce dernier, et un emplacement de stockage pourra être copié pour que le nouveau encapsule ces mêmes valeurs. Malheureusement, la modification de l’une des valeurs encapsulées par un emplacement de stockage nécessite généralement la construction d’une nouvelle instance, comme l’ancienne, sauf que cette valeur a été modifiée. Par exemple, si on a une variable pt de type Immutable3dPoint et que l’on souhaite augmenter la pt.X de un, il faudrait faire quelque chose comme: pt = new Immutable3dPoint(pt.X+1, pt.Y, pt.Z); Peut-être tolérable si le type encapsule seulement trois valeurs, mais plutôt ennuyeux s’il y en a beaucoup.

L’autre approche basée sur la classe consiste à utiliser une classe mutable; Cela nécessite généralement que l’on s’assure que chaque emplacement de stockage du type de classe détient la seule référence dans l’univers pour une instance de cette classe. Lorsqu’un emplacement de stockage est créé, il faut créer une nouvelle instance et y stocker une référence. Si l’on souhaite copier les valeurs de l’emplacement de stockage P vers l’emplacement de stockage Q , il faut copier tous les champs ou propriétés d’une instance à l’autre (peut-être en CopyFrom le type implémente une méthode CopyFrom et en disant Q.CopyFrom(P); Notez que si l’on dit plutôt Q=P; cela peut sembler fonctionner, mais les futures tentatives de modification de P modifieront également Q et vice-versa. facile de gâcher les choses.

Les structures de champ exposé combinent la sémantique pratique de la copie de valeur des classes immuables avec les modifications par morceaux commodes autorisées par les classes mutables. Les grandes structures sont plus lentes à copier que les références à des objects immuables, mais le coût de modification d’une partie d’une structure de champ exposé dépend uniquement de l’étendue de la modification, plutôt que de la taille globale de la structure. En revanche, le coût de la modification d’une donnée encapsulée dans un type de classe immuable sera proportionnel à la taille totale de la classe.