C # 'da numaralandırmaların örtük dönüşümlerini tanımlayabilir miyiz?


129

C # 'da numaralandırmaların örtük bir dönüşümünü tanımlamak mümkün müdür?

bunu başarabilecek bir şey?

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

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

Değilse neden olmasın?


2
Bunu ben de yapmak isterim. enum YesNo {Yes, No}Örtük olarak bool'a dönüşebilen bir numaralandırmamız var.
Albay Panic

Bu konseptin derleyici tipi güvenlik kontrolünü devre dışı bıraktığına dikkat edin. Daha uzun vadede, sondaki '~' gibi açık bir dönüştürme kısaltması daha iyi olabilir.
crokusek

Bağlantı artık geçerli değil - bağlantıyı kaldırabilir miyiz veya web sitesini bir yerde yeniden yayınlayabilir miyiz?
ワ イ き ん ぐ

Yanıtlar:


128

Bir çözüm var. Aşağıdakileri göz önünde bulundur:

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

Yukarıdakiler örtük dönüştürme sunar:

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

Bu, normal bir enum bildirmekten biraz daha fazla iştir (ancak yukarıdakilerin bazılarını ortak bir genel temel sınıfa yeniden düzenleyebilirsiniz). Temel sınıfın IComparable & IEquatable uygulamasına sahip olmanın yanı sıra DescriptionAttributes, bildirilen adlar vb. Değerlerini döndürmek için yöntemler ekleyerek daha da ileri gidebilirsiniz.

Grunt çalışmasının çoğunu işlemek için bir temel sınıf (RichEnum <>) yazdım, bu da yukarıdaki numaralandırma bildirimini şu şekilde kolaylaştırır:

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

Temel sınıf (RichEnum) aşağıda listelenmiştir.

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

Gönderideki küçük bir güvenlik hatası düzeltildi :-) Genel statik örtük operatörü AccountStatus (bayt değeri) {return Convert (değer); } Dönüştürme (bayt) döndürülmez;
Mehdi LAMRANI

Bu temel sınıf derlemesini yaptım. Değişiklikleri düzenlememin sakıncası var mı?
sehe

64
Bu çözüm, bir alıştırma olarak veya birinin programlama becerilerini test etmek için 'doğru' olabilir, ancak lütfen bunu gerçek hayatta yapmayın. Sadece aşırı öldürmekle kalmıyor, verimsiz, sürdürülemez ve cehennem kadar çirkin. Sırf bunun için bir sıralama kullanmanıza gerek yok. Ya açık bir tür atarsınız ya da sabitlerle statik bir sınıf yazarsınız.
Trap

3
Temelde yeniden uygulanan Java listesi değil mi?
Agent_L

2
Önemli bir sorun, bu statik salt okunur sabitleri switch deyimlerinde kullanamamanızdır.
Ian Goldby

34

Örtülü dönüşümler yapamazsınız (sıfır hariç) ve kendi örnek yöntemlerinizi yazamazsınız - ancak muhtemelen kendi uzantı yöntemlerinizi yazabilirsiniz:

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

Yine de bu size pek bir şey vermez (sadece açık bir oyuncu kadrosuna kıyasla).

İnsanların bunu istediğini gördüğüm ana zamanlardan biri, [Flags]jenerikler aracılığıyla manipülasyon yapmaktı - yani bir bool IsFlagSet<T>(T value, T flag);yöntem. Maalesef, C # 3.0, jeneriklerle ilgili operatörleri desteklemez, ancak bunun gibi şeyleri kullanarak bunun üstesinden gelebilirsiniz , bu da operatörlerin jeneriklerle tamamen kullanılabilir olmasını sağlar.


Evet, bu C # 4 için en çok arananlardan biriydi: stackoverflow.com/questions/138367/… ve stackoverflow.com/questions/7244
Keith

@Keith - iyi iş başardı, o zaman ;-p Dinamik / operatör desteği CTP'ye girmedi, ancak operatörler için iki yaklaşımı dinamik ( vs jenerik / İfade) oraya vardığında.
Marc Gravell

@Keith - MiscUtil'de Operator sınıfına bir girdap vermek isteyebilirsiniz; İstediğinin çoğunu yapacağından oldukça eminim.
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";

ama neden struct?
Konrad

1
Sebep yok gerçekten. static classSanırım kullanabilirsin . Nihai ILkodda her iki durumu da tartışmanın bir avantajı yok .
Glenn Slayden

18

Mark'ın mükemmel RichEnum genel temel sınıfını uyarladım.

tespit

  1. kütüphanelerindeki eksik bitlerden kaynaklanan bir dizi derleme problemi (özellikle: kaynağa bağlı görünen adlar tamamen kaldırılmadı; şimdi onlar)
  2. Başlatma mükemmel değildi: Yaptığınız ilk şey temel sınıftan statik .Values ​​özelliğine erişmek olsaydı, bir NPE elde edersiniz. CheckInitialized sırasında tam zamanında Terived'in statik yapısını ilginç bir şekilde yinelemeli ( CRTP ) zorlamak için temel sınıfı zorlayarak bu sorunu giderildi.
  3. Sonunda CheckInitialized mantığı statik bir kurucuya taşındı (her seferinde kontrol etme cezasından kaçınmak için, çok iş parçacıklı başlatma üzerindeki yarış durumu; belki de bu benim madde 1'im tarafından çözülen imkansız bir şeydi?)

Muhteşem fikir ve uygulama için İşaretlemekten Tebrikler, işte hepinize:

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Diagnostics;
using System.Linq;
using System.Reflection;
using System.Resources;

namespace NMatrix
{

    [DebuggerDisplay("{Value} ({Name})")]
    public abstract class RichEnum<TValue, TDerived>
                : IEquatable<TDerived>,
                  IComparable<TDerived>,
                  IComparable, IComparer<TDerived>
        where TValue : struct, IComparable<TValue>, IEquatable<TValue>
        where TDerived : RichEnum<TValue, TDerived>
    {
        #region Backing Fields

        /// <summary>
        /// The value of the enum item
        /// </summary>
        public readonly TValue Value;

        /// <summary>
        /// The public field name, determined from reflection
        /// </summary>
        private string _name;

        /// <summary>
        /// The DescriptionAttribute, if any, linked to the declaring field
        /// </summary>
        private DescriptionAttribute _descriptionAttribute;

        /// <summary>
        /// Reverse lookup to convert values back to local instances
        /// </summary>
        private static readonly SortedList<TValue, TDerived> _values = new SortedList<TValue, TDerived>();

        #endregion

        #region Constructors

        protected RichEnum(TValue value)
        {
            this.Value = value;
            _values.Add(value, (TDerived)this);
        }

        #endregion

        #region Properties

        public string Name
        {
            get
            {
                return _name;
            }
        }

        public string Description
        {
            get
            {
                if (_descriptionAttribute != null)
                    return _descriptionAttribute.Description;

                return _name;
            }
        }

        #endregion

        #region Initialization

        static RichEnum()
        {
            var fields = typeof(TDerived)
                .GetFields(BindingFlags.Static | BindingFlags.GetField | BindingFlags.Public)
                .Where(t => t.FieldType == typeof(TDerived));

            foreach (var field in fields)
            {
                /*var dummy =*/ field.GetValue(null); // forces static initializer to run for TDerived

                TDerived instance = (TDerived)field.GetValue(null);
                instance._name = field.Name;
                                    instance._descriptionAttribute = field.GetCustomAttributes(true).OfType<DescriptionAttribute>().FirstOrDefault();
            }
        }

        #endregion

        #region Conversion and Equality

        public static TDerived Convert(TValue value)
        {
            return _values[value];
        }

        public static bool TryConvert(TValue value, out TDerived result)
        {
            return _values.TryGetValue(value, out result);
        }

        public static implicit operator TValue(RichEnum<TValue, TDerived> value)
        {
            return value.Value;
        }

        public static implicit operator RichEnum<TValue, TDerived>(TValue value)
        {
            return _values[value];
        }

        public static implicit operator TDerived(RichEnum<TValue, TDerived> value)
        {
            return value;
        }

        public override string ToString()
        {
            return _name;
        }

        #endregion

        #region IEquatable<TDerived> Members

        public override bool Equals(object obj)
        {
            if (obj != null)
            {
                if (obj is TValue)
                    return Value.Equals((TValue)obj);

                if (obj is TDerived)
                    return Value.Equals(((TDerived)obj).Value);
            }
            return false;
        }

        bool IEquatable<TDerived>.Equals(TDerived other)
        {
            return Value.Equals(other.Value);
        }


        public override int GetHashCode()
        {
            return Value.GetHashCode();
        }

        #endregion

        #region IComparable Members

        int IComparable<TDerived>.CompareTo(TDerived other)
        {
            return Value.CompareTo(other.Value);
        }

        int IComparable.CompareTo(object obj)
        {
            if (obj != null)
            {
                if (obj is TValue)
                    return Value.CompareTo((TValue)obj);

                if (obj is TDerived)
                    return Value.CompareTo(((TDerived)obj).Value);
            }
            return -1;
        }

        int IComparer<TDerived>.Compare(TDerived x, TDerived y)
        {
            return (x == null) ? -1 :
                   (y == null) ? 1 :
                    x.Value.CompareTo(y.Value);
        }

        #endregion

        public static IEnumerable<TDerived> Values
        {
            get
            {
                return _values.Values;
            }
        }

        public static TDerived Parse(string name)
        {
            foreach (TDerived value in Values)
                if (0 == string.Compare(value.Name, name, true))
                    return value;

            return null;
        }
    }
}

Mono üzerinde çalıştırdığım bir kullanım örneği:

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

Çıktı üretmek

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

Not: mono 2.6.7, mono 2.8.2 kullanılırken gerekli olmayan ekstra bir açık dönüşüm gerektirir ...


Description özelliğini almak için .Single () kullanmak iyi bir fikir değildir. Öznitelik yoksa Single () bir istisna atar, SingleOrDefault () yapmaz.
kerem

@kerem iyi bir nokta, onu güncelledim ( FirstOrDefaultsadece tek bir öznitelik olduğunu varsaymaktan kaçınmak için kullanarak ). Bu tür şeyleri varsaymanın 'iyi bir fikir' (veya bu konuda kötü bir fikir ) olup olmadığı elbette içeriğe bağlıdır
sehe

1
Bunu sevdim, ancak bir sorunla karşılaştım: Windows 7 / .NET 4.5'te bu satır oluşuyla TDerived instance = (TDerived)field.GetValue(null);sonuçlanıyor . Görünüşe göre Mono çalışma zamanı, bunun çalışmasına izin veren .NET'ten farklı bir tür başlatma sırasına sahip olmalıdır. Şaşırtıcı! Bunun yerine bu kodu statik bir yönteme taşımak ve alt sınıftaki tür başlatıcıdan çağırmak zorunda kaldım. instancenull
agentnega

@agentnega Eklediğiniz için teşekkürler. Birine yardımcı olabilir.
sehe

@agentnega .net 4.5.1'de aynı sorunu yaşıyorum. C # belirtimini "ihlal ediyor" görünüyor, b / c değeri ilk kullanımdan önce başlatmıyor - en azından yansıma kullanırken değil. Alt sınıfın ('TDerived') dahil edilmesini gerektirmeyen bir geçici çözüm uyguladım. @ sehe cevabınızı düzenlemeli ve cevabınıza geçici çözümü eklemeli miyim yoksa yeni bir cevap mı göndermeliyim?
BatteryBackupUnit

5

Yöntemleri tanımlayamadıkları için, enum türlerinde örtük dönüştürmeler bildiremezsiniz. C # örtük anahtar sözcüğü 'op_' ile başlayan bir yöntemde derlenir ve bu durumda işe yaramaz.


4

Muhtemelen yapabilirsiniz, ancak sıralama için değil (buna bir yöntem ekleyemezsiniz). Bir numaralamanın ona dönüştürülmesine izin vermek için kendi sınıfınıza örtük bir dönüşüm ekleyebilirsiniz,

public class MyClass {

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

MyClass m = MyEnum.One;

Soru neden olacak?

Genel olarak .Net, verilerin kaybolabileceği herhangi bir örtük dönüşümden kaçınır (ve siz de yapmalısınız).


3

Numaralamanın tabanını uzun olarak tanımlarsanız, açık dönüştürme gerçekleştirebilirsiniz. Numaralandırmalar üzerinde tanımlanmış yöntemler olamayacağından örtük dönüştürmeleri kullanıp kullanamayacağınızı bilmiyorum.

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

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

Ayrıca, bununla, sıfırlanmış bir numaralandırmanın varsayılan olarak 0 değerine veya ilk öğeye sahip olacağının farkında olun - bu nedenle, yukarıdaki durumda da muhtemelen en iyisi tanımlamak olacaktır zero = 0.


5
Buraya ihtiyacınız : longyok; açık dönüştürme, onsuz iyi çalışır. Tek yasal örtük dönüştürme sıfırdır.
Marc Gravell

3
Hayır; varsayılan numaralandırma Int32
Marc Gravell

1
Bakınız: enum Foo {A, B, C} Console.WriteLine (Enum.GetUnderlyingType (typeof (Foo)));
Marc Gravell

14
Neden bu cevap olarak işaretlendi ve bu kadar çok puanı var? Bu, OP sorusu ile ilgili DEĞİLDİR !!! IMPLICIT Conversion'dan bahsediyor ... Katma değer sıfır.
Mehdi LAMRANI

3
Soru zaten açık yayınların anlaşıldığını ima ediyor, soru, BU gönderinin geçerli olmadığı "Açıkça yayınlamadan nasıl kaçınırım?" Sorusuna eşdeğerdir.
Kit10

2

numaralandırmalar bu nedenle benim için büyük ölçüde işe yaramaz, OP.

Her zaman resimle ilgili yapıyorum:

basit çözüm

Klasik örnek problem, tuşlara basmaları algılamak için VirtualKey setidir.

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

buradaki sorun şu ki diziyi enum ile indeksleyemezsiniz çünkü enum'u ushort'a örtük olarak dönüştüremez (numaralandırmayı ushort'a dayandırsak bile)

bu özel bağlamda, numaralandırmalar aşağıdaki veri yapısı tarafından geçersiz kılınmıştır. . . .

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

1

Kodu MS .net (Mono olmayan) üzerinde çalıştırırken sehe'nin cevabıyla ilgili bir sorunu çözdüm . Benim için sorun özellikle .net 4.5.1'de meydana geldi, ancak diğer sürümler de etkilenmiş görünüyor.

Sorun

Bir erişerek public static TDervied MyEnumValue(yansıma yoluyla yoluyla FieldInfo.GetValue(null)yok değil initialize alan söyledi.

Geçici çözüm

Bunun TDerivedstatik başlatıcısı üzerine örneklere ad atamak yerine, RichEnum<TValue, TDerived>öğesine ilk erişimde tembel olarak yapılır TDerived.Name. Kod:

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

ki - benim durumumda - şunlara dayanmaktadır 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();
}

Not

Yukarıdaki kod, Mark'ın orijinal cevabının tüm özelliklerini içermiyor !

Teşekkürler

Sayesinde Mark onun sağlamak için RichEnumiçin uygulanmasını ve teşekkür sehe bazı iyileştirmeler sağlamak için!


1

Buradan /codereview/7566/enum-vs-int-wrapper-struct adresinden alınan daha da kolay bir çözüm buldum . Gelecekte çalışmaması durumunda aşağıdaki kodu bu bağlantıdan yapıştırdım.

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

Bana bir dönüştürmek yardım için bu programı oluşturdu Enum için PrimitiveEnum ve PrimitiveEnum içinbyte, sbyte, short, ushort, int, uint, long, or ulong .

Dolayısıyla, bu teknik olarak herhangi bir numaralandırmayı herhangi bir ilkel değerine dönüştürür.

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 adresinde taahhüt bakın

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 uintOyunun kendisinin genellikle s yaptığı birçok şeyin olduğu bir oyun çerçevem ​​var enum, ancak çerçeve hakkında hiçbir şey bilmiyor. Zorunda (uint)çerçeveyi çağrılırken bir ağrı oldu. Geriye dönük fikriniz mükemmel çalışıyor. structDepolamak yerine, bir olana Enumsahibim ama bunları struct IdNumbersaklayan uintama Enumoyunun kullandığı s'lerden örtük olarak dönüştüren . Çerçevenin parametrelerini olduğu gibi yazmak yerine, uintonları yazabilirim IdNumberve çerçeve bunları dahili olarak verimli bir şekilde dolaştırabilir, hatta bunlara entegre operasyonlar yapabilir.
Kevin

-2

Numaralandırma türleri için örtük dönüşümlerin tanıtılması, tür güvenliğini bozar, bu nedenle bunu yapmanızı önermem. Bunu neden yapmak istersiniz? Bunun için gördüğüm tek kullanım durumu, enum değerlerini önceden tanımlanmış bir düzene sahip bir yapıya koymak istediğiniz zamandır. Ancak o zaman bile, yapıda enum türünü kullanabilir ve sadece Marshaller'a bununla ne yapması gerektiğini söyleyebilirsiniz.


Numaralandırmaları örtük olarak dönüştürmek için bir kullanımım var. Aynı site koleksiyonunun birden çok sitesinde LINQ to SharePoint sınıfları oluşturmak için SPMetal kullanma. Listelerimden bazıları bir alt sitede, diğerleri farklı bir alt sitede. SPMetal'in kodu nasıl ürettiğine bağlı olarak, koleksiyonun birden çok listesinde kullanılan site sütunları birden çok ad alanında tanımlanabilir. Ancak, bir ad alanındaki seçim alanı numaralandırmasını başka bir ad alanındaki aynı numaralandırmaya dönüştürmem gerekiyor. Örtük dönüştürme çok yardımcı olacaktır.
Zarepheth
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.