- Boxed nullable underlying type can be cast to enum but boxed enum type can't be cast to nullable type.
And similarly,
- Boxed nullable enum can be cast to underlying type but boxed underlying type can't be cast to nullable enum.
Ok, I know "boxed nullable type" is not the best way to describe it, but it's for the sake of the question. I'm aware it's the underlying value type that's getting boxed.
I will show it with examples. Assume I have an enum
with int
as the underlying type.
enum Sex { Male, Female }
Case I:
int? i = 1;
object o = i;
Sex e = (Sex)o; //success
//but
Sex e = Sex.Male;
object o = e;
int? i = (int?)o; //invalid cast
Case II:
Sex? e = Sex.Male;
object o = e;
int i = (int)o; //success
//but
int i = 1;
object o = i;
Sex? e = (Sex?)o; //invalid cast
In a nutshell,
(enum)int? -> succeeds
(int?)enum -> the reverse fails
(int)enum? -> succeeds
(enum?)int -> the reverse fails
Or in even simpler terms,
cast to non-nullable -> succeeds
cast to nullable -> fails
Now I do know that once you box a value type, it can be cast back only to the original type. But since by C# rules, a boxed int
can be cast to enum
and a boxed enum
can be cast to int
, and a boxed int
to int?
and a boxed int?
to int
, I was looking for a consistent understanding of other scenarios as well, ie the ones listed above. But I dont get the logic. For one, I feel if they all failed or if they all succeeded, it made more sense to developers. Two, even the successful casts look a little odd. I mean since a value type can be implicitly cast to its nullable equivalent (and not the other way around), a cast to nullable should anyway succeed, but with the current implementation a nullable type is being successfully cast to non-nullable which can even fail if the former had a null value. Had this whole thing been other way around, it would have been easier comprehending. An example like:
Sex? e = null;
object o = e;
int i = (int)o; //succeeds, but sure to explode on cast
//but
int i = 1;
object o = i;
Sex? e = (Sex?)o; //invalid cast, even though its always a safe cast
Questions:
So what C# rule is letting this happen?
Is there a simple way I can remember this?
I think this is a subtlety of the
unbox
andunbox.any
IL instructions.From ECMA 335, section III.4.32 (
unbox
operation -unbox.any
is similar)So for example, in this case:
it fails entirely correctly - because valuetype is
Nullable<int>
and the value of obj is not a boxedint
. The "verifier-assignable-to" part doesn't apply for theNullable<T>
case.I doubt that any of this behaviour is described in the C# specification, unfortunately - I don't think the unboxing behaviour from "boxed
int
" to "enum with an underlying type ofint
" is described, as far as I can see, which is a sort of prerequisite to then including nullability in the mix.