Click here to Skip to main content
6,822,613 members and growing! (20,785 online)
Email Password   helpLost your password?
Languages » C# » Enumerations     Beginner License: The Code Project Open License (CPOL)

Custom Flag "Enum" Using Binary Numbers

By Joe Enos

An unnecessarily complicated way of defining an object similar to a flags enum with binary numbers as values.
C#, .NET, Dev
Revision:2 (See All)
Posted:24 Nov 2009
Views:2,033
Bookmarked:11 times
printPrint   add Share
      Discuss Discuss   Broken Article?Report  
7 votes for this article.
Popularity: 2.38 Rating: 2.81 out of 5
1 vote, 14.3%
1
2 votes, 28.6%
2
2 votes, 28.6%
3
2 votes, 28.6%
4

5

Introduction

C# does not include the ability to declare numbers using binary format. Only decimal and hexadecimal are provided. Why they decided to provide hex and not binary or octal, who knows, but if we want the ability to use binary representations of numbers, we have to do it ourselves. This is no big deal, but there's one specific situation I know of that binary would be very useful: enumerations as flags.

I'm going to describe a ridiculously unnecessary method that will allow you to define numbers as binary for an object similar to an enum with flags. I absolutely would not do this in real life - there's no real benefit, other than saving two seconds of figuring out how to list out powers of 2 in decimal or hex. It's just an interesting exercise that shows that binary numbers would be really nice in the language.

Background

If you're unfamiliar with flag-enums, the very basics are pretty straightforward: each enum value has an underlying value of a power of 2, so that each of them represents a unique bit. You must define the values in either decimal or hexadecimal. For example:

[Flags]
public enum Permissions {
    Select = 1, // 00000001
    Insert = 2, // 00000010
    Delete = 4, // 00000100
    Update = 8, // 00001000
    Backup = 16 // 00010000
}
// or
[Flags]
public enum Permissions {
    Select = 0x01, // 00000001
    Insert = 0x02, // 00000010
    Delete = 0x04, // 00000100
    Update = 0x08, // 00001000
    Backup = 0x10  // 00010000
}

The idea behind this is that you could combine values using bitwise operators - in the example above, a user might have a permission of Select and Update, but not Insert or Delete or Backup. The user's security level could be represented in binary as 0001 | 1000 = 1001 (9 in decimal). In order to determine if you are able to do a certain thing, you use the bitwise AND against that value, and the result will either be zero or the value itself. For example, to see if you can insert, call 0010 & 1001, and the result is 0000. To see if you can update, call 1000 & 1001, and the result is 1000, so you can update. In code:

var userPermissions = Permissions.Select | Permissions.Update;
bool canInsert = ((Permissions.Insert & userPermissions) == Permissions.Insert); // false
bool canUpdate = ((Permissions.Update & userPermissions) == Permissions.Update); // true

This is all simple enough. But wouldn't it be nice if you could have defined the binary values in the enum as binary numbers, to more clearly see the bits? Something like:

[Flags]
public enum Permissions {
    /* will not compile */
    Select = 0b00000001,
    Insert = 0b00000010,
    Delete = 0b00000100,
    Update = 0b00001000,
    Backup = 0b00010000
}

The following code will "solve" this problem.

Implementation

The first thing we need to do is to create a BinaryNumber structure, which is basically just a wrapper around an Int64 (a.k.a. long). While C# doesn't let us represent numbers as binary, it does provide simple ways of converting numbers to a binary representation, and vice versa. So this struct is pretty straightforward.

public struct BinaryNumber {
    private readonly long _value;

    public BinaryNumber(long value) {
        _value = value;
    }

    public BinaryNumber(string value) {
        if (value == null) {
            throw new ArgumentNullException("value");
        }
        if (Array.Exists(value.ToCharArray(), c => ((c != '0') && (c != '1')))) {
            throw new ArgumentException("String must contain only 0's and 1's");
        }
        if (value.Length > 64) {
            throw new ArgumentException("Must be no more than 64 bits");
        }
        _value = Convert.ToInt64(value, 2);
    }

    public long Value { get { return _value; } }

    public string ToBinaryString() {
        return ToBinaryString(1);
    }

    public string ToBinaryString(int numBinaryDigits) {
        return Convert.ToString(Value, 2).PadLeft(numBinaryDigits, '0');
    }

    public override string ToString() {
        return ToString(1);
    }

    public string ToString(int numBinaryDigits) {
        return string.Format("[Binary: {0}, Decimal: {1}]", 
                             ToBinaryString(numBinaryDigits), Value);
    }

    public static implicit operator long(BinaryNumber binaryNumber) {
        return binaryNumber.Value;
    }

    public static implicit operator BinaryNumber(long i) {
        return new BinaryNumber(i);
    }

