In .NET, custom enum types are of value type so they cannot be extended. This can create an issue if you want to include an enum in your framework API, where you wish that the enum could be extended by the applications that are using the framework. After examining the implementation of System.Enum, I came up with the following implementation. I haven't googled thoroughly yet so please no flames if someone has done something similar :-) Also note the code posted is POC quality and I am looking to improve it. [Serializable()] public abstract class EnumEx<T> where T : struct { // holds the actual value protected T _t; // default constructor protected EnumEx() { _t = default(T); } // constructor that takes the enum value protected EnumEx(T t) { _t = t; } // performs an implicit conversion to the underlying value public static implicit operator T(EnumEx<T> obj) { return obj._t; } // parses a string to the specified type, returns null if no string match is defined // this is a case-sensitive version public static object Parse(string s, Type type) { FieldInfo[] fis = type.GetFields(BindingFlags.Static | BindingFlags.Public); foreach (FieldInfo fi in fis) { if (s.Equals(fi.Name)) { object obj = fi.GetValue(null); return obj; } } return null; } // System.Object overrides public override int GetHashCode() { return _t.GetHashCode(); } public override string ToString() { FieldInfo[] fis = this.GetType().GetFields(BindingFlags.Static | BindingFlags.Public); foreach (FieldInfo fi in fis) { object obj = fi.GetValue(this); T t = ((EnumEx<T>)obj)._t; if (_t.Equals(t)) return fi.Name; } return string.Empty; } } public class LogType : EnumEx<int> { protected LogType() : base() { } protected LogType(int value) : base(value) { } public static implicit operator LogType(int i) { return new LogType(i); } public static LogType Info = 1; public static LogType Warning = 2; public static LogType Error = 3; } public class LogTypeEx : LogType { public static LogType Verbose = 4; } LogType logType = LogType.Error; Console.WriteLine(logType.ToString()); LogType logType2 = LogType.Parse("Warning", typeof(LogType)) as LogType; if (logType2.Equals(LogType.Warning)) Console.WriteLine("Parsing succeeds"); int i = logType2; if (i == 2) Console.WriteLine("Implicit conversion to underlying type succeeds"); switch (logType2) { case 2: Console.WriteLine("Do logic based on LogType.Info"); break; } switch (logType2) { case LogType.Info: Console.WriteLine("Do logic based on LogType.Info"); break; }
First I will start off with a base class EnumEx
This can be how a framework enum is defined:
And if the application wishes to extend the enum:
If I write the following test code:
I will get the following result in the console:
Error
Parsing succeeds
Implicit conversion to underlying type succeeds
So you see that this extensible enum type is pretty straightforward to use, extending the enum is very easy and it achieves pretty much what the native enum type has to offer. One thing left to be desired though is an issue using switch statement with C# compiler. Only the following works:
while this doesn't compile (it fails at the case statement):
The C# compiler requires a constant in the case statement. It's also picky about what's in the switch statement. Without the implicit operator, the first switch code snippet wouldn't even have compiled either.
A Simple Design Pattern for Extensible Enumeration Types
Posted by Hugh Ang at 5/15/2007 12:50:00 PMThis entry was posted on 5/15/2007 12:50:00 PM . You can follow any responses to this entry through the RSS 2.0 feed. You can leave a response, or trackback from your own site.
Subscribe to:
Post Comments (Atom)
2 comments:
Very neat implementation. Thank you!
All you need to do to make the switch statement compile is change your 'static' public fields to 'const'.
Post a Comment