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);
行会导致错误:
无法转换为&#39;方法组&#39;至 &#39;&System.Linq.Expressions.Expression LT; System.Action&LT; INT&GT;&GT;&#39;
这类似于Convert Method Group to Expression,但我对&#34;为什么感兴趣。&#34;我理解Features cost money, time and effort;我想知道是否有更有趣的解释。
答案 0 :(得分:8)
因为,为了获得表达式树,我们需要在(未编译的)源形式中表示该方法。 Lambda表达式在源代码中是本地可用的,因此始终可用于未编译。但是方法可能不是来自当前程序集内部,因此可能只能以编译形式提供。
当然,C#编译器可以反编译程序集的IL代码来检索表达式树,但正如您所提到的,实现功能需要花钱,这个特殊功能并不简单,而且好处不明确。
答案 1 :(得分:2)
原则上没有理由。它可以这样做。编译器可以在转换之前自己创建lambda(这显然总是可行的 - 它知道被调用的确切方法,因此它可以从其参数创建一个lambda)。
但有一个问题。 lambda参数的名称通常硬编码到正在生成的IL中。如果没有lambda,则没有名字。但编译器可以创建一个虚拟名称,也可以重用被调用方法的名称(它们始终以.NET汇编格式提供)。
为什么C#团队决定不启用此功能?想到的唯一原因是他们想把时间花在别的地方。我赞赏他们做出这个决定。我宁愿拥有LINQ或async而不是这个模糊的功能。
答案 2 :(得分:0)
在One
示例中,您隐式创建了Action<int>
委托。它与:
One( new Action<int>( PrintInteger ) );
我相信这是用于改进订阅事件语法的语言。
同样的事情不会发生在Expression<T>
上,这就是你的第二个例子不能编译的原因。
编辑:
它被称为“方法组转换”。这是在C#规范 - 第6.6节
从方法组(第7.1节)到兼容的委托类型
存在隐式转换(第6.1节)