Bisakah kita mendefinisikan konversi enum implisit dalam c #?

129

Apakah mungkin untuk mendefinisikan konversi enum secara implisit dalam c #?

sesuatu yang bisa mencapai ini?

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

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

Jika tidak, mengapa tidak?

Adam Naylor
sumber
2
Saya juga ingin melakukan ini. Kami memiliki enum enum YesNo {Yes, No}yang secara implisit dapat dikonversi menjadi bool.
Kolonel Panic
Memperhatikan bahwa konsep ini menonaktifkan pemeriksaan keamanan tipe kompiler. Jangka panjang, singkatan konversi eksplisit seperti trailing '~' mungkin lebih baik.
crokusek
Tautan tidak lagi valid - bisakah kita menghapus tautan, atau mengirim ulang situs web di suatu tempat?
ワ イ き ん ぐ

Jawaban:

128

Ada solusinya. Pertimbangkan yang berikut ini:

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

Di atas menawarkan konversi implisit:

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

Ini adalah pekerjaan yang agak lebih adil daripada mendeklarasikan enum normal (meskipun Anda dapat mengubah beberapa hal di atas menjadi kelas dasar umum umum). Anda dapat melangkah lebih jauh dengan meminta kelas dasar mengimplementasikan IComparable & IEquatable, serta menambahkan metode untuk mengembalikan nilai DescriptionAttributes, nama yang dideklarasikan, dll., Dll.

Saya menulis kelas dasar (RichEnum <>) untuk menangani sebagian besar pekerjaan kasar, yang memudahkan deklarasi enum di atas ke:

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

Kelas dasar (RichEnum) tercantum di bawah ini.

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;
        }
    }
}
Menandai
sumber
Mengoreksi sedikit kesalahan pengawal di pos :-) Ini operator publik implisit, AccountStatus (nilai byte) {return Convert (nilai); } JANGAN mengembalikan Konversi (byte);
Mehdi LAMRANI
Saya membuat kompilasi baseclass ini. Apakah Anda keberatan jika saya mengedit di perubahan?
sehe
64
Solusi ini mungkin 'benar' sebagai latihan, atau menguji kemampuan pemrograman seseorang tetapi, tolong jangan lakukan ini dalam kehidupan nyata. Tidak hanya itu berlebihan, itu tidak produktif, tidak dapat dipelihara dan jelek sekali. Anda tidak perlu menggunakan enum hanya untuk itu. Anda bisa memasang pemeran eksplisit atau hanya menulis kelas statis dengan const ints.
Perangkap
3
Bukankah pada dasarnya Java Java diimplementasikan ulang?
Agent_L
2
Salah satu masalah utama adalah bahwa Anda tidak dapat menggunakan konstanta readonly statis itu dalam pernyataan switch.
Ian Goldby
34

Anda tidak dapat melakukan konversi tersirat (kecuali untuk nol), dan Anda tidak dapat menulis metode contoh Anda sendiri - namun, Anda mungkin dapat menulis metode ekstensi Anda sendiri:

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

Ini tidak memberi Anda banyak, meskipun (dibandingkan dengan hanya melakukan pemeran eksplisit).

Salah satu waktu utama saya melihat orang menginginkan ini adalah untuk melakukan [Flags]manipulasi melalui obat generik - yaitu bool IsFlagSet<T>(T value, T flag);metode. Sayangnya, C # 3.0 tidak mendukung operator pada obat generik, tetapi Anda dapat menyiasati hal ini menggunakan hal-hal seperti ini , yang membuat operator sepenuhnya tersedia dengan obat generik.

Marc Gravell
sumber
Ya, itu adalah salah satu yang paling saya inginkan untuk C # 4: stackoverflow.com/questions/138367/… dan stackoverflow.com/questions/7244
Keith
@Keith - pekerjaan bagus yang membuatnya, kemudian ;-p Dukungan dinamis / operator tidak berhasil masuk ke dalam CTP, tapi saya punya rig uji siap-pakai untuk membandingkan dua pendekatan untuk operator dengan dinamis ( vs obat generik / Ekspresi) ketika sampai di sana.
Marc Gravell
@Keith - Anda mungkin ingin memberikan kelas Operator di MiscUtil berputar; Saya cukup yakin itu akan melakukan sebagian besar dari apa yang Anda inginkan.
Marc Gravell
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";
Glenn Slayden
sumber
tapi mengapa struct?
Konrad
1
Tidak ada alasan kok. Anda bisa menggunakan static classsaya kira. Tidak ada untungnya berdebat untuk kedua kasus dalam ILkode akhir .
Glenn Slayden
18

Saya mengadaptasi baseclass generik yang sangat baik dari Markus RichEnum.

