Вопрос по c# – Как попробоватьParse для значения Enum?

88

Я хочу написать функцию, которая может проверять заданное значение (передается в виде строки) по возможным значениямenum, В случае совпадения он должен вернуть экземпляр enum; в противном случае он должен вернуть значение по умолчанию.

Функция не может использоваться внутриtry/catch, что исключает использованиеEnum.Parse, который выдает исключение, когда ему дан неверный аргумент.

Я хотел бы использовать что-то вродеTryParse Функция для реализации этого:

public static TEnum ToEnum<TEnum>(this string strEnumValue, TEnum defaultValue)
{
   object enumValue;
   if (!TryParse (typeof (TEnum), strEnumValue, out enumValue))
   {
       return defaultValue;
   }
   return (TEnum) enumValue;
}
Я не понимаю этого вопроса; Вы говорите: «Я хочу решить эту проблему, но я не хочу использовать ни один из методов, которые дадут мне решение». В чем смысл Domenic
@ Amby, стоимость простого входа в блок try / catch ничтожна. Стоимость БРОСЕНИЯ не исключение, но тогда это должно быть исключительным, нет? Кроме того, не говорите «мы никогда не узнаем» ... профилируйте код и узнайте. Не тратьте свое время на размышления, если что-то идет медленно, НАЙДИТЕ! akmad
@ Domenic: я просто ищу лучшее решение, чем то, что я уже знаю. Пойдете ли вы когда-нибудь на железнодорожный запрос, чтобы узнать маршрут или поезд, который вы уже знаете :). Manish Basantani
Стоимость обработки исключений не так уж и плоха. Черт, внутренние реализации всего этого преобразования перечисления полны обработки исключений. Мне действительно не нравится, когда исключения генерируются и отлавливаются во время обычной логики приложения. Иногда бывает полезно разбить все исключения, которые выдают (даже когда они пойманы). Создание исключений повсюду сделает это намного более раздражающим для использования:) Thorarin
Каково твое отвращение, чтобы попробовать / поймать решение? Если вы пытаетесь избежать Exception, потому что они «дорогостоящие», пожалуйста, дайте себе перерыв. В 99% случаев исключение стоимости выбрасывания / улова незначительно по сравнению с вашим основным кодом. SolutionYogi

Ваш Ответ

14   ответов
31

TryParse. Саймон Мурье обеспечивает полную реализацию, которая заботится обо всем.

Если вы используете перечисления битовых полей (т. Е. Флаги), вы также должны обрабатывать строку наподобие"MyEnum.Val1|MyEnum.Val2", который является комбинацией двух значений enum. Если вы просто позвонитеEnum.IsDefined с этой строкой, он вернет false, хотяEnum.Parse справляется с этим правильно.

Обновит

Как упоминали Лиза и Кристиан в комментариях,Enum.TryParse теперь доступен для C # в .NET4 и выше.

MSDN Docs

Возможно, наименее привлекательный, но я согласен, что это определенно лучше, пока ваш код не будет перенесен в .NET 4. Lisa
Как упомянуто ниже, но на самом деле не видно: с .Net 4 Enum.TryParse доступен и работает без дополнительного кодирования. Дополнительная информация доступна на MSDN: Msdn.microsoft.com / библиотека / VStudio / dd991317% 28В =% против 100 29.aspx Christian
98

он не так эффективен, как TryParse, но будет работать без обработки исключений.

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

Стоит отметить:TryParse метод был добавлен в .NET 4.0.

Лучший ответ, который я видел до сих пор ... не пытайся и не лови, нет GetNames:) Thomas Levesque
Обратные ссылки в Enum.IsDefined: Blogs.msdn.com / Brada / архив / 2003/11/29 / 50903.aspx Nader Shirazie
GetNames () имеет те же недостатки ... Thorarin
Также на IsDefined @ нет случая игнорирован Anthony Johnston
@ Энтони: если вы хотите поддерживать нечувствительность к регистру, вам понадобитсяGetNames. Внутренне все эти методы (включаяParse) использоватьGetHashEntry, который делает реальное отражение - один раз. С другой стороны, в .NET 4.0 есть TryParse, и он тоже универсальный:) Thorarin
19

