Почему лямбды конвертируются в выражения, но группы методов нет?
Пример 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) в совместимый тип делегата