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 Expression
s, it works a bit different: each expression has a return type, and in the case of TryCatch
, the return type of the try
Expression
has to be the same as the return type of any of the catch
Expression
s.
In your case, the type of the try
is int
, but the type of the catch
is void
, so they can't be used together. To fix this, you need to either change the type of the try
to void
, or change the type of the catch
to int
.
To change the type of the try
to void
, you can use an overload of Expression.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):
Expression.TryCatch(
Expression.Block(
typeof(void),
Expression.Assign(…)),
Expression.Catch(
typeof(NullReferenceException),
Expression.Throw(
Expression.Constant(
new InvalidOperationException("Unhandled exception")))))
To change the type of the catch
to int
, you would somehow need to change the type of the Throw
expression. And since for a Throw
expression, any return type can be valid (because it doesn't actually return), there is an overload that lets you specify the return type:
Expression.TryCatch(
Expression.Assign(…),
Expression.Catch(
typeof(NullReferenceException),
Expression.Throw(
Expression.Constant(
new InvalidOperationException("Unhandled exception")),
typeof(int))))
I think changing the type of the try
is conceptually clearer, because you don't actually want to return anything from the whole expression.
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