目錄
- Emit異常處理流程
- 顯示Exception對象的Message屬性
返回目錄
Emit異常處理流程
來看這種C#異常處理代碼:
static void doo(Exception e)
{
try
{
throw e;
}
catch (ApplicationException ex)
{
Console.WriteLine("捕獲ApplicationException");
}
catch
{
Console.WriteLine("捕獲Exception");
}
finally
{
Console.WriteLine("finally塊");
}
}
我們將用反射Emit建立一個這樣的方法
其實IL中的異常處理代碼還是比較複雜的,你可以在Reflector下看看異常處理的IL代碼。不過好在ILGenerator類提供了一些方便的方法來建立異常處理代碼。
基本套路就是用如下ILGenerator的方法:
- BeginExceptionBlock方法來開始異常處理代碼(相當於try)。
- 之後的代碼可以用Opcodes.Throw來拋出異常,或者調用其他可以拋出異常的代碼。
- 接著用BeginCatchBlock方法來開始一個Catch塊,該方法可以指定catch需要捕獲的異常類型,另外有一點需要注意的是凡是進入該catch方法,邏輯棧上會有相應類型的異常對象。 同時,這裡也可以用Opcodes.Rethrow來重新拋出異常。
- 最後BeginFinallyBlock方法開始一個finally塊。 (這裡不需要手動加Opcodes.Leave)
- 當全部異常處理代碼寫完後,加上EndExceptionBlock方法來結束整塊異常處理代碼塊。
- 注意方法最後還是必須要加IL的ret指令的(Opcodes.Ret),否則CLR無法運行此方法。
來看代碼:
//+ using System.Reflection;
//+ using System.Reflection.Emit;
static void Main(string[] args)
{
var dm = GetMethod();
dm.Invoke(null, new object[] { new ApplicationException() });
dm.Invoke(null, new object[] { new Exception() });
}
static DynamicMethod GetMethod()
{
var dm = new DynamicMethod("", null, new Type[] { typeof(Exception) });
var ilgen = dm.GetILGenerator();
//try {
ilgen.BeginExceptionBlock();
//載入第一個參數,並throw
ilgen.Emit(OpCodes.Ldarg_0);
ilgen.Emit(OpCodes.Throw);
ilgen.BeginCatchBlock(typeof(ApplicationException));
//清空棧上的異常對象
ilgen.Emit(OpCodes.Pop);
ilgen.EmitWriteLine("捕獲ApplicationException");
ilgen.BeginCatchBlock(typeof(Exception));
//清空棧上的異常對象
ilgen.Emit(OpCodes.Pop);
ilgen.EmitWriteLine("捕獲Exception");
ilgen.BeginFinallyBlock();
ilgen.EmitWriteLine("finally塊");
//結束整個處理塊
ilgen.EndExceptionBlock();
ilgen.Emit(OpCodes.Ret);
return dm;
}
輸出:
捕獲ApplicationException
finally塊
捕獲Exception
finally塊
返回目錄
顯示Exception對象的Message屬性
上面的代碼並沒有顯示Exception對象的Message屬性,上面主要是介紹Emit異常處理的流程,下面來看看怎樣顯示Message屬性,如果是直接輸出當然簡單了,不過如果用到Console.WriteLine的格式字串的話,需要在catch代碼塊中用一個臨時變數。
如下代碼:
//+ using System.Reflection;
//+ using System.Reflection.Emit;
static void Main(string[] args)
{
var dm = GetMethod();
dm.Invoke(null, new object[] { new Exception("來自Mgen!") });
}
static DynamicMethod GetMethod()
{
var dm = new DynamicMethod("", null, new Type[] { typeof(Exception) });
var ilgen = dm.GetILGenerator();
//try {
ilgen.BeginExceptionBlock();
//載入第一個參數,並throw
ilgen.Emit(OpCodes.Ldarg_0);
ilgen.Emit(OpCodes.Throw);
ilgen.BeginCatchBlock(typeof(Exception));
//臨時變數 和 需要的反射資訊
var exp = ilgen.DeclareLocal(typeof(Exception));
var msg = typeof(Exception).GetProperty("Message").GetGetMethod();
var output = typeof(Console).GetMethod("WriteLine", new Type[] { typeof(string), typeof(object) });
//儲存異常對象到臨時變數exp
ilgen.Emit(OpCodes.Stloc, exp);
//格式字串進棧
ilgen.Emit(OpCodes.Ldstr, "錯誤資訊: {0}");
//載入臨時變數
ilgen.Emit(OpCodes.Ldloc, exp);
//擷取Message屬性
ilgen.Emit(OpCodes.Callvirt, msg);
//調用有格式字串的Console.WriteLine
ilgen.Emit(OpCodes.Call, output);
//結束整個處理塊
ilgen.EndExceptionBlock();
ilgen.Emit(OpCodes.Ret);
return dm;
}
輸出:
錯誤資訊: 來自Mgen!