I've encountered something quite surprising when using generic constraints with inheritance. I have an overloaded methods Foo
that differ with parameter - either base or derived class instance. In both cases it's generally just passing the instance to the second pair of overloaded methods - Bar
.
When I call Foo
with base class instance, Bar
overload for the base class is called. When I call Foo
with derived class instance, Bar
overload for the derived class is called. This is clear and expected.
But when I tried to merge Foo
methods into single one GenericFoo
that use generics and constraints, methods are resolved differently - T is resolved correctly, but only base-class overload of Bar
is called.
public class Animal { }
public class Cat : Animal { }
public class AnimalProcessor
{
public static void Foo(Animal obj)
{
Console.WriteLine("Foo(Animal)");
Bar(obj);
}
public static void Foo(Cat obj)
{
Console.WriteLine("Foo(Cat)");
Bar(obj);
}
// new generic method to replace the two above
public static void GenericFoo<T>(T obj)
where T : Animal
{
Console.WriteLine("Foo(generic)");
Bar(obj);
}
public static void Bar(Animal obj)
{
Console.WriteLine("Bar(Animal)");
}
public static void Bar(Cat obj)
{
Console.WriteLine("Bar(Cat)");
}
}
Testing code - two first cases for non-generic old methods, two last for new generic method.
Console.WriteLine("Animal()");
AnimalProcessor.Foo(new Animal());
Console.WriteLine();
Console.WriteLine("Cat()");
AnimalProcessor.Foo(new Cat());
Console.WriteLine();
Console.WriteLine("Animal()");
AnimalProcessor.GenericFoo(new Animal());
Console.WriteLine();
Console.WriteLine("Cat()");
AnimalProcessor.GenericFoo(new Cat());
Console.ReadLine();
And the result - note the difference in type resolved in Bar
:
Animal()
Foo(Animal)
Bar(Animal)
Cat()
Foo(Cat)
Bar(Cat)
Animal()
Foo(generic)
Bar(Animal)
Cat()
Foo(generic)
Bar(Animal)
It looks like the compiler binds all calls from GenericFoo
to the least specific overload, even if all more specific-typed calls are known at compile time. Why is that, what is the reason for such behaviour? Which part of specs defines this?
Per OP's request, comment re-posted as answer:
Generics are not templates. Generic methods are compiled once and their behavior is for the 'most generic' case (in this case, Animal.) This is different from C++ style templating, where the template is compiled separately for each specialization by type.
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