-->

Generics and calling overloaded method from differ

2020-08-26 04:31发布

问题:

First of all, sorry for the title, but I couldn't think about anything better ...

My problem can be presented by simple code sample:

public static class Test<T>
{
    public static int GetInt(T source)
    {
        return Convert.ToInt32(source);
    }
}

public static class Convert
{
    public static int ToInt32(byte source)
    {
        return 30;
    }

    public static int ToInt32(object source)
    {
        return 10;
    }
}

Why does Console.WriteLine(Test<byte>.GetInt(20)); prints 10, instead of 30?

I always thought that generics in .NET are resolved by JIT during runtime. Why then jitter isn't smart enough, to find out that there is ToInt32(byte) method, which suits our byte parameter type here?

This behavior makes Convert static class methods call result in boxing/unboxing operations for simple types.

回答1:

The compiler has to decide at compile time which method to choose. It doesn't emit any code to decide at runtime which of the two overloads to pick. Because you haven't provided any evidence to the C# compiler that GetInt(T source) only works with byte structures, the compiler has to pick the other overload.

Or let me put it in a different perspective: if you remove the ToInt32(object) overload, your program fails to compile.



回答2:

Compiler decides at compile time which method to execute.

I see through Reflector for IL code and found this -

.method public hidebysig static int32 GetInt(!T source) cil managed
{
    .maxstack 1
    .locals init (
        [0] int32 CS$1$0000)
    L_0000: nop 
    L_0001: ldarg.0 
    L_0002: box !T
    L_0007: call int32 ConsoleApplication1.Convert::ToInt32(object) <-- HERE
    L_000c: stloc.0 
    L_000d: br.s L_000f
    L_000f: ldloc.0 
    L_0010: ret 
}

As mentioned by Jon Skeet here, you can make a call to byte method using dynamic which provides typed information at execution time instead of compile time.

public static class Test<T>
{
    public static int GetInt(T source)
    {
        dynamic dynamicSource = source;
        return Convert.ToInt32(dynamicSource );
    }
}