Можем ли мы определить неявные преобразования перечислений в c #?

Можно ли определить неявное преобразование перечислений в c #?

что-то, что могло бы достичь этого?

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

Если нет, почему бы и нет?

Для дальнейшего обсуждения и идей по этому вопросу я продолжил работу над тем, как я сейчас обрабатываю это: Улучшение enums C #

Есть решение. Рассмотрим следующее:

 public sealed class AccountStatus { public static readonly AccountStatus Open = new AccountStatus(1); public static readonly AccountStatus Closed = new AccountStatus(2); public static readonly SortedList Values = 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[byte]; } public static implicit operator byte(AccountStatus value) { return value.Value; } } 

Вышеупомянутое предлагает неявное преобразование:

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

Это справедливая битва больше работы, чем объявление нормального enums (хотя вы можете реорганизовать некоторые из перечисленных выше в общий базовый базовый class). Вы можете пойти еще дальше, если базовый class реализует IComparable & IEquatable, а также добавляет методы для возврата значения DescriptionAttributes, объявленных имен и т. Д. И т. Д.

Я написал базовый class (RichEnum <>), чтобы обрабатывать большую часть работы grunt, что облегчает приведенное выше объявление перечислений:

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

Базовый class (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(T value, T flag); метод. К сожалению, 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"; 

Я адаптировал превосходный базовый class RichEnum от Mark.

фиксация

  1. ряд проблем компиляции из-за отсутствия битов из его библиотек (в частности: имена, зависящие от ресурса, не были полностью удалены, они теперь)
  2. инициализация не была идеальной: если первое, что вы сделали, это доступ к статическому свойству .Values ​​из базового classа, вы получите NPE. Исправлено это, заставляя базовый class любопытно-рекурсивно ( CRTP ) заставлять статическую конструкцию TDerived как раз во время CheckInitialized
  3. наконец, переместил CheckInitialized логику в статический конструктор (чтобы избежать штрафа за проверку каждый раз, состояние гонки при многопоточной инициализации, возможно, это была невозможность, решаемая моей пhive 1.?)

Kudos to 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 : 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) 

Примечание: mono 2.6.7 требует дополнительного явного приведения, которое не требуется при использовании mono 2.8.2 …

Если вы определяете базу enums как длинную, вы можете выполнить явное преобразование. Я не знаю, можете ли вы использовать неявные преобразования, поскольку enums не могут определять методы на них.

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

Кроме того, имейте в виду, что uninitalised перечисление по умолчанию будет использовать значение 0 или первый элемент, поэтому в ситуации, описанной выше, вероятно, было бы лучше определить zero = 0 .

Возможно, вы могли бы, но не для enums (вы не можете добавить к нему метод). Вы можете добавить неявное преобразование в собственный class, чтобы разрешить преобразование в него,

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

Вопрос в том, почему?

В общем .Net избегает (и вам тоже) любого неявного преобразования, где данные могут быть потеряны.

Вы не можете объявлять неявные преобразования для типов enums, потому что они не могут определять методы. Неявное ключевое слово C # компилируется в метод, начинающийся с ‘op_’, и в этом случае это не сработает.

Я работал над проблемой с ответом sehe при запуске кода на MS .net (не Mono). Для меня конкретно проблема возникла в .net 4.5.1, но другие версии также затронуты.

Проблема

доступ к public static TDervied MyEnumValue путем отражения (через FieldInfo.GetValue(null) не инициализирует указанное поле.

Обходной путь

Вместо присвоения имен экземплярам TDerived при статическом инициализаторе RichEnum это делается лениво при первом доступе к TDerived.Name . Код:

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

который – в моем случае – основан на EquatableBase :

 public abstract class EquatableBase 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 для обеспечения его реализации RichEnum и благодаря тому, что он предлагает некоторые улучшения!

Я нашел еще более легкое решение, взятое здесь 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); } } 

Перечисления в основном бесполезны для меня из-за этого, ОП.

Я в конечном итоге занимаюсь pic-related все время:

простое решение

Проблема classического примера – набор 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]; 

проблема здесь заключается в том, что вы не можете индексировать массив с перечислением, потому что он не может неявно конвертировать enum в ushort (хотя мы даже основываем перечисление на ushort)

в этом конкретном контексте enums устаревают следующей структурой данных. , , ,

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

Внедрение неявных преобразований для типов перечислений приведет к нарушению безопасности типов, поэтому я бы не рекомендовал это делать. Почему вы хотите это сделать? Единственный случай использования, который я видел, – это когда вы хотите поместить значения enum в структуру с заранее определенным макетом. Но даже тогда вы можете использовать тип enums в структуре и просто сообщить Маршаллеру, что он должен делать с этим.

Давайте будем гением компьютера.