为什么 C # 编译器对这个嵌套的 LINQ 查询如此疯狂?

尝试编译以下代码,您会发现编译器需要 > 3GB 的 RAM (我的机器上所有的空闲内存)和很长的时间来编译(实际上我得到 IO 异常后10分钟)。

using System;
using System.Linq;


public class Test
{
public static void Main()
{
Enumerable.Range(0, 1).Sum(a =>
Enumerable.Range(0, 1).Sum(b =>
Enumerable.Range(0, 1).Sum(c =>
Enumerable.Range(0, 1).Sum(d =>
Enumerable.Range(0, 1).Sum(e =>
Enumerable.Range(0, 1).Sum(f =>
Enumerable.Range(0, 1).Count(g => true)))))));
}
}

有人能解释一下这种奇怪的行为吗?

CS Version:     Microsoft (R) Visual C# Compiler version 4.0.30319.17929
OS Name:        Microsoft Windows 7 Ultimate
OS Version:     6.1.7601 Service Pack 1 Build 7601

Memory Usage

2141 次浏览

I believe that it's related to type inference and/or lambda generation (when type inference has to go in the opposite direction to normal), combined with overload resolution. Unfortunately, just supplying the type parameters doesn't help the situation (where it presumably still has to perform the type checking).

The following code, which should logically be the equivalent code from yours, after lambdas have been analyzed, compiles without issue:

static void Main()
{
var x = Enumerable.Range(0, 1).Sum(a);
}


private static int a(int a)
{
return Enumerable.Range(0, 1).Sum(b);
}
private static int b(int b)
{
return Enumerable.Range(0, 1).Sum(c);
}
private static int c(int c)
{
return Enumerable.Range(0, 1).Sum(d);
}
private static int d(int d)
{
return Enumerable.Range(0, 1).Sum(e);
}
private static int e(int e)
{
return Enumerable.Range(0, 1).Sum(f);
}
private static int f(int f)
{
return Enumerable.Range(0, 1).Count(g);
}
private static bool g(int g)
{
return true;
}

I believe Eric Lippert has posted before that type inference is one of the places in the C# compiler where (certain problems) may force the compiler to try to solve an NP-Complete problem and its only real strategy (as here) is brute force. If I can find the relevant references, I'll add them here.


The best reference I can find is here where Eric's discussing the fact that it's the overload resolution work that causes the real cost - remember, Enumerable.Sum has 10 overloads that accept a lambda/method.