Understanding C# Covariance And Contravariance (3) Samples

Understanding C# Covariance And Conreavariance:

In part 2, the in and out keywords for generic type parameters are introduced. Actually they can be used not only for generic interfaces, but also for delegates.

In this part, let’s review some samples in .NET 4.0 FCL.

Covariance

In .NET 4.0 FCL, IEnumerator<T> looks updated:

namespace System.Collections.Generic
{
    // Type parameters:
    //   T:
    //     The type of objects to enumerate. This type parameter is covariant, as indicated
    //     by the out keyword. That is, after you specify T, you can use either the
    //     type you specified or any type that is more derived (that is, any class derived
    //     from T). For more information, see Covariance and Contravariance in the Common
    //     Language Runtime.
    //     in the Common Language Runtime.
    public interface IEnumerator<out T> : IDisposable, IEnumerator
    {
        T Current { get; }
    }
}

You may noticed this interface definition includes a property rather than methods. But this is totally Ok. Because a read-only property is just a getter method.

T is only used for output (return value), it is natural to add a out keyword at T. Now IEnumerator<T> supports covariance. Or we say, T of IEnumerator<T> is covariant:

IEnumerator<Base> baseEnumerator = GetBaseEnumerator();
IEnumerator<Derived> derivedEnumerator = GetDerivedEnumerator();

// Covariance: IEnumerator<Derived> "is a" IEnumerable<Base>
baseEnumerator = derivedEnumerator;

Contravariance

Here is IComparable<T>:

namespace System
{
    // Type parameters:
    //   T:
    //     The type of objects to compare. This type parameter is contravariant, as indicated
    //     by the in keyword. That is, after you specify T, you can use either the type
    //     you specified or any type that is less derived (that is, any base class of
    //     T). For more information, see Covariance and Contravariance in the Common
    //     Language Runtime.
    public interface IComparable<in T>
    {
        int CompareTo(T other);
    }
}

The in keyword implies covariance like this can work:

IComparable<Derived> derivedComparable = null;
IComparable<Base> baseComparable = null;

// Contravariance: IComparable<Base> "is a" IComparable<Derived>
derivedComparable = baseComparable;

Covariance and contravariance

The example is the generic delegate, Func<T, TResult>:

namespace System
{
    // Type parameters:
    //   T:
    //     The type of the parameter of the method that this delegate encapsulates. This
    //     type parameter is contravariant, as indicated by the in keyword. That is,
    //     after you specify T, you can use either the type you specified or any type
    //     that is less derived (that is, any base class of T). For more information,
    //     see Covariance and Contravariance in the Common Language Runtime.
    //
    //   TResult:
    //     The type of the return value of the method that this delegate encapsulates.This
    //     type parameter is covariant, as indicated by the out keyword. That is, after
    //     you specify TResult, you can use either the type you specified or any type
    //     that is more derived (that is, any class derived from TResult). For more
    //     information, see Covariance and Contravariance in the Common Language Runtime.
    public delegate TResult Func<in T, out TResult>(T arg);
}

The T of Func<T, TResult> is contra-variant, while TResult is covariant.

Neither covariance nor contravariance

If one type parameter is used for both output and input, it is neither covariant nor contra-variant, just like T of IList<T>:

namespace System.Collections.Generic
{
    public interface IList<T> : ICollection<T>, IEnumerable<T>, IEnumerable
    {
        // T is output, cannot be contravariant.
        T this[int index] { get; set; }

        // T is input, cannot be covariant.
        int IndexOf(T item);

        // Other members.
    }
}

So logically the covariance and contravariance cannot work here, and the in / out keywords for T cannot be used of course.

More samples

This list on MSDN shows more interface and delegate types having covariant and / or contravariant type parameters in .NET 4.0 Beta 1.

Published Monday, August 31, 2009 1:00 PM by Dixin

Comments

No Comments

Leave a Comment

(required) 
(required) 
(optional)
(required)