定義函數A,B,C,調用A->B->C,這樣就形成了函數靜態調用鏈,而AOP要做的是能動態的添加多個B,形成A->B1->B2->B3...->C這樣的效果,在EntLib(MS的企業庫)Unity中有這樣的實現,不過要看明白里面的代碼的確需要花不少腦子,3年前看過里面的代碼並做了記錄,但是這兩天翻出來看時照樣化了很大精力,並殺死殺傷大量腦細胞,於是痛下決心將整個過程整理並畫出時序圖。
測試代碼:

public partial class Form1 : Form { public Form1() { InitializeComponent(); } private void button1_Click(object sender, EventArgs e) { BLLObj bll = new BLLObj(); InterceptionUtil util = new InterceptionUtil(); util.Pipeline.InterceptionBehaviors.Add(new BehaviorA() { Id = "1" }); util.Pipeline.InterceptionBehaviors.Add(new BehaviorA() { Id = "2" }); util.Pipeline.InterceptionBehaviors.Add(new BehaviorA() { Id = "3" }); var r = util.Call(bll, "Add", new object[] { 3, 4 }); Console.WriteLine("結果:" + r); Console.WriteLine(new string('-', 40)); var r1 = util.Call(bll, "Concat", new object[]{ new object[] { "a",1, "b", 2 }}); Console.WriteLine("結果:" + r1); Console.WriteLine(new string('-', 40)); var r2 = util.Call(bll, "DoSome", new object[] { }); Console.WriteLine("結果:" + r2); } } #region 測試業務類 public class BLLObj { public int Add(int a, int b) { Console.WriteLine("Add"); return a + b; } public string Concat(object[] args) { Console.WriteLine("Concat"); return string.Concat(args); } public void DoSome() { Console.WriteLine("DoSome"); } } #endregion
(圖1)函數鏈調用的效果圖:
從圖1可以看到,在目標對象的目標方法被調用前包裹了3層攔截類的函數調用,正是這樣嵌套結構允許我們實現數據庫事務控制,錯誤記錄,權限控制,性能監測等一系列AOP應用.
實現代碼
下面的代碼涉及2個接口與5個類,話說人腦在同時考慮多個對象時會很受傷,所以俺在代碼里加了必要的注釋並在下面付上了調用序列圖,請配合起來看。

using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Reflection; namespace FuncLinkT2 { //兩個互相引用的接口,從定義的這一刻開始 //就注定了他們需要互相依靠,相伴一身....... //當然哥們的糾結也從此開始 public interface IInterceptionBehavior { MethodReturn Invoke(MethodInvocation input, IGetNextInterceptionBehavior nb); } public interface IGetNextInterceptionBehavior { IInterceptionBehavior GetNext(); } /// <summary> /// 作為IInterceptionBehavior.Invoke參數, /// 便於攔截行為類獲取當前攔截方法的信息 /// 包括被攔截對象與被攔截方法 /// </summary> public class MethodInvocation { public MethodBase MethodBase { get; set; } public object Target { get; set; } } /// <summary> /// 被攔截方法的返回結果包裝 /// 同樣便於攔截行為類獲取方法調用后接返回結果 /// </summary> public class MethodReturn { public object ReturnValue { get; set; } public Exception Err { get; set; } } #region 一個攔截行為類的實現 public class BehaviorA : IInterceptionBehavior { public string Id { get; set; } public MethodReturn Invoke(MethodInvocation input, IGetNextInterceptionBehavior nb) { Console.WriteLine("Behavior:" + Id + "->Invoke Befor!"); IInterceptionBehavior behavior = nb.GetNext(); var r = behavior.Invoke(input, nb); Console.WriteLine("Behavior:" + Id + "-> invoke After!"); return r; } } #endregion /// <summary> /// 實際對象調用代理類 /// 包裝成IInterceptionBehavior以便加到BehaviorPipeline末尾 /// </summary> public sealed class InterceptionInvocationProxy : IInterceptionBehavior { public object[] Args; public MethodInvocation Invocation; public object Target; public MethodReturn Invoke(MethodInvocation input, IGetNextInterceptionBehavior nb) { Console.WriteLine("InterceptionInvocationProxy->實際對象將被調用!"); var ret= new MethodReturn(); try { ret.ReturnValue = Invocation.MethodBase.Invoke(Target, Args); } catch (Exception ex) { ret.Err = ex; } return ret; } } public sealed class NextBehaviorsImp : IGetNextInterceptionBehavior { internal InterceptionBehaviorPipeline Pipeline; internal int InterceptorIndex; internal IInterceptionBehavior Target; public IInterceptionBehavior GetNext() { InterceptorIndex++; if (InterceptorIndex < Pipeline.InterceptionBehaviors.Count) { IInterceptionBehavior local1 = Pipeline.InterceptionBehaviors[InterceptorIndex]; return local1; } return Target; } } /// <summary> /// 攔截行為鏈條 /// </summary> public class InterceptionBehaviorPipeline { internal readonly List<IInterceptionBehavior> InterceptionBehaviors; // Properties public int Count { get { return InterceptionBehaviors.Count; } } // Methods public InterceptionBehaviorPipeline() { InterceptionBehaviors = new List<IInterceptionBehavior>(); } public MethodReturn Call(MethodInvocation input, IInterceptionBehavior target) { //無任何攔截行為 if (InterceptionBehaviors.Count == 0) { return target.Invoke(input, null); } //至少有一個IInterceptionBehavior NextBehaviorsImp nb = new NextBehaviorsImp(); nb.Target = target; nb.Pipeline = this; nb.InterceptorIndex = 0; return InterceptionBehaviors[0].Invoke(input, nb); } } public class InterceptionUtil { internal InterceptionBehaviorPipeline Pipeline = new InterceptionBehaviorPipeline(); public object Call(BLLObj targetObj, string methodName, object[] args) { InterceptionInvocationProxy proxy = new InterceptionInvocationProxy(); proxy.Target = targetObj; proxy.Args = args; proxy.Invocation = new MethodInvocation(); proxy.Invocation.MethodBase = proxy.Target.GetType().GetMethod(methodName); proxy.Invocation.Target = proxy.Target; //將目標類的方法封裝代理提供給調用連,開始進行攔截調用 MethodReturn ret= Pipeline.Call(proxy.Invocation, proxy); return ret.ReturnValue; } } }
(圖2)調用序列--務必鼠標右鍵找到圖片url后看大圖
參考
1.當然實現函數調用鏈不一定要搞這么麻煩,可以通過嵌套代理類來實現上面的調用效果.
地址:http://www.cnblogs.com/wdfrog/p/3162524.html
2.如果哥們覺得上面的看起來很輕松,那么你可以直接看MS使用委托代替上面兩個接口的實現代碼.
地址:http://www.cnblogs.com/wdfrog/archive/2010/10/23/1859021.html
至於為什么微軟要用委托(而且用了大量的匿名委托)來實現,估計是覺得用接口是java干的事情,為了跟其保持距離所以某個MS的程序員在領導的要求下寫出了這樣變態的代碼。