Как преобразовать строку в перечисление в C#?

каков наилучший способ преобразования строки в значение перечисления в C#?

У меня есть тег выбора HTML, содержащий значения перечисления. Когда страница будет опубликована, я хочу забрать значение (которое будет в виде строки) и преобразовать его в значение перечисления.

в идеальном мире, я мог бы сделать что-то вроде этого:

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

но это недопустимый код.

20 ответов


в .NET Core и .NET >4 существует общий метод синтаксического анализа:

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

это также включает в себя новый встроенный c#7 out переменные, так что это делает 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) было бы допустимо, но бессмысленно) . Часто лучше избегать загромождения основных классов Microsoft дополнительными методами, которые применяются только в очень конкретных контекстах, если вся ваша команда разработчиков не имеет очень хорошего понимания того, что делают эти расширения.


использовать Enum.TryParse<T>(String, T) (≥ .Net версии 4.0):

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

его можно упростить еще больше с C# 7.0 л!--8-->тип параметра inlining:

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

обратите внимание, что производительность перечисление.Parse () ужасен, потому что он реализуется через отражение. (То же самое относится и к Enum.ToString, который идет в другую сторону.)

Если вам нужно преобразовать строки В перечисления в коде, чувствительном к производительности, лучше всего создать Dictionary<String,YourEnum> при запуске и используйте это для преобразования.


вы ищете перечисление.Разбор.

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

можно использовать методы расширения теперь:

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

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

поэтому, если бы у вас было перечисление с именем mood, оно выглядело бы так:

   enum Mood
   {
      Angry,
      Happy,
      Sad
   } 

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

внимание:

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

перечисление.Разбор - ваш друг:

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

вы можете расширить принятый ответ со значением по умолчанию, чтобы избежать исключений:

public static T ParseEnum<T>(string value, T defaultValue) where T : struct
{
    try
    {
        T enumValue;
        if (!Enum.TryParse(value, true, out enumValue))
        {
            return defaultValue;
        }
        return enumValue;
    }
    catch (Exception)
    {
        return defaultValue;
    }
}

тогда вы называете это так:

StatusEnum MyStatus = EnumUtil.ParseEnum("Active", StatusEnum.None);

мы не могли предположить совершенно корректный ввод и пошли с этой вариацией ответа @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;
}

// str.ToEnum<EnumType>()
T static ToEnum<T>(this string str) 
{ 
    return (T) Enum.Parse(typeof(T), str);
}

анализирует строку в TEnum без try / catch и без метода TryParse () из .NET 4.5

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

попробуйте этот пример:

 public static T GetEnum<T>(string model)
    {
        var newModel = GetStringForEnum(model);

        if (!Enum.IsDefined(typeof(T), newModel))
        {
            return (T)Enum.Parse(typeof(T), "None", true);
        }

        return (T)Enum.Parse(typeof(T), newModel.Result, true);
    }

    private static Task<string> GetStringForEnum(string model)
    {
        return Task.Run(() =>
        {
            Regex rgx = new Regex("[^a-zA-Z0-9 -]");
            var nonAlphanumericData = rgx.Matches(model);
            if (nonAlphanumericData.Count < 1)
            {
                return model;
            }
            foreach (var item in nonAlphanumericData)
            {
                model = model.Replace((string)item, "");
            }
            return model;
        });
    }

в этом примере вы можете отправить каждую строку, и установить Enum. Если Enum были данные, которые вы хотели, верните это как ваш Enum тип.

GoodLock.


супер простой код с помощью TryParse:

var value = "Active";

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

мне нравится решение метода расширения..

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
        }

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.

я использовал класс (строго типизированная версия перечисления с разбором и улучшением производительности). Я нашел его на GitHub,и он должен работать и для .NET 3.5. Он имеет некоторые накладные расходы памяти, так как он буферизует словарь.

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

блогпост-это перечисления-лучший синтаксис, улучшенная производительность и TryParse в NET 3.5.

и код: https://github.com/damieng/DamienGKit/blob/master/CSharp/DamienG.Library/System/EnumT.cs


для производительности это может помочь:

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

я обнаружил, что здесь случай со значениями перечисления, которые имеют значение 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;
}

и пример enum типа:

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

вы должны использовать перечислимый.Разбор, чтобы получить значение объекта из перечисления, после этого вы должны изменить значение объекта на определенное значение перечисления. Приведение к значению перечисления можно сделать с помощью Convert.Метод changetype. Пожалуйста, посмотрите на следующий фрагмент кода

public T ConvertStringValueToEnum<T>(string valueToParse){
    return Convert.ChangeType(Enum.Parse(typeof(T), valueToParse, true), typeof(T));
}