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

mtr*_*udt 3 c# lambda delegates 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));
    }
}
Run Code Online (Sandbox Code Playgroud)

Ani*_*Ani 6

您已经确定了该语言的不一致性.

这在语言规范中明确调出:

7.15.1匿名功能签名

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

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

为什么语言设计师不支持这个功能呢?

<speculation>

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

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

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

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

</speculation>