c# enum用法 - 我應該如何將字符串轉換為C#中的枚舉?




enum描述 int (17)

將字符串轉換為C#中的枚舉值的最佳方式是什麼?

我有一個包含枚舉值的HTML選擇標記。 當頁面發佈時,我想獲取值(它將以字符串的形式)並將其轉換為枚舉值。

在理想的世界中,我可以做這樣的事情:

StatusEnum MyStatus = StatusEnum.Parse("Active");

但這不是有效的代碼。


Answers

我喜歡擴展方法解決方案..

namespace System
{
    public static class StringExtensions
    {

        public static bool TryParseAsEnum<T>(this string value, out T output) where T : struct
        {
            T result;

            var isEnum = Enum.TryParse(value, out result);

            output = isEnum ? result : default(T);

            return isEnum;
        }
    }
}

這裡下面我的測試執行。

using static Microsoft.VisualStudio.TestTools.UnitTesting.Assert;
using static System.Console;

private enum Countries
    {
        NorthAmerica,
        Europe,
        Rusia,
        Brasil,
        China,
        Asia,
        Australia
    }

   [TestMethod]
        public void StringExtensions_On_TryParseAsEnum()
        {
            var countryName = "Rusia";

            Countries country;
            var isCountry = countryName.TryParseAsEnum(out country);

            WriteLine(country);

            IsTrue(isCountry);
            AreEqual(Countries.Rusia, country);

            countryName = "Don't exist";

            isCountry = countryName.TryParseAsEnum(out country);

            WriteLine(country);

            IsFalse(isCountry);
            AreEqual(Countries.NorthAmerica, country); // the 1rst one in the enumeration
        }

Enum.Parse是你的朋友:

StatusEnum MyStatus = (StatusEnum)Enum.Parse(typeof(StatusEnum), "Active");

從.NET 4.5開始,沒有try / catch和沒有TryParse()方法的情況下,將字符串解析為TEnum

/// <summary>
/// Parses string to TEnum without try/catch and .NET 4.5 TryParse()
/// </summary>
public static bool TryParseToEnum<TEnum>(string probablyEnumAsString_, out TEnum enumValue_) where TEnum : struct
{
    enumValue_ = (TEnum)Enum.GetValues(typeof(TEnum)).GetValue(0);
    if(!Enum.IsDefined(typeof(TEnum), probablyEnumAsString_))
        return false;

    enumValue_ = (TEnum) Enum.Parse(typeof(TEnum), probablyEnumAsString_);
    return true;
}

謹防:

enum Example
{
    One = 1,
    Two = 2,
    Three = 3
}

Enum.(Try)Parse() 接受多個逗號分隔的參數,並將它們與二進制“或”相結合 。 你不能禁用這個,在我看來你幾乎不需要它。

var x = Enum.Parse("One,Two"); // x is now Three

即使Three沒有定義, x仍然會得到int值3 。 這更糟糕:Enum.Parse()可以給你一個甚至沒有為枚舉定義的值!

我不想親自或不願意體驗用戶的後果,引發這種行為。

此外,正如其他人所提到的那樣,對於大枚舉來說性能並不理想,即可能值的數量是線性的。

我建議如下:

    public static bool TryParse<T>(string value, out T result)
        where T : struct
    {
        var cacheKey = "Enum_" + typeof(T).FullName;

        // [Use MemoryCache to retrieve or create&store a dictionary for this enum, permanently or temporarily.
        // [Implementation off-topic.]
        var enumDictionary = CacheHelper.GetCacheItem(cacheKey, CreateEnumDictionary<T>, EnumCacheExpiration);

        return enumDictionary.TryGetValue(value.Trim(), out result);
    }

    private static Dictionary<string, T> CreateEnumDictionary<T>()
    {
        return Enum.GetValues(typeof(T))
            .Cast<T>()
            .ToDictionary(value => value.ToString(), value => value, StringComparer.OrdinalIgnoreCase);
    }

為了表現這可能有所幫助:

    private static Dictionary<Type, Dictionary<string, object>> dicEnum = new Dictionary<Type, Dictionary<string, object>>();
    public static T ToEnum<T>(this string value, T defaultValue)
    {
        var t = typeof(T);
        Dictionary<string, object> dic;
        if (!dicEnum.ContainsKey(t))
        {
            dic = new Dictionary<string, object>();
            dicEnum.Add(t, dic);
            foreach (var en in Enum.GetValues(t))
                dic.Add(en.ToString(), en);
        }
        else
            dic = dicEnum[t];
        if (!dic.ContainsKey(value))
            return defaultValue;
        else
            return (T)dic[value];
    }

public static T ParseEnum<T>(string value)            //function declaration  
{
    return (T) Enum.Parse(typeof(T), value);
}

Importance imp = EnumUtil.ParseEnum<Importance>("Active");   //function call

====================完整的程序====================

using System;

class Program
{
    enum PetType
    {
    None,
    Cat = 1,
    Dog = 2
    }

    static void Main()
    {

    // Possible user input:
    string value = "Dog";

    // Try to convert the string to an enum:
    PetType pet = (PetType)Enum.Parse(typeof(PetType), value);

    // See if the conversion succeeded:
    if (pet == PetType.Dog)
    {
        Console.WriteLine("Equals dog.");
    }
    }
}
-------------
Output

Equals dog.

