Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

.NET 4.0 Covariance

In response to another question I have tried to do the following. I don't think I interpreted that question correctly, but I do wonder if the below is possible somehow (my attempts have failed) and if not why not:

    public class MyBaseClass { }

    public class MyClass : MyBaseClass { }

    public class B<T> { }

    public class A<T> : B<T> { }

    static void Main(string[] args)
    {
        // Does not compile
        B<MyBaseClass> myVar = new A<MyClass>();
    }

I thought this might be made to work using a generic interface with a covariant type parameter:

    interface IB<out T> { }

    public class B<T> : IB<T> { }

but I was wrong about that, that does not work either.

EDIT

As SLaks has pointed out 'Interfaces are covariant; classes are not.' (thanks SLaks). So now my question is why? What was the thinking behind the design (one for Eric Lippert I think) is it not possible, undesirable or is it on a 'maybe one day' list?

like image 613
Myles McDonnell Avatar asked Feb 21 '12 16:02

Myles McDonnell


People also ask

What is covariant in C#?

Covariance in C# Covariance enables you to pass a derived type where a base type is expected. Co-variance is like variance of the same kind. The base class and other derived classes are considered to be the same kind of class that adds extra functionalities to the base type.

What is meant by covariance and contravariance?

Covariance and contravariance are terms that refer to the ability to use a more derived type (more specific) or a less derived type (less specific) than originally specified. Generic type parameters support covariance and contravariance to provide greater flexibility in assigning and using generic types.

What is covariance and contravariance in generics in Java?

Covariance can be translated as "different in the same direction," or with-different, whereas contravariance means "different in the opposite direction," or against-different. Covariant and contravariant types are not the same, but there is a correlation between them.

What is covariance in Scala?

Covariance allows assigning an instance to a variable whose type is one of the instance's generic type; i.e. supertype. Contravariance allows assigning an instance to a variable whose type is one of the instance's derived type; i.e. subtype.


3 Answers

What was the thinking behind the design decision to not implement variance on generic class types? Is it not possible, undesirable or is it on a 'maybe one day' list?

You are in good company; Jon Skeet and Bill Wagner asked me that same question a couple weeks ago. I have been working up a blog post on it, but briefly:

The right person to ask for a definitive answer is Andrew Kennedy at Microsoft Research Cambridge, who designed and implemented much of the generic and variance logic originally. However, I can hazard an educated guess as to why we decided to eschew variance on generic classes.

The short version is: Safe covariance of T requires that operations on T be "read only". Contravariance of T requires that operations on T be "write only". You have a class C<T> which you wish to be variant in T. Suppose `C has a field of type T: would you like to have that field be only readable or only writable? Because those are your choices!

Under what circumstances is it even vaguely useful to have a field that can be written to but not read? Not many. Under what circumstances is it useful to have a field that can be read but not written to? Only if the field is marked readonly.

In short, contravariant generic classes are almost never useful because you can't read any generic data from them, and covariant classes are mostly only useful if the class is an immutable data type.

I am a big fan of immutable data types and I think it would be a great feature to be able to make a covariant immutable stack without having to get an interface involved. But covariant generic persistent immutable functional data structures are not exactly mainstream in C# yet, and they certainly were not when generics were added to the CLR. Moreover, we have no supporting infrastructure other than readonly fields to express the notion "this is an immutable data type" in C# or the CLR; if we were to do covariant class types for immutable classes, it would be nice to do lots of features that support immutable classes, not just this obscure one.

So I can see how this feature did not make the cut in CLR 2.0 / C# 2.0. If we were designing it again today, when functional-style programming is somewhat more popular, maybe it would. But we have no plans to do so any time soon.

I'll write a blog post in the next few months giving a more detailed answer.

like image 93
Eric Lippert Avatar answered Oct 01 '22 07:10

Eric Lippert


Interfaces are covariant; classes are not.

A<MyClass> can be converted to IB<MyBaseClass>.

like image 45
SLaks Avatar answered Oct 01 '22 08:10

SLaks


The direct class to class conversion doesn't work, but if you use your IB interface for the variable type you're good to go. As per this MSDN covariance FAQ blog post and the MSDN page on Covariance and Contravariance, variant type parameters are restricted to generic interface and generic delegate types.

public class MyBaseClass  {}

public class MyClass : MyBaseClass {}

interface IB<out T>{}

public class B<T> : IB<T> {}

public class A<T> : B<T> {}

static void Main(string[] args)
{
    IB<MyBaseClass> myVar = new A<MyClass>();
}
like image 35
Rich Visotcky Avatar answered Oct 01 '22 07:10

Rich Visotcky