.NET:为什么没有检查Enum的范围/值?

时间:2009-01-11 13:38:38

标签: .net enums

这一直困扰着我。也许对.NET内部有一些核心知识的人可以向我解释。

假设我按如下方式定义枚举:

public enum Foo
{
   Eenie = 1,
   Meenie = 2,
   Miney = 3,
   Moe = 4
}

现在,还假设在我的代码中的某处,我有以下代码:

int bar = (Foo)5;

这将编译得很好,并且不会引发任何异常,即使值5显然不是Foo中定义的有效值。

或者,请考虑以下事项:

public void ProcessFoo(Foo theFoo)
{
    // Do processing
}

public static void Main()
{
    ProcessFoo((Foo)5);
}

同样,也不例外。

在我看来,这会导致类型不匹配异常,因为5不是Foo。但设计师选择不这样做。

现在,我编写了一个可以验证这种情况的扩展方法,并且调用它以确保就是这种情况没什么大不了的,但是我必须使用反射来完成它(具有所有性能)惩罚等等。

再说一次,有什么令人信服的理由可能导致决定没有检查枚举?

供参考,来自the MSDN documentation for the Enum class

  

定义方法或属性时   将枚举常量作为a   值,考虑验证该值。   原因是你可以施展一个   枚举类型的数值   即使那个数值不是   在枚举中定义。

9 个答案:

答案 0 :(得分:14)

问题在于表现。对于常规枚举(例如Color

),检查enum非常简单
enum Color {
  Red,
  Blue
}

但问题是枚举用作位标志。

enum Property {
  IsFirst = 0x1,
  IsDefault = 0x2,
  IsLastAccessed = 0x4
}

对每个转换为Enum值的整数进行逐位检查被认为太昂贵了。因此轻松转换为枚举值。

答案 1 :(得分:4)

范围检查可能会产生不必要的成本。因此不能隐含地执行它是合理的。如前所述,[Flags]要求不进行此类检查。如果运行时检查是否存在[Flags],则每次执行转换时仍会导致运行时损失。

解决这个问题的唯一方法是让编译器知道[Flags]属性。我想这不是为了减少硬编码到编译器中的运行时知识量。

答案 2 :(得分:2)

如果对它们进行了范围检查,您将如何使用[Flags]枚举并使用按位或组合它们?

一个例子是ControlStyles enum

答案 3 :(得分:1)

有两个原因让人想起。首先,生成超出范围的值需要强制转换。如果你故意施放,为什么你会期望在运行时被打耳光?禁止演员阵容会容易得多。

另一个引人注目的是这一个:

enum VeryHardToRangeCheck {
  one = 1,
  three = 3,
  five = 5
}

答案 4 :(得分:1)

我可以看到两个原因:

  1. [标志]没有检查就更顺畅了。对于你的例子,

    (Foo)5 == Foo.Eenie | Foo.Moe;

  2. 枚举是一种值类型。如果不初始化它,它将等于零。如果你想检查枚举值,那么在这种情况下应该抛出异常并不清楚 - 例如,当你创建一个包含这个枚举作为字段的类的实例时,零值可能会潜入你的代码。

    因此,当前行为更加一致 - 您只知道您可以拥有超出范围的值并进行检查。

  3. 此外,您应该始终明确地执行检查并为无法处理的值抛出异常。否则,在枚举中添加新值可能会改变现有代码的行为。幸运的是你使用单个switch语句是一个返回值的方法,编译器会让你明确指定你想要做什么,如果没有找到匹配项 - 在切换后的默认部分你将不得不返回一个值或抛出异常。在大多数情况下,NotSupportedExcpetion是首选。

答案 5 :(得分:0)

这是因为(Foo)5Foo.Eenie | Foo.Moe

答案 6 :(得分:0)

我想说的原因是因为枚举只是在编译时由编译器进行类型检查和内联。对于使用Flags属性扩展枚举特别有用(因为它突然变得向前兼容......

答案 7 :(得分:0)

微软的C# Programming Guide明确表示不要做你要问的事情:

  

可以将任意整数值分配给meetingDay。例如,这行代码不会产生错误:meetingDay =(Days)42。但是,您不应该这样做,因为隐含的期望是枚举变量只保留枚举定义的值之一。将任意值分配给枚举类型的变量是为了引入高风险的错误。

int实际上只是存储类型。实际上,it's possible to specify other integer storage types,如byte:

enum Days : byte {Sat=1, Sun, Mon, Tue, Wed, Thu, Fri};

存储类型决定了枚举使用的内存量。

答案 8 :(得分:-1)

对不起来了。您是否有可能将Enum与键值集合混淆在一起?

当你为一个枚举项目分配一个整数Meenie = 2时,你所做的只是说Meenie是第三个索引,而它之后的所有内容,如果没有指定,将采用2+的索引(距离Meenie的距离) )。所以当你寻找Foo [5]时,你正在寻找索引5,而不是一些将5作为值的键。

在大多数情况下,你不会这样做;你要求Foo.Meenie - 这是enum的 point ,设置一个已知的值范围,然后通过它们的暴露名称来引用它们。这只是开发人员的便利。有更好的结构来做你在你的例子中做的事情。