Преобразовать строку в перечисление в С#

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

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

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

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

но это недействительный код.

Ответ 1

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

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

Это также включает в себя новые переменные inline out С# 7, поэтому это происходит с помощью try-parse, преобразования в явный тип перечисления и инициализирует + заполняет переменную myStatus.

Если у вас есть доступ к С# 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 дополнительными методами, которые применяются только в очень специфических контекстах, если только ваша команда разработчиков не очень хорошо понимает, что делают эти расширения.

Ответ 3

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

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

Ответ 4

Вы ищете Enum.Parse.

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

Ответ 5

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

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

Ответ 6

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

Ответ 7

BEWARE:

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

Ответ 8

Enum.Parse твой друг:

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

Ответ 9

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

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

Если значение по умолчанию не является enum, Enum.TryParse завершится ошибкой и выдаст исключение, которое перехватывается.

После многих лет использования этой функции в нашем коде во многих местах может быть полезно добавить информацию, что эта операция стоит производительности!

Ответ 10

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

Ответ 11

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

Ответ 12

Переводит строку в 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;
}

Ответ 13

Супер простой код с использованием TryParse:

var value = "Active";

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

Ответ 14

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

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
        }

Ответ 15

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

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

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

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

Ответ 16

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.

Ответ 17

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

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

Ответ 18

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

Ответ 19

Вы должны использовать Enum.Parse для получения значения объекта из Enum, после чего вам нужно изменить значение объекта на определенное значение enum. Значение Casting to enum может быть выполнено с помощью Convert.ChangeType. Пожалуйста, посмотрите следующий фрагмент кода

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

Ответ 20

Попробуйте этот образец:

 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.

Ответ 21

        <Extension()>
    Public Function ToEnum(Of TEnum)(ByVal value As String, ByVal defaultValue As TEnum) As TEnum
        If String.IsNullOrEmpty(value) Then
            Return defaultValue
        End If

        Return [Enum].Parse(GetType(TEnum), value, True)
    End Function

Ответ 22

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

return Enum.Parse(typeof(TEnum), value, true);}