Unity AOP 处理异常的方法
生活随笔
收集整理的這篇文章主要介紹了
Unity AOP 处理异常的方法
小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.
摘要
使用 unity 處理異常的方法可能會(huì)與你的直覺不符。本文將給出正確的處理方法,并簡單剖析Unity這部分源代碼。
處理異常
打算用Unity的AOP截獲未處理的異常,然后寫個(gè)日志什么的,于是我寫下了這樣的代碼(注意 這段代碼是錯(cuò)誤的):
public?class?MyHandler?:?ICallHandler
{
????public?int?Order?{?get;?set;?}//?這是ICallHandler的成員,表示執(zhí)行順序???
????public?IMethodReturn?Invoke(IMethodInvocation?input,?GetNextHandlerDelegate?getNext)
????{
????????//這之前插入方法執(zhí)行前的處理??
????????Console.WriteLine("執(zhí)行前");
????????IMethodReturn?retvalue?=?null;
????????try
????????{
????????????retvalue?=?getNext()(input,?getNext);//?在這里執(zhí)行方法??
????????}
????????catch?(Exception?ex)
????????{
????????????Console.WriteLine("ExMsg:"?+?ex.Message);?//?處理異常,例如寫日志之類的。
????????}
????????//這之后插入方法執(zhí)行后的處理??
????????Console.WriteLine("完成");
????????return?retvalue;
????}
}為了測試一下異常有沒有被成功截獲,我讓被調(diào)用的函數(shù)拋出一個(gè)異常:
[MyHandler]
public?class?OutputImplement1?:?IOutput
{
????public?void?Output(int?x)
????{
????????Console.WriteLine("執(zhí)行此方法輸出:{0}",?x);
????????throw?new?Exception("這里拋個(gè)異常出來");
????}
} 出人意料的是,異常并沒有被我的Catch截獲。單步執(zhí)行,可以發(fā)現(xiàn)在執(zhí)行try區(qū)塊里面的代碼時(shí)確實(shí)有異常拋出,只是catch區(qū)塊里面的代碼根本沒有執(zhí)行,然后控制臺(tái)就顯示有未處理的異常了。其實(shí),第一段代碼是錯(cuò)誤的,正確的代碼應(yīng)該像這樣:
public?class?MyHandler?:?ICallHandler
{
????public?int?Order?{?get;?set;?}//這是ICallHandler的成員,表示執(zhí)行順序???
????public?IMethodReturn?Invoke(IMethodInvocation?input,?GetNextHandlerDelegate?getNext)
????{
????????//這之前插入方法執(zhí)行前的處理??
????????Console.WriteLine("執(zhí)行前");
????????IMethodReturn?retvalue?=?getNext()(input,?getNext);//在這里執(zhí)行方法??
????????if?(retvalue.Exception?==?null)?//?retvalue.Exception=null說明函數(shù)執(zhí)行時(shí)沒有拋出異常
????????{
????????????Console.WriteLine("執(zhí)行成功,無異常");
????????}
????????else
????????{
????????????Console.WriteLine("Exxxxxx:"?+?retvalue.Exception.Message);
????????????retvalue.Exception?=?null;?//?將retvalue.Exception設(shè)為null表示異常已經(jīng)被處理過了,
???????????????????????????????????????//?如果不把retvalue.Exception設(shè)為null,Unity會(huì)再次拋出此異常。
????????}
????????//這之后插入方法執(zhí)行后的處理??
????????Console.WriteLine("完成");
????????return?retvalue;
????}
}
原因
我百思不得其解:既然在try里面已經(jīng)執(zhí)行了OutputImplement1.Output()函數(shù),也拋出了異常,為什么catch區(qū)塊內(nèi)的代碼卻沒有被執(zhí)行呢?好在Untity是開源的,可以深入源碼一探究竟。
找到原因后,稍稍有些失望,因?yàn)檫@原因說起來平平無奇:因?yàn)?strong>在我try之前Unity已經(jīng)try完了。也就是說,第一段代碼執(zhí)行起來會(huì)像這樣(偽代碼,加粗部分是Unity動(dòng)態(tài)生成的):
public?class?MyHandler?:?ICallHandler
{
????public?int?Order?{?get;?set;?}//?這是ICallHandler的成員,表示執(zhí)行順序???
????public?IMethodReturn?Invoke(IMethodInvocation?input,?GetNextHandlerDelegate?getNext)
????{
????????//這之前插入方法執(zhí)行前的處理??
????????Console.WriteLine("執(zhí)行前");
????????IMethodReturn?retvalue?=?null;
????????try
????????{
????????????try
????????????{
????????????????IParameterCollection?arguments?=?inputs.Arguments;
????????????????this.target.Output((int)?arguments[0]);?//?在這里執(zhí)行方法,并拋出異常
????????????????retvalue?=?inputs.CreateMethodReturn(null,?new?object[]?{?arguments[0]?});
????????????}
????????????catch?(Exception?exception)
????????????{
????????????????retvalue?=?inputs.CreateExceptionMethodReturn(exception);
????????????}
????????}
????????catch?(Exception?ex)
????????{
????????????Console.WriteLine("ExMsg:"?+?ex.Message);?//?處理異常,例如寫日志之類的。
????????}
????????//這之后插入方法執(zhí)行后的處理??
????????Console.WriteLine("完成");
????????return?retvalue;
????}
} 當(dāng)然,上面這段代碼為了說明問題作了改寫和簡化,如果對真實(shí)的代碼感興趣可以接著往下看。
源碼剖析
類圖:
藍(lán)色背景的類是我寫的演示程序中的代碼,是照著重典的教程做的。IOutPut 和 OutputImplement1相當(dāng)于業(yè)務(wù)代碼的接口和實(shí)現(xiàn)。MyHandler用來截獲對OutputImplement1里的函數(shù)的調(diào)用:
Unity AOP 演示代碼
class?Program
{
????static?void?Main(string[]?args)
????{
????????var?container1?=?new?UnityContainer().AddNewExtension<Interception>().RegisterType<IOutput,?OutputImplement1>();//聲明UnityContainer并注冊IOutput???
????????container1.Configure<Interception>().SetInterceptorFor<IOutput>(new?InterfaceInterceptor());
????????IOutput?op1?=?container1.Resolve<IOutput>();
????????op1.Output(11);//調(diào)用
????????Console.ReadLine();
????}
}
public?interface?IOutput
{
????void?Output(int?x);
}
[MyHandler]
public?class?OutputImplement1?:?IOutput
{
????public?void?Output(int?x)
????{
????????Console.WriteLine("執(zhí)行此方法輸出:{0}",?x);
????????throw?new?Exception("這里拋個(gè)異常出來");
????}
}
public?class?MyHandler?:?ICallHandler
{
????public?int?Order?{?get;?set;?}//這是ICallHandler的成員,表示執(zhí)行順序???
????public?IMethodReturn?Invoke(IMethodInvocation?input,?GetNextHandlerDelegate?getNext)
????{
????????//這之前插入方法執(zhí)行前的處理??
????????Console.WriteLine("執(zhí)行前");
????????IMethodReturn?retvalue?=?getNext()(input,?getNext);//在這里執(zhí)行方法??
????????if?(retvalue.Exception?==?null)?//?retvalue.Exception=null說明函數(shù)執(zhí)行時(shí)沒有拋出異常
????????{
????????????Console.WriteLine("執(zhí)行成功,無異常");
????????}
????????else
????????{
????????????Console.WriteLine("Exxxxxx:"?+?retvalue.Exception.Message);
????????????retvalue.Exception?=?null;?//?將retvalue.Exception設(shè)為null表示異常已經(jīng)被處理過了,
???????????????????????????????????????//?如果不把retvalue.Exception設(shè)為null,Unity會(huì)再次拋出此異常。
????????}
????????//這之后插入方法執(zhí)行后的處理??
????????Console.WriteLine("完成");
????????return?retvalue;
????}
}
public?class?MyHandlerAttribute?:?HandlerAttribute
{
????public?override?ICallHandler?CreateHandler(IUnityContainer?container)
????{
????????return?new?MyHandler();//返回MyHandler???
????}
}
淺綠色背景的類“Wrapped_IOutput_f56a10859ef14d7794f3aa40d4eb36cc”是由Unity使用Emit在運(yùn)行期動(dòng)態(tài)創(chuàng)建的包裝類。它實(shí)現(xiàn)IOutput接口,隱式實(shí)現(xiàn)IInterceptingProxy接口。Main()函數(shù)中的“IOutput op1 = container1.Resolve<IOutput>();”實(shí)際返回的就是這個(gè)類對象。所以下一句“op1.Output(11);”執(zhí)行的是Wrapped_IOutput_f56a10859ef14d7794f3aa40d4eb36cc.Output(11)。這個(gè)函數(shù)首先將被調(diào)用函數(shù)“op1.Output(11)”的元數(shù)據(jù)(函數(shù)名、調(diào)用對象、參數(shù)等等)封裝到一個(gè)VirtualMethodInvocation對象中,然后把它作為參數(shù)傳遞給pipeline.Invoke()。pipeline.Invoke()遞歸調(diào)用所有的攔截器(即ICallHandler的實(shí)現(xiàn)類,本例中是MyHandler)的Invoke()函數(shù)。
對OutputImplement1.Output()的調(diào)用封裝在了Wrapped_IOutput_f56a10859ef14d7794f3aa40d4eb36cc.<Output_DelegateImplementation>__0()函數(shù)中。“<Output_DelegateImplementation>__0”這個(gè)函數(shù)名看上去有些怪,那兩個(gè)“<>”符號(hào)很容易讓人感覺是泛型,但其實(shí)這就是個(gè)比較怪的函數(shù)名而已。
附錄 如何取得動(dòng)態(tài)生成的類的代碼?
Wrapped_IOutput_f56a10859ef14d7794f3aa40d4eb36cc這個(gè)類是Unity使用Emit動(dòng)態(tài)創(chuàng)建的,所以無論看Unity的源代碼或是使用Reflector都無法取得它的源代碼。要想看它的源代碼,有兩種方法。
第一種方法是使用Reflector的能加載運(yùn)行著的進(jìn)程的插件。但是實(shí)際上我嘗試了N多次也沒成功,總是剛剛執(zhí)行了Main()函數(shù)的第一行就報(bào)錯(cuò)。
第二種方法是直接用Emit的AssemblyBuilder.Save()函數(shù)將動(dòng)態(tài)生成的程序集保存到硬盤上。事實(shí)上,Unity的開發(fā)人員已經(jīng)把這個(gè)代碼寫好了,就在“Microsoft Unity Application Block 1.2\UnitySource\UnitySource\Src\Unity.Interception\Interceptors\InstanceInterceptors\InterfaceInterception\InterfaceInterceptorClassGenerator.cs”的第79行:
#if?DEBUG_SAVE_GENERATED_ASSEMBLY
????assemblyBuilder.Save("Unity_ILEmit_InterfaceProxies.dll");
#endif我們所需要做的就是在此文件的第一行添加一句“#define DEBUG_SAVE_GENERATED_ASSEMBLY”,讓這段代碼執(zhí)行就行了。記得要重新編譯,然后讓我們的演示程序引用修改后生成的那個(gè)Microsoft.Practices.Unity.Interception.dll。再次運(yùn)行我們的演示程序,就可以在它的bin\Debug\找到名為“Unity_ILEmit_InterfaceProxies.dll”的程序集,用Reflector打開它就可以找到
類似于“Wrapped_IOutput_f56a10859ef14d7794f3aa40d4eb36cc”這樣的類了。
使用 unity 處理異常的方法可能會(huì)與你的直覺不符。本文將給出正確的處理方法,并簡單剖析Unity這部分源代碼。
處理異常
打算用Unity的AOP截獲未處理的異常,然后寫個(gè)日志什么的,于是我寫下了這樣的代碼(注意 這段代碼是錯(cuò)誤的):
public?class?MyHandler?:?ICallHandler
{
????public?int?Order?{?get;?set;?}//?這是ICallHandler的成員,表示執(zhí)行順序???
????public?IMethodReturn?Invoke(IMethodInvocation?input,?GetNextHandlerDelegate?getNext)
????{
????????//這之前插入方法執(zhí)行前的處理??
????????Console.WriteLine("執(zhí)行前");
????????IMethodReturn?retvalue?=?null;
????????try
????????{
????????????retvalue?=?getNext()(input,?getNext);//?在這里執(zhí)行方法??
????????}
????????catch?(Exception?ex)
????????{
????????????Console.WriteLine("ExMsg:"?+?ex.Message);?//?處理異常,例如寫日志之類的。
????????}
????????//這之后插入方法執(zhí)行后的處理??
????????Console.WriteLine("完成");
????????return?retvalue;
????}
}為了測試一下異常有沒有被成功截獲,我讓被調(diào)用的函數(shù)拋出一個(gè)異常:
[MyHandler]
public?class?OutputImplement1?:?IOutput
{
????public?void?Output(int?x)
????{
????????Console.WriteLine("執(zhí)行此方法輸出:{0}",?x);
????????throw?new?Exception("這里拋個(gè)異常出來");
????}
} 出人意料的是,異常并沒有被我的Catch截獲。單步執(zhí)行,可以發(fā)現(xiàn)在執(zhí)行try區(qū)塊里面的代碼時(shí)確實(shí)有異常拋出,只是catch區(qū)塊里面的代碼根本沒有執(zhí)行,然后控制臺(tái)就顯示有未處理的異常了。其實(shí),第一段代碼是錯(cuò)誤的,正確的代碼應(yīng)該像這樣:
public?class?MyHandler?:?ICallHandler
{
????public?int?Order?{?get;?set;?}//這是ICallHandler的成員,表示執(zhí)行順序???
????public?IMethodReturn?Invoke(IMethodInvocation?input,?GetNextHandlerDelegate?getNext)
????{
????????//這之前插入方法執(zhí)行前的處理??
????????Console.WriteLine("執(zhí)行前");
????????IMethodReturn?retvalue?=?getNext()(input,?getNext);//在這里執(zhí)行方法??
????????if?(retvalue.Exception?==?null)?//?retvalue.Exception=null說明函數(shù)執(zhí)行時(shí)沒有拋出異常
????????{
????????????Console.WriteLine("執(zhí)行成功,無異常");
????????}
????????else
????????{
????????????Console.WriteLine("Exxxxxx:"?+?retvalue.Exception.Message);
????????????retvalue.Exception?=?null;?//?將retvalue.Exception設(shè)為null表示異常已經(jīng)被處理過了,
???????????????????????????????????????//?如果不把retvalue.Exception設(shè)為null,Unity會(huì)再次拋出此異常。
????????}
????????//這之后插入方法執(zhí)行后的處理??
????????Console.WriteLine("完成");
????????return?retvalue;
????}
}
原因
我百思不得其解:既然在try里面已經(jīng)執(zhí)行了OutputImplement1.Output()函數(shù),也拋出了異常,為什么catch區(qū)塊內(nèi)的代碼卻沒有被執(zhí)行呢?好在Untity是開源的,可以深入源碼一探究竟。
找到原因后,稍稍有些失望,因?yàn)檫@原因說起來平平無奇:因?yàn)?strong>在我try之前Unity已經(jīng)try完了。也就是說,第一段代碼執(zhí)行起來會(huì)像這樣(偽代碼,加粗部分是Unity動(dòng)態(tài)生成的):
public?class?MyHandler?:?ICallHandler
{
????public?int?Order?{?get;?set;?}//?這是ICallHandler的成員,表示執(zhí)行順序???
????public?IMethodReturn?Invoke(IMethodInvocation?input,?GetNextHandlerDelegate?getNext)
????{
????????//這之前插入方法執(zhí)行前的處理??
????????Console.WriteLine("執(zhí)行前");
????????IMethodReturn?retvalue?=?null;
????????try
????????{
????????????try
????????????{
????????????????IParameterCollection?arguments?=?inputs.Arguments;
????????????????this.target.Output((int)?arguments[0]);?//?在這里執(zhí)行方法,并拋出異常
????????????????retvalue?=?inputs.CreateMethodReturn(null,?new?object[]?{?arguments[0]?});
????????????}
????????????catch?(Exception?exception)
????????????{
????????????????retvalue?=?inputs.CreateExceptionMethodReturn(exception);
????????????}
????????}
????????catch?(Exception?ex)
????????{
????????????Console.WriteLine("ExMsg:"?+?ex.Message);?//?處理異常,例如寫日志之類的。
????????}
????????//這之后插入方法執(zhí)行后的處理??
????????Console.WriteLine("完成");
????????return?retvalue;
????}
} 當(dāng)然,上面這段代碼為了說明問題作了改寫和簡化,如果對真實(shí)的代碼感興趣可以接著往下看。
源碼剖析
類圖:
藍(lán)色背景的類是我寫的演示程序中的代碼,是照著重典的教程做的。IOutPut 和 OutputImplement1相當(dāng)于業(yè)務(wù)代碼的接口和實(shí)現(xiàn)。MyHandler用來截獲對OutputImplement1里的函數(shù)的調(diào)用:
Unity AOP 演示代碼
class?Program
{
????static?void?Main(string[]?args)
????{
????????var?container1?=?new?UnityContainer().AddNewExtension<Interception>().RegisterType<IOutput,?OutputImplement1>();//聲明UnityContainer并注冊IOutput???
????????container1.Configure<Interception>().SetInterceptorFor<IOutput>(new?InterfaceInterceptor());
????????IOutput?op1?=?container1.Resolve<IOutput>();
????????op1.Output(11);//調(diào)用
????????Console.ReadLine();
????}
}
public?interface?IOutput
{
????void?Output(int?x);
}
[MyHandler]
public?class?OutputImplement1?:?IOutput
{
????public?void?Output(int?x)
????{
????????Console.WriteLine("執(zhí)行此方法輸出:{0}",?x);
????????throw?new?Exception("這里拋個(gè)異常出來");
????}
}
public?class?MyHandler?:?ICallHandler
{
????public?int?Order?{?get;?set;?}//這是ICallHandler的成員,表示執(zhí)行順序???
????public?IMethodReturn?Invoke(IMethodInvocation?input,?GetNextHandlerDelegate?getNext)
????{
????????//這之前插入方法執(zhí)行前的處理??
????????Console.WriteLine("執(zhí)行前");
????????IMethodReturn?retvalue?=?getNext()(input,?getNext);//在這里執(zhí)行方法??
????????if?(retvalue.Exception?==?null)?//?retvalue.Exception=null說明函數(shù)執(zhí)行時(shí)沒有拋出異常
????????{
????????????Console.WriteLine("執(zhí)行成功,無異常");
????????}
????????else
????????{
????????????Console.WriteLine("Exxxxxx:"?+?retvalue.Exception.Message);
????????????retvalue.Exception?=?null;?//?將retvalue.Exception設(shè)為null表示異常已經(jīng)被處理過了,
???????????????????????????????????????//?如果不把retvalue.Exception設(shè)為null,Unity會(huì)再次拋出此異常。
????????}
????????//這之后插入方法執(zhí)行后的處理??
????????Console.WriteLine("完成");
????????return?retvalue;
????}
}
public?class?MyHandlerAttribute?:?HandlerAttribute
{
????public?override?ICallHandler?CreateHandler(IUnityContainer?container)
????{
????????return?new?MyHandler();//返回MyHandler???
????}
}
淺綠色背景的類“Wrapped_IOutput_f56a10859ef14d7794f3aa40d4eb36cc”是由Unity使用Emit在運(yùn)行期動(dòng)態(tài)創(chuàng)建的包裝類。它實(shí)現(xiàn)IOutput接口,隱式實(shí)現(xiàn)IInterceptingProxy接口。Main()函數(shù)中的“IOutput op1 = container1.Resolve<IOutput>();”實(shí)際返回的就是這個(gè)類對象。所以下一句“op1.Output(11);”執(zhí)行的是Wrapped_IOutput_f56a10859ef14d7794f3aa40d4eb36cc.Output(11)。這個(gè)函數(shù)首先將被調(diào)用函數(shù)“op1.Output(11)”的元數(shù)據(jù)(函數(shù)名、調(diào)用對象、參數(shù)等等)封裝到一個(gè)VirtualMethodInvocation對象中,然后把它作為參數(shù)傳遞給pipeline.Invoke()。pipeline.Invoke()遞歸調(diào)用所有的攔截器(即ICallHandler的實(shí)現(xiàn)類,本例中是MyHandler)的Invoke()函數(shù)。
對OutputImplement1.Output()的調(diào)用封裝在了Wrapped_IOutput_f56a10859ef14d7794f3aa40d4eb36cc.<Output_DelegateImplementation>__0()函數(shù)中。“<Output_DelegateImplementation>__0”這個(gè)函數(shù)名看上去有些怪,那兩個(gè)“<>”符號(hào)很容易讓人感覺是泛型,但其實(shí)這就是個(gè)比較怪的函數(shù)名而已。
附錄 如何取得動(dòng)態(tài)生成的類的代碼?
Wrapped_IOutput_f56a10859ef14d7794f3aa40d4eb36cc這個(gè)類是Unity使用Emit動(dòng)態(tài)創(chuàng)建的,所以無論看Unity的源代碼或是使用Reflector都無法取得它的源代碼。要想看它的源代碼,有兩種方法。
第一種方法是使用Reflector的能加載運(yùn)行著的進(jìn)程的插件。但是實(shí)際上我嘗試了N多次也沒成功,總是剛剛執(zhí)行了Main()函數(shù)的第一行就報(bào)錯(cuò)。
第二種方法是直接用Emit的AssemblyBuilder.Save()函數(shù)將動(dòng)態(tài)生成的程序集保存到硬盤上。事實(shí)上,Unity的開發(fā)人員已經(jīng)把這個(gè)代碼寫好了,就在“Microsoft Unity Application Block 1.2\UnitySource\UnitySource\Src\Unity.Interception\Interceptors\InstanceInterceptors\InterfaceInterception\InterfaceInterceptorClassGenerator.cs”的第79行:
#if?DEBUG_SAVE_GENERATED_ASSEMBLY
????assemblyBuilder.Save("Unity_ILEmit_InterfaceProxies.dll");
#endif我們所需要做的就是在此文件的第一行添加一句“#define DEBUG_SAVE_GENERATED_ASSEMBLY”,讓這段代碼執(zhí)行就行了。記得要重新編譯,然后讓我們的演示程序引用修改后生成的那個(gè)Microsoft.Practices.Unity.Interception.dll。再次運(yùn)行我們的演示程序,就可以在它的bin\Debug\找到名為“Unity_ILEmit_InterfaceProxies.dll”的程序集,用Reflector打開它就可以找到
類似于“Wrapped_IOutput_f56a10859ef14d7794f3aa40d4eb36cc”這樣的類了。
總結(jié)
以上是生活随笔為你收集整理的Unity AOP 处理异常的方法的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: PHP serialize JSON
- 下一篇: Visual Studio 2010 U