Linq.Expression TryCatch-将异常传递给Catch块?

Dav*_*vid 6 c# lambda linq-expressions

因此,我一直在修改Linq.Expressions(如果有人可以提出一种更合适或更优雅的方式来做我在做的事情,请随时鸣叫),并在尝试做某事时碰壁。

假设我们有一个简单的数学课:

public class SimpleMath {
    public int AddNumbers(int number1, int number2) {           
        return number1 + number2;
    }
}
Run Code Online (Sandbox Code Playgroud)

我决定将我们的AddNumbers方法转换为简单的Func<object, object, object>委托。

为此,请执行以下操作:

// 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
Run Code Online (Sandbox Code Playgroud)

因此,如果您运行该代码,func则应返回简单的计算。但是,它接受类型对象的参数,这显然使它在运行时容易出现问题,例如:

object result1 = func(20, "f"); // Throws InvalidCastException
Run Code Online (Sandbox Code Playgroud)

因此,我想将方法​​包装为Try...Catch(显然,如果我们要直接调用AddNumbers并将字符串作为参数进行传递,则此确切问题将在编译时解决)。

因此,要捕获此异常,我可以执行以下操作:

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
Run Code Online (Sandbox Code Playgroud)

TryExpression.TryCatch需要一个表达的身体,然后CatchBlock处理程序的集合。returnMethodWithParametersAsObject是我们希望包装的表达式,Expression.Catch定义我们要捕获的Exception类型为InvalidCastException,其Expression主体为常数55。

因此可以处理异常,但是除非抛出异常,否则我总是想返回一个静态值,否则它的用处不大。因此,回到SimpleMath类中,我添加了一个新方法HandleException

public class SimpleMath {
    public int AddNumbers(int number1, int number2) {
        return number1 + number2;
    }

    public int HandleException() {
        return 100;
    }
}
Run Code Online (Sandbox Code Playgroud)

然后按照上述相同的过程,将新方法转换为表达式:

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));
Run Code Online (Sandbox Code Playgroud)

然后在创建TryCatch块时使用它:

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
Run Code Online (Sandbox Code Playgroud)

因此,这一次InvalidCastException抛出时,该SimpleMath.HandleException方法将被执行。到目前为止,到目前为止,我可以在出现异常时执行一些代码。

我的问题现在是,在常规的内联Try ... Catch块中,实际上您可以使用异常对象。例如

try {
    // Do stuff that causes an exception
} catch (InvalidCastException ex) {
    // Do stuff with InvalidCastException ex
}
Run Code Online (Sandbox Code Playgroud)

当引发异常时,我可以执行代码,但是我似乎无法弄清楚如何像实际的Try ... Catch块中那样实际使用异常对象。

任何帮助,将不胜感激!

ps:我知道您实际上不会像上面所做的那样组织任何事情,但是我认为有必要出于示例目的展示我想做的事情的机制。

Geo*_*ria 3

您需要将捕获的异常CatchBlock作为参数传递给表达式。为此你应该这样做:

  • 更改 的签名HandleException。它将接受异常作为参数:

    public int HandleException(InvalidCastException exp)
    {
        // Put here some real logic. I tested it using line below
        Console.WriteLine(exp.Message);
        return 100;
    }
    
    Run Code Online (Sandbox Code Playgroud)
  • 用于CatchBlock.Variable将处理的异常传递到 catch 块中。您可以使用构造函数设置它。阅读下面代码中的注释:

        // 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
    
    Run Code Online (Sandbox Code Playgroud)