Почему компилятор С# сошел с ума по этому вложенному запросу LINQ?

Попробуйте скомпилировать следующий код, и вы обнаружите, что компилятор занимает > 3 ГБ ОЗУ (вся свободная память на моей машине) и очень долгое время для компиляции (на самом деле я получаю исключение 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

Ответ 1

Я считаю, что это связано с типом вывода и/или генерацией лямбда (когда вывод типа должен идти в обратном направлении к нормальному) в сочетании с разрешением перегрузки. К сожалению, просто поставка параметров типа не помогает ситуации (там, где она, по-видимому, все еще должна выполнять проверку типа).

Следующий код, который должен логически быть эквивалентным кодом из вашего, после анализа lambdas, компилируется без проблем:

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;
}

Я считаю, что Эрик Липперт опубликовал до того, как этот тип является одним из мест в компиляторе С#, где (некоторые проблемы) могут заставить компилятор попытаться решить проблему NP-Complete, и единственная его реальная стратегия (как здесь) грубая сила. Если я найду соответствующие ссылки, я добавлю их здесь.


Лучшая ссылка, которую я могу найти, здесь, где Эрик обсуждает тот факт, что это работа по перегрузке, которая вызывает реальную стоимость, помните, Enumerable.Sum имеет 10 перегрузок, которые принимают лямбда/метод.