Какова цель класса Expression?

#c# #lambda #expression-trees

#c# #лямбда #деревья выражений

Вопрос:

Мне интересно, в чем именно разница между переносом делегата внутрь Expression<> и нет?

Я вижу Expression<Foo> , что его часто используют с LinQ, но пока я не нашел ни одной статьи, которая объясняла бы разницу между этим и просто использованием делегата.

Например.

 Func<int, bool> Is42 = (value) => value == 42;
  

против.

 Expression<Func<int, bool>> Is42 = (value) => value == 42;
  

Ответ №1:

tl; dr, иметь выражение — это все равно что иметь исходный код приложения, а делегат — это исполняемый файл для запуска приложения. Выражение можно рассматривать как «источник» (т. Е. синтаксическое дерево) кода, который будет выполняться. Делегат — это конкретная компиляция, которую вы бы запустили и выполнили.


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

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

Также намного проще создать делегат динамически, используя выражения, чем он выдает код. Вы можете представить свой код на более высоком уровне как выражения, что очень похоже на то, как компилятор просматривает код вместо перехода на низкий уровень и просмотра вашего кода как инструкций IL.

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

Ответ №2:

Func<> это просто тип делегата. Выражение — это представление полного дерева операций во время выполнения, которое, при необходимости, может быть скомпилировано во время выполнения в делегат. Именно это дерево анализируется анализаторами выражений, такими как Linq-to-SQL, для генерации операторов SQL или других умных вещей. Когда вы присваиваете лямбда-выражение типу выражения, компилятор генерирует это дерево выражений, а также обычный IL-код. Подробнее о деревьях выражений.

Ответ №3:

Чтобы проиллюстрировать другие ответы, если вы скомпилируете эти 2 выражения и посмотрите на сгенерированный компилятором код, вот что вы увидите:

Func<int, bool> Is42 = (value) => value == 42;

 Func<int, bool> Is42 = new Func<int, bool>((@value) => value == 42);
  

Expression<Func<int, bool>> Is42 = (value) => value == 42;

 ParameterExpression[] parameterExpressionArray;
ParameterExpression parameterExpression = Expression.Parameter(typeof(int), "value");
Expression<Func<int, bool>> Is42 = Expression.Lambda<Func<int, bool>>(Expression.Equal(parameterExpression, Expression.Constant(42, typeof(int))), new ParameterExpression[] { parameterExpression });
  

Комментарии:

1. Он спрашивал, в чем различия, и сгенерированный код не требует пояснений к различиям.

Ответ №4:

Деревья выражений позволяют вам проверять код внутри выражения, в вашем коде.

Например, если вы передали это выражение: o => o.Name , ваш код может обнаружить, что доступ к Name свойству осуществляется внутри выражения.

Ответ №5:

Предоставляет базовый класс, из которого выводятся классы, представляющие узлы дерева выражений.

 System.Linq.Expressions.BinaryExpression
System.Linq.Expressions.BlockExpression
System.Linq.Expressions.ConditionalExpression
System.Linq.Expressions.ConstantExpression
System.Linq.Expressions.DebugInfoExpression
System.Linq.Expressions.DefaultExpression
System.Linq.Expressions.DynamicExpression
System.Linq.Expressions.GotoExpression
System.Linq.Expressions.IndexExpression
System.Linq.Expressions.InvocationExpression
System.Linq.Expressions.LabelExpression
System.Linq.Expressions.LambdaExpression
System.Linq.Expressions.ListInitExpression
System.Linq.Expressions.LoopExpression
System.Linq.Expressions.MemberExpression
System.Linq.Expressions.MemberInitExpression
System.Linq.Expressions.MethodCallExpression
System.Linq.Expressions.NewArrayExpression
System.Linq.Expressions.NewExpression
System.Linq.Expressions.ParameterExpression
System.Linq.Expressions.RuntimeVariablesExpression
System.Linq.Expressions.SwitchExpression
System.Linq.Expressions.TryExpression
System.Linq.Expressions.TypeBinaryExpression
System.Linq.Expressions.UnaryExpression
  

http://msdn.microsoft.com/en-us/library/system.linq.expressions.expression.aspx

Дерево выражений представляет выражение linq, которое может быть проанализировано и, например, преобразовано в SQL-запрос.

Ответ №6:

К тому, что написал другой (это абсолютно правильно) Я добавлю, что с помощью Expression класса вы можете создавать новые методы во время выполнения. Существуют некоторые ограничения. Не все, что вы можете сделать на C #, можно сделать в Expression дереве (по крайней мере, в .NET 3.5 . С .NET 4.0 они добавили большое количество возможных Expression «типов»). Это может быть использовано (например) для создания динамического запроса и передачи его в LINQ-to-SQL или для выполнения некоторой фильтрации на основе ввода пользователя… (вы всегда могли бы сделать это с помощью CodeDom, если бы все, что вам было нужно, это динамический метод, несовместимый с LINQ-to-SQL, но выдавать прямой IL-код довольно сложно :-))