Reputation: 4241
I am trying to understand how constraints (on class or generic methods) can affect the methods themselves. Take this code for example:
class Base<T> where T:class, IComparable<T>
{
public virtual void Method(T obj) { }
}
class Derived<T> : Base<T> where T:class, IComparable<T>, IEnumerable<T>
{
public override void Method(T obj) { }
}
This code compiles fine and the compiler/runtime is able to resolve polymorphic calls to the non-generic method 'Method' which takes a parameter that is of generic type. In the base class and the derived class, the constraints on the type parameter are different.
I have also specified a class constraint to keep out value types which may probably cause a problem because a different class is generated for each value-type instantiation while only one such class is instantiated for reference types.
On the other hand, the following code does not compile.
class Base
{
public virtual void Method<T>() where T : class, IComparable<T> { }
}
class Derived : Base
{
public override void Method<T>() where T : class, IComparable<T>, IEnumerable<T> { }
}
The language specification for C# says that the constraints on a generic method are carried over as it is to an overriding method and that it is illegal to specify any constraints as such. I did some searching for it on Google and found out that this is disallowed because of the complexities associated with supporting polymorphic calls (with respect to maintaining a method table and so on). But I still don't understand why it works in Case 1 above which is similar to this except that it is the class which is generic. How is the compiler/runtime able to do it in Case 1 while Case 2 is flagged as a compiler error?
Upvotes: 6
Views: 2968
Reputation: 660024
Let's just consider the question "why is it illegal to add a constraint onto a generic method in a virtual override?" Because that is really straightforward.
class Foo : IComparable<Foo> { ... }
...
Base b = new Derived();
b.Method<Foo>();
First off, should this be illegal? Yes. Calling b.Method<Foo>
actually calls Derived.Method<Foo>
, but Foo
does not meet the contraints. So this must be illegal.
On which line of code should the error be reported? The declaration of Foo
is legal. The conversion of Derived
to Base
is legal. From the compiler's perspective, the call to b.Method<Foo>
is legal; b
is of type Base
and Foo
meets the constraints for Base.Method
. So the error cannot be reported on any of those lines. The only place to report the error is on the line in Base.Method
that has the where
clause that causes the problem. Therefore that kind of where
clause must be illegal, to prevent anyone from ever writing the otherwise-legal program fragment above.
Now what about your first case, where the class is generic? Well, how would you get a Base<Foo>
in that scenario? Certainly not from an instance of Derived<Foo>
, because you can't even make that type in the first place! The equivalent problem program is:
Base<Foo> b = new Derived<Foo>();
b.Method();
Now where should the error be reported? Clearly it can be reported on the line that creates the Derived<Foo>
! So there is no need to make the additional where
clause illegal.
Upvotes: 3
Reputation: 698
In case 1, the method can be used for any object of type T, when T is determined by the class. For any particular class, there is only one type T, so overriding is simple. The fact that you can use a wider set of types T to generate Base<T> than to generate Derived<T> is no problem.
In case 2, the methods can be called for any number of types. However, the types that are allowed in the Derived class are only a subset of the types allowed in Base class, which means you have a "partial override", which makes things a mess.
Upvotes: 4