It looks like in the following case the polymorphism does not work properly I have the following definitions:
interface BaseInterface{}
interface NewInterface:BaseInterface{}
class NewClass:NewInterface{}
class GenericClass<T> where T:BaseInterface
{
public string WhoIAm(T anObject)
{
return TestPolymorphism.CheckInterface(anObject);
}
}
class ImplementedClass:GenericClass<NewInterface>{}
class TestPolymorphism
{
public static string CheckInterface(BaseInterface anInterface)
{
return "BaseInterface";
}
public static string CheckInterface(NewInterface anInterface)
{
return "NewInterface";
}
}
Then when I call :
NewClass nc = new NewClass();
ImplementedClass impClass = new ImplementedClass();
Console.WriteLine("The result is " + impClass.WhoIAm(nc));
I have "The result is BaseInterface"
I was expecting to have "The result is NewInterface" as nc implement BaseClass and NewClass
What would be the best way to get "NewClass" as the result ?
Thanks
Keep in mind with generic methods, that non-virtual method calls are still resolved at compile time of the generic itself, not at the compile time of the realization of the generic.
Thus this:
class GenericClass<T> where T:BaseInterface
{
public string WhoIAm(T anObject)
{
return TestPolymorphism.CheckInterface(anObject);
}
}
Will resolve to the overload that takes a BaseInterface
because that's what you've constrained it to regardless of the type T
actually is.
Generics in C# aren't quite like templates in C++, in C# generics, all reference types share the same code for the generic, thus they all treat the generic type similarly at compile time. This means that any compile time overloads your generic invokes using the generic type placeholder can only go by any constraints you provide on the generic type itself, since generics are compiled before they are actually realized.
That is, your GenericClass<T>
is compiled before any usage of it is considered (which is very different from the way C++ does templates -- both methods have their pros and cons). So, if you have an unconstrained generic (say, just T
) then it is considered object
for the purposes of overloading (roughly speaking), but if you have a constrained generic (say where T : BaseInterface
) then it is considered BaseInterface
for the purposes of overloading.
You'd see something similar in this case:
public static bool UberEquals<T>(T left, T right) where T : class
{
return left == right;
}
So you'd think if you called this by:
var s1 = "ello";
var s2 = "Hello";
UberEquals<string>('H' + s1, s2);
Since T
is type string
then it would call string
s ==
overload, but it doesn't, because you haven't constrained T
, thus at compile time it assumes a least common denominator of object
and uses object
s ==
instead.
Another way to think of this:
BaseInterface bi = new ImplementedClass();
var x = TestPolymorphism.CheckInterface(bi);
X will always say BaseInterface
in the above, since overloads are resolved at compile time, not dynamically at run-time. Very similar to generics, just keep in mind that the generic is compiled before it is realized, so it can only go on whatever base class or interface you constrain it to for the purposes of overload resolution.
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