Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

What is the parameter type for a generic function parameter when called from a base class with 'this'?

Confusing question, I know. Given the following:

    class Test
    {
        public static void GenericFunc<T>(T SomeType)
        {
            System.Console.WriteLine("typeof(T): " + typeof(T).Name);
            System.Console.WriteLine("SomeType.GetType(): " + SomeType.GetType().Name);
        }
    }

    public class BaseType
    {
        public void RunTest() { Test.GenericFunc(this); }
    }

    public class DerivedType : BaseType { }

The following code produces interesting output:

    DerivedType Derived = new DerivedType();
    Derived.RunTest();

    // output:
    // typeof(T): BaseType
    // SomeType.GetType(): DerivedType

However, this behaves as I would expect:

    Test.GenericFunc(new Derived());

    // output:
    // typeof(T): DerivedType
    // SomeType.GetType(): DerivedType

Can anyone help me understand the mechanism here that's causing T to be evaluated as BaseType in the first case?

Thanks in advance!

like image 223
Bill Brooks Avatar asked Dec 01 '22 08:12

Bill Brooks


2 Answers

Because this:

public class BaseType
{
    public void RunTest() { Test.GenericFunc(this); }
}

Is basically equivalent to:

public class BaseType
{
    public void RunTest() { Test.GenericFunc<BaseType>(this); }
}

Therefore the GenericFunc gets instatiated at compile time with T = BaseType. However the this object you are passing in at runtime is the derived type which you get by SomeType.GetType().

In the second case the compiler infers the type as DerivedType directly from the usage and therefore GenericFunc gets instatiated with T = DerivedType.

like image 35
ChrisWue Avatar answered Dec 02 '22 21:12

ChrisWue


Unless the "dynamic" type is involved, overload resolution and type inference are performed at compile time, not at run time.

When overload resolution sees Test.GenericFunc(this); it must deduce the generic type argument that corresponds to the missing generic type parameter. That is an easy problem; it uses the compile time type of this, and inside a method of BaseType, clearly the compile time type of this is always BaseType.

Therefore overload resolution assumes that you intended to call Test.GenericFunc<BaseType>(this).

If you intend this to be worked out a runtime, you can do that in C# 4. Just say Test.GenericFunc((dynamic)this). That will start the overload resolution engine again at runtime, which will then pretend that you had cast this to its runtime type back at compile time.

Yes, that is every bit as expensive as it sounds. If you want semantic analysis at runtime then we're going to have to run a semantic analyzer at runtime.

like image 143
Eric Lippert Avatar answered Dec 02 '22 20:12

Eric Lippert