Created
July 11, 2011 17:47
-
-
Save slovely/1076365 to your computer and use it in GitHub Desktop.
Type-safe Enumerations in c#
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
public class MyType : Enumeration | |
{ | |
//don't allow type to be constructed | |
private MyType(int value, string displayName) : base(value, displayName) { } | |
public static readonly MyType Type1 = new MyType(1, "Type One"); | |
public static readonly MyType Type2 = new MyType(2, "Type Two"); | |
public static readonly MyType DerivedType3 = new DerivedMyType(3, "Type Three"); | |
public virtual int GetSomethingAboutThisType() | |
{ | |
//Can implement some logic here (which could switch on this.Value if you really wanted) | |
if (Value == 1) | |
return 10; | |
return 42; | |
} | |
//Or we can move logic to derived types to make it cleaner (cos it's a private nested class | |
//no one else can see it as well which is good, and we only expose it as the base 'MyType' type) | |
private class DerivedMyType : MyType | |
{ | |
public DerivedMyType(int value, string displayName) : base(value, displayName) { } | |
public override int GetSomethingAboutThisType() | |
{ | |
return 150; | |
} | |
} | |
} | |
//based on Jimmy Bogard's post here: http://lostechies.com/jimmybogard/2008/08/12/enumeration-classes/ | |
//But modified to lose the "new ()" where clause as it's not needed. | |
public abstract class Enumeration : IComparable | |
{ | |
private readonly int _value; | |
private readonly string _displayName; | |
protected Enumeration() | |
{ | |
} | |
protected Enumeration(int value, string displayName) | |
{ | |
_value = value; | |
_displayName = displayName; | |
} | |
public int Value | |
{ | |
get { return _value; } | |
} | |
public string DisplayName | |
{ | |
get { return _displayName; } | |
} | |
public override string ToString() | |
{ | |
return DisplayName; | |
} | |
public static IEnumerable<T> GetAll<T>() where T : Enumeration | |
{ | |
var type = typeof(T); | |
var fields = type.GetFields(BindingFlags.Public | BindingFlags.Static | BindingFlags.DeclaredOnly); | |
return fields.Select(info => info.GetValue(null)).OfType<T>(); | |
} | |
public override bool Equals(object obj) | |
{ | |
var otherValue = obj as Enumeration; | |
if (otherValue == null) | |
{ | |
return false; | |
} | |
var typeMatches = GetType().Equals(obj.GetType()); | |
var valueMatches = _value.Equals(otherValue.Value); | |
return typeMatches && valueMatches; | |
} | |
public override int GetHashCode() | |
{ | |
return _value.GetHashCode(); | |
} | |
public static T FromValue<T>(int value) where T : Enumeration | |
{ | |
var matchingItem = parse<T, int>(value, "value", item => item.Value == value); | |
return matchingItem; | |
} | |
public static T FromDisplayName<T>(string displayName) where T : Enumeration | |
{ | |
var matchingItem = parse<T, string>(displayName, "display name", item => item.DisplayName == displayName); | |
return matchingItem; | |
} | |
private static T parse<T, K>(K value, string description, Func<T, bool> predicate) where T : Enumeration | |
{ | |
var matchingItem = GetAll<T>().FirstOrDefault(predicate); | |
if (matchingItem == null) | |
{ | |
var message = string.Format("'{0}' is not a valid {1} in {2}", value, description, typeof(T)); | |
throw new ApplicationException(message); | |
} | |
return matchingItem; | |
} | |
public int CompareTo(object other) | |
{ | |
return Value.CompareTo(((Enumeration)other).Value); | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Apologies, 3.5 years late.... no, you are correct that is a downside of this approach.