I come from a C++ background and I am relatively new to C#.
Currently, I am trying to write two print functions, the first of which accepts a generic array parameter (and prints the items of the array to the command line) and a second one which accepts a generic primitive parameter (and invokes its ToString()
method). Here's my code:
using System;
namespace OverloadResolution
{
class Program
{
static void Main(string[] args)
{
string[] foo = new string[] { "A", "B", "C" };
Extensions.PrintMe(foo);
Extensions.PrintMe("Hello world");
Console.ReadLine();
}
}
public static class Extensions
{
public static void PrintMe<T>(T[] elm)
{
Console.WriteLine("PrintMe<T>(T[] elm)");
Console.WriteLine(string.Join("", elm));
}
public static void PrintMe<T>(T elm)
{
Console.WriteLine("PrintMe<T>(T elm)");
Console.WriteLine(elm.ToString());
}
}
}
Everything works as expected and the correct overloads are chosen. However, if I change my code as follows:
using System;
namespace OverloadResolution
{
class Program
{
static void Main(string[] args)
{
string[] foo = new string[] { "A", "B", "C" };
Extensions.PrintMe2(foo); // <<< Note the PrintMe2 function
Extensions.PrintMe2("Hello world"); // <<< Note the PrintMe2 function
Console.ReadLine();
}
}
public static class Extensions
{
public static void PrintMe2<T>(T elm)
{
PrintMe(elm);
}
public static void PrintMe<T>(T[] elm)
{
Console.WriteLine("PrintMe<T>(T[] elm)");
Console.WriteLine(string.Join("", elm));
}
public static void PrintMe<T>(T elm)
{
Console.WriteLine("PrintMe<T>(T elm)");
Console.WriteLine(elm.ToString());
}
}
}
the type information seems to get lost when calling the PrintMe2
function because, internally, the second PrintMe
function is invoked in both cases. Are there any special rules which apply in this case? Or am I missing something? I use C#7.0 and .NET framework 4.7. I should note that I already learned that the use of C# generics is quite limited as compared to C++ templates...
When PrintMe2
is being compiled, we have to perform overload resolution to determine which PrintMe
method is being called, and we have to emit the correct method token into PrintMe2
s IL to identify that method.
At the point at which PrintMe2
is being compiled, we do not know the nature of its T
type parameter. It could be anything. The only version of PrintMe
that is similarly unrestricted is PrintMe<T>(T elm)
. Therefore, we emit the method token for that specific method into our IL.
Generics are different to templates, most notably because they are compiled separately from any code that may make use of them. We have to make decisions at their compile time, and those decisions have to be valid for all possible type parameters (subject to any type constraints applied to the class/method in which the type parameter(s) are declared)
If you want a method in which overload resolution only takes place based on the runtime type of the passed in parameter, you can use dynamic
, but that's quite ugly and brings in lots of overheads. It may also lead to runtime errors in no specific overload is most specific for some use cases.
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