In the following code, boxing occurs (in Generic<Type>.Print):
using System;
namespace Test
{
static class Program
{
static void Main()
{
Generic<string> generic = new Generic<string>("test");
generic.Print();
}
}
class Generic<Type>
{
Type value;
public Generic(Type value)
{
this.value = value;
}
public void Print()
{
Console.WriteLine(value);
}
}
}
ILSpy output:
.method public hidebysig
instance void Print () cil managed
{
// Method begins at RVA 0x207d
// Code size 17 (0x11)
.maxstack 8
IL_0000: ldarg.0
IL_0001: ldfld !0 class Test.Generic`1<!Type>::'value'
IL_0006: box !Type
IL_000b: call void [mscorlib]System.Console::WriteLine(object)
IL_0010: ret
} // end of method Generic`1::Print
It’s boxing and calling Console.WriteLine(object). I assumed that it would simply call Console.WriteLine(string). What is going on here?
Its chosen the
objectoverload forConsole.WriteLinebecause this is the most appropriate overload for that call.Remember that overload resolution is done at compile time – the compiler must choose a suitable overload based on the provided type information, and in this situation the only suitable one is the
objectoverload.To understand this it may help to ignore your
Mainmethod and consider the case where theGenericclass is in a different assembly. The compiler needs to choose an overload and knows only thatTypecan either be cast or boxed into anobject. Just because there is in fact code elsewhere in the assembly that uses this class with astringtype parameter doesn’t affect the way thatGenericis compiled.Alternatively consider what would happen if
Console.WriteLinedidn’t have an overload accepting anobject– in this case the method simply would not compile (as there are no restrictions onTypewhich would make another overload suitable).