An aspect of generics that often comes across as surprising is that the following is illegal:
IList strings = new List();
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>
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 : IEnumerable
{
IEnumerator GetEnumerator();
}
public interface IEnumerator : 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
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:
public interface IComparer
{
public int Compare(T left, T right);
}
No comments:
Post a Comment