Почему лямбды конвертируются в выражения, но группы методов нет?

Пример 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); приводит к ошибке:

не может конвертировать из группы методов в 'System.Linq.Expressions.Expression < System.Action <INT → '

Это похоже на Преобразование группы методов в выражение, но меня интересует "почему". Я понимаю, что Функции стоят денег, времени и усилий; Мне интересно, есть ли более интересные объяснения.

Ответ 1

Потому что для того, чтобы получить дерево выражений, нам нужно представление метода в (нескомпилированной) исходной форме. Лямбда-выражения локально доступны в исходном коде и поэтому всегда доступны без компиляции. Но методы могут быть не изнутри текущей сборки, и поэтому могут быть доступны только в скомпилированной форме.

Конечно, компилятор С# может декомпилировать код сборки IL для извлечения дерева выражений, но, как вы сказали, реализация функции стоит денег, эта особенность не является тривиальной, и преимущества неясно.

Ответ 2

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

Однако есть один улов. Имя параметра вашей лямбда обычно жестко закодировано в генерируемый IL. Если нет лямбда, нет имени. Но компилятор может либо создать фиктивное имя, либо повторно использовать имена вызываемого метода (они всегда доступны в формате сборки .NET).

Почему команда С# не решила включить это? Единственная причина, которая приходит на ум, - это то, что они хотели провести время в другом месте. Я приветствую их за это решение. Я предпочел бы использовать LINQ или асинхронную функцию, чем эта неясная функция.

Ответ 3

В примере One вы неявно создаете делегат Action<int>. Это то же самое, что:

One( new Action<int>( PrintInteger ) );

Я считаю, что это на языке, чтобы улучшить синтаксис для подписки на события.

То же самое не происходит для Expression<T>, поэтому ваш второй пример не компилируется.


EDIT:

Он назвал "преобразование группы методов". Это в спецификации С# 6.6

Неявное преобразование (§6.1) существует из группы методов (§7.1) в совместимый тип делегата