Чи можемо ми визначити неявні перетворення перерахунків у c #?


129

Чи можна визначити неявну конверсію переліків у c #?

щось, що могло цього досягти?

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

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

Якщо ні, то чому б ні?


2
Я також хотів би це зробити. У нас є перерахунок, enum YesNo {Yes, No}який може неявно перетворитись на bool.
Полковник Паніка

Зазначаючи, що ця концепція відключає перевірку безпеки компілятора. Більш тривалий термін, явна стенограма конверсії, як кінцевий '~', може бути кращою.
crokusek

Посилання вже не дійсне - ми можемо або видалити посилання, або повернути веб-сайт десь?
ワ イ き ん ぐ

Відповіді:


128

Є рішення. Розглянемо наступне:

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 (значення); } НЕ повертати Перетворити (байт);
Мехді ЛАМРАНІ

Я зробив цей базовий компілятор. Ви не заперечуєте, якщо я редагую зміни?
sehe

64
Це рішення може бути «правильним» як вправа, або перевірка чиїхось навичок програмування, але, будь ласка, не робіть цього в реальному житті. Мало того, що це надмірно, це малопродуктивно, нездійсненно і некрасиво, як пекло. Вам не потрібно використовувати enum лише заради цього. Ви або ставите явний лінійку або просто пишете статичний клас із const ints.
Пастка

3
Хіба це в основному не повторно реалізовано Java enum?
Agent_L

2
Однією з головних проблем є те, що ви не можете використовувати ці статичні константи, що читаються тільки в операторах переключення.
Ян Голдбі

34

Ви не можете робити неявні перетворення (крім нуля), а також не можете писати власні методи екземпляра - однак, ви, ймовірно, можете написати власні методи розширення:

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 крутіння; Я впевнений, що це зробить більшість того, що ви хочете.
Марк Гравелл

22
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";

але чому структура?
Конрад

1
Насправді немає причин. Ви можете використовувати, static classя думаю. Немає переваги, сперечаючись у будь-якому випадку у кінцевому ILкоді.
Гленн Слейден

18

Я адаптував відмінний базовий базовий клас RichEnum від Марка.

Закріплення

  1. низка проблем із компіляцією через відсутні біти в його бібліотеках (зокрема: відображувані імена, що залежать від ресурсів, не були повністю видалені; вони зараз)
  2. ініціалізація не була ідеальною: якщо перше, що ви зробили, це отримати доступ до статичної властивості .Values ​​з базового класу, ви отримаєте NPE. Виправлено це шляхом змушення базового класу до цікаво-рекурсивно ( CRTP) ) змусити статичну побудову TDerived точно вчасно під час CheckInitialized
  3. нарешті перемістив логіку CheckInitialized в статичний конструктор (щоб уникнути штрафу перевірки кожного разу, умова перегону на багатопотокову ініціалізацію; можливо, це було неможливим рішенням моєї кулі 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<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, щоб не припускати, що існує лише один атрибут). Припускати чи ні вважати такі речі "хорошою ідеєю" (або поганою , з цього приводу), звичайно, залежить від контексту
Sehe

1
Люблю це, але я зіткнувся з проблемою: у Windows 7 / .NET 4.5 ця лінія TDerived instance = (TDerived)field.GetValue(null);призводить до instanceіснування null. Здається, що час виконання Mono повинен мати інший порядок ініціалізації типів, ніж той, який працює .NET, який дозволяє це працювати. Неймовірно! Я повинен був замість цього перенести цей код у статичний метод і викликати його з ініціалізатора типів у підкласі.
agentnega

@agentnega Дякую за це доповнення. Це може комусь допомогти.
sehe

@agentnega У мене є те саме питання в .net 4.5.1. Здається, "порушує" специфікацію C # b / c, вона не ініціалізує значення перед першим використанням - принаймні, не під час використання відображення. Я реалізував рішення, яке не вимагає участі підкласу ('TDerived'). @ sehe я повинен редагувати вашу відповідь і додати обмін до вашої відповіді чи я повинен розмістити нову відповідь?
BatteryBackupUnit

5

