This is a little experiment to get your head around co- and contravariance, as applied to generic interfaces in C#. Covariance allows the result of a computation to vary, while Contravariance allows the input to vary. There are three interfaces in the code sample. The first demonstrates covariance by emulating a function that only returns a result. The second demonstrates contravariance by emulating an action that only takes an input. The third demonstrates both co- and contravariance. Let's look at the code:
None of the methods are implemented, because a) I'm lazy and b) you don't need to see an implementation to witness the co- or contravariance first hand. The next code block shows the phenomena at work:
Notes:
#region Covariance
interface IFunction<out TResult>
{
TResult Invoke();
}
class Function<TResult> : IFunction<TResult>
{
public TResult Invoke()
{
throw new NotImplementedException();
}
}
#endregion
#region Contravariance
interface IAction<in T>
{
void Invoke(T arg);
}
class Action<T> : IAction<T>
{
public void Invoke(T arg)
{
throw new NotImplementedException();
}
}
#endregion
#region Both
interface IFunction<in T, out TResult>
{
TResult Invoke(T arg);
}
class Function<T, TResult> : IFunction<T, TResult>
{
public TResult Invoke(T arg)
{
throw new NotImplementedException();
}
}
#endregion
None of the methods are implemented, because a) I'm lazy and b) you don't need to see an implementation to witness the co- or contravariance first hand. The next code block shows the phenomena at work:
IFunction<object> f1 = new Function<string>();
IAction<string> a1 = new Action<object>();
IFunction<string, object> f2 = new Function<object, string>();
Notes:
- In the case of covariance, the generic argument type of the class is more specific than the interface (covariance: we can always implicitly cast from string to object when reading from the container).
- In the case of contravariance, the generic argument type of the interface is more specific than the class (contravariance: we can always implicitly cast from string to object when writing to the container).
- In the mixed case, we can see covariance for the output and contravariance for the input.