为什么lambda可转换为表达式,而方法组不能

本文关键字:方法 不能 表达式 lambda 可转换 为什么 | 更新日期: 2023-09-27 18:08:08

LINQPad示例:

void Main()
{
    One(i => PrintInteger(i));
    One(PrintInteger);
    Two(i => PrintInteger(i));
    // Two(PrintInteger); - won't compile
}
static void One(Action<int> a)
{
    a(1);
}
static void Two(Expression<Action<int>> e)
{
    e.Compile()(2);
}
static void PrintInteger(int i)
{
    Console.WriteLine(i);
}

取消注释Two(PrintInteger);行导致错误:

不能从'method group'转换为"System.Linq.Expressions.Expression<System.Action&lt>祝辞的

这类似于将方法组转换为表达式,但我对"为什么"感兴趣。我理解功能需要花费金钱、时间和精力;我想知道是否有更有趣的解释。

为什么lambda可转换为表达式,而方法组不能

因为,为了获得表达式树,我们需要以(未编译的)形式表示方法。Lambda表达式在源代码中是本地可用的,因此总是在未编译的情况下可用。但是方法不能来自当前程序集中,因此只能以编译后的形式使用。

当然,c#编译器可以反编译程序集的IL代码来检索表达式树,但正如您提到的,实现功能需要花钱,这个特定的功能不是微不足道的,而且好处还不清楚。

原则上没有理由。可以这样做。编译器可以在转换lambda之前自己创建它(这显然总是可能的——它知道被调用的确切方法,所以它可以从它的参数创建一个lambda)。

但是有一个问题。lambda的参数名通常硬编码到生成的IL中。如果没有lambda,则没有名称。但是编译器可以创建一个虚拟名称,也可以重用被调用的方法的名称(它们在. net程序集格式中总是可用的)。

为什么c#团队没有决定启用这个功能?唯一能想到的原因是他们想把时间花在其他地方。我为他们的决定鼓掌。我宁愿使用LINQ或async而不是这个晦涩的功能。

One示例中,您隐式创建了Action<int>委托。与

相同
One( new Action<int>( PrintInteger ) );

我相信这是为了改进语言中订阅事件的语法。

同样的事情不会发生在Expression<T>,这就是为什么你的第二个例子不能编译。


编辑:

这被称为"方法组转换"。它在c#规范第6.6节

存在从方法组(§7.1)到兼容委托类型的隐式转换(§6.1)