Running with Code

Like scissors, only more dangerous

  Home  |   Contact  |   Syndication    |   Login
  79 Posts | 0 Stories | 173 Comments | 2 Trackbacks

News



Archives

Post Categories

All Terralever

ASP.NET

Misc

OK, so I lied; I'm not stopping at 5 parts.

I've been working with enumerations frequently lately; the Battle.net chat protocol is binary and therefore the values that come over the wire have different contextual meanings based on the values that might have preceded them.  For example, a chat message event actually can have about a dozen meanings; it can be a server-broadcasted message, a message from another user, or just an announcement that a user joined the channel.  In addition to the standard values identifying things like message type, messages typically have one form or another of flags; if the event is based on a user, the flags contain information about the user's status on the server (whether the user is an administrator or has operator privileges in the channel).  Others, such as channel information updates, contain information about the chat channel itself, such as whether it is public, silent, or otherwise normal.

The Problem

Having had to deal with enumerations frequently has made me hate code like this:

   1: if (((UserFlags)e.Flags & UserFlags.ChannelOperator) == UserFlags.ChannelOperator)

Especially when working with bitwise values (enumerations decorated with the [Flags] attribute), because of the specific operator precedence constraints that C# places on the developer, this becomes annoying quickly.  So much so, that classes where I have to do that frequently end up with several TestFlag() methods, but even these are limited.  Consider code like this:

   1: bool TestFlag(UserFlags test, UserFlags reference) { ... }
   2: bool TestFlag(ChannelFlags test, ChannelFlags reference { ... }

Or this:

   1: bool TestFlag<T>(T test, T reference) {
   2:  // hard to implement since no meaningful type constraint can be placed on T
   3: }

Or this:

   1: bool TestFlag(int test, int reference) { ... }

In proposition 1 we have to implement n methods, either repeatedly or in a globally-defined, internal utility class; that stinks.  Proposition 2 is difficult to implement; we can't place a type constraint because C# doesn't allow enum type constraints, and since enums have a type constraint themselves of always being an integral value, this would be ideal; but type constraints in this case are limited to struct, which doesn't guarantee operator | or operator &.  In proposition 3, every time we want to test, we need to cast to int (or long) and lose type information.  I guess that works, but then you worry that you end up with code like this:

   1: if (TestFlag((int)e.User.Flags, (int)UserFlags.ServerAdministrator))
   2: { 
   3:     // ...
   4: } 
   5: else if (TestFlag((int)e.User.Flags, (int)UserFlags.ChannelOperator))
   6: {
   7:     // ...
   8: } // ...

No, there's a cleaner solution, and, like the compiler features added to C# 3.0, it doesn't require a new CLR: automatic properties on enumerations.

The Solution

Internally, enumerations are treated as their base numeric type by the CLR; the variable itself carries around type information, but it's not strong and can be changed by direct casting.  But the compiler always knows the type of a local variable and can apply it directly.  So, consider applying a property to an enumeration variable called IsEnumField.  Consider this [Flags] enumeration, and look at the code that uses it when using this style of coding:

   1: if (e.User.Flags.IsNone)
   2: {   } 
   3: else if (e.User.Flags.IsBlizzardRepresentative || e.User.Flags.IsBattleNetAdministrator)
   4: {   }
   5: else if (e.User.Flags.IsChannelOperator
   6: {   }
   7: else if (e.User.Flags.IsNoUDP)
   8: {   }

We can easily identify the pattern that the compiler supports; prefix "Is" to the field name and perform the underlying logic.

The great part about this solution is that the emitted code is exactly the same as what you or I would produce right now.  So the compiler can know by its clever compiler tricks to do this:

   1: if (e.User.Flags == UserFlags.None) {   }
   2: else if ((e.User.Flags & UserFlags.BlizzardRepresentative) == UserFlags.BlizzardRepresentative
   3:        || (e.User.Flags & UserFlags.BattleNetAdministrator) == UserFlags.BattleNetAdministrator) {   }
   4: else if ((e.User.Flags & UserFlags.ChannelOperator) == UserFlags.ChannelOperator) {   }
   5: else if ((e.User.Flags & UserFlags.NoUDP) == UserFlags.NoUDP) {   }

In that example, I qualified the type name UserFlags nine times.  Can you say "carpal tunnel"?

Future-Proofing

There are some considerations to make about this.  First, there are already going to be some enumerations in the wild with field names that begin with "Is," and it could very easily raise confusion if someone sees code such as user.Flags.IsIsOnline.  Fortunately, the solution is equally simple: create a decorator attribute, just like we did for extension methods:

   1: namespace System
   2: {
   3:     [AttributeUsage(AttributeTargets.Enum)]
   4:     public sealed class EnumPropertiesAttribute : Attribute { }
   5: }

Then, when you create an enumeration that you'd like to expose these style of properties, simply decorate the enumeration with this attribute.  IntelliSense knows to show the properties, the compiler knows to translate the properties, and we're in the free and clear.

Wouldn't it be great?

The C# 4.0 Wishlist series

posted on Friday, August 1, 2008 1:57 AM

Feedback

# re: My C# 4.0 Wishlist Part 6: Automatic Properties for Enum Variables 8/27/2008 7:20 AM Jakob Just
But another solution could be to add you own extensions, like:

public static bool IsNone(this class.enum.Flag flag)
{
if(flag == class.enum.Flag.None)
return true;

return false;
}

This would still give you the IntelliSense on the values, but the drawback is you would have to do one for each Flag type.

But I agree your approach would work better on large scale, especially if you have a very large solution.

# re: My C# 4.0 Wishlist Part 6: Automatic Properties for Enum Variables 8/29/2008 5:36 PM Robert Taylor
How about a more general purpose solution that won't break any existing code?

The CLR/BCL team could add the following methods to [Flags] Enum types:

public bool Contains<T>(T flag)
{
return (this & flag) == flag;
}

public bool ContainsAny<T>(IEnumerable<T> flags)
{
return flags.Any(f => Contains(f));
}

public bool ContainsAll<T>(IEnumerable<T> flags)
{
return flags.All(f => Contains(f));
}

These could then be used as:
if (e.User.Flags.Contains(UserFlags.None))
{ }
else if (e.User.Flags.ContainsAny(UserFlags.BlizzardRepresentative, UserFlags.BattleNetAdministrator))
{ }
else if (e.User.Flags.Contains(UserFlags.ChannelOperator))
{ }
else if (e.User.Flags.Contains(UserFlags.NoUDP))
{ }

# re: My C# 4.0 Wishlist Part 6: Automatic Properties for Enum Variables 9/2/2008 9:09 AM Rob
Hi Robert,

I don't believe my solution breaks any existing code. The intent is to function exactly as C# 3.0's additions worked on the 2.0 CLR; they're compiler tricks. It doesn't involve any changes to the BCL and doesn't require CLR changes.

My suggestion is implemented within the compiler:

e.User.Flags.IsChannelOperator

is converted by the compiler to the expression:

(e.User.Flags & UserFlags.ChannelOperator) == UserFlags.ChannelOperator

No existing code is broken since the normal equality operator is still valid, and no BCL changes need to be shipped. No CLR changes need to be shipped. (Of course, your suggestion can be implemented in the compiler as well).

Post A Comment
Title:
Name:
Email:
Comment:
Verification: