I have a generic class, on which I define a method that should take arguments of another type, but only if the other type implements the type parameter of the class. However, this won't compile:
class GenericClass<TClass>
{
public void DoSomething<TMethod>(TMethod input) where TClass : TMethod
{
// ...
}
}
I get a compiler error on TClass
in the constraint on the method. Is there another way to specify this?
Clarification:
I am under the impression that TMethod : TClass
means that TMethod
must inherit or implement TClass
(depending on whether TClass
is a concrete type or an interface). In other, slightly unconventional, notation TMethod > TClass
(meaning TMethod
is a superset of TClass
).
What I want here, is that TMethod
should only be a valid type if TClass
inherits or implements it, i.e. TClass > TMethod
. Will TMethod : TClass
do that as well?
(One of the answers states that TMethod : TClass
requires TMethod
to be assignable from TClass
. I'm not sure if that meets my requirements, but if it does, please explain in more detail what being assignable from means, since if it helps me I've probably misunderstood it...)
Chris Hannon's answer is basically correct. However, there are sneaky tricks involving interfaces and extension methods that might get you what you want.
We do occasionally get asked for this sort of constraint, and there are other languages that have constraints like this. Java and I believe Scala both have ways of saying "this has to be a supertype of that". As others have pointed out, this sort of constraint cannot be represented in either the C# type system or the underlying CLR type system.
The reason why you might want this sort of variance would be for a situation like this:
class ImmutableStack<T>
{
public static ImmutableStack<T> Empty { get { return empty; } }
private static ImmutableStack<T> empty = new ImmutableStack<T>();
private T head;
private ImmutableStack<T> tail;
public ImmutableStack<T> Pop()
{
if (this == empty) throw new Exception();
return this.tail;
}
public ImmutableStack<N> Push(N value) where T : N // not legal
{
var result = new ImmutableStack<N>();
result.head = value;
result.tail = this; // also not legal
return result;
}
}
And now you can do something like:
Tiger tony = new Tiger();
Elephant dumbo = new Elephant();
ImmutableStack<Tiger> tigers = ImmutableStack<Tiger>.Empty;
tigers = tigers.Push<Tiger>(tony);
ImmutableStack<Mammal> mammals = tigers.Push<Mammal>(dumbo);
And hey, now you can push an elephant onto a stack of tigers and it magically becomes a stack of mammals!
This is not possible in C# as I've shown here for two reasons: first, because there is no such generic constraint, and second, because immutable class types are not covariant. (The assignment to the tail requires covariance.)
However it is possible to do this in C# if you are sneaky. You can get around the covariance problem by representing everything as a covariant interface rather than a class. You can get around the "no such constraint" problem by moving the method in question out of the class proper and making it into an extension method! I give an example of how to do so here:
http://blogs.msdn.com/b/ericlippert/archive/2007/12/06/immutability-in-c-part-three-a-covariant-immutable-stack.aspx
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