C#委托与lambda表达式的逆向性

时间:2012-09-01 15:07:09

标签: c# delegates lambda contravariance method-group

下面的第二个测试方法无法编译(无法将lambda表达式转换为目标类型D1)。这是否意味着(非泛型)委托逆向不适用于lambda表达式?

[TestFixture]
public class MyVarianceTests
{
    private abstract class Animal {}
    private class Tiger : Animal {}

    private delegate Type D1(Tiger tiger);

    private static Type M1(Animal animal)
    {
        return animal.GetType();
    }

    [Test]
    public void ContravariantDelegateWithMethod()
    {
        D1 func = M1;
        Type result = func(new Tiger());
        Assert.AreEqual(result, typeof (Tiger));
    }

    [Test]
    public void ContravariantDelegateWithLambda()
    {
        D1 func = (Animal animal) => animal.GetType();
        Type result = func(new Tiger());
        Assert.AreEqual(result, typeof (Tiger));
    }
}

2 个答案:

答案 0 :(得分:6)

您发现该语言存在不一致。

这在language specification

中明确调出
  

7.15.1匿名功能签名

     

[...]与方法组转换(第6.6节)相比,匿名函数的反方差   不支持参数类型。 [...]

......提出了一个问题:

语言设计师为何不支持此功能?

<speculation>

显然,该功能有一些小的好处。但它是否证明合规编译器实现所需的额外复杂性的成本是合理的?

当你编写一个lambda表达式时,你必须已经确切地知道它被转换为哪个委托/表达式树类型(没有可以“保持”任意lambda的通用类型) 。从C#5开始,lambda(与方法相反)除了帮助创建单个委托/表达式树实例之外,完全没有任何用途。因此,显式指定比参数所需的更通用的类型并期望来自编译器的反方差支持没有优势(除了方便)。您可以完全省略该类型并依赖于类型推断(或者,最坏的情况,明确指定所需的确切参数类型),而不会丢失可重用性或可表达性。

这显然不适用于除了创建委托/表达式树之外的其他目的的方法。您可能需要特定的函数签名(不同于委托),因为它是最合适的,或 require 它,因为它必须满足接口契约。此外,考虑到您(作为创建委托/表达式树的程序员)在执行方法组转换时不一定“拥有”有问题的方法(可能在第三方程序集中)。 lambdas永远不会这样。

与语法组不同,语言设计者认为实施lambdas参数类型的反差方法的好处并不能证明成本是合理的。

</speculation>

答案 1 :(得分:2)

D1除了Tiger类型的参数外,你传递的是一种Animal类型。 动物不是老虎 但老虎是一个动物