I've read this answer and understood from it the specific case it highlights, which is when you have a lambda inside another lambda and you don't want to accidentally have the inner lambda also compile with the outer one. When the outer one is compiled, you want the inner lambda expression to remain an expression tree. There, yes, it makes sense quoting the inner lambda expression.
But that's about it, I believe. Is there any other use case for quoting a lambda expression?
And if there isn't, why do all the LINQ operators, i.e. the extensions on IQueryable<T>
that are declared in the Queryable
class quote the predicates or lambdas they receive as arguments when they package that information in the MethodCallExpression
.
I tried an example (and a few others over the last couple of days) and it doesn't seem to make any sense to quote a lambda in this case.
Here's a method call expression to a method that expects a lambda expression (and not a delegate instance) as its only parameter.
I then compile the MethodCallExpression
by wrapping it inside a lambda.
But that doesn't compile the inner LambdaExpression
(the argument to the GimmeExpression
method) as well. It leaves the inner lambda expression as an expression tree and does not make a delegate instance of it.
In fact, it works well without quoting it.
And if I do quote the argument, it breaks and gives me an error indicating that I am passing in the wrong type of argument to the GimmeExpression
method.
What's the deal? What's this quoting all about?
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());
}
Lambda expressions in C# are used like anonymous functions, with the difference that in Lambda expressions you don't need to specify the type of the value that you input thus making it more flexible to use. The '=>' is the lambda operator which is used in all lambda expressions.
To create a lambda expression, you specify input parameters (if any) on the left side of the lambda operator and an expression or a statement block on the other side. When you use method-based syntax to call the Enumerable. Select method in the System.
The term 'Lambda expression' has derived its name from 'lambda' calculus which in turn is a mathematical notation applied for defining functions. Lambda expressions as a LINQ equation's executable part translate logic in a way at run time so it can pass on to the data source conveniently.
The concept of lambda expression was introduced in C# 3.0. Basically, Lambda expression is a more concise syntax of anonymous method. It is just a new way to write anonymous methods. At compile time all the lambda expressions are converted into anonymous methods according to lambda expression conversion rules.
You have to pass the argument as a 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're passing a constant value, so it has to be a ConstantExpression
. By passing the expression directly, you're explicitly saying "and get the value of exp
from this complicated expression tree". And since that expression tree doesn't actually return a value of Expression<Func<bool>>
, you get an error.
The way IQueryable
works doesn't really have much to do with this. The extension methods on IQueryable
have to preserve all information about the expressions - including the types and references of the ParameterExpression
s and similar. This is because they don't actually do anything - they just build the expression tree. The real work happens when you call queryable.Provider.Execute(expression)
. Basically, this is how the polymorphism is preserved even though we're doing composition, rather than inheritance (/interface implementation). But it does mean that the IQueryable
extension methods themselves cannot do any shortcuts - they don't know anything about the way the IQueryProvider
is actually going to interpret the query, so they can't throw anything away.
The most important benefit you get from this, though, is that you can compose the queries and subqueries. Consider a query like this:
from item in dataSource
where item.SomeRelatedItem.Where(subItem => subItem.SomeValue == 42).Count() > 2
select item;
Now, this is translated to something like this:
dataSource.Where(item => item.SomeRelatedItem.Where(subItem => subItem.SomeValue == 42).Count() > 2);
The outer query is pretty obvious - we'll get a Where
with the given predicate. The inner query, however, is actually going to be a Call
to Where
, taking the actual predicate as an argument.
By making sure that actual invocations of the Where
method are actually translated into a Call
of the Where
method, both of these cases become the same, and your LINQProvider is that one bit simpler :)
I've actually written LINQ providers that don't implement IQueryable
, and which actually have some useful logic in the methods like Where
. It's a lot simpler and more efficient, but has the drawback described above - the only way to handle subqueries would be to manually Invoke
the Call
expressions to get the "real" predicate expression. Yikes - that's quite an overhead for a simple LINQ query!
And of course, it helps you compose different queryable providers, although I haven't actually seen (m)any examples of using two completely different providers in a single query.
As for the difference between Expression.Constant
and Expression.Quote
themselves, they seem rather similar. The crucial difference is that Expression.Constant
will treat any closures as actual constants, rather than closures. Expression.Quote
on the other hand, will preserve the "closure-ness" of the closures. Why? Because the closure objects themselves are also passed as Expression.Constant
:) And since IQueryable
trees are doing lambdas of lambdas of lambdas of [...], you really don't want to lose the closure semantics at any point.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With