Click here to Skip to main content
15,881,757 members
Articles / Programming Languages / C#
Tip/Trick

Assignments According to Covariance and Contravariance in Generics

Rate me:
Please Sign up or sign in to vote.
4.78/5 (4 votes)
11 Sep 2014CPOL 13.4K   9   2
Just a quick sample to clarify what we can do and what we can't

Introduction

Speaking with some younger colleagues, I realized that often the concepts of covariance and contravariance are not completely assimilated.

Referring to the official documentation for the correct explanation, this tip is only meant to facilitate understanding of these concepts through simple examples in a few lines of code, indeed I remember that I really understood these concepts only after I started to read and write some code about it.

How to Manipulate Generic Covariant and Contravariant Delegates (and Interfaces)

For the following examples, we just need to declare two empty classes: Base and Derived (where Derived implements Base).

C#
class Base {  }

class Derived : Base {  }

I considered assignments for delegates with:

  • a covariant type parameter: Func<out TResult>
  • a contravariant type parameter: Action<in T>
  • both covariant and contravariant type parameters: Func<in T, out TResult>

Please note that all samples are with delegates but for interfaces is the same thing.

C#
namespace Bagnasco.Samples.Variance
{
    using System;

    class Base { }

    class Derived : Base { }

    /// <summary>
    /// Sample class just for clarifying variance concepts
    /// (see: http://msdn.microsoft.com/it-it/library/dd799517(v=vs.110).aspx)
    /// </summary>
    class VarianceAssignmentsSample
    {
        public void VarianceSample()
        {
            Base baseInstance = new Base();
            Derived derivedInstance = new Derived();

            //Ok (ordinary polymorphism)
            baseInstance = derivedInstance;

            //Type parameters signature: <out TResult>
            Func<Base> funcThatReturnsBase = () => baseInstance;
            Func<Derived> funcThatReturnsDerived = () => derivedInstance;

            //Ok, mutch like ordinary polymorphism (TResult is covariant)
            funcThatReturnsBase = funcThatReturnsDerived;

            //Not allowed (TResult is more derived in funcThatReturnsDerived and is not contravariant)
            //funcThatReturnsDerived = funcThatReturnsBase;

            //Type parameters signature: <in T>
            Action<Base> actionThatTakesBase = _ => { };
            Action<Derived> actionThatTakesDerived = _ => { };

            //Ok (T is contravariant)
            actionThatTakesDerived = actionThatTakesBase;

            //Not allowed (T is not covariant and is less derived in actionThatTakesBase)
            //actionThatTakesBase = actionThatTakesDerived

            //Type parameters signature: <in T, out TResult>
            Func<Base, Base> functionThatTakesBaseAndReturnsBase = _ => baseInstance;
            Func<Base, Derived> functionThatTakesBaseAndReturnsDerived = _ => derivedInstance;
            Func<Derived, Base> functionThatTakesDerivedAndReturnsBase = _ => baseInstance;
            Func<Derived, Derived> functionThatTakesDerivedAndReturnsDerived = _ => derivedInstance;

            //Ok (TResult is covariant and in functionThatTakesBaseAndReturnsBase is less derived)
            functionThatTakesBaseAndReturnsBase = functionThatTakesBaseAndReturnsDerived;

            //Not allowed
            //(T is not covariant and in functionThatTakesBaseAndReturnsBase is less derived)
            //functionThatTakesBaseAndReturnsBase = functionThatTakesDerivedAndReturnsBase;
            //functionThatTakesBaseAndReturnsBase = functionThatTakesDerivedAndReturnsDerived;

            //Not allowed
            //(TResult is covariant and in functionThatTakesBaseAndReturnsDerived is more derived)
            //functionThatTakesBaseAndReturnsDerived = functionThatTakesBaseAndReturnsBase;            

            //Not allowed
            //(T is contravariant and in functionThatTakesBaseAndReturnsDerived is less derived
            //functionThatTakesBaseAndReturnsDerived = functionThatTakesDerivedAndReturnsDerived;

            //Not allowed
            //(T is contravariant and in functionThatTakesBaseAndReturnsDerived is less derived,
            // TResult is covariant and in functionThatTakesBaseAndReturnsDerived is more derived)
            //functionThatTakesBaseAndReturnsDerived = functionThatTakesDerivedAndReturnsBase;

            //All assignments are allowed
            //(T is contravariant and in functionThatTakesDerivedAndReturnsBase is more derived,
            // TResult is covariant and in functionThatTakesDerivedAndReturnsBase is less derived)
            functionThatTakesDerivedAndReturnsBase = functionThatTakesBaseAndReturnsBase;
            functionThatTakesDerivedAndReturnsBase = functionThatTakesBaseAndReturnsDerived;
            functionThatTakesDerivedAndReturnsBase = functionThatTakesDerivedAndReturnsDerived;

            //Ok (T is contravariant and in functionThatTakesDerivedAndReturnsDerived is more derived)
            functionThatTakesDerivedAndReturnsDerived = functionThatTakesBaseAndReturnsDerived;

            //Not allowed
            //(TResult is covariant and in functionThatTakesDerivedAndReturnsDerived is more derived)
            //functionThatTakesDerivedAndReturnsDerived = functionThatTakesBaseAndReturnsBase;            
            //functionThatTakesDerivedAndReturnsDerived = functionThatTakesDerivedAndReturnsBase;
        }
    }
}

I hope this will help to better understand covariance and contravariance in Generics.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)


Written By
Software Developer (Senior) AlmavivA S.p.A.
Italy Italy
This member has not yet provided a Biography. Assume it's interesting and varied, and probably something to do with programming.

Comments and Discussions

 
GeneralMy vote of 5 Pin
cjb11011-Sep-14 21:54
cjb11011-Sep-14 21:54 
GeneralRe: My vote of 5 Pin
Francesco Bagnasco11-Sep-14 23:17
Francesco Bagnasco11-Sep-14 23:17 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.