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

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

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

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

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

но это не правильный код

19.08.2008 12:51:55
24 ОТВЕТА
РЕШЕНИЕ

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

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

Это также включает в себя новые встроенные outпеременные C # 7 , так что это делает try-parse, преобразование в явный тип enum и инициализирует + заполняет 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 дополнительными методами, которые применяются только в очень специфических контекстах, если только ваша команда разработчиков не очень хорошо понимает, что делают эти расширения.

1469
16.02.2017 16:58:04
Если важна производительность (которая всегда такова), ответ ЧК, данный Маккензигом1 ниже: stackoverflow.com/questions/16100/…
Nash 19.07.2009 19:04:12
@avinashr прав насчет ответа @ McKenzieG1, но это не всегда важно. Например, было бы бессмысленной микрооптимизацией беспокоиться о разборе enum, если вы выполняете вызов БД для каждого разбора.
Keith 19.07.2009 20:19:13
@HM Я не думаю, что здесь уместно расширение - это особый случай, и расширение будет применяться к каждой строке. Если бы вы действительно хотели сделать это, хотя это было бы тривиальным изменением.
Keith 22.08.2013 10:17:07
Как насчет Enum.TryParse?
Elaine 3.06.2014 08:39:25
очень хорошо. вам нужно где T: struct в вашем последнем примере.
bbrik 22.12.2015 17:13:00

Вы ищете Enum.Parse .

SomeEnum enum = (SomeEnum)Enum.Parse(typeof(SomeEnum), "EnumValue");
93
30.08.2015 12:37:12

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

StatusEnum MyStatus = (StatusEnum)Enum.Parse(typeof(StatusEnum), "Active");
16
30.08.2015 12:37:32
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());
20
19.08.2008 12:58:57
// str.ToEnum<EnumType>()
T static ToEnum<T>(this string str) 
{ 
    return (T) Enum.Parse(typeof(T), str);
}
7
5.09.2014 15:06:47

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

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

195
26.07.2019 01:19:27
Я измерял 3 мс, чтобы преобразовать строку в Enum при первом запуске на настольном компьютере. (Просто чтобы проиллюстрировать уровень ужасности).
Matthieu Charbonnier 26.11.2017 21:00:06
Вау, 3 мс - ужасные порядки
John Stock 14.12.2017 03:25:37
Можете ли вы добавить пример кода вокруг этого, чтобы мы получили представление о том, как заменить и использовать
transformer 5.01.2018 07:32:35
Если вашим приложением пользуются 1 миллион человек =>, это добавляет до 50 часов человеческой жизни, которую вы потребляете :) На одной странице. : P
Cătălin Rădoi 22.04.2020 15:31:24

Мы не могли предположить абсолютно достоверный ввод, и пошли с этим вариантом ответа @ 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;
}
8
30.08.2012 15:07:10

Разбирает строку в 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;
}
5
30.10.2013 12:46:11
Нужно ли составлять описание, если код уже содержит описание? Хорошо, я сделал это :)
jite.gs 30.10.2013 12:43:44

Используйте Enum.TryParse<T>(String, T)(≥ .NET 4.0):

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

Это можно еще больше упростить с помощью встроенного типа параметра в C # 7.0 :

Enum.TryParse("Active", out StatusEnum myStatus);
324
14.10.2017 16:20:56
Добавьте средний логический параметр для чувствительности к регистру, и это самое безопасное и элегантное решение на сегодняшний день.
DanM7 28.02.2014 22:05:18
Да ладно, сколько из вас реализовало этот выбранный ответ 2008 года, чтобы прокрутить вниз и найти, что это лучший (современный) ответ.
TEK 18.03.2016 14:47:36
@TEK Я действительно предпочитаю ответ 2008 года.
Zero3 24.11.2016 10:20:51
Я не понимаю Parseгенерирует объяснительные исключения для того, что пошло не так с преобразованием (значение было nullпустым или не имело соответствующей константы перечисления), что намного лучше, чем TryParseлогическое возвращаемое значение (которое подавляет конкретную ошибку)
yair 4.01.2018 18:42:32
Enum.TryParse <T> (String, T) имеет недостатки при разборе целочисленных строк. Например, этот код будет успешно анализировать бессмысленную строку как бессмысленное перечисление: var result = Enum.TryParse<System.DayOfWeek>("55", out var parsedEnum);
Mass Dot Net 11.09.2019 21:38:12

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

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

И вы можете вызвать их по следующему коду (здесь FilterTypeэто тип enum):

FilterType filterType = type.ToEnum<FilterType>();
30
30.08.2015 12:40:29
Я обновил это, чтобы принять значение в качестве объекта и привести его к строке внутри этого метода. Таким образом, я могу принять значение типа .ToEnum вместо только строк.
RealSollyM 14.02.2014 12:05:04
@ SollyM Я бы сказал, что это ужасная идея, потому что тогда этот метод расширения будет применяться ко всем типам объектов. Два метода расширения, один для строки и один для int, на мой взгляд, будут чище и намного безопаснее.
Svish 9.12.2014 11:56:40
@ Свист, это правда. Единственная причина, по которой я это сделал, заключается в том, что наш код используется только для внутреннего использования, и я хотел избежать написания двух расширений. И поскольку мы конвертируем в Enum единственный раз с помощью string или int, я не вижу в этом проблемы.
RealSollyM 10.12.2014 14:36:42
@SollyM Внутренний или нет, я все еще поддерживаю и использую свой код: PI будет раздражен, если я получу ToEnum в каждом меню intellisense, и, как вы говорите, поскольку единственный раз, когда вы конвертируете в enum, это строка или int, вы можете быть уверены, что вам понадобятся только эти два метода. И два метода не намного больше, чем один, особенно когда они такие маленькие и имеют тип утилиты: P
Svish 11.12.2014 11:09:25

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

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 завершится ошибкой и выдаст исключение, которое перехватывается.

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

