Hugoware

The product of a web developer with a little too much caffeine

Enums, Flags and C# — Oh my! (bad pun…)

with 12 comments

New Code! An updated blog post on this topic can be found here!

I’m not sure about everyone else, but I just love Enumerated types. Whats more, I love the Flags Attribute when you combine them together. This post explores how you can use these two things along with Extension Methods to make your code more compact and easier to understand.

If you’ve never used this combination before, then you’re missing out. Consider the following code…

class User {
    bool CanDelete;
    bool CanRead;
    bool CanWrite;
    bool CanModify;
    bool CanCreate;
}

Okay, so that’s no big deal even though it may be quite a few extra lines of code. It would be nice to be able to combine all of those permissions into a single value. That’s where an Enumerated Type with a FlagAttribute comes in.

[Flags]
enum PermissionTypes : int {
    None = 0,
    Read = 1,
    Write = 2,
    Modify = 4,
    Delete = 8
    Create = 16,
    All = Read | Write | Modify | Delete | Create
}

//and the class from before
class User {
    PermissionTypes Permissions = PermissionTypes.None;
}

Excellent…. so now what?

So now whats great about this is now we can assign multiple values onto the same property. Not only that, but we can also check for existing values with a (strange) comparison.

//create a new user
User admin = new User();
admin.Permissions = PermissionTypes.Read 
    | PermissionTypes.Write 
    | PermissionTypes.Delete;

//check for permissions
bool canRead = ((PermissionTypes.Read & admin.Permissions) == PermissionTypes.Read);
bool canWrite = ((PermissionTypes.Write & admin.Permissions) == PermissionTypes.Write);
bool canCreate = ((PermissionTypes.Create & admin.Permissions) == PermissionTypes.Create);

//and the results
Console.WriteLine(canRead); //true
Console.WriteLine(canWrite); //true
Console.WriteLine(canCreate); //false

Now shorter and easier to read — sorta. See that really odd comparison? That’s what you need to write each time you want to check for a value. It’s not that bad, but it isn’t really something I’d like to type very often. You could write a separate function to do these comparisons for you, but we can do even better than that.

Taking Advantage Of Extension Methods

Since an Enumerated type isn’t really a class you can’t extend methods onto them. However, you can extend methods onto the class System.Enum. Methods added to this class will appear in the methods for all of your enumerated types!.

Here is an example method…

//full class included at the end of the post
public static class EnumerationExtensions {

        //checks to see if an enumerated value contains a type
        public static bool Has<T>(this System.Enum type, T value) {
            try {
                return (((int)(object)type & (int)(object)value) == (int)(object)value);
            } 
            catch {
                return false;
            }
        }
}

Now, this code does make an assumption that it can cast your Enumerated Type to an integer. You could do some type checking before you do the comparisons, but for the sake of this example, we’re going to keep this short.

So just how do you use this extension?

//start with a value
PermissionTypes permissions = PermissionTypes.Read | PermissionTypes.Write;

//then check for the values
bool canRead = permissions.Has(PermissionTypes.Read); //true
bool canWrite = permissions.Has(PermissionTypes.Write); //true
bool canDelete = permissions.Has(PermissionTypes.Delete); //false

Now that is much easier to read! Even better, you’ll notice despite the fact this has a Generic parameter, we don’t have to provide the type at the start since the method can infer it from the parameter (implicitly typed parameters — sweeeeet!)

And don’t forget, System.Enum isn’t the only class you can do this with, there are other classes (like System.Array for example) that you can add your own extension methods to for surprising results!

Below is the full source code for the EnumerationExtensions class. If you have any improvements, please let me know!

namespace Enum.Extensions {

    public static class EnumerationExtensions {

        //checks if the value contains the provided type
        public static bool Has<T>(this System.Enum type, T value) {
            try {
                return (((int)(object)type & (int)(object)value) == (int)(object)value);
            } 
            catch {
                return false;
            }
        }

        //checks if the value is only the provided type
        public static bool Is<T>(this System.Enum type, T value) {
            try {
                return (int)(object)type == (int)(object)value;
            }
            catch {
                return false;
            }    
        }

        //appends a value
        public static T Add<T>(this System.Enum type, T value) {
            try {
                return (T)(object)(((int)(object)type | (int)(object)value));
            }
            catch(Exception ex) {
                throw new ArgumentException(
                    string.Format(
                        "Could not append value from enumerated type '{0}'.",
                        typeof(T).Name
                        ), ex);
            }    
        }

        //completely removes the value
        public static T Remove<T>(this System.Enum type, T value) {
            try {
                return (T)(object)(((int)(object)type & ~(int)(object)value));
            }
            catch (Exception ex) {
                throw new ArgumentException(
                    string.Format(
                        "Could not remove value from enumerated type '{0}'.",
                        typeof(T).Name
                        ), ex);
            }  
        }

    }
}
About these ads

Written by hugoware

June 13, 2009 at 2:59 am

12 Responses

Subscribe to comments with RSS.

  1. Enums, Flags and C# — Oh my! (bad pun…) « Yet Another WebDev Blog…

    Thank you for submitting this cool story – Trackback from DotNetShoutout…

    DotNetShoutout

    June 15, 2009 at 2:18 pm

  2. Hiya,
    I have tried the example above, but surprisingly I am unable to see the extended methods. ex: permissions.Has()

    Is there something that I am missing? reference / linking etc?

    Thanks,
    Kiran

    Kiran

    June 25, 2009 at 7:41 am

  3. Otherthing, is that I am using VS 2005 C#.

    it is not allowing me to compile if the methods has (this) param in the begining, as shown in your class above.

    ex:
    public static bool Has(this System.Enum type, T value)

    Kiran

    June 25, 2009 at 7:43 am

  4. The “Is” and the “Has” methods use exception handling as a flow condition. This can cause bad performance and is in general not considered a very good practice.

    Manu

    July 8, 2009 at 10:48 am

    • True, it would be a good idea to go back and add something that checks the value before it tries to cast the type.

      Thanks for the suggestion!

      webdev_hb

      July 8, 2009 at 11:00 am

  5. Very handy, Thank You.

    Wes

    August 19, 2009 at 10:28 pm

  6. Maybe I’m missing the obvious, but I don’t see you specifying the Flags attribute anywhere in this code.

    Michael Bate

    September 7, 2009 at 4:32 pm

    • Thanks for pointing it out – I’ll add it to the example.

      webdev_hb

      September 7, 2009 at 5:12 pm

  7. […] on using flags in C#.  If you don't have any knowledge in using flags in C#, check out this blog post on C# flags  I was going to go over flags in more detail, but when I found this, I felt that there was no […]

  8. […] 4 comments A while back I had posted some code about making it easier to work with enumerated types — Especially with the Flags attribute. An experienced programmer won’t have a hard time […]

  9. Good one!
    The post was very helpful for me!

    yossi

    June 8, 2010 at 7:49 am


Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

Follow

Get every new post delivered to your Inbox.

%d bloggers like this: