Understanding C# Covariance And Contravariance (2) Interfaces

Understanding C# Covariance And Conreavariance:

In C# 4.0+, covariance and contravariance are used for generic interfaces. Covariance and contravariance

An interface  can be viewed as a set of method signatures, for example:

public interface IOut<TOut> // TOut is only used as output.
{
    TOut Out1(); // TOut is covariant for Out1 (Func<TOut>).
</span><span style="background: #f5f5f5; color: #2b91af">TOut </span><span style="background: #f5f5f5; color: black">Out2(</span><span style="background: #f5f5f5; color: blue">object </span><span style="background: #f5f5f5; color: black">@in); </span><span style="background: #f5f5f5; color: green">// TOut is covariant for Out2 (Func&lt;object, TOut&gt;).

</span><span style="background: #f5f5f5; color: #2b91af">TOut </span><span style="background: #f5f5f5; color: black">Out3 { </span><span style="background: #f5f5f5; color: blue">get</span><span style="background: #f5f5f5; color: black">; } </span><span style="background: #f5f5f5; color: green">// TOut is covariant for Out3's getter (Func&lt;object, TOut&gt;).

}

public interface IIn<TIn> // TIn is only used as input. { void In1(TIn @in); // TIn is contravariant for In1 (Action<TIn>).

</span><span style="background: #f5f5f5; color: blue">object </span><span style="background: #f5f5f5; color: black">In2(</span><span style="background: #f5f5f5; color: #2b91af">TIn </span><span style="background: #f5f5f5; color: black">@in); </span><span style="background: #f5f5f5; color: green">// TIn is contravariant for In2 (Func&lt;TIn, object&gt;).

</span><span style="background: #f5f5f5; color: #2b91af">TIn </span><span style="background: #f5f5f5; color: black">In3 { </span><span style="background: #f5f5f5; color: blue">set</span><span style="background: #f5f5f5; color: black">; } </span><span style="background: #f5f5f5; color: green">// TIn is contravariant for In3's setter (Action&lt;TIn&gt;).

}

Covariance

For interface IOut<TOut>, TOut is covariant for all members, so TOut can be made covariant at interface level:

public interface IOut<out TOut> // TOut is covariant for all members of interface.
{
    TOut Out1();
</span><span style="background: #f5f5f5; color: #2b91af">TOut </span><span style="background: #f5f5f5; color: black">Out2(</span><span style="background: #f5f5f5; color: blue">object </span><span style="background: #f5f5f5; color: black">@in);

</span><span style="background: #f5f5f5; color: #2b91af">TOut </span><span style="background: #f5f5f5; color: black">Out3 { </span><span style="background: #f5f5f5; color: blue">get</span><span style="background: #f5f5f5; color: black">; } </span><span style="background: #f5f5f5; color: green">// TOut get_Out3();

}

Then the following interface binding (assignment) works:

public static partial class GenericInterfaceWithVariances
{
    public static void Covariance()
    {
        IOut<Base> baseOut = default(IOut<Base>);
        IOut<Derived> derivedOut = default(IOut<Derived>);
    </span><span style="background: #f5f5f5; color: green">// Covariance: Derived "is a" Base =&gt; IOut&lt;Derived&gt; "is a" IOut&lt;Base&gt;.
    </span><span style="background: #f5f5f5; color: black">baseOut = derivedOut;

    </span><span style="background: #f5f5f5; color: green">// So that, when calling baseOut.Out1, the underlying derivedOut.Out1 executes.
    // derivedOut.Out1 method (Func&lt;Derived&gt;) "is a" baseOut.Out1 method (Func&lt;Base&gt;).
    </span><span style="background: #f5f5f5; color: #2b91af">Base </span><span style="background: #f5f5f5; color: black">out1 = baseOut.Out1();

    </span><span style="background: #f5f5f5; color: green">// When calling baseOut.Out2, the underlying derivedOut.Out2 executes.
    // derivedOut.Out2 (Func&lt;object, Derived&gt;) "is a" baseOut.Out2 (Func&lt;object, Base&gt;).
    </span><span style="background: #f5f5f5; color: #2b91af">Base </span><span style="background: #f5f5f5; color: black">out2 = baseOut.Out2(@in: </span><span style="background: #f5f5f5; color: blue">new object</span><span style="background: #f5f5f5; color: black">());

    </span><span style="background: #f5f5f5; color: green">// Out3 property is getter only. The getter is a get_Out3 method (Func&lt;TOut&gt;).
    // derivedOut.Out3 getter (Func&lt;Derived&gt;) "is a" baseOut.Out3 getter (Func&lt;Base&gt;).
    </span><span style="background: #f5f5f5; color: #2b91af">Base </span><span style="background: #f5f5f5; color: black">out3 = baseOut.Out3;

    </span><span style="background: #f5f5f5; color: green">// So, IOut&lt;Derived&gt; interface "is an" IOut&lt;Base&gt; interface. Above binding always works.
</span><span style="background: #f5f5f5; color: black">}

}

In .NET 4.0+, System.Collections.Generic.IEnumerator<T> is such an interface:

namespace System.Collections.Generic
{
    /// <summary>Supports a simple iteration over a generic collection.</summary>
    /// <typeparam name="T">The type of objects to enumerate.This type parameter is covariant. That is, you can use either the type you specified or any type that is more derived. For more information about covariance and contravariance, see Covariance and Contravariance in Generics.</typeparam>
    public interface IEnumerator<out T> : IDisposable, IEnumerator
    {
        T Current { get; }
    }
}

Contravariance

For interface IIn<TIn>, TIn is contravariant for all members, so TIn can be made contravariant at interface level:

public interface IIn<in TIn> // TIn is contravariant for all members of interface.
{
    void In1(TIn @in);
</span><span style="background: #f5f5f5; color: blue">object </span><span style="background: #f5f5f5; color: black">In2(</span><span style="background: #f5f5f5; color: #2b91af">TIn </span><span style="background: #f5f5f5; color: black">@in);

</span><span style="background: #f5f5f5; color: #2b91af">TIn </span><span style="background: #f5f5f5; color: black">In3 { </span><span style="background: #f5f5f5; color: blue">set</span><span style="background: #f5f5f5; color: black">; } </span><span style="background: #f5f5f5; color: green">// void set_In3(TIn @in);

}

Then the following interface binding works:

public static partial class GenericInterfaceWithVariances
{
    public static void Contravariance()
    {
        IIn<Derived> derivedIn = default(IIn<Derived>);
        IIn<Base> baseIn = default(IIn<Base>);
    </span><span style="background: #f5f5f5; color: green">// Contravariance: Derived "is a" Base =&gt; IIn&lt;Base&gt; "is a" IIn&lt;Derived&gt;.
    </span><span style="background: #f5f5f5; color: black">derivedIn = baseIn;

    </span><span style="background: #f5f5f5; color: green">// When calling derivedIn.In1, the underlying baseIn.In1 executes.
    // baseIn.In1 method (Action&lt;Base&gt;) "is a" derivedIn.In1 method (Action&lt;Derived&gt;).
    </span><span style="background: #f5f5f5; color: black">derivedIn.In1(</span><span style="background: #f5f5f5; color: blue">new </span><span style="background: #f5f5f5; color: #2b91af">Derived</span><span style="background: #f5f5f5; color: black">());

    </span><span style="background: #f5f5f5; color: green">// When calling derivedIn.In2, the underlying baseIn.In2 executes.
    // baseIn.In2 (Func&lt;Base, object&gt;) "is a" derivedIn.In2 (Func&lt;Derived, object&gt;).
    </span><span style="background: #f5f5f5; color: blue">object </span><span style="background: #f5f5f5; color: black">@out = derivedIn.In2(</span><span style="background: #f5f5f5; color: blue">new </span><span style="background: #f5f5f5; color: #2b91af">Derived</span><span style="background: #f5f5f5; color: black">());

    </span><span style="background: #f5f5f5; color: green">// In3 property is setter only. The setter is a set_In3 method (Action&lt;TOut&gt;).
    // baseIn.In3 setter (Action&lt;Base&gt;) "is a" derivedIn.In3 setter (Action&lt;Base&gt;).
    </span><span style="background: #f5f5f5; color: black">derivedIn.In3 = </span><span style="background: #f5f5f5; color: blue">new </span><span style="background: #f5f5f5; color: #2b91af">Derived</span><span style="background: #f5f5f5; color: black">();

    </span><span style="background: #f5f5f5; color: green">// So, IIn&lt;Base&gt; interface "is an" IIn&lt;Derived&gt; interface. Above binding always works.
</span><span style="background: #f5f5f5; color: black">}

}

