Came across an interesting issue in the upgrading our code to c# 3.0 compiler today. Seems that this new compiler is a little too liberal in choosing the correct override of a method to call when enums are considered. You would think this would call Print(object) every time, but you would be wrong if you were using the C# 3.0 compiler. The first call would call Print(Y) even though there is no implicit conversion from int to our enum type Y.
enum X { Zero = 0, One = 1 }
enum Y { Zero = 0, One = 1 }
class Program
{
static void Main()
{
X zero = X.Zero;
Print((int)X.Zero);
Print((int)X.One);
Print((int)zero);
}
static void Print(object val)
{
System.Console.WriteLine("object: [{0}]", val);
}
static void Print(Y val)
{
System.Console.WriteLine("Y: [{0}]", val);
}
}
Compiled in C# 2.0 this prints:
object: [0]
object: [1]
object: [0]
Compiled in C# 3.0 this prints:
Y: [Zero]
object: [1]
object: [0]
So, it looks like the 3.0 compiler is throwing out the type information, but only if the underlying value of the enum is 0. If the value is something other than 0 it works as expected.
Lets take a look at the IL that is generated for our Main() method. Differences in bold.
2.0 IL
L_0000: nop
L_0001: ldc.i4.0
L_0002: stloc.0
L_0003: ldc.i4.0
L_0004: box int32
L_0009: call void Program::Print(object)
L_000e: nop
L_000f: ldc.i4.1
L_0010: box int32
L_0015: call void Program::Print(object)
L_001a: nop
L_001b: ldloc.0
L_001c: box int32
L_0021: call void Program::Print(object)
L_0026: nop
L_0027: ret
In this case the compiler gets this right and calls the only version of the method that makes sense for it.
3.0 IL
L_0000: nop
L_0001: ldc.i4.0
L_0002: stloc.0
L_0003: ldc.i4.0
L_0004: call void Program::Print(valuetype Y)
L_0009: nop
L_000a: ldc.i4.1
L_000b: box int32
L_0010: call void Program::Print(object)
L_0015: nop
L_0016: ldloc.0
L_0017: box int32
L_001c: call void Program::Print(object)
L_0021: nop
L_0022: ret
The main difference is that the compiler choose a different overload of the method to connect to and excluded the boxing that would allow the int to be passed as an object. Crazy stuff indeed.