Structs versus classes

Je suis sur le sharepoint créer 100 000 objects en code. Ils sont petits, avec seulement 2 ou 3 propriétés. Je les mettrai dans une liste générique et quand ils le seront, je les bouclerai et vérifierai la valeur a et peut-être mettre à jour la valeur b .

Est-il plus rapide / meilleur de créer ces objects en tant que classe ou en tant que struct?

MODIFIER

une. Les propriétés sont des types de valeur (sauf la chaîne que je pense?)

b. Ils pourraient (nous ne sums pas encore sûrs) avoir une méthode de validation

EDIT 2

Je me demandais: les objects sur le tas et la stack sont-ils traités de la même manière par le garbage collector, ou est-ce que cela fonctionne différemment?

Est-il plus rapide de créer ces objects en classe ou en struct?

Vous êtes la seule personne qui peut déterminer la réponse à cette question. Essayez les deux méthodes, mesurez une mesure de performance pertinente, centrée sur l’utilisateur et pertinente, puis vous saurez si le changement a un impact significatif sur les utilisateurs réels dans des scénarios pertinents.

Les structures consumnt moins de mémoire de tas (car elles sont plus petites et plus faciles à compacter, non pas parce qu’elles sont “sur la stack”). Mais ils prennent plus de temps à copier qu’une copie de référence. Je ne sais pas quelles sont vos mesures de performance pour l’utilisation de la mémoire ou la vitesse; Il y a un compromis ici et vous êtes la personne qui sait ce que c’est.

Est-il préférable de créer ces objects en classe ou en struct?

Peut-être que la classe, peut-être struct. En règle générale: Si l’object est:
1. petit
2. Logiquement une valeur immuable
3. Il y en a beaucoup
Ensuite, je considérerais en faire une structure. Sinon, je restrais avec un type de référence.

Si vous devez modifier un champ d’une structure, il est généralement préférable de construire un constructeur qui renvoie une nouvelle structure entière avec le champ défini correctement. C’est peut-être un peu plus lent (mesurez-le!) Mais logiquement beaucoup plus facile à raisonner.

Les objects sur le tas et la stack sont-ils traités de la même manière par le garbage collector?

Non , ce ne sont pas les mêmes car les objects sur la stack sont les racines de la collection . Le ramasse-miettes n’a jamais besoin de demander “Est-ce que cette chose est présente sur la stack?” parce que la réponse à cette question est toujours “Oui, c’est sur la stack”. (Maintenant, vous ne pouvez pas compter sur cela pour garder un object en vie car la stack est un détail d’implémentation. La gigue est autorisée à introduire des optimisations, par exemple, enregistrer ce qui serait normalement une valeur de stack, et jamais de sorte que le GC ne sait pas qu’il est toujours en vie, un object enregistré peut avoir ses descendants collectés de manière agressive, dès que le registre qui s’y tient ne sera pas lu à nouveau.)

Mais le ramasse-miettes doit traiter les objects de la stack de la même manière que les objects connus pour être vivants. L’object sur la stack peut faire référence à des objects alloués au tas qui doivent être maintenus en vie, de sorte que le GC doit traiter les objects de stack tels que les objects vivants alloués au tas afin de déterminer l’ensemble en direct. Mais évidemment, ils ne sont pas traités comme des “objects en direct” dans le but de compacter le tas, car ils ne sont pas sur le tas en premier lieu.

Est-ce clair?

Parfois, avec struct vous n’avez pas besoin d’appeler le constructeur new () et d’affecter directement les champs, ce qui le rend beaucoup plus rapide que d’habitude.

Exemple:

 Value[] list = new Value[N]; for (int i = 0; i < N; i++) { list[i].id = i; list[i].is_valid = true; } 

est environ 2 à 3 fois plus rapide que

 Value[] list = new Value[N]; for (int i = 0; i < N; i++) { list[i] = new Value(i, true); } 

Value est une struct avec deux champs (id et is_valid).

Par contre, les éléments doivent être déplacés ou les types de valeur sélectionnés tout ce que la copie va ralentir. Pour obtenir la réponse exacte, je pense que vous devez définir votre code et le tester.

Une liste renverra une copie de la structure. Si vous le mettez à jour, vous devrez le retirer de la liste et l’append à nouveau, en créant une nouvelle copie avec de nouvelles valeurs et en l’atsortingbuant à l’index de la liste.

Par conséquent, il est préférable d’utiliser des classes.

Lecture connexe: Pourquoi les structures mutables sont-elles «mauvaises»?

Les structures peuvent sembler similaires aux classes, mais il existe des différences importantes dont vous devez être conscient. Tout d’abord, les classes sont des types de référence et les structures sont des types de valeur. En utilisant des structures, vous pouvez créer des objects qui se comportent comme les types intégrés et profiter de leurs avantages.

Lorsque vous appelez l’opérateur New sur une classe, il sera alloué sur le tas. Cependant, lorsque vous instanciez une structure, elle est créée sur la stack. Cela générera des gains de performance. De même, vous ne traiterez pas les références à une instance d’une structure comme vous le feriez avec les classes. Vous travaillerez directement avec l’instance de la structure. De ce fait, lors du passage d’une structure à une méthode, elle est transmise par valeur plutôt que comme référence.

Plus ici:

http://msdn.microsoft.com/en-us/library/aa288471(VS.71).aspx

Les tableaux de structures sont représentés sur le tas dans un bloc de mémoire contigu, alors qu’un tableau d’objects est représenté sous la forme d’un bloc de références contigu avec les objects eux-mêmes, ce qui nécessite de la mémoire pour les objects et leurs références. .

Dans ce cas, comme vous les placez dans une List<> (et une List<> est sauvegardée sur un tableau), il serait plus efficace d’utiliser des structures en termes de mémoire.

(Attention cependant, les grands tableaux trouveront leur chemin sur le tas d’objects volumineux où, si leur durée de vie est longue, cela peut avoir un impact négatif sur la gestion de la mémoire de votre processus. N’oubliez pas que cette mémoire n’est pas la seule considération).

S’ils ont une sémantique de valeur, vous devriez probablement utiliser une structure. S’ils ont une sémantique de référence, vous devriez probablement utiliser une classe. Il y a des exceptions, qui visent principalement à créer une classe même lorsqu’il existe une sémantique de valeur, mais partent de là.

En ce qui concerne votre deuxième édition, le GC ne traite que le tas, mais il y a beaucoup plus de place dans le tas que dans la stack, donc placer des choses sur la stack n’est pas toujours une victoire. En outre, une liste de types de structure et une liste de types de classes seront sur le tas dans un sens ou dans l’autre, ce qui n’est pas pertinent dans ce cas.

Modifier:

Je commence à considérer le terme de mal comme nuisible. Après tout, rendre une classe mutable est une mauvaise idée si elle n’est pas activement requirejse, et je n’exclurais jamais d’utiliser une structure mutable. C’est une mauvaise idée si souvent qu’elle est presque toujours une mauvaise idée, mais la plupart du temps elle ne coïncide pas avec la sémantique de la valeur.

Il peut exister des exceptions raisonnables avec les structures nestedes privées, où toutes les utilisations de cette structure sont limitées à une scope très limitée. Cela ne s’applique pas ici cependant.

Vraiment, je pense que “ça mute donc c’est un mauvais comportement” n’est pas beaucoup mieux que de parler du tas et de la stack (ce qui a au moins un impact sur les performances, même s’il est souvent mal représenté). “Ça mute, donc ça n’a pas de sens de le considérer comme ayant une sémantique de valeur, donc c’est une mauvaise structure” n’est que légèrement différente, mais surtout je pense.

La meilleure solution consiste à mesurer, à mesurer à nouveau, puis à mesurer un peu plus. Il y a peut-être des détails sur ce que vous faites qui peuvent rendre une réponse simple et simplifiée comme “utiliser les structures” ou “utiliser les classes”.

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 “prétendre” être un object, et pour chaque type de structure, .NET définit implicitement un type d’object avec les mêmes champs et méthodes qui – étant un object tas – se comporteront comme un object. . Une variable contenant une référence à un tel object tas (structure “encadrée”) 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 entre les structures et les classes 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 de MS ne les distinguent pas. Parfois, il y a un besoin de quelque chose qui se comporte comme un object; Dans ce cas, les consignes MS sont assez raisonnables, bien que la “limite de 16 octets” soit probablement plus proche de 24-32. Parfois, cependant, il faut une agrégation de variables. Une structure utilisée à cet effet doit simplement consister en un groupe de champs publics, et éventuellement une substitution Equals , un remplacement ToSsortingng et une IEquatable(itsType).Equals . Les structures utilisées comme agrégations de champs ne sont pas des objects et ne doivent pas prétendre l’être. Du sharepoint vue de la structure, la signification du champ ne devrait être ni plus ni moins que “la dernière chose écrite dans ce domaine”. 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 promettre que Minimum <= Maximum . Le code qui reçoit une telle structure en tant que paramètre doit se comporter comme s'il avait reçu des valeurs Minimum et Maximum distinctes. Une exigence selon laquelle Minimum ne doit pas être supérieur à Maximum doit être considérée comme une exigence selon laquelle un paramètre Minimum ne doit pas être supérieur à un paramètre Maximum passé séparément.

Un modèle utile à prendre en compte parfois est de ExposedHolder une ExposedHolder comme:

 class ExposedHolder { public T Value; ExposedHolder() { } ExposedHolder(T val) { Value = T; } } 

Si on a une List> , 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 de Value plutôt que de lui donner un moyen de le modifier. En revanche, si on utilisait un List , il faudrait utiliser var temp=myList[3]; temp.someField += 7; myList[3] = temp; 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 externe nécessiterait de copier tous les champs dans un autre object. Si l'on utilisait un type de classe immuable, ou une structure "object-style", il serait nécessaire de construire une nouvelle instance qui ressemblerait à myList[3] exception de someField qui était différent, puis someField cette nouvelle instance dans la liste.

Une remarque supplémentaire: si vous stockez un grand nombre de choses similaires, il peut être utile de les stocker dans des tableaux de structures éventuellement nesteds, en essayant de préférence de conserver la taille de chaque tableau entre 1 Ko et 64 Ko environ. Les tableaux de structures sont spéciaux, dans la mesure où l’indexation donne une référence directe à une structure à l’intérieur de laquelle on peut dire "a [12] .x = 5;". Bien que l'on puisse définir des objects de type tableau, C # ne leur permet pas de partager une telle syntaxe avec des tableaux.

D’un sharepoint vue c ++, je suis d’accord sur le fait qu’il sera plus lent de modifier les propriétés d’une structure par rapport à une classe. Mais je pense qu’ils seront plus rapides à lire car la structure est allouée sur la stack au lieu du tas. La lecture des données à partir du tas nécessite plus de contrôles que de la stack.

Utilisez des classes.

Sur une note générale Pourquoi ne pas mettre à jour la valeur b lors de leur création?

Eh bien, si vous optez pour struct afterall, débarrassez-vous de la chaîne et utilisez un tampon de caractères ou d’octets de taille fixe.

C’est re: performance.