13
2.07.2019 09:45:53
Я не люблю значения по умолчанию. Это может привести к непредсказуемым результатам.
Daniël Tulp 26.05.2016 07:46:27
когда это когда-нибудь вызовет исключение?
andleer 28.05.2016 15:28:23
@andleer, если значение перечисления не соответствует тому же типу перечисления, что и значение по умолчанию
Nelly 7.01.2020 14:43:58
@Nelly Старый код здесь, но defaultValueтип возвращаемого значения и метод оба имеют тип T. Если типы различаются, вы получите ошибку времени компиляции: «невозможно преобразовать из« ConsoleApp1.Size »в« ConsoleApp1.Color »» или что-то еще из ваших типов.
andleer 7.01.2020 15:33:39
@andleer, извини, мой последний ответ был неверным. Возможно, что этот метод генерирует исключение Syste.ArgumentException в случае, если кто-то вызывает эту функцию со значением по умолчанию, отличным от типа enum. С c # 7.0 я не мог сделать предложение where T: Enum. Вот почему я поймал эту возможность с попыткой поймать.
Nelly 9.01.2020 08:12:17

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

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

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

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

1
30.08.2015 16:33:43
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.
1
18.08.2015 05:28:07

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

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
        }
2
1.10.2015 21:57:29

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 () может дать вам значение, которое даже не определено для enum!

Я не хотел бы испытывать последствия пользователей, добровольно или невольно, запускающих такое поведение.

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

Я предлагаю следующее:

    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);
    }
18
22.03.2016 08:21:54
На самом деле это очень полезно знать Enum.(Try)Parse accepts multiple, comma-separated arguments, and combines them with binary 'or'. Означает, что вы можете установить значения enum в виде степеней 2, и у вас есть очень простой способ анализа нескольких логических флагов, например. "UseSSL, NoRetries, Sync". На самом деле это, вероятно, то, для чего он был разработан.
pcdev 22.08.2018 08:00:35

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

    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];
    }
1
22.06.2016 14:10:58

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

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

var value = "Active";

StatusEnum status;
if (!Enum.TryParse<StatusEnum>(value, out status))
    status = StatusEnum.Unknown;
3
25.11.2016 02:30:35

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

public T ConvertStringValueToEnum<T>(string valueToParse){
    return Convert.ChangeType(Enum.Parse(typeof(T), valueToParse, true), typeof(T));
}
1
12.02.2017 12:11:01

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

 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тип.

1
17.10.2018 14:59:18
Вы перезаписываете newModelкаждую строку, поэтому, если она содержит тире, она не будет заменена. Кроме того, вам не нужно проверять, содержит ли строка что-либо, вы все Replaceравно можете просто позвонить :var newModel = model.Replace("-", "").Replace(" ", "");
Lars Kristensen 1.10.2018 09:52:47
@LarsKristensen Да, мы можем создать метод удаления не буквенно-цифровых символов.
AmirReza-Farahlagha 1.10.2018 09:56:07
        <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
0
22.05.2019 07:15:54
public TEnum ToEnum<TEnum>(this string value, TEnum defaultValue){
if (string.IsNullOrEmpty(value))
    return defaultValue;

return Enum.Parse(typeof(TEnum), value, true);}
0
22.05.2019 07:19:51

Не уверен, когда это было добавлено, но в классе Enum теперь есть

Parse<TEnum>(stringValue)

Используется примерно так с примером в вопросе:

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

или игнорирование корпуса:

var MyStatus = Enum.Parse<StatusEnum >("active", true)

Вот декомпилированные методы, которые это использует:

    [NullableContext(0)]
    public static TEnum Parse<TEnum>([Nullable(1)] string value) where TEnum : struct
    {
      return Enum.Parse<TEnum>(value, false);
    }

    [NullableContext(0)]
    public static TEnum Parse<TEnum>([Nullable(1)] string value, bool ignoreCase) where TEnum : struct
    {
      TEnum result;
      Enum.TryParse<TEnum>(value, ignoreCase, true, out result);
      return result;
    }
1
27.11.2019 18:39:51

Если имя свойства отличается от того, что вы хотите назвать (т.е. языковые различия), вы можете сделать это следующим образом:

MyType.cs

using System;
using System.Runtime.Serialization;
using Newtonsoft.Json;
using Newtonsoft.Json.Converters;

[JsonConverter(typeof(StringEnumConverter))]
public enum MyType
{
    [EnumMember(Value = "person")]
    Person,
    [EnumMember(Value = "annan_deltagare")]
    OtherPerson,
    [EnumMember(Value = "regel")]
    Rule,
}

EnumExtensions.cs

using System;
using Newtonsoft.Json;
using Newtonsoft.Json.Converters;

public static class EnumExtensions
{
    public static TEnum ToEnum<TEnum>(this string value) where TEnum : Enum
    {
        var jsonString = $"'{value.ToLower()}'";
        return JsonConvert.DeserializeObject<TEnum>(jsonString, new StringEnumConverter());
    }

    public static bool EqualsTo<TEnum>(this string strA, TEnum enumB) where TEnum : Enum
    {
        TEnum enumA;
        try
        {
            enumA = strA.ToEnum<TEnum>();
        }
        catch
        {
            return false;
        }
        return enumA.Equals(enumB);
    }
}

Program.cs

public class Program
{
    static public void Main(String[] args) 
    { 
        var myString = "annan_deltagare";
        var myType = myString.ToEnum<MyType>();
        var isEqual = myString.EqualsTo(MyType.OtherPerson);
        //Output: true
    }     
}
0
24.01.2020 13:28:38