Вот пользовательская реализацияEnumTryParse. В отличие от других распространенных реализаций, он также поддерживает перечисление, помеченное знакомFlags атрибут.

    /// <summary>
    /// Converts the string representation of an enum to its Enum equivalent value. A return value indicates whether the operation succeeded.
    /// This method does not rely on Enum.Parse and therefore will never raise any first or second chance exception.
    /// </summary>
    /// <param name="type">The enum target type. May not be null.</param>
    /// <param name="input">The input text. May be null.</param>
    /// <param name="value">When this method returns, contains Enum equivalent value to the enum contained in input, if the conversion succeeded.</param>
    /// <returns>
    /// true if s was converted successfully; otherwise, false.
    /// </returns>
    public static bool EnumTryParse(Type type, string input, out object value)
    {
        if (type == null)
            throw new ArgumentNullException("type");

        if (!type.IsEnum)
            throw new ArgumentException(null, "type");

        if (input == null)
        {
            value = Activator.CreateInstance(type);
            return false;
        }

        input = input.Trim();
        if (input.Length == 0)
        {
            value = Activator.CreateInstance(type);
            return false;
        }

        string[] names = Enum.GetNames(type);
        if (names.Length == 0)
        {
            value = Activator.CreateInstance(type);
            return false;
        }

        Type underlyingType = Enum.GetUnderlyingType(type);
        Array values = Enum.GetValues(type);
        // some enums like System.CodeDom.MemberAttributes *are* flags but are not declared with Flags...
        if ((!type.IsDefined(typeof(FlagsAttribute), true)) && (input.IndexOfAny(_enumSeperators) < 0))
            return EnumToObject(type, underlyingType, names, values, input, out value);

        // multi value enum
        string[] tokens = input.Split(_enumSeperators, StringSplitOptions.RemoveEmptyEntries);
        if (tokens.Length == 0)
        {
            value = Activator.CreateInstance(type);
            return false;
        }

        ulong ul = 0;
        foreach (string tok in tokens)
        {
            string token = tok.Trim(); // NOTE: we don't consider empty tokens as errors
            if (token.Length == 0)
                continue;

            object tokenValue;
            if (!EnumToObject(type, underlyingType, names, values, token, out tokenValue))
            {
                value = Activator.CreateInstance(type);
                return false;
            }

            ulong tokenUl;
            switch (Convert.GetTypeCode(tokenValue))
            {
                case TypeCode.Int16:
                case TypeCode.Int32:
                case TypeCode.Int64:
                case TypeCode.SByte:
                    tokenUl = (ulong)Convert.ToInt64(tokenValue, CultureInfo.InvariantCulture);
                    break;

                //case TypeCode.Byte:
                //case TypeCode.UInt16:
                //case TypeCode.UInt32:
                //case TypeCode.UInt64:
                default:
                    tokenUl = Convert.ToUInt64(tokenValue, CultureInfo.InvariantCulture);
                    break;
            }

            ul |= tokenUl;
        }
        value = Enum.ToObject(type, ul);
        return true;
    }

    private static char[] _enumSeperators = new char[] { ',', ';', '+', '|', ' ' };

    private static object EnumToObject(Type underlyingType, string input)
    {
        if (underlyingType == typeof(int))
        {
            int s;
            if (int.TryParse(input, out s))
                return s;
        }

        if (underlyingType == typeof(uint))
        {
            uint s;
            if (uint.TryParse(input, out s))
                return s;
        }

        if (underlyingType == typeof(ulong))
        {
            ulong s;
            if (ulong.TryParse(input, out s))
                return s;
        }

        if (underlyingType == typeof(long))
        {
            long s;
            if (long.TryParse(input, out s))
                return s;
        }

        if (underlyingType == typeof(short))
        {
            short s;
            if (short.TryParse(input, out s))
                return s;
        }

        if (underlyingType == typeof(ushort))
        {
            ushort s;
            if (ushort.TryParse(input, out s))
                return s;
        }

        if (underlyingType == typeof(byte))
        {
            byte s;
            if (byte.TryParse(input, out s))
                return s;
        }

        if (underlyingType == typeof(sbyte))
        {
            sbyte s;
            if (sbyte.TryParse(input, out s))
                return s;
        }

        return null;
    }

    private static bool EnumToObject(Type type, Type underlyingType, string[] names, Array values, string input, out object value)
    {
        for (int i = 0; i < names.Length; i++)
        {
            if (string.Compare(names[i], input, StringComparison.OrdinalIgnoreCase) == 0)
            {
                value = values.GetValue(i);
                return true;
            }
        }

        if ((char.IsDigit(input[0]) || (input[0] == '-')) || (input[0] == '+'))
        {
            object obj = EnumToObject(underlyingType, input);
            if (obj == null)
            {
                value = Activator.CreateInstance(type);
                return false;
            }
            value = obj;
            return true;
        }

        value = Activator.CreateInstance(type);
        return false;
    }
