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);
}
Related
I have the "problem" with the following expression tree that it creates a local variable inside the "Try" instead of setting the value directly for the LabelTarget. It's not necessarily a problem, yet I would like to understand why this happens and if I can somehow prevent the creation of the local variable as well.
Expression Tree:
var returnTarget = Expression.Label(typeof(string), "value");
var instanceArgument = Expression.Parameter(typeof(object), "instance");
var instanceAsType = Expression.Convert(instanceArgument, propertyInfo.DeclaringType);
var propertyValueAsType = Expression.Property(instanceAsType, propertyInfo.Name);
var exceptionParameter = Expression.Parameter(typeof(Exception), "exception");
Expression tryCatch = Expression.TryCatch(
Expression.Label(
returnTarget,
Expression.Call(
typeof(Enum).GetMethod("GetName", new[] { typeof(Type), typeof(object) }),
Expression.Constant(propertyInfo.PropertyType),
Expression.Convert(propertyValueAsType, typeof(object)))),
Expression.Catch(
exceptionParameter,
Expression.Label(
returnTarget,
Expression.Property(
exceptionParameter,
"Message"))));
return Expression.Lambda<Func<object, object>>(tryCatch, instanceArgument);
Resulting code:
public static object getValue_Enum(object instance)
{
string message;
try
{
// This should not happen. Instead, it should directly assign the value to "message"
string name = Enum.GetName((RuntimeType)typeof(Program.TestEnum), ((Program.Test2)instance).get_Enum());
message = name;
}
catch (Exception exception)
{
message = exception.Message;
}
return message;
}
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)
);
So I've been tinkering with Linq.Expressions (and if anyone can suggest a more proper or more elegant way to do what I'm doing please feel free to chime in) and have hit a wall in trying to do something.
Let's imagine we have a simple math class:
public class SimpleMath {
public int AddNumbers(int number1, int number2) {
return number1 + number2;
}
}
I decide I want to convert our AddNumbers method to a simple Func<object, object, object> delegate.
To do this I do the following:
// Two collections, one for Type Object paramaters and one for converting to Type int.
List<ParameterExpression> parameters = new List<ParameterExpression>();
List<Expression> convertedParameters = new List<Expression>();
// Populate collections with Parameter and conversion
ParameterExpression parameter1 = Expression.Parameter(typeof(object));
parameters.Add(parameter1);
convertedParameters.Add(Expression.Convert(parameter1, typeof(int)));
ParameterExpression parameter2 = Expression.Parameter(typeof(object));
parameters.Add(parameter2);
convertedParameters.Add(Expression.Convert(parameter2, typeof(int)));
// Create instance of SimpleMath
SimpleMath simpleMath = new SimpleMath();
// Get the MethodInfo for the AddNumbers method
MethodInfo addNumebrsMethodInfo = simpleMath.GetType().GetMethods().Where(x => x.Name == "AddNumbers").ToArray()[0];
// Create MethodCallExpression using the SimpleMath object, the MethodInfo of the method we want and the converted parameters
MethodCallExpression returnMethodWithParameters = Expression.Call(Expression.Constant(simpleMath), addNumebrsMethodInfo, convertedParameters);
// Convert the MethodCallExpression to return an Object rather than int
UnaryExpression returnMethodWithParametersAsObject = Expression.Convert(returnMethodWithParameters, typeof(object));
// Create the Func<object, object, object> with our converted Expression and Parameters of Type Object
Func<object, object, object> func = Expression.Lambda<Func<object, object, object>>(returnMethodWithParametersAsObject, parameters).Compile();
object result = func(20, 40); // result = 60
So if you run that code func should return simple calculations. However, it accepts parameters of Type Object, which obviously leaves it open to problems at runtime, for example:
object result1 = func(20, "f"); // Throws InvalidCastException
So I want to wrap the method in a Try...Catch (obviously this exact problem would be picked up at compile time if we were dealing with a straight call to AddNumbers and passing a string as a parameter).
So to catch this exception I can do the following:
TryExpression tryCatchMethod = TryExpression.TryCatch(returnMethodWithParametersAsObject, Expression.Catch(typeof(InvalidCastException), Expression.Constant(55, typeof(object))));
Func<object, object, object> func = Expression.Lambda<Func<object, object, object>>(tryCatchMethod, parameters).Compile();
object result = func(20, "f"); // result = 55
The TryExpression.TryCatch takes an Expression body and then a collection of CatchBlock handlers. returnMethodWithParametersAsObject is the expression we wish to wrap, Expression.Catch defines that the Exception we want to catch is of Type InvalidCastException and its Expression body is a constant, 55.
So the exception is handled, but it's not much use unless I want to always return a static value when the exception is thrown. So returning to the SimpleMath class I add a new method HandleException:
public class SimpleMath {
public int AddNumbers(int number1, int number2) {
return number1 + number2;
}
public int HandleException() {
return 100;
}
}
And following the same process above I convert the new method to an Expression:
MethodInfo handleExceptionMethodInfo = simpleMath.GetType().GetMethods().Where(x => x.Name == "HandleException").ToArray()[0];
MethodCallExpression returnMethodWithParameters2 = Expression.Call(Expression.Constant(simpleMath), handleExceptionMethodInfo);
UnaryExpression returnMethodWithParametersAsObject2 = Expression.Convert(returnMethodWithParameters2, typeof(object));
Then using it when creating the TryCatch block:
TryExpression tryCatchMethod2 = TryExpression.TryCatch(returnMethodWithParametersAsObject, Expression.Catch(typeof(InvalidCastException), returnMethodWithParametersAsObject2));
Func<object, object, object> func = Expression.Lambda<Func<object, object, object>>(tryCatchMethod2, parameters).Compile();
object result = func(20, "f"); // result = 100
So this time when the InvalidCastException is thrown the SimpleMath.HandleException method will be executed. So far so good, I can now execute some code when there is an exception.
My problem now is, in a normal inline Try...Catch block you actually have the exception object at your disposal. E.g.
try {
// Do stuff that causes an exception
} catch (InvalidCastException ex) {
// Do stuff with InvalidCastException ex
}
I can execute code when an exception is thrown but I can't seem to figure out how to actually get my hands on the exception object like you would in a normal Try...Catch block.
Any help would be appreciated!
p.s. I know that you wouldn't actually organise anything in the way I've done above but I thought it was necessary for example purposes to show the mechanics of what I want to do.
You need to pass your catched exception to CatchBlock expression as parameter.
For this you should do:
Change signature of HandleException. It will takes a exception as argument:
public int HandleException(InvalidCastException exp)
{
// Put here some real logic. I tested it using line below
Console.WriteLine(exp.Message);
return 100;
}
Use CatchBlock.Variable to pass your handled exception into catch block. You can set it use constructor. Read comment in the code below:
// Create parameter that will be passed to catch block
var excepParam = Expression.Parameter(typeof(InvalidCastException));
MethodInfo handleExceptionMethodInfo = simpleMath.GetType().GetMethods().Where(x => x.Name == "HandleException").ToArray()[0];
MethodCallExpression returnMethodWithParameters2 = Expression.Call(Expression.Constant(simpleMath), handleExceptionMethodInfo, excepParam);
UnaryExpression returnMethodWithParametersAsObject2 = Expression.Convert(returnMethodWithParameters2, typeof(object));
// Put created parameter before to CatchBlock.Variable using Expression.Catch
// that takes the first argument as ParameterExpression
TryExpression tryCatchMethod2 = TryExpression.TryCatch(returnMethodWithParametersAsObject, Expression.Catch(excepParam, returnMethodWithParametersAsObject2));
var exppp = Expression.Lambda<Func<object, object, object>>(tryCatchMethod2, parameters);
Func<object, object, object> func2 = Expression.Lambda<Func<object, object, object>>(tryCatchMethod2, parameters).Compile();
object result2 = func2(20, "f"); // result = 100
#GeorgeAlexandria's answer is fully correct. But I found it very hard work to decode it all when I came to do the same thing.
Perhaps the following code (written as 2 helper methods), will help the next person who needs to do something like this ...
private Expression WrapActionExpressionIn_Try_Catch_ThrowNewMessage(Expression coreExpression, string newMessage)
{
return
Expression.TryCatch(
coreExpression,
Expression.Catch(typeof(Exception),
Expression.Throw(
Expression.Constant(new Exception(newMessage))
)
));
}
private Expression WrapActionExpressionIn_Try_Catch_RethrowWithAdditionalMessage(Expression coreExpression, string additionalMessage)
{
var caughtExceptionParameter = Expression.Parameter(typeof(Exception));
//We want to call `new Exception(additionalMessage, caughtException)`
var ctorForExceptionWithMessageAndInnerException = typeof(Exception).GetConstructor(new[] {typeof(string), typeof(Exception)});
var replacementExceptionExpresion = Expression.New(ctorForExceptionWithMessageAndInnerException, Expression.Constant(additionalMessage), caughtExceptionParameter);
return
Expression.TryCatch(
coreExpression,
Expression.Catch(caughtExceptionParameter,
Expression.Throw( replacementExceptionExpresion )
));
}
These two methods both start from a premise of "we already have an Expression representing an Action<TInstance> that we are about to invoke. And now we want to add a try-catch around that Action."
Without the Try-Catch, we would have done:
Action finalLamda = Expression.Lambda<Action<TInstance>>(actionExpression, instanceExpression).Compile();
now we do:
var actionWithTryCatchExpression = WrapActionExpressionIn_Try_Catch_RethrowWithAdditionalMessage(actionExpression);
Action finalLamda = Expression.Lambda<Action<TInstance>>(actionWithTryCatchExpression, instanceExpression).Compile();
Respectively the two helpers represent:
try
{
action();
}
catch(Exception)
{
throw new Exception(newMessage);
}
\\and
try
{
action();
}
catch(Exception e)
{
throw new Exception(additionalMessage, e);
}
I am trying to construct function that accepts more that one parameter. Lambda function accepting one parameter works fine. Here is the code.
var value = 22.55;
var method = typeof(TextWriter).GetMethod("WriteLine", new Type[] { value.GetType() });
ParameterExpression inputParameter = Expression.Parameter(typeof(TextWriter));
var block = Expression.Block(
inputParameter,
Expression.Call(inputParameter, method, Expression.Constant(value)));
var function = Expression.Lambda<Action<TextWriter>>(block, inputParameter).Compile();
function(Console.Out);
However when I add one more parameter that I do not even use then function throws null reference exception. I must be missing something but have no idea what. Here is the code that does not work:
var value = 22.55;
var method = typeof(TextWriter).GetMethod("WriteLine", new Type[] { value.GetType() });
ParameterExpression inputParameter = Expression.Parameter(typeof(TextWriter));
// Additional parameter here
ParameterExpression inputParameter2 = Expression.Parameter(typeof(double));
var block = Expression.Block(
// Function block accepts two parameters
new List<ParameterExpression>() { inputParameter, inputParameter2 },
Expression.Call(inputParameter, method, Expression.Constant(value)));
var function = Expression.Lambda<Action<TextWriter, double>>(block, inputParameter, inputParameter2).Compile();
// ....aaand null exception here. Why?
function(Console.Out, value);
What am I doing wrong here?
Judging by your comment, you're misunderstanding what you're passing to Block. The variables argument of Block is to define local variables, rather than parameters.
var block = Expression.Block(
// Function block accepts two parameters
new List<ParameterExpression>() { inputParameter, inputParameter2 },
Expression.Call(inputParameter, method, Expression.Constant(value)));
Should be simply
var block = Expression.Block(Expression.Call(inputParameter, method, Expression.Constant(value)));
What you're currently doing is equivelant to:
void function(TextWriter tw, double d) {
TextWriter tw = default(TextWriter);
double d = default(double);
tw.WriteLine(22.55);
}
While you actually just want:
void function(TextWriter tw, double d) {
tw.WriteLine(22.55);
}
Also, you're not actually using your second parameter. The complete working code is:
var method = typeof(TextWriter).GetMethod("WriteLine", new Type[] { typeof(double) });
ParameterExpression inputParameter = Expression.Parameter(typeof(TextWriter));
ParameterExpression inputParameter2 = Expression.Parameter(typeof(double));
var block = Expression.Block(Expression.Call(inputParameter, method, inputParameter2));
var function = Expression.Lambda<Action<TextWriter, double>>(block, inputParameter, inputParameter2).Compile();
function(Console.Out, 35.5);
Your first code worked because you used a different overload of block. Instead of declaring a local variable for inputParameter, you passed it as an expression itself. So, the code looked like this:
void function(TextWriter tw, double d) {
tw; //Illegal for the C# compiler, but not illegal as an expression. This will do nothing.
tw.WriteLine(22.55);
}
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.