Enum.Parse () или Switch - PullRequest
       54

Enum.Parse () или Switch

11 голосов
/ 21 сентября 2011

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

  1. Этот код:

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

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

Ответы [ 11 ]

8 голосов
/ 21 сентября 2011

Вы должны использовать 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);
7 голосов
/ 21 сентября 2011

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

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

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

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

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

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

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

4 голосов
/ 21 сентября 2011

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

string myColorStr = "red";
colorEnum myColor;
if(!Enum.TryParse<colorEnum>(myColorStr, true, out myColor))
{
    throw new InvalidOperationException("Unknown color " + myColorStr);
}
2 голосов
/ 21 сентября 2011

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

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

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

2 голосов
/ 21 сентября 2011

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

2 голосов
/ 21 сентября 2011

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

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

.
1 голос
/ 03 декабря 2012

Лично, хотя я полностью согласен с решением Enum.Parse для неэффективных сценариев (читай: время от времени запускается эта функция ... и таких сценариев много, чтобы быть уверенным), я не могу согласиться мысль о возможном использовании некоторого метода типа отражения, когда эта функция должна выполняться в цикле с сотнями / тысячами плюс значения enum одновременно. 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;
        }
    }
1 голос
/ 21 сентября 2011

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

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.

0 голосов
/ 22 сентября 2011

Я использую следующее, оно дает вам полную безопасность типов, но при добавлении новых значений в 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 элементами вenum показывает, что этот способ быстрее, чем метод switch.

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

public static colorEnum? GetColorFromString(string colorString)
{
    foreach (colorEnum col in Enum.GetValues(typeof(colorEnum)))
    {
        if (col.ToString().Equals(colorString))
        {
            return col;
        }
    }
    return null;
}
0 голосов
/ 21 сентября 2011

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

...