C# 4.0 is the next version of the C# language being shipped with VS 2010. Personally, I'm still coming to terms with Generics as introduced in C# 2.0 and LINQ which came with 3.0. Anders Hejlsberg touched on the Covariance/Contravariance topic in his TechEd presentation in LA last month entitled "The Future of C#".

Evolution of C# - Anders Hejlsberg

 

The terms Invariance, Covariance and Contravariance are not by any means new. Anyone coming from a computer science background will have encountered the terms. Prior to attending Anders' session, I hadn't given this much conscious thought in a long time - we tend to know what we can and can't do within the syntactical constraints of a language - and tend not to question things further. However, in C# 4.0, delegates and interfaces will play nicer when working with generics. So much attention is likely to be focused on the dynamic programming additions in C# 4.0 (mainly in support of LINQ), that this addition may go unnoticed.

Variance in C# 4.0 - Anders Hejlsberg - Anders Hejlsberg

 

Terminology:

Invariant: A return parameter is invariant if we must use the exact match of the type name. In other words, neither covariance nor contravariance is permitted

Covariant: A parameter is covariant if we can use a more derived type as a substitute for the parameter type. In other words, a subclass instance can be used where a parent class instance was expected.

Contravariant: A return value is contravariant if we can assign the return type to a variable of a less derived type than the parameter. In other words, a super class instance can be used where a subclass instance was expected.

Variance in C# 4.0 - Anders Hejlsberg - Anders Hejlsberg

 

Generally, C# supports covariant parameters and contravariant return types. There has always been support for covariance and contravariance in C# - C# 4.0 will just ensure that generic delegates and interfaces will also behave they way we would expect.

Eric Lippert is the developer responsible for this feature of the C# 4.0 release and has an eleven-part blog series on just this topic. I would also recommend reading Charlie Calvert's article on this topic for some code samples.

Side Note: You do not need VS 2010 to experiment with these features - just download the framework to a test VM.

Tags: , ,

.NET 4.0 | C#



Comments (10) -

brabbl
brabbl
6/12/2009 8:52:13 PM #

You mixed it up.

In a subclass parameters can be substituted contravariant and return types can be substituted covariant without affecting type safety.

The opposite is never true. Parameters are never covariant and return types are never contravariant.

agrace
agrace United States
6/12/2009 9:31:13 PM #

Covariant return types are not supported:

srtsolutions.com/.../...int-return-types-in-c.aspx

brabbl
brabbl
6/12/2009 9:53:06 PM #

I guess you don't believe me. That's why you deleted my previous comment.

Parameters can be redefined only in a contravariant manner
and return types only covariant.

Here's an example:
-------------------------------------------------
class BaseClass
{
    public virtual object Method1(string s) { return null; }
}

class SubClass : BaseClass
{
    // return type redefined covariant (from object to string)
    // parameter redefined contravariant (from string to object)
    public override string Method1(object obj) { return null; }
}

BaseClass bc = new BaseClass();
object ret1 = bc.Method1("test");

// Now the same with SubClass.
// The method signature has changed but is still compatible.
bc = new SubClass();
ret1 = bc.Method1("test"); // no compile error.
-------------------------------------------------

agrace
agrace United States
6/12/2009 10:00:09 PM #

Hi Brabbl,

I didn't delete your entry. I'll try and respond as soon as I can. Thanks for the feedback, it's always welcome! Smile

Rgds,
Anthony Smile

brabbl
brabbl
6/12/2009 10:44:04 PM #

Yep, my mistake, sorry.
And I didn't know C# doesn't have covariant return types (I'm from the Java corner).

A counter-example:
contravariant return types, does this work?
-------------------------------------------------
class BaseClass {
virtual string Method1() { return "test"; }
}

class SubClass : BaseClass {
// return type contravariant redefined
override object Method1() { return 15; }
}

string ret1 = new BaseClass().Method1();
ret1 = new SubClass().Method1(); // eeks
-------------------------------------------------

I'm sure this not an example of contravariance in C#. So what would a correct one look like?

agrace
agrace United States
6/12/2009 11:06:59 PM #

Contravariant (acceptable) return value:

object lessDerived = Foo(string moreDerived);

brabbl
brabbl
6/13/2009 1:59:55 AM #

Ok, I was talking about co-/contravariance in aspect of compatible API changes when subclassing and overriding. Which changes are compatible in theory and which are supported by a concrete language. I thought C# 4.0 will introduce more of this kind of variancing ability.

This site explains it very well (better than I ever could):
wapedia.mobi/.../Covariance_and_contravariance_(computer_science)

agrace
agrace United States
6/13/2009 2:02:21 AM #

Here's another neat article Smile
visualstudiomagazine.com/.../...iance-in-c-40.aspx

brabbl
brabbl
6/13/2009 5:09:34 AM #

I've read the article you've linked. I've read it before but I didn't recognize that there is more than one page.

C# 4.0 will introduce co-/contravariance for parameterized interfaces. A generic parameter <out T> denotes a covariant return type (output), <in T> stands for a contravariant parameter (input).

On page 3 of the linked artikel:
> C# 4.0 syntax
> [..]
> The C# 4.0 language specification uses the terms
> "output safe" and "input safe" to describe type
> parameters that are safely covariant or contravariant,
> respectively. Those terms are somewhat more descriptive,
> if less precise, to describe how covariance and
> contravariance work.

So that's it what it is about here. Before C# 4.0 generic parameters where invariant. You were bound to the type specified by yourself or the API when using a parameterized interface.

With C# 4.0 you can define that a parameterized interface uses it's parameter T only as a return type (<out T>). In this case T is typesafe to be covariantly substituted. When such an interface is expected you can use implementations of it parameterized with the expected type of T or ... which is new ... subtypes of that type.

Analogous an interface with an <in T> parameter (T can only appear as a parameter of methods) means it is typesafe to use implementations of that interface parameterized with the given type of T or basetypes of it.

agrace
agrace United States
6/13/2009 5:28:49 AM #

I think it helps to understand the trade-offs they made when creating C# 2.0. Covariant arrays in C# were borrowed from Java despite being flawed. For example (from Jon Skeet's "C# in Depth"):
Animal[] animals = new Cat[5];
animals[0] = new Animal();
This will compile but will blow up at run-time. Generics give us static typing, allowing us to find this type of error at compile time:
List<Animal> animals = new List<Cat>();
animals.Add(new Animal());
Now the compiler will not allow us to convert the cat to an animal in the first line, which is much preferable. C# 4.0 extends this type as safety as you describe yourself.

I think it's better to get a basic understanding of why we are doing what we are doing rather than attempting to memorize a pile of grammar - that's what we have reference books for. There are guys doing this for twenty plus years who can't remember this stuff... what's important is getting the "feel" Smile

Pingbacks and trackbacks (4)+