Универсальная версия Enum.Parse в C #

Я регулярно задавался вопросом, почему C # до сих пор не реализовал Generic Enum.Parse

Допустим, у меня есть

enum MyEnum
{
   Value1,
   Value2
}

И из XML-файла / записи в БД я хочу создать Enum.

MyEnum val = (MyEnum)Enum.Parse(typeof(MyEnum), "value1", true);

Разве это не могло быть реализовано как что-то вроде

MyEnum cal = Enum.Parse<MyEnum>("value1");

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

Есть предположения?


person Adriaan Stander    schedule 11.02.2010    source источник


Ответы (6)


Это уже реализовано в .NET 4;) Взгляните на здесь.

MyEnum cal;
if (!Enum.TryParse<MyEnum>("value1", out cal))
   throw new Exception("value1 is not valid member of enumeration MyEnum");

Кроме того, здесь обсуждение также содержит некоторые интересные моменты.

person Tomas Vana    schedule 11.02.2010
comment
Это ссылка на неуниверсальный метод Enum.Parse. Вы хотели добавить ссылку на новый метод Enum.TryParse<T>? msdn.microsoft.com/en- us / library / - person Samuel Neff; 11.02.2010
comment
Интересно, что они ограничили его до struct, new () вместо добавления нового ограничения enum к языку. - person Yuriy Faktorovich; 11.02.2010
comment
Извините, я уже исправил, это именно то, что я имел в виду;) - person Tomas Vana; 11.02.2010
comment
Интересно то, что им даже не нужно было бы накладывать новое ограничение на язык - это был бы просто метод, который на самом деле нельзя было бы выразить на C #. Компилятор C # может подчиняться ограничениям, даже если вы не можете написать их на C # :) - person Jon Skeet; 11.02.2010
comment
Жаль, что TryParse ‹T› столь же неудобен, как и обычный Parse. - person AaronLS; 13.03.2013
comment
Внимание: с Enum.TryParse<T>, если вы конвертируете строковое представление целого числа, то есть "2", это всегда успешно, независимо от того, имеет ли какой-либо из целевых членов перечисления значение int 2. Впоследствии вам придется позвонить IsDefined на результат для полной проверки. - person Saeb Amini; 27.12.2017
comment
Я предлагаю изменить ответ, чтобы использовать более свежий синтаксис .NET: if (!Enum.TryParse("value1", out MyEnum cal)) throw new Exception("..."); - person Josh Gallagher; 21.08.2019

И в желаемом синтаксисе вопроса:

MyEnum cal = Toolkit.Parse<MyEnum>("value1");

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

/// <summary>
/// Converts the string representation of the name or numeric value of one or
//  more enumerated constants to an equivalent enumerated object.
/// </summary>
/// <typeparam name="TEnum">An enumeration type.</typeparam>
/// <param name="value">A string containing the name or value to convert.</param>
/// <returns>An object of type TEnum whose value is represented by value</returns>
/// <exception cref="System.ArgumentNullException">enumType or value is null.</exception>
/// <exception cref=" System.ArgumentException"> enumType is not an System.Enum. -or- 
/// value is either an empty string or only contains white space.-or- 
/// value is a name, but not one of the named constants defined for the enumeration.</exception>
/// <exception cref="System.OverflowException">value is outside the range of the underlying type of enumType.</exception>
public static TEnum Parse<TEnum>(String value) where TEnum : struct
{
   return (TEnum)Enum.Parse(typeof(TEnum), value);
}
person Ian Boyd    schedule 13.12.2012

Хотя ограничение System.Enum не разрешено в C #, оно разрешено в .NET, и C # может использовать типы или методы с такими ограничениями. См. Библиотеку Джона Скита Unconstrained Melody, которая включает код, который делает именно то, что вы хотите.

person kvb    schedule 11.02.2010
comment
Шутка, я даже не могу подключить свою собственную библиотеку, пока кто-нибудь не доберется до нее первым;) - person Jon Skeet; 11.02.2010

Слегка измененная версия ответа @ ian-boyd с использованием метода расширения, чтобы избежать необходимости указывать имя статического класса в вызове:

