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
).
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.
namespace Bagnasco.Samples.Variance
{
using System;
class Base { }
class Derived : Base { }
class VarianceAssignmentsSample
{
public void VarianceSample()
{
Base baseInstance = new Base();
Derived derivedInstance = new Derived();
baseInstance = derivedInstance;
Func<Base> funcThatReturnsBase = () => baseInstance;
Func<Derived> funcThatReturnsDerived = () => derivedInstance;
funcThatReturnsBase = funcThatReturnsDerived;
Action<Base> actionThatTakesBase = _ => { };
Action<Derived> actionThatTakesDerived = _ => { };
actionThatTakesDerived = actionThatTakesBase;
Func<Base, Base> functionThatTakesBaseAndReturnsBase = _ => baseInstance;
Func<Base, Derived> functionThatTakesBaseAndReturnsDerived = _ => derivedInstance;
Func<Derived, Base> functionThatTakesDerivedAndReturnsBase = _ => baseInstance;
Func<Derived, Derived> functionThatTakesDerivedAndReturnsDerived = _ => derivedInstance;
functionThatTakesBaseAndReturnsBase = functionThatTakesBaseAndReturnsDerived;
functionThatTakesDerivedAndReturnsBase = functionThatTakesBaseAndReturnsBase;
functionThatTakesDerivedAndReturnsBase = functionThatTakesBaseAndReturnsDerived;
functionThatTakesDerivedAndReturnsBase = functionThatTakesDerivedAndReturnsDerived;
functionThatTakesDerivedAndReturnsDerived = functionThatTakesBaseAndReturnsDerived;
}
}
}
I hope this will help to better understand covariance and contravariance in Generics.