switch - enum new c#




Enum的字符串表示 (20)

我有以下枚舉:

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

但問題是,當我要求AuthenticationMethod.FORMS而不是id 1時,我需要“FORMS”一詞。

我發現了這個問題的下列解決方案( link ):

首先,我需要創建一個名為“StringValue”的自定義屬性:

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

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

    public string Value
    {
        get { return _value; }
    }

}

然後我可以將此屬性添加到我的枚舉器中:

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

當然,我需要一些東西來檢索StringValue:

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

現在好,我已經有了獲得一個枚舉器字符串值的工具。 然後我可以像這樣使用它:

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

好吧,現在所有這些工作都像一個魅力,但我發現它很多工作。 我想知道是否有更好的解決方案。

我也嘗試了一些字典和靜態屬性,但那也不是更好。


old post but...

The answer to this may actually be very simple. Use Enum.ToString() function

There are 6 overloads of this function, you can use Enum.Tostring("F") or Enum.ToString() to return the string value. No need to bother with anything else. Here is a working Demo

Note that this solution may not work for all compilers ( this demo does not work as expected ) but at least it works for the latest compiler.


我同意基思,但我不能投票(還)。

我使用靜態方法和swith語句來返回我想要的。 在數據庫中存儲tinyint,我的代碼只使用實際枚舉,所以這些字符串用於UI需求。 在經過多次測試後,這會產生最佳性能和對輸出的最大控制。

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

但是,根據一些說法,這會導致可能的維護噩夢和一些代碼異味。 我試圖密切關注那些很長且很多枚舉的枚舉,或那些頻繁變化的枚舉。 否則,這對我來說是一個很好的解決方案。


Here is yet another way to accomplish the task of associating strings with enums:

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

This method is called like this:

public FormMain() {
    DATABASE dbEnum;

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

You can group related enums in their own struct. Since this method uses the enum type, you can use Intellisense to display the list of enums when making the GetString() call.

You can optionally use the new operator on the DATABASE struct. Not using it means the strings List is not allocated until the first GetString() call is made.


If I'm understanding you correctly, you can simply use .ToString() to retrieve the name of the enum from the value (Assuming it's already cast as the Enum); If you had the naked int (lets say from a database or something) you can first cast it to the enum. Both methods below will get you the enum name.

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

Keep in mind though, the second technique assumes you are using ints and your index is 1 based (not 0 based). The function GetNames also is quite heavy by comparison, you are generating a whole array each time it's called. As you can see in the first technique, .ToString() is actually called implicitly. Both of these are already mentioned in the answers of course, I'm just trying to clarify the differences between them.


My answer, working on @user29964 's answer (which is by far the simplest and closest to a Enum) is

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

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

        public string Value
        {
            get { return _value; }
        }



        public static string GetStringValue(Enum Flagvalue)
        {
            Type type = Flagvalue.GetType();
            string[] flags = Flagvalue.ToString().Split(',').Select(x => x.Trim()).ToArray();
            List<string> values = new List<string>();

            for (int i = 0; i < flags.Length; i++)
            {

                FieldInfo fi = type.GetField(flags[i].ToString());

                StringValue[] attrs =
                   fi.GetCustomAttributes(typeof(StringValue),
                                           false) as StringValue[];
                if (attrs.Length > 0)
                {
                    values.Add(attrs[0].Value);
                }
            }
            return String.Join(",", values);

        }

用法

[Flags]
    public enum CompeteMetric
    {

        /// <summary>
        /// u
        /// </summary>
        [StringValue("u")]//Json mapping
        Basic_UniqueVisitors = 1 //Basic
             ,
        /// <summary>
        /// vi
        /// </summary>
        [StringValue("vi")]//json mapping
        Basic_Visits = 2// Basic
            ,
        /// <summary>
        /// rank
        /// </summary>
        [StringValue("rank")]//json mapping
        Basic_Rank = 4//Basic
 }

        CompeteMetric metrics = CompeteMetric.Basic_Visits | CompeteMetric.Basic_Rank;
        string strmetrics = StringValue.GetStringValue(metrics);

this will return "vi,rank"



When I am in a situation like that I propose the solution below.

And as a consuming class you could have

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

    }
}

And using a bidirectional dictionary: Based on this ( https://.com/a/255638/986160 ) assuming that the keys will be associated with single values in the dictionary and similar to ( https://.com/a/255630/986160 ) but a bit more elegant. This dictionary is also enumerable and you can go back and forth from ints to strings. Also you don't have to have any string in your codebase with the exception of this class.

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


不幸的是,獲得枚舉屬性的反射非常緩慢:

看到這個問題: 任何人都知道快速獲取枚舉值的自定義屬性的方法嗎?

.ToString()在枚舉上也很慢。

您可以編寫枚舉的擴展方法:

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

這不是很好,但會很快,不需要反映屬性或字段名稱。

C#6更新

如果你可以使用C#6,那麼運算符的新nameof可以用於枚舉,所以nameof(MyEnum.WINDOWSAUTHENTICATION)將在編譯時轉換為"WINDOWSAUTHENTICATION" ,這是獲取枚舉名稱的最快方法。

請注意,這會將顯式枚舉轉換為內聯常量,因此它不適用於變量中的枚舉。 所以:

nameof(AuthenticationMethod.FORMS) == "FORMS"

但...

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

使用.Net 4.0及以上版本的非常簡單的解決方案。 不需要其他代碼。

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

要獲取有關使用的字符串:

MyStatus.Active.ToString("f");

要么

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

該值將是“活動”或“存檔”。

要在調用Enum.ToString查看不同的字符串格式(上面的“f”),請參閱此Enumeration Format Strings頁面


只需使用ToString()方法即可

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

要引用字符串Tomato ,只需使用

any.Tomato.ToString();

嘗試使用type-safe-enum模式。

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

}