MyEnum cal = "value1".Parse<MyEnum>();

/// <summary>
/// Converts the string representation of the name or numeric value of one or
//  more enumerated constants to an equivalent enumerated object.
/// </summary>
/// <typeparam name="TEnum">An enumeration type.</typeparam>
/// <returns>An object of type TEnum whose value is represented by value</returns>
/// <exception cref="System.ArgumentNullException">enumType or value is null.</exception>
/// <exception cref=" System.ArgumentException"> enumType is not an System.Enum. -or- 
/// value is either an empty string or only contains white space.-or- 
/// value is a name, but not one of the named constants defined for the enumeration.</exception>
/// <exception cref="System.OverflowException">value is outside the range of the underlying type of enumType.</exception>
public static TEnum Parse<TEnum>(this String value) where TEnum : struct
{
   return (TEnum)Enum.Parse(typeof(TEnum), value);
}
person Jimmy    schedule 12.12.2015

Общая версия Parse ‹ TEnum ›(String) был представлен в .NET Core 2.0. Так что вы можете просто написать:

    class Program
    {
        static void Main(string[] args)
        {            
            var e = Enum.Parse<MyEnum>("Value1");
            Console.WriteLine($"Enum values is: {e}");
        }
    }

    enum MyEnum
    {
        Value1,
        Value2
    }

Просто имейте в виду, что этого нет в старой .Net Framework (.NET 4.8 и ниже) или в каком-либо стандарте .NET. Вам необходимо настроить таргетинг на .NET Core ›= 2 (или .NET› = 5, поскольку Microsoft упал Core naming).

Имеется общая версия TryParserer ‹TEnum› (String, TEnum), начиная с .NET Framework 4.0. Таким образом, вы можете использовать это так:

if (Enum.TryParse<MyEnum>("Value2", out var e2))
{
    Console.WriteLine($"Enum values is: {e2}");
}

или создайте свой собственный вспомогательный метод, например:

public static class EnumUtils
{
    public static TEnum Parse<TEnum>(String value) where TEnum : struct
    {
        return (TEnum)Enum.Parse(typeof(TEnum), value);
    }
}

...
var e3 = EnumUtils.Parse<MyEnum>("Value1");

и, конечно, вы можете просто использовать неуниверсальную версию, пока не перенесете свой проект на более новую версию .NET;)

var e4 = (MyEnum)Enum.Parse(typeof(MyEnum), "Value1");
person Mariusz Pawelski    schedule 23.11.2020

Немного подправив некоторые методы, пытаясь построить что-то похожее на первоначальное предложение:

MyEnum cal = Enum.Parse<MyEnum>("value1");

Мне казалось, что такой синтаксис невозможен в C #, поскольку тип Enum рассматривается как не допускающий значения NULL.

Если мы вызываем метод «Enum.TryParse», передавая значение, не соответствующее элементу перечисления, значение Enum по умолчанию будет возвращено в переменной «out». Вот почему нам нужно сначала протестировать результат "Enum.TryParse", поскольку простой вызов

MyEnum cal;
Enum.TryParse<MyEnum>("value1", out cal);

и проверка значения "cal" не всегда дает надежный результат.

person Jose Tepedino    schedule 08.09.2013
comment
общий Parse может быть просто оболочкой вокруг TryParse и выдавать ArgumentException, если TryParse возвращает false. - person Eren Ersönmez; 08.09.2013
comment
Он может просто вызвать исключение вместо значения по умолчанию. То же, что и неуниверсальная версия Parse. - person Maxim; 22.07.2017
comment
Идея состоит в том, чтобы избежать (когда это возможно) обработки исключений, которая считается анти-шаблоном для управления потоком (см. https://web.archive.org/web/20140430044213/http://c2.com/cgi-bin/wiki?DontUseExceptionsForFlowControl). Согласно спецификации API, метод Enum.TryParse устраняет необходимость в обработке исключений при синтаксическом анализе строкового представления значения перечисления. (см. msdn.microsoft.com/en -us // библиотека / dd991317 (v = vs.110) .aspx) - person Jose Tepedino; 22.07.2017