108 votes

Utiliser un masque de bits en C #

Disons que j'ai suivantes

int susan = 2; //0010
int bob = 4; //0100
int karen = 8; //1000

et je passe 10 (8 + 2) en tant que paramètre à une méthode et je veux décoder ce à dire que susan et karen

Je sais que 10 est de 1010

mais comment puis-je faire un peu de logique pour voir si un bit spécifique est vérifiée que dans

if (condition_for_karen) // How to quickly check whether effective karen bit is 1

Maintenant tout ce que je peux penser à est de vérifier si le nombre que j'ai adopté est

14 // 1110
12 // 1100
10 // 1010
8 //  1000

Quand j'ai un plus grand nombre de bits dans mon monde réel scénario, cela semble impossible, ce qui est une meilleure façon à l'aide d'un masque de simplement vérifier si oui ou non je remplir la condition pour seulement karen?

Je ne peux penser à décalage à gauche puis de nouveau alors décaler à droite, puis de nouveau effacer les bits d'autre que celui qui m'intéresse, mais cela semble trop complexe.

219voto

Dan Tao Points 60518

La manière traditionnelle de faire cela est d'utiliser l' Flags d'attribut sur un enum:

[Flags]
public enum Names
{
    None = 0,
    Susan = 1,
    Bob = 2,
    Karen = 4
}

Ensuite, vous devriez vérifier pour un nom particulier comme suit:

Names names = Names.Susan | Names.Bob;

// evaluates to true
bool susanIsIncluded = (names & Names.Susan) != Names.None;

// evaluates to false
bool karenIsIncluded = (names & Names.Karen) != Names.None;

Logique au niveau du bit combinaisons peuvent être difficiles à retenir, j'ai donc rendre la vie plus facile sur moi-même avec un FlagsHelper classe*:

// The casts to object in the below code are an unfortunate necessity due to
// C#'s restriction against a where T : Enum constraint. (There are ways around
// this, but they're outside the scope of this simple illustration.)
public static class FlagsHelper
{
    public static bool IsSet<T>(T flags, T flag) where T : struct
    {
        int flagsValue = (int)(object)flags;
        int flagValue = (int)(object)flag;

        return (flagsValue & flagValue) != 0;
    }

    public static void Set<T>(ref T flags, T flag) where T : struct
    {
        int flagsValue = (int)(object)flags;
        int flagValue = (int)(object)flag;

        flags = (T)(object)(flagsValue | flagValue);
    }

    public static void Unset<T>(ref T flags, T flag) where T : struct
    {
        int flagsValue = (int)(object)flags;
        int flagValue = (int)(object)flag;

        flags = (T)(object)(flagsValue & (~flagValue));
    }
}

Cela me permettrait de réécrire le code ci-dessous:

Names names = Names.Susan | Names.Bob;

bool susanIsIncluded = FlagsHelper.IsSet(names, Names.Susan);

bool karenIsIncluded = FlagsHelper.IsSet(names, Names.Karen);

Remarque, je pourrais ajouter Karen de l'ensemble en faisant ceci:

FlagsHelper.Set(ref names, Names.Karen);

Et j'ai pu supprimer Susan d'une manière similaire:

FlagsHelper.Unset(ref names, Names.Susan);

*Comme Porges souligné, un équivalent de l' IsSet méthode ci-dessus, il existe déjà dans le .NET 4.0: Enum.HasFlag. L' Set et Unset méthodes ne semblent pas avoir d'équivalents, bien que j'aimerais encore dire que cette classe a un certain mérite.


Remarque: l'Utilisation des énumérations est juste le classique de s'attaquer à ce problème. Vous pouvez tout à fait traduire le code ci-dessus pour utiliser les services de renseignements à la place et ça marchera tout aussi bien.

21voto

eldarerathis Points 14545
 if ( ( param & karen ) == karen )
{
  // Do stuff
}
 

Le bitwise 'et' masquera tout sauf le bit qui "représente" Karen. Tant que chaque personne est représentée par une position de bit unique, vous pouvez vérifier plusieurs personnes avec un simple:

 if ( ( param & karen ) == karen )
{
  // Do Karen's stuff
}
if ( ( param & bob ) == bob )
  // Do Bob's stuff
}
 

13voto

user925327 Points 63

J'ai inclus un exemple ici qui montre comment vous pouvez stocker le masque dans une colonne de la base de données en tant qu'int, et comment vous pourrez le rétablir ultérieurement:

 public enum DaysBitMask { Mon=0, Tues=1, Wed=2, Thu = 4, Fri = 8, Sat = 16, Sun = 32 }


DaysBitMask mask = DaysBitMask.Sat | DaysBitMask.Thu;
bool test;
if ((mask & DaysBitMask.Sat) == DaysBitMask.Sat)
    test = true;
if ((mask & DaysBitMask.Thu) == DaysBitMask.Thu)
    test = true;
if ((mask & DaysBitMask.Wed) != DaysBitMask.Wed)
    test = true;

// Store the value
int storedVal = (int)mask;

// Reinstate the mask and re-test
DaysBitMask reHydratedMask = (DaysBitMask)storedVal;

if ((reHydratedMask & DaysBitMask.Sat) == DaysBitMask.Sat)
    test = true;
if ((reHydratedMask & DaysBitMask.Thu) == DaysBitMask.Thu)
    test = true;
if ((reHydratedMask & DaysBitMask.Wed) != DaysBitMask.Wed)
    test = true;
 

7voto

Blindy Points 26706

Pour combiner les masques de bits que vous souhaitez utiliser, ou . Dans le cas trivial où chaque valeur que vous combinez a exactement 1 bit (comme dans votre exemple), cela revient à les ajouter. Cependant, si vous avez des bits qui se chevauchent, ou si vous les gérez, le cas est traité avec élégance.

Pour décoder les masques de bits et votre valeur avec un masque, procédez comme suit:

 if(val & (1<<1)) SusanIsOn();
if(val & (1<<2)) BobIsOn();
if(val & (1<<3)) KarenIsOn();
 

0voto

Greg Osborne Points 21

Une autre bonne raison d'utiliser un masque de bits individuels ou de bool est en tant que développeur web, lors de l'intégration d'un site à l'autre, nous avons souvent besoin d'envoyer des paramètres ou des indicateurs dans la chaîne de requête. Tant que tous vos drapeaux sont binaires, il rend beaucoup plus simple à utiliser qu'une seule valeur comme un masque de bits que d'envoyer plusieurs valeurs bool. Je sais qu'il y a sinon pour envoyer des données (GET, POST, etc.), mais un simple paramètre dans la chaîne de requête est la plupart du temps suffisant pour pas confidentiels éléments. Essayez d'envoyer 128 bool valeurs sur une chaîne de requête pour communiquer avec un site externe. Cela permet aussi à l'ajout de la possibilité de ne pas pousser la limite url querystrings dans les navigateurs

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