更新顯式(或隱式)類型轉換可以通過

  • 添加靜態字段與映射

    private static readonly Dictionary<string, AuthenticationMethod> instance = new Dictionary<string,AuthenticationMethod>();
    
    • nb為了讓“enum member”字段的初始化在調用實例構造函數時不拋出NullReferenceException,一定要將Dictionary字段放在類的“enum member”字段之前。 這是因為按照聲明順序調用靜態字段初始化程序,並在靜態構造函數之前創建奇怪且必要但令人困惑的情況,即可以在所有靜態字段初始化之前以及在調用靜態構造函數之前調用實例構造函數。
  • 在實例構造函數中填充此映射

    instance[name] = this;
    
  • 並添加用戶定義的類型轉換運算符

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

如果你來這裡想要實現一個簡單的“枚舉”,但其值是字符串而不是整數,下面是最簡單的解決方案:

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

執行:

var someStringVariable = MetricValueList.Brand;

就像你們中的大多數人一樣,我真的很喜歡JakubŠturc所選擇的答案 ,但我也非常討厭複製粘貼代碼,盡可能少地嘗試。

所以我決定我想要一個EnumBase類,其中大部分功能都是繼承/內置的,讓我專注於內容而不是行為。

這種方法的主要問題是基於這樣一個事實,即雖然Enum值是類型安全的實例,但交互與Enum類類型的Static實現相同。 所以藉助泛型魔法的一點幫助,我想我終於得到了正確的組合。 希望有人發現這個和我一樣有用。

我將從Jakub的例子開始,但使用繼承和泛型:

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

這裡是基類:

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
}

我使用System.ComponentModel命名空間中的Description屬性。 只需裝飾枚舉,然後使用此代碼來檢索它:

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

        }

舉個例子:

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

這段代碼很好地迎合了你不需要“友好名稱”的枚舉,並且只返回枚舉的.ToString()。


我使用上述幾種建議的組合,並結合一些緩存。 現在,我從網上找到的一些代碼中得到了這個想法,但我都記不清哪裡找到或找到了它。 因此,如果有人發現類似的內容,請使用該屬性進行評論。

無論如何,這個用法涉及類型轉換器,所以如果你綁定到UI,它就會“正常工作”。 通過從類型轉換器初始化靜態方法,您可以使用Jakub的快速代碼查找模式進行擴展。

基本用法看起來像這樣

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

自定義枚舉類型轉換器的代碼如下所示:

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

}


我如何解決這個問題作為擴展方法:

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

枚舉:

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

用法(其中o.OrderType是與枚舉同名的屬性):

o.OrderType.GetDescription()

這給了我一串“新卡”或“重新加載”,而不是實際的枚舉值NewCard和Refill。


我想把這篇文章作為對下面引用的帖子的評論發表,但不能因為我沒有足夠的代表 - 所以請不要投票。 該代碼包含一個錯誤,我想指出個人試圖使用此解決方案:

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

應該

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

高明!


我非常喜歡JakubŠturc的回答,但缺點是你不能將它與switch-case語句一起使用。 這是他的答案的一個稍微修改過的版本,可以與switch語句一起使用:

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

因此,您可以獲得JakubŠturc答案的所有好處,此外我們還可以使用switch語句來使用它:

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

當我遇到這個問題時,我有一些問題試圖首先找到答案:

  • 我的枚舉值的名稱是否足夠友好,還是需要提供更友好的名稱?
  • 我需要往返嗎? 也就是說,我需要獲取文本值並將它們解析為枚舉值嗎?
  • 這是我需要為我的項目中的許多枚舉做什麼,或者只有一個?
  • 我將使用哪種UI元素來呈現此信息 - 特別是,我將綁定到UI還是使用屬性表?
  • 這是否需要本地化?

最簡單的方法是使用Enum.GetValue (並支持使用Enum.Parse進行Enum.Parse )。 像Steve Mitcham所建議的那樣,經常需要構建TypeConverter來支持UI綁定。 (當你使用屬性表時,沒有必要建立一個TypeConverter ,這是關於屬性表的好事之一,儘管Lord知道他們有自己的問題。)

一般情況下,如果上述問題的答案表明不起作用,那麼我的下一步是創建並填充一個靜態Dictionary<MyEnum, string> ,或者可能是一個Dictionary<Type, Dictionary<int, string>> 。 我傾向於跳過中間裝飾 - 帶代碼的屬性步驟,因為接下來派克通常會在部署之後需要更改友好值(通常,但並非總是,因為本地化)。







enums