Why is enum casting allowed even when there is no valid value defined in the enumeration
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.
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...
If the limitation imposed by the enum definition is so easily broken, what is the point
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
- the language specification [the reason I mention this is to remind people about the limited use of debating facts; a phrase like
... then what's the point
triggers this for me]
- performance (its hard/impossible to tell when a validation would not needed and this would hamper performance in a big way for specific applications
(Remember CLR functions can be called from anywhere, not just C#, not just your assembly)
It is more questioning in what you can do when enums are not limited to there values:
Flags is one of these examples:
[Flags]
enum MyFlag
{
a,
b,
c
}
Now you can do bit operations:
MyFlag flags = MyFlag.a|MyFlag.b;
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?
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:
.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)
}
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 just const
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.
enum MyLongEnum:long
{
Value1=0x0101010102020202,
Value2=0x0303030304040404
}
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.
精彩评论