我們不能假設完全有效的輸入,並且與@ Keith的答案的這種變化一起使用:

public static TEnum ParseEnum<TEnum>(string value) where TEnum : struct
{
    TEnum tmp; 
    if (!Enum.TryParse<TEnum>(value, true, out tmp))
    {
        tmp = new TEnum();
    }
    return tmp;
}

我發現這裡沒有考慮具有EnumMember值的枚舉值的情況。 所以我們走吧:

using System.Runtime.Serialization;

public static TEnum ToEnum<TEnum>(this string value, TEnum defaultValue) where TEnum : struct
{
    if (string.IsNullOrEmpty(value))
    {
        return defaultValue;
    }

    TEnum result;
    var enumType = typeof(TEnum);
    foreach (var enumName in Enum.GetNames(enumType))
    {
        var fieldInfo = enumType.GetField(enumName);
        var enumMemberAttribute = ((EnumMemberAttribute[]) fieldInfo.GetCustomAttributes(typeof(EnumMemberAttribute), true)).FirstOrDefault();
        if (enumMemberAttribute?.Value == value)
        {
            return Enum.TryParse(enumName, true, out result) ? result : defaultValue;
        }
    }

    return Enum.TryParse(value, true, out result) ? result : defaultValue;
}

那個枚舉的例子:

public enum OracleInstanceStatus
{
    Unknown = -1,
    Started = 1,
    Mounted = 2,
    Open = 3,
    [EnumMember(Value = "OPEN MIGRATE")]
    OpenMigrate = 4
}

object Enum.Parse(System.Type enumType, string value, bool ignoreCase);

所以如果你有一個名為情緒的枚舉,它看起來像這樣:

   enum Mood
   {
      Angry,
      Happy,
      Sad
   } 

   // ...
   Mood m = (Mood) Enum.Parse(typeof(Mood), "Happy", true);
   Console.WriteLine("My mood is: {0}", m.ToString());

請注意,Enum.Parse()的性能很糟糕,因為它是通過反射來實現的。 (Enum.ToString也是如此,反之亦然。)

如果您需要在性能敏感的代碼中將字符串轉換為枚舉類型,最好的方法是在啟動時創建一個Dictionary<String,YourEnum> ,並使用它來完成轉換。



您現在可以使用擴展方法

public static T ToEnum<T>(this string value, bool ignoreCase = true)
{
    return (T) Enum.Parse(typeof (T), value, ignoreCase);
}

你可以通過下面的代碼調用它們(在這裡, FilterType是一個枚舉類型):

FilterType filterType = type.ToEnum<FilterType>();

在.NET Core和.NET> 4中有一個通用的分析方法

Enum.TryParse("Active", out StatusEnum myStatus);

這還包括C#7的新內聯變量,因此它會執行try-parse,轉換為顯式枚舉類型並初始化+填充myStatus變量。

如果你有權訪問C#7和最新的.NET,這是最好的方法。

原始答复

在.NET中它相當難看(直到4或以上):

StatusEnum MyStatus = (StatusEnum) Enum.Parse(typeof(StatusEnum), "Active", true);

我傾向於通過以下方式簡化它:

public static T ParseEnum<T>(string value)
{
    return (T) Enum.Parse(typeof(T), value, true);
}

然後我可以這樣做:

StatusEnum MyStatus = EnumUtil.ParseEnum<StatusEnum>("Active");

評論中提出的一個選擇是添加一個足夠簡單的擴展:

public static T ToEnum<T>(this string value)
{
    return (T) Enum.Parse(typeof(T), value, true);
}

StatusEnum MyStatus = "Active".ToEnum<StatusEnum>();

最後,如果字符串不能被解析,你可能想要使用默認的枚舉:

public static T ToEnum<T>(this string value, T defaultValue) 
{
    if (string.IsNullOrEmpty(value))
    {
        return defaultValue;
    }

    T result;
    return Enum.TryParse<T>(value, true, out result) ? result : defaultValue;
}

這就是呼籲:

StatusEnum MyStatus = "Active".ToEnum(StatusEnum.None);

然而,我會小心增加一個像這樣的擴展方法來作為(沒有命名空間控制) string它會出現在string所有實例是否持有一個枚舉或不(因此1234.ToString().ToEnum(StatusEnum.None)會有效但無意義)。 除非整個開發團隊都非常了解這些擴展的功能,否則通常最好避免使用僅適用於特定環境的額外方法混淆微軟的核心類。


你正在尋找Enum.Parse

SomeEnum enum = (SomeEnum)Enum.Parse(typeof(SomeEnum), "EnumValue");

我使用類(Enum的強類型版本解析和性能改進)。 我在GitHub上找到了它,它也適用於.NET 3.5。 它有一些內存開銷,因為它緩衝了一個字典。

StatusEnum MyStatus = Enum<StatusEnum>.Parse("Active");

blogpost是枚舉 - 更好的語法,改進的性能和NET 3.5中的TryParse

和代碼: https://github.com/damieng/DamienGKit/blob/master/CSharp/DamienG.Library/System/EnumT.cshttps://github.com/damieng/DamienGKit/blob/master/CSharp/DamienG.Library/System/EnumT.cs


超級簡單的代碼使用TryParse:

var value = "Active";

StatusEnum status;
if (!Enum.TryParse<StatusEnum>(value, out status))
    status = StatusEnum.Unknown;







c# string enums