I need to create a dynamic linq expression an i started work with many examples. I tested some and some work and some not. In this case i want to create a method that looks like :
public bool Check(int intvar)
{
if ( i > 2 )
return true;
else
return false;
}
Now i have written the following :
LabelTarget returnTarget = Expression.Label("label");
ParameterExpression para = Expression.Parameter(typeof(int), "intvalue");
Expression test = Expression.GreaterThan(para, Expression.Constant(5));
Expression iftrue = Expression.Return(returnTarget, Expression.Constant(true));
Expression iffalse = Expression.Return(returnTarget, Expression.Constant(false));
Expression.IfThenElse(test, iftrue, iffalse);
this.TheExpression = Expression.IfThenElse(test, iftrue, iffalse);
Expression.Lambda<Action<int>>(
this.TheExpression,
new ParameterExpression[] { para }
).Compile()(5);
Now it throws InvalidOperationException:
Cannot jump to the label "label"`
What is wrong ? I only need a return true or false.
You need to change a few things:
Put the return label at the bottom of your function in a block expression, as René suggested. This is where your return statement will jump.
Declare the Lambda as type Func<int, bool>. Since you want a return value, this needs to be a function, not an action.
Declare the returnTarget label as type bool. Since the return value of a block expression is the value of its last statement, the label must be of the correct type.
Provide a default value for the final label (= the return value of your function if the label is reached by normal control flow instead of a return statement).
LabelTarget returnTarget = Expression.Label(typeof(bool));
ParameterExpression para = Expression.Parameter(typeof(int), "intvalue");
Expression test = Expression.GreaterThan(para, Expression.Constant(5));
Expression iftrue = Expression.Return(returnTarget, Expression.Constant(true));
Expression iffalse = Expression.Return(returnTarget, Expression.Constant(false));
var ex = Expression.Block(
Expression.IfThenElse(test, iftrue, iffalse),
Expression.Label(returnTarget, Expression.Constant(false)));
var compiled = Expression.Lambda<Func<int, bool>>(
ex,
new ParameterExpression[] { para }
).Compile();
Console.WriteLine(compiled(5)); // prints "False"
Console.WriteLine(compiled(6)); // prints "True"
If you have simple condition statement like this:
if (condition)
return expression1;
else
return expression2;
You can transform that into ternary expression: condition ? expression1 : expression2.
And then you can create an expression without using Label, Return, or Goto.
Expression condition;
Expression expression1;
Expression expression2;
/* ... */
Expression body = Expression.Condition(
test: condition,
ifTrue: expression1,
ifFalse: expression2);
returnTarget currently is only referenced by your if/then/else statement. The label is not placed in the statement anywhere. So it does not know where to jump to. The label is only defined and referenced, but not placed.
Try using Expression.Block to combine your lambda and your label.
Expression.Lambda<Action<int>>(
Expression.Block(
this.TheExpression,
Expression.Label(returnTarget)
),
new ParameterExpression[] { para }
).Compile()(5);
Haven't tested it, but this is the general direction in which you can find your answer.
-update- tested it, the lambda above compiles and runs just fine as it stands now.
-update2- apparantly, you want to return a value as well, let me have a look, at the least, it should be a Func rather than an Action.
Related
i'm trying to do for an expression tree and try to let it return a simple int value. but it not working anymore
var method = typeof(Console).GetMethod("WriteLine", new Type[] {typeof(string)});
var result = Expression.Variable(typeof(int));
var block = Expression.Block(
result,
Expression.Assign(result,Expression.Constant(0)),
Expression.IfThenElse(Expression.Constant(true),
Expression.Block(Expression.Call(null, method, Expression.Constant("True")),
Expression.Assign(result, Expression.Constant(1))),
Expression.Block(Expression.Call(null, method, Expression.Constant("False")), Expression.Assign(
result, Expression.Constant(0)
))),
result
);
Expression.Lambda<Func<int>>(block).Compile()();
The problem is not with returning a vaue from the block (you are doing it correctly), but the out of scope variable due to the used wrong Expression.Block method overload.
Variable expressions like your result must be passed to the block expression using some of the overloads with IEnumerable<ParameterExpression> variables argument, for instance
var block = Expression.Block(
new ParameterExpression[] { result },
//... (the rest of the sample code unchanged)
);
I need to divide two input double number,and with try catch to handle DivideByZeroException and others.
ParameterExpression dInput1 = Expression.Parameter(typeof(double), "input1");
ParameterExpression dInput2 = Expression.Parameter(typeof(double), "input2");
ParameterExpression e_DBZ = Expression.Parameter(typeof(DivideByZeroException));
ParameterExpression e_ = Expression.Parameter(typeof(Exception));
var _calc = Expression.Divide(dInput1, dInput2);
MethodInfo _consolewrite = typeof(Console).GetMethod("WriteLine",new Type[] { typeof(string) });
MethodCallExpression _output = Expression.Call(null, _consolewrite, Expression.Convert(Expression.Constant(_calc,typeof(double)), typeof(string)));
var _catch_DivideByZeroException = Expression.Catch(e_DBZ, Expression.Call(null, _consolewrite, Expression.Property(e_DBZ, "Message")));
var _catch_Exception = Expression.Catch(e_, Expression.Call(null, _consolewrite, Expression.Property(e_DBZ, "Message")));
var _calc_Block = Expression.Block(_calc, Expression.Constant(0));
var _try = Expression.TryCatch(_calc_Block, _catch_DivideByZeroException, _catch_Exception);
var _lambda = Expression.Lambda<Func<double,double, double>>(_try, dInput1,dInput2);
Console.WriteLine(_lambda.Body);
Console.WriteLine(_lambda.Compile().Invoke(1,0));
But the Expression.Convert throw System.ArgumentException.
You're entire approach doesn't seem right, when you call Expression.Convert and outside you issue Expression.Call()
But nonetheless, here is a working example of Expression.Convert:
// This expression represents a type convertion operation.
Expression convertExpr = Expression.Convert(
Expression.Constant(5.5),
typeof(Int16)
);
// Print out the expression.
Console.WriteLine(convertExpr.ToString());
// The following statement first creates an expression tree,
// then compiles it, and then executes it.
Console.WriteLine(Expression.Lambda<Func<Int16>>(convertExpr).Compile()());
// This code example produces the following output:
//
// Convert(5.5)
// 5
If you dig in into Expression.Convert doc, you will find a table with the following line:
Exceptions
Exception:
ArgumentNullException
Condition:
expression or type is null.
that means that either you're entire expression produces null, or your string is null.
What's wrong with this?
try
{
var e_DBZ = dInput1 / dInput2;
}
catch (Exception e)
{
// Handle Divide by Zero error
Console.WriteLine(e.Message);
}
The following code is a helper I've created for a system which allows administrators to create their own queries on a database. It returns a lambda expression based on the method and value provided.
I am attempting to find a way to pass parameters to the method call - in this example I am using the StartsWith parameter of String, and attempting to set StringComparison.OrdinalIgnoreCase as a parameter.
There will be others parameters required too, depending on the type of the property specified. I'm hoping that understanding the method of supplying the string comparison property will enable me to add the rest later.
The underlying code works correctly, I just need it to be case-insensitive.
I have used this question as a guide, but the solution does not seem applicable here.
Here is the code:
public static class LambdaExpressionHelper<T> {
public static Expression<Func<T, bool>> Build(string propertyName, string method, string propertyValue) {
PropertyInfo propertyInfo = typeof(T).GetProperty(propertyName);
ParameterExpression e = Expression.Parameter(typeof(T), "e");
MemberExpression m = Expression.MakeMemberAccess(e, propertyInfo);
ConstantExpression c = Expression.Constant(propertyValue, m.Type);
MethodInfo mi = m.Type.GetMethod(method, new Type[] { m.Type }, );
// The below caused errors
//object classInstance = Activator.CreateInstance(typeof(T), null);
//object[] paramArray = new object[] { StringComparison.OrdinalIgnoreCase };
//mi.Invoke(classInstance, paramArray);
Expression call = Expression.Call(m, mi, c);
Expression<Func<T, bool>> lambda = Expression.Lambda<Func<T, bool>>(call, e);
return lambda;
}
}
// External code:
var lambda = LambdaExpressionHelper<MailingListMember>.Build("EmailAddress", "StartsWith", "RoRy#");
Thanks for any help.
In the general case this should work:
public static Expression<Func<T, bool>>
Build(string propertyName, string method, params object[] args)
{
var propertyInfo = typeof(T).GetProperty(propertyName);
var e = Expression.Parameter(typeof(T), "e");
var m = Expression.MakeMemberAccess(e, propertyInfo);
var mi = m.Type.GetMethod(method, args.Select(a => a.GetType()).ToArray());
var c = args.Select(a => Expression.Constant(a, a.GetType())).ToArray();
Expression call = Expression.Call(m, mi, c);
Expression<Func<T, bool>> lambda = Expression.Lambda<Func<T, bool>>(call, e);
return lambda;
}
The idea is that you accept any number of constant arguments, and use their types to select the appropriate overload when calling GetMethod. After that, you create the appropriate number of constant expressions from those arguments to pass to Expression.Call.
So with the above code, you could do:
var l1 = LambdaExpressionHelper<MailingListMember>.Build(
"EmailAddress", "StartsWith", "RoRy#");
var l2 = LambdaExpressionHelper<MailingListMember>.Build(
"EmailAddress", "StartsWith", "RoRy#", StringComparison.OrdinalIgnoreCase);
If you also need to get the value StringComparison.OrdinalIgnoreCase from the string "StringComparison.OrdinalIgnoreCase", I would factor this out into a separate method so that the interface of Build can remain generic. Exactly how to do it is covered in Getting Enum value via reflection (and I guess in lots of similar questions as well).
Note: I don't have convenient access to a compiler right now, so please excuse any mistakes.
I'm not sure i understand your question exactly, but hopefully this will help you on the way.
If you are using .NET 4.0 you can use the new keyword "dynamic" for much easier reflection code:
dynamic myDynamicObj = "Hello World!"; // or Activator.CreateInstance...
var doesIndeed = myDynamicObj.StartsWith("Hello", StringComparison.OrdinalIgnoreCase);
This does evaluate at run time so make sure spelling/case etc is correct.
Edit:
Assuming you always wanted to call methods with one String-arg returning bool, a possible solution would be to create a delegate type -
delegate bool CompareString(String str);
and than have the second argument of Build as that type:
Build(String .., CompareString cs, String ...)
But this does not work if you need to add extra arguments, as in the second arg of type StringComparison. A if/switch could be the answer there though, i.e if(CompareString is StartsWith)...
Sorry, not at a windows-computer so i can't test further.
Is it possible to write code like the following. I'm trying to using Moq with objects that I'm reflecting on as part of a testing framework. The code below raises a "Unhandled expression type: 'Goto'" exception from Moq, which I guess is expecting something different. It kind of looks like it should work though!
private void button1_Click(object sender, EventArgs e)
{
Ifoo = foo Foo();
// Create input parameter for lambda
ParameterExpression value = Expression.Parameter(typeof(IFoo), "value");
// create return statement for lambda
Expression setupProperty = Expression.Return(Expression.Label(), Expression.Property(value, "Bar"), typeof(string));
// convert expression to lambda (should now be the equivalent of "v => v.Bar")
var func = Expression.Lambda<Func<IFoo, string>>(setupProperty, value);//.Compile();
//string s = func(foo); // this bit works fine if .Compile() is included
var mockFoo = new Mock<IFoo>();
mockFoo.SetupProperty(func); // exception thrown by moq here, obviously isn't exactly the same as "v => v.Bar"
mockFoo.Object.Bar = "Burge+";
}
Thanks!
Ok, this is possible, here is the corrected code.
// Create input parameter for lambda
ParameterExpression value = Expression.Parameter(typeof(IFoo), "value");
// create return statement for lambda
Expression setupProperty = Expression.Property(value, "Bar");
// convert expression to lambda (should now be the equivalent of "v => v.Bar")
var func = Expression.Lambda<Func<IFoo, string>>(setupProperty, value);
var mockFoo = new Mock<IFoo>();
mockFoo.SetupProperty(func); // this works now
mockFoo.Object.Bar = "Burge+";
I investigated this by creating an expression from a lambda using the code below
Expression<Func<IFoo, string>> setupBar = v => c.Bar;
I then looked at this in the debugger in vs 2010. Expressions have a "Debug View" that shows a text representation of the expression so it is possible to add a watch on that or something similar. The above comes out as
.Lambda #Lambda1<System.Func`2[WindowsFormsApplication1.IFoo,System.String]>(WindowsFormsApplication1.IFoo
$v) {
$v.Bar
}
I looked at this and tried to work out what Expressions would make this, then created an expression and compared it in the debugger.
The interesting thing for me is that although this expression returns a value there is no assignment or return statement. I guess this must be implicit somehow.
I have a fairly simple expression parser that uses the Linq.Expression namespace.
Input is something like: (1+1), it finds the left and right constants, and converts the operator char to an ExpressionTypes enum value, creates the appropriate expression, compiles and executes.
I'd really like to be able to do string manipulations, (abc+def) would evaluate to abcdef for instance, however:
System.InvalidOperationException: The binary operator Add is not defined for the types 'System.String' and 'System.String'
How would I go about implementing this myself?
Something like the equavlant to an ExpressionTypes.Concat would be ideal.
You have to create the MethodCallExpression yourself, which in this case is for the static method string.Concat. (This is what the compiler does itself when you compile such code)
Here's some code that demonstrates using MethodInfo to extend the definition of the binary expression Add to concatenation of strings.
One example uses the existing Concat method in the String type.
The second example uses a custom Concat method in the Program type (the class this is all embedded in):
private MethodInfo ConcatMethod = typeof(string).GetMethod("Concat", new Type[] { typeof(string), typeof(string) });
private MethodInfo ConcatMethod2 = typeof(Program).GetMethod("Concat", new Type[] { typeof(string), typeof(int) });
public static string Concat(string obj1, int obj2)
{
return string.Concat(obj1, obj2.ToString());
}
private Expression SomeCode(string leftStr, string rightStr)
{
Expression left = Expression.Constant(leftStr);
Expression right = Expression.Constant(rightStr);
return Expression.Add(left, right, ConcatMethod);
}
private Expression SomeCode(string leftStr, int rightInt)
{
Expression left = Expression.Constant(leftStr);
Expression right = Expression.Constant(rightInt);
return Expression.Add(left, right, ConcatMethod2);
}
static void CheesyTest2()
{
Program foo = new Program();
Expression exp = foo.SomeCode("abc", "def");
LambdaExpression lambdaExpression = Expression.Lambda(exp);
Delegate func = lambdaExpression.Compile();
object result = func.DynamicInvoke();
Console.WriteLine(string.Format("Expression result: {0}", result));
exp = foo.SomeCode("abc", 42);
lambdaExpression = Expression.Lambda(exp);
func = lambdaExpression.Compile();
result = func.DynamicInvoke();
Console.WriteLine(string.Format("Expression2 result: {0}", result));
}
For custom operators it's actually a method call so you need to find the corresponding method and create the expression tree for calling that method. Haven't work much with expression trees so im affraid I can't give you a code sample but I hope this Helps none the less