Вы предоставили лучшую реализацию, и я использовал ее в своих целях; Однако мне интересно, почему вы используетеActivator.CreateInstance(type) для создания значения перечисления по умолчанию, а неEnum.ToObject(type, 0). Просто дело вкуса? Pierre Arnaud
@ Pierre - Хммм ... нет, в то время это казалось более естественным :-) Может быть, Enum.ToObject работает быстрее, поскольку он использует внутренний вызов InternalBoxEnum? Я никогда не проверял это ... Simon Mourier
Как упомянуто ниже, но на самом деле не видно: с .Net 4 Enum.TryParse доступен и работает без дополнительного кодирования. Дополнительная информация доступна на MSDN: Msdn.microsoft.com / библиотека / VStudio / dd991317% 28В =% против 100 29.aspx Christian
16

вы должны реализовать это вокругEnum.GetNames:

public bool TryParseEnum<T>(string str, bool caseSensitive, out T value) where T : struct {
    // Can't make this a type constraint...
    if (!typeof(T).IsEnum) {
        throw new ArgumentException("Type parameter must be an enum");
    }
    var names = Enum.GetNames(typeof(T));
    value = (Enum.GetValues(typeof(T)) as T[])[0];  // For want of a better default
    foreach (var name in names) {
        if (String.Equals(name, str, caseSensitive ? StringComparison.Ordinal : StringComparison.OrdinalIgnoreCase)) {
            value = (T)Enum.Parse(typeof(T), name);
            return true;
        }
    }
    return false;
}

Дополнительные замечания

Enum.TryParse включен в .NET 4. Смотрите здесьhttp: //msdn.microsoft.com/library/dd991876 (VS.100) .aspx Другой подход будет заключаться в прямом переносеEnum.Parse перехватывает исключение, которое выдается при сбое. Это может быть быстрее, когда найдено совпадение, но, скорее всего, будет медленнее, если нет. В зависимости от данных, которые вы обрабатываете, это может или не может быть чистым улучшением.

EDIT: только что видел лучшую реализацию этого, которая кэширует необходимую информацию:http: //damieng.com/blog/2010/10/17/enums-better-syntax-improved-performance-and-tryparse-in-net-3-

Я собирался предложить использовать default (T), чтобы установить значение по умолчанию. Оказывается, это не будет работать для всех перечислений. Например. Если базовый тип для перечисления был int default (T) всегда будет возвращать 0, что может быть или не быть допустимым для перечисления. Daniel Ballinger
Реализация в блоге Дамиенга делаетн поддерживает перечисления сFlags атрибут. Uwe Keim
8

Пример кода ниже

using System;

enum Importance
{
    None,
    Low,
    Medium,
    Critical
}

class Program
{
    static void Main()
    {
    // The input value.
    string value = "Medium";

    // An unitialized variable.
    Importance importance;

    // Call Enum.TryParse method.
    if (Enum.TryParse(value, out importance))
    {
        // We now have an enum type.
        Console.WriteLine(importance == Importance.Medium);
    }
    }
}

Ссылка :http: //www.dotnetperls.com/enum-pars

4

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

