Fun with enums in C#. Take one generic list that is created to store some Enum that you had defined previously and add few items in it. Iterate with foreach or GetEnumerator<T>()
but specify some other enum then the original and see what happens. I was expecting InvalidCastException or something like that but it perfectly works :).
For the illustration let's take a simple console application and create two enums there: Cars and Animals:
public enum Cars
{
Honda = 0,
Toyota = 1,
Chevrolet = 2
}
public enum Animals
{
Dog = 0,
Cat = 1,
Tiger = 2
}
And do this in main method:
public static void Main()
{
List<Cars> cars = new List<Cars>();
List<Animals> animals = new List<Animals>();
cars.Add(Cars.Chevrolet);
cars.Add(Cars.Honda);
cars.Add(Cars.Toyota);
foreach (Animals isItACar in cars)
{
Console.WriteLine(isItACar.ToString());
}
Console.ReadLine();
}
It will print this in console:
Tiger Dog Cat
Why is this happening? My first guess was that enum is not actually a Type by himself it's just and int but that's not true: If we write:
Console.WriteLine(Animals.Tiger.GetType().FullName);
We will get his fully qualified name printed! So why this?
Enum types are distinct, but you're being confused by an implicit cast which is in foreach.
Let's rewrite your loop a bit:
public static void Main()
{
List<Cars> cars = new List<Cars>();
List<Animals> animals = new List<Animals>();
cars.Add(Cars.Chevrolet);
cars.Add(Cars.Honda);
cars.Add(Cars.Toyota);
foreach (Cars value in cars)
{
// This time the cast is explicit.
Animals isItACar = (Animals) value;
Console.WriteLine(isItACar.ToString());
}
Console.ReadLine();
}
Now does the result surprise you? Hopefully not, except possibly the fact that you can cast from one enum to another. This is just a more explicit version of what your original code is doing.
The fact that there's a cast implicit in every foreach
loop (even though it's usually a no-op) is the bit which most developers would find confusing, I think.
From section 8.8.4 of the C# 3.0 spec:
The above steps, if successful, unambiguously produce a collection type C, enumerator type E and element type T. A foreach statement of the form
foreach (V v in x) embedded-statement
is then expanded to:
{
E e = ((C)(x)).GetEnumerator();
try {
V v;
while (e.MoveNext()) {
v = (V)(T)e.Current;
embedded-statement
}
}
finally {
... // Dispose e
}
}
The enumeration conversion itself is covered in section 6.2.2:
The explicit enumeration conversions are:
An explicit enumeration conversion between two types is processed by treating any participating enum-type as the underlying type of that enum-type, and then performing an implicit or explicit numeric conversion between the resulting types. For example, given an enum-type E with and underlying type of int, a conversion from E to byte is processed as an explicit numeric conversion (§6.2.1) from int to byte, and a conversion from byte to E is processed as an implicit numeric conversion (§6.1.2) from byte to int.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With