Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Overload resolution, extension methods and genericity in C#

I have the following scenario in my C# source:

class A{}

class Dispatch<T>{}

static class DispatchExt
{
    public static void D<T>(this Dispatch<T> d, int a)
    {
         Console.WriteLine("Generic D chosen with a = " + a.ToString());
    }

    public static void D(this Dispatch<A> d, int a)
    {
         Console.WriteLine("D<A> chosen with a = " + a.ToString());
    }
}

class Program
{
     static void D<T>(Dispatch<T> d, int a)
     {
          d.D(a);
     }

     static void Main(string[] args)
     {
         int a = 5;
         var dispatch = new Dispatch<A>();
         dispatch.D(a);
         D(dispatch, a);
     }
}

When I run this code the output is:

"D<A> chosen with a = 5"

"Generic D chosen with a = 5"

That result surprised me, because I was expecting "D<A> chosen with a = 5" in both situations.

I would like to know what are the general overload resolution rules in this scenario, or anything that cause this output. Furthermore, I wonder if there is a way to achieve the first output in both situations.

like image 991
Raul Alonso Avatar asked Sep 26 '22 03:09

Raul Alonso


1 Answers

Extension methods are syntactic sugar that is interpreted at compile-time using information taken from the static type system only.

Taking your first example, you have this:

dispatch.D(a);

dispatch is of type Dispatch<A>, for which an extension method exists. So the compiler translates this into DispatchExt.D(dispatch, a) (the non-generic version).

In your second example, you have this:

d.D(a);

d is of type Dispatch<T>. So this takes the generic extension method DispatchExt.D<T>(d, a).

Since the translation happens at compile-time, the actual run-time type is not taken into account.


This is btw. the same behavior used when determining overloads in other situations: Only the static compile-time type is taken into account:

A a = new A();
B b = new B();
A ba = b;

Test(a); // "a"
Test(b); // "b"
Test(ba); // "a"

Using the following definitions:

public void Test(A a) { Console.WriteLine("a"); }
public void Test(B a) { Console.WriteLine("b"); }
public class A {}
public class B : A {}
like image 159
poke Avatar answered Nov 03 '22 02:11

poke