Can we define implicit conversions of enums in c#?

前端 未结 13 2381
孤街浪徒
孤街浪徒 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 18:43

    I adapted Mark's excellent RichEnum generic baseclass.

    Fixing

    1. a number of compilation problems due to missing bits from his libraries (notably: the resource dependent display names weren't completely removed; they are now)
    2. initialization wasn't perfect: if the first thing you did was access the static .Values property from the base class, you'd get a NPE. Fixed this by forcing the base class to curiously-recursively (CRTP) force the static construction of TDerived just in time during CheckInitialized
    3. finally moved CheckInitialized logic into a static constructor (to avoid the penalty of checking each time, the race condition on multithreaded initialization; perhaps this was an impossibility solved by my bullet 1.?)

    Kudos to Mark for the splendid idea + implementation, here's to you all:

    using System;
    using System.Collections.Generic;
    using System.ComponentModel;
    using System.Diagnostics;
    using System.Linq;
    using System.Reflection;
    using System.Resources;
    
    namespace NMatrix
    {
    
        [DebuggerDisplay("{Value} ({Name})")]
        public abstract class RichEnum
                    : IEquatable,
                      IComparable,
                      IComparable, IComparer
            where TValue : struct, IComparable, IEquatable
            where TDerived : RichEnum
        {
            #region Backing Fields
    
            /// 
            /// The value of the enum item
            /// 
            public readonly TValue Value;
    
            /// 
            /// The public field name, determined from reflection
            /// 
            private string _name;
    
            /// 
            /// The DescriptionAttribute, if any, linked to the declaring field
            /// 
            private DescriptionAttribute _descriptionAttribute;
    
            /// 
            /// Reverse lookup to convert values back to local instances
            /// 
            private static readonly SortedList _values = new SortedList();
    
            #endregion
    
            #region Constructors
    
            protected RichEnum(TValue value)
            {
                this.Value = value;
                _values.Add(value, (TDerived)this);
            }
    
            #endregion
    
            #region Properties
    
            public string Name
            {
                get
                {
                    return _name;
                }
            }
    
            public string Description
            {
                get
                {
                    if (_descriptionAttribute != null)
                        return _descriptionAttribute.Description;
    
                    return _name;
                }
            }
    
            #endregion
    
            #region Initialization
    
            static RichEnum()
            {
                var fields = typeof(TDerived)
                    .GetFields(BindingFlags.Static | BindingFlags.GetField | BindingFlags.Public)
                    .Where(t => t.FieldType == typeof(TDerived));
    
                foreach (var field in fields)
                {
                    /*var dummy =*/ field.GetValue(null); // forces static initializer to run for TDerived
    
                    TDerived instance = (TDerived)field.GetValue(null);
                    instance._name = field.Name;
                                        instance._descriptionAttribute = field.GetCustomAttributes(true).OfType().FirstOrDefault();
                }
            }
    
            #endregion
    
            #region Conversion and Equality
    
            public static TDerived Convert(TValue value)
            {
                return _values[value];
            }
    
            public static bool TryConvert(TValue value, out TDerived result)
            {
                return _values.TryGetValue(value, out result);
            }
    
            public static implicit operator TValue(RichEnum value)
            {
                return value.Value;
            }
    
            public static implicit operator RichEnum(TValue value)
            {
                return _values[value];
            }
    
            public static implicit operator TDerived(RichEnum value)
            {
                return value;
            }
    
            public override string ToString()
            {
                return _name;
            }
    
            #endregion
    
            #region IEquatable Members
    
            public override bool Equals(object obj)
            {
                if (obj != null)
                {
                    if (obj is TValue)
                        return Value.Equals((TValue)obj);
    
                    if (obj is TDerived)
                        return Value.Equals(((TDerived)obj).Value);
                }
                return false;
            }
    
            bool IEquatable.Equals(TDerived other)
            {
                return Value.Equals(other.Value);
            }
    
    
            public override int GetHashCode()
            {
                return Value.GetHashCode();
            }
    
            #endregion
    
            #region IComparable Members
    
            int IComparable.CompareTo(TDerived other)
            {
                return Value.CompareTo(other.Value);
            }
    
            int IComparable.CompareTo(object obj)
            {
                if (obj != null)
                {
                    if (obj is TValue)
                        return Value.CompareTo((TValue)obj);
    
                    if (obj is TDerived)
                        return Value.CompareTo(((TDerived)obj).Value);
                }
                return -1;
            }
    
            int IComparer.Compare(TDerived x, TDerived y)
            {
                return (x == null) ? -1 :
                       (y == null) ? 1 :
                        x.Value.CompareTo(y.Value);
            }
    
            #endregion
    
            public static IEnumerable Values
            {
                get
                {
                    return _values.Values;
                }
            }
    
            public static TDerived Parse(string name)
            {
                foreach (TDerived value in Values)
                    if (0 == string.Compare(value.Name, name, true))
                        return value;
    
                return null;
            }
        }
    }
    

    A sample of usage that I ran on mono:

    using System.ComponentModel;
    using System;
    
    namespace NMatrix
    {    
        public sealed class MyEnum : RichEnum
        {
            [Description("aap")]  public static readonly MyEnum my_aap   = new MyEnum(63000);
            [Description("noot")] public static readonly MyEnum my_noot  = new MyEnum(63001);
            [Description("mies")] public static readonly MyEnum my_mies  = new MyEnum(63002);
    
            private MyEnum(int value) : base (value) { } 
            public static implicit operator MyEnum(int value) { return Convert(value); }
        }
    
        public static class Program
        {
            public static void Main(string[] args)
            {
                foreach (var enumvalue in MyEnum.Values)
                    Console.WriteLine("MyEnum {0}: {1} ({2})", (int) enumvalue, enumvalue, enumvalue.Description);
            }
        }
    }
    

    Producing the output

    [mono] ~/custom/demo @ gmcs test.cs richenum.cs && ./test.exe 
    MyEnum 63000: my_aap (aap)
    MyEnum 63001: my_noot (noot)
    MyEnum 63002: my_mies (mies)
    

    Note: mono 2.6.7 requires an extra explicit cast that is not required when using mono 2.8.2...

提交回复
热议问题