Variance in C# 4.0

An aspect of generics that often comes across as surprising is that the following is illegal:

IList<string> strings = new List<string>();
IList<object> objects = strings;

The second assignment is disallowed because strings does not have the same element type as objects. There is a perfectly good reason for this. If it were allowed you could write:

objects[0] = 5;
string s = strings[0];

Allowing an int to be inserted into a list of strings and subsequently extracted as a string. This would be a breach of type safety.

However, there are certain interfaces where the above cannot occur, notably where there is no way to insert an object into the collection. Such an interface is IEnumerable<T>. If instead you say:

IEnumerable<object> objects = strings;

There is no way we can put the wrong kind of thing into strings through objects, because objects doesn't have a method that takes an element in. Variance is about allowing assignments such as this in cases where it is safe. The result is that a lot of situations that were previously surprising now just work.

Covariance

In .NET 4.0 the IEnumerable<T> interface will be declared in the following way:

public interface IEnumerable<out T> : IEnumerable
{
           IEnumerator<T> GetEnumerator();
}

public interface IEnumerator<out T> : IEnumerator
{
           bool MoveNext();
           T Current { get; }
}

The "out" in these declarations signifies that the T can only occur in output position in the interface – the compiler will complain otherwise. In return for this restriction, the interface becomes "covariant" in T, which means that an IEnumerable<A> is considered an IEnumerable<B> if A has a reference conversion to B.

As a result, any sequence of strings is also e.g. a sequence of objects.

This is useful e.g. in many LINQ methods. Using the declarations above:

var result = strings.Union(objects); // succeeds with an IEnumerable<object>

This would previously have been disallowed, and you would have had to to some cumbersome wrapping to get the two sequences to have the same element type.

Contravariance

Type parameters can also have an "in" modifier, restricting them to occur only in input positions. An example is IComparer<T>:

public interface IComparer<in T>
{
           public int Compare(T left, T right);
}

The somewhat baffling result is that an IComparer<object> can in fact be considered an IComparer<string>! It makes sense when you think about it: If a comparer can compare any two objects, it can certainly also compare two strings. This property is referred to as contravariance.

A generic type can have both in and out modifiers on its type parameters, as is the case with the Func<…> delegate types:

public delegate TResult Func<in TArg, out TResult>(TArg arg);

Obviously the argument only ever comes in, and the result only ever comes out. Therefore a Func<object,string> can in fact be used as a Func<string,object>.


Download complete features list here:
http://code.msdn.microsoft.com/csharpfuture/Release/ProjectReleases.aspx?ReleaseId=1686