Why do explicit C# interface calls within a generic method that has an interface type constraint always call the base implementation?
For example, consider the following code:
public interface IBase
{
string Method();
}
public interface IDerived : IBase
{
new string Method();
}
public class Foo : IDerived
{
string IBase.Method()
{
return "IBase.Method";
}
string IDerived.Method()
{
return "IDerived.Method";
}
}
static class Program
{
static void Main()
{
IDerived foo = new Foo();
Console.WriteLine(foo.Method());
Console.WriteLine(GenericMethod<IDerived>(foo));
}
private static string GenericMethod<T>(object foo) where T : class, IBase
{
return (foo as T).Method();
}
}
This code outputs the following:
IDerived.Method
IBase.Method
Instead of what one might expect:
IDerived.Method
IDerived.Method
There seems to be no way (short of reflection) to call a hidden, more derived explicit interface implementation of a type decided at runtime.
EDIT: To be clear, the following if check evaluates to true in the GenericMethod call above:
if (typeof(T) == typeof(IDerived))
So the answer is not that T is always treated as IBase due to the generic type constraint "where T : class, IBase".
The key here is to remember that IBase.Method
and IDerived.Method
are two completely different methods. We just happened to give them similar names and signatures. Since anything that implements IDerived
also implements IBase
that means it will have two methods named Method
taking no parameters. One belongs to IDerived
and one belongs to IBase
.
All the compiler knows when compiling GenericMethod
is that the generic parameter will implement at least IBase
, so it can only guarantee that the IBase.Method
implementation exists. So that's the method that's called.
Unlike C++ templates, the generic substitution doesn't happen whenever the method is compiled (which with templates would happen once for every combination of template parameters that's used). Instead the method is compiled exactly once in such a way that any type can be substituted at runtime.
In your case the compiler emits IL for GenericMethod
that looks something like this:
IL_0000: ldarg.0
IL_0001: isinst <T>
IL_0006: unbox.any <T>
IL_000B: box <T>
IL_0010: callvirt IBase.Method
IL_0015: ret
Notice it explicitly calls IBase.Method
. There's no virtual/override relationship between that method and IDerived.Method
so the base is all that's called, regardless of what type gets substituted for T in the runtime.
Adding to Kyle's answer, which I cannot do in a comment because I do not have sufficient reputation yet...
I think this is telling:
private static string GenericMethod<T>(T foo) where T : class, IBase
{
return foo.Method() + " " + typeof(T) + " " + typeof(Foo);
}
Removing object and having the parameter be a T, so the as-cast is unnecessary still calls the IBase.Method.
I’m pretty sure this is all directly due to 4.4.4 Satisfying Constraints in the C# specification.
C# generics do not behave like C++ templates in this regard.
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