编译器如何从LAMBDA表达式推断委托类型

本文关键字:类型 表达式 LAMBDA 编译器 | 更新日期: 2023-09-27 17:54:01

示例代码

int id = 123;
ThreadPool.QueueUserWorkItem(state => ThreadEntryPoint((int)state), id);
public void ThreadEntryPoint(int uniqueId)
{
   Console.WriteLine("uniqueId=" + uniqueId);
}

从提供的LAMBDA表达式,编译器如何知道它需要创建QueueUserWorkItem(WaitCallback, Object)的实例?

更具体地说:我理解它是在推断委托类型。我不明白的是什么决策树(从高层)是通过选择正确的委托类型实例化?

引用

  • ThreadPool。QueueUserWorkItem
    • ThreadPool.QueueUserWorkItem(WaitCallback, Object)
  • WaitCallback委托
    • QueueUserWorkItem(WaitCallback, Object)
    • QueueUserWorkItem(WaitCallback)

编译器如何从LAMBDA表达式推断委托类型

编译器如何从LAMBDA表达式推断委托类型?

基本上没有。

编译器从名为QueueUserWorkItem的方法组中可用的重载推断委托类型。只有两个重载,只有一个有两个参数,两个重载都使用委托类型WaitCallback

因此,委托类型必须为WaitCallback。确定了这一点后,编译器就可以将lambda表达式编译为一个匿名方法,并实例化为QueueUserWorkItem()方法的参数,调用必要的委托对象来调用该匿名方法。

在更复杂的场景中,编译器将不得不执行一些分析来确定重载的"最佳"匹配,并且该分析可能涉及lambda表达式,从而可以基于lambda表达式消除重载的可能性。

但是编译器不会从lambda表达式开始,直接转到委托类型。要将lambda表达式转换为委托实例,需要为lambda表达式提供一些其他上下文,以确定所需的委托类型,例如对类型化变量的赋值、显式强制转换,或者(如本例)方法重载,其中使用lambda表达式的参数具有与lambda表达式兼容的特定委托类型。

请注意,对于泛型方法仍然存在类型推断,其中lambda用于推断类型参数。Eric Lippert的评论清楚地解释了这一点:

在某些情况下,编译器必须从lambda中推断构造的委托类型。例如,如果我们有M<A, R>(Func<A, R> f)M((string x) => x.Length),那么编译器将首先从lambda形参中推断出Func<string, something>,然后从lambda形参的体中推断出Func<string, int>

我的观点是编译器不会从头开始推断Func<T, TResult>。正如Eric指出的那样,编译器确实使用lambda来推断类型参数,但是编译器仍然需要上下文,其中开放泛型类型是已知的。

要了解更多细节,您应该阅读c#规范。它将详细说明重载解析是如何执行的,以及控制该解析和将lambda表达式与类型匹配的规则。

额外的阅读:
.Net lambda表达式——这个参数是从哪里来的?
为什么不能将匿名方法赋值给var?
在这种重载解析的情况下,为什么编译器不能告诉更好的转换目标?(协方差)
将此委托转换为匿名方法或lambda
并非巧合的是,前三本书包含了Eric Lippert所写的关于该主题和相关问题的精彩讨论,Eric Lippert曾在Visual Studio c#编译器和语言设计团队工作。