Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

C# overload resolution with generics

Tags:

c#

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...

like image 396
Marcel Avatar asked Nov 06 '18 07:11

Marcel


1 Answers

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 PrintMe2s 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.

like image 182
Damien_The_Unbeliever Avatar answered Nov 10 '22 14:11

Damien_The_Unbeliever