一尘不染

在C#中使用位掩码

c#

假设我有以下内容

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

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

我可以想到先左移然后再右移然后再清除以清除除我感兴趣的位以外的其他位,但这似乎过于复杂。


阅读 367

收藏
2020-05-19

共1个答案

一尘不染

传统方法是在上使用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。不过,SetUnset方法似乎没有等效项。所以我仍然会说这堂课有一些优点。


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

2020-05-19