I'm trying to learn Expressions, mainly for my own education. I am trying to work out how to build up an Expression that would represent something more complex than things like a+b
and so on.
I'll take this step by step, so you can see how I'm building it up. Please feel free to comment on any aspect of my approach, although the actual question comes on the third code block.
I understand how to make a function that divides the input by 2:
// Set up a parameter for use below
ParameterExpression x = Expression.Parameter(typeof(double), "x");
Expression two = Expression.Constant((double)2);
Expression halve = Expression.MakeBinary(ExpressionType.Divide, x, two);
// Test it
double halfOfTwenty = Expression.Lambda<Func<double, double>>(halve, x).Compile()(20);
I have also worked out how to make an expression that computes sin(x):
Expression sine = Expression.Call(typeof(Math).GetMethod("Sin"), x);
// Test it
double sinePiOverTwo = Expression.Lambda<Func<double, double>>(sine, x).Compile()(Math.PI / 2);
What I would like to do now is create an expression that computes sin(x/2). I can do this like this...
Expression sineOfHalf = Expression.Call(typeof(Math).GetMethod("Sin"), halve);
...but ideally I would like to reuse my existing sine expression, rather than creating a new one.
I'm sure this is straightforward, but to a newbie in this area, I'm finding it quite difficult. Anyone able to show me how to do this? I've looked through the Expression class, but have obviously overlooked the method that I need.
ParameterExpression x = Expression.Parameter(typeof(double), "x");
Expression two = Expression.Constant((double)2);
Expression halve = Expression.MakeBinary(ExpressionType.Divide, x, two);
Okay, at this point you have something that represents x / 2
and your next step created the lambda expression x => x / 2
. (Incidentally, you could also have used Expression.Divide()
rather than MakeBinary
to be a bit more concise.
Expression sine = Expression.Call(typeof(Math).GetMethod("Sin"), x);
At this point you have an expression that represents Math.Sin(x)
and your next step created the lambda expression x => Math.Sin(x)
.
So what you need to do is to combine the two points you were at before you created a lambda expression each time:
Expression sine = Expression.Call(typeof(Math).GetMethod("Sin"), halve);
And now you can do the final step on that:
Expression.Lambda<Func<double, double>>(sine, x) // x => Math.Sin(x / 2.0)
Entire code:
ParameterExpression x = Expression.Parameter(typeof(double), "x");
Expression two = Expression.Constant((double)2);
Expression halve = Expression.MakeBinary(ExpressionType.Divide, x, two);
Expression sine = Expression.Call(typeof(Math).GetMethod("Sin"), halve);
Expression<Func<double, double>> sineHalveLambda = Expression.Lambda<Func<double, double>>(sine, x);
And to test:
Func<double, double> f = sineHalveLambda.Compile();
Console.WriteLine(f(Math.PI)); // 1
Console.WriteLine(f(0)); // 0
Console.WriteLine(f(-Math.PI)); // -1
Incidentally, it can often be useful to have using static System.Linq.Expressions.Expression;
in your file when working directly with the Expression
class, since you'll use its static members so often, and then it can sometimes help visualise the tree produced if you do it as a one-liner but with indenting reflecting the tree:
ParameterExpression x = Parameter(typeof(double), "x");
Expression<Func<double, double>> sineHalveLambda = Lambda<Func<double, double>>(
Call(
typeof(Math).GetMethod("Sin"),
Divide(
x,
Constant(2.0)
)
)
, x);
Because then the indentation reflects the branches of the expression tree produced. There's a balance here though between the readability advantage in reflecting the tree structure, and the general readability disadvantage of one-liners generally.
Edit: As @Evk points out, I missed the bit in your question that said "I can do this like this…" which is much as the above.
To actually reuse the sine
Expression there are a few possible approaches.
You can use Update
which produces an Expression
based on the Expression
you are working with, with different children. This is heavily used in ExpressionVisitor
s.
You can also create a lambda expression and invoke that lambda in another expression:
ParameterExpression x = Expression.Parameter(typeof(double), "x");
Expression two = Expression.Constant((double)2);
Expression halve = Expression.MakeBinary(ExpressionType.Divide, x, two);
Expression sine = Expression.Call(typeof(Math).GetMethod("Sin"), x);
Expression sineLambda = Expression.Lambda<Func<double, double>>(sine, x);
Expression<Func<double, double>> sineHalfLambda = Expression.Lambda<Func<double, double>>(Expression.Invoke(sineLambda, halve), x);
Func<double, double> sineHalfDelegate = sineHalfLambda.Compile();
Here what you are actually producing isn't x => Math.Sin(x/2)
but rather first a sine
that is x => Math.Sin(x)
and then a second expression that is x => sine(x / 2)
.
Conceptually this means you've got two compiled lambda expressions, but the compiler is able to inline the inner lambda so that what is actually being compiled is back to being x => Math.Sin(x/2)
again, so you don't have the overhead of two separate compilations.
More generally though, it pays to think about what your units of reuse really are. If I wanted to produce several expressions that called Math.Sin
on different expressions' results, I'd probably hold on to the MethodInfo
that typeof(Math).GetMethod("Sin")
returns and use that as my reusable component.
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