Назначение Func для выражения и наоборот
Я искал выражения, и я немного смутился в некоторых точках
-
Мы можем назначить одно и то же выражение LamdaExpression для выражения и/или Func. Но мы не можем назначить Func для выражения (или выражения для Func). Почему мы не можем это сделать? Я искал, если определен оператор преобразования между Expression и Func, но я не смог найти его.
Func<int, int> sumFunc = i => i + i;
Expression<Func<int, int>> sumExp = i => i + i;
// sumExp = sumFunc; // Cannot convert source type 'System.Func<int,int>' to target type 'System.Linq.Expressions.Expression<System.Func<int,int>>'
// sumFunc = sumExp; // Cannot convert source type 'System.Linq.Expressions.Expression<System.Func<int,int>>' to target type 'System.Func<int,int>'
-
Даже мы не можем назначить LambdaExpression объекту. Опять же, почему мы не можем этого сделать?
// object o = i => i + i; // Cannot convert source type 'lambda expression' to target type 'object'
-
Я думаю, что есть что-то о компиляторе. Если да, можем ли мы написать наши пользовательские типы, которые ведут себя в этой (запутанной) манере и что-то используют.
Ответы
Ответ 1
Что касается Спецификации языка С#, то лямбда-выражение, например
i => i + i
- анонимная функция. Выражение с этой классификацией может быть неявно преобразовано в совместимый тип делегата или тип дерева выражений. Вот почему вы можете написать оба
Func<int, int> sumFunc = i => i + i;
Expression<Func<int, int>> sumExp = i => i + i;
Первый тип делегата, второй - тип дерева выражений. Поскольку не существует неявного преобразования между этими типами, вы не можете назначить sumFunc = sumExp
или наоборот. Но так как дерево выражений sumExp
представляет собой лямбда-выражение, вы можете скомпилировать это выражение в исполняемый делегат и назначить его sumFunc
, потому что это такой совместимый делегат:
sumFunc = sumExp.Compile();
Другое направление невозможно, потому что делегат не может быть легко декомпилирован в дерево выражений.
Причина, по которой вы не можете писать
object o = i => i + i;
заключается в том, что анонимная функция не имеет значения или типа сама по себе, она просто конвертируется в тип делегата или дерева выражений. Вы должны сообщить компилятору, который вы хотите, чтобы вы могли сначала преобразовать его, а затем присвоить результат переменной типа object
:
object sumFuncObject = (Func<int, int>) (i => i + i);
object sumExpObject = (Expression<Func<int, int>>) (i => i + i);
Относительно вашего последнего вопроса: вы можете создавать пользовательские неявные или явные преобразования между сложными типами, чтобы эта "магия" могла применяться к назначениям. Подробнее см. В Руководстве по программированию операций конверсии.
Ответ 2
Фактически оба этих выражения - синтаксический сахар, преобразованный компилятором в разных языковых конструкциях.
Когда вы пишете лямбда-выражение, компилятор делает это: делает функцию-член, которая соответствует вашему выражению lamda, и присваивает ее переменной sumFunc (это не точный код, просто чтобы получить представление):
class Program
{
private static int generatedname(int i)
{
return i + i;
}
static void Main()
{
Func<int, int> sumFunc = generatedname;
}
}
Когда вы пишете дерево выражений, происходит еще большее волшебство. Во время компиляции компилятор преобразует ваше выражение в дерево выражений "construction". Вот так.
class Program
{
static void Main()
{
var addPrm = Expression.Parameter(typeof(int), "i");
Expression<Func<int, int>> sumExp =
Expression.Lambda<Func<int, int>>(
Expression.Add(
addPrm,
addPrm
),
addPrm
);
}
}
Вы видите, что это совершенно разные вещи, поэтому вы не можете просто бросать друг друга.
Ответ 3
Выражение - это абстракция. Это позволяет Linq эффективно создавать SQL-запросы для баз данных, для XML или для другого источника данных. Он по аналогии с абстрактным синтаксическим деревом, он хранит синтаксические элементы запроса, поэтому поставщик Linq может построить запрос.
Если бы он работал так, как вы ожидаете, то ему нужно было бы взять абстрактное синтаксическое дерево функции лямбда и создать дерево объектов выражения. Что ж, это не совсем возможно, насколько я знаю, и что хорошего в любом случае?