#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.
No comments:
Post a Comment