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

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

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

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

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

но это неверный код.

Стоит ли изучать PHP в 2026-2027 годах?
Стоит ли изучать PHP в 2026-2027 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
1 011
0
815 432
25
Перейти к ответу Данный вопрос помечен как решенный

Ответы 25

Вы ищете Enum.Parse.

SomeEnum enum = (SomeEnum)Enum.Parse(typeof(SomeEnum), "EnumValue");
Ответ принят как подходящий

В .NET Core и .NET Framework ≥4.0 есть общий метод синтаксического анализа:

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

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

Если производительность важна (что всегда так), chk ответ, данный Mckenzieg1 ниже: stackoverflow.com/questions/16100/…

Nash 19.07.2009 23:04

@avinashr прав насчет ответа @McKenzieG1, но это не ВСЕГДА важно. Например, было бы бессмысленной микро-оптимизацией беспокоиться о синтаксическом анализе перечисления, если бы вы выполняли вызов БД для каждого синтаксического анализа.

Keith 20.07.2009 00:19

сделайте это методом расширения, и он будет идеальным

H.M. 22.08.2013 12:57

@ H.M. Я не думаю, что здесь уместно расширение - это немного особый случай, и расширение будет применяться к строке каждый. Если бы вы действительно хотели это сделать, это было бы тривиальным изменением.

Keith 22.08.2013 14:17

@keith, это было мое мнение, потому что статические классы очень трудно запомнить, но всегда следует помнить о методе расширения! и да, для параметра требуется только ключевое слово <code>this</code>

H.M. 27.08.2013 18:32

Как насчет Enum.TryParse?

Elaine 03.06.2014 12:39

@ H.M. Я добавил версию расширения, с предупреждением.

Keith 10.08.2015 09:57

@Elaine Добавлен вариант, использующий TryParse.

Keith 10.08.2015 09:58

@Keith: Не могли бы вы рассказать немного о том, что было бы против создания из этого метода расширения? Я не понимаю, в чем может быть проблема.

Aage 02.10.2015 12:46

@bump да - как правило, вы должны быть очень осторожны, расширяя встроенные объекты Microsoft, особенно очень распространенные, такие как object или string, поскольку без тщательного контроля пространства имен это приведет к тому, что ваш метод расширения будет виден в каждом экземпляре всей вашей базы кода . Это не проблема, если метод всегда подходит (например, .Trim() или .Split()), но анализ перечисления будет применяться только тогда, когда ожидается, что строка будет содержать перечисление. Один или два таких метода, вероятно, подойдут, но вы можете столкнуться с множеством запутанных в больших проектах.

Keith 02.10.2015 12:54

@Keith Хорошо, но это применимо только в том случае, если пространство имен включено в права использования? Значит, namespace SomeNameSpace.Extensions.Strings не вызовет никаких проблем?

Aage 02.10.2015 13:01

@bump да, именно это я имею в виду под управлением пространством имен. Вы можете поместить эти расширения в пространства имен, чтобы разрешить их доступность - это баланс удобства и стандартизации кода, который во многом зависит от вашей команды разработчиков.

Keith 02.10.2015 13:03

очень хорошо. вам нужна структура where T: в последнем примере.

bbrik 22.12.2015 20:13

@bbrik это не особо помогает - нам действительно нужно общее ограничение enum: stackoverflow.com/q/7244/905

Keith 23.12.2015 11:52

Я не считаю расширение таким уж плохим. С помощью встроенных классов и т. д. Вы можете писать бессмысленный код независимо от расширений. Чтобы использовать расширение, вы должны включить его, а затем фактически использовать. Даже в случаях, следующих за предложенным в этих комментариях использованием расширений, вам все равно понадобится обработка ошибок в случае неправильного ввода, я не понимаю, чем это отличается. Очевидно, что вы должны поместить любые и все расширения, которые вы пишете, которые расширяют базовые классы, в ваше собственное пространство имен.

Peter 16.03.2016 14:36

@Peter Я не сказал, что это плохо, просто нужно быть осторожным - расширение, похоже, применяется ко всем экземплярам строки, но имеет смысл только в подмножестве контекстов. Вы можете обрабатывать ошибочные вводы, но это проверка времени выполнения на наличие проблемы времени компиляции. Например, SubString() всегда применяется к строке, и во время выполнения вы проверяете, является ли строка слишком короткой для предоставленных индексов, но ToEnum может или не может применяться в зависимости от того, что вы вводите. Это неплохо или блокирует как таковое, просто что-то быть осторожным.

