Warning: file_get_contents(/data/phpspider/zhask/data//catemap/4/algorithm/11.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C# 为什么将int强制转换为无效枚举值不会引发异常?_C# - Fatal编程技术网

C# 为什么将int强制转换为无效枚举值不会引发异常?

C# 为什么将int强制转换为无效枚举值不会引发异常?,c#,C#,如果我有这样一个枚举: enum Beer { Bud = 10, Stella = 20, Unknown } 当将这些值之外的int强制转换为Beer类型时,为什么不抛出异常 例如,以下代码不会引发异常,它会将“50”输出到控制台: int i = 50; var b = (Beer) i; Console.WriteLine(b.ToString()); 我觉得很奇怪……有人能澄清一下吗?简短的回答是:语言设计者决定用这种方式设计语言 答案很长:C#语言规范的

如果我有这样一个枚举:

enum Beer
{
    Bud = 10,
    Stella = 20,
    Unknown
}
当将这些值之外的
int
强制转换为
Beer
类型时,为什么不抛出异常

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

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

Console.WriteLine(b.ToString());

我觉得很奇怪……有人能澄清一下吗?

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

答案很长:
C#语言规范的第6.2.2节:显式枚举转换说:

处理两种类型之间的显式枚举转换的方法是将任何参与的枚举类型视为该枚举类型的基础类型,然后在结果类型之间执行隐式或显式数字转换。例如,给定一个枚举类型E和基本类型int,从E到字节的转换被处理为从int到字节的显式数字转换(§6.2.1),从字节到E的转换被处理为从字节到int的隐式数字转换(§6.1.2)

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

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

来自:

类型为Days的变量可以是 在指定的范围内指定任何值 底层类型;这些值不正确 仅限于命名常量

取自

这是创建.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);
    }
}
。。。或:

EnumUtil<Stooge>.Parse(s); // throws an exception if s is not a defined value.
EnumUtil.Parse(s);//如果s不是定义的值,则引发异常。
编辑
除了上面给出的解释之外,您还必须认识到.NET版本的Enum遵循的是一种比Java更受C启发的模式。这使得可以使用二进制模式来确定枚举值中的特定“标志”是否处于活动状态。如果您必须定义所有可能的标志组合(即
周一和周二
周一和周三以及周四
),那么这些组合将非常繁琐。因此,能够使用未定义的枚举值非常方便。当您想要在不利用这些技巧的枚举类型上执行快速失败行为时,只需要做一些额外的工作。

枚举通常用作标志:

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

Permission p = Permission.Read | Permission.Write;
p的值是整数3,它不是枚举的值,但显然是有效值


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

您觉得这奇怪吗?我认为枚举的一个要点是安全地对一系列整数进行分组,我们也可以赋予它们各自的含义。对我来说,允许枚举类型保存任何整数值违背了它的目的。@dormisher:我刚刚编辑并在末尾添加了一点。提供你所追求的“安全”是有成本的。也就是说,在正常使用情况下,这真的一点问题都没有。注意,你可以随时检查一个值是否有效。很酷,我不知道,可能会在我目前试图解决的问题中使用它,但也读到以下内容:投票支持使Stella的价值比Bud高一倍。很好……我也不喜欢它的工作方式,看起来很奇怪me@dormisher:“‘这太疯狂了,但还有方法不可以。”请看我的编辑。@StriplingWarrior,以获取兴趣。Java等价物是EnumSet.of(星期一、星期三、星期四)。枚举集中只有一个长的。因此,它提供了一个很好的API,没有太多的效率损失。“Require.That()”=>@StriplingWarrior在您试图调用的解析方法中定义为一个扩展方法。然而,IsDefined方法不能成为扩展方法,因为您试图使用约束创建EnumUtil类(对enum进行静态检查,我非常喜欢)。然而实际上,它告诉我不能在泛型类上创建扩展方法。有什么想法吗?但3“显然是一个有效的值”某种程度上加强了OP的观点。该[Flags]属性可以告诉编译器查找已定义的枚举或有效值。就这么说吧。
[Flags]
enum Permission
{
    None = 0x00,
    Read = 0x01,
    Write = 0x02,
}
...

Permission p = Permission.Read | Permission.Write;