Pourquoi utiliser des drapeaux + bitmasks plutôt qu'une série de booléens?

StackOverflow https://stackoverflow.com/questions/1406554

  •  05-07-2019
  •  | 
  •  

Question

Étant donné que j'ai un objet qui peut être dans un ou plusieurs états true / false, j'ai toujours été un peu flou sur la raison pour laquelle les programmeurs utilisent fréquemment les options flags + bitmasks au lieu d'utiliser plusieurs valeurs booléennes.

Tout est sur le framework .NET. Vous ne savez pas si c'est le meilleur exemple, mais le framework .NET présente les caractéristiques suivantes:

public enum AnchorStyles
{
    None = 0,
    Top = 1,
    Bottom = 2,
    Left = 4,
    Right = 8
}

Donc, avec un style d'ancrage, nous pouvons utiliser des masques de bits pour déterminer quel état est sélectionné. Cependant, il semble que vous puissiez accomplir la même chose avec une classe / structure AnchorStyle avec des propriétés bool définies pour chaque valeur possible, ou un tableau de valeurs enum individuelles.

Bien sûr, la raison principale de ma question est que je me demande si je devrais suivre une pratique similaire avec mon propre code.

Alors, pourquoi utiliser cette approche?

  • Moins de consommation de mémoire? ( il ne semble pas qu'il consomme moins qu'un tableau / structure de bools)
  • Meilleures performances pile / tas qu’une structure ou un tableau?
  • Opérations de comparaison plus rapides? Ajout / suppression de valeur plus rapide?
  • Plus pratique pour le développeur qui l'a écrit?
Était-ce utile?

La solution

C’était traditionnellement un moyen de réduire l’utilisation de la mémoire. Donc, oui, c'est plutôt obsolète en C #: -)

En tant que technique de programmation, il est peut-être obsolète dans les systèmes actuels, et vous seriez bien d'accord pour utiliser un tableau de bools, mais ...

Il est rapide de comparer les valeurs stockées sous forme de masque de bits. Utilisez les opérateurs logiques AND et OR et comparez les 2 ints résultants.

Il utilise beaucoup moins de mémoire. Mettre les 4 valeurs de votre exemple dans un masque de bits prendrait un demi-octet. En utilisant un tableau de bools, le plus susceptible d'utiliser quelques octets pour l'objet tableau plus un long mot pour chaque bool. Si vous devez stocker un million de valeurs, vous verrez exactement pourquoi une version à masque de bits est supérieure.

C’est plus facile à gérer, il suffit de traiter avec un seul entier, alors qu’un tableau de bools se stockerait différemment dans, disons une base de données.

Et, en raison de la disposition de la mémoire, beaucoup plus rapide dans tous les aspects qu’un tableau. C'est presque aussi rapide que d'utiliser un seul entier 32 bits. Nous savons tous que cela est aussi rapide que vous pouvez obtenir pour les opérations sur les données.

Autres conseils

  • Configuration facile de plusieurs indicateurs dans n’importe quel ordre.

  • Facile à enregistrer et obtenir une série de 0101011 dans la base de données.

Entre autres choses, il est plus facile d'ajouter de nouvelles significations de bits à un champ de bits que d'ajouter de nouvelles valeurs booléennes à une classe. Il est également plus facile de copier un champ de bits d'une instance à une autre qu'une série de booléens.

Cela peut également rendre les méthodes plus claires. Imaginez une méthode avec 10 bools vs 1 Bitmask.

En fait, les performances peuvent être meilleures, principalement si votre enum provient d’un octet. Dans ce cas extrême, chaque valeur enum serait représentée par un octet, contenant toutes les combinaisons, jusqu'à 256. Avoir autant de combinaisons possibles avec des booléens conduirait à 256 octets.

Mais, même dans ce cas, je ne pense pas que ce soit la vraie raison. La raison pour laquelle je préfère ceux-ci est le pouvoir que C # me donne pour gérer ces enums. Je peux ajouter plusieurs valeurs avec une seule expression. Je peux les enlever aussi. Je peux même comparer plusieurs valeurs à la fois avec une seule expression en utilisant l'énum. Avec des booléens, le code peut devenir, disons, plus verbeux.

Raymond Chen a un billet de blog sur ce sujet .

  

Bien sûr, les champs de bits sauvegardent la mémoire de données, mais   vous devez équilibrer cela avec le   coût en taille de code, débogage, et   multithreading réduit.

Comme d’autres l’ont dit, son temps est largement passé. C’est tentant de continuer à le faire, car il est amusant de sembler amusant, mais il n’est plus plus efficace, il présente de sérieux inconvénients en termes de maintenance, il ne fonctionne pas bien avec les bases de données, et à moins que vous ne travailliez dans une monde intégré, vous avez assez de mémoire.

Je suggérerais de ne jamais utiliser de drapeaux enum, sauf si vous avez affaire à des limitations de mémoire assez sérieuses (peu probables). Vous devez toujours écrire du code optimisé pour la maintenance.

Le fait d’avoir plusieurs propriétés booléennes facilite la lecture et la compréhension du code, la modification des valeurs et la fourniture de commentaires Intellisense, sans parler de réduire le risque de bogues. Si nécessaire, vous pouvez toujours utiliser un champ d'indicateur enum en interne, assurez-vous simplement d'exposer le réglage / l'obtention des valeurs avec les propriétés booléennes.

Du point de vue des modèles de domaine, il permet simplement de mieux représenter la réalité dans certaines situations. Si vous avez trois booléens tels que AccountIsInDefault et IsPreferredCustomer et REQUSalesTaxState, il n’a aucun sens de les ajouter à une seule énumération Drapés et décorés, car ils ne constituent pas trois valeurs distinctes pour le même élément de modèle de domaine.

Mais si vous avez un ensemble de booléens comme:

 [Flags] enum AccountStatus {AccountIsInDefault=1, 
         AccountOverdue=2 and AccountFrozen=4}

ou

  [Flags] enum CargoState {ExceedsWeightLimit=1,  
         ContainsDangerousCargo=2, IsFlammableCargo=4, 
         ContainsRadioactive=8}

Ensuite, il est utile de pouvoir stocker l’état total du compte (ou de la cargaison) dans UNE variable ... qui représente UN élément de domaine dont la valeur peut représenter toute combinaison d’états possible.

  1. Efficacité spatiale - 1 bit
  2. Gain de temps - les comparaisons de bits sont gérées rapidement par le matériel.
  3. Indépendance linguistique - lorsque les données peuvent être traitées par un certain nombre de programmes différents, vous n'avez pas à vous soucier de la mise en œuvre des booléens dans différentes langues / plates-formes.

La plupart du temps, ils ne valent pas la peine d'être compensés en termes de maintenance. Cependant, il y a des moments où cela est utile:

  1. Protocoles réseau - vous réduirez considérablement la taille des messages
  2. Logiciel hérité - une fois, j’ai dû ajouter des informations pour pouvoir les retrouver dans certains logiciels hérités.

Coût de modification de l'en-tête: millions de dollars et années d'effort. Coût pour mettre les informations sur 2 octets dans l’entête non utilisés: 0.

Bien sûr, il y avait un coût supplémentaire dans le code qui accédait à ces informations et les manipulait, mais celles-ci étaient effectuées par des fonctions de sorte qu'une fois que vous aviez défini les accesseurs, elles n'étaient pas moins faciles à gérer que l'utilisation de booléens.

C’est pour la rapidité et l’efficacité. En gros, vous ne travaillez qu'avec un seul int.

if ((flags & AnchorStyles.Top) == AnchorStyles.Top)
{
    //Do stuff
} 
Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top