35 votes

Pourquoi est-il acceptable que cette structure soit modifiable? Quand les structures mutables sont-elles acceptables?

Eric Lippert m'a dit que je devrais "essayez de toujours faire des types de valeur immuable", alors j'ai pensé que je devrais essayer de toujours faire des types de valeur immuable.

Mais, je viens de trouver cette mutable struct, System.Web.Util.SimpleBitVector32, en System.Web de l'assemblée, qui me fait penser qu'il doit y avoir une bonne raison pour avoir une mutable struct. Je devine la raison qu'ils n'ont cela de cette façon c'est parce qu'il fait mieux à l'essai, et ils l'ont gardé interne pour décourager son utilisation abusive. Cependant, c'est de la spéculation.

J'ai C&P, la source de cette structure. Qu'est-ce qui justifie la décision d'utiliser une mutable struct? En général, ce genre de avantages peuvent être obtenus par l'approche et quand ces avantages assez importants pour justifier les éventuels inconvénients?

[Serializable, StructLayout(LayoutKind.Sequential)]
internal struct SimpleBitVector32
{
    private int data;
    internal SimpleBitVector32(int data)
    {
        this.data = data;
    }

    internal int IntegerValue
    {
        get { return this.data; }
        set { this.data = value; }
    }

    internal bool this[int bit]
    {
        get { 
            return ((this.data & bit) == bit); 
        }
        set {
            int data = this.data;
            if (value) this.data = data | bit;
            else this.data = data & ~bit;
        }
    }

    internal int this[int mask, int offset]
    {
        get { return ((this.data & mask) >> offset); }
        set { this.data = (this.data & ~mask) | (value << offset); }
    }

    internal void Set(int bit)
    {
        this.data |= bit;
    }

    internal void Clear(int bit)
    {
        this.data &= ~bit;
    }
}

19voto

Joe White Points 32629

Étant donné que la charge utile est un entier de 32 bits, je dirais que cela pourrait facilement avoir été écrit comme une immuable struct, avec probablement aucun impact sur les performances. Si vous appelez une méthode mutateur qui modifie la valeur de 32 bits, champ, ou le remplacement d'un 32-bit struct avec un nouveau 32 bits struct, vous êtes encore en train de faire exactement les mêmes opérations de mémoire.

Probablement que quelqu'un voulait quelque chose qui agit un peu comme un tableau (alors que vraiment juste être bits en entier de 32 bits), donc ils ont décidé qu'ils voulaient utiliser l'indexeur de syntaxe avec elle, au lieu d'un moins évidentes .WithTheseBitsChanged() méthode qui renvoie une nouvelle structure. Puisqu'il n'était pas destiné à être utilisé directement par quiconque à l'extérieur de MS de l'équipe web, et probablement pas par de très nombreuses personnes, même au sein de l'équipe web, j'imagine qu'ils ont eu un peu plus de marge de manœuvre dans les décisions de conception que les personnes qui construisent l'Api publiques.

Donc, non, probablement pas de cette façon pour les performances, c'était probablement juste un programmeur à la préférence personnelle, dans le style de codage, et jamais il n'y a aucune raison impérieuse de le changer.

Si vous êtes à la recherche pour les lignes directrices de conception, je ne voudrais pas passer trop de temps à regarder le code qui n'a pas été poli pour la consommation publique.

15voto

IAbstract Points 9384

SimpleBitVector32 est mutable, je le soupçonne, pour les mêmes raisons qu' BitVector32 est mutable. À mon avis, l' immuable la ligne directrice est juste que, une ligne directrice; cependant, on devrait avoir une très bonne raison pour le faire.

Considérez aussi l' Dictionary<TKey, TValue> - je entrer dans quelques détails supplémentaires ici. Le dictionnaire de l' Entry struct est mutable - vous pouvez changer TValue à tout moment. Mais, Entry logiquement représente une valeur.

La mutabilité doit avoir un sens. Je suis d'accord avec @JoeWhite: quelqu'un voulait quelque chose qui agit un peu comme un tableau (alors que vraiment juste être bits en entier de 32 bits), et aussi que les deux BitVector structs pourrait facilement avoir été ... immuables.

Mais, comme une déclaration générale, je suis en désaccord avec c'était probablement juste un programmeur à la préférence personnelle, dans le style de codage et de se pencher plus vers il n'a jamais été [ni] aucune raison convaincante de le changer. Simplement de connaître et de comprendre la responsabilité de l'utilisation d'une mutable struct.

Modifier
Pour l'enregistrement, je le fais de bon coeur acceptez que vous devriez toujours essayer de faire une structure immuable. Si vous trouvez que les exigences de dicter membre de la mutabilité, de revoir la conception de décision et d'obtenir des pairs impliqués.

Mise à jour
Je n'ai pas été initialement confiant dans mon évaluation de la performance lors de l'examen d'une mutable valeur de type v. immuable. Cependant, comme @David souligne Eric Lippert écrit ceci:

Il ya des moments où vous avez besoin de tordre de chaque bit de la performance d'un système. Et dans ces scénarios, il est parfois nécessaire de faire une compromis entre le code qui est propre, pur, robuste , compréhensible, prévisible, modifiable et le code qui n'est pas le ci-dessus, mais incroyablement rapide.

J'ai mis en caractères gras pur , car une mutable struct ne correspond pas à l'idéal pur qu' une structure doit être immuable. Il existe de côté l'influence de l'écriture d'une mutable struct: understability et la prévisibilité sont compromis, comme Eric poursuit en expliquant:

