[英]Why can't I pass a lambda expression with a less-derived parameter type to a variable of type Action<T> given that the latter is contravariant on T?
Let's say I have these classes:假设我有这些课程:
class Animal { }
class Cat : Animal { }
And then I declare a variable like this:然后我像这样声明一个变量:
Action<Cat> c;
Now, Action<T>
is contravariant on T
, so I can do this:现在,
Action<T>
在T
上是逆变的,所以我可以这样做:
void Foo(Animal a) { }
c = Foo;
That makes sense.这就说得通了。 But when I do this, I get compiler error CS16611 :
但是当我这样做时,我得到编译器错误 CS16611 :
c = (Animal a) => { };
I can even do this with no error:我什至可以毫无错误地做到这一点:
Action<Animal> b = (Animal a) => { };
c = b;
What am I missing here?我在这里错过了什么?
Super short version: you can tell the compiler which delegate type you want it to use for your expression using casting:超短版本:您可以使用强制转换告诉编译器您希望它用于表达式的委托类型:
c = (Action<Animal>)(a => { });
Right, that aside..对了,那一边..
As madreflection pointed out while I was writing this (thanks mad), the problem is that pure delegates are not contravariant.正如 madreflection 在我写这篇文章时指出的那样(感谢 mad),问题在于纯委托不是逆变的。 Contravariance works only for generic delegates of the same generic type.
逆变仅适用于相同泛型类型的泛型委托。
Consider this list of items:考虑这个项目列表:
// Let's start with defining some delegate types:
delegate void AnimalAction(Animal parm);
delegate void MyAction<in T>(T parm);
// Now here are my variables, all working fine:
Action<Animal> actAnimal = (Animal a) => { };
MyAction<Animal> myAnimal = (Animal a) => { };
AnimalAction animalAction = (Animal a) => { };
They all look basically the same, and indeed are all functionally identical.它们看起来基本相同,实际上在功能上也完全相同。 Sadly no matter how similar they look, and no matter the fact that the method
void DoNothing(Animal parm) { }
can be assigned to all of them, the compiler will tell you that they're not the same.遗憾的是,无论它们看起来多么相似,也无论可以将方法
void DoNothing(Animal parm) { }
分配给所有这些,编译器都会告诉您它们并不相同。
So what happens when we have a lambda expression like (Animal a) => { }
?那么当我们有一个像
(Animal a) => { }
这样的 lambda 表达式时会发生什么呢? Well all of the above will accept it because the compiler is bright enough to figure out what you're doing.好吧,以上所有内容都会接受它,因为编译器足够聪明,可以弄清楚您在做什么。
Now let's change it up:现在让我们改变它:
// New delegate for cats
delegate void CatAction(Cat parm);
// New variables, but these all fail:
Action<Cat> actCat = (Animal a) => { };
MyAction<Cat> myCat = (Animal a) => { };
CatAction catAction = (Animal a) => { };
The compiler objects in all of these cases because it the transitional type of the lambda expression ( delegate _expression_type(Animal a)
) is not reference-convertable to the various target delegate types.在所有这些情况下,编译器都反对,因为它是 lambda 表达式的过渡类型 (
delegate _expression_type(Animal a)
) 不可引用转换为各种目标委托类型。
You can get around this in a few ways however.但是,您可以通过几种方式解决这个问题。 You can wrap one delegate in another of compatible type using
new Action<Cat>(some_animal_action)
.您可以使用
new Action<Cat>(some_animal_action)
将一个委托包装在另一个兼容类型中。 Or you can simply tell the compiler to make the right type from the beginning, like I put at the top.或者你可以简单地告诉编译器从一开始就创建正确的类型,就像我放在顶部的那样。
So these all work:所以这些都有效:
c = (Action<Animal>)(a => { });
c = new Action<Animal>(a => { });
c = new Action<Animal>((Animal a) => { });
(But don't do the new
versions, it will probably create a nested delegate.) (但不要做
new
版本,它可能会创建一个嵌套委托。)
Oddly, you can also capture the output in an auto-typed variable using var
.奇怪的是,您还可以使用
var
在自动类型变量中捕获 output。 This works because the compiler knows to use Action<...>
and Func<...>
for expression delegates, but only when the delegate's type is not fully specified beforehand.这是有效的,因为编译器知道将
Action<...>
和Func<...>
用于表达式委托,但前提是委托的类型没有事先完全指定。 So this works too:所以这也有效:
var temp = (Animal a) => { };
// 'temp' is of type Actions<Animal> so this works:
c = temp;
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.