这是EnterLib PIAB的BUG吗?
在默認的情況下,EnterLib的PIAB采用基于TransparentProxy/RealProxy的機制實現對方法調用的攔截,進而實現了對橫切關注點(Crosscutting Concern)的動態注入。也正是其來截機制本身的局限,當我們才用PIAB的方式進行對象的創建的時候,要求本創建對象的類型要么實現某一個接口,要么繼承MarshalByRefObject類型。但是當我們讓抽象基類繼承自MarshalByRefObject就不行了,我個人覺得這是微軟需要改進的地方。
一、基于接口實現和對MarshalByRefObject直接繼承的編程
我們先來看看PIAB默認支持的編程方法。為此便于演示,我創建了一個自定義的CallHandler:FooCallHandler。在Invoke方法中,我在調用目標方法前后在控制臺輸出相應的文字,表明該CallHandler得以正常執行。下面是FooCallHandler和它對應的HandlerAttributed的定義:
1: public class FooCallHandler : ICallHandler 2: { 3: public IMethodReturn Invoke(IMethodInvocation input, GetNextHandlerDelegate getNext) 4: { 5: Console.WriteLine("PreOperation..."); 6: var methodReturn = getNext()(input, getNext); 7: Console.WriteLine("PostOperation..."); 8: return methodReturn; 9: } 10:? 11: public int Order { get; set; } 12: } 13:? 14: public class FooCallHandlerAttribute : HandlerAttribute 15: { 16: public override ICallHandler CreateHandler(IUnityContainer container) 17: { 18: return new FooCallHandler { Order = this.Order }; 19: } 20: }先來模擬以及接口實現的編程方式,為此我們定義了一個接口IFoo,實現該接口的類型Foo。IFoo和Foo定義在如下的代碼片斷中,上面創建的FooCallHandler通過自定義特性的方式應用到類型Foo上面。在Main方法中,調用PolicyInjection的泛型方法Create,并指明接口和具體類型的方式來創建Foo對象。
1: class Program 2: { 3: static void Main() 4: { 5: var foo = PolicyInjection.Create<Foo, IFoo>(); 6: foo.DoSomething(); 7: } 8: } 9: public interface IFoo 10: { 11: void DoSomething(); 12: } 13: [FooCallHandler] 14: public class Foo : IFoo 15: { 16: public void DoSomething() 17: { 18: Console.WriteLine("Do something..."); 19: } 20: }從下面的輸出我們可以看出,應用在類型Foo上面的FooCallHandler在目標方法被調用之前先得到了執行。
1: PreOperation... 2: Do something... 3: PostOperation...你也可以讓Foo直接繼承自MarshalByRefObject。如果你執行下面的代碼,你依然可以得到與上面一樣的輸出結果:
1: class Program 2: { 3: static void Main() 4: { 5: var foo = PolicyInjection.Create<Foo>(); 6: foo.DoSomething(); 7: } 8: } 9: [FooCallHandler] 10: public class Foo : MarshalByRefObject 11: { 12: public void DoSomething() 13: { 14: Console.WriteLine("Do something..."); 15: } 16: }二、將接口變成抽象類
我們推薦面向抽象的編程方式,具體有兩種主要的表現:基于接口編程和基于抽象類編程。現在,我們將接口IFoo換成抽象類FooBase,具體改變如下所示。
1: class Program 2: { 3: static void Main() 4: { 5: var foo = PolicyInjection.Create<Foo, FooBase>(); 6: foo.DoSomething(); 7: } 8: } 9:? 10: public abstract class FooBase 11: { 12: public abstract void DoSomething(); 13: } 14:? 15: [FooCallHandler] 16: public class Foo: FooBase 17: { 18: public override void DoSomething() 19: { 20: Console.WriteLine("Do something..."); 21: } 22: }?
上面的程序運行后,會拋出如下圖所示的ResolutionFailedException異常。錯誤消息表明異常是應該FooBase不能被實例化導致的——FooBase是抽象類。但是我們實例化的時具體類型Foo,FooBase能否實例化與此無關。
三、讓FooBase繼承MarshalByRefObject
上面我們說過,能被PIAB進行攔截的類型要么實現一個接口,要么繼承MarshalByReObject類。如果我們將FooBase繼承自MarshalByReObject,是否會避免上述異常的拋出呢?為此,我們對FooBase加上了這個基類。
1: public abstract class FooBase: MarshalByRefObject 2: { 3: public abstract void DoSomething(); 4: }運行我們的程序,會拋出和上面完全一樣的異常。
四、抽象類可以這樣用
經過我的實驗,抽象類可以這樣用:將繼承自MarshalByRefObject的具體類作為抽象類的基類。按照這個原理,我們對上面的例子作了如下改動:將FooBase從抽象類換成具體類,將Foo變成抽象類(Foo依然繼承自FooBase),然后創建另一個繼承自Foo的具體類FooImpl。
1: class Program 2: { 3: static void Main() 4: { 5: var foo = PolicyInjection.Create<FooImpl, FooBase>(); 6: foo.DoSomething(); 7: } 8: } 9: public class FooBase: MarshalByRefObject 10: { 11: public virtual void DoSomething() 12: { 13: throw new NotImplementedException("This method must be implemented by subclasses"); 14: } 15: } 16: public abstract class Foo : FooBase { } 17: [FooCallHandler] 18: public class FooImpl : Foo 19: { 20: public override void DoSomething() 21: { 22: Console.WriteLine("Do something..."); 23: } 24: }作了如此修改后,運行我們的程序之后我們能夠得到正確的結果。不過,為了讓PIAB提供對抽象類的支持而多加上一個非抽象的基類,在設計上是很丑陋的,我個人是不能接受的。實際上,我覺得這是PIAB自身的一個BUG,或者是自身欠考慮的地方。因為在實現的可行性上沒有任何問題。我親自在我自己開發的基于TransparentProxy/RealProxy的AOP框架(《自己動手創建迷你版AOP框架》)中經過驗證,讓抽象類繼承MarshalByRefObject,并基于該抽象類創建一個可被攔截的TransparentProxy對象是可以實現的。
作者:Artech出處:http://artech.cnblogs.com/
本文版權歸作者和博客園共有,歡迎轉載,但未經作者同意必須保留此段聲明,且在文章頁面明顯位置給出原文連接,否則保留追究法律責任的權利。
總結
以上是生活随笔為你收集整理的这是EnterLib PIAB的BUG吗?的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 房贷不还会有什么后果
- 下一篇: Java 学习网站汇总贴