tags:

views:

140

answers:

5

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.

A: 
[Flags]
public enum Names
{
    Susan = 2,
    Bob = 4,
    Karen = 8
}

//......

Names value = (Names.Susan & Names.Karen);
//.....



if (value == (Names.Susan & Names.Karen))
{
    //do something...
}
Millionbonus
both those conditions would evaluate to false..
Blindy
Actually, value is set to `0`, and then compared against `0`, so the condition is in fact true. Almost certainly not what was intended though.
Ben Voigt
@Ben, Millionbonus has changed the question since Blindy's comment. Actually, though, the original code didn't even compile.
Matthew Flaschen
Nothing like an edit that fails to solve anything...
Ben Voigt
A: 

First, I wouse first use an enum instead of int.

But to check for it

if (value & karen) { ... }
Daniel A. White
This doesn't work in C#. Neither numbers nor enums can be implicitly used as booleans.
Matthew Flaschen
+3  A: 
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
}
eldarerathis
A: 

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();
Blindy
+6  A: 

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.

Dan Tao
Matthew Flaschen
@Matthew: Oh yeah, good point. I guess I just get in the habit of always defining a `None` value for all my enums, as I find it ends up being convenient in many scenarios.
Dan Tao
This is built in, you don't need your helper methods... `var susanIsIncluded = names.HasFlag(Names.Susan);`
Porges
@Porges: Wow, no idea how I missed that... thanks for pointing that out! (Looks like it's only available as of .NET 4.0, though... also, there's no equivalent for the `Set` method. So, I'd say the helper methods are at least not *totally* worthless.)
Dan Tao
`"also, there's no equivalent for the Set method"` I think this is probably because set is much easier to write out than `HasFlag`: `names | Names.Susan`. Unset gets more complicated, so I don't know why they didn't just include all of them...
Porges
@Porges: Well, I never argued that the code in these methods was difficult to *write out*. It's more that whenever these kinds of operations are needed, I have to stop for a second and actually think about bits before I remember how they work. By abstracting these into methods, like I said, "I make life easier" for myself. Plus, as you hinted, the `Set` method's really more there for completeness than anything else.
Dan Tao