Pemasangan

  1. sejumlah masalah kompilasi karena bit-bit yang hilang dari perpustakaannya (terutama: nama tampilan yang bergantung pada sumber daya tidak sepenuhnya dihapus; sekarang ada)
  2. inisialisasi tidak sempurna: jika hal pertama yang Anda lakukan adalah mengakses properti .Values ​​statis dari kelas dasar, Anda akan mendapatkan NPE. Memperbaiki ini dengan memaksa kelas dasar untuk penasaran-recursively ( CRTP ) memaksa konstruksi statis TDerived tepat pada waktunya selama CheckInitialized
  3. akhirnya memindahkan logika CheckInitialized ke konstruktor statis (untuk menghindari hukuman memeriksa setiap kali, kondisi balapan pada inisialisasi multithreaded; mungkin ini adalah suatu kemustahilan yang dipecahkan oleh peluru saya 1.?)

Kudos to Mark untuk ide + implementasi indah, ini untuk Anda semua:

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

Contoh penggunaan yang saya jalankan di 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);
        }
    }
}

Memproduksi output

[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)

Catatan: mono 2.6.7 membutuhkan pemeran ekstra eksplisit yang tidak diperlukan saat menggunakan mono 2.8.2 ...

lihat
sumber
Menggunakan .Single () untuk mendapatkan atribut deskripsi bukanlah ide yang baik. Jika tidak ada atribut, Single () melempar pengecualian, SingleOrDefault () tidak.
kerem
@ poin baik, saya memperbaruinya (menggunakan FirstOrDefault, untuk menghindari asumsi hanya ada satu atribut). Apakah mengasumsikan hal-hal seperti itu adalah 'ide yang baik' (atau yang buruk , dalam hal ini) tentu saja, tergantung pada konteksnya
sehe
1
Cinta ini, tapi aku berlari ke masalah: pada Windows 7 / NET 4.5 baris ini TDerived instance = (TDerived)field.GetValue(null);hasil instancemenjadi null. Tampaknya runtime Mono harus memiliki urutan inisialisasi tipe yang berbeda dari .NET yang memungkinkan ini berfungsi. Membingungkan! Sebagai gantinya saya harus memindahkan kode itu ke metode statis dan menyebutnya dari tipe initializer di subclass.
agentnega
@ agentnega Terima kasih untuk penambahan itu. Mungkin membantu seseorang.
lihat
@ agentnega saya mengalami masalah yang sama pada .net 4.5.1. Tampaknya "melanggar" spesifikasi C # b / c itu tidak menginisialisasi nilai sebelum digunakan pertama - setidaknya tidak ketika menggunakan refleksi. Saya telah mengimplementasikan solusi yang tidak memerlukan subclass ('TDerived') untuk terlibat. @ sehe haruskah saya mengedit jawaban Anda dan menambahkan solusi untuk jawaban Anda atau haruskah saya mengirim jawaban baru?
BatteryBackupUnit
5

Anda tidak dapat mendeklarasikan konversi implisit pada tipe enum, karena mereka tidak dapat menentukan metode. Kata kunci implisit C # mengkompilasi menjadi metode yang dimulai dengan 'op_', dan itu tidak akan berfungsi dalam kasus ini.

Igal Tabachnik
sumber
4

Anda mungkin bisa, tetapi tidak untuk enum (Anda tidak dapat menambahkan metode untuk itu). Anda bisa menambahkan konversi implisit ke kelas Anda sendiri untuk memungkinkan enum dikonversi ke dalamnya,

public class MyClass {

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

MyClass m = MyEnum.One;

Pertanyaannya adalah mengapa?

Secara umum. Net menghindari (dan Anda juga harus) setiap konversi implisit di mana data dapat hilang.

Keith
sumber
3

Jika Anda menetapkan basis enum sebagai panjang maka Anda dapat melakukan konversi eksplisit. Saya tidak tahu apakah Anda dapat menggunakan konversi tersirat karena enum tidak dapat memiliki metode yang ditentukan pada mereka.

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

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

Perlu diketahui juga bahwa enumerasi yang tidak diinisialisasi akan default ke nilai 0, atau item pertama - jadi dalam situasi di atas mungkin akan lebih baik untuk mendefinisikan zero = 0juga.

Abu
sumber
5
Anda tidak perlu di : longsini; konversi eksplisit akan bekerja dengan baik tanpa itu. Satu-satunya konversi implisit legal adalah nol.
Marc Gravell
3
Tidak; enum default adalah Int32
Marc Gravell
1
Lihat: enum Foo {A, B, C} Console.WriteLine (Enum.GetUnderlyingType (typeof (Foo)));
Marc Gravell
14
MENGAPA ini ditandai sebagai jawaban dan memiliki banyak poin? Ini TIDAK relevan dengan pertanyaan OP !!! Dia berbicara tentang Konversi IMPLICIT ... Nilai tambahnya adalah nol.
Mehdi LAMRANI
3
Pertanyaannya sudah menyiratkan bahwa para pemain eksplisit dipahami, pertanyaan itu setara dengan menanyakan "Bagaimana saya menghindari casting secara eksplisit?", Yang tidak berlaku untuk posting INI.
Kit10
2

enum sebagian besar tidak berguna bagi saya karena ini, OP.

Saya akhirnya melakukan hal-hal yang berhubungan dengan foto setiap saat:

solusi sederhana

Contoh masalah klasik adalah set VirtualKey untuk mendeteksi penekanan tombol.

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]; 

