2011-02-03 2 views
4

Я преобразовываю числовое значение, которое имеет тип string в соответствующее Enum. Пока я тестировал свой код, я обнаружил интересное поведение, которое меня смутило.Путаница с разбором enum

Используя приведенный ниже пример кода, может ли кто-нибудь пролить свет на то, почему исключение не возникает, если/когда переменная s имеет значение, которое не соответствует одному из значений Enum? Кроме того, как можно установить значение sEnum var в значение, которое не существует в определении перечисления Stooge?

class Program 
{ 
    enum Stooge 
    { 
     Unspecified, 
     Moe, 
     Larry, 
     Curly, 
     Shemp 
    } 

    static void Main(string[] args) 
    { 
     while (true) 
     { 
      Console.WriteLine("Enter a number..."); 

      string s = Console.ReadLine(); 
      Stooge sEnum = (Stooge)(int.Parse(s)); //Why doesn't this line throw if s != 0, 1, 2, 3, or 4? 

      Console.WriteLine("\r\nYou entered: {0}\r\nEnum String Value: {1}\r\nEnum Int Value: {2}\r\n", s, sEnum.ToString(), (int)sEnum); 
     } 
    } 
} 
+0

Возможный дубликат [Почему листинг int для недопустимого значения перечисления НЕ выбрасывает исключение?] (Http://stackoverflow.com/questions/6413804/why-does-casting-int-to-invalid-enum-value-not -throw-exception) – nawfal

ответ

7

Это было решение со стороны людей, которые создали .NET. Перечисление поддерживается другим типом значения (int, short, byte и т. Д.), И поэтому он может фактически иметь любое значение, которое действительно для этих типов значений.

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

/// <summary> 
/// Utility methods for enum values. This static type will fail to initialize 
/// (throwing a <see cref="TypeInitializationException"/>) if 
/// you try to provide a value that is not an enum. 
/// </summary> 
/// <typeparam name="T">An enum type. </typeparam> 
public static class EnumUtil<T> 
    where T : struct, IConvertible // Try to get as much of a static check as we can. 
{ 
    // The .NET framework doesn't provide a compile-checked 
    // way to ensure that a type is an enum, so we have to check when the type 
    // is statically invoked. 
    static EnumUtil() 
    { 
     // Throw Exception on static initialization if the given type isn't an enum. 
     Require.That(typeof (T).IsEnum,() => typeof(T).FullName + " is not an enum type."); 
    } 

    /// <summary> 
    /// In the .NET Framework, objects can be cast to enum values which are not 
    /// defined for their type. This method provides a simple fail-fast check 
    /// that the enum value is defined, and creates a cast at the same time. 
    /// Cast the given value as the given enum type. 
    /// Throw an exception if the value is not defined for the given enum type. 
    /// </summary> 
    /// <typeparam name="T"></typeparam> 
    /// <param name="enumValue"></param> 
    /// <exception cref="InvalidCastException"> 
    /// If the given value is not a defined value of the enum type. 
    /// </exception> 
    /// <returns></returns> 
    public static T DefinedCast(object enumValue) 

    { 
     if (!System.Enum.IsDefined(typeof(T), enumValue)) 
      throw new InvalidCastException(enumValue + " is not a defined value for enum type " + 
              typeof (T).FullName); 
     return (T) enumValue; 
    } 

    /// <summary> 
    /// 
    /// </summary> 
    /// <param name="enumValue"></param> 
    /// <returns></returns> 
    public static T Parse(string enumValue) 
    { 
     var parsedValue = (T)System.Enum.Parse(typeof (T), enumValue); 
     //Require that the parsed value is defined 
     Require.That(parsedValue.IsDefined(), 
      () => new ArgumentException(string.Format("{0} is not a defined value for enum type {1}", 
       enumValue, typeof(T).FullName))); 
     return parsedValue; 
    } 

    public static bool IsDefined(T enumValue) 
    { 
     return System.Enum.IsDefined(typeof (T), enumValue); 
    } 

} 

public static class EnumExtensions 
{ 
    public static bool IsDefined<T>(this T enumValue) 
     where T : struct, IConvertible 
    { 
     return EnumUtil<T>.IsDefined(enumValue); 
    } 
} 

Таким образом, я могу сказать:

if(!sEnum.IsDefined()) throw new Exception(...); 

... или :

EnumUtil<Stooge>.Parse(s); // throws an exception if s is not a defined value. 
+0

'Require.That 'также поступает из моей собственной библиотеки, между прочим. Вы можете заменить его на 'if (! ...) throw new Exception (...);' – StriplingWarrior

+0

Мне нравится Require.That бы вы поделились тем, как вы это сделали? –

+1

@ kirsteng: Это очень просто: https://gist.github.com/j2jensen/11377210 – StriplingWarrior

2

Перечисление - это просто технически int (или то, что вы определили в качестве базового типа перечисления). вы можете проверить соответствующее значение в перечислении, но с вызовом Enum.IsDefined. Больше информации здесь: Cast int to enum in C#

1

Enum - действительно тонкая обертка над int. В основном это int + статическая коллекция возможных значений (вид констант). Все проверки находятся во время компиляции, проверка типов и т. Д. Но когда вы на самом деле бросаете int в enum, время выполнения не заботится. Поэтому подтвердите свой вклад!

-1

Используйте int.Parse(), если вы не хотите исключать исключение, если переданное значение не может быть проанализировано. Используйте int.TryParse(), если вы не будете анализировать значение, которое может быть недействительным без исключения исключения.

+1

Это не совсем то, что он собирается делать. Он хочет получить исключение, если значение не является определенным значением для его типа перечисления, независимо от того, является ли оно допустимым значением int. – StriplingWarrior