繁体   English   中英

为什么将 int 转换为无效的枚举值不会引发异常?

[英]Why does casting int to invalid enum value NOT throw exception?

如果我有这样的枚举:

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

为什么将这些值之外的int类型转换为Beer时不会抛出异常?

例如,以下代码不会引发异常,它会向控制台输出“50”:

int i = 50;
var b = (Beer) i;

Console.WriteLine(b.ToString());

我觉得这很奇怪......有人可以澄清一下吗?

取自Confusion with parsing an Enum

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

我个人不喜欢这种工作方式,所以我做了一系列实用方法:

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

编辑

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

枚举通常用作标志:

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

Permission p = Permission.Read | Permission.Write;

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

我个人宁愿看到不同的解决方案; 我宁愿有能力将“位数组”整数类型“一组不同的值”类型作为两种不同的语言特性,而不是将它们都混为“枚举”。 但这就是最初的语言和框架设计者想出的; 因此,我们必须允许枚举的未声明值成为合法值。

简短的回答:语言设计者决定以这种方式设计语言。

长答案: Section 6.2.2: Explicit enumeration conversions说:

通过将任何参与的枚举类型视为该枚举类型的基础类型,然后在结果类型之间执行隐式或显式数字转换来处理两种类型之间的显式枚举转换。 例如,给定一个具有 int 基础类型的枚举类型 E,从 E 到 byte 的转换被处理为从 int 到 byte 的显式数字转换(第 6.2.1 节),从 byte 到 E 的转换被处理为从字节到整数的隐式数字转换(第 6.1.2 节)。

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

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

文档中:

可以为 Days 类型的变量分配基础类型范围内的任何值; 这些值不限于命名常量。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM