Consider this code snippet, featuring generics and overloaded functions :
using System;
namespace Test_Project
{
public interface Interface
{
void f();
}
public class U : Interface
{
public void f() {}
}
public class Class<T> where T: Interface
{
public static void OverloadedFunction(T a)
{
Console.WriteLine("T");
a.f();
}
public static void OverloadedFunction(U a)
{
Console.WriteLine("U");
a.f();
}
}
class Program
{
public static void Invoke(U instance)
{
Class<U>.OverloadedFunction(instance);
}
static void Main(string[] args)
{
Invoke(new U());
}
}
}
I would say it doesn't compile, as I have two suitable candidates methods for OverloadedFunction. However it does and prints "U".
In the generated IL, I can see that:
.method public hidebysig static
void Invoke (
class Test_Project.U 'instance'
) cil managed
{
// Method begins at RVA 0x2085
// Code size 7 (0x7)
.maxstack 8
IL_0000: ldarg.0
IL_0001: call void class Test_Project.Class`1<class Test_Project.U>::OverloadedFunction(class Test_Project.U)
IL_0006: ret
} // end of method Program::Invoke
meaning that the C# compiler resolved the call to OverloadedFunction to a call instead of the callvirt which would have been required by the "generic" function. I can guess that the 'U' method is a better candidate from a compiler perspective, but I can't definitely explain why...
I'd really like to understand what happened here and I have no clue.
But it gets even weirder if you consider this modified version of the snippet, where we introduce another level of indirection :
using System;
namespace Test_Project
{
public interface Interface
{
void f();
}
public class U : Interface
{
public void f() {}
}
public class V : U { }
public class Class<T> where T: Interface
{
public static void OverloadedFunction(T a)
{
Console.WriteLine("T");
a.f();
}
public static void OverloadedFunction(U a)
{
Console.WriteLine("U");
a.f();
}
}
class Program
{
public static void Invoke(V instance)
{
Class<V>.OverloadedFunction(instance);
}
static void Main(string[] args)
{
Invoke(new V());
}
}
}
I would expect this program to still print 'U', as 'V' are 'U' by inheritance. But it prints 'T', as show by the MSIL :
.method public hidebysig static
void Invoke (
class Test_Project.V 'instance'
) cil managed
{
// Method begins at RVA 0x208d
// Code size 7 (0x7)
.maxstack 8
IL_0000: ldarg.0
IL_0001: call void class Test_Project.Class`1<class Test_Project.V>::OverloadedFunction(!0)
IL_0006: ret
} // end of method Program::Invoke
meaning that the generic version has been preferred by the c# compiler.
Could please someone explain what are the rules of overloaded methods resolution when it comes to generic parameters and inheritance?