C#でタイプセーフEnum
Java5でタイプセーフEnumが実装されたのを知って以来、C#でも何とか実現できないか挑戦していました。
とりあえず形になるものができたので公開。いろいろ欠点はあるけどタイプセーフEnum一番の特徴である「振る舞いを持つ定数」は実現できたかな。
海外サイト見てたらこんなサイト見つけたんでこれを元に変更加えてみるかも。
public abstract class TypeSafeEnum<T> : IComparable<TypeSafeEnum<T>>, IEquatable<TypeSafeEnum<T>> where T : TypeSafeEnum<T> { private string name = ""; public string Name { get { return name; } } private int ordinal = 0; public int Ordinal { get { return this.ordinal; } } #region IComparable<T> メンバ public int CompareTo(TypeSafeEnum<T> other) { return this.ordinal - other.ordinal; } #endregion #region IEquatable<T> メンバ public bool Equals(TypeSafeEnum<T> other) { return (this.ordinal == other.ordinal); } #endregion #region コンストラクタ protected TypeSafeEnum() { } #endregion #region Objectクラスのオーバーライド public override string ToString() { return this.name; } public override bool Equals(object obj) { return this.Equals((TypeSafeEnum<T>)obj); } public override int GetHashCode() { return this.ordinal.GetHashCode(); } #endregion #region 演算子 public static bool operator >(TypeSafeEnum<T> x, TypeSafeEnum<T> y) { return (x.CompareTo(y) > 0); } public static bool operator <(TypeSafeEnum<T> x, TypeSafeEnum<T> y) { return (x.CompareTo(y) < 0); } public static bool operator >=(TypeSafeEnum<T> x, TypeSafeEnum<T> y) { return (x.CompareTo(y) >= 0); } public static bool operator <=(TypeSafeEnum<T> x, TypeSafeEnum<T> y) { return (x.CompareTo(y) <= 0); } public static bool operator ==(TypeSafeEnum<T> x, TypeSafeEnum<T> y) { return x.Equals(y); } public static bool operator !=(TypeSafeEnum<T> x, TypeSafeEnum<T> y) { return (!x.Equals(y)); } #endregion public static void Initialize() { Type type = typeof(T); FieldInfo[] fields = type.GetFields( BindingFlags.GetField | BindingFlags.Public | BindingFlags.Static | BindingFlags.DeclaredOnly); int i = 0; foreach (FieldInfo field in fields) { T t = (T)field.GetValue(null); t.name = field.Name; t.ordinal = i; i++; } } }
これを継承したクラスはこんな感じ。
public class Suit : TypeSafeEnum<Suit> { static Suit() { Initialize(); } private Suit(GetNameMethod method) { this.method = method; } public static readonly Suit Clubs = new Suit(new GetNameMethod(delegate() { return "Suit: Clubs"; })), Diamonds = new Suit(new GetNameMethod(delegate() { return "Suit: Diamonds"; })), Hearts = new Suit(new GetNameMethod(delegate() { return "Suit: Hearts"; })), Spades = new Suit(new GetNameMethod(delegate() { return "Suit: Spades"; })) ; private delegate string GetNameMethod(); private GetNameMethod method = null; public string GetName() { return method(); } }
使い方
Suit[] suits = new Suit[] { Suit.Clubs, Suit.Diamonds, Suit.Hearts, Suit.Spades }; foreach (Suit s in suits) { System.Console.WriteLine("[{0}] {1}\t\t{2}", s.Ordinal, s.Name, s.GetName()); } if (suits[0] != suits[1]) { System.Console.WriteLine("{0}と{1}は異なります。", suits[0].Name, suits[1].Name); }