Keith 16.03.2016 20:42

Я понимаю, что вы имеете в виду, возможно, если бы он назывался TryToEnum (), он был бы более явным и прояснил бы, что он может потерпеть неудачу. По сути, расширения - это сахар компилятора, метод расширения .ToEnum () ничем не отличается от Enum.Parse () - параметр строки не может быть гарантированно преобразован.

Peter 18.03.2016 13:43

Как упоминалось в @bbrik, вам нужно добавить where T: struct в ваш последний пример, чтобы компилятор принял его.

Kenci 19.08.2016 09:52

@Keith - я хочу использовать метод расширения, в котором можно указать значение по умолчанию. Я скопировал и вставил ваш метод расширения, но получил сообщение об ошибке: тип 'T' должен быть типом значения, не допускающим значения NULL, чтобы использовать его в качестве параметра 'TEnum' в универсальном типе или методе 'System.Enum.TryParse(строка, булево, выход TEnum) '

barrypicker 14.09.2016 02:30

@barrypicker смотрите комментарий выше. public static T ToEnum<T>(this string value, T defaultValue) where T : struct

Dominic Jonas 13.12.2016 12:07

where T : struct, IComparable необходимо добавить в конце определения метода расширения.

Siyavash Hamdi 16.10.2017 18:54

Должно получиться следующее: StatusEnum myStatus; Enum.TryParse («Активный», вне myStatus);

Altaf Patel 22.06.2018 12:57

@AltafPatel в C# 7 и выше вам не нужно сначала объявлять перечисление, вы можете сделать if (Enum.TryParse("Active", out StatusEnum myStatus)) { /* use myStatus */ }

Keith 22.06.2018 13:05

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

Nyerguds 20.01.2020 11:52

Так что, если это применимо к каждой строке. Если ToEnum в каком-либо сценарии недостаточно ясен - вы определенно не должны быть разработчиком. Вроде да ладно ... нет ничего яснее этого. Мы пограничное программирование на английском языке.

Bojidar Stanchev 14.07.2020 18:35

@BojidarStanchev ToEnum достаточно ясен, проблема в том, что всегда расширяет string. Это нормально для небольшой команды, но если у вас большая команда или вы работаете над API, это может стать довольно запутанным, если каждый расширяет базовые типы .NET. Существует соглашение (за пределами .NET Core DI, которое использует это намеренно), чтобы быть осторожным при публичном расширении типов, таких как int и string, без пространства имен, поскольку пользователи вашего API получат эти расширения, они получат все расширения из каждой библиотеки. они используют.

Keith 15.07.2020 10:23

@Nyerguds - вот почему вы должны следовать приведенному ниже ответу McKenzieG1, который рекомендует создать словарь из типа перечисления. Это позволяет избежать упаковки / распаковки и отражения, используемых в Enum.Parse и Enum.TryParse. В качестве дополнительного бонуса вы получаете производительность 0 (1) и получаете только фактические значения перечисления в типе.

sanmcp 03.08.2020 20:35

@sanmcp Я сказал, что это из файла конфигурации. Файлы конфигурации обычно читаются только один раз при запуске, и этот конкретный файл конфигурации принадлежал службе Windows, поэтому его никогда не следует перезапускать, если что-то специально не требует обновления. Так что это ситуация, когда производительность не имеет ни малейшего значения.

Nyerguds 07.08.2020 13:22

Я сказал, что производительность - это дополнительный бонус, но важно то, что вам не нужно использовать check for numeric content in advance перед преобразованием строки в enum. Если перечисление преобразовано в словарь, как предлагает @ McKenzieG1, вам не нужно беспокоиться о том, является ли аргумент числом или не частью перечисления.

sanmcp 07.08.2020 18:25

@sanmcp Да, если производительность критична, стоит потратить накладные расходы на создание словаря из отражения в первый раз, а затем на его проверку. Фактически, именно так ToEnum, который моя организация использовала во время этого ответа, полностью работал, хотя и немного по-другому - мы добавили бы ConcurrentDictionary<string, T> для каждого типа перечисления при первом синтаксическом анализе, а затем использовали бы его в качестве кеша (у нас было несколько значений которые часто возникали из большого набора).

Keith 10.08.2020 20:52

