Edit: Comments at bottom. Also, this.
Here's what's kind of confusing me. My understanding is that if I have an enum like this...
enum Animal
{
Dog,
Cat
}
...what I've essentially done is defined a value type called Animal
with two defined values, Dog
and Cat
. This type derives from the reference type System.Enum
(something which value types can't normally do—at least not in C#—but which is permitted in this case), and has a facility for casting back and forth to/from int
values.
If the way I just described the enum type above were true, then I would expect the following code to throw an InvalidCastException
:
public class Program
{
public static void Main(string[] args)
{
// Box it.
object animal = Animal.Dog;
// Unbox it. How are these both successful?
int i = (int)animal;
Enum e = (Enum)animal;
// Prints "0".
Console.WriteLine(i);
// Prints "Dog".
Console.WriteLine(e);
}
}
Normally, you cannot unbox a value type from System.Object
as anything other than its exact type. So how is the above possible? It is as if the Animal
type is an int
(not just convertible to int
) and is an Enum
(not just convertible to Enum
) at the same time. Is it multiple inheritance? Does System.Enum
somehow inherit from System.Int32
(something I would not have expected to be possible)?
Edit: It can't be either of the above. The following code demonstrates this (I think) conclusively:
object animal = Animal.Dog;
Console.WriteLine(animal is Enum);
Console.WriteLine(animal is int);
The above outputs:
True
False
Both the MSDN documentation on enumerations and the C# specification make use of the term "underlying type"; but I don't know what this means, nor have I ever heard it used in reference to anything other than enums. What does "underlying type" actually mean?
So, is this yet another case that gets special treatment from the CLR?
My money's on that being the case... but an answer/explanation would be nice.
Update: Damien_The_Unbeliever provided the reference to truly answer this question. The explanation can be found in Partition II of the CLI specification, in the section on enums:
For binding purposes (e.g., for
locating a method definition from the
method reference used to call it)
enums shall be distinct from their
underlying type. For all other
purposes, including verification and
execution of code, an unboxed enum
freely interconverts with its
underlying type. Enums can be boxed
to a corresponding boxed instance
type, but this type is not the same
as the boxed type of the underlying
type, so boxing does not lose the
original type of the enum.
Edit (again?!): Wait, actually, I don't know that I read that right the first time. Maybe it doesn't 100% explain the specialized unboxing behavior itself (though I'm leaving Damien's answer as accepted, as it shed a great deal of light on this issue). I will continue looking into this...
Another Edit: Man, then yodaj007's answer threw me for another loop. Somehow an enum is not exactly the same as an int
; yet an int
can be assigned to an enum variable with no cast? Buh?
I think this is all ultimately illuminated by Hans's answer, which is why I've accepted it. (Sorry, Damien!)
question from:
https://stackoverflow.com/questions/4626394/how-is-it-that-an-enum-derives-from-system-enum-and-is-an-integer-at-the-same-ti