Build expression dynamically to call Expression.GreaterThan() - c#

I'm trying to choose the method in Expression dynamically at runtime. For example, I want achieve something similar to what I'm trying below:
ConstantExpression one = Expression.Constant(1);
ConstantExpression two = Expression.Constant(2);
// Here the 'GreaterThan' is the method name received at runtime:
var method = typeof(Expression).GetMethods().Single(mi => mi.Name == "GreaterThan" && mi.GetParameters().Length == 2);
var expr = Expression.Call(method, one, two);
At the last line, I get the error:
System.ArgumentException: 'Expression of type 'System.Int32' cannot be used for parameter of type 'System.Linq.Expressions.Expression' of method 'System.Linq.Expressions.BinaryExpression GreaterThan(System.Linq.Expressions.Expression, System.Linq.Expressions.Expression)''
What I want to do is to build the lambda function by choosing methods in Expression dynamically at run time. Here, the method name will refer to some method that compares to numbers (or strings) depending on the expression method.

What exactly is dynamic? The operation values (i.e. the type of "one" and "two")? Or the type of the operation ("GreaterThan", "LessThen")?
If it's the former, you don't need to do anything as the expression builder will take care of it.
In
Expression.GreaterThan(Expression.Constant(1), Expression.Constant(2));
and
Expression.GreaterThan(Expression.Constant("Some"), Expression.Constant("text"));
the proper greater than operators for ints and strings will be automatically chosen.
If it's the latter you want, i.e. dynamically choosing the operation, you need to write
var expr = method.Invoke(null, new object[] { one, two });
Which means you need to invoke the expression method to get the GreaterThan expression thus yielding the same result as if you had written Expression.GreaterThan(one, two).
Calling Expression.Call on an expression method is akin to creating an expression to create expressions.

I think you should avoid using reflection here and use switch instead to build it. Using this approach allow to get all the benefits of strong typing. See https://www.codeproject.com/Articles/1079028/Build-Lambda-Expressions-Dynamically as an example.

Related

Is there a conceptual reason why calling a method with a dynamic parameter always returns dynamic?

I can imagine this would be a problem with multiple overloads, but (besides Linq) a big part of the code probably has only one overload.
When there is only one overload it can save extra casting boilerplate, avoiding errors like Cannot use a lambda expression as an argument to a dynamically dispatched operation without first casting it to a delegate or expression tree type. in situations.
Of course you could argue that when you add an overload at a later stage the code would i.e. start triggering the above compiler error again. But then again - adding overloads can also break typed behavior (with i.e. multiple interfaces).
Is this just not a priority (for now) or am I missing some conceptual problem? I'm asking this to learn more about language design.
static int MapLolCats(int amountOfLolcats, Func<dynamic,int> mappingFunction)
{
return mappingFunction(amountOfLolcats);
}
// This works as expected
var amountOfLegs = MapLolCats(5, x => x * 4);
// This creates a compilation error (despite you could deduct what is meant, because there is only one overload)
// Cannot use a lambda expression as an argument to a dynamically dispatched operation without first casting it to a delegate or expression tree type
dynamic jsonValue = 5;
var amountOfLegs = MapLolCats(jsonValue, x => x * 4);

How to pass 'out' parameter into lambda expression

I have a method with the following signature:
private PropertyInfo getPropertyForDBField(string dbField, out string prettyName)
In it, I find the associated value prettyName based on the given dbField. I then want to find all properties, if any, that have the name prettyName, so I'm trying to do the following:
IEnumerable<PropertyInfo> matchingProperties =
getLocalProperties().Where(prop =>
prop.Name.Equals(prettyName)
);
However, this gives the following error:
Cannot use ref or out parameter 'prettyName' inside an anonymous method, lambda expression, or query expression
By the point in the method where I'm trying to use prettyName in the Where lambda parameter, prettyName is definitely initialized. I return if prettyName cannot be initialized to a valid value. Is there some trick I could do here to let me use prettyName in the lambda expression?
Edit: I'm using .NET 3.5 if it matters.
Just to clarify. It's possible to use ref/out arguments from a called method in a lambda.
You can also use a ref or out if you specify type of the parameter. Which means sending prettyName as a parameter to the lambda.
(prop, ref string prettyName) => prop.Name.Equals(prettyName);
Where clause takes in only one argument, which is the property element in the list. This is what prevents you from adding an argument to the lambda.
Didn't want to leave people the false impression that you cannot use these arguments in a lambda. You just can't use them by capture.
As the compiler error indicates, it isn't allowed to use out or ref parameters inside lambda expressions.
Why not just use a copy? It's not like the lambda wants to mutate the variable anyway, so I don't see a downside.
string prettyNameCopy = prettyName;
var matchingProperties = getLocalProperties()
.Where(prop => prop.Name == prettyNameCopy);
Alternatively, you can use a local throughout (to evaluate the appropriate name etc.), and assign the outparameter prettyName just before returning from the method. This will probably be more readable if there isn't significant branching within the method.

