C# 编译器如何从LAMBDA表达式推断委托类型? 示例代码 问题

C# 编译器如何从LAMBDA表达式推断委托类型? 示例代码 问题,c#,.net,reflection,lambda,C#,.net,Reflection,Lambda,从提供的LAMBDA表达式中,编译器如何知道它需要创建QueueUserWorkItemWaitCallback的实例, 反对 更具体地说:我知道它是在推断委托类型。我不明白的是,从高层选择正确的委托类型进行实例化要经历什么样的决策树 参考资料 ThreadPool.QueueUserWorkItemWaitCallback,对象 QueueUserWorkItemWaitCallback, 对象 QueueUserWorkItemWaitCallback 编译器如何从LAMBDA表达式推断委托

从提供的LAMBDA表达式中,编译器如何知道它需要创建QueueUserWorkItemWaitCallback的实例, 反对

更具体地说:我知道它是在推断委托类型。我不明白的是,从高层选择正确的委托类型进行实例化要经历什么样的决策树

参考资料 ThreadPool.QueueUserWorkItemWaitCallback,对象 QueueUserWorkItemWaitCallback, 对象 QueueUserWorkItemWaitCallback 编译器如何从LAMBDA表达式推断委托类型

从根本上说,事实并非如此

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

因此,委托类型必须是WaitCallback。确定此参数后,编译器可以将lambda表达式编译为匿名方法,并将其实例化为QueueUserWorkItem方法调用所需委托对象以调用该匿名方法的参数

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

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

注意,泛型方法仍然存在类型推断,其中lambda用于推断类型参数。解释得很清楚:


在某些情况下,编译器必须从lambda推断构造的委托类型。例如,如果我们有M,它将详细说明重载解析是如何执行的,以及管理重载解析的规则,并将lambda表达式与类型匹配

补充阅读:
不巧的是,前三篇包含了Eric Lippert撰写的关于该主题和相关问题的精彩讨论,他曾在Visual Studio C编译器和语言设计团队工作。

实际上编译器不会创建任何实例,不会创建QueueUserWorkItem的实例,因为这是一种方法。但是方法QueueUserWorkItem需要一个WaitCallback委托,并且委托本身被指定为没有返回类型void,并且接受一个参数对象,如public delegate void WaitCallbackObject state;。您编写的lambda语句表示匿名方法。因此,此匿名方法将封装在添加到调用列表中的WaitCallback委托中。我始终感兴趣的是停止点,编译器说我无法推断出更好的匹配,因此我必须失败,这在不存在隐式强制转换但存在显式强制转换的情况下很常见。存在两个不同的问题。第一个是重载解析,第二个是泛型方法的类型推断。但是是的,在某些情况下,上下文对可用方法重载的约束不足以让编译器做出判断。C规范详细说明了重载解析和类型推断规则,如果遵循规范中的规则不会产生唯一的结果,则最终会出现错误。@Peter:感谢您提供了一些关于这一工作原理的见解!在某些情况下,编译器必须从lambda推断构造的委托类型。例如,如果我们有MFunc f和Mstring x=>x.Length,那么编译器将首先从lambda参数推断Func,然后从lambda的主体推断Func。但是编译器在任何情况下都不会从lambda推断Func类型,这就是我认为你在这个答案中得到的。@EricLippert:这就是我认为你在这个答案中得到的-是的,这是正确的。非常感谢您的进一步澄清。我将把这一点补充到答案中。
int id = 123;

ThreadPool.QueueUserWorkItem(state => ThreadEntryPoint((int)state), id);

public void ThreadEntryPoint(int uniqueId)
{
   Console.WriteLine("uniqueId=" + uniqueId);
}