为什么即使枚举中没有定义有效值也允许枚举转换
在阅读Jon Skeet对此特定问题的回答时我怎样才能使我的类变量只能设置为三个选择之一? 你学到了一些我在 C# 中不知道的新东西,我猜CLR 类型系统。
这是合法的背后的原因是什么:
public enum Tests
{
Zero = 0,
One,
Two
}
var nonExistantTestsValue = (Tests)1000;
这将编译得很好,但我不明白为什么会这样。据我所知,枚举的全部要点是将指定类型的给定变量的值限制为一定数量的选项。
如果 enum
定义所施加的限制如此容易被打破,那么还有什么意义(除了可读性问题)?显然,您可以使用反射来确保它是一个有效值,但为什么不在编译时完成呢?
这样做可能有一个很好的理由,但我看不到它。
When reading Jon Skeet's answer to this particular question How can I make it so my class variables can only be set to one of three choices? y learned something new I was not aware of in C# and I guess the CLR type system.
What is the reasoning behind this being legal:
public enum Tests
{
Zero = 0,
One,
Two
}
var nonExistantTestsValue = (Tests)1000;
This will compile perfectly fine but I fail to see the reason of why it should be so. The whole point of enumerations as far as I can see is to limit to a certain number of options the value of a given variable of the specified type.
If the limitation imposed by the enum
definition is so easily broken, what is the point (besides the readability issue)? You can obviously make sure it is a valid value using reflection but why isn't this done at compile time?
There is probably a good reason for this to work the way it does but I fail to see it.
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(5)
枚举< /a> 本质上是唯一的类型,允许您将符号名称分配给整数值。它们不用于限制变量的允许值......
Enumerations are essentially unique types that allow you to assign symbolic names to integral values. They are not used for restricting the allowable values of a variable...
我认为枚举抽象的设计没有考虑到限制或保证有什么意义。我认为它的设计考虑了便利性和可维护性
良好的理由:
如果您现在不想看到简单的事实,请跳过第一个项目符号 >
... 那么有什么意义
会为我触发这个](记住 CLR 函数可以从任何地方调用,而不仅仅是 C#,不仅仅是 < em>你的集会)
I think the enum abstraction was not designed with limitation or guarantees in mind. I think it was designed with convenience and maintainability in mind
Good reasons:
Skip the first bullet if you don't want to see simple truths rigth now
... then what's the point
triggers this for me](Remember CLR functions can be called from anywhere, not just C#, not just your assembly)
当枚举不限于这些值时,您可以做什么更令人质疑:
Flags 是这些示例之一:
现在您可以执行位操作:
It is more questioning in what you can do when enums are not limited to there values:
Flags is one of these examples:
Now you can do bit operations:
允许使用任何值,因为您可以使用“Flags”属性来标记枚举。这意味着您可以通过对枚举本身的各个成员进行“或”运算来组成任何值。
基本上,编译器不够智能,无法处理使用枚举的任何可能的方式。
编辑:找到埃里克·利珀特(Eric Lippert)之前的帖子:
为什么将 int 转换为无效的枚举值不会引发异常?
It is allowed any value because you may mark the enum with the "Flags" attribute. That means you may compose any value by OR-ing various members of the enum itself.
Basically the compiler is not smart enough to take care of any possible way where the enum is used.
EDIT: found a previous post by Eric Lippert:
Why does casting int to invalid enum value NOT throw exception?
我不知道这个设计决定的原因,但我们可以看看它的一些后果。
让我们看一下枚举的 IL 表示:
首先我们注意到它是一个值类型,因此
(MyEnum)0
必须有效。其次,我们看到枚举的可能值只是 const,并且运行时级别的枚举与整数文字兼容。枚举常量通常会变成整数文字。因此,如果您想防止出现无效的枚举,则需要在从枚举转换时引入昂贵的运行时检查,或者引入重要的跨程序集加载时间检查,以确保烘焙到另一个程序集中的枚举文字是有效的。
另一件事是可以创建由 long 支持的枚举。但长整型的一个特性是它们的赋值不能保证是原子的。因此,保证基于 long 的枚举值有效是很困难的。
如果您从多个线程分配给此类枚举,即使您从未分配过无效值,最终也可能会得到无效的混合值。
还有一个简单的解决方法来获得安全的枚举:使用具有私有构造函数的类以及可能值的静态只读字段或属性。这样你就失去了整数转换、文字和不可空性,但获得了类型安全和更好的版本控制。
I don't know about the reasons for this design decision, but we can look at a few of its consequences.
Let's look at the IL representation of an enum:
First we note that it is a value type and thus
(MyEnum)0
must be valid. Second we see that the possible values of the enums are justconst
s and that enums at the runtime level are assignment compatible to integer literals.Enum constants generally become an integer literal. So if you wanted to prevent invalid enums from appearing you'd need to introduce either expensive runtime checks when converting from an enum, or non trivial cross assembly load time checks to make sure enum literals baked into another assembly are valid.
Another thing is that it is possible to create enums backed by a long. But one property of longs is that their assignment is not guaranteed to be atomic. So guaranteeing that the value of a
long
based enum is valid is hard.If you assigned to such an enum from multiple threads you can end up with a mixed value that's invalid even if you never assigned an invalid value.
There is also an easy workaround to get safe enums: Use a class with a private constructor and static readonly fields or properties for the possible values. This way you lose integer conversions, literals and non-nullability, but gain type safety and better versioning.