C# Generics and polymorphism: an oxymoron?
Solution 1
As far as I can see, consuming code doesn't need specifics of generic class (i.e., it doesn't depends on what T
is). So, why don't you introduce interface that SomeClass<T>
will implement, and use instance of this interface.
E.g.:
public interface ISome
{
void SomeMethod();
}
public class SomeClass<T>: ISome
{
public virtual void SomeMethod(){ }
}
public void DoSomethingClienty()
{
Factory factory = new Factory();
ISome someInstance = factory.Create();
someInstance.SomeMethod();
}
Now, subclasses of SomeClass<T>
can operate differently on different T
s, but consuming code won't change.
Solution 2
I think you are misunderstanding the point of generics. Generics allows you to generalise a class that requires a type, but doesn't particularly care itself what type that is. For instance, a List<string>
is a list of strings, but what would a List
be? It's a rather useless concept to have a list of nothing.
Each specialised class (ie, List<string>
) is it's own distinct type, and the compiler treats it as such. It is possible to get at the generic type itself (typeof(List<>)
for instance), but in most cases it's useless, and you certainly can't make an instance of it.
Solution 3
I would prefer using abstract class to act as base of all generic types.
public abstract class SomeClass
{
public abstract void SomeMethod();
}
public class SomeClass<T> : SomeClass
{
public override void SomeMethod() { }
}
public class DeriveFrom<String> : SomeClass<String>
{
public override void SomeMethod() { base.SomeMethod(); }
}
Solution 4
It seems that once you introduce a Generic into an inheritance hierarchy or interface, you can no longer use that family of classes in a polymorphic way
Correct, it's quite similar to this situation:
class StringContainer
{
}
class IntContainer
{
}
StringContainer container = new IntContainer(); // fails to compile
but you could do this:
class Container
{
}
class Container<T> : Container
{
}
Container container = new Container<String>(); // OK
Comments
-
Nick Swarr almost 2 years
I just want to confirm what I've understood about Generics in C#. This has come up in a couple code bases I've worked in where a generic base class is used to create type-safe derived instances. A very simple example of what I'm talking about,
public class SomeClass<T> { public virtual void SomeMethod(){ } } public class DeriveFrom :SomeClass<string> { public override void SomeMethod() { base.SomeMethod(); } }
The problem comes up when I then want to use derived instances in a polymorphic way.
public class ClientCode { public void DoSomethingClienty() { Factory factory = new Factory(); //Doesn't compile because SomeClass needs a type parameter! SomeClass someInstance = factory.Create(); someInstance.SomeMethod(); } }
It seems that once you introduce a Generic into an inheritance hierarchy or interface, you can no longer use that family of classes in a polymorphic way except perhaps internal to itself. Is that true?
-
Nick Swarr over 14 yearsI think this is the best solution. We extend only the interface the client cares about and we hide the generics under the hood.
-
Manish Basantani over 14 yearsHave no idea about the implementation of "Factory" mentioned by Nick. But i am quite sure, it can very well be implemented in the abstract non-generic 'SomeClass'.
-
user1126454 about 12 yearsThe above is an example of how polymorphism with generics would be really useful.
-
Elisabeth over 11 years@Amby But what would you do if the abstract void SomeMethod() has a return type of T and a parameter of type T ? Then you can not hide the generics under your non-generic abstract class. I would appreciate an answer or tip from you :)
-
Servadac about 5 years@Elisabeth A lot of time has passed but, have you found out a solution to this approach? I'm dealing with the same problem right now.
-
ricecakebear almost 5 years@Servadac The reason op tries to use polymorphism is that there is some common functionality independent of type parameters, which dictates there must be a "SomeMethod" with a signature void of type parameters.