在C#中使用位掩码

Mat*_*att 91 c# bitmask

假设我有以下内容

int susan = 2; //0010
int bob = 4; //0100
int karen = 8; //1000
Run Code Online (Sandbox Code Playgroud)

我将10(8 + 2)作为参数传递给方法,我想将其解码为苏珊和卡伦

我知道10是1010

但是我如何做一些逻辑来查看是否检查了特定位

if (condition_for_karen) // How to quickly check whether effective karen bit is 1
Run Code Online (Sandbox Code Playgroud)

现在,我能想到的是检查我通过的号码是否正确

14 // 1110
12 // 1100
10 // 1010
8 //  1000
Run Code Online (Sandbox Code Playgroud)

当我在我的真实场景中拥有更多的实际比特时,这似乎是不切实际的,使用掩码来检查我是否满足karen条件的更好方法是什么?

我可以想到向左移动然后向后移动然后向右移动然后移回到除了我感兴趣的那个之外的其他位置,但这似乎也过于复杂.

Dan*_*Tao 185

传统的方法是使用以下Flags属性enum:

[Flags]
public enum Names
{
    None = 0,
    Susan = 1,
    Bob = 2,
    Karen = 4
}
Run Code Online (Sandbox Code Playgroud)

然后,您将检查特定名称,如下所示:

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;
Run Code Online (Sandbox Code Playgroud)

逻辑按位组合可能很难记住,因此我通过FlagsHelper类*使自己的生活变得更轻松:

// 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));
    }
}
Run Code Online (Sandbox Code Playgroud)

这将允许我将上面的代码重写为:

Names names = Names.Susan | Names.Bob;

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

bool karenIsIncluded = FlagsHelper.IsSet(names, Names.Karen);
Run Code Online (Sandbox Code Playgroud)

注意我也可以Karen通过这样做添加到集合:

FlagsHelper.Set(ref names, Names.Karen);
Run Code Online (Sandbox Code Playgroud)

我可以Susan用类似的方式删除:

FlagsHelper.Unset(ref names, Names.Susan);
Run Code Online (Sandbox Code Playgroud)

*正如Porges指出的那样,IsSet.NET 4.0中已存在上述方法的等价物:Enum.HasFlag.但是,SetUnset方法似乎没有等价物; 所以我仍然说这堂课有一些优点.


注意:使用枚举只是解决此问题的传统方法.您可以完全翻译所有上述代码以使用ints,它也可以正常工作.

  • 这是内置的,你不需要你的帮助方法......`var susanIsIncluded = names.HasFlag(Names.Susan);` (26认同)
  • +1是第一个实际工作的代码.你也可以做`(names&Names.Susan)== Names.Susan`,它不需要`None`. (13认同)
  • 注意,使用`names.HasFlag(Names.Susan)`就像`(names&Names.Susan)== Names.Susan`,它们并不总是像`(names&Names.Susan)!= Names.None`.例如,如果你要检查`names.HasFlag(Names.none)`或`names.HasFlag(Names.Susan | Names.Karen)` (5认同)
  • @Matthew:哦,是的,好点。我想我只是养成了总是为所有枚举定义“None”值的习惯,因为我发现它在许多情况下都很方便。 (2认同)
  • @Porges:哇,不知道我是怎么错过的……谢谢你指出这一点!(看起来它只在 .NET 4.0 中可用,不过......而且,`Set` 方法没有等价物。所以,我想说辅助方法至少不是*完全*毫无价值的。) (2认同)

eld*_*his 20

if ( ( param & karen ) == karen )
{
  // Do stuff
}
Run Code Online (Sandbox Code Playgroud)

按位'和'将屏蔽除"代表"Karen的位之外的所有内容.只要每个人都由一个位位置代表,您就可以通过简单的方式检查多个人:

if ( ( param & karen ) == karen )
{
  // Do Karen's stuff
}
if ( ( param & bob ) == bob )
  // Do Bob's stuff
}
Run Code Online (Sandbox Code Playgroud)

  • 我建议将“// Do stuff”更改为“//让她和你的经理谈谈” (3认同)

A-S*_*ani 14

简单的方法:

[Flags]
public enum MyFlags {
    None = 0,
    Susan = 1,
    Alice = 2,
    Bob = 4,
    Eve = 8
}
Run Code Online (Sandbox Code Playgroud)

要设置标志,请使用逻辑“或”运算符|

MyFlags f = new MyFlags();
f = MyFlags.Alice | MyFlags.Bob;
Run Code Online (Sandbox Code Playgroud)

并检查是否包含标志使用HasFlag

if(f.HasFlag(MyFlags.Alice)) { /* true */}
if(f.HasFlag(MyFlags.Eve)) { /* false */}
Run Code Online (Sandbox Code Playgroud)

  • 其他答案中未提供使用“HasFlag()”和“[Flags]”的简单示例。 (3认同)

Nic*_*ght 11

我在这里包含了一个示例,演示了如何将掩码作为int存储在数据库列中,以及如何在以后恢复掩码:

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;
Run Code Online (Sandbox Code Playgroud)


Bli*_*ndy 7

要组合您想要按位使用的位掩码.在简单的情况下,你组合的每个值都只有1位(就像你的例子),它相当于添加它们.但是,如果你有重叠位,或者他们优雅地处理案例.

要使用掩码解码您您的值的位掩码,如下所示:

if(val & (1<<1)) SusanIsOn();
if(val & (1<<2)) BobIsOn();
if(val & (1<<3)) KarenIsOn();
Run Code Online (Sandbox Code Playgroud)

  • 在 C# 中不能将整数用作布尔值。 (2认同)