Can we define implicit conversions of enums in c#?

前端 未结 13 2401
孤街浪徒
孤街浪徒 2020-11-30 18:37

Is it possible to define an implicit conversion of enums in c#?

something that could achieve this?

public enum MyEnum
{
    one = 1, two = 2
}

MyEnu         


        
13条回答
  •  感情败类
    2020-11-30 19:02

    I've worked around an issue with sehe's answer when running the code on MS .net (non-Mono). For me specifically the issue occurred on .net 4.5.1 but other versions seem affected, too.

    The issue

    accessing a public static TDervied MyEnumValue by reflection (via FieldInfo.GetValue(null) does not initialize said field.

    The workaround

    Instead of assigning names to TDerived instances upon the static initializer of RichEnum this is done lazily on first access of TDerived.Name. The code:

    public abstract class RichEnum : EquatableBase
        where TValue : struct, IComparable, IEquatable
        where TDerived : RichEnum
    {
        // Enforcing that the field Name (´SomeEnum.SomeEnumValue´) is the same as its 
        // instances ´SomeEnum.Name´ is done by the static initializer of this class.
        // Explanation of initialization sequence:
        // 1. the static initializer of ´RichEnum´ reflects TDervied and 
        //    creates a list of all ´public static TDervied´ fields:
        //   ´EnumInstanceToNameMapping´
        // 2. the static initializer of ´TDerive´d assigns values to these fields
        // 3. The user is now able to access the values of a field.
        //    Upon first access of ´TDervied.Name´ we search the list 
        //    ´EnumInstanceToNameMapping´ (created at step 1) for the field that holds
        //    ´this´ instance of ´TDerived´.
        //    We then get the Name for ´this´ from the FieldInfo
        private static readonly IReadOnlyCollection 
                                EnumInstanceToNameMapping = 
            typeof(TDerived)
                .GetFields(BindingFlags.Static | BindingFlags.GetField | BindingFlags.Public)
                .Where(t => t.FieldType == typeof(TDerived))
                .Select(fieldInfo => new EnumInstanceReflectionInfo(fieldInfo))
                .ToList();
    
        private static readonly SortedList Values =
            new SortedList();
    
        public readonly TValue Value;
    
        private readonly Lazy _name;
    
        protected RichEnum(TValue value)
        {
            Value = value;
    
            // SortedList doesn't allow duplicates so we don't need to do
            // duplicate checking ourselves
            Values.Add(value, (TDerived)this);
    
            _name = new Lazy(
                        () => EnumInstanceToNameMapping
                             .First(x => ReferenceEquals(this, x.Instance))
                             .Name);
        }
    
        public string Name
        {
            get { return _name.Value; }
        }
    
        public static implicit operator TValue(RichEnum richEnum)
        {
            return richEnum.Value;
        }
    
        public static TDerived Convert(TValue value)
        {
            return Values[value];
        }
    
        protected override bool Equals(TDerived other)
        {
            return Value.Equals(other.Value);
        }
    
        protected override int ComputeHashCode()
        {
            return Value.GetHashCode();
        }
    
        private class EnumInstanceReflectionInfo
        {
            private readonly FieldInfo _field;
            private readonly Lazy _instance;
    
            public EnumInstanceReflectionInfo(FieldInfo field)
            {
                _field = field;
                _instance = new Lazy(() => (TDerived)field.GetValue(null));
            }
    
            public TDerived Instance
            {
                get { return _instance.Value; }
            }
    
            public string Name { get { return _field.Name; } }
        }
    }
    

    which - in my case - is based upon EquatableBase:

    public abstract class EquatableBase
        where T : class 
    {
        public override bool Equals(object obj)
        {
            if (this == obj)
            {
                return true;
            }
    
            T other = obj as T;
            if (other == null)
            {
                return false;
            }
    
            return Equals(other);
        }
    
        protected abstract bool Equals(T other);
    
        public override int GetHashCode()
        {
            unchecked
            {
                return ComputeHashCode();
            }
        }
    
        protected abstract int ComputeHashCode();
    }
    

    Note

    The above code does not incorporate all features of Mark's original answer!

    Thanks

    Thanks to Mark for providing his RichEnum implementation and thanks to sehe for providing some improvements!

提交回复
热议问题