@sanmcp Однако это является микрооптимизация, так что это не то, что в подавляющем большинстве контекстов необходимость. В основном вы анализируете перечисления при сериализации: DB читает или анализирует JSON / XML, и во всех этих случаях экономия на пропуске отражения крошечная по сравнению с синтаксическим анализом DB / JSON / XML. Просто задержка подключения к БД или сети для JSON / XML, вероятно, будет на порядок медленнее. Итак, имея это в виду, я бы порекомендовал начало с простым Enum.TryParse, а затем переключиться на какой-то кеш отражения во время оптимизации, если он вам нужен.

Keith 10.08.2020 20:57

«Если производительность важна» (что всегда так) »- одно из самых глупых утверждений, которые я когда-либо читал на SO. Другие приводили контрпримеры, поэтому я не буду беспокоиться, но помните, что« Преждевременная оптимизация - это корень всего. зло"

ckapilla 31.10.2020 20:59

Enum.Parse ваш друг:

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

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

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

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

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

Я измерил 3 мс, чтобы преобразовать строку в Enum при первом запуске на настольном компьютере. (Просто чтобы проиллюстрировать уровень ужасности).

Matthieu Charbonnier 27.11.2017 00:00

Вау 3 мс - это ужасные порядки

John Stock 14.12.2017 06:25

можете ли вы добавить пример кода вокруг этого, чтобы мы получили представление о том, как заменить и использовать

Transformer 05.01.2018 10:32

Если ваше приложение используют 1 миллион человек => это добавляет к 50 часам человеческой жизни, которые вы потребляете :) На использование одной страницы. :П

Cătălin Rădoi 22.04.2020 18:31

Хотя первый запуск 3 мс определенно ужасен, будет ли второй запуск лучше? Если это 3 мс каждый раз, мы бы избегали этого, как чумы

Lionet Chen 19.10.2020 04:01

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

Разбирает строку в 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;
}

Нужно ли делать описание, если код уже содержит описание? Хорошо, я сделал это :)

jite.gs 30.10.2013 16:43

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

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

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

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

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

DanM7 01.03.2014 02:05

Давай, сколько из вас реализовали этот выбранный ответ из 2008 года, чтобы только прокрутить вниз и найти, что это лучший (современный) ответ.

TEK 18.03.2016 17:47

@TEK Я предпочитаю ответ 2008 года.

Zero3 24.11.2016 13:20

Я не понимаю. Parse выдает объяснительные исключения для того, что пошло не так с преобразованием (значение было null, пусто или не было соответствующей константы перечисления), что намного лучше, чем логическое возвращаемое значение TryParse (которое подавляет конкретную ошибку)

yair 04.01.2018 21:42

Enum.TryParse(String, T) ошибочен при синтаксическом анализе целочисленных строк. Например, этот код успешно проанализирует бессмысленную строку как бессмысленное перечисление: var result = Enum.TryParse<System.DayOfWeek>("55", out var parsedEnum);

Mass Dot Net 12.09.2019 00:38

@MassDotNet: Да, но var result = (System.DayOfWeek)Enum.Parse("55") и var result = (System.DayOfWeek)55 тоже. Вы не можете доверять перечислениям, чтобы быть действительными.

Eclipse 03.12.2019 17:50

@Eclipse: Я никогда не предлагал иначе. Это фундаментальное раздражение в том, как Microsoft реализует синтаксический анализ значений в виде перечислений.

Mass Dot Net 11.12.2019 22:20

@MassDotNet В этом случае добавьте: && Enum.IsDefined(typeof(System.DayOfWeek), parsedEnum), чтобы убедиться, что проанализированное Enum действительно существует.

Walter 02.06.2020 04:26

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

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

Я обновил это, чтобы принять значение как объект и преобразовать его в строку внутри этого метода. Таким образом, я могу взять значение типа int .ToEnum вместо только строк.

RealSollyM 14.02.2014 16:05

@SollyM Я бы сказал, что это ужасная идея, потому что тогда этот метод расширения будет применяться к типам объектов все. На мой взгляд, два метода расширения, один для строки и один для int, были бы чище и безопаснее.

Svish 09.12.2014 14:56

@ Свиш, это правда. Единственная причина, по которой я это сделал, заключается в том, что наш код используется только для внутренних целей, и я хотел избежать написания двух расширений. И поскольку мы конвертируем в Enum только со строкой или int, в противном случае я не видел в этом проблемы.

RealSollyM 10.12.2014 17:36