    public static implicit operator string(BinaryNumber binaryNumber) {
        return binaryNumber.ToBinaryString();
    }

    public static implicit operator BinaryNumber(string s) {
        return new BinaryNumber(s);
    }
}

Notice the operators - these allow you to switch back and forth easily between BinaryNumber and string, or BinaryNumber and long.

So now, problem solved, right? We can just set the enum values to BinaryNumber values, right? Unfortunately, no. Enum values must be constant, and you can't have a constant BinaryNumber object. So we've still got work to do.

There's a design pattern out there to make classes out of what would otherwise be enums, in order to extend the functionality while providing a reasonably consistent coding experience. Rupert Benbrook provides a good implementation here: http://phazed.com/blog/flexible-enumerations/. The following class is based on that concept, but implemented a little differently to serve our needs.

public abstract class CustomFlags {
    private readonly long _value;

    protected CustomFlags(BinaryNumber binaryNumber) {
        _value = binaryNumber;
    }

    public long Value { get { return _value; } }

    public static implicit operator long(CustomFlags customFlags) {
        return customFlags.Value;
    }

    public static long operator |(CustomFlags f1, CustomFlags f2) {
        return (f1.Value | f2.Value);
    }

    public static long operator &(CustomFlags f1, CustomFlags f2) {
        return (f1.Value & f2.Value);
    }
}

This acts as the base class for an enumeration that we will build. Notice the operators that provide bitwise AND and OR onto any two CustomFlags objects.

Now, let's rebuild the Permissions enum as a class:

public class Permissions : CustomFlags {
    private Permissions(BinaryNumber binaryNumber)
        : base(binaryNumber)
    { }

    public static implicit operator Permissions(string s) {
        return new Permissions(s);
    }

    public static readonly Permissions Select = "00000001";
    public static readonly Permissions Insert = "00000010";
    public static readonly Permissions Delete = "00000100";
    public static readonly Permissions Update = "00001000";
    public static readonly Permissions Backup = "00010000";
}

Success! We now have our "enum" values represented as binary (kind of). We can now call the same code from above:

var userPermissions = Permissions.Select | Permissions.Update;
// will return false
bool canInsert = ((Permissions.Insert & userPermissions) == Permissions.Insert);
// will return true
bool canUpdate = ((Permissions.Update & userPermissions) == Permissions.Update);

One side effect of doing things this way is that bitwise AND and OR operators have to return long, not a Permissions object. Unless you want to define the AND and OR operators at the concrete class level, this is just something you have to deal with - not really a big deal.

We can define more "enums" the same way - we just need a constructor that references the base, and an operator that converts a string into the object (we don't really need the operator, but it makes it nicer to look at).

There are other operators you may want, to cover bit-shifting or other fun stuff you can do with bits. The AND and OR are just the two easiest to picture.

Conclusion

This was a lot of code to serve a pretty silly requirement. If you just want a flag enum, then just build it and use integer constants and pay attention to what you're doing. But any opportunity to look at things from a different point of view can be useful - hopefully this article provides you something interesting, noteworthy, or is at least not a complete and utter waste of time.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)

About the Author

Joe Enos


Member
Joe Enos is a software engineer in Phoenix, Arizona, with 5 years of .NET experience. Currently working as a software development supervisor over a small team in Scottsdale.
Occupation: Software Developer (Senior)
Company: American Traffic Solutions
Location: United States United States

Other popular C# articles:

Article Top
You must Sign In to use this message board.
FAQ FAQ 
 
Noise Tolerance  Layout  Per page   
 Msgs 1 to 8 of 8 (Total in Forum: 8) (Refresh)FirstPrevNext
GeneralMy vote of 1 Pinmemberjharrison7:25 25 Nov '09  
General2 ? PinmemberAlex Fr3:51 25 Nov '09  
GeneralRe: 2 ? PinmemberJoe Enos4:28 25 Nov '09  
GeneralMy vote of 2 PinmemberMichael B. Hansen2:29 25 Nov '09  
GeneralMy vote of 2 PinmemberPaw Jershauge1:50 25 Nov '09  
GeneralRe: My vote of 2 PinmemberJoe Enos4:26 25 Nov '09  
GeneralAnother trick PinmemberAlex Fr23:36 24 Nov '09  
GeneralRe: Another trick PinmemberJoe Enos4:22 25 Nov '09  

General General    News News    Question Question    Answer Answer    Joke Joke    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads.

PermaLink | Privacy | Terms of Use
Last Updated: 24 Nov 2009
Editor: Smitha Vijayan
Copyright 2009 by Joe Enos
Everything else Copyright © CodeProject, 1999-2010
Web17 | Advertise on the Code Project