我们可以在c#中定义枚举的隐式转换吗?
是否有可能定义隐式转换的枚举在C#?
有什么可以做到这一点?
public enum MyEnum { one = 1, two = 2 } MyEnum number = MyEnum.one; long i = number;
如果没有,为什么不呢?
有关这方面的进一步讨论和想法,我跟随我如何处理这个: 改进C#枚举
有一个解决scheme。 考虑以下:
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
这比声明一个普通的枚举(虽然你可以将上面的一些重构成一个通用的generics基类)要多一点工作。 通过让基类实现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; } } }
你不能做暗示转换(除了零),你不能写你自己的实例方法 – 但是,你可以编写自己的扩展方法:
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(); } }
这并不能给你很多,但是(只是做一个明确的演员)。
其中一个主要的时代,我看到人们想要这个是通过generics操作[Flags]
– 即一个bool IsFlagSet<T>(T value, T flag);
方法。 不幸的是,C#3.0不支持generics的运算符,但是你可以使用类似这样的东西来解决这个问题 ,这使得运算符可以在generics上完全可用。
我改编了Mark的优秀RichEnumgenerics基类。
定影
- 由于缺less来自他的图书馆的一些编辑问题(特别是:资源依赖的显示名称没有被完全删除,现在是)
- 初始化并不完美:如果你所做的第一件事是从基类访问静态.Values属性,你会得到一个NPE。 通过强制基类强制recursion ( CRTP )在CheckInitialized期间及时强制TDerived的静态构造
- 最后将CheckInitialized逻辑转移到一个静态构造函数中(为了避免每次检查都会受到惩罚,multithreading初始化时的竞争条件;也许这是我的子弹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<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; } } }
我在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); } } }
生成输出
[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)
1单声道2.6.7需要使用单声道2.8.2时不需要额外显式演员…
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";
如果您将枚举的基础定义为long,则可以执行显式转换。 我不知道你是否可以使用隐式转换,因为枚举不能在它们上定义方法。
public enum MyEnum : long { one = 1, two = 2, } MyEnum number = MyEnum.one; long i = (long)number;
此外,请注意,未初始化的枚举将默认为0值或第一项 – 所以在上面的情况下,最好也可以定义zero = 0
。
你可能可以,但不能为枚举(你不能添加一个方法)。 你可以添加一个隐式转换给你自己的类,以允许一个枚举转换为它,
public class MyClass { public static implicit operator MyClass ( MyEnum input ) { //... } } MyClass m = MyEnum.One;
这个问题会是为什么?
一般来说,.net避免(也应该)任何隐式转换,数据可能会丢失。
您不能在枚举types上声明隐式转换,因为它们不能定义方法。 C# 隐式关键字编译成以“op_”开头的方法,在这种情况下不起作用。
在MS .net(非单声道)上运行代码时,我已经解决了sehe的答案 。 对于我来说,特定的问题出现在.net 4.5.1上,但其他版本也受到影响。
问题
通过reflection(通过FieldInfo.GetValue(null)
访问public static TDervied MyEnumValue
不会初始化所述字段。
解决方法
而不是在RichEnum<TValue, TDerived>
的静态初始化程序中为TDerived
实例分配名称,这是在TDerived.Name
第一次访问时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(); }
注意
上面的代码没有包含马克原来的答案的所有function!
谢谢
感谢Mark提供他的RichEnum
实现,并感谢sehe提供了一些改进!
我发现从这里取得更简单的解决schemehttps://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); } }
引入enumtypes的隐式转换会破坏types安全性,所以我不build议这样做。 你为什么想这么做? 我见过的唯一用例是当你想把枚举值放入一个预定义布局的结构中。 但即使如此,你可以在结构中使用枚举types,并告诉Marshaller他应该怎么做。