Search code examples
c#genericslinq-expressions

Why do expressions on generic types reference the constraint type instead of the runtime type?


The easiest way to ask this is to show the example (LinqPad) code that demonstrates the issue at hand:

void Main()
{
    GetProp<IFace>().DeclaringType.Dump(); // iface
    GetProp<C>().DeclaringType.Dump(); // iface
    GetProp().DeclaringType.Dump(); // c
}

public interface IFace { int A { get; set; } }

public class C : IFace { public int A { get; set; } }

public PropertyInfo GetProp<T>() where T : IFace
{
    return ExtractProperty((T x) => x.A);
}

public PropertyInfo GetProp()
{
    return ExtractProperty((C x) => x.A);
}

private PropertyInfo ExtractProperty<T, V>(Expression<Func<T, V>> exp)
{
    return (PropertyInfo) ((MemberExpression) exp.Body).Member;
}

I am intrigued by why GetProp<C> uses returns a property on IFace instead of a property on C. Can anyone explain this behavior? Looking at the IL code, I can see that the generic version of GetProp<T> uses ldtoken on the IFace type, but why is it implemented this way? Can anyone point me to a justification or spec for this behavior?


Solution

  • Because member lookup is done at compile time.

    The compiler binds x.A in your lambda to the A property in the interface.

    This is specified in §7.4 of the spec:

    A member lookup of a name N with K type parameters in a type T is processed as follows:

    • First, a set of accessible members named N is determined:

      • If T is a type parameter, then the set is the union of the sets of accessible members named N in each of the types specified as a primary constraint or secondary constraint (§10.1.5) for T, along with the set of accessible members named N in object.