How to create an Expression Tree to do something similar to the SQL "Like " command

I’m working on some expression tree code written by a colleague and am looking into the possibility of adding additional expressions. It currently supports: equals, not-equals, IsNull etc. I need to add something that will allow it to use a wildcard comparison similar to the SQL “Like” command or using regular expressions. At the moment the code parses an XML file and extracts the data which is then processed using code similar to the line shown below. This is an example of the “Equal” expression. “callExp” is a MemberExpression that basically holds the field name of my table (Entities) and GetConstantExpression gets details about the data I am comparing.
xRet = Expression.MakeBinary(ExpressionType.Equal, callExp, GetConstantExpression(element.Element("Value"), callExp.Type));
What I’m after is a way to create an “Expression” that is similar to the “Like” command. Can this be done using a few lines similar to above or is this going to be more complex? Any good resources that could help in this area?
==================================================================================
New code based on feedback:
I was looking at some examples and tried the following which I was hoping would create me an Expression. It gives me the error shown below. Am I going in the right direction to create a “StartsWith” expression? _entityExp is a ParameterExpression reference to MyClass.
ParameterExpression p = Expression.Parameter(_entityExp.Type, "entity");
MethodInfo method = typeof(string).GetMethod("StartsWith", new[] { typeof(string) });
var containsMethodExp = Expression.Call(p, method, Expression.Constant("root"), p);
Method 'Boolean StartsWith(System.String)' declared on type 'System.String' cannot be called with instance of type 'MyClass'
Expression trees can only represent the same sort of functionality as you get in .NET languages - method calls, property evaluation etc.
The closest you normally get to "like" is to call string.StartsWith, string.EndsWith or string.Contains. If you want to deal with regular expressions instead, you might want to use Regex.IsMatch instead. Either way, this is something which is encapsulated in methods rather than in the "language" of expression trees itself.
Without knowing more about how your expression trees are consumed, it's hard to say exactly what you should do. You could create your own "Like" method which the consumer would notice and handle appropriately, for example... or you could use the existing string/regex methods.
Use Contains, startswith or endswith for like
We can create as below.
Expression.Call(memberExpression, typeof(string).GetMethod("Contains", new[] { typeof(string) }), constantExpression);
Expression.Call(memberExpression, typeof(string).GetMethod("EndsWith", new[] { typeof(string) }), constantExpression);
Expression.Call(memberExpression, typeof(string).GetMethod("StartsWith", new[] { typeof(string) }), constantExpression);

Getting run-time value of a ParameterExpression in a expression tree

