[英]Linq.Expression TryCatch - Pass exception to Catch Block?
所以我一直在修改 Linq.Expressions(如果有人可以建議一種更合適或更優雅的方式來做我正在做的事情,請隨時加入)並且在嘗試做某事時遇到了障礙。
假設我們有一個簡單的數學課:
public class SimpleMath {
public int AddNumbers(int number1, int number2) {
return number1 + number2;
}
}
我決定將我們的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
因此,如果您運行該代碼func
應該返回簡單的計算。 然而,它接受類型對象的參數,這顯然使它在運行時容易出現問題,例如:
object result1 = func(20, "f"); // Throws InvalidCastException
因此,我想將該方法包裝在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
TryExpression.TryCatch
接受一個 Expression 主體,然后是一個 CatchBlock 處理程序的集合。 returnMethodWithParametersAsObject
是我們想要包裝的Expression.Catch
, Expression.Catch
定義了我們想要捕獲的異常類型為 InvalidCastException 並且它的 Expression 主體是一個常量,55。
所以異常被處理了,但它沒有多大用處,除非我想在拋出異常時總是返回一個靜態值。 所以回到SimpleMath
類,我添加了一個新方法HandleException
:
public class SimpleMath {
public int AddNumbers(int number1, int number2) {
return number1 + number2;
}
public int HandleException() {
return 100;
}
}
按照上述相同的過程,我將新方法轉換為表達式:
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));
然后在創建 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
所以這次當拋出InvalidCastException
時, SimpleMath.HandleException
方法將被執行。 到目前為止一切順利,我現在可以在出現異常時執行一些代碼。
我現在的問題是,在普通的內聯 Try...Catch 塊中,您實際上可以使用異常對象。 例如
try {
// Do stuff that causes an exception
} catch (InvalidCastException ex) {
// Do stuff with InvalidCastException ex
}
我可以在拋出異常時執行代碼,但我似乎無法弄清楚如何像在普通的 Try...Catch 塊中那樣實際處理異常對象。
任何幫助將不勝感激!
ps 我知道你實際上不會按照我上面所做的方式組織任何事情,但我認為出於示例目的,有必要展示我想要做的事情的機制。
您需要將CatchBlock
異常作為參數傳遞給CatchBlock
表達式。 為此,您應該這樣做:
更改HandleException
簽名。 它將以異常作為參數:
public int HandleException(InvalidCastException exp) { // Put here some real logic. I tested it using line below Console.WriteLine(exp.Message); return 100; }
使用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
@GeorgeAlexandria 的回答是完全正確的。 但是當我開始做同樣的事情時,我發現解碼這一切非常困難。
也許下面的代碼(編寫為 2 個輔助方法)將幫助下一個需要做這樣的事情的人......
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 )
));
}
這兩種方法都以“我們已經有一個表達式來表示我們將要調用的Action<TInstance>
的前提開始。現在我們想要在該 Action 周圍添加一個 try-catch。”
如果沒有 Try-Catch,我們會這樣做:
Action finalLamda = Expression.Lambda<Action<TInstance>>(actionExpression, instanceExpression).Compile();
現在我們這樣做:
var actionWithTryCatchExpression = WrapActionExpressionIn_Try_Catch_RethrowWithAdditionalMessage(actionExpression);
Action finalLamda = Expression.Lambda<Action<TInstance>>(actionWithTryCatchExpression, instanceExpression).Compile();
兩個助手分別代表:
try
{
action();
}
catch(Exception)
{
throw new Exception(newMessage);
}
\\and
try
{
action();
}
catch(Exception e)
{
throw new Exception(additionalMessage, e);
}
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.