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);
}