Les types de valeur mutables se comporter ... d'une manière que beaucoup de gens trouvent profondément contraire à l'intuition, et ainsi assurez-il facile d'écrire du code bogué (ou de corriger le code qui est facilement transformé en code bogué par accident.) Mais oui, ils sont vraiment rapides.

Le point de Eric fait est que vous, en tant que concepteur et/ou le développeur a besoin pour faire un travail conscient et informé de la décision. Comment avez-vous été informé? Eric nous explique également que:

Je considère le codage de deux benchmark des solutions -- un à l'aide d' mutable structures, l'une utilisant des structures immuables -- et d'exécuter certains réaliste de l'utilisateur axés sur le scénario de tests. Mais voici la chose: ne pas choisir le plus rapide. Au lieu de cela, décider AVANT d'exécuter le test comment lent, c'est inacceptable lent.

Nous savons que la modification d'un type de valeur est plus rapide que de créer un nouveau type de valeur; mais compte tenu de l'exactitude:

Si les deux solutions sont acceptables, choisir celui qui est propre, correct et assez rapide.

L'essentiel est d'être rapide assez pour compenser les effets secondaires de choisir mutable plus immuable. Vous seul pouvez déterminer que.

15voto

Simon Mourier Points 49585

En fait, si vous recherchez pour toutes les classes contenant BitVector dans le .NET framework, vous trouverez un tas de ces bêtes :-)

  • Système.Les Collections.Spécialisés.BitVector32 (le seul public...)
  • Système.Web.Util.SafeBitVector32 (thread-safe)
  • Système.Web.Util.SimpleBitVector32
  • Système.Moment de l'exécution.La mise en cache.SafeBitVector32 (thread-safe)
  • Système.La Configuration.SafeBitVector32 (thread-safe)
  • Système.La Configuration.SimpleBitVector32

Et si vous regardez ici étaient réside la SSCLI (Microsoft Shared Source de la CLI, aka ROTOR) source du Système.La Configuration.SimpleBitVector32, vous trouverez ce commentaire:

//
// This is a cut down copy of System.Collections.Specialized.BitVector32. The
// reason this is here is because it is used rather intensively by Control and
// WebControl. As a result, being able to inline this operations results in a
// measurable performance gain, at the expense of some maintainability.
//
[Serializable()]
internal struct SimpleBitVector32

Je crois que cela en dit long. Je pense que le Système.Web.Util est plus complexe, mais construit sur le même terrain.

2voto

supercat Points 25534

À l'aide d'une structure pour un 32 ou 64 bits vecteur tel que montré ici est raisonnable, avec quelques mises en garde:

  1. Je vous conseille d'utiliser un Contrefil.CompareExchange boucle lors de l'exécution de toutes les mises à jour de la structure, plutôt que de simplement en utilisant l'ordinaire des opérateurs Booléens directement. Si un thread tente d'écrire des bits 3, tandis que l'autre essaie d'écrire de 8 bits, aucune opération ne devrait interférer avec les autres au-delà de retarder un peu. L'utilisation d'un Contrefil.CompareExchange boucle permet d'éviter la possibilité de errantes comportement (thread 1 lit de valeur, le thread 2 lit ancienne valeur, le thread 1 écrit la nouvelle valeur, le thread 2 écrit la valeur calculée sur la base de l'ancienne valeur et annule thread 1 changement) sans avoir besoin d'aucun autre type de verrouillage.
  2. La Structure des membres, autres que les accesseurs de propriété, qui modifient le "ce" doit être évité. Il est préférable d'utiliser une méthode statique qui accepte la structure comme un paramètre de référence. L'invocation d'une structure membre qui modifie "ce" est généralement identique à l'appel d'une méthode statique qui accepte le membre comme un paramètre de référence, à la fois à partir d'une sémantique et le plan de la performance, mais il y a une différence essentielle: Si l'on essaie de passer en lecture seule structure par référence à une méthode statique, on obtient une erreur de compilation. En revanche, si l'on invoque en lecture seule structure d'une méthode qui modifie "ce", il n'y aura aucune erreur de compilation, mais la modification ne se produira pas. Car même mutable structures peuvent être traités comme étant en lecture seule, dans certains contextes, il est de loin préférable d'obtenir une erreur de compilation lorsque cela arrive, que d'avoir du code qui compile mais ne fonctionne pas.

Eric Lippert aime rail sur la façon dont mutable structures sont mauvais, mais il est important de reconnaître son rapport à eux: il est l'un des gens sur le C# de l'équipe qui est chargée de faire de la langue de support de fonctionnalités tels que les fermetures et les itérateurs. En raison de certaines décisions de conception au début de la création de .net, soutenant correctement la valeur de type sémantique est difficile dans certains contextes; son travail serait beaucoup plus facile si il n'y avait pas tout les types de valeur mutables. Je n'ai pas hésiter à Eric pour son point de vue, mais il est important de noter que certains des principes qui peuvent être importantes dans le cadre et la langue de conception ne sont pas applicables à la conception de l'application.

0voto

Zenexer Points 4192

Si je comprends bien, vous ne pouvez pas créer une structure immuable sérialisable simplement en utilisant SerializableAttribute . En effet, pendant la désérialisation, le sérialiseur instancie une instance par défaut de la structure, puis définit tous les champs après l'instanciation. S'ils sont en lecture seule, la désérialisation échouera.

Ainsi, la structure devait être modifiable, sinon un système de sérialisation complexe aurait été nécessaire.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X