首页 文章

我们可以在c#中定义枚举的隐式转换吗?

提问于
浏览
116

是否可以在c#中定义枚举的隐式转换?

可以实现这一目标的东西?

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

MyEnum number = MyEnum.one;
long i = number;

如果没有,为什么不呢?

有关这方面的进一步讨论和想法,我跟进了我目前处理这个问题的方法:Improving the C# enum

11 回答

  • 28

    如果将枚举的基数定义为long,则可以执行显式转换 . 我不知道你是否可以使用隐式转换,因为枚举不能在它们上定义方法 .

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

    此外,请注意,未经初始化的枚举将默认为0值或第一项 - 因此在上述情况下,最好定义 zero = 0 .

  • 5

    你可能可以,但不是枚举(你不能添加一个方法) . 您可以向自己的类添加隐式转换,以允许将枚举转换为它,

    public class MyClass {
    
        public static implicit operator MyClass ( MyEnum input ) {
            //...
        }
    }
    
    MyClass m = MyEnum.One;
    

    问题是为什么?

    通常.Net避免(并且你也应该)任何可能丢失数据的隐式转换 .

  • 118

    您不能在枚举类型上声明隐式转换,因为它们无法定义方法 . C#implicit关键字编译成一个以'op_'开头的方法,在这种情况下它不起作用 .

  • 0

    我在MS .net(非Mono)上运行代码时遇到了sehe's answer的问题 . 对我来说,问题特别发生在.net 4.5.1上,但其他版本似乎也受到了影响 .

    这个问题

    通过反射访问 public static TDervied MyEnumValue (通过 FieldInfo.GetValue(null) 不初始化所述字段 .

    解决方法

    而不是在 RichEnum<TValue, TDerived> 的静态初始化器上为 TDerived 实例分配名称,而是在第一次访问 TDerived.Name 时懒得完成 . 代码:

    public abstract class RichEnum<TValue, TDerived> : EquatableBase<TDerived>
        where TValue : struct, IComparable<TValue>, IEquatable<TValue>
        where TDerived : RichEnum<TValue, TDerived>
    {
        // 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<TValue, TDerived>´ 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<EnumInstanceReflectionInfo> 
                                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<TValue, TDerived> Values =
            new SortedList<TValue, TDerived>();
    
        public readonly TValue Value;
    
        private readonly Lazy<string> _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<string>(
                        () => EnumInstanceToNameMapping
                             .First(x => ReferenceEquals(this, x.Instance))
                             .Name);
        }
    
        public string Name
        {
            get { return _name.Value; }
        }
    
        public static implicit operator TValue(RichEnum<TValue, TDerived> 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<TDerived> _instance;
    
            public EnumInstanceReflectionInfo(FieldInfo field)
            {
                _field = field;
                _instance = new Lazy<TDerived>(() => (TDerived)field.GetValue(null));
            }
    
            public TDerived Instance
            {
                get { return _instance.Value; }
            }
    
            public string Name { get { return _field.Name; } }
        }
    }
    

    在我的情况下 - 基于 EquatableBase<T>

    public abstract class EquatableBase<T>
        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();
    }
    

    注意

    上面的代码没有包含Mark原始答案的所有功能!

    谢谢

    感谢Mark提供了他的 RichEnum 实施,并感谢sehe提供了一些改进!

  • 18
    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";
    
  • -2

    我改编了Mark优秀的RichEnum通用基类 .

    定影

    • 由于缺少来自其库的位而导致的许多编译问题(特别是:资源相关的显示名称未被完全删除;它们现在是)

    • 初始化没有't perfect: if the first thing you did was access the static .Values property from the base class, you'得到一个NPE . 通过强制基类强制递归(CRTP)在CheckInitialized期间及时强制TDerived的静态构造来解决此问题

    • 最后将CheckInitialized逻辑移动到一个静态构造函数中(为了避免每次检查的惩罚,多线程初始化时的竞争条件;也许这是我的子弹1解决的不可能性 . ?)

    感谢Mark为这个出色的想法实施,大家向大家致意:

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

    我在单声道上运行的一个使用示例:

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

    产生输出

    [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)
    

    注意:mono 2.6.7需要使用单声道2.8.2时不需要的额外显式强制转换...

  • 1

    有一个解决方案 . 考虑以下:

    public sealed class AccountStatus
    {
        public static readonly AccountStatus Open = new AccountStatus(1);
        public static readonly AccountStatus Closed = new AccountStatus(2);
    
        public static readonly SortedList<byte, AccountStatus> Values = new SortedList<byte, AccountStatus>();
        private readonly byte Value;
    
        private AccountStatus(byte value)
        {
            this.Value = value;
            Values.Add(value, this);
        }
    
    
        public static implicit operator AccountStatus(byte value)
        {
            return Values[byte];
        }
    
        public static implicit operator byte(AccountStatus value)
        {
            return value.Value;
        }
    }
    

    以上提供隐式转换:

    AccountStatus openedAccount = 1;            // Works
            byte openedValue = AccountStatus.Open;      // Works
    

    这比声明一个普通的枚举更加重要(尽管你可以将上面的一些重构为一个通用的通用基类) . 您可以通过让基类实现IComparable和IEquatable,以及添加方法来返回DescriptionAttributes的值,声明的名称等等,甚至可以更进一步 .

    我编写了一个基类(RichEnum <>)来处理大部分繁琐的工作,这使得上面的枚举声明简化为:

    public sealed class AccountStatus : RichEnum<byte, AccountStatus>
    {
        public static readonly AccountStatus Open = new AccountStatus(1);
        public static readonly AccountStatus Closed = new AccountStatus(2);
    
        private AccountStatus(byte value) : base (value)
        {
        }
    
        public static implicit operator AccountStatus(byte value)
        {
            return Convert(value);
        }
    }
    

    基类(RichEnum)如下所示 .

    using System;
    using System.Collections.Generic;
    using System.ComponentModel;
    using System.Diagnostics;
    using System.Linq;
    using System.Reflection;
    using System.Resources;
    
    namespace Ethica
    {
        using Reflection;
        using Text;
    
        [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 SortedList<TValue, TDerived> _values;
    
            private static bool _isInitialized;
    
    
            #endregion
    
            #region Constructors
    
            protected RichEnum(TValue value)
            {
                if (_values == null)
                    _values = new SortedList<TValue, TDerived>();
                this.Value = value;
                _values.Add(value, (TDerived)this);
            }
    
            #endregion
    
            #region Properties
    
            public string Name
            {
                get
                {
                    CheckInitialized();
                    return _name;
                }
            }
    
            public string Description
            {
                get
                {
                    CheckInitialized();
    
                    if (_descriptionAttribute != null)
                        return _descriptionAttribute.Description;
    
                    return _name;
                }
            }
    
            #endregion
    
            #region Initialization
    
            private static void CheckInitialized()
            {
                if (!_isInitialized)
                {
                    ResourceManager _resources = new ResourceManager(typeof(TDerived).Name, typeof(TDerived).Assembly);
    
                    var fields = typeof(TDerived)
                                    .GetFields(BindingFlags.Static | BindingFlags.GetField | BindingFlags.Public)
                                    .Where(t => t.FieldType == typeof(TDerived));
    
                    foreach (var field in fields)
                    {
    
                        TDerived instance = (TDerived)field.GetValue(null);
                        instance._name = field.Name;
                        instance._descriptionAttribute = field.GetAttribute<DescriptionAttribute>();
    
                        var displayName = field.Name.ToPhrase();
                    }
                    _isInitialized = true;
                }
            }
    
            #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.Values)
                    if (0 == string.Compare(value.Name, name, true) || 0 == string.Compare(value.DisplayName, name, true))
                        return value;
    
                return null;
            }
        }
    }
    
  • 1

    你不能做隐含的转换(零除外),你不能编写自己的实例方法 - 但是,你可以编写自己的扩展方法:

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

    但是,这并没有给你很多(与仅进行显式演员相比) .

    我见过人们想要的主要时间之一是通过泛型进行 [Flags] 操作 - 即 bool IsFlagSet<T>(T value, T flag); 方法 . 不幸的是,C#3.0不支持泛型运算符,但是你可以使用things like this来解决这个问题,这使得运算符完全可用于泛型 .

  • 17

    我从这里找到了更简单的解决方案https://codereview.stackexchange.com/questions/7566/enum-vs-int-wrapper-struct我从该链接粘贴了下面的代码,以防万一它以后不起作用 .

    struct Day
    {
        readonly int day;
    
        public static readonly Day Monday = 0;
        public static readonly Day Tuesday = 1;
        public static readonly Day Wednesday = 2;
        public static readonly Day Thursday = 3;
        public static readonly Day Friday = 4;
        public static readonly Day Saturday = 5;
        public static readonly Day Sunday = 6;
    
        private Day(int day)
        {
            this.day = day;
        }
    
        public static implicit operator int(Day value)
        {
            return value.day;
        }
    
        public static implicit operator Day(int value)
        {
            return new Day(value);
        }
    }
    
  • 3

    对我来说,枚举对我来说基本没用,OP .

    我最终总是和pic有关:

    the simple solution

    经典示例问题是用于检测按键的VirtualKey集 .

    enum VKeys : ushort
    {
    a = 1,
    b = 2,
    c = 3
    }
    // the goal is to index the array using predefined constants
    int[] array = new int[500];
    var x = array[VKeys.VK_LSHIFT];
    

    这里的问题是你不能用枚举索引数组,因为它不能隐式地将枚举转换为ushort(即使我们甚至在ushort上使用enum)

    在此特定上下文中,枚举由以下数据结构废弃 . . . .

    public static class VKeys
    {
    public const ushort
    a = 1,
    b = 2, 
    c = 3;
    }
    
  • 4

    引入枚举类型的隐式转换会破坏类型安全性,所以我不建议这样做 . 你为什么想这么做?我见过的唯一用例是当你想将枚举值放入具有预定义布局的结构中时 . 但即使这样,你也可以在结构中使用枚举类型,然后告诉Marshaller他应该怎么做 .

相关问题