108

假设我有以下

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

我将 10 (8 + 2) 作为参数传递给方法,我想将其解码为 susan 和 karen

我知道10是1010

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

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

现在我能想到的就是检查我传递的数字是否是

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

当我在现实世界的场景中有大量实际位时,这似乎不切实际,有什么更好的方法使用掩码来检查我是否满足卡伦的条件?

我可以考虑向左移动然后返回然后向右移动然后返回以清除我感兴趣的位以外的位,但这似乎也过于复杂。

4

6 回答 6

218

执行此操作的传统方法是在 a 上使用Flags属性enum

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

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

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;

逻辑按位组合可能很难记住,所以我通过一个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));
    }
}

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

Names names = Names.Susan | Names.Bob;

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

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

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

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

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

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

*正如 Porges 指出的那样,IsSet.NET 4.0 中已经存在与上述方法等效的方法:Enum.HasFlag. 不过,SetandUnset方法似乎没有等价物。所以我仍然会说这门课有一些优点。


注意:使用枚举只是解决此问题的常规方法。您可以完全将上述所有代码转换为使用 int,它也可以正常工作。

于 2010-07-16T02:17:17.650 回答
21
if ( ( param & karen ) == karen )
{
  // Do stuff
}

按位“和”将屏蔽除“代表”凯伦的位之外的所有内容。只要每个人都由一个位位置表示,您就可以用一个简单的方法检查多个人:

if ( ( param & karen ) == karen )
{
  // Do Karen's stuff
}
if ( ( param & bob ) == bob )
  // Do Bob's stuff
}
于 2010-07-16T02:13:34.633 回答
17

简单的方法:

[Flags]
public enum MyFlags {
    None = 0,
    Susan = 1,
    Alice = 2,
    Bob = 4,
    Eve = 8
}

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

MyFlags f = new MyFlags();
f = MyFlags.Alice | MyFlags.Bob;

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

if(f.HasFlag(MyFlags.Alice)) { /* true */}
if(f.HasFlag(MyFlags.Eve)) { /* false */}
于 2019-08-09T19:04:57.253 回答
13

我在这里包含了一个示例,该示例演示了如何将掩码作为 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;
于 2012-05-17T10:02:49.697 回答
7

要组合您想要使用按位的位掩码。在您组合的每个值都恰好有 1 位的简单情况下(如您的示例),这相当于添加它们。但是,如果您有重叠的位,或者'ing 他们可以优雅地处理这种情况。

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

if(val & (1<<1)) SusanIsOn();
if(val & (1<<2)) BobIsOn();
if(val & (1<<3)) KarenIsOn();
于 2010-07-16T02:13:42.807 回答
0

使用位掩码与单独布尔值的另一个非常好的理由是作为 Web 开发人员,在将一个网站集成到另一个网站时,我们经常需要在查询字符串中发送参数或标志。只要您的所有标志都是二进制的,使用单个值作为位掩码就比将多个值作为布尔值发送要简单得多。我知道还有其他方法可以发送数据(GET、POST 等),但是查询字符串上的一个简单参数在大多数情况下对于非敏感项目来说就足够了。尝试在查询字符串上发送 128 个布尔值以与外部站点进行通信。这也提供了不限制浏览器中 url 查询字符串的附加功能

于 2011-02-03T19:01:56.367 回答