为什么我不能将派生参数类型较少的 lambda 表达式传递给 Action 类型的变量,<T>因为后者在 T 上是逆变的?



假设我有这些类:

class Animal { }
class Cat : Animal { }

然后我像这样声明一个变量:

Action<Cat> c;

现在,Action<T>T的逆变,所以我可以这样做:

void Foo(Animal a) { }
c = Foo;

有道理。但当我这样做时,我得到编译器错误CS16611:

c = (Animal a) => { };

我甚至可以这样做而不会出错:

Action<Animal> b = (Animal a) => { };
c = b;

我在这里错过了什么?

超级短版本:你可以告诉编译器你想用哪种委托类型来使用你的表达式:

c = (Action<Animal>)(a => { });

对了,那一边…

正如madreflection在我写这篇文章时指出的那样(谢谢mad),问题是纯委托不是逆变的。逆变仅对相同泛型类型的泛型委托有效。

考虑这个项目列表:

// 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) => { };

它们看起来基本相同,而且实际上都是功能相同的。遗憾的是,无论它们看起来多么相似,无论void DoNothing(Animal parm) { }方法可以被赋值给它们,编译器都会告诉你它们是不一样的。

那么当我们有一个像(Animal a) => { }这样的lambda表达式时会发生什么呢?以上这些都可以接受,因为编译器足够聪明,知道你在做什么。

现在让我们改变一下:

// 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) => { };

编译器对象在所有这些情况下,因为它的lambda表达式的过渡类型(delegate _expression_type(Animal a))是不可引用转换到各种目标委托类型。

你可以用几种方法来解决这个问题。您可以使用new Action<Cat>(some_animal_action)将一个委托包装成另一个兼容类型的委托。或者您可以简单地告诉编译器从一开始就生成正确的类型,就像我在顶部写的那样。

这些都可以

c = (Action<Animal>)(a => { });
c = new Action<Animal>(a => { });
c = new Action<Animal>((Animal a) => { });

(但不要做new版本,它可能会创建一个嵌套的委托。)

奇怪的是,您还可以使用var在自动类型化变量中捕获输出。这是有效的,因为编译器知道使用Action<...>Func<...>的表达式委托,但只有当委托的类型没有事先完全指定。

var temp = (Animal a) => { };
// 'temp' is of type Actions<Animal> so this works:
c = temp;

相关内容

  • 没有找到相关文章

最新更新