2012-09-01 44 views
3

下面的第二个测试方法不能编译(不能将lambda表达式转换为目标类型D1)。这是否意味着(非泛型)委托逆转不适用于lambda表达式?C#委托与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)); 
    } 
} 

回答

5

您已经确定了语言中的不一致性。

这在language specification明确地调出:

7.15.1匿名函数签名

[...]相比于方法组转换(6.6节),禁忌方差不支持匿名功能 参数类型。 [...]

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

为什么没有语言的设计者费心支持这个功能吗?

<speculation>

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

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

这显然不适用于除创建委托/表达式树之外的其他用途的方法。您可能需要一个特定的功能签名(与委托人不同),因为它是最合适的,或者它需要它,因为它必须满足一个接口合同。更进一步,当您执行方法组转换时,请考虑您(作为创建委托/表达式树的程序员)不一定“拥有”有问题的方法(它可能在第三方程序集中)。 lambda表达式从来就不是这种情况。

似乎语言设计者认为实现lambda表达式的参数类型的反差并不能证明成本,与方法组不同。

</speculation>

+1

谢谢,这是我正在寻找的参考。而你对于为什么存在不一致的猜测对我来说也是有意义的。 – mtraudt

2

D1除了一个类型为Tiger的参数,但您正在传递一种动物类型。 动物不是老虎 但老虎是动物

+0

动物是不太具体,所以分配函数,它接受一个'Animal'向被指定'Tiger'不应该是一个问题的委托。 'M1'不如'D1'具体。 – BAF

+0

+1。是啊。合作和逆变通常令人难以置信,并且不会像你第一次直觉地想到的那样工作。 –

+0

你的回答如何不适用于方法组转换'D1 func = M1;'? – Ani

相关问题