2009-07-04 2 views
83

Я хочу написать функцию, которая может проверять заданное значение (переданное как строка) на возможные значения enum. В случае совпадения он должен возвращать экземпляр enum; в противном случае он должен вернуть значение по умолчанию.Как попробоватьParse для значения 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; 
} 
+7

Я не понимаю этот вопрос; вы говорите: «Я хочу решить эту проблему, но я не хочу использовать какие-либо методы, которые дадут мне решение». В чем смысл? – Domenic

+1

Каково ваше отвращение к решению проблемы? Если вы пытаетесь избежать исключения, потому что они «дорогостоящие», пожалуйста, дайте себе перерыв. В 99% случаев исключение затрат на выброс/улов является незначительным по сравнению с вашим основным кодом. – SolutionYogi

+1

Стоимость обработки исключений не так уж плоха. Черт, внутренние реализации всего этого преобразования перечислений полны обработки исключений. Мне действительно не нравятся исключения, которые бросают и поймают во время обычной логики приложения. Иногда бывает полезно разбить все исключения, которые бросаются (даже когда их поймают). Бросание исключений по всему месту сделает намного более раздражающим использование :) – Thorarin

ответ

27

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

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

Update

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

MSDN Docs

+0

Возможно, наименее сексуальный, но я согласен, что это, безусловно, самое лучшее, пока ваш код не будет перенесен в .NET 4. – Lisa

+1

Как уже упоминалось ниже, но на самом деле не видно: Начиная с .Net 4 Enum.TryParse доступен и работает без дополнительного кодирования , Дополнительную информацию можно получить в MSDN: http://msdn.microsoft.com/library/vstudio/dd991317%28v=vs.100%29.aspx – Christian

-1

Посмотрите на класс Enum (STRUCT?) Сам по себе. На этом есть метод Parse, но я не уверен в tryparse.

+0

Я знаю об Enum.Parse (typeof (TEnum), strEnumValue). Он выдает исключение ArgumentException, если strEnumValue недопустим. Ищет TryParse ........ –

1

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

+1

Это не только * единственный способ. Enum.IsDefined (..) предотвратит исключение исключений из кода пользователя. – Thorarin

2

Там в настоящее время нет из коробки Enum. TryParse. Это было запрошено в Connect (Still no Enum.TryParse) и получил ответ, указывающий на возможное включение в следующую структуру после .NET 3.5. На данный момент вам придется реализовать предлагаемые обходные пути.

1

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

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

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

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

+0

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

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

+0

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

+0

Реализация в блоге Damieng делает _not_ поддержку перечислений с атрибутом 'Flags'. –

97

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

+1

Лучший ответ, который я видел до сих пор ... нет try/catch, нет GetNames :) –

+13

Недостатки Enum.IsDefined: http://blogs.msdn.com/brada/archive/2003/11/29/50903. aspx –

+1

GetNames() имеет те же недостатки ... – Thorarin

0

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

  • www.objectreference.net/post/Enum-TryParse-Extension-Method.aspx
  • flatlinerdoa.spaces.live.com/blog/cns!17124D03A9A052B0!605.entry
  • mironabramson.com/ блог/запись/2008/03/Другая-версия-для-недостающего-метод-EnumTryParse.aspx
  • lazyloading.blogspot.com/2008/04/enumtryparse-with-net-35-extension.html
4

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

-2

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

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

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

+3

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

0

Существует не TryParse, потому что тип 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); 
    } 
} 
+0

Для методов 'Try', результаты которых могут быть типами значений или где' null' может быть законным результатом (например, 'Dictionary.TryGetValue, который имеет обе эти черты), нормальный шаблон для метода' Try' возвращает 'bool 'и передать результат как параметр' out'. Для тех, которые возвращают типы классов, где «null» не является допустимым результатом, нетрудно использовать возврат «null» для указания отказа. – supercat

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; 
    } 
+0

вы обеспечили лучшую реализацию, и я использовал ее для своих целей; однако мне интересно, почему вы используете 'Activator.CreateInstance (type)' для создания значения enum по умолчанию, а не 'Enum.ToObject (type, 0)'. Просто вопрос вкуса? –

+0

@Pierre - Хммм ... нет, в то время это казалось более естественным :-) Возможно, Enum.ToObject быстрее, поскольку он внутренне использует внутренний вызов InternalBoxEnum? Я никогда не проверял это ... –

+0

Как упоминалось ниже, но не очень заметно: Начиная с .Net 4 Enum.TryParse доступен и работает без дополнительного кодирования. Дополнительная информация доступна в MSDN: http://msdn.microsoft.com/library/vstudio/dd991317%28v=vs.100%29.aspx – Christian

6

Основано на .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

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

}

Смежные вопросы