I am missing the obvious: How do I access the value of a parameter inside a lambda expression expression tree?
Scenario: For a delegate x I dynamically create a lambda expression with an expression tree body which has the same signature as the delegate x. Inside the lamdba's body, I do some validation, checking, logging stuff (this is just testing code, not production), and then I invoke the original delegate x with the original parameters. If the delegate has a return value, this gets returned as well.
That works quite well (including passing the parameters to the original delegate).
But I am hitting a brick wall if I want to access the original parameter values passed to the delegate/lambda.
pseudo code:
var del = new Func<string, int>(_=> {return 42;});
var paramDefs = Array.ConvertAll<ParameterInfo, ParameterExpression>(del.Method.GetParameters(), _ => { return Expression.Parameter(_.ParameterType, _.Name); });
var variableTest = Expression.Variable(typeof(string), "str");
var expression = Expression.Block(
new [] { variableTest },
// this line assigns the actual run time value (which is what I need) of the parameter to the variable - but I cannot hardcode the index.
//Expression.Assign(variableTest, paramDefs[0])
// this line would assigns the ParameterExpression object (causing a run-time exception since the type of the variable is string) ... I need the _value_ of the first (or nth) parameter.
Expression.Assign(variableTest, Expression.ArrayIndex(Expression.Constant(paramDefs), Expression.Constant(0)))
);
var lamdba = Expression.Lambda(del.GetType(), expression, "foo", paramDefs);
var del2 = lamdba.Compile() as Func<string, int>;
del2("this is a test");
Looks like you confused the expression trees compiler too much (well, I was confused by this code too). I can see what you tried to do: you got an element from an array, then you decided to loop over the array. But you couldn't do array[ParameterExpression], so you used ArrayIndex. But...
But ArrayIndex in fact doesn't return "string". It returns MethodCallExpression. So, in this "Assign" expression you actually have ParameterExpression and MethodCallExpression. The ET compiler is smart enough to compile these expressions and try to assign results. But the result of your MethodCallExpression is ParameterExpression. When you had paramDefs[0], you had ParameterExpression right away and compiler could handle that. But compiling nested expressions is harder and it is totally unclear whether you really want to compile this nested expression or not.
What you can do is to compile and invoke the MethodCallExpression yourself, so you will have ParameterExpression in the Assign expression (as you had before).
It may look like this:
// Replace Assign in your Block expression.
Expression.Assign(variableTest, Expression.Lambda<Func<ParameterExpression>>(Expression.ArrayIndex(Expression.Constant(paramDefs), Expression.Constant(0))).Compile()()),
But it might be very heavy in terms of performance (plus the code is ugly). So, I'd stick with your idea of pulling the loop out of the expression tree.

Is it possible to cast a delegate instance into a Lambda expression?

Here the context for my question:
A common technique is to declare the parameter of a method as a Lambda expression rather than a delegate. This is so that the method can examine the expression to do interesting things like find out the names of method calls in the body of the delegate instance.
Problem is that you lose some of the intelli-sense features of Resharper. If the parameter of the method was declared as a delegate, Resharper would help out when writing the call to this method, prompting you with the x => x syntax to supply as the argument value to this method.
So... back to my question I would like to do the follow:
MethodThatTakesDelegate(s => s.Length);
}
private void MethodThatTakesDelegate(Func<string, object> func)
{
//convert func into expression
//Expression<Func<string, object>> expr = "code I need to write"
MethodThatTakesExpression(expr);
}
private void MethodThatTakesExpression(Expression<Func<string, object>> expr)
{
//code here to determine the name of the property called against string (ie the Length)
}
Everywhere that you're using the term "lambda expression" you actually mean "expression tree".
A lambda expression is the bit in source code which is
parameters => code
e.g.
x => x * 2
Expression trees are instances of the System.Linq.Expressions.Expression class (or rather, one of the derived classes) which represent code as data.
Lambda expressions are converted by the compiler into either expression trees (or rather, code which generates an expression tree at execution time) or delegate instances.
You can compile an instance of LambdaExpression (which is one of the subclasses of Expression) into a delegate, but you can't go the other way round.
In theory it might be possible to write such a "decompiler" based on the IL returned by MethodBase.GetMethodBody in some situations, but currently there are various delegates which can't be represented by expression trees. An expression tree represents an expression rather than a statement or statement block - so there's no looping, branching (except conditionals), assignment etc. I believe this may change in .NET 4.0, though I wouldn't expect a decompilation step from Microsoft unless there's a really good reason for one.
I don't believe it's possible to achieve what you'd like here. From the comments in your code it looks like you are attempting to capture the name of the property which did the assignment in MethodThatTakesExpression. This requires an expression tree lambda expression which captures the contexnt of the property access.
At the point you pass a delegate into MethodThatTakesDelegate this context is lost. Delegates only store a method address not any context about the method information. Once this conversion is made it's not possible to get it back.
An example of why this is not possible is that there might not even be a named method backing a delegate. It's possible to use ReflectionEmit to generate a method which has no name whatsoever and only exists in memory. It is possible though to assign this out to a Func object.
No, it is not possible.

Categories

Resources