3
enum EnumStatus
{
    NAO_INFORMADO = 0,
    ENCONTRADO = 1,
    BLOQUEADA_PELO_ENTREGADOR = 2,
    DISPOSITIVO_DESABILITADO = 3,
    ERRO_INTERNO = 4,
    AGARDANDO = 5
}

...

if (Enum.TryParse<EnumStatus>(item.status, out status)) {

}
2

в Connect До сих пор нет Enum.TryParse) и получил ответ, указывающий на возможное включение в следующую структуру после .NET 3.5. Вам придется реализовать предложенные обходные пути.

1

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

Это не тоттольк путь. Enum.IsDefined (..) предотвратит появление исключений в пользовательском коде. Thorarin
1

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

Вы даже можете кэшировать результат Enum.GetNames ()

Вы пытаетесь оптимизировать процессор или память? Выдействительн нужно

Идея заключается в оптимизации процессора. Согласитесь, что я могу сделать это за плату памяти. Но это не решение, которое я ищу. Благодарность Manish Basantani
0

Catch, вам нужно использовать IsDefined или GetNames ... Вот несколько примеров ... они в основном все одинаковые, первый обрабатывает обнуляемые перечисления. Я предпочитаю второй, так как это расширение для строк, а не для перечислений ... но вы можете смешивать их как хотите!

Www.objectreference.net / пост / Enum-TryParse-Extension-Method.aspx Flatlinerdoa.spaces.live.com / блог / СНН! 17124D03A9A052B0! 605.entry Mironabramson.com / блог / запись / 2008/03 / Другая-версия-для-недостающего-метод-EnumTryParse.aspx Lazyloading.blogspot.com / 2008/04 / enumtryparse-с-нетто-35-extension.html
0

потому что тип Enum не известен до времени выполнения. TryParse, использующий ту же методологию, что и метод Date.TryParse, выдает явную ошибку преобразования для параметра ByRef.

Я предлагаю сделать что-то вроде этого:

//1 line call to get value
MyEnums enumValue = (Sections)EnumValue(typeof(Sections), myEnumTextValue, MyEnums.SomeEnumDefault);

//Put this somewhere where you can reuse
public static object EnumValue(System.Type enumType, string value, object NotDefinedReplacement)
{
    if (Enum.IsDefined(enumType, value)) {
        return Enum.Parse(enumType, value);
    } else {
        return Enum.Parse(enumType, NotDefinedReplacement);
    }
}
ДляTry методы, результаты которых могут быть типами значений, или гдеnull может быть законным результатом (например,Dictionary.TryGetValue, which has both such traits), the normal pattern is for a Попробуй вернуть методbool и передайте результат какout параметр. Для тех, которые возвращают типы классов, гдеnull не является действительным результатом, нет проблем с использованиемnull вернуться, чтобы указать сбой. supercat
-1

но я не уверен насчет трипса.

Я знаю о методе Enum.Parse (typeof (TEnum), strEnumValue). Выдает ArgumentException, если strEnumValue недопустимо. Ищете TryParse ........ Manish Basantani
-2

Этот метод преобразует тип enum:

  public static TEnum ToEnum<TEnum>(object EnumValue, TEnum defaultValue)
    {
        if (!Enum.IsDefined(typeof(TEnum), EnumValue))
        {
            Type enumType = Enum.GetUnderlyingType(typeof(TEnum));
            if ( EnumValue.GetType() == enumType )
            {
                string name = Enum.GetName(typeof(HLink.ViewModels.ClaimHeaderViewModel.ClaimStatus), EnumValue);
                if( name != null)
                    return (TEnum)Enum.Parse(typeof(TEnum), name);
                return defaultValue;
            }
        }
        return (TEnum)Enum.Parse(typeof(TEnum), EnumValue.ToString());
    } 

Он проверяет базовый тип и получает его имя для анализа. Если все не удается, он вернет значение по умолчанию.

что это делает "Enum.GetName (typeof (HLink.ViewModels.ClaimHeaderViewModel.ClaimStatus), EnumValue)" Возможно, некоторая зависимость от вашего локального кода. Manish Basantani

Похожие вопросы