@SollyM Internal или нет, но я все еще поддерживаю и использую свой код: PI был бы раздражен, если бы я получил ToEnum в каждом меню intellisense, и, как вы говорите, так как единственный раз, когда вы конвертируете в перечисление, это из строки или int, можете быть уверены, что вам понадобятся только эти два метода. И два метода не намного больше, чем один, особенно когда они такие маленькие и имеют служебный тип: P

Svish 11.12.2014 14:09

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

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

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

Мне не нравятся значения по умолчанию. Это может привести к непредсказуемым результатам.

Daniël Tulp 26.05.2016 10:46

когда это когда-нибудь вызовет исключение?

andleer 28.05.2016 18:28

@andleer, если значение перечисления не соответствует тому же типу перечисления, что и значение по умолчанию

Nelly 07.01.2020 17:43

@Nelly Старый код здесь, но defaultValue и возвращаемый тип метода имеют тип T. Если типы различны, вы получите ошибку времени компиляции: «невозможно преобразовать из 'ConsoleApp1.Size' в 'ConsoleApp1.Color'» или каковы бы ни были ваши типы.

andleer 07.01.2020 18:33

@andleer, мне очень жаль, что мой последний ответ был неправильным. Возможно, этот метод вызывает исключение Syste.ArgumentException в случае, если кто-то вызывает эту функцию со значением по умолчанию, которое не относится к типу enum. С C# 7.0 я не мог создать предложение where в T: Enum. Вот почему я уловил эту возможность попыткой улова.

Nelly 09.01.2020 11:12

Я использовал класс (строго типизированная версия 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

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.

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

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

На самом деле очень полезно знать, что Enum.(Try)Parse accepts multiple, comma-separated arguments, and combines them with binary 'or'. Означает, что вы можете установить значения перечисления как степени двойки, и у вас есть очень простой способ синтаксического анализа нескольких логических флагов, например. «UseSSL, NoRetries, Sync». На самом деле, вероятно, для этого он был разработан.

pcdev 22.08.2018 11:00

@pcdev Не уверен, что вы в курсе, но эта функция предназначена для поддержки (Атрибут Flags для перечислений) (docs.microsoft.com/en-us/dotnet/csharp/language-refer ence /…).

Trisped 15.08.2020 00:02

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

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

Вы также должны были предоставить результаты тестирования производительности, например, время, затраченное на выполнение вышеуказанного кода при преобразовании строки в перечисление с использованием вашего метода и с использованием обычного Enum.Parse, если кто-то хочет проверить строку для перечисления или перечисление в строку в C#, проверьте qawithexperts.com/article/c-sharp/…

Vikram 08.12.2020 18:30

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

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

var value = "Active";

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

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

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

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

 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.

Вы перезаписываете newModel в каждой строке, поэтому, если он содержит тире, он не будет заменен. Кроме того, вам не нужно проверять, содержит ли строка что-либо, вы все равно можете просто вызвать Replace: var newModel = model.Replace("-", "").Replace(" ", "");

Lars Kristensen 01.10.2018 12:52

@LarsKristensen Да, мы можем создать метод, удаляющий не буквенно-цифровые символы.

AmirReza-Farahlagha 01.10.2018 12:56

        <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

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

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

Не уверен, когда это было добавлено, но в классе 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;
    }

Это было добавлено в .NET Core 2.0 (я немного писал об этом в другой ответ)

Mariusz Pawelski 24.11.2020 00:56

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

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

Если вы хотите использовать значение по умолчанию, когда оно пустое или пустое (например, при извлечении из файла конфигурации и значение не существует), и генерировать исключение, когда строка или число не соответствуют ни одному из значений перечисления. Остерегайтесь оговорок в ответе Тимо (https://stackoverflow.com/a/34267134/2454604).

    public static T ParseEnum<T>(this string s, T defaultValue, bool ignoreCase = false) 
        where T : struct, IComparable, IConvertible, IFormattable//If C# >=7.3: struct, System.Enum 
    {
        if ((s?.Length ?? 0) == 0)
        {
            return defaultValue;
        }

        var valid = Enum.TryParse<T>(s, ignoreCase, out T res);

        if (!valid || !Enum.IsDefined(typeof(T), res))
        {
            throw new InvalidOperationException(
                $"'{s}' is not a valid value of enum '{typeof(T).FullName}'!");
        }
        return res;
    }

Другие вопросы по теме