If we have it for interfaces, why dont we have it also for classes? What would be the problem that we would incur when using it?
-
5Post some trivial example of what you're talking about. I can't think of a scenario where I would use this. – Ian P Apr 28 '10 at 21:47
-
2@Ian P: see my answer for an example. – Eric Lippert Apr 30 '10 at 17:13
-
possible duplicate of [Why does C# (4.0) not allow co- and contravariance in generic class types?](http://stackoverflow.com/questions/2541467/why-does-c-sharp-4-0-not-allow-co-and-contravariance-in-generic-class-types) – nawfal Jul 07 '14 at 07:52
2 Answers
Suppose you had a class C<T>
that was covariant in T. What might its implementation look like? T has to be out only. That means that C<T>
cannot have any method that takes a T, any property of type T with a setter, or any field of type T, because fields are logically the same as property setters; T goes in.
Pretty much the only useful thing you could build with a covariant class is something immutable as far as T is concerned. Now, I think it would be awesome to have covariant immutable lists and stacks and whatnot that were class types. But that feature is not so obviously awesome that it would clearly justify the massive expenditure in making the type system natively support covariant immutable class types.
A comment above asked for an example of where this would be useful. Consider the following sketch:
sealed class Stack<out T>
{
private readonly T head;
private readonly Stack<T> tail;
public T Peek() { return head; }
public Stack<T> Pop() { return tail; }
public Stack(T head, Stack<T> tail)
{
this.tail = tail;
this.head = head;
}
}
static class StackExtensions
{
public static Stack<T> Push<T>(this Stack<T> tail, T head)
{
return new Stack<T>(head, tail);
}
public static bool IsEmpty<T>(this Stack<T> stack)
{
return stack == null;
}
}
Suppose you had covariant classes. Now you can say
Stack<string> strings = null;
strings = strings.Push("hello");
strings = strings.Push("goodbye");
Stack<object> objects = strings;
objects = objects.Push(123);
And hey, we just pushed an integer onto a stack of strings, but everything worked out just fine! There's no reason why this couldn't be typesafe. An operation which would violate type safety on a mutable data structure can be safely covariant on an immutable data structure.

- 647,829
- 179
- 1,238
- 2,067
-
8The idea of immutable collections is so useful that Scala has a whole set of immutable collections, and they all support covariance. – Ken Bloom Jul 05 '11 at 08:15
-
6Classes that were covariant with regard to type T would be useful in cases where every instance would receive in its constructor all of the T's or T-creators it would ever need; classes that were contravariant with regard to type T would be most useful in cases where the class would receive in its constructor one or more T-consumers. Such situations are not necessarily rare or arcane, but in most of them consumers of the class could use a covariant/contravariant interface type instead of the class type. – supercat Aug 24 '11 at 20:17
-
-
2@mathk: Extensibility is a feature, and has costs. Designing for extensibility is hard; it requires a lot of research into how users will extend the class in order to make sure that the extensibility points are correct. Therefore *always seal any class that you do not intend to be extended*. – Eric Lippert May 06 '14 at 13:19
-
1I don't really have the same opinion on that, if a user want to extend a class I rather let him do what he want. If it is a silly thing to do at some point he will realize that by himself. Knowing the good OO practice is more valuable then enforcing them. – mathk May 06 '14 at 14:42
-
1@mathk: And do you make all your properties that could be read-only into read-write properties? If a user wants to write a property, let them do it! If that turns out to cause a bug, they'll figure it out when the brakes fail or the lander crashes into the surface of the planet, or whatever the consequence of the bug is. After all, knowing about accessibility is more important than enforcing it. Would you agree with that, or would you make properties that are intended to be read-only into read-only properties? How is that different? – Eric Lippert May 06 '14 at 16:04
-
1Properties are a different matter. They are link to encapsulation. Subclassing is more about changing the behavior. I can see many way to extend the Stack in your example. Suppose you what a distributed Stack, a persistent stack... After all If you don't test your lander before you launch it you deserve the crash. – mathk May 06 '14 at 16:28
-
3@mathk: Sure, and what guarantees that my base class will be useful to build a persistent stack, a distributed stack, etc? What guarantees that a change to the base class in the future will not break a derived class, a derived class that *I do not know even exists*? You are absolutely correct that subclassing is about changing behaviour; what you miss is that *subclassing is also about relying upon services provided by a base class*, services that the base class author might not have *intended* to provide to you. – Eric Lippert May 06 '14 at 16:33
-
2@EricLippert Could you explain, why the class, covariant on T, cannot have readonly fields of type T? In your example these fields are ok and do not break type safety. These fields can be initialized in type constructor, which is always invariant on T and thus can accept values of type T. – Ilya Sep 27 '15 at 00:30
-
3@Ilya: Because the designers of the CLR type system did not create a type system in which classes that have readonly fields of T may be covariant in T. Would it be possible to create such a feature? Sure, it is logically sensible. It just wasn't ever implemented. Just because a feature is possible doesn't make it implemented; someone has to do the work. – Eric Lippert Sep 28 '15 at 14:05
The .NET team along with the C# and VB.NET team has limited resources, the work they have done on co- and contravariance solves most of the real world problem. Type systems are very complex to get right – a solution that works in 99.9999% of cases is not good enough if it leads to unsafe code in the other cases.
I don’t think the cost/time of supporting co- and contravariance specs (e.g. “in”/”out”) on class methods is of a great enough value. I can see very few cases when they would be useable – due to the lack of multiply class inheritance.
Would you rather had waited for another 6 months for .net so as to get this support?
Another way to think of this is that in .net
- Interfaces / delegates – are used to model the conceptual type system of an application
- Class are used to implement the above types
- Class inheritance is used to reduce code duplication while doing the above
- co- and contravariance is about the conceptual type system of an application

- 51,220
- 55
- 213
- 317
-
1Great answer. Can you please elaborate on your last bulleted point: *co- and contravariance is about the conceptual type system of an application*? Why do you say it is about the conceptual? And so what if it is? – CodingYoshi Feb 05 '19 at 19:22