UPDATE: Microsoft will be offering a substitute for this method in .NET Framework 4, so you could say that this post is obsolete.
I was working with Windows ACL permissions and I was attempting to find out whether a user had Read permissions to a file. Permissions are represented in the form of an enum to something of this effect:
To further complicate things, Windows permissions are represented under 2 different enum classes, System.IO.FileAccess and System.Security.AccessControl.FileSystemRights. So, in order to persist these permissions to the database, I chose to also persist the respective enum class in the form of text, and then pull it back out of the database and perform Enum.Parse() on the text.
Whenever a file had Modify permissions, there was no concise way of determining that this also implied the Read permission because I was dealing with the generic Enum class. As you can see, when you attempt bitwise operations on the Enum class, this is what you get:
Error: Operator '&' cannot be applied to operands of type 'System.Enum' and 'System.Enum'
So, in order to get around this, I chose to cast the Enums to a type that I could use bitwise operations on, which lead me to generate this function:
public static bool EnumIsCompositeOf(Enum little, Enum big) { if (!Type.Equals(little.GetType(), big.GetType())) throw new ArgumentException("Enums must be of the same type."); ulong littleVal; ulong bigVal; TypeCode typeCode = little.GetTypeCode(); switch (typeCode) { case TypeCode.Byte: case TypeCode.UInt16: case TypeCode.UInt32: case TypeCode.UInt64: littleVal = ulong.Parse(little.ToString("D")); bigVal = ulong.Parse(big.ToString("D")); break; case TypeCode.SByte: case TypeCode.Int16: case TypeCode.Int32: case TypeCode.Int64: littleVal = (ulong)long.Parse(little.ToString("D")); bigVal = (ulong)long.Parse(big.ToString("D")); break; default: throw new ArgumentException("Enums are derived from an unknown type."); } ulong intersection = littleVal & bigVal; return intersection == littleVal ? true : false; }
4 comments:
While this may work, there is a far more elegant solution: just use bitwise operations. To test if Modify contains Read in the example above, you just need to write one line:
(FilePermission.Modify & FilePermission.Read) == FilePermission.Read
That line returns true since the logical AND operation between 0001 (Read) and 0011 (Modify) returns 0001 (Read).
Mr. Anonymous,
You are absolutely correct. I totally misrepresented the context that I was working in, where this function would be handy. That context was that I didn't have a strongly typed enum to work with. What I WAS working with was the "Enum" class, which you cannot perform bitwise operations on.
Thank you for pointing out my oversight. I'll update my post accordingly.
This application seems so narrow, I don't see why you'd need a 30-line generic implementation to address the issue. I don't understand why you couldn't use the other commenter's simpler solution. Do you honestly have multiple enumerations whose values are used as bit flags, and whose underlying value types you don't know? Speaking of which, ideally you should be using the [Flags] attribute, which should also help simplify this.
Luckily, in .NET 4 you can use the Enum.HasFlag method and avoid jumping through even the hoop of the simple bitwise AND.
Also, what do you mean by "strongly typed enum"? C# enums are naturally strongly typed.
I agree with you on is that the application of this function is narrow. It's one of the first blog posts I've ever made so I cherry-picked a topic with a narrow scope thinking that people wouldn't criticize the usefulness of something that they don't plan on using or don't care about. But I digress...
When I say "strongly typed enum", I'm describing the difference between dealing directly with the enum in question verses the enum cast to the Enum class. The keyword "enum" vs. the class "Enum". You can see from the screen shot I posted that Anon's method doesn't compile when using Enum (the class).
Good observation that Microsoft will be offering a replacement to this function in .NET 4. I'll put a note at the top of my post to observe that this code snippet is obsolete. Thanks!
Post a Comment