3

下面的第二种测试方法无法编译(无法将 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));
    }
}
4

2 回答 2

6

您已发现语言不一致。

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

7.15.1 匿名函数签名

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

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

为什么语言设计者不费心支持这个功能?

<speculation>

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

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

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

与方法组不同,语言设计者似乎认为为 lambdas 实现参数类型的逆变换带来的好处并不能证明成本是合理的。

</speculation>

于 2012-09-01T15:35:09.903 回答
2

D1 排除了 Tiger 类型的参数,但您传入的是 Animal 类型。动物不是老虎,但老虎是动物

于 2012-09-01T15:12:02.307 回答