I am trying to add some error-handling to a block of code in the Effort library which generates an expression tree to perform a conversion and assign that result to a property.
The problem with the existing code is that a NullReferenceException is thrown when this expression is invoked at runtime when trying to assign null to a property with a value type. In this case I have no information about the property it was attempting to assign, so I want to throw a more specific exception.
Following is my first attempt to just encapsulate this logic in a try/catch block, and throw an exception if the conversion fails. Eventually I would add more information to the InvalidOperationException.
blockElements.Add(
Expression.TryCatch(
Expression.Assign(
Expression.Property(result, this.Properties[i]),
Expression.Convert(
Expression.ArrayIndex(parameter, Expression.Constant(i)),
this.Properties[i].PropertyType)),
Expression.Catch(typeof(NullReferenceException),
Expression.Throw(Expression.Constant(
new InvalidOperationException("Unhandled exception"))))));
In my mind this is what I’m trying to do:
try
{
Property = (int)value;
}
catch (NullReferenceException)
{
throw new InvalidOperationException("Unhandled exception");
}
However at runtime, that expression now throws an ArgumentException with the message “Body of catch must have the same type as body of try.” What am I doing wrong here? Do I need to create a Block in the Catch expression to “return” some dummy value, even though it will never be hit due to the Throw?
Or am I approaching this in entirely the wrong way?
In normal C# code, a method as a whole has to either return a value or throw an exception.
With
Expressions, it works a bit different: each expression has a return type, and in the case ofTryCatch, the return type of thetryExpressionhas to be the same as the return type of any of thecatchExpressions.In your case, the type of the
tryisint, but the type of thecatchisvoid, so they can’t be used together. To fix this, you need to either change the type of thetrytovoid, or change the type of thecatchtoint.To change the type of the
trytovoid, you can use an overload ofExpression.Block()that lets you specify the type of the block (normally, it’s the same as the type of the last expression in the block):To change the type of the
catchtoint, you would somehow need to change the type of theThrowexpression. And since for aThrowexpression, any return type can be valid (because it doesn’t actually return), there is an overload that lets you specify the return type:I think changing the type of the
tryis conceptually clearer, because you don’t actually want to return anything from the whole expression.