Я строю функцию для расширения Enum.Parse
концепции, которая
- Позволяет проанализировать значение по умолчанию в случае, если значение Enum не найдено
- Нечувствителен к регистру
Поэтому я написал следующее:
public static T GetEnumFromString<T>(string value, T defaultValue) where T : Enum
{
if (string.IsNullOrEmpty(value)) return defaultValue;
foreach (T item in Enum.GetValues(typeof(T)))
{
if (item.ToString().ToLower().Equals(value.Trim().ToLower())) return item;
}
return defaultValue;
}
Я получаю ошибку Ограничение не может быть специальным классом System.Enum
.
Справедливо, но есть ли обходной путь, чтобы разрешить Generic Enum, или я собираюсь имитировать Parse
функцию и передавать тип в качестве атрибута, что вынуждает предъявлять к вашему уродливому требованию боксирование.
РЕДАКТИРОВАТЬ Все предложения ниже были высоко оценены, спасибо.
Поселились (я оставил цикл, чтобы сохранить нечувствительность к регистру - я использую это при разборе XML)
public static class EnumUtils
{
public static T ParseEnum<T>(string value, T defaultValue) where T : struct, IConvertible
{
if (!typeof(T).IsEnum) throw new ArgumentException("T must be an enumerated type");
if (string.IsNullOrEmpty(value)) return defaultValue;
foreach (T item in Enum.GetValues(typeof(T)))
{
if (item.ToString().ToLower().Equals(value.Trim().ToLower())) return item;
}
return defaultValue;
}
}
РЕДАКТИРОВАТЬ: (16 февраля 2015 г.) Жюльен Лебосквейн недавно опубликовал приведенное ниже компиляторное универсальное безопасное решение с типобезопасным кодом в MSIL или F # , на которое стоит обратить внимание, и приветствие. Я уберу это редактирование, если решение будет пузыриться дальше на странице.
Ответы:
Поскольку
Enum
Type реализуетIConvertible
интерфейс, лучшая реализация должна выглядеть примерно так:Это все еще позволит передавать значения типов реализации
IConvertible
. Хотя шансы редки.источник
Эта функция наконец поддерживается в C # 7.3!
Следующий фрагмент (из примеров dotnet ) демонстрирует, как:
Убедитесь, что в вашем проекте C # установлена языковая версия версии 7.3.
Оригинальный ответ ниже:
Я опоздал на игру, но я воспринял это как вызов, чтобы посмотреть, как это можно сделать. Это не возможно в C # (или VB.NET, но прокрутите вниз для F #), но возможно в MSIL. Я написал эту маленькую .... вещь
Который генерирует функцию, которая выглядела бы так, если бы это был допустимый C #:
Затем с помощью следующего кода C #:
К сожалению, это означает, что эта часть вашего кода написана на MSIL вместо C #, и единственным дополнительным преимуществом является то, что вы можете ограничить этот метод
System.Enum
. Это также своего рода облом, потому что он собирается в отдельную сборку. Тем не менее, это не значит, что вы должны использовать его таким образом.Удаляя строку
.assembly MyThing{}
и вызывая ilasm следующим образом:вы получаете сетевой модуль вместо сборки.
К сожалению, VS2010 (и ранее, очевидно) не поддерживает добавление ссылок сетевого модуля, что означает, что вам придется оставить его в 2 отдельных сборках при отладке. Единственный способ добавить их как часть вашей сборки - это запустить csc.exe самостоятельно, используя
/addmodule:{files}
аргумент командной строки. Это не было бы слишком больно в скрипте MSBuild. Конечно, если вы храбры или глупы, вы можете каждый раз запускать csc самостоятельно. И это, безусловно, усложняется, поскольку доступ к нему требуется нескольким сборкам.Таким образом, это может быть сделано в .Net. Это стоит дополнительных усилий? Ну, я думаю, я позволю тебе определиться с этим.
Решение F # как альтернатива
Дополнительный кредит: оказывается, что общее ограничение
enum
возможно по крайней мере на одном другом языке .NET, кроме MSIL: F #.Этот вариант проще поддерживать, поскольку он является хорошо известным языком с полной поддержкой Visual Studio IDE, но для него вам все равно нужен отдельный проект. Тем не менее, он , естественно , производит значительно отличается IL (код является очень разные) и опирается на
FSharp.Core
библиотеку, которая, так же как и любой другой внешней библиотеки, должна стать частью вашего дистрибутива.Вот как вы можете использовать его (в основном то же самое, что и решение MSIL), и показать, что оно корректно завершается ошибкой в других синонимичных структурах:
источник
There's no particularly unusual reason why not; we have lots of other things to do, limited budgets, and this one has never made it past the "wouldn't this be nice?" discussion in the language design team.
T
чтобыSystem.Enum
не быть в состоянии сделать все, что с тем,T
что люди могли бы ожидать, авторы C # полагал , что они , возможно, также запретить его полностью. Я считаю это решение неудачным, поскольку в C # он просто игнорировал какую-либо специальную обработкуSystem.Enum
ограничений, можно было бы написатьHasAnyFlags<T>(this T it, T other)
метод расширения, который был бы на несколько порядков быстрееEnum.HasFlag(Enum)
и проверял бы его аргументы.C # ≥ 7.3
Начиная с C # 7.3 (доступно с Visual Studio 2017 ≥ v15.7), этот код теперь полностью действителен:
C # ≤ 7,2
Вы можете получить реальное принудительное ограничение перечисления путем использования наследования ограничений. Следующий код определяет одновременно
class
и a, иstruct
ограничения:Применение:
Примечание: это специально указано в спецификации языка C # 5.0:
источник
EnumClassUtils<System.Enum>
достаточно, чтобы ограничить T любымиSystem.Enum
и любыми производными типами.struct
наParse
то , ограничивает его дальше к реальному перечислимого типа. Вы должны ограничитьEnum
в какой-то момент. Для этого ваш класс должен быть вложенным. См. Gist.github.com/MrJul/7da12f5f2d6c69f03d79where TClass : class
ограничение?enum DefinitelyNotAnInt : byte { Realize, That, I, Am, Not, An, Int }
enum AlsoNotAnInt : long { Well, Bummer }
редактировать
На этот вопрос теперь великолепно ответил Жюльен Лебосквейн . Я также хотел бы выразить свой ответ с
ignoreCase
,defaultValue
и необязательными аргументами, в то время как добавлениеTryParse
иParseOrDefault
.Примеры использования:
старый
Мои старые улучшения в ответе Вивека с использованием комментариев и «новых» разработок:
TEnum
для ясности для пользователейTryParse
справимсяignoreCase
существующий параметр (введен в VS2010 / .Net 4)default
значение (введено в VS2005 / .Net 2)defaultValue
иignoreCase
в результате чего:
источник
Вы можете определить статический конструктор для класса, который проверит, что тип T является перечислением, и выдаст исключение, если это не так. Этот метод упоминается Джеффри Рихтером в его книге CLR через C #.
Затем в методе разбора вы можете просто использовать Enum.Parse (typeof (T), input, true) для преобразования строки в перечисление. Последний истинный параметр предназначен для игнорирования регистра ввода.
источник
Enum
T
при выполнении конструктора. Хотя это гораздо приятнее, чем ждать конструктора экземпляра.Следует также учитывать, что, поскольку выпуск C # 7.3 с использованием ограничений Enum поддерживается "из коробки", не требуя дополнительной проверки и прочего.
Итак, если вы изменили языковую версию вашего проекта на C # 7.3, следующий код будет работать отлично:
Если вы не знаете, как изменить языковую версию на C # 7.3, см. Следующий снимок экрана:
РЕДАКТИРОВАТЬ 1 - Требуемая версия Visual Studio и учитывая ReSharper
Чтобы Visual Studio мог распознать новый синтаксис, вам нужна как минимум версия 15.7. Вы также можете найти это в примечаниях к выпуску Microsoft, см. Замечания к выпуску Visual Studio 2017 15.7 . Спасибо @MohamedElshawaf за указание на этот действительный вопрос.
Просьба также заметить, что в моем случае ReSharper 2018.1 на момент написания этой EDIT еще не поддерживает C # 7.3. После активации ReSharper ограничение Enum выделяется как ошибка, сообщающая, что я не могу использовать 'System.Array', 'System.Delegate', 'System.Enum', 'System.ValueType', 'object' в качестве ограничения параметра типа . ReSharper предлагает в качестве быстрого исправления удалить ограничение 'Enum' для параметра типа T метода
Однако, если вы временно отключите ReSharper в Сервис -> Параметры -> ReSharper Ultimate -> Общие, вы увидите, что синтаксис отлично работает, если вы используете VS 15.7 или выше и C # 7.3 или выше.
источник
where T : struct, Enum
, чтобы не передаватьSystem.Enum
себя в качестве параметра типа.struct, Enum
. Мое обоснование объясняется в ответе и комментариях здесь .Я модифицировал образец от dimarzionist. Эта версия будет работать только с Enums и не пропускать структуры.
источник
Я попытался немного улучшить код:
источник
defaultValue.ToString("D", System.Globalization.NumberFormatInfo.CurrentInfo)
даже если вы не знаете, какой это тип перечисления, только то, что объект является перечислением.IsDefined
менее, предварительная проверка с помощью уничтожит нечувствительность к регистру. В отличие отParse
,IsDefined
не имеетignoreCase
аргументов, и MSDN говорит, что это только точный случай .У меня есть конкретное требование, когда я должен использовать enum с текстом, связанным со значением enum. Например, когда я использую enum для указания типа ошибки, требуется описать детали ошибки.
источник
Надеюсь, что это полезно:
источник
return (TValue)Enum.Parse(typeof (TValue), value);
наreturn (TValue)Enum.Parse(typeof (TValue), value, true);
Интересно, что это возможно в других языках (Managed C ++, IL напрямую).
Цитировать:
Кто знает
источник
Это мое мнение. В сочетании с ответами и MSDN
MSDN Source
источник
TEnum
самом деле это тип Enum, ноtext
пустая строка, вы получитеArgumentException
выражение «TEnum должно быть типом Enum», даже если это так.Существующие ответы верны на C # <= 7.2. Однако существует запрос функции языка C # (связанный с запросом функции corefx ), чтобы разрешить следующее;
На момент написания статьи функция «В обсуждении» на собраниях по развитию языка.
РЕДАКТИРОВАТЬ
Согласно информации nawfal , это вводится в C # 7.3 .
источник
Мне всегда нравилось это (вы можете изменить по мере необходимости):
источник
Мне понравилось решение Christopher Currens, использующее IL, но для тех, кто не хочет заниматься сложными задачами, связанными с включением MSIL в процесс сборки, я написал аналогичную функцию на C #.
Обратите внимание, что вы не можете использовать общее ограничение, например,
where T : Enum
потому что Enum является специальным типом. Поэтому я должен проверить, действительно ли данный универсальный тип является enum.Моя функция:
источник
Я инкапсулировал решение Vivek в служебный класс, который вы можете использовать повторно. Обратите внимание, что вы все равно должны определить ограничения типа «где T: struct, IConvertible» для вашего типа.
источник
Я создал расширение метода
to get integer value from enum
взглянуть на реализацию методаэто использование
источник
Как указано в других ответах ранее; хотя это не может быть выражено в исходном коде, на самом деле это может быть сделано на уровне IL. @Christopher Currens ответ показывает, как IL делает с этим.
С помощью надстройки Fody ExtraConstraints.Fody для этого есть очень простой способ, в комплекте с инструментами сборки. Просто добавьте свои пакеты nuget (
Fody
,ExtraConstraints.Fody
) в ваш проект и добавьте ограничения следующим образом (выдержка из Readme of ExtraConstraints):и Фоди добавит необходимый IL для присутствия ограничения. Также обратите внимание на дополнительную функцию ограничения делегатов:
Что касается Enums, вы также можете обратить внимание на очень интересный Enums.NET .
источник
Это моя реализация. В принципе, вы можете настроить любой атрибут, и он работает.
источник
Если потом можно использовать прямое приведение, я думаю, вы можете использовать
System.Enum
базовый класс в своем методе, где это необходимо. Вам просто нужно аккуратно заменить параметры типа. Таким образом, реализация метода будет выглядеть так:Тогда вы можете использовать его как:
источник
Enum.ToObject()
даст более гибкий результат. Кроме того, вы можете выполнять сравнения строк без учета регистра, чтоToLower()
Просто для полноты, следующее решение Java. Я уверен, что то же самое можно сделать и в C #. Это избавляет от необходимости указывать тип где-либо в коде - вместо этого вы указываете его в строках, которые вы пытаетесь проанализировать.
Проблема в том, что нет никакого способа узнать, какому перечислению может соответствовать строка - поэтому ответ состоит в том, чтобы решить эту проблему.
Вместо того, чтобы принимать только строковое значение, примите String, которая имеет и перечисление, и значение в форме "enumeration.value". Рабочий код ниже - требуется Java 1.8 или новее. Это также сделает XML более точным, так как вы увидите что-то вроде color = "Color.red" вместо просто color = "red".
Вы бы вызвали метод acceptEnumeratedValue () со строкой, содержащей имя точечного значения имени перечисления.
Метод возвращает формальное перечисляемое значение.
источник