是否可以在c#中定义枚举的隐式转换?
可以实现这一目标的东西?
public enum MyEnum { one = 1, two = 2 } MyEnum number = MyEnum.one; long i = number;
如果没有,为什么不呢?
为了进一步讨论和提出这个想法,我跟进了我目前处理这个问题的方法:改进C#enum
有一个解决方案.考虑以下:
public sealed class AccountStatus { public static readonly AccountStatus Open = new AccountStatus(1); public static readonly AccountStatus Closed = new AccountStatus(2); public static readonly SortedListValues = new SortedList (); private readonly byte Value; private AccountStatus(byte value) { this.Value = value; Values.Add(value, this); } public static implicit operator AccountStatus(byte value) { return Values[value]; } 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{ 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: 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 SortedList_values; private static bool _isInitialized; #endregion #region Constructors protected RichEnum(TValue value) { if (_values == null) _values = new SortedList (); 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 (); 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 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.Values) if (0 == string.Compare(value.Name, name, true) || 0 == string.Compare(value.DisplayName, name, true)) return value; return null; } } }
你不能做隐含的转换(零除外),你不能编写自己的实例方法 - 但是,你可以编写自己的扩展方法:
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
方法.不幸的是,C#3.0不支持泛型运算符,但你可以使用这样的东西解决这个问题,这使得运算符完全可用于泛型.
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";
我改编了Mark的优秀RichEnum通用基类.
定影
由于缺少来自其库的位而导致的许多编译问题(特别是:资源相关的显示名称未被完全删除;它们现在是)
初始化并不完美:如果您做的第一件事是从基类访问静态.Values属性,那么您将得到一个NPE.通过强制基类以奇怪的递归方式(CRTP)在CheckInitialized期间及时强制TDerived的静态构造来解决此问题
最后将CheckInitialized逻辑移动到一个静态构造函数中(以避免每次检查的惩罚,多线程初始化时的竞争条件;也许这是我的子弹1解决的不可能性.?)
感谢马克的精彩创意+实施,大家都在这里:
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; } } }
我在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); } } }
产生输出
[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)
注意:单声道2.6.7需要使用单声道2.8.2时不需要的额外显式强制转换...