Можно ли определить неявное преобразование перечислений в с #?
что-то, что могло бы этого достичь?
public enum MyEnum
{
one = 1, two = 2
}
MyEnum number = MyEnum.one;
long i = number;
Если нет, то почему?
Отметим, что эта концепция отключает проверку безопасности типов компилятора. В долгосрочной перспективе может быть лучше явное сокращение преобразования, такое как завершающий "~".
Ссылка больше не действительна - можем ли мы либо удалить ссылку, либо где-нибудь перепостить сайт?





Если вы определяете базу перечисления как длинную, вы можете выполнить явное преобразование. Я не знаю, можно ли использовать неявные преобразования, поскольку в перечислениях не могут быть определены методы.
public enum MyEnum : long
{
one = 1,
two = 2,
}
MyEnum number = MyEnum.one;
long i = (long)number;
Кроме того, имейте в виду, что неинициализированное перечисление по умолчанию будет иметь значение 0 или первый элемент, поэтому в ситуации выше, вероятно, будет лучше также определить zero = 0.
: long здесь не нужен; явное преобразование нормально работало бы и без него. Единственное допустимое неявное преобразование - ноль.
Я думал все перечисления, где лонги по умолчанию? Поэтому явные преобразования в лонги уже существуют?
Нет; перечисление по умолчанию - Int32
ints по умолчанию, вы можете сделать любой целочисленный тип (byte, short, int, long)
См .: перечисление Foo {A, B, C} Console.WriteLine (Enum.GetUnderlyingType (typeof (Foo)));
Почему это отмечено как ответ и имеет столько очков? Это НЕ имеет отношения к вопросу OP !!! Он говорит о НЕПРИЯТНОЙ конверсии ... Добавленная стоимость равна нулю.
Вопрос уже подразумевает, что явные приведения понятны, вопрос эквивалентен вопросу «Как мне избежать явного приведения?», К которому ЭТО сообщение не относится.
Вы не можете объявлять неявные преобразования для перечислимых типов, потому что они не могут определять методы. Ключевое слово C# скрытый компилируется в метод, начинающийся с op_, и в этом случае он не будет работать.
Возможно, вы могли бы, но не для перечисления (вы не можете добавить к нему метод). Вы можете добавить неявное преобразование в свой собственный класс, чтобы преобразовать в него перечисление,
public class MyClass {
public static implicit operator MyClass ( MyEnum input ) {
//...
}
}
MyClass m = MyEnum.One;
Возникает вопрос: почему?
В целом .Net избегает (и вам следует тоже) любого неявного преобразования, при котором данные могут быть потеряны.
Введение неявных преобразований для перечисляемых типов нарушит безопасность типов, поэтому я бы не рекомендовал этого делать. Почему вы хотите это сделать? Единственный вариант использования этого, который я видел, - это когда вы хотите поместить значения перечисления в структуру с заранее определенным макетом. Но даже тогда вы можете использовать тип enum в структуре и просто сказать Маршаллеру, что он должен с этим делать.
Я использую неявное преобразование перечислений. Использование SPMetal для создания классов LINQ to SharePoint на нескольких сайтах одного семейства сайтов. Некоторые из моих списков находятся на одном дочернем сайте, другие - на другом. Из-за того, как SPMetal генерирует код, столбцы сайта, используемые в нескольких списках коллекции, могут быть определены в нескольких пространствах имен. Однако мне нужно преобразовать перечисление поля выбора в одном пространстве имен в одно и то же перечисление в другом пространстве имен. Неявное преобразование было бы очень полезно.
Вы не можете выполнять неявные преобразования (кроме нуля), и вы не можете писать свои собственные методы экземпляра - однако вы, вероятно, можете написать свои собственные методы расширения:
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 не поддерживает операторы в универсальных шаблонах, но вы можете обойти это с помощью такие вещи, что делает операторы полностью доступными для универсальных шаблонов.
Да, это было одним из моих самых желанных для C# 4: stackoverflow.com/questions/138367/… и stackoverflow.com/questions/7244
@Keith - хорошо поработал, тогда ;-p Поддержка динамических / операторов не вошла в CTP, но у меня есть готовая к работе испытательная установка, чтобы сравнить два подхода для операторов с динамическим ( vs generics / Expression), когда он туда попадет.
@Keith - вы можете захотеть дать класс Operator в MiscUtil; Я почти уверен, что он сделает большую часть того, что вы хотите.
Выход есть. Учтите следующее:
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[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<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;
}
}
}
Исправлена небольшая ошибка в сообщении :-) Это публичный статический неявный оператор AccountStatus (байтовое значение) {return Convert (value); } НЕ возвращать Convert (byte);
Я скомпилировал этот базовый класс. Вы не возражаете, если я внесу изменения?
В вашем первом примере (подход, не связанный с базовым классом) SortedList должен быть определен до (выше в коде) экземпляров AccountStatus, иначе конструктор будет жаловаться во время выполнения, что SortedList имеет значение null
Я думал, вы не можете захватить this () в конструкторе ... Я считаю, что это должно вызвать ошибку инициализатора типа, не так ли?
Это решение может быть «правильным» в качестве упражнения или проверки чьих-либо навыков программирования, но, пожалуйста, не делайте этого в реальной жизни. Это не только излишество, но и непродуктивно, невозможно обслуживать и чертовски уродливо. Вам не нужно использовать перечисление только ради этого. Вы либо помещаете явное приведение, либо просто пишете статический класс с константными целыми числами.
Разве это не переработанное Java enum?
Одна из основных проблем заключается в том, что вы не можете использовать эти статические константы только для чтения в операторах switch.
В некоторых местах нельзя использовать элементы static readonly, а использовать только поля const. Например. Параметр ctor Attribute, а также операторы switch, как сказал Ян.
@IanGoldby Да, вы можете использовать это в операторе switch. Определите статический неявный оператор, который преобразуется в целое число, и экземпляр класса можно использовать в стиле switch(instance). Но опять же, вы не можете использовать его для меток в операторе switch, поскольку они должны быть константами времени компиляции.
В проекте, над которым я работаю, у нас есть перечисления с метаданными. Он соответствует записям в базе данных, которые не изменяются динамически и поэтому являются статическими. Например. поле в перечислении имеет связанный порядок. Мы используем их в коде с помощью методов расширения. Например. MyEnum.SomeValue.SequenceOrder(). Nedd для перечислений с переменными метаданными кажется мне недостатком дизайна.
@MikedeKlerk Я должен был быть более явным - поскольку вы упомянули, что метки должны быть константами времени компиляции, поэтому я сказал, что вы не можете использовать это в операторе switch. Дело в том, что вы нарушаете инкапсуляцию, если используете метку литерала 2, а не AccountStatus.Closed. Да, вы можете определить константы для использования в метках переключателей и использовать те же константы в своем определении AccountStatus, но я думаю, что это ставит под угрозу исходную цель и смысл существования AccountStatus.
это также не позволит вам использовать значения в качестве значений по умолчанию для дополнительных аргументов
Я адаптировал отличный универсальный базовый класс RichEnum Марка.
Фиксация
Престижность Марку за великолепную идею + реализацию, всем вам:
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)
Примечание: для моно 2.6.7 требуется дополнительное явное приведение, которое не требуется при использовании моно 2.8.2 ...
Использование .Single () для получения атрибута описания - не лучшая идея. Если атрибута нет, Single () выдает исключение, а SingleOrDefault () - нет.
@kerem хороший момент, я обновил его (используя FirstOrDefault, чтобы не предполагать, что есть только один атрибут). Считать ли такие вещи `` хорошей идеей '' (или плохой, если на то пошло), конечно, зависит от контекста.
Мне это нравится, но я столкнулся с проблемой: в Windows 7 / .NET 4.5 эта строка TDerived instance = (TDerived)field.GetValue(null); приводит к тому, что instance становится null. Кажется, что среда выполнения Mono должна иметь другой порядок инициализации типов, чем .NET, который позволяет этому работать. Непонятно! Вместо этого мне пришлось переместить этот код в статический метод и вызвать его из инициализатора типа в подклассе.
@agentnega Спасибо за это добавление. Это может кому-то помочь.
@agentnega У меня такая же проблема в .net 4.5.1. Кажется, что "нарушает" Спецификация C# b / c, он не инициализирует значение перед первым использованием - по крайней мере, не при использовании отражения. Я реализовал обходной путь, который не требует участия подкласса (TDerived). @ sehe, мне следует отредактировать ваш ответ и добавить обходной путь к вашему ответу или опубликовать новый ответ?
@agentnega, @ sehe тем временем я опубликовал обходной путь (который должен работать в моно и "нормальном" .net) в отдельном ответе, см .: stackoverflow.com/a/30395889/684096
@BatteryBackupUnit Не стесняйтесь редактировать этот ответ. Я постараюсь вернуться к этому позже
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";
но зачем строить?
На самом деле нет причин. Полагаю, вы могли бы использовать static class. В окончательном коде IL нет аргументов в пользу того или иного преимущества.
Я работал над проблемой с ответ при запуске кода в MS .net (не Mono). Для меня проблема возникла в .net 4.5.1, но, похоже, затронуты и другие версии.
доступ к public static TDervied MyEnumValue посредством отражения (через FieldInfo.GetValue(null) инициализирует ли нет указанное поле.
Вместо присвоения имен экземплярам TDerived при статическом инициализаторе RichEnum<TValue, 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();
}
Приведенный выше код не включает все функции исходного ответа отметка!
Спасибо отметка за предоставление своей реализации RichEnum и спасибо sehe за предоставление некоторых улучшений!
Я нашел еще более простое решение, взятое отсюда. 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);
}
}
перечисления в значительной степени бесполезны для меня из-за этого OP.
В итоге я все время занимаюсь фотографиями:
Классическим примером проблемы является набор 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, потому что он не может неявно преобразовать enum в ushort (хотя мы даже основали перечисление на ushort)
в этом конкретном контексте перечисления устарели следующей структурой данных . . . .
public static class VKeys
{
public const ushort
a = 1,
b = 2,
c = 3;
}
Я создал эту утилиту, чтобы помочь мне преобразовать Enum в PrimitiveEnum и PrimitiveEnum в byte, sbyte, short, ushort, int, uint, long, or ulong.
Таким образом, это технически преобразует любое перечисление в любое его примитивное значение.
public enum MyEnum
{
one = 1, two = 2
}
PrimitiveEnum number = MyEnum.one;
long i = number;
См. Фиксацию в 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);
}
}
}
+1 У меня есть игровой фреймворк со многими вещами, идентифицированными uint, для которых сама игра обычно делает enum, но фреймворк ничего не знает. Наличие (uint) при вызове фреймворка было проблемой. Ваша идея задом наперед работает отлично. Вместо struct, хранящего Enum, у меня есть struct IdNumber, который хранит uint, но неявно конвертирует из Enum, которые использует игра. Вместо того, чтобы вводить параметры фреймворка как uint, я могу ввести их как IdNumber, и фреймворк сможет эффективно передавать их внутри, даже выполняя с ними комплексные операции.
@BatteryBackupUnit Эй, это звучит как классное решение, но не могли бы вы объяснить эту часть здесь?
Поскольку я получаю с .NET 4.7.2 "InvalidCastException", к сожалению: /
_name = new Lazy<string>(
() => EnumInstanceToNameMapping
.First(x => ReferenceEquals(this, x.Instance))
.Name);
Я не знаю почему, я создал производный тип RichEnum и инициализировал, как все, что вы делали в примере, но я получаю это исключение annyoingg ..
Был бы рад некоторой помощи в этом, так как мне очень нравится этот подход.
Я тоже хочу это сделать. У нас есть перечисление
enum YesNo {Yes, No}, которое можно неявно преобразовать в bool.