views:

269

answers:

3

If we have it for interfaces, why dont we have it also for classes? What would be the problem that we would incurr when using it?

Thanks

+2  A: 

Check out this msdn post: Variance in Generic Types (C# Programming Guide)

ntziolis
Any particular reason you posted the VS2005 version of the article? I fixed it.
John Saunders
+25  A: 

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.

Eric Lippert
Great example, Eric. I'm sorry I can't upvote twice.
John Saunders
A: 

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
Ian Ringrose