Masalahnya di sini adalah Anda tidak dapat mengindeks array dengan enum karena tidak dapat secara implisit mengkonversi enum ke ushort (meskipun kami bahkan mendasarkan enum pada ushort)

dalam konteks khusus ini, enum dihancurkan oleh struktur data berikut. . . .

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

Saya telah mengatasi masalah dengan jawaban sehe ketika menjalankan kode pada MS .net (non-Mono). Bagi saya secara khusus masalah terjadi pada .net 4.5.1 tetapi versi lain juga terpengaruh.

Masalah

mengakses public static TDervied MyEnumValuedengan refleksi (via FieldInfo.GetValue(null)tidak tidak initialize kata lapangan.

Penanganannya

Alih-alih menugaskan nama untuk TDerivedinstance pada penginisialisasi statis RichEnum<TValue, TDerived>ini dilakukan malas pada akses pertama TDerived.Name. Kode:

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

yang - dalam kasus saya - didasarkan pada 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();
}

Catatan

Kode di atas tidak memasukkan semua fitur jawaban asli Markus !

Terima kasih

Terima kasih kepada Mark karena menyediakan RichEnumimplementasinya dan terima kasih kepada Sehe karena telah memberikan beberapa perbaikan!

BatteryBackupUnit
sumber
1

Saya menemukan solusi yang lebih mudah diambil dari sini /codereview/7566/enum-vs-int-wrapper-struct Saya menyisipkan kode di bawah ini dari tautan itu kalau-kalau itu tidak berfungsi di masa depan.

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);
    }
}
adminSoftDK
sumber
1

Saya membuat utilitas ini untuk membantu saya mengonversi Enum ke PrimitiveEnum dan PrimitiveEnum menjadibyte, sbyte, short, ushort, int, uint, long, or ulong .

Jadi, ini secara teknis mengkonversi enum apa pun ke nilai primitifnya.

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

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

Lihat komit di 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);
        }
    }
}
McKabue
sumber
+1 Saya punya kerangka permainan dengan banyak hal yang diidentifikasi oleh uints yang biasanya dibuat oleh permainan itu sendiri enum, tetapi kerangka itu tidak tahu apa-apa tentangnya. Harus (uint)ketika memanggil kerangka kerja itu menyakitkan. Ide Anda mundur bekerja dengan sempurna. Alih-alih structmenyimpan Enum, saya punya struct IdNumberyang menyimpan uinttetapi secara implisit mengkonversi dari Enumpermainan menggunakan. Alih-alih mengetik param kerangka sebagai uint, saya bisa mengetiknya IdNumber, dan kerangka kerja secara internal dapat membagikannya secara efisien, bahkan melakukan operasi integral pada mereka.
Kevin
-2

Memperkenalkan konversi implisit untuk tipe enum akan merusak keamanan tipe, jadi saya tidak akan merekomendasikan untuk melakukannya. Mengapa Anda ingin melakukan itu? Satu-satunya use case untuk ini yang saya lihat adalah ketika Anda ingin meletakkan nilai enum ke dalam struktur dengan tata letak yang telah ditentukan. Tetapi bahkan kemudian, Anda dapat menggunakan tipe enum dalam struktur dan hanya memberi tahu Marshaller apa yang harus ia lakukan dengan ini.

OregonGhost
sumber
Saya telah menggunakan konversi enum secara implisit. Menggunakan SPMetal untuk menghasilkan kelas LINQ ke SharePoint di beberapa situs dari kumpulan situs yang sama. Beberapa daftar saya ada di satu subsite, yang lain di subsite berbeda. Karena cara SPMetal menghasilkan kode, kolom situs yang digunakan dalam beberapa daftar koleksi dapat didefinisikan dalam beberapa ruang nama. Namun, saya perlu mengkonversi antara bidang pilihan enum di satu namespace ke enum yang sama di namespace lain. Konversi tersirat akan sangat membantu.
Zarepheth