Ви не можете оголошувати неявні перетворення для типів enum, оскільки вони не можуть визначати методи. У C # неявні компілює ключове слово в метод , починаючи з «op_», і він не буде працювати в цьому випадку.


4

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

public class MyClass {

    public static implicit operator MyClass ( MyEnum input ) {
        //...
    }
}

MyClass m = MyEnum.One;

Питання було б чому?

Взагалі .Net уникає (і вам теж слід) будь-яких неявних перетворень, де дані можуть бути втрачені.


3

Якщо ви визначаєте основу перерахунку як довгу, ви можете виконати явне перетворення. Я не знаю, чи можна використовувати неявні перетворення, оскільки переліки не можуть визначати методи.

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

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

Також майте на увазі, що неініталізоване перерахування за замовчуванням буде значенням 0 або першим пунктом - тому в ситуації, що знаходиться вище, це було б найкраще також визначити zero = 0.


5
Вам тут не потрібно : long; явна конверсія спрацювала б без неї. Єдине юридичне неявне перетворення - нульове.
Марк Гравелл

3
Немає; за замовчуванням
перераховується Int32

1
Див.: Enum Foo {A, B, C} Console.WriteLine (Enum.GetUnderlyingType (typeof (Foo)));
Марк Гравелл

14
Чому це позначено як відповідь і має стільки балів? Це НЕ стосується питання щодо ОП !!! Він говорить про IMPLICIT Conversion ... Додана вартість - нульова.
Мехді ЛАМРАНІ

3
Питання вже означає, що явні ролики розуміються, питання еквівалентно запитуванню "Як я уникаю явної трансляції?", До якої ця публікація не поширюється.
Kit10

2

перерахунки для мене в основному марні через це, ОП.

Я в кінцевому підсумку весь час займаюся pic:

просте рішення

класичний приклад проблеми - це набір 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 (навіть якщо ми навіть базували enum на ushort)

у цьому конкретному контексті перерахунки застаріли за допомогою наступної структури даних. . . .

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

1

Я працював над проблемою з відповіддю Sehe час запуску коду в MS .net (non-Mono). Особливо для мене проблема виникла в .net 4.5.1, але, здається, також впливають й інші версії.

Питання

Доступ до public static TDervied MyEnumValueвідображенню ( з допомогою FieldInfo.GetValue(null)робить НЕ Initialize згаданого поля.

Вирішення проблеми

Замість присвоєння імен 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 за надання деяких удосконалень!


1

Я знайшов ще простіше рішення, взяте звідси /codereview/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);
    }
}

1

Я створив цю утиліту, щоб допомогти мені перетворити 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 У мене є ігрова рамка з багатьма речами, визначеними uints, якими зазвичай гра сама enum, але рамка нічого не знає. Необхідність (uint)при виклику рамки була болем. Ваша ідея назад працює чудово. Замість того, щоб structзберігати Enum, у мене є a, struct IdNumberякий зберігає, uintале неявно перетворюється з Enums, яку гра використовує. Замість того, щоб вводити параметри фреймворку як uint, я можу їх набрати IdNumber, і фреймворк може внутрішньо ефективно їх обходити, навіть роблячи цілісні операційні можливості на них.
Кевін

-2

Введення неявних перетворень для типів enum може порушити безпеку типу, тому я б не рекомендував цього робити. Чому б ти хотів це зробити? Єдиний випадок використання для цього, який я бачив, - це коли ви хочете ввести значення перерахунків у структуру із заздалегідь визначеним компонуванням. Але навіть тоді ви можете використовувати тип enum в структурі і просто сказати Маршаллеру, що він повинен робити з цим.


Я використовую для неявного перетворення перерахунків. Використання SPMetal для генерації LINQ до класів SharePoint на кількох сайтах однієї колекції сайтів. Деякі з моїх списків знаходяться в одному приміщенні, інші - в іншому. Завдяки тому, як SPMetal генерує код, стовпці сайтів, що використовуються у кількох списках колекції, можуть бути визначені у кількох просторах імен. Однак мені потрібно перетворити enum поля вибору в одному просторі імен в той самий enum в іншому просторі імен. Неявне перетворення було б дуже корисно.
Зарефет
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.