Skip to main content
1 of 2
Denis
  • 8.6k
  • 5
  • 33
  • 76

After incorporating the suggestions by t3chb0t, the class now looks like this:

[DebuggerDisplay("{Name} = {Value}")]
public abstract partial class Enumeration<T>
    where T : Enumeration<T>
{
    private static readonly IDictionary<int, Enumeration<T>> _valueCache =
        new Dictionary<int, Enumeration<T>>();
    public static IDictionary<int, Enumeration<T>> ValueCache
    {
        get
        {
            RuntimeHelpers.RunClassConstructor(typeof(T).TypeHandle);
            return _valueCache;
        }
    }

    private static readonly IDictionary<string, Enumeration<T>> _nameCache =
        new Dictionary<string, Enumeration<T>>(StringComparer.OrdinalIgnoreCase);
    public static IDictionary<string, Enumeration<T>> NameCache
    {
        get
        {
            RuntimeHelpers.RunClassConstructor(typeof(T).TypeHandle);
            return _nameCache;
        }
    }

    public string Name { get; }
    public int Value { get; }

    public static IEnumerable<string> Names => NameCache.Keys;
    public static IEnumerable<int> Values => ValueCache.Keys;

    protected Enumeration(string name, int value)
    {
        Name = name;
        Value = value;
        ValueCache.Add(value, this);
        NameCache.Add(name, this);
    }

    public static T Parse(string name)
    {
        if (TryParse(name, out var value))
        {
            return value;
        }
        throw new InvalidOperationException($"Requested value {name} was not found.");
    }

    public static bool TryParse(string name, out T value)
    {
        if (NameCache.TryGetValue(name, out var containedValue))
        {
            value = (T)containedValue;
            return true;
        }
        value = default(T);
        return false;
    }

    public static string Format(T value, string format)
    {
        return value.ToString(format);
    }

    public static bool IsDefined(string name)
    {
        return NameCache.ContainsKey(name);
    }
}

public abstract partial class Enumeration<T> : IConvertible
{
    TypeCode IConvertible.GetTypeCode() => TypeCode.Int32;
    bool IConvertible.ToBoolean(IFormatProvider provider) => Convert.ToBoolean(Value, provider);
    char IConvertible.ToChar(IFormatProvider provider) => Convert.ToChar(Value, provider);
    sbyte IConvertible.ToSByte(IFormatProvider provider) => Convert.ToSByte(Value, provider);
    byte IConvertible.ToByte(IFormatProvider provider) => Convert.ToByte(Value, provider);
    short IConvertible.ToInt16(IFormatProvider provider) => Convert.ToInt16(Value, provider);
    ushort IConvertible.ToUInt16(IFormatProvider provider) => Convert.ToUInt16(Value, provider);
    int IConvertible.ToInt32(IFormatProvider provider) => Value;
    uint IConvertible.ToUInt32(IFormatProvider provider) => Convert.ToUInt32(Value, provider);
    long IConvertible.ToInt64(IFormatProvider provider) => Convert.ToInt64(Value, provider);
    ulong IConvertible.ToUInt64(IFormatProvider provider) => Convert.ToUInt64(Value, provider);
    float IConvertible.ToSingle(IFormatProvider provider) => Convert.ToSingle(Value, provider);
    double IConvertible.ToDouble(IFormatProvider provider) => Convert.ToDouble(Value, provider);
    decimal IConvertible.ToDecimal(IFormatProvider provider) => Convert.ToDecimal(Value, provider);
    DateTime IConvertible.ToDateTime(IFormatProvider provider) => throw new InvalidCastException("Invalid cast.");
    string IConvertible.ToString(IFormatProvider provider) => ToString();

    object IConvertible.ToType(Type conversionType, IFormatProvider provider)
        => Convert.ChangeType(this, conversionType, provider);
}

public abstract partial class Enumeration<T> : IComparable<Enumeration<T>>
{
    public int CompareTo(Enumeration<T> other)
    {
        if (ReferenceEquals(this, other)) return 0;
        if (ReferenceEquals(null, other)) return 1;
        return Value.CompareTo(other.Value);
    }
}

public abstract partial class Enumeration<T> : IFormattable
{
    public string ToString(string format)
    {
        if (string.IsNullOrEmpty(format))
        {
            format = "G";
        }
        if (string.Compare(format, "G", StringComparison.OrdinalIgnoreCase) == 0)
        {
            return Name;
        }
        if (string.Compare(format, "D", StringComparison.OrdinalIgnoreCase) == 0)
        {
            return Value.ToString();
        }
        if (string.Compare(format, "X", StringComparison.OrdinalIgnoreCase) == 0)
        {
            return Value.ToString("X8");
        }
        throw new FormatException("Invalid format");
    }

    public override string ToString() => ToString("G");
    public string ToString(string format, IFormatProvider formatProvider) => ToString(format);
}

public abstract partial class Enumeration<T> : IEquatable<Enumeration<T>>
{
    public bool Equals(Enumeration<T> other)
    {
        if (ReferenceEquals(null, other)) return false;
        if (ReferenceEquals(this, other)) return true;
        return Value == other.Value;
    }

    public override bool Equals(object obj)
    {
        if (ReferenceEquals(null, obj)) return false;
        if (ReferenceEquals(this, obj)) return true;
        if (obj.GetType() != GetType()) return false;
        return Equals((Enumeration<T>)obj);
    }

    public override int GetHashCode()
    {
        return Value.GetHashCode();
    }

    public static bool operator >(Enumeration<T> item1, Enumeration<T> item2) => item1.CompareTo(item2) > 0;
    public static bool operator <(Enumeration<T> item1, Enumeration<T> item2) => item1.CompareTo(item2) < 0;

    public static explicit operator Enumeration<T>(int value)
    {
        return (Enumeration<T>)Activator.CreateInstance(
            typeof(T),
            BindingFlags.NonPublic | BindingFlags.Instance, null,
            new object[] { value.ToString(), value }, null);
    }
}
Denis
  • 8.6k
  • 5
  • 33
  • 76