In order to simplify, let say I have a parent and a child class:
public class MyParent { }
public class MyChild : MyParent { }
And this two function with some code:
public void DoSomethingBy_MyChild(MyChild myChild) { //code }
public void DoSomethingBy_MyParent(MyParent myParent) { //code }
But when I try this unit test of Action<MyChild>
delegate with DoSomethingBy_MyParent
with a MyParent
param the compiler says:
Error CS1503 Argument 1: cannot convert from 'MyParent' to 'MyChild'.
public void UnitTest()
{
Action<MyChild> processor;
processor = DoSomethingBy_MyChild;
processor(new MyChild()); //OK
processor = DoSomethingBy_MyParent;
processor(new MyChild()); //OK
processor = DoSomethingBy_MyParent;
processor(new MyParent()); //Error
}
From Using Variance in Delegates (C#):
When you assign a method to a delegate, covariance and contravariance provide flexibility for matching a delegate type with a method signature. Covariance permits a method to have return type that is more derived than that defined in the delegate. Contravariance permits a method that has parameter types that are less derived than those in the delegate type.
It is fine to assign DoSomethingBy_MyParent
to processor
(a contravariant assignment since MyParent
is less-derived than MyChild
) because anything which is MyChild
is, by definition also MyParent
:
Action<MyChild> processor;
processor = DoSomethingBy_MyParent;
However, what happens when you then try to pass a MyParent
into processor
is effectively
Action<MyChild> processor;
processor(new MyParent());
This is not fine because processor
requires a MyChild
to be passed into it - it cannot be called contravariantly. It doesn't matter that you've assigned DoSomethingBy_MyParent
to it - processor
is declared as Action<MyChild>
so it must receive an instance of MyChild
or a more-derived type.
To put it another way, you have
public void DoSomethingBy_MyChild(MyChild myChild) { //code }
and you wouldn't expect to be able to call it like this:
DoSomethingBy_MyChild(new Parent());
because method calls work covariantly (you can pass in an instance of a more-derived type), not contravariantly (you cannot pass in an instance of a less-derived type).