Can we define implicit conversions of enums in c#?

前端 未结 13 2355
孤街浪徒
孤街浪徒 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:40

    If you define the base of the enum as a long then you can perform explicit conversion. I don't know if you can use implicit conversions as enums cannot have methods defined on them.

    public enum MyEnum : long
    {
        one = 1,
        two = 2,
    }
    
    MyEnum number = MyEnum.one;
    long i = (long)number;
    

    Also, be aware with this that an uninitalised enumeration will default to the 0 value, or the first item - so in the situation above it would probably be best to define zero = 0 as well.

    0 讨论(0)
  • 2020-11-30 18:43

    You can't do implict conversions (except for zero), and you can't write your own instance methods - however, you can probably write your own extension methods:

    public enum MyEnum { A, B, C }
    public static class MyEnumExt
    {
        public static int Value(this MyEnum foo) { return (int)foo; }
        static void Main()
        {
            MyEnum val = MyEnum.A;
            int i = val.Value();
        }
    }
    

    This doesn't give you a lot, though (compared to just doing an explicit cast).

    One of the main times I've seen people want this is for doing [Flags] manipulation via generics - i.e. a bool IsFlagSet<T>(T value, T flag); method. Unfortunately, C# 3.0 doesn't support operators on generics, but you can get around this using things like this, which make operators fully available with generics.

    0 讨论(0)
  • 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<TValue, TDerived>
                    : IEquatable<TDerived>,
                      IComparable<TDerived>,
                      IComparable, IComparer<TDerived>
            where TValue : struct, IComparable<TValue>, IEquatable<TValue>
            where TDerived : RichEnum<TValue, TDerived>
        {
            #region Backing Fields
    
            /// <summary>
            /// The value of the enum item
            /// </summary>
            public readonly TValue Value;
    
            /// <summary>
            /// The public field name, determined from reflection
            /// </summary>
            private string _name;
    
            /// <summary>
            /// The DescriptionAttribute, if any, linked to the declaring field
            /// </summary>
            private DescriptionAttribute _descriptionAttribute;
    
            /// <summary>
            /// Reverse lookup to convert values back to local instances
            /// </summary>
            private static readonly SortedList<TValue, TDerived> _values = new SortedList<TValue, TDerived>();
    
            #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<DescriptionAttribute>().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<TValue, TDerived> value)
            {
                return value.Value;
            }
    
            public static implicit operator RichEnum<TValue, TDerived>(TValue value)
            {
                return _values[value];
            }
    
            public static implicit operator TDerived(RichEnum<TValue, TDerived> value)
            {
                return value;
            }
    
            public override string ToString()
            {
                return _name;
            }
    
            #endregion
    
            #region IEquatable<TDerived> 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<TDerived>.Equals(TDerived other)
            {
                return Value.Equals(other.Value);
            }
    
    
            public override int GetHashCode()
            {
                return Value.GetHashCode();
            }
    
            #endregion
    
            #region IComparable Members
    
            int IComparable<TDerived>.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<TDerived>.Compare(TDerived x, TDerived y)
            {
                return (x == null) ? -1 :
                       (y == null) ? 1 :
                        x.Value.CompareTo(y.Value);
            }
    
            #endregion
    
            public static IEnumerable<TDerived> 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<int, MyEnum>
        {
            [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...

    0 讨论(0)
  • 2020-11-30 18:44

    @BatteryBackupUnit Hey this sounds like a cool solution but could you explain this part here?

    Since im getting with .NET 4.7.2 an "InvalidCastException" out of this sadly :/

     _name = new Lazy<string>(
                    () => EnumInstanceToNameMapping
                         .First(x => ReferenceEquals(this, x.Instance))
                         .Name);
    

    I dont know why, i have created a derived type of the RichEnum and initialized as everything u did in the example but i getthis annyoingg exception..

    Would be glad of some help to this since i like this approach alot tbh.

    0 讨论(0)
  • 2020-11-30 18:48

    I created this utility to help me convert an Enum to PrimitiveEnum and PrimitiveEnum to byte, sbyte, short, ushort, int, uint, long, or ulong.

    So, this technically converts any enum to any its primitive value.

    public enum MyEnum
    {
        one = 1, two = 2
    }
    
    PrimitiveEnum number = MyEnum.one;
    long i = number;
    

    See commit at https://github.com/McKabue/McKabue.Extentions.Utility/blob/master/src/McKabue.Extentions.Utility/Enums/PrimitiveEnum.cs

    using System;
    
    namespace McKabue.Extentions.Utility.Enums
    {
        /// <summary>
        /// <see href="https://stackoverflow.com/q/261663/3563013">
        /// Can we define implicit conversions of enums in c#?
        /// </see>
        /// </summary>
        public struct PrimitiveEnum
        {
            private Enum _enum;
    
            public PrimitiveEnum(Enum _enum)
            {
                this._enum = _enum;
            }
    
            public Enum Enum => _enum;
    
    
            public static implicit operator PrimitiveEnum(Enum _enum)
            {
                return new PrimitiveEnum(_enum);
            }
    
            public static implicit operator Enum(PrimitiveEnum primitiveEnum)
            {
                return primitiveEnum.Enum;
            }
    
            public static implicit operator byte(PrimitiveEnum primitiveEnum)
            {
                return Convert.ToByte(primitiveEnum.Enum);
            }
    
            public static implicit operator sbyte(PrimitiveEnum primitiveEnum)
            {
                return Convert.ToSByte(primitiveEnum.Enum);
            }
    
            public static implicit operator short(PrimitiveEnum primitiveEnum)
            {
                return Convert.ToInt16(primitiveEnum.Enum);
            }
    
            public static implicit operator ushort(PrimitiveEnum primitiveEnum)
            {
                return Convert.ToUInt16(primitiveEnum.Enum);
            }
    
            public static implicit operator int(PrimitiveEnum primitiveEnum)
            {
                return Convert.ToInt32(primitiveEnum.Enum);
            }
    
            public static implicit operator uint(PrimitiveEnum primitiveEnum)
            {
                return Convert.ToUInt32(primitiveEnum.Enum);
            }
    
            public static implicit operator long(PrimitiveEnum primitiveEnum)
            {
                return Convert.ToInt64(primitiveEnum.Enum);
            }
    
            public static implicit operator ulong(PrimitiveEnum primitiveEnum)
            {
                return Convert.ToUInt64(primitiveEnum.Enum);
            }
        }
    }
    
    0 讨论(0)
  • 2020-11-30 18:49
    struct PseudoEnum
    {
        public const int 
                  INPT = 0,
                  CTXT = 1,
                  OUTP = 2;
    };
    
    // ...
    
    var arr = new String[3];
    
    arr[PseudoEnum.CTXT] = "can";
    arr[PseudoEnum.INPT] = "use";
    arr[PseudoEnum.CTXT] = "as";
    arr[PseudoEnum.CTXT] = "array";
    arr[PseudoEnum.OUTP] = "index";
    
    0 讨论(0)
提交回复
热议问题