Как TryParse для значения перечисления?



Я хочу написать функцию, которая может проверить заданному значению (передается как строка) против возможных значений 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;
}
547   14  

14 ответов:

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

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

обновление

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

MSDN Docs

перечисление.Т. е. будет что-то сделать. Это может быть не так эффективно, как 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);
}

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

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

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

В конце концов вы должны реализовать это 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-5

на основе .NET 4.5

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

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-parse

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

в настоящее время нет из коробки перечисление.Метод tryparse. Это было запрошено при подключении (по-прежнему нет перечисления.TryParse) и получил ответ, указывающий на возможное включение в следующий фреймворк после .NET 3.5. Вам придется реализовать предложенные обходные пути на данный момент.

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

допустимо ли кэширование динамически генерируемой функции / словаря?

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

вы можете даже кэшировать результат перечисления.GetNames ()

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

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)) {

}

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

  • www.objectreference.net/post/Enum-TryParse-Extension-Method.aspx
  • flatlinerdoa.spaces.live.com/blog/cns! 17124D03A9A052B0! 605. запись
  • mironabramson.com/blog/post/2008/03/Another-version-for-the-missing-method-EnumTryParse.aspx
  • lazyloading.blogspot.com/2008/04/enumtryparse-with-net-35-extension.html

нет TryParse, потому что тип перечисления не известен до времени выполнения. TryParse, который следует той же методологии, что и сказать дату.Метод 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);
    }
}

взгляните на класс Enum (struct ? ) сам. Существует метод разбора на это, но я не уверен в tryparse.

этот метод преобразует тип перечисления:

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

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

Comments

    Ничего не найдено.