在 C# 中使用位掩码

发布于 2024-09-09 19:38:24 字数 538 浏览 3 评论 0原文

假设我有以下内容

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

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

我知道 10 是 1010

,但是我如何做一些逻辑来查看特定的位被检查,

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

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

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

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

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

Let's say I have the following

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

and I pass 10 (8 + 2) as a parameter to a method and I want to decode this to mean susan and karen

I know that 10 is 1010

but how can I do some logic to see if a specific bit is checked as in

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

Right now all i can think of is to check whether the number i passed is

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

When I have a larger number of actual bits in my real world scenario, this seems impractical, what is a better way using a mask to just check whether or not I meet the condition for just karen?

I can think of shifting left then back then shifting right then back to clear bits other than the one I'm interested in, but this also seems overly complex.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(6

肩上的翅膀 2024-09-16 19:38:25

简单方法:

[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 */}

Easy Way:

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

To set the flags use logical "or" operator |:

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

And to check if a flag is included use HasFlag:

if(f.HasFlag(MyFlags.Alice)) { /* true */}
if(f.HasFlag(MyFlags.Eve)) { /* false */}
十年不长 2024-09-16 19:38:25
if ( ( param & karen ) == karen )
{
  // Do stuff
}

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

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

The bitwise 'and' will mask out everything except the bit that "represents" Karen. As long as each person is represented by a single bit position, you could check multiple people with a simple:

if ( ( param & karen ) == karen )
{
  // Do Karen's stuff
}
if ( ( param & bob ) == bob )
  // Do Bob's stuff
}
浅暮の光 2024-09-16 19:38:25

我在这里提供了一个示例,演示了如何将掩码作为 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;

I have included an example here which demonstrates how you might store the mask in a database column as an int, and how you would reinstate the mask later on:

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;
浮世清欢 2024-09-16 19:38:25

要组合位掩码,您需要使用按位-。在简单的情况下,您组合的每个值都恰好有 1 位(如您的示例),这相当于将它们相加。然而,如果您有重叠的位,对它们进行处理可以优雅地处理这种情况。

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

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

To combine bitmasks you want to use bitwise-or. In the trivial case where every value you combine has exactly 1 bit on (like your example), it's equivalent to adding them. If you have overlapping bits however, or'ing them handles the case gracefully.

To decode the bitmasks you and your value with a mask, like so:

if(val & (1<<1)) SusanIsOn();
if(val & (1<<2)) BobIsOn();
if(val & (1<<3)) KarenIsOn();
谎言 2024-09-16 19:38:25

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

One other really good reason to use a bitmask vs individual bools is as a web developer, when integrating one website to another, we frequently need to send parameters or flags in the querystring. As long as all of your flags are binary, it makes it much simpler to use a single value as a bitmask than send multiple values as bools. I know there are otherways to send data (GET, POST, etc.), but a simple parameter on the querystring is most of the time sufficient for nonsensitive items. Try to send 128 bool values on a querystring to communicate with an external site. This also gives the added ability of not pushing the limit on url querystrings in browsers

杀お生予夺 2024-09-16 19:38:24

传统的方法是在 enum 上使用 Flags 属性:

[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 指出的,.NET 4.0 中已经存在与上面 IsSet 方法等效的方法:< a href="http://msdn.microsoft.com/en-us/library/system.enum.hasflag.aspx" rel="noreferrer">Enum.HasFlag。不过,SetUnset 方法似乎没有等效项;所以我仍然认为这个类有一些优点。


注意:使用枚举只是解决这个问题的传统方法。您完全可以将上述所有代码转换为使用整数,它也能正常工作。

The traditional way to do this is to use the Flags attribute on an enum:

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

Then you'd check for a particular name as follows:

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;

Logical bitwise combinations can be tough to remember, so I make life easier on myself with a FlagsHelper class*:

// 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));
    }
}

This would allow me to rewrite the above code as:

Names names = Names.Susan | Names.Bob;

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

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

Note I could also add Karen to the set by doing this:

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

And I could remove Susan in a similar way:

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

*As Porges pointed out, an equivalent of the IsSet method above already exists in .NET 4.0: Enum.HasFlag. The Set and Unset methods don't appear to have equivalents, though; so I'd still say this class has some merit.


Note: Using enums is just the conventional way of tackling this problem. You can totally translate all of the above code to use ints instead and it'll work just as well.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文