Enum.Parse() или переключатель

Для преобразования строки в перечисление, какой из следующих способов лучше?

  • Этот код:

    colorEnum color = (colorEnum)Enum.Parse(typeof(colorEnum), "Green");
    
  • или это:

    string colorString = ...
    colorEnum color;        
    switch (colorString)
    {
        case "Green":
            color = colorEnum.Green;
            break;
        case "Red":
            color = colorEnum.Red;
            break;
        case "Orange":
            color = colorEnum.Orange;
            break;
        ....
    }
    

Ответ 1

Вы должны использовать Enum.TryParse, если он не работает, вы можете правильно обрабатывать ошибку.

Пример:

     ColorsEnum colorValue; 
     if (Enum.TryParse(colorString, out colorValue))        
        if (Enum.IsDefined(typeof(Colors), colorValue) | colorValue.ToString().Contains(","))  
           Console.WriteLine("Converted '{0}' to {1}.", colorString, colorValue.ToString());
        else
           Console.WriteLine("{0} is not an underlying value of the Colors enumeration.", colorString);
     else
        Console.WriteLine("{0} is not a member of the Colors enumeration.", colorString);

Ответ 2

(Предупреждение: включает плагин для моей собственной библиотеки с открытым исходным кодом...)

Лично я бы использовал Unconstrained Melody, который заканчивается более чистым и более типичным кодом:

ColorEnum color = Enums.ParseName<ColorEnum>(text);

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

Enum.TryParse из .NET 4 лучше, чем другие встроенные параметры, но:

  • В момент компиляции вы не будете улавливать типы без перечисления, например. Enum.TryParse<int>(...) будет компилироваться; Unconstrained Melody действительно позволяет только перечислять типы
  • Enum.TryParse также будет анализировать "1" (или любое другое числовое значение при преобразовании в строку). Если вы действительно ожидаете только имена, я думаю, что лучше всего принимать имена

Я определенно не включал бы строковые значения - это означает, что если вы переименуете значения перечисления, вам также нужно будет переименовать значение case.

Ответ 3

А как насчет Enum.TryParse<TEnum>?

string myColorStr = "red";
colorEnum myColor;
if(!Enum.TryParse<colorEnum>(myColorStr, true, out myColor))
{
    throw new InvalidOperationException("Unknown color " + myColorStr);
}

Ответ 4

1) Это намного лучше. Это чистый код. Вы делаете в одной строке то, что займет несколько в 2). Кроме того, он менее подвержен ошибкам. Когда вы добавляете еще один элемент в colorEnum, вам нужно будет помнить, что нужно продлить 2), поскольку 1) будет просто работать.

Вам также может потребоваться некоторая обработка ошибок на Enum.Parse.

Ответ 5

Номер 1 просто по удобочитаемости и ремонтопригодности. Если вы удлиняете перечисление, вам не нужно делать лишнюю работу, а с помощью 2 вы должны добавить больше случаев в оператор switch

Ответ 6

Поскольку вы добавили тег 'performance', я собираюсь пойти с коммутатором.
Да, вам придется изменить случаи, когда вы переименовываете/добавляете/удаляете что-либо в перечислении. Ну, это тоже плохо. Любой вариант Enum.Parse/TryParse использует много странного кода и некоторого отражения, просто загляните внутрь функции с помощью ILSpy или такого. Тогда есть также проблема принятия "-12354" и даже списка допустимых имен, разделенных запятыми (в результате чего все они ORed вместе), даже если перечисление не имеет атрибута [Flags].

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

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

Ответ 7

Кроме того, что два разных фрагмента кода не делают то же самое, я бы использовал следующее:

colorEnum color;
if (!colorEnum.TryParse(colorString, true, out color)
    color = colorEnum.Green;    // Or whatever default value you wish to have.

Если у вас нет .NET 4.0, я бы сделал что-то вроде этого:

public static TEnum ToEnum<TEnum>(this string strEnumValue, TEnum defaultValue)
{
    if (!Enum.IsDefined(typeof(TEnum), strEnumValue))
        return defaultValue;

    return (TEnum)Enum.Parse(typeof(TEnum), strEnumValue);
}

Это Метод расширения до string.

Ответ 8

Лично, хотя я полностью согласен с решением Enum.Parse для сценариев без производительности (читайте: один раз запустите эту функцию изредка... и есть много таких сценариев, чтобы быть уверенным), я не могу видеть мысль о возможном задействовании метода типа отражения, когда эта функция должна выполняться в цикле сотен/тысяч плюс значения перечисления сразу. Gack!

Итак, следующее решение является одним из лучших в обоих мирах.

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

    private static Dictionary<string, Color> colorDictionary;
    public static Dictionary<string, Color> ColorDictionary
    {
        get
        {
            if (colorDictionary== null) {
                colorDictionary = new Dictionary<string, Color>();
                var all = Enum.GetValues(typeof(Color)).OfType<Color>();
                foreach (var val in all)
                    dict.Add(val.ToString(), val);
            }
            return colorDictionary;
        }
    }

Ответ 9

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

Мне нравится использовать TryParse, принадлежащий вашему перечислению. Поэтому вы можете использовать его так:

string colorString = .....
colorEnum color;

colorEnum.TryParse(colorString, out color);

Или, если вас не волнует случай строки

colorEnum.TryParse(colorString, true, out color);

Возвращаемое значение TryParse является истинным, если строка была допустимым перечислением, false, если нет.

Ответ 10

В представлении производительности, поскольку перечисления реализованы как статические поля, метод parse, вероятно, завершит выполнение рефлексии после этого типа перечисления и попробует метод GetField, который может быть быстрее, чем случай. С другой стороны, если 90% случаев, цвет зеленый, дело будет очень быстрым... Обратите внимание, что CLR иногда меняет код внутри, меняя порядок дела на основе статистики (на самом деле, я не уверен, что это так, но документ утверждает, что он мог).

Ответ 11

Я использую следующее: он получает всю безопасность типа, но не падает, когда вы добавляете новые значения в Enum, это также очень быстро.

public static colorEnum? GetColorFromString(string colorString)
{
    colorEnum? retVal = null;
    if(Enum.IsDefined(typeof(colorEnum), colorString))
        retVal = (colorEnum)Enum.Parse(typeof(colorEnum), colorString);
    return retVal;
}

Мой тест с 8 элементами в перечислении показывает этот способ быстрее, чем метод переключения.

Или вы можете использовать (очень медленный путь):

public static colorEnum? GetColorFromString(string colorString)
{
    foreach (colorEnum col in Enum.GetValues(typeof(colorEnum)))
    {
        if (col.ToString().Equals(colorString))
        {
            return col;
        }
    }
    return null;
}