10

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);行会导致错误:

无法从“方法组”转换为“System.Linq.Expressions.Expression<System.Action<int>>”

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

4

3 回答 3

9

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

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

于 2013-04-07T18:42:36.310 回答
3

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

不过,有一个问题。lambda 参数的名称通常被硬编码到正在生成的 IL 中。如果没有 lambda,就没有名字。但是编译器可以创建一个虚拟名称或重用被调用方法的名称(它们始终以 .NET 程序集格式提供)。

为什么 C# 团队不决定启用此功能?想到的唯一原因是他们想把时间花在其他地方。我为他们的决定鼓掌。我宁愿使用 LINQ 或异步,也不愿使用这种晦涩难懂的功能。

于 2013-04-07T18:45:47.667 回答
0

One示例中,您正在隐式创建Action<int>委托。它与以下内容相同:

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

我相信这是一种改进订阅事件语法的语言。

不会发生同样的事情Expression<T>,这就是为什么您的第二个示例无法编译的原因。


编辑 :

这称为“方法组转换”。它在 C# 规范中 - 第 6.6 节

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

于 2013-04-07T18:46:54.543 回答