Why are you quoting LambdaExpression?

I read this answer and understood from it the specific case that it highlights when you have a lambda inside another lambda, and you don’t want to accidentally the inner lambda also compiles with the outer one. When the outer compiles, you want the inner lambda expression to remain an expression tree. There, yes, it makes sense to quote the inner lambda expression.

But about that, I suppose. Is there any other use case for quoting a lambda expression?

And if that doesn't happen, why are all LINQ statements, that is, extensions to IQueryable<T> declared in the Queryable class, quote the predicates or lambda that they get as arguments when they MethodCallExpression this information in MethodCallExpression .

I tried an example (and several others in the last couple of days), and in this case it makes no sense to quote lambda.

Here, a method invocation expression is used for a method that expects a lambda expression (rather than a delegate instance) as the only parameter.

Then I compile MethodCallExpression , wrapping it inside a lambda.

But this will not compile the internal LambdaExpression (argument to the GimmeExpression method). It leaves the inner lambda expression as an expression tree and does not make it an instance of a delegate.

In fact, it works well without quoting it.

And if I quote the argument, it breaks and gives me an error indicating that I am passing the wrong type of argument to the GimmeExpression method.

What a deal? What it is?

 private static void TestMethodCallCompilation() { var methodInfo = typeof(Program).GetMethod("GimmeExpression", BindingFlags.NonPublic | BindingFlags.Static); var lambdaExpression = Expression.Lambda<Func<bool>>(Expression.Constant(true)); var methodCallExpression = Expression.Call(null, methodInfo, lambdaExpression); var wrapperLambda = Expression.Lambda(methodCallExpression); wrapperLambda.Compile().DynamicInvoke(); } private static void GimmeExpression(Expression<Func<bool>> exp) { Console.WriteLine(exp.GetType()); Console.WriteLine("Compiling and executing expression..."); Console.WriteLine(exp.Compile().Invoke()); } 
+6
source share
1 answer

You must pass an argument as ConstantExpression :

 private static void TestMethodCallCompilation() { var methodInfo = typeof(Program).GetMethod("GimmeExpression", BindingFlags.NonPublic | BindingFlags.Static); var lambdaExpression = Expression.Lambda<Func<bool>>(Expression.Constant(true)); var methodCallExpression = Expression.Call(null, methodInfo, Expression.Constant(lambdaExpression)); var wrapperLambda = Expression.Lambda(methodCallExpression); wrapperLambda.Compile().DynamicInvoke(); } private static void GimmeExpression(Expression<Func<bool>> exp) { Console.WriteLine(exp.GetType()); Console.WriteLine("Compiling and executing expression..."); Console.WriteLine(exp.Compile().Invoke()); } 

The reason should be pretty obvious - you are passing a constant value, so it should be ConstantExpression . By passing this expression directly, you explicitly say "and get the exp value from this complex expression tree." And since this expression tree does not actually return Expression<Func<bool>> , you get an error message.

How IQueryable works, in fact it has no special relation. Extension methods on IQueryable should store all information about expressions - including the types and references of ParameterExpression and the like. That’s because they don’t actually do anything - they just build an expression tree. The real work happens when you call queryable.Provider.Execute(expression) . In principle, this is how polymorphism persists, even if we perform composition rather than inheritance (/ implementation of an interface). But this means that the IQueryable extension methods themselves cannot make any shortcuts - they do not know anything about how IQueryProvider will actually interpret the query, so they cannot throw anything away.

The most important benefit you get from this is that you can compose queries and subqueries. Consider the following query:

 from item in dataSource where item.SomeRelatedItem.Where(subItem => subItem.SomeValue == 42).Count() > 2 select item; 

Now it is translated something like this:

 dataSource.Where(item => item.SomeRelatedItem.Where(subItem => subItem.SomeValue == 42).Count() > 2); 

The external query is pretty obvious - we get Where with the given predicate. The internal query, however, will actually Call before Where , taking the actual predicate as an argument.

After making sure that the actual Where method calls are actually translated into the Where method Calls, both of these cases become the same, and your LINQProvider is that one bit is simpler :)

I actually wrote LINQ providers that do not implement IQueryable , and which do have some useful logic in methods like Where . This is much simpler and more efficient, but it has the drawback described above - the only way to handle subqueries is to manually Invoke Call expressions to get a "real" predicate expression. Yikes is pretty overhead for a simple LINQ query!

And of course, this helps you compile the various requested providers, although I have not actually seen (m) any examples of using two completely different providers in the same request.

As for the difference between Expression.Constant and Expression.Quote , they seem pretty similar. The most important difference is that Expression.Constant will treat any closures as actual constants, not closures. Expression.Quote , on the other hand, will preserve closure closures. What for? Since the closure objects themselves are also passed as Expression.Constant :) And since IQueryable trees make lambdas lambdas lambdas from [...], you really do not want to lose closure semantics at any point.

+4
source

Source: https://habr.com/ru/post/986758/


All Articles