In .NET 4.0+, System.IComparable<T> is such an interface:

namespace System
{
    /// <summary>Defines a generalized comparison method that a value type or class implements to create a type-specific comparison method for ordering instances.</summary>
    /// <typeparam name="T">The type of objects to compare.This type parameter is contravariant. That is, you can use either the type you specified or any type that is less derived. For more information about covariance and contravariance, see Covariance and Contravariance in Generics.</typeparam>
    public interface IComparable<in T>
    {
        int CompareTo(T other);
    }
}

Covariance and contravariance

A generic interface can have both covariant and contravariance type parameters, for example:

public interface IIn_Out<in TIn, out TOut>
{
    void In(TIn @in);
    TOut Out();
}

Then:

public static partial class GenericInterfaceWithVariances
{
    public static void CovarianceAndContravariance()
    {
        IIn_Out<Derived, Base> derivedIn_BaseOut = default(IIn_Out<Derived, Base>);
        IIn_Out<Base, Derived> baseIn_DerivedOut = default(IIn_Out<Base, Derived>);
    </span><span style="background: #f5f5f5; color: green">// Covariance and contravariance: IIn_Out&lt;Base, Derived&gt; "is a" IIn_Out&lt;Derived, Base&gt;.
    </span><span style="background: #f5f5f5; color: black">derivedIn_BaseOut = baseIn_DerivedOut;
}

}

Invariance

In the following generic interface:

public interface IIn_Out<T>
{
    T Out(); // T is covariant for Out (Func<T>).
</span><span style="background: #f5f5f5; color: blue">void </span><span style="background: #f5f5f5; color: black">In(</span><span style="background: #f5f5f5; color: #2b91af">T </span><span style="background: #f5f5f5; color: black">@in); </span><span style="background: #f5f5f5; color: green">// T is contravaraint for In (Action&lt;T&gt;).

}

T is not covariant for some member, and not contravariant for some other member. So, T cannot be variant at the interface level. In .NET, System.Collections.Generic.IList<T> is such an interface:

namespace System.Collections.Generic
{
    public interface IList<T> : ICollection<T>, IEnumerable<T>, IEnumerable
    {
        T this[int index]
        {
            get; // T is covariant.
            set; // T is contravariant.
        }
    </span><span style="background: #f5f5f5; color: green">// Other members.
</span><span style="background: #f5f5f5; color: black">}

}

Is-a relationship of generic interfaces

The “is-a” relationship can be promoted to generic interfaces (sets of method signatures):

  • Covariance: Derived is a Base => IOut<Derived> "is a" IOut<Base>;
  • Contravariance: Derived is a Base => IIn<Base> "is a" IIn<Derived>;
  • Covariance and contravariance: Derived is a Base => IIn_Out<Base, Derived> "is a" IIn_Out<Derived, Base>.

    2 Comments

    • The example is clear and easy to understand. Thanks.

    • ??? ??? ??? ??? ??? ??? ??? ??? ??? ??? ??? ??? ??? ??? ??? ??? ??? ??? ??? ??? ??? ??? ??? ??? ??? ??? ??? ??? ??? ??? ??? ??? ??? ??? ??? ??? ??? ??? ??? ??? ??? ??? ??? ??? ??? ??? ??? ??? ??? ??? ??? ??? ??? ??? ??? ??? ??? ??? ??? ??? ??? ??? ??? ??? ??? ??? ??? ??? ??? ???

    Comments have been disabled for this content.