Bir Enum'un dize ile temsili


912

Aşağıdaki numaralandırmaya sahibim:

public enum AuthenticationMethod
{
    FORMS = 1,
    WINDOWSAUTHENTICATION = 2,
    SINGLESIGNON = 3
}

Ancak sorun Kimlik 1 değil AuthenticationMethod.FORMS için sorulduğunda "FORMS" kelimesine ihtiyacım var.

Bu sorun için aşağıdaki çözümü buldum ( bağlantı ):

İlk önce "StringValue" adlı özel bir özellik oluşturmak gerekir:

public class StringValue : System.Attribute
{
    private readonly string _value;

    public StringValue(string value)
    {
        _value = value;
    }

    public string Value
    {
        get { return _value; }
    }

}

Sonra bu özelliği numaralandırıcıma ekleyebilirim:

public enum AuthenticationMethod
{
    [StringValue("FORMS")]
    FORMS = 1,
    [StringValue("WINDOWS")]
    WINDOWSAUTHENTICATION = 2,
    [StringValue("SSO")]
    SINGLESIGNON = 3
}

Ve tabii ki bu StringValue almak için bir şeye ihtiyacım var:

public static class StringEnum
{
    public static string GetStringValue(Enum value)
    {
        string output = null;
        Type type = value.GetType();

        //Check first in our cached results...

        //Look for our 'StringValueAttribute' 

        //in the field's custom attributes

        FieldInfo fi = type.GetField(value.ToString());
        StringValue[] attrs =
           fi.GetCustomAttributes(typeof(StringValue),
                                   false) as StringValue[];
        if (attrs.Length > 0)
        {
            output = attrs[0].Value;
        }

        return output;
    }
}

İyi şimdi bir numaralandırıcı için dize değeri elde etmek için araçlar var. Daha sonra şu şekilde kullanabilirsiniz:

string valueOfAuthenticationMethod = StringEnum.GetStringValue(AuthenticationMethod.FORMS);

Tamam şimdi tüm bunlar bir cazibe gibi çalışıyor ama ben bir sürü iş buluyorum. Bunun için daha iyi bir çözüm olup olmadığını merak ediyordum.

Ayrıca sözlük ve statik özellikleri olan bir şey denedim ama bu da daha iyi değildi.


8
Güzel! Enum değerlerini yerelleştirilmiş dizelere çevirmek için bunu kullanabilirim.
Øyvind Skaar

5
Bu uzun soluklu görünse de, aslında başka şeyler için oldukça esnek bir yol. Meslektaşlarımdan birinin işaret ettiği gibi, bu birçok durumda veritabanı kodlarını enum değerlerine vb.
Eşleyen

27
MSDN reccoments "Attribute" soneki bulunan öznitelik sınıfları eki. So "class StringValueAttribute";)
serhio 30:11

14
@BenAlabaster ile katılıyorum, bu aslında oldukça esnektir. Ayrıca, bunu statik yönteminizde thisönüne ekleyerek bir uzantı yöntemi yapabilirsiniz Enum. Sonra yapabilirsin AuthenticationMethod.Forms.GetStringValue();
Justin Pihony

5
Bu yaklaşım öznitelik değerlerini okumak için yansıma kullanır ve deneyimimde birçok kez GetStringValue () yöntemini çağırmanız çok yavaştır. Güvenli tip-enum modeli daha hızlıdır.
Rn222

Yanıtlar:


868

Güvenli yazım şablonunu deneyin .

public sealed class AuthenticationMethod {

    private readonly String name;
    private readonly int value;

    public static readonly AuthenticationMethod FORMS = new AuthenticationMethod (1, "FORMS");
    public static readonly AuthenticationMethod WINDOWSAUTHENTICATION = new AuthenticationMethod (2, "WINDOWS");
    public static readonly AuthenticationMethod SINGLESIGNON = new AuthenticationMethod (3, "SSN");        

    private AuthenticationMethod(int value, String name){
        this.name = name;
        this.value = value;
    }

    public override String ToString(){
        return name;
    }

}

Güncelleme Açık (veya örtük) tür dönüşümü aşağıdakiler tarafından yapılabilir

  • eşleme ile statik alan ekleme

    private static readonly Dictionary<string, AuthenticationMethod> instance = new Dictionary<string,AuthenticationMethod>();
    • nb Örnek yapıcısını çağırırken "enum member" alanlarının başlatılmasının bir NullReferenceException oluşturmaması için, Dictionary alanını sınıfınızdaki "enum member" alanlarının önüne yerleştirdiğinizden emin olun. Bunun nedeni, statik alan başlatıcılarının bildirim sırasında ve statik kurucudan önce çağrılmasıdır, tüm statik alanlar başlatılmadan önce ve statik kurucu çağrılmadan önce örnek yapıcısının çağrılabileceği garip ve gerekli ama kafa karıştırıcı bir durum yaratır.
  • bu eşleştirmeyi örnek yapıcıda doldurma

    instance[name] = this;
  • ve kullanıcı tanımlı tür dönüştürme operatörü ekleme

    public static explicit operator AuthenticationMethod(string str)
    {
        AuthenticationMethod result;
        if (instance.TryGetValue(str, out result))
            return result;
        else
            throw new InvalidCastException();
    }

17
Bir enum gibi görünüyor ama bir enum değil. Eğer insanlar AuthenticationMethods karşılaştırmaya başlarsanız bazı ilginç sorunlara neden olduğunu hayal edebiliyorum. Muhtemelen çeşitli eşitlik operatörlerini de aşırı yüklemeniz gerekir.
Ant

36
@Ant: Mecbur değilim. Her bir AuthenticationMethod öğesinin yalnızca bir örneğine sahip olduğumuzdan, Object'ten devralınan referans eşitliği iyi çalışıyor.
Jakub Šturc

10
@tyriker: Derleyici yapar. Yapıcı özeldir, bu nedenle yeni örnek oluşturamazsınız. Ayrıca statik üyelere örnek yoluyla erişilemez.
Jakub Šturc

21
@ Jakub Çok ilginç. Nasıl kullanılacağını anlamak ve faydalarını anlamak için onunla oynamak zorunda kaldım. Herkese açık, statik olmayan bir sınıftır, ancak somutlaştırılamaz ve yalnızca statik üyelerine erişebilirsiniz. Temel olarak, bir enum gibi davranır. Ama en iyi bölüm ... statik üyeler genel bir dize veya int değil, sınıftan yazılır. Bu ... [bekle] ... güvenli bir numaralandırma yazın! Anlamama yardım ettiğin için teşekkürler.
tyriker

6
@kiran Jakub Šturc'un cevabının, Switch-Case ifadeleriyle kullanılmasına izin veren biraz değiştirilmiş bir versiyonunu yayınladım, bu yüzden şimdi bu yaklaşımın dezavantajı yok :)
deadlydog

228

Kullanım yöntemi

Enum.GetName(Type MyEnumType,  object enumvariable)  

olduğu gibi (Varsayım Shippertanımlı bir Enum'dur)

Shipper x = Shipper.FederalExpress;
string s = Enum.GetName(typeof(Shipper), x);

Enum sınıfında da araştırmaya değer bir sürü başka statik yöntem var ...


5
Kesinlikle. Bir dize açıklaması için özel bir öznitelik yaptım, ama bunun nedeni kolayca bir ComboBox veya benzeri bağlanabilir kullanıcı dostu bir sürüm (boşluklar ve diğer özel karakterler ile) istiyorum.
Ic.

5
Enum.GetName, .ToString () ile aynı numaralandırmadaki alan adlarını yansıtır. Performans bir sorunsa sorun olabilir. Ama enums bir sürü dönüştürmek sürece bu konuda endişe olmaz.
Keith

8
Eğer ekstra işlevsellik ile bir enum gerekiyorsa, bir yapı kullanarak "yr kendi" için düşünmek için başka bir seçenek ... yapı tek tek örnekleri üreten kuruculara başlatılan enum değerleri temsil etmek için statik salt okunur adlandırılmış özellikleri eklemek ...
Charles Bretana

1
o zaman bu "enum" sahip olmasını istediğiniz işlevselliği uygulamak için istediğiniz diğer yapı üyelerini ekleyebilirsiniz ...
Charles Bretana

2
Burada sorun GetName yerelleştirilebilir değildir. Bu her zaman bir endişe kaynağı değildir, ancak farkında olunması gereken bir şeydir.
Joel Coehoorn

79

ToString () kullanarak değer yerine ada başvurabilirsiniz

Console.WriteLine("Auth method: {0}", AuthenticationMethod.Forms.ToString());

Belgeler burada:

http://msdn.microsoft.com/en-us/library/16c1xs4z.aspx

... ve enumlarınızı Pascal Case olarak adlandırırsanız (benim yaptığım gibi - ThisIsMyEnumValue = 1 vb. gibi), o zaman kolay formu yazdırmak için çok basit bir regex kullanabilirsiniz:

static string ToFriendlyCase(this string EnumString)
{
    return Regex.Replace(EnumString, "(?!^)([A-Z])", " $1");
}

herhangi bir dizeden kolayca çağrılabilir:

Console.WriteLine("ConvertMyCrazyPascalCaseSentenceToFriendlyCase".ToFriendlyCase());

Çıktılar:

Benim Crazy Pascal Davası Cümle Dostu Dava Dönüştür

Bu, özel nitelikler oluşturarak ve numaralandırmalarınıza ekleyerek veya enum değerini dostça bir dize ve en iyisi kendi kendini yöneten ve sonsuz olarak kullanılabilen herhangi bir Pascal Case dizesinde kullanılabilecek arama tablolarını kullanarak evlerin etrafından koşarak tasarruf sağlar daha yeniden kullanılabilir. Tabii ki, çözümünüzün sağladığı enumunuzdan farklı bir kolay ada sahip olmanıza izin vermez.

Yine de daha karmaşık senaryolar için orijinal çözümünüzü seviyorum. Çözümünüzü bir adım daha ileri götürebilir ve GetStringValue'yu numaralandırma işleminizin bir uzantı yöntemi haline getirebilir ve daha sonra StringEnum.GetStringValue gibi referans vermeniz gerekmez ...

public static string GetStringValue(this AuthenticationMethod value)
{
  string output = null;
  Type type = value.GetType();
  FieldInfo fi = type.GetField(value.ToString());
  StringValue[] attrs = fi.GetCustomAttributes(typeof(StringValue), false) as StringValue[];
  if (attrs.Length > 0)
    output = attrs[0].Value;
  return output;
}

Daha sonra doğrudan numaralandırma örneğinizden kolayca erişebilirsiniz:

Console.WriteLine(AuthenticationMethod.SSO.GetStringValue());

2
"Kolay ad" ın bir alana ihtiyacı varsa bu yardımcı olmaz. "Form Kimlik Doğrulaması" gibi
Ray Booysen

4
Bu nedenle numaralandırmaya FormsAuthentication gibi büyük harflerin verildiğinden emin olun ve başında olmayan büyük harflerden önce bir boşluk ekleyin. Bir dizeye boşluk eklemek roket bilimi değil ...
BenAlabaster

4
Pascal Vaka adlarının otomatik olarak boşluklandırılması, büyük harfle yazılmış kısaltmalar, XML veya GPS içeriyorsa sorunlu hale gelir.
Richard Ev

2
@RichardEv, bunun için mükemmel bir normal ifade yok ama burada kısaltmalar ile biraz daha iyi çalışması gereken bir tane var. "(?!^)([^A-Z])([A-Z])", "$1 $2". Böylece HereIsATESTolur Here Is ATEST.
sparebytes

Bu küçük "hack'leri" yapıyorlar. OP'nin söylediklerini alıyorum ve benzer bir çözüm bulmaya çalışıyorum, yani Numaralandırmaların zarafetini kullanarak ancak ilişkili mesaja kolayca erişebiliyorum. Aklıma gelen tek çözüm numaralandırma adı ve bir dize değeri arasında bir tür eşleme uygulamaktır, ancak bu dize verilerinin bakımı sorununu ortadan kaldırmaz (ancak çok bölgeye sahip olmanız gereken senaryolar için pratik hale getirir. )
Tahir Halid

72

Ne yazık ki numaralandırmalarda nitelikleri almanın yansıması oldukça yavaştır:

Bu soruya bakın: Bir numaralandırma değerindeki özel özelliklere erişmenin hızlı bir yolunu bilen var mı?

.ToString()Çok çeteleler oldukça yavaştır.

Ancak numaralandırmalar için uzantı yöntemleri yazabilirsiniz:

public static string GetName( this MyEnum input ) {
    switch ( input ) {
        case MyEnum.WINDOWSAUTHENTICATION:
            return "Windows";
        //and so on
    }
}

Bu harika değil, ancak hızlı olacak ve özellikler veya alan adı için yansımayı gerektirmeyecek.


C # 6 Güncellemesi

C 6. kullanabiliyorsa ardından yeni nameofoperatör bu yüzden, çeteleler için çalışır nameof(MyEnum.WINDOWSAUTHENTICATION)dönüştürülecektir "WINDOWSAUTHENTICATION"de derleme zamanında o enum isimleri almanın en hızlı yolu yapım.

Bunun açık numaralandırmayı eğik bir sabite dönüştüreceğini unutmayın, bu nedenle bir değişkende bulunan numaralandırmalar için çalışmaz. Yani:

nameof(AuthenticationMethod.FORMS) == "FORMS"

Fakat...

var myMethod = AuthenticationMethod.FORMS;
nameof(myMethod) == "myMethod"

24
Özellik değerlerini bir kez getirebilir ve bildirim niteliğini korumak için bunları bir Sözlük <MyEnum, string> içine koyabilirsiniz.
Jon Skeet

1
Evet, yansımanın şişe boynu olduğunu öğrendiğimizde çok sayıda numaralandırılmış bir uygulamada bunu yaptık.
Keith

Teşekkürler Jon ve Keith, Sözlük önerinizi kullandım. Harika çalışıyor (ve hızlı!).
Helge Klein

@JonSkeet Bunun eski olduğunu biliyorum. Fakat bunu nasıl başarabiliriz?
user919426

2
@ user919426: İstediğinizi mi elde edersiniz? Onları sözlüğe mi koyuyorsunuz? Sadece bir sözlük oluşturmak, ideal olarak bir koleksiyon başlatıcısı ile ... ne istediğini net değil.
Jon Skeet

59

Bir uzantı yöntemi kullanıyorum:

public static class AttributesHelperExtension
    {
        public static string ToDescription(this Enum value)
        {
            var da = (DescriptionAttribute[])(value.GetType().GetField(value.ToString())).GetCustomAttributes(typeof(DescriptionAttribute), false);
            return da.Length > 0 ? da[0].Description : value.ToString();
        }
}

Şimdi ile süsleyin enum:

public enum AuthenticationMethod
{
    [Description("FORMS")]
    FORMS = 1,
    [Description("WINDOWSAUTHENTICATION")]
    WINDOWSAUTHENTICATION = 2,
    [Description("SINGLESIGNON ")]
    SINGLESIGNON = 3
}

Aradığın zaman

AuthenticationMethod.FORMS.ToDescription()alacaksın "FORMS".


1
using System.ComponentModel;Ayrıca eklemek zorunda kaldım Ayrıca, bu yöntem yalnızca Dize değeri Enum adıyla aynı olmasını istiyorsanız çalışır. OP farklı bir değer istedi.
elcool

2
Aradığın anlamına mı gelmiyorsun AuthenticationMethod.FORMS.ToDescription()?
nicodemus13

41

Sadece ToString()yöntemi kullanın

public enum any{Tomato=0,Melon,Watermelon}

Dizeye başvurmak için şunu Tomatokullanın:

any.Tomato.ToString();

Vay. Kolaydı. OP özel dize açıklamaları eklemek istediğini biliyorum, ama bu ihtiyacım olan şey. Bunu geriye doğru bakmayı denemeliydim, ama Enum.GetName rotasına indim.
Rafe

7
Diğer herkes bunu neden fazla karmaşıklaştırıyor?
Brent

18
@Brent Çünkü çoğunlukla .ToString()ihtiyacınız olan kullanıcı dostu değerden farklı bir değere sahipsiniz.
Novitchi S

2
@Brent - çünkü bu sorulan sorudan farklı. Sorulması gereken soru, bu dizeyi numaralandırılmış bir değer atanmış bir değişkenden nasıl alacağınızdır. Bu, çalışma zamanında dinamiktir. Bu, türün tanımını kontrol eder ve çalışma zamanında ayarlanır.
Hogan

1
@Hogan - ToString () değişkenler üzerinde de çalışır: any fruit = any.Tomato; string tomato = fruit.ToString();
LiborV

29

Net 4.0 ve üstü ile buna çok basit bir çözüm. Başka bir kod gerekmez.

public enum MyStatus
{
    Active = 1,
    Archived = 2
}

Hakkında dize almak için sadece kullanın:

MyStatus.Active.ToString("f");

veya

MyStatus.Archived.ToString("f");`

Değer "Etkin" veya "Arşivlendi" olacaktır.

Arama yaparken farklı dize biçimlerini (yukarıdan "f") Enum.ToStringgörmek için bu Numaralandırma Biçimi Dizeleri sayfasına bakın


28

System.ComponentModel ad alanından Description özniteliğini kullanın. Numaralandırmayı dekore edin ve ardından almak için bu kodu kullanın:

public static string GetDescription<T>(this object enumerationValue)
            where T : struct
        {
            Type type = enumerationValue.GetType();
            if (!type.IsEnum)
            {
                throw new ArgumentException("EnumerationValue must be of Enum type", "enumerationValue");
            }

            //Tries to find a DescriptionAttribute for a potential friendly name
            //for the enum
            MemberInfo[] memberInfo = type.GetMember(enumerationValue.ToString());
            if (memberInfo != null && memberInfo.Length > 0)
            {
                object[] attrs = memberInfo[0].GetCustomAttributes(typeof(DescriptionAttribute), false);

                if (attrs != null && attrs.Length > 0)
                {
                    //Pull out the description value
                    return ((DescriptionAttribute)attrs[0]).Description;
                }
            }
            //If we have no description attribute, just return the ToString of the enum
            return enumerationValue.ToString();

        }

Örnek olarak:

public enum Cycle : int
{        
   [Description("Daily Cycle")]
   Daily = 1,
   Weekly,
   Monthly
}

Bu kod güzel bir "Kolay ad" gerekmez ve enum sadece .ToString () döner enums sunmaktadır.


27

Jakub Šturc'un cevabını gerçekten çok seviyorum, ancak eksiklik, bir anahtar durum ifadesiyle kullanamamanız. Yanıtının bir switch deyimiyle kullanılabilecek biraz değiştirilmiş bir sürümü:

public sealed class AuthenticationMethod
{
    #region This code never needs to change.
    private readonly string _name;
    public readonly Values Value;

    private AuthenticationMethod(Values value, String name){
        this._name = name;
        this.Value = value;
    }

    public override String ToString(){
        return _name;
    }
    #endregion

    public enum Values
    {
        Forms = 1,
        Windows = 2,
        SSN = 3
    }

    public static readonly AuthenticationMethod FORMS = new AuthenticationMethod (Values.Forms, "FORMS");
    public static readonly AuthenticationMethod WINDOWSAUTHENTICATION = new AuthenticationMethod (Values.Windows, "WINDOWS");
    public static readonly AuthenticationMethod SINGLESIGNON = new AuthenticationMethod (Values.SSN, "SSN");
}

Böylece Jakub Šturc'un cevabının tüm avantajlarından faydalanırsınız, ayrıca aşağıdaki gibi bir anahtar ifadesiyle kullanabiliriz:

var authenticationMethodVariable = AuthenticationMethod.FORMS;  // Set the "enum" value we want to use.
var methodName = authenticationMethodVariable.ToString();       // Get the user-friendly "name" of the "enum" value.

// Perform logic based on which "enum" value was chosen.
switch (authenticationMethodVariable.Value)
{
    case authenticationMethodVariable.Values.Forms: // Do something
        break;
    case authenticationMethodVariable.Values.Windows: // Do something
        break;
    case authenticationMethodVariable.Values.SSN: // Do something
        break;      
}

Daha kısa bir çözüm, enumları {} kaldırmak ve bunun yerine kaç Enum oluşturduğunuzu statik olarak saymak olacaktır. Bu, numaralandırma listesine yaptığınız yeni bir örneği eklemek zorunda kalmamanız da yarar sağlar. örneğin, public static int nextAvailable { get; private set; }sonra yapıcıdathis.Value = nextAvailable++;
kjhf

İlginç bir fikir @kjhf. Benim endişem olsa, eğer birisi kodu yeniden sıralarsa, enum değerlerine atanan değer de değişebilir. Örneğin, numaralandırma değeri bir dosyaya / veritabanına kaydedildiğinde yanlış numaralandırma değeri alınmasına neden olabilir, "yeni AuthenticationMethod (...)" satırlarının sırası değiştirilir (örn. Biri kaldırılır) ve sonra uygulamayı tekrar çalıştırmak ve enum değerini dosyadan / veritabanından almak; enum değeri, başlangıçta kaydedilen AuthenticationMethod ile eşleşmeyebilir.
deadlydog

İyi bir nokta - umarım bu özel durumlarda insanlar enum'un tamsayı değerine güvenmezler (veya numaralandırma kodunu yeniden sıralarlar) - ve bu değer tamamen bir anahtar olarak ve muhtemelen .Equals () ve. GetHashCode (). Eğer ilgileniyorsanız, her zaman "DOĞRU DEĞİL" ile büyük bir yorum koyabilirsiniz: p
kjhf

=Anahtarın çalışmasına izin vermek için operatöre aşırı yüklenemez misiniz ? Bunu VB'de yaptım ve şimdi select caseifadede kullanabilirsiniz .
user1318499

@ user1318499 Hayır, C #, switch deyimi çevresinde VB'den daha katı kurallara sahiptir. Case ifadeleri için sınıf örneklerini kullanamazsınız; sadece sabit ilkelleri kullanabilirsiniz.
deadlydog

13

Bazı önbellekleme ile birlikte yukarıdaki önerilerin birkaçından oluşan bir kombinasyon kullanıyorum. Şimdi, internette bir yerde bulduğum bazı kodlardan fikri aldım, ama ne bulduğumu hatırlayamıyorum ya da bulamıyorum. Herkes benzer bir şey bulursa, lütfen atıf ile yorum yapın.

Her neyse, kullanım tip dönüştürücüler içerir, bu nedenle kullanıcı arayüzüne bağlanırsanız 'sadece çalışır'. Tür dönüştürücüsünden statik yöntemlere başlatarak, hızlı kod araması için Jakub'ın deseni ile genişletebilirsiniz.

Temel kullanım şöyle görünecektir

[TypeConverter(typeof(CustomEnumTypeConverter<MyEnum>))]
public enum MyEnum
{
    // The custom type converter will use the description attribute
    [Description("A custom description")]
    ValueWithCustomDescription,

   // This will be exposed exactly.
   Exact
}

Özel numaralandırma türü dönüştürücünün kodu aşağıdaki gibidir:

public class CustomEnumTypeConverter<T> : EnumConverter
    where T : struct
{
    private static readonly Dictionary<T,string> s_toString = 
      new Dictionary<T, string>();

    private static readonly Dictionary<string, T> s_toValue = 
      new Dictionary<string, T>();

    private static bool s_isInitialized;

    static CustomEnumTypeConverter()
    {
        System.Diagnostics.Debug.Assert(typeof(T).IsEnum,
          "The custom enum class must be used with an enum type.");
    }

    public CustomEnumTypeConverter() : base(typeof(T))
    {
        if (!s_isInitialized)
        {
            Initialize();
            s_isInitialized = true;
        }
    }

    protected void Initialize()
    {
        foreach (T item in Enum.GetValues(typeof(T)))
        {
            string description = GetDescription(item);
            s_toString[item] = description;
            s_toValue[description] = item;
        }
    }

    private static string GetDescription(T optionValue)
    {
        var optionDescription = optionValue.ToString();
        var optionInfo = typeof(T).GetField(optionDescription);
        if (Attribute.IsDefined(optionInfo, typeof(DescriptionAttribute)))
        {
            var attribute = 
              (DescriptionAttribute)Attribute.
                 GetCustomAttribute(optionInfo, typeof(DescriptionAttribute));
            return attribute.Description;
        }
        return optionDescription;
    }

    public override object ConvertTo(ITypeDescriptorContext context, 
       System.Globalization.CultureInfo culture, 
       object value, Type destinationType)
    {
        var optionValue = (T)value;

        if (destinationType == typeof(string) && 
            s_toString.ContainsKey(optionValue))
        {
            return s_toString[optionValue];
        }

        return base.ConvertTo(context, culture, value, destinationType);
    }

    public override object ConvertFrom(ITypeDescriptorContext context, 
       System.Globalization.CultureInfo culture, object value)
    {
        var stringValue = value as string;

        if (!string.IsNullOrEmpty(stringValue) && s_toValue.ContainsKey(stringValue))
        {
            return s_toValue[stringValue];
        }

        return base.ConvertFrom(context, culture, value);
    }
}

}


12

Sorunuzda, her yerde enumun sayısal değerine gerçekten ihtiyacınız olduğunu söylemediniz.

Dize türüne ihtiyacınız yoksa ve yalnızca bir dizi numarasına ihtiyacınız varsa (bu ayrılmaz bir tür değildir, bu nedenle numaralandırmanın tabanı olamaz) burada bir yol vardır:

    static class AuthenticationMethod
    {
        public static readonly string
            FORMS = "Forms",
            WINDOWSAUTHENTICATION = "WindowsAuthentication";
    }

referans vermek için enum ile aynı sözdizimini kullanabilirsiniz

if (bla == AuthenticationMethod.FORMS)

Sayısal değerlerden biraz daha yavaş olacaktır (sayılar yerine dizeleri karşılaştırarak), ancak artı tarafta dizeye erişmek için yansıma (yavaş) kullanmaz.


"statik salt okunur" yerine "const" kullanırsanız, değerleri bir switch deyiminde vaka etiketleri olarak kullanabilirsiniz.
Ed N.

11

Bunu bir uzantı yöntemi olarak nasıl çözdüm:

using System.ComponentModel;
public static string GetDescription(this Enum value)
{
    var descriptionAttribute = (DescriptionAttribute)value.GetType()
        .GetField(value.ToString())
        .GetCustomAttributes(false)
        .Where(a => a is DescriptionAttribute)
        .FirstOrDefault();

    return descriptionAttribute != null ? descriptionAttribute.Description : value.ToString();
}

Sıralama:

public enum OrderType
{
    None = 0,
    [Description("New Card")]
    NewCard = 1,
    [Description("Reload")]
    Refill = 2
}

Kullanım (burada o.OrderType, numaralandırma ile aynı ada sahip bir özelliktir):

o.OrderType.GetDescription()

Bu da bana gerçek NewCard ve Refill enum değeri yerine "New Card" veya "Reload" dizesini verir.


Tamlık için DescriptionAttribute sınıfınızın bir kopyasını eklemelisiniz.
Bernie White

3
Bernie, DescriptionAttribute System.ComponentModel'de
ajannega

11

Güncelleme: Bu sayfayı 8 yıl sonra, C # 'a uzun süre dokunmadıktan sonra, cevabım artık en iyi çözüm değil gibi görünüyor. Nitelik fonksiyonları ile bağlanmış dönüştürücü çözümünü gerçekten seviyorum.

Bunu okuyorsanız, lütfen diğer cevapları da kontrol ettiğinizden emin olun.
(ipucu: bunun üzerindedir)


Çoğunuz olarak , Jakub Šturc tarafından seçilen cevabı gerçekten beğendim , ancak kodu kopyalayıp yapıştırmaktan gerçekten nefret ediyorum ve mümkün olduğunca az yapmaya çalışıyorum.

Bu yüzden, işlevselliğin çoğunun miras alınan / yerleşik olduğu bir EnumBase sınıfı istediğime karar verdim, bu da beni davranış yerine içeriğe odaklanmaya bıraktı.

Bu yaklaşımla ilgili temel sorun, Enum değerleri tür güvenli örnekler olmasına rağmen, etkileşimin, Enum Sınıfı türünün Statik uygulaması ile olmasıdır. Jenerik büyü biraz yardımıyla, sonunda doğru karışımı aldım sanırım. Umarım birisi bunu benim kadar yararlı bulur.

Jakub'un örneğiyle başlayacağım, ancak miras ve jenerikleri kullanarak:

public sealed class AuthenticationMethod : EnumBase<AuthenticationMethod, int>
{
    public static readonly AuthenticationMethod FORMS =
        new AuthenticationMethod(1, "FORMS");
    public static readonly AuthenticationMethod WINDOWSAUTHENTICATION =
        new AuthenticationMethod(2, "WINDOWS");
    public static readonly AuthenticationMethod SINGLESIGNON =
        new AuthenticationMethod(3, "SSN");

    private AuthenticationMethod(int Value, String Name)
        : base( Value, Name ) { }
    public new static IEnumerable<AuthenticationMethod> All
    { get { return EnumBase<AuthenticationMethod, int>.All; } }
    public static explicit operator AuthenticationMethod(string str)
    { return Parse(str); }
}

Ve işte temel sınıf:

using System;
using System.Collections.Generic;
using System.Linq; // for the .AsEnumerable() method call

// E is the derived type-safe-enum class
// - this allows all static members to be truly unique to the specific
//   derived class
public class EnumBase<E, T> where E: EnumBase<E, T>
{
    #region Instance code
    public T Value { get; private set; }
    public string Name { get; private set; }

    protected EnumBase(T EnumValue, string Name)
    {
        Value = EnumValue;
        this.Name = Name;
        mapping.Add(Name, this);
    }

    public override string ToString() { return Name; }
    #endregion

    #region Static tools
    static private readonly Dictionary<string, EnumBase<E, T>> mapping;
    static EnumBase() { mapping = new Dictionary<string, EnumBase<E, T>>(); }
    protected static E Parse(string name)
    {
        EnumBase<E, T> result;
        if (mapping.TryGetValue(name, out result))
        {
            return (E)result;
        }

        throw new InvalidCastException();
    }
    // This is protected to force the child class to expose it's own static
    // method.
    // By recreating this static method at the derived class, static
    // initialization will be explicit, promising the mapping dictionary
    // will never be empty when this method is called.
    protected static IEnumerable<E> All
    { get { return mapping.Values.AsEnumerable().Cast<E>(); } }
    #endregion
}

Temel statik yapıcıdan türetilmiş statik kurucuyu çağırabilirsiniz. Hala bakıyorum, ama şimdiye kadar onunla ilgili bir sorun bulamadım: stackoverflow.com/questions/55290034/…
Cory-G

10

Keith ile aynı fikirdeyim ama oy kullanamıyorum (henüz).

Tam olarak istediğimi döndürmek için statik bir yöntem ve swith deyimi kullanıyorum. Veritabanında tinyint'i saklıyorum ve kodum sadece gerçek numaralandırmayı kullanıyor, bu nedenle dizeler UI gereksinimleri için. Çok sayıda testten sonra bu, en iyi performans ve çıktı üzerinde en fazla kontrol sağladı.

public static string ToSimpleString(this enum)
{
     switch (enum)
     {
         case ComplexForms:
             return "ComplexForms";
             break;
     }
}

public static string ToFormattedString(this enum)
{
     switch (enum)
     {
         case ComplexForms:
             return "Complex Forms";
             break;
     }
}

Bununla birlikte, bazı hesaplar tarafından, bu olası bir bakım kabusu ve bazı kod kokusuna yol açar. Uzun ve çok sayıda numaralandırılmış veya sık sık değişen numaralara göz kulak olmaya çalışıyorum. Aksi takdirde, bu benim için harika bir çözüm oldu.


10

Buraya basit bir "Enum" uygulamak için geldiyseniz, ancak değerleri ints yerine dize olan, en basit çözüm:

    public sealed class MetricValueList
    {
        public static readonly string Brand = "A4082457-D467-E111-98DC-0026B9010912";
        public static readonly string Name = "B5B5E167-D467-E111-98DC-0026B9010912";
    }

Uygulama:

var someStringVariable = MetricValueList.Brand;

2
Değişkenleri kullanmak yerine sabit yapmak muhtemelen daha iyidir static readonly.
AndyGeek

1
consts genel olarak erişilebilir sınıflar için iyi değildir, derleme zamanında pişirildiklerinden, tüm kodunuzu consts ile derlemeden üçüncü taraf bir DLL'in yerini alamazsınız.
Kristian Williams

7

Bu sorunla karşılaştığımda, ilk önce cevapları bulmaya çalıştığım birkaç soru var:

  • Enum değerlerimin adları bu amaç için yeterince kolay mı yoksa daha kolay olanları sağlamam mı gerekiyor?
  • Gidiş-dönüşe ihtiyacım var mı? Yani, metin değerlerini alıp bunları enum değerlerine ayrıştırmam gerekir mi?
  • Bu, projemdeki birçok numaralandırma için yapmam gereken bir şey mi, yoksa sadece bir tane mi?
  • Bu bilgileri ne tür UI öğeleri sunacağım - özellikle de kullanıcı arayüzüne bağlanacak mı yoksa özellik sayfaları mı kullanacağım?
  • Bunun yerelleştirilmesi gerekiyor mu?

Bunu yapmanın en basit yolu Enum.GetValue(ve yuvarlak açma desteği Enum.Parse) kullanmaktır . TypeConverterSteve Mitcham'ın önerdiği gibi, kullanıcı arayüzünü desteklemek için genellikle bir a inşa etmeye değer . ( TypeConverterÖzellik sayfalarını kullandığınızda bir özellik oluşturmak gerekmez , bu özellik özellik sayfalarıyla ilgili güzel şeylerden biridir. Lord kendi sorunlarının olduğunu biliyor olsa da.)

Genel olarak, yukarıdaki soruların cevapları bunun işe yaramayacağını gösteriyorsa, bir sonraki adımım statik Dictionary<MyEnum, string>veya muhtemelen a oluşturmak ve doldurmaktır Dictionary<Type, Dictionary<int, string>>. Ben genellikle sonra pike aşağı gelecek şey dağıtımdan sonra (genellikle, ama her zaman değil, yerelleştirme nedeniyle) dostu değerleri değiştirme ihtiyacı olduğu için ara-decode-kod-öznitelikleri adım atlamak eğilimindedir.


7

Bunu aşağıdaki alıntıya bir yorum olarak göndermek istedim, ancak yeterli temsilcim olmadığı için yapamadım - bu yüzden lütfen aşağı oy vermeyin. Kod bir hata içeriyordu ve ben bu çözümü kullanmaya çalışan kişilere işaret etmek istedim:

[TypeConverter(typeof(CustomEnumTypeConverter(typeof(MyEnum))]
public enum MyEnum
{
  // The custom type converter will use the description attribute
  [Description("A custom description")]
  ValueWithCustomDescription,
  // This will be exposed exactly.
  Exact
}

olmalı

[TypeConverter(typeof(CustomEnumTypeConverter<MyEnum>))]
public enum MyEnum
{
  // The custom type converter will use the description attribute
  [Description("A custom description")]
  ValueWithCustomDescription,

  // This will be exposed exactly.
  Exact
}

Brillant!


5

Değişkem

public struct Colors
{
    private String current;

    private static string red = "#ff0000";
    private static string green = "#00ff00";
    private static string blue = "#0000ff";

    private static IList<String> possibleColors; 

    public static Colors Red { get { return (Colors) red; } }
    public static Colors Green { get { return (Colors) green; } }
    public static Colors Blue { get { return (Colors) blue; } }

    static Colors()
    {
        possibleColors = new List<string>() {red, green, blue};
    }

    public static explicit operator String(Colors value)
    {
        return value.current;
    }

    public static explicit operator Colors(String value)
    {
        if (!possibleColors.Contains(value))
        {
            throw new InvalidCastException();
        }

        Colors color = new Colors();
        color.current = value;
        return color;
    }

    public static bool operator ==(Colors left, Colors right)
    {
        return left.current == right.current;
    }

    public static bool operator !=(Colors left, Colors right)
    {
        return left.current != right.current;
    }

    public bool Equals(Colors other)
    {
        return Equals(other.current, current);
    }

    public override bool Equals(object obj)
    {
        if (ReferenceEquals(null, obj)) return false;
        if (obj.GetType() != typeof(Colors)) return false;
        return Equals((Colors)obj);
    }

    public override int GetHashCode()
    {
        return (current != null ? current.GetHashCode() : 0);
    }

    public override string ToString()
    {
        return current;
    }
}

Kod biraz çirkin gözüküyor, ancak bu yapının kullanımları oldukça iyi.

Colors color1 = Colors.Red;
Console.WriteLine(color1); // #ff0000

Colors color2 = (Colors) "#00ff00";
Console.WriteLine(color2); // #00ff00

// Colors color3 = "#0000ff"; // Compilation error
// String color4 = Colors.Red; // Compilation error

Colors color5 = (Colors)"#ff0000";
Console.WriteLine(color1 == color5); // True

Colors color6 = (Colors)"#00ff00";
Console.WriteLine(color1 == color6); // False

Ayrıca, böylesi numaralandırmaların birçoğu gerekirse kod üretimi (örneğin T4) kullanılabileceğini düşünüyorum.


4

Seçenek 1:

public sealed class FormsAuth
{
     public override string ToString{return "Forms Authtentication";}
}
public sealed class WindowsAuth
{
     public override string ToString{return "Windows Authtentication";}
}

public sealed class SsoAuth
{
     public override string ToString{return "SSO";}
}

ve sonra

object auth = new SsoAuth(); //or whatever

//...
//...
// blablabla

DoSomethingWithTheAuth(auth.ToString());

Seçenek 2:

public enum AuthenticationMethod
{
        FORMS = 1,
        WINDOWSAUTHENTICATION = 2,
        SINGLESIGNON = 3
}

public class MyClass
{
    private Dictionary<AuthenticationMethod, String> map = new Dictionary<AuthenticationMethod, String>();
    public MyClass()
    {
         map.Add(AuthenticationMethod.FORMS,"Forms Authentication");
         map.Add(AuthenticationMethod.WINDOWSAUTHENTICATION ,"Windows Authentication");
         map.Add(AuthenticationMethod.SINGLESIGNON ,"SSo Authentication");
    }
}

4

Çözmeye çalıştığımız sorunu düşünürseniz, hiç ihtiyacımız olan bir numaralandırma değildir. Belirli sayıda değerin birbiriyle ilişkilendirilmesine izin veren bir nesneye ihtiyacımız var; başka bir deyişle, bir sınıf tanımlamak için.

Jakub Šturc'un tip-güvenli numaralandırma düzeni burada gördüğüm en iyi seçenektir.

Ona bak:

  • Özel bir kurucuya sahiptir, böylece izin verilen değerleri yalnızca sınıfın kendisi tanımlayabilir.
  • Mühürlü bir sınıftır, bu nedenle değerler miras yoluyla değiştirilemez.
  • Türlerin güvenliğini sağlar ve yöntemlerinizin yalnızca bu türden olmasını gerektirir.
  • Değerlere erişildiğinde gerçekleşen bir yansıma performansı isabeti yoktur.
  • Ve son olarak, ikiden fazla alanı, örneğin Ad, Açıklama ve sayısal bir değer ilişkilendirmek için değiştirilebilir.

4

benim için pragmatik yaklaşım sınıf içindeki sınıftır, örnek:

public class MSEModel
{
    class WITS
    {
        public const string DATE = "5005";
        public const string TIME = "5006";
        public const string MD = "5008";
        public const string ROP = "5075";
        public const string WOB = "5073";
        public const string RPM = "7001";
... 
    }

4

.NET'te dize değerli numaralandırmalar oluşturmak için bir temel sınıf oluşturdum. Projelerinize kopyalayıp yapıştırabileceğiniz veya StringEnum adlı NuGet paketi aracılığıyla yükleyebileceğiniz sadece bir C # dosyasıdır . GitHub Repo

  • Intellisense, sınıf xml yorumuyla açıklama eklenmişse numaralandırma adını önerir <completitionlist>. (Hem C # hem de VB'de çalışır)

Intellisense demosu

  • Düzenli bir numaralamaya benzer kullanım:
///<completionlist cref="HexColor"/> 
class HexColor : StringEnum<HexColor>
{
    public static readonly HexColor Blue = Create("#FF0000");
    public static readonly HexColor Green = Create("#00FF00");
    public static readonly HexColor Red = Create("#000FF");
}
    // Static Parse Method
    HexColor.Parse("#FF0000") // => HexColor.Red
    HexColor.Parse("#ff0000", caseSensitive: false) // => HexColor.Red
    HexColor.Parse("invalid") // => throws InvalidOperationException

    // Static TryParse method.
    HexColor.TryParse("#FF0000") // => HexColor.Red
    HexColor.TryParse("#ff0000", caseSensitive: false) // => HexColor.Red
    HexColor.TryParse("invalid") // => null

    // Parse and TryParse returns the preexistent instances
    object.ReferenceEquals(HexColor.Parse("#FF0000"), HexColor.Red) // => true

    // Conversion from your `StringEnum` to `string`
    string myString1 = HexColor.Red.ToString(); // => "#FF0000"
    string myString2 = HexColor.Red; // => "#FF0000" (implicit cast)

Kurulum:

  • Aşağıdaki StringEnum temel sınıfını projenize yapıştırın. ( son sürüm )
  • Veya > = 1.0, > = 4.5, > = 4.6 vb. Üzerinde çalışmasını temel alan StringEnum NuGet paketini yükleyin ..Net Standard 1.0.Net Core.Net FrameworkMono
    /// <summary>
    /// Base class for creating string-valued enums in .NET.<br/>
    /// Provides static Parse() and TryParse() methods and implicit cast to string.
    /// </summary>
    /// <example> 
    /// <code>
    /// class Color : StringEnum &lt;Color&gt;
    /// {
    ///     public static readonly Color Blue = Create("Blue");
    ///     public static readonly Color Red = Create("Red");
    ///     public static readonly Color Green = Create("Green");
    /// }
    /// </code>
    /// </example>
    /// <typeparam name="T">The string-valued enum type. (i.e. class Color : StringEnum&lt;Color&gt;)</typeparam>
    public abstract class StringEnum<T> : IEquatable<T> where T : StringEnum<T>, new()
    {
        protected string Value;
        private static Dictionary<string, T> valueDict = new Dictionary<string, T>();
        protected static T Create(string value)
        {
            if (value == null)
                return null; // the null-valued instance is null.

            var result = new T() { Value = value };
            valueDict.Add(value, result);
            return result;
        }

        public static implicit operator string(StringEnum<T> enumValue) => enumValue.Value;
        public override string ToString() => Value;

        public static bool operator !=(StringEnum<T> o1, StringEnum<T> o2) => o1?.Value != o2?.Value;
        public static bool operator ==(StringEnum<T> o1, StringEnum<T> o2) => o1?.Value == o2?.Value;

        public override bool Equals(object other) => this.Value.Equals((other as T)?.Value ?? (other as string));
        bool IEquatable<T>.Equals(T other) => this.Value.Equals(other.Value);
        public override int GetHashCode() => Value.GetHashCode();

        /// <summary>
        /// Parse the <paramref name="value"/> specified and returns a valid <typeparamref name="T"/> or else throws InvalidOperationException.
        /// </summary>
        /// <param name="value">The string value representad by an instance of <typeparamref name="T"/>. Matches by string value, not by the member name.</param>
        /// <param name="caseSensitive">If true, the strings must match case and takes O(log n). False allows different case but is little bit slower (O(n))</param>
        public static T Parse(string value, bool caseSensitive = true)
        {
            var result = TryParse(value, caseSensitive);
            if (result == null)
                throw new InvalidOperationException((value == null ? "null" : $"'{value}'") + $" is not a valid {typeof(T).Name}");

            return result;
        }

        /// <summary>
        /// Parse the <paramref name="value"/> specified and returns a valid <typeparamref name="T"/> or else returns null.
        /// </summary>
        /// <param name="value">The string value representad by an instance of <typeparamref name="T"/>. Matches by string value, not by the member name.</param>
        /// <param name="caseSensitive">If true, the strings must match case. False allows different case but is slower: O(n)</param>
        public static T TryParse(string value, bool caseSensitive = true)
        {
            if (value == null) return null;
            if (valueDict.Count == 0) System.Runtime.CompilerServices.RuntimeHelpers.RunClassConstructor(typeof(T).TypeHandle); // force static fields initialization
            if (caseSensitive)
            {
                if (valueDict.TryGetValue(value, out T item))
                    return item;
                else
                    return null;
            }
            else
            {
                // slower O(n) case insensitive search
                return valueDict.FirstOrDefault(f => f.Key.Equals(value, StringComparison.OrdinalIgnoreCase)).Value;
                // Why Ordinal? => https://esmithy.net/2007/10/15/why-stringcomparisonordinal-is-usually-the-right-choice/
            }
        }
    }

3

Dizeleri numaralandırmalarla ilişkilendirme görevini gerçekleştirmenin başka bir yolu:

struct DATABASE {
    public enum enums {NOTCONNECTED, CONNECTED, ERROR}
    static List<string> strings =
        new List<string>() {"Not Connected", "Connected", "Error"};

    public string GetString(DATABASE.enums value) {
        return strings[(int)value];
    }
}

Bu yönteme şöyle denir:

public FormMain() {
    DATABASE dbEnum;

    string enumName = dbEnum.GetString(DATABASE.enums.NOTCONNECTED);
}

İlgili numaralandırmaları kendi yapılarında gruplandırabilirsiniz. Bu yöntem enum türünü kullandığından, GetString()çağrı yaparken numaralandırma listesini görüntülemek için Intellisense kullanabilirsiniz .

İsteğe bağlı olarak DATABASEyapı üzerinde yeni işleci kullanabilirsiniz . Kullanılmaması List, ilk GetString()arama yapılana kadar dizelerin tahsis edilmediği anlamına gelir .


3

Burada çok büyük cevaplar var ama benim durumumda bir "string enum" dışında ne istediğini çözmedi, ki:

  1. Bir switch deyiminde kullanılabilir örn. Switch (myEnum)
  2. Foo (myEnum tipi) gibi fonksiyon parametrelerinde kullanılabilir
  3. Referans verilebilir, örneğin myEnum.FirstElement
  4. Dizeleri kullanabilirim, örneğin foo ("FirstElement") == foo (myEnum.FirstElement)

1,2 ve 4 aslında bir dizenin C # Typedef'i ile çözülebilir (dizeler c # ile değiştirilebilir olduğundan)

3 statik sabit dizgiler ile çözülebilir. Aynı ihtiyaçlarınız varsa, bu en basit yaklaşımdır:

public sealed class Types
{

    private readonly String name;

    private Types(String name)
    {
        this.name = name;

    }

    public override String ToString()
    {
        return name;
    }

    public static implicit operator Types(string str)
    {
        return new Types(str);

    }
    public static implicit operator string(Types str)
    {
        return str.ToString();
    }


    #region enum

    public const string DataType = "Data";
    public const string ImageType = "Image";
    public const string Folder = "Folder";
    #endregion

}

Bu, örneğin şunları sağlar:

    public TypeArgs(Types SelectedType)
    {
        Types SelectedType = SelectedType
    }

ve

public TypeObject CreateType(Types type)
    {
        switch (type)
        {

            case Types.ImageType:
              //
                break;

            case Types.DataType:
             //
                break;

        }
    }

Burada CreateType bir dize veya tür ile çağrılabilir. Ancak olumsuz, herhangi bir dizenin otomatik olarak geçerli bir numaralandırma olmasıdır , bu değiştirilebilir, ancak daha sonra bir tür init fonksiyonu gerektirecektir ...

Bir int değer (belki karşılaştırma hızı için) sizin için önemli olsaydı Şimdi, olabilir Jakub Šturc fantastik cevap bazı fikirler kullanmak ve bir şey yapmak biraz deli, bu ona benim bıçak geçerli:

    public sealed class Types
{
    private static readonly Dictionary<string, Types> strInstance = new Dictionary<string, Types>();
    private static readonly Dictionary<int, Types> intInstance = new Dictionary<int, Types>();

    private readonly String name;
    private static int layerTypeCount = 0;
    private int value;
    private Types(String name)
    {
        this.name = name;
        value = layerTypeCount++;
        strInstance[name] = this;
        intInstance[value] = this;
    }

    public override String ToString()
    {
        return name;
    }


    public static implicit operator Types(int val)
    {
        Types result;
        if (intInstance.TryGetValue(val, out result))
            return result;
        else
            throw new InvalidCastException();
    }

    public static implicit operator Types(string str)
    {
        Types result;
        if (strInstance.TryGetValue(str, out result))
        {
            return result;
        }
        else
        {
            result = new Types(str);
            return result;
        }

    }
    public static implicit operator string(Types str)
    {
        return str.ToString();
    }

    public static bool operator ==(Types a, Types b)
    {
        return a.value == b.value;
    }
    public static bool operator !=(Types a, Types b)
    {
        return a.value != b.value;
    }

    #region enum

    public const string DataType = "Data";
    public const string ImageType = "Image";

    #endregion

}

ama elbette "Tipler bob = 4;" önce onları başlatmış olmadıkça anlamsız olurdu ...

Ama teoride TypeA == TypeB daha hızlı olurdu ...


3

Seni doğru anlıyorsam, değerden enum adını almak için .ToString () öğesini kullanabilirsiniz (Zaten Enum olarak kullanıldığını varsayarak); Eğer çıplak int'iniz varsa (bir veritabanından falan diyelim) önce numaraya atabilirsiniz. Aşağıdaki her iki yöntem de numaralandırma adını alır.

AuthenticationMethod myCurrentSetting = AuthenticationMethod.FORMS;
Console.WriteLine(myCurrentSetting); // Prints: FORMS
string name = Enum.GetNames(typeof(AuthenticationMethod))[(int)myCurrentSetting-1];
Console.WriteLine(name); // Prints: FORMS

Yine de, ikinci tekniğin ints kullandığınızı ve dizininizin 1 tabanlı (0 tabanlı değil) olduğunu varsayalım. GetNames işlevi de kıyaslandığında oldukça ağırdır, her çağrıldığında bir dizi oluşturursunuz. İlk teknikte görebileceğiniz gibi, .ToString () aslında örtük olarak çağrılır. Bunların her ikisi de elbette cevaplarda zaten belirtilmiş, sadece aralarındaki farkları açıklığa kavuşturmaya çalışıyorum.


3

eski yazı ama ...

Bunun cevabı aslında çok basit olabilir. Kullanım Enum.ToString () fonksiyonu

Bu işlevin 6 aşırı yüklenmesi vardır, dize değerini döndürmek için Enum.Tostring ("F") veya Enum.ToString () kullanabilirsiniz. Başka bir şeyle uğraşmanıza gerek yok. İşte çalışan bir demo

Bu çözümün tüm derleyiciler için çalışmayabileceğini unutmayın ( bu demo beklendiği gibi çalışmaz ), ancak en azından en son derleyici için çalışır.



2

Yukarıdakilerin hepsini okuduktan sonra, adamların sayım görevlilerini dizelere dönüştürme konusunu aşırı derecede karmaşıklaştırdığını hissediyorum. Numaralandırılmış alanlar üzerinde özniteliklere sahip olma fikrini beğendim, ancak özniteliklerin ağırlıklı olarak Meta veriler için kullanıldığını düşünüyorum, ancak sizin durumunuzda ihtiyacınız olan tek şey bir çeşit yerelleştirme olduğunu düşünüyorum.

public enum Color 
{ Red = 1, Green = 2, Blue = 3}


public static EnumUtils 
{
   public static string GetEnumResourceString(object enumValue)
    {
        Type enumType = enumValue.GetType();
        string value = Enum.GetName(enumValue.GetType(), enumValue);
        string resourceKey = String.Format("{0}_{1}", enumType.Name, value);
        string result = Resources.Enums.ResourceManager.GetString(resourceKey);
        if (string.IsNullOrEmpty(result))
        {
            result = String.Format("{0}", value);
        }
        return result;
    }
}

Şimdi yukarıdaki yöntemi çağırmaya çalışırsak, bu yöntemi çağırabiliriz.

public void Foo()
{
  var col = Color.Red;
  Console.WriteLine (EnumUtils.GetEnumResourceString (col));
}

Tek yapmanız gereken, tüm numaralandırıcı değerlerini ve karşılık gelen dizeleri içeren bir kaynak dosyası oluşturmaktır

Kaynak Adı Kaynak Değeri
Color_Red Dize Rengim Kırmızı
Color_Blue Blueeey
Color_Green Hulk Rengi

Aslında çok güzel olan şey, uygulamanızın yerelleştirilmesi için çok yararlı olacağıdır, çünkü yapmanız gereken tek şey yeni dilinizle başka bir kaynak dosyası oluşturmaktır! ve Voe-la!


1

Böyle bir durumdayken aşağıdaki çözümü öneririm.

Ve tüketen bir sınıf olarak

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace MyApp.Dictionaries
{
    class Greek
    {

        public static readonly string Alpha = "Alpha";
        public static readonly string Beta = "Beta";
        public static readonly string Gamma = "Gamma";
        public static readonly string Delta = "Delta";


        private static readonly BiDictionary<int, string> Dictionary = new BiDictionary<int, string>();


        static Greek() {
            Dictionary.Add(1, Alpha);
            Dictionary.Add(2, Beta);
            Dictionary.Add(3, Gamma);
            Dictionary.Add(4, Delta);
        }

        public static string getById(int id){
            return Dictionary.GetByFirst(id);
        }

        public static int getByValue(string value)
        {
            return Dictionary.GetBySecond(value);
        }

    }
}

Ve iki yönlü sözlük kullanılarak: Buna dayanarak ( https://stackoverflow.com/a/255638/986160 ) tuşları sözlükte tek değerlerle ilişkili ve benzer olacağını varsayarak ( https://stackoverflow.com/a / 255630/986160 ) ama biraz daha zarif. Bu sözlük de numaralandırılabilir ve ints'tan dizelere gidip gelebilirsiniz. Ayrıca, kod sınıfı içinde bu sınıf dışında herhangi bir dize olması gerekmez.

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Collections;

namespace MyApp.Dictionaries
{

    class BiDictionary<TFirst, TSecond> : IEnumerable
    {
        IDictionary<TFirst, TSecond> firstToSecond = new Dictionary<TFirst, TSecond>();
        IDictionary<TSecond, TFirst> secondToFirst = new Dictionary<TSecond, TFirst>();

        public void Add(TFirst first, TSecond second)
        {
            firstToSecond.Add(first, second);
            secondToFirst.Add(second, first);
        }

        public TSecond this[TFirst first]
        {
            get { return GetByFirst(first); }
        }

        public TFirst this[TSecond second]
        {
            get { return GetBySecond(second); }
        }

        public TSecond GetByFirst(TFirst first)
        {
            return firstToSecond[first];
        }

        public TFirst GetBySecond(TSecond second)
        {
            return secondToFirst[second];
        }

        public IEnumerator GetEnumerator()
        {
            return GetFirstEnumerator();
        }

        public IEnumerator GetFirstEnumerator()
        {
            return firstToSecond.GetEnumerator();
        }

        public IEnumerator GetSecondEnumerator()
        {
            return secondToFirst.GetEnumerator();
        }
    }
}

1

Daha büyük dize numaralandırma setleri için, listelenen örnekler yorucu olabilir. Bir durum kodu listesi veya diğer dize tabanlı numaralandırmaların bir listesini istiyorsanız, bir öznitelik sistemi kullanmak can sıkıcıdır ve kendisinin örneklerini içeren statik bir sınıf yapılandırmak can sıkıcıdır. Kendi çözümüm için, T4 şablonlamayı ip destekli numaralandırmalara sahip olmayı kolaylaştırmak için kullanıyorum. Sonuç, HttpMethod sınıfının nasıl çalıştığına benzer.

Bu şekilde kullanabilirsiniz:

    string statusCode = ResponseStatusCode.SUCCESS; // Automatically converts to string when needed
    ResponseStatusCode codeByValueOf = ResponseStatusCode.ValueOf(statusCode); // Returns null if not found

    // Implements TypeConverter so you can use it with string conversion methods.
    var converter = System.ComponentModel.TypeDescriptor.GetConverter(typeof(ResponseStatusCode));
    ResponseStatusCode code = (ResponseStatusCode) converter.ConvertFromInvariantString(statusCode);

    // You can get a full list of the values
    bool canIterateOverValues = ResponseStatusCode.Values.Any(); 

    // Comparisons are by value of the "Name" property. Not by memory pointer location.
    bool implementsByValueEqualsEqualsOperator = "SUCCESS" == ResponseStatusCode.SUCCESS; 

Bir Enum.tt dosyasıyla başlarsınız.

<#@ include file="StringEnum.ttinclude" #>


<#+
public static class Configuration
{
    public static readonly string Namespace = "YourName.Space";
    public static readonly string EnumName = "ResponseStatusCode";
    public static readonly bool IncludeComments = true;

    public static readonly object Nodes = new
    {
        SUCCESS = "The response was successful.",
        NON_SUCCESS = "The request was not successful.",
        RESOURCE_IS_DISCONTINUED = "The resource requested has been discontinued and can no longer be accessed."
    };
}
#>

Ardından, StringEnum.ttinclude dosyanıza ekleyin.

<#@ template debug="false" hostspecific="false" language="C#" #>
<#@ assembly name="System.Core" #>
<#@ import namespace="System" #>
<#@ import namespace="System.Linq" #>
<#@ import namespace="System.Text" #>
<#@ import namespace="System.Reflection" #>
<#@ import namespace="System.Collections.Generic" #>
<#@ output extension=".cs" #>
<#@ CleanupBehavior processor="T4VSHost" CleanupAfterProcessingtemplate="true" #>

//------------------------------------------------------------------------------
// <auto-generated>
//     This code was generated by a tool.
//
//     Changes to this file may cause incorrect behavior and will be lost if
//     the code is regenerated.
// </auto-generated>
//------------------------------------------------------------------------------

using System;
using System.Linq;
using System.Collections.Generic;
using System.ComponentModel;
using System.Globalization;

namespace <#= Configuration.Namespace #>
{
    /// <summary>
    /// TypeConverter implementations allow you to use features like string.ToNullable(T).
    /// </summary>
    public class <#= Configuration.EnumName #>TypeConverter : TypeConverter
    {
        public override bool CanConvertFrom(ITypeDescriptorContext context, Type sourceType)
        {
            return sourceType == typeof(string) || base.CanConvertFrom(context, sourceType);
        }

        public override object ConvertFrom(ITypeDescriptorContext context, CultureInfo culture, object value)
        {
            var casted = value as string;

            if (casted != null)
            {
                var result = <#= Configuration.EnumName #>.ValueOf(casted);
                if (result != null)
                {
                    return result;
                }
            }

            return base.ConvertFrom(context, culture, value);
        }

        public override object ConvertTo(ITypeDescriptorContext context, CultureInfo culture, object value, Type destinationType)
        {
            var casted = value as <#= Configuration.EnumName #>;
            if (casted != null && destinationType == typeof(string))
            {
                return casted.ToString();
            }

            return base.ConvertTo(context, culture, value, destinationType);
        }
    }

    [TypeConverter(typeof(<#= Configuration.EnumName #>TypeConverter))]
    public class <#= Configuration.EnumName #> : IEquatable<<#= Configuration.EnumName #>>
    {
//---------------------------------------------------------------------------------------------------
// V A L U E S _ L I S T
//---------------------------------------------------------------------------------------------------
<# Write(Helpers.PrintEnumProperties(Configuration.Nodes)); #>

        private static List<<#= Configuration.EnumName #>> _list { get; set; } = null;
        public static List<<#= Configuration.EnumName #>> ToList()
        {
            if (_list == null)
            {
                _list = typeof(<#= Configuration.EnumName #>).GetFields().Where(x => x.IsStatic && x.IsPublic && x.FieldType == typeof(<#= Configuration.EnumName #>))
                    .Select(x => x.GetValue(null)).OfType<<#= Configuration.EnumName #>>().ToList();
            }

            return _list;
        }

        public static List<<#= Configuration.EnumName #>> Values()
        {
            return ToList();
        }

        /// <summary>
        /// Returns the enum value based on the matching Name of the enum. Case-insensitive search.
        /// </summary>
        /// <param name="key"></param>
        /// <returns></returns>
        public static <#= Configuration.EnumName #> ValueOf(string key)
        {
            return ToList().FirstOrDefault(x => string.Compare(x.Name, key, true) == 0);
        }


//---------------------------------------------------------------------------------------------------
// I N S T A N C E _ D E F I N I T I O N
//---------------------------------------------------------------------------------------------------      
        public string Name { get; private set; }
        public string Description { get; private set; }
        public override string ToString() { return this.Name; }

        /// <summary>
        /// Implcitly converts to string.
        /// </summary>
        /// <param name="d"></param>
        public static implicit operator string(<#= Configuration.EnumName #> d)
        {
            return d.ToString();
        }

        /// <summary>
        /// Compares based on the == method. Handles nulls gracefully.
        /// </summary>
        /// <param name="a"></param>
        /// <param name="b"></param>
        /// <returns></returns>
        public static bool operator !=(<#= Configuration.EnumName #> a, <#= Configuration.EnumName #> b)
        {
            return !(a == b);
        }

        /// <summary>
        /// Compares based on the .Equals method. Handles nulls gracefully.
        /// </summary>
        /// <param name="a"></param>
        /// <param name="b"></param>
        /// <returns></returns>
        public static bool operator ==(<#= Configuration.EnumName #> a, <#= Configuration.EnumName #> b)
        {
            return a?.ToString() == b?.ToString();
        }

        /// <summary>
        /// Compares based on the .ToString() method
        /// </summary>
        /// <param name="o"></param>
        /// <returns></returns>
        public override bool Equals(object o)
        {
            return this.ToString() == o?.ToString();
        }

        /// <summary>
        /// Compares based on the .ToString() method
        /// </summary>
        /// <param name="other"></param>
        /// <returns></returns>
        public bool Equals(<#= Configuration.EnumName #> other)
        {
            return this.ToString() == other?.ToString();
        }

        /// <summary>
        /// Compares based on the .Name property
        /// </summary>
        /// <returns></returns>
        public override int GetHashCode()
        {
            return this.Name.GetHashCode();
        }
    }
}

<#+

public static class Helpers
{
        public static string PrintEnumProperties(object nodes)
        {
            string o = "";
            Type nodesTp = Configuration.Nodes.GetType();
            PropertyInfo[] props = nodesTp.GetProperties().OrderBy(p => p.Name).ToArray();

            for(int i = 0; i < props.Length; i++)
            {
                var prop = props[i];
                if (Configuration.IncludeComments)
                {
                    o += "\r\n\r\n";
                    o += "\r\n        ///<summary>";
                    o += "\r\n        /// "+Helpers.PrintPropertyValue(prop, Configuration.Nodes);
                    o += "\r\n        ///</summary>";
                }

                o += "\r\n        public static readonly "+Configuration.EnumName+" "+prop.Name+ " = new "+Configuration.EnumName+"(){ Name = \""+prop.Name+"\", Description = "+Helpers.PrintPropertyValue(prop, Configuration.Nodes)+ "};";
            }

            o += "\r\n\r\n";

            return o;
        }

        private static Dictionary<string, string> GetValuesMap()
        {
            Type nodesTp = Configuration.Nodes.GetType();
            PropertyInfo[] props= nodesTp.GetProperties();
            var dic = new Dictionary<string,string>();
            for(int i = 0; i < props.Length; i++)
            {
                var prop = nodesTp.GetProperties()[i];
                dic[prop.Name] = prop.GetValue(Configuration.Nodes).ToString();
            }
            return dic;
        }

        public static string PrintMasterValuesMap(object nodes)
        {
            Type nodesTp = Configuration.Nodes.GetType();
            PropertyInfo[] props= nodesTp.GetProperties();
            string o = "        private static readonly Dictionary<string, string> ValuesMap = new Dictionary<string, string>()\r\n        {";
            for(int i = 0; i < props.Length; i++)
            {
                var prop = nodesTp.GetProperties()[i];
                o += "\r\n            { \""+prop.Name+"\", "+(Helpers.PrintPropertyValue(prop,Configuration.Nodes)+" },");
            }
            o += ("\r\n        };\r\n");

            return o;
        }


        public static string PrintPropertyValue(PropertyInfo prop, object objInstance)
        {
            switch(prop.PropertyType.ToString()){
                case "System.Double":
                    return prop.GetValue(objInstance).ToString()+"D";
                case "System.Float":
                    return prop.GetValue(objInstance).ToString()+"F";
                case "System.Decimal":
                    return prop.GetValue(objInstance).ToString()+"M";
                case "System.Long":
                    return prop.GetValue(objInstance).ToString()+"L";
                case "System.Boolean":
                case "System.Int16":
                case "System.Int32":
                    return prop.GetValue(objInstance).ToString().ToLowerInvariant();
                case "System.String":
                    return "\""+prop.GetValue(objInstance)+"\"";
            }

            return prop.GetValue(objInstance).ToString();
        }

        public static string _ (int numSpaces)
        {
            string o = "";
            for(int i = 0; i < numSpaces; i++){
                o += " ";
            }

            return o;
        }
}
#>

Son olarak, Enum.tt dosyanızı yeniden derlersiniz ve çıktı şu şekilde görünür:

//------------------------------------------------------------------------------
// <auto-generated>
//     This code was generated by a tool.
//
//     Changes to this file may cause incorrect behavior and will be lost if
//     the code is regenerated.
// </auto-generated>
//------------------------------------------------------------------------------

using System;
using System.Linq;
using System.Collections.Generic;

namespace YourName.Space
{
    public class ResponseStatusCode
    {
//---------------------------------------------------------------------------------------------------
// V A L U E S _ L I S T 
//---------------------------------------------------------------------------------------------------



        ///<summary>
        /// "The response was successful."
        ///</summary>
        public static readonly ResponseStatusCode SUCCESS = new ResponseStatusCode(){ Name = "SUCCESS", Description = "The response was successful."};


        ///<summary>
        /// "The request was not successful."
        ///</summary>
        public static readonly ResponseStatusCode NON_SUCCESS = new ResponseStatusCode(){ Name = "NON_SUCCESS", Description = "The request was not successful."};


        ///<summary>
        /// "The resource requested has been discontinued and can no longer be accessed."
        ///</summary>
        public static readonly ResponseStatusCode RESOURCE_IS_DISCONTINUED = new ResponseStatusCode(){ Name = "RESOURCE_IS_DISCONTINUED", Description = "The resource requested has been discontinued and can no longer be accessed."};


        private static List<ResponseStatusCode> _list { get; set; } = null;
        public static List<ResponseStatusCode> ToList()
        {
            if (_list == null)
            {
                _list = typeof(ResponseStatusCode).GetFields().Where(x => x.IsStatic && x.IsPublic && x.FieldType == typeof(ResponseStatusCode))
                    .Select(x => x.GetValue(null)).OfType<ResponseStatusCode>().ToList();
            }

            return _list;
        }

        public static List<ResponseStatusCode> Values()
        {
            return ToList();
        }

        /// <summary>
        /// Returns the enum value based on the matching Name of the enum. Case-insensitive search.
        /// </summary>
        /// <param name="key"></param>
        /// <returns></returns>
        public static ResponseStatusCode ValueOf(string key)
        {
            return ToList().FirstOrDefault(x => string.Compare(x.Name, key, true) == 0);
        }


//---------------------------------------------------------------------------------------------------
// I N S T A N C E _ D E F I N I T I O N 
//---------------------------------------------------------------------------------------------------       
        public string Name { get; set; }
        public string Description { get; set; }
        public override string ToString() { return this.Name; }

        /// <summary>
        /// Implcitly converts to string.
        /// </summary>
        /// <param name="d"></param>
        public static implicit operator string(ResponseStatusCode d)
        {
            return d.ToString();
        }

        /// <summary>
        /// Compares based on the == method. Handles nulls gracefully.
        /// </summary>
        /// <param name="a"></param>
        /// <param name="b"></param>
        /// <returns></returns>
        public static bool operator !=(ResponseStatusCode a, ResponseStatusCode b)
        {
            return !(a == b);
        }

        /// <summary>
        /// Compares based on the .Equals method. Handles nulls gracefully.
        /// </summary>
        /// <param name="a"></param>
        /// <param name="b"></param>
        /// <returns></returns>
        public static bool operator ==(ResponseStatusCode a, ResponseStatusCode b)
        {
            return a?.ToString() == b?.ToString();
        }

        /// <summary>
        /// Compares based on the .ToString() method
        /// </summary>
        /// <param name="o"></param>
        /// <returns></returns>
        public override bool Equals(object o)
        {
            return this.ToString() == o?.ToString();
        }

        /// <summary>
        /// Compares based on the .Name property
        /// </summary>
        /// <returns></returns>
        public override int GetHashCode()
        {
            return this.Name.GetHashCode();
        }
    }
}
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.