C# 为什么即使枚举中没有定义有效值,也允许枚举强制转换

C# 为什么即使枚举中没有定义有效值,也允许枚举强制转换,c#,enums,type-systems,C#,Enums,Type Systems,当雷丁回答这个问题时,我学到了一些我在C#中没有意识到的新东西,我猜是CLR类型的系统 这是合法的原因是什么: public enum Tests { Zero = 0, One, Two } var nonExistantTestsValue = (Tests)1000; 这将非常好,但我不明白为什么会这样。据我所知,枚举的全部要点是将指定类型的给定变量的值限制为一定数量的选项 如果enum定义施加的限制很容易被打破,那么除了可读性问题之外,还有什么意义?显然,您可

当雷丁回答这个问题时,我学到了一些我在C#中没有意识到的新东西,我猜是CLR类型的系统

这是合法的原因是什么:

public enum Tests
{
    Zero = 0,
    One,
    Two
}

var nonExistantTestsValue = (Tests)1000;
这将非常好,但我不明白为什么会这样。据我所知,枚举的全部要点是将指定类型的给定变量的值限制为一定数量的选项

如果
enum
定义施加的限制很容易被打破,那么除了可读性问题之外,还有什么意义?显然,您可以使用反射来确保它是一个有效的值,但为什么不在编译时完成呢

也许有一个很好的理由让它这样工作,但我没有看到它

如果enum定义施加的限制很容易被打破,那有什么意义呢

我认为enum抽象的设计没有考虑到限制或保证。我认为它的设计考虑到了方便性和可维护性

充分理由:

如果你现在不想看到简单的事实,跳过第一个项目

  • 语言规范[我之所以提到这一点,是为了提醒人们,辩论事实的使用是有限的;像
    …这样的短语对我来说有什么意义

  • 性能(很难/不可能判断何时不需要验证,这将在很大程度上影响特定应用程序的性能)

(请记住,CLR函数可以从任何地方调用,而不仅仅是从C#调用,也不仅仅是从程序集调用)

允许任何值,因为您可以使用“Flags”属性标记枚举。这意味着您可以通过或调用枚举本身的各种成员来组合任何值。 基本上,编译器不够聪明,无法处理使用枚举的任何可能方式

编辑:找到Eric Lippert以前的帖子:


我不知道这个设计决定的原因,但我们可以看看它的一些后果

让我们看一下枚举的IL表示:

.class private auto ansi sealed MyEnum
    extends [mscorlib]System.Enum
{
    // Fields
    .field public specialname rtspecialname int32 value__
    .field public static literal valuetype MyEnum Value0 = int32(0)
    .field public static literal valuetype MyEnum Value1 = int32(1)
    .field public static literal valuetype MyEnum Value2 = int32(2)

}
首先,我们注意到它是一种值类型,因此
(MyEnum)0
必须有效。其次,我们看到枚举的可能值只是
const
s,并且运行时级别的枚举与整数文本的赋值兼容

枚举常量通常会变成整数文本。因此,如果要防止出现无效枚举,则需要在从枚举转换时引入昂贵的运行时检查,或者引入非平凡的跨程序集加载时间检查,以确保烘焙到另一个程序集中的枚举文本有效


另一件事是可以创建由long支持的枚举。但是long的一个属性是,它们的赋值不能保证是原子的。因此,很难保证基于
long
的枚举的值是有效的

enum MyLongEnum:long
{
  Value1=0x0101010102020202,
  Value2=0x0303030304040404
}
如果从多个线程分配给这样一个枚举,即使从未分配过无效值,最终也可能会得到一个无效的混合值



获取安全枚举还有一个简单的解决方法:使用一个具有私有构造函数的类和可能值的静态只读字段或属性。这样一来,您将失去整数转换、文本和非空性,但会获得类型安全性和更好的版本控制。

本质上是唯一的类型,允许您分配符号名对于整数值。它们不用于限制变量的允许值…

当枚举不限于这些值时,您可以做什么更令人质疑:

Flags是以下示例之一:

[Flags]
enum MyFlag
{
     a,
     b,
     c
}
现在您可以执行位操作:

MyFlag flags = MyFlag.a|MyFlag.b;

谢谢你的回答。1)是的,否则这将是一个真正的错误。我已经猜出来了。2) 是的,我知道可能是这样,但为什么一开始就允许这样做(不是c#)?它可以是一个简单的编译时检查,在任何语言中都没有任何代价。最后一句话+1。我认为OP不明白强制转换是危险的,应该避免。将语言规范称为理由是一个循环论证。我不明白为什么你仍然可以通过你的回答将1000个类型转换成
测试。@CodeInChaos:谢谢你指出这一点。有时候,陈述(显而易见的)真相并没有什么坏处。我想我对枚举的理解有点偏离了。我现在明白了为什么它是这样,特别是它们被用作旗帜。尽管如此,我认为在类型系统中使用某种枚举来施加选项限制(如果可能,通过属性或其他方式)是一个不错的选择。这意味着每当我有一个不打算用作标志的枚举参数时,我必须检查它是否有效。使用枚举在可读性方面所获得的好处,在验证它们的代码混乱中也会部分丢失。@interween:那么这将不利于方便,这就是@sehe的目的。@0A0D:是的,我现在明白了。这就是为什么我只是在考虑一个选择,如果它有一些好处的话,我可以这样做。无论如何,这不是一个选项,因为这里已经发布了很多好的理由。这是一个非常好的理由,谢谢。到底是谁否决了这个答案和peer的?!?[Flags]将枚举的语义更改为编译器-如果枚举是[flag]ged,则编译器应发出运行时检查,以根据a、b和c生成的可能位掩码验证转换后的值。@Luther Blissett:据我所知,它没有。谢谢!好的观点。而且,使用静态类来确保选项限制的想法似乎已经足够好了。这是我对不可标记枚举的行为的可怕错误想法。谢谢!有趣的是,似乎我和Eric的感觉一样,如果能有一个机制来确保期权的一致性,那就太好了。枚举的行为方式的原因似乎是这样的,或者Eric声称(这