Une structure n'est, en son cœur, ni plus ni moins qu'une agrégation de champs. Dans .NET, il est possible pour une structure de "faire semblant" d'être un objet, et pour chaque type de structure .NET définit implicitement un type d'objet de tas avec les mêmes champs et méthodes qui - étant un objet de tas - se comporteront comme un objet . Une variable qui contient une référence à un tel objet de tas (structure "en boîte") présentera une sémantique de référence, mais celle qui contient directement une structure est simplement une agrégation de variables.
Je pense qu'une grande partie de la confusion struct-versus-class provient du fait que les structures ont deux cas d'utilisation très différents, qui devraient avoir des directives de conception très différentes, mais les directives MS ne font pas de distinction entre elles. Parfois, il y a un besoin de quelque chose qui se comporte comme un objet; dans ce cas, les directives MS sont assez raisonnables, bien que la "limite de 16 octets" devrait probablement être plus comme 24-32. Parfois, cependant, il faut une agrégation de variables. Une structure utilisée à cette fin devrait simplement consister en un ensemble de champs publics, et éventuellement un Equals
remplacement, un ToString
remplacement etIEquatable(itsType).Equals
la mise en oeuvre. Les structures qui sont utilisées comme agrégations de champs ne sont pas des objets et ne doivent pas prétendre l'être. Du point de vue de la structure, la signification du champ ne doit être ni plus ni moins que «la dernière chose écrite dans ce champ». Toute signification supplémentaire doit être déterminée par le code client.
Par exemple, si une structure d'agrégation de variables a des membres Minimum
et Maximum
, la structure elle-même ne doit pas le promettre Minimum <= Maximum
. Le code qui reçoit une telle structure en tant que paramètre doit se comporter comme s'il avait été transmis séparément Minimum
et Maximum
valeurs. Une exigence qui Minimum
n'est pas supérieure à Maximum
doit être considérée comme une exigence selon laquelle un Minimum
paramètre n'est pas supérieur à un paramètre passé séparément Maximum
.
Un modèle utile à considérer parfois est d'avoir une ExposedHolder<T>
classe définie quelque chose comme:
class ExposedHolder<T>
{
public T Value;
ExposedHolder() { }
ExposedHolder(T val) { Value = T; }
}
Si on a un List<ExposedHolder<someStruct>>
, où someStruct
est une structure d'agrégation de variables, on peut faire des choses comme myList[3].Value.someField += 7;
, mais donner myList[3].Value
à un autre code lui donnera le contenu Value
plutôt que de lui donner un moyen de le modifier. En revanche, si on utilisait a List<someStruct>
, il faudrait utiliser var temp=myList[3]; temp.someField += 7; myList[3] = temp;
. Si l'on utilisait un type de classe mutable, exposer le contenu de myList[3]
à un code extérieur nécessiterait de copier tous les champs vers un autre objet. Si l'on utilisait un type de classe immuable, ou une structure "de style objet", il serait nécessaire de construire une nouvelle instance qui ressemblerait à l' myList[3]
exception de celle someField
qui était différente, puis de stocker cette nouvelle instance dans la liste.
Une note supplémentaire: si vous stockez un grand nombre de choses similaires, il peut être bon de les stocker dans des tableaux de structures éventuellement imbriqués, en essayant de préférence de garder la taille de chaque tableau entre 1K et 64K environ. Les tableaux de structures sont spéciaux, en ce que l'indexation donnera une référence directe à une structure à l'intérieur, donc on peut dire "a [12] .x = 5;". Bien que l'on puisse définir des objets de type tableau, C # ne leur permet pas de partager une telle syntaxe avec des tableaux.