Pourquoi est-ce implémenté en tant que struct?

Dans System.Data.Linq, EntitySet utilise deux ItemList qui ressemblent à ceci:

  internal struct ItemList where T : class { private T[] items; private int count; ...(methods)... } 

(Il m’a fallu plus de temps que EntitySet pour découvrir cela – je ne pouvais pas comprendre pourquoi le champ d’entités dans EntitySet ne lançait pas d’exceptions de référence nulles!)

Ma question est de savoir quels sont les avantages de l’implémentation en tant que structure sur une classe.

Supposons que vous souhaitiez stocker ItemList dans un tableau.

L’allocation d’un tableau de types de valeur ( struct ) stockera les données dans le tableau. Si d’autre part ItemList était un type de référence ( class ), seules les références aux ItemList seraient stockées dans le tableau. Les ItemList seraient alloués sur le tas. Un niveau supplémentaire d’indirection est requirejs pour atteindre une ItemList et comme il s’agit simplement d’un tableau combiné à une longueur, il est plus efficace d’utiliser un type de valeur.

Struct vs class

Après avoir inspecté le code pour EntitySet je peux voir qu’aucun tableau n’est impliqué. Cependant, un EntitySet contient toujours deux ItemList . Comme ItemList est une struct le stockage de ces instances est alloué à l’intérieur de l’object EntitySet . Si une class était utilisée à la place, EntitySet aurait contenu des références pointant vers les EntitySet alloués séparément.

La différence de performance entre l’utilisation de l’un ou de l’autre peut ne pas être perceptible dans la plupart des cas mais le développeur a peut-être décidé de traiter le tableau et le compte étroitement couplé comme une valeur unique.

Pour les petites structures de données internes critiques telles que ItemList , nous avons souvent le choix d’utiliser un type de référence ou un type de valeur. Si le code est bien écrit, le passage de l’un à l’autre est un changement sortingvial.

Nous pouvons supposer qu’un type de valeur évite l’allocation de tas et qu’un type de référence évite la copie de structure, de sorte qu’il ne soit pas immédiatement clair, car cela dépend beaucoup de la manière dont il est utilisé.

Le meilleur moyen de savoir lequel est le meilleur est de le mesurer . Le plus rapide est le gagnant évident. Je suis sûr qu’ils ont fait leur benchmarking et struct était plus rapide. Après avoir fait cela à quelques resockets, votre intuition est plutôt bonne et le benchmark confirme que votre choix était correct.

Peut-être que c’est important que … citation à propos de struct d’ ici

La nouvelle variable et la variable d’origine contiennent donc deux copies distinctes des mêmes données. Les modifications apscopes à une copie n’affectent pas l’autre copie .

Je pense juste, ne me juge pas dur 🙂

Il n’y a vraiment que deux raisons d’utiliser une structure, à savoir soit obtenir une sémantique de type valeur, soit améliorer les performances.

Comme la structure contient un tableau, la sémantique de type valeur ne fonctionne pas bien. Lorsque vous copiez la structure, vous obtenez une copie du count , mais vous ne recevez qu’une copie de la référence dans le tableau, et non une copie des éléments du tableau. Par conséquent, vous devrez faire particulièrement attention lorsque la structure est copiée afin de ne pas en avoir des instances incohérentes.

Donc, la seule raison valable rest la performance. Il y a un léger surcoût pour chaque instance de type de référence, donc si vous en avez beaucoup, il peut y avoir un gain de performance notable.

Une caractéristique intéressante d’une telle structure est que vous pouvez en créer un tableau et obtenir un tableau de listes vides sans avoir à initialiser chaque liste:

 ItemList[] = new ItemList[42]; 

Comme les éléments du tableau sont remplis à zéro, le membre count sera zéro et le membre items sera nul.

En spéculant purement ici:

L’object étant assez petit (il ne comporte que deux variables membres), il est un bon candidat pour en faire une struct permettant son passage en tant que ValueType .

En outre, comme @Martin Liversage le fait remarquer, en étant un ValueType il peut être stocké plus efficacement dans des structures de données plus grandes (par exemple, en tant qu’élément dans un tableau), sans avoir un object individuel et une référence.