为什么铸造int无效的枚举值不抛出exception?

如果我有一个像这样的枚举:

enum Beer { Bud = 10, Stella = 20, Unknown } 

为什么在将这些值以外的int值转换为Beertypes时不会抛出exception呢?

例如下面的代码不会引发exception,它会向控制台输出'50':

 int i = 50; var b = (Beer) i; Console.WriteLine(b.ToString()); 

我觉得这个奇怪的…任何人都可以澄清?

从parsing一个枚举混淆

这是创build.NET的人的一个决定。 一个枚举由另一个值types( intshortbyte等)支持,所以它实际上可以有任何对这些值types有效的值。

我个人不是这种方式的迷,所以我做了一系列的实用方法:

 /// <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. 

编辑

除了上面给出的解释之外,你必须认识到Enum的.NET版本遵循比Java启发的更受C启发的模式。 这使得可以使用“位标志”枚举来使用二进制模式来确定某个特定的“标志”是否在枚举值中处于活动状态。 如果你必须定义每一个可能的标志组合(即MondayAndTuesdayMondayAndWednesdayAndThursday MondayAndTuesdayMondayAndWednesdayAndThursday ),这些将是非常单调乏味的。 所以有能力使用未定义的枚举值可以非常方便。 只需要一些额外的工作,当你想要不使用这些技巧的枚举types的快速行为。

枚举通常用作标志:

 [Flags] enum Permission { None = 0x00, Read = 0x01, Write = 0x02, } ... Permission p = Permission.Read | Permission.Write; 

p的值是整数3,它不是枚举的值,但显然是一个有效的值。

我个人宁愿看到不同的解决办法; 我宁愿有能力使“位arrays”整数types “一组不同的语言特征”,而不是将它们混合成“枚举”。 但这就是原来的语言和框架devise师想出来的; 因此,我们必须允许非声明的枚举值是合法值。

简短的回答:语言devise者决定用这种方式来devise语言。

长篇答案: Section 6.2.2: Explicit enumeration conversions C#语言规范的Section 6.2.2: Explicit enumeration conversions说:

通过将任何参与的枚举types视为该枚举types的基础types,然后在结果types之间执行隐式或显式数值转换,来处理两种types之间的显式枚举转换。 例如,如果枚举typesE的基本types为int,则从E到字节的转换将作为显式数字转换(第6.2.1节)从int到byte进行处理,而从byte到E的转换将作为从字节到整数的隐式数字转换(第6.1.2节)。

基本上, 枚举在进行转换操作时被视为基础types。 默认情况下, 枚举的基础types是Int32 ,这意味着转换的处理与Int32的转换完全相同。 这意味着任何有效的int值都是可以允许的。

我怀疑这主要是出于性能的原因。 通过enum一个简单的整型,并允许任何整型转换,CLR不需要做所有额外的检查。 这意味着,与使用整数相比,使用enum并没有真正的性能损失,这反过来又有助于鼓励它的使用。

从文档 :

types为Days的variables可以在基础types的范围内分配任何值; 值不限于指定的常量。