AOP (Aspect Oriented Programming,面向切面程式設計) 與 OOP 同為程式設計範式。核心思想:在不修改原有業務邏輯 (Original Logic) 的前提下,將可重用的橫切關注點 (Cross-cutting Concerns / Other Logic) 「注入」到目標方法執行流程中。
典型切面:
AOP 將這些「散佈在各處、難以復用」的邏輯提取為獨立 Aspect (切面),實現 解耦 與 代碼簡潔化。
public void SaveRetireUserInfo(User user)
{
using (TransactionScope transaction = new TransactionScope())
{
try
{
RetireUser retireUser = new RetireUser();
retireUser.Name = user.Name;
retireUser.Department = user.Department;
// ...
user.Delete();
retireUser.Save();
transaction.VoteCommit();
}
catch (Exception ex)
{
Log.Write(ex.Message);
transaction.VoteRollBack();
}
}
}
[AOPProxy(Interception = typeof(TransactionInterception))]
public class AOPTest : ContextBoundObject
{
public void SaveRetireUserInfo(User user)
{
RetireUser retireUser = new RetireUser();
retireUser.Name = user.Name;
retireUser.Department = user.Department;
// ...
user.Delete();
retireUser.Save();
// 完全看不見 Transaction/Try-Catch!
}
}
public class TransactionInterception : IInterception
{
private TransactionScope transaction;
public void PreInvoke() { transaction = new TransactionScope(); }
public void PostInvoke() { transaction.VoteCommit(); }
public void ExceptionHandle() { transaction.VoteRollBack(); }
}
執行流程:
PreInvoke() — 方法執行前ExceptionHandle();否則 → PostInvoke()代理 = 中間層,保護真實主題 (Real Subject),在轉發呼叫時執行額外邏輯。
生活類比:你是巨星 SexyBaby,只負責唱歌;經紀人 Underdog 負責接洽、合約、行程。外界只能透過經紀人聯繫你。
Url, ShowInformation())ISite,內部持有真實 ISiteISite 實例Parse(ISite))ChinaDNSFilter:攔截 AVSite → 回傳 WarnningSiteUSADNSFilter:放行所有關鍵程式碼片段:
// MyProxySite.ShowInformation()
Console.WriteLine("Proxy start!");
Console.WriteLine("Real url:{0}", _site.Url);
_site = new USADNSFilter().Parse(_site); // 換用美規則過濾
_site.ShowInformation(); // 真正顯示 AVSite 內容
Console.WriteLine("Proxy end!");



public interface IInterception
{
void PreInvoke(); // 方法前
void PostInvoke(); // 方法後 (無例外)
void ExceptionHandle(); // 方法拋出例外
}
NullInterceptionpublic class NullInterception : IInterception
{
public virtual void PreInvoke() { }
public virtual void PostInvoke() { }
public void ExceptionHandle() { }
}
避免 Null 檢查,預設「不做任何事」。
AOPRealProxy 繼承 RealProxy利用 System.Runtime.Remoting.Messaging.IMessage 攔截訊息,類似 MFC Hook。
class AOPRealProxy : RealProxy, IProxyDI
{
private MarshalByRefObject _target;
private IInterception _interception = new NullInterception();
public AOPRealProxy(Type targetType, MarshalByRefObject target) : base(targetType)
{
_target = target;
}
public override IMessage Invoke(IMessage msg)
{
IMethodReturnMessage methodReturnMessage = null;
IMethodCallMessage methodCallMessage = msg as IMethodCallMessage;
if (methodCallMessage != null)
{
IConstructionCallMessage ccm = methodCallMessage as IConstructionCallMessage;
if (ccm != null) // 建構子
{
RealProxy defaultProxy = RemotingServices.GetRealProxy(_target);
defaultProxy.InitializeServerObject(ccm);
methodReturnMessage = EnterpriseServicesHelper.CreateConstructionReturnMessage(ccm, (MarshalByRefObject)GetTransparentProxy());
}
else // 一般方法
{
_interception.PreInvoke();
try
{
methodReturnMessage = RemotingServices.ExecuteMessage(_target, methodCallMessage);
}
catch { }
if (methodReturnMessage.Exception != null)
_interception.ExceptionHandle();
else
_interception.PostInvoke();
}
}
return methodReturnMessage;
}
public void InterceptionDI(IInterception interception) { _interception = interception; }
}
IProxyDIinterface IProxyDI { void InterceptionDI(IInterception interception); }
方式一:Proxy Factory (程式碼式)
public class ProxyFactory
{
public static T CreateProxyInstance<T>(IInterception interception) where T : new()
{
Type serverType = typeof(T);
MarshalByRefObject target = Activator.CreateInstance(serverType) as MarshalByRefObject;
AOPRealProxy aopRealProxy = new AOPRealProxy(serverType, target);
aopRealProxy.InterceptionDI(interception);
return (T)aopRealProxy.GetTransparentProxy();
}
}
// 使用
IInterception interception = new MyInterception();
var proxy = ProxyFactory.CreateProxyInstance<AOPTestWithProxyClass>(interception);
proxy.Show();
方式二:Attribute (聲明式) — 繼承 ProxyAttribute
[AttributeUsage(AttributeTargets.Class)]
public class AOPProxyAttribute : ProxyAttribute
{
private IInterception _interception = new NullInterception();
public Type Interception
{
get { return _interception.GetType(); }
set { _interception = Activator.CreateInstance(value) as IInterception; }
}
public override MarshalByRefObject CreateInstance(Type serverType)
{
MarshalByRefObject target = base.CreateInstance(serverType);
AOPRealProxy aopRealProxy = new AOPRealProxy(serverType, target);
aopRealProxy.InterceptionDI(_interception);
return aopRealProxy.GetTransparentProxy() as MarshalByRefObject;
}
}
// 使用:目標類別必須繼承 ContextBoundObject
[AOPProxy(Interception = typeof(MyInterception))]
public class AOPTestWithAttribute : ContextBoundObject
{
public void Show() { Console.WriteLine("Hello, I am AOPTest."); }
}
// 直接 new 即可,Framework 自動攔截
var obj = new AOPTestWithAttribute();
obj.Show();
public class MyInterception : IInterception
{
public void PreInvoke() { Console.WriteLine("===Pre MyInterception.==="); }
public void PostInvoke() { Console.WriteLine("===Post MyInterception.==="); }
public void ExceptionHandle() { Console.WriteLine("There is a exception!"); }
}


兩種方式均正確輸出 Pre/Post 環繞。
[AOPProxy(Interception = typeof(TransactionInterception))]
public class AOPTestWithAttributForTrasaction : ContextBoundObject
{
public void ExecuteTransactionSuccessfully() { Console.WriteLine("Execute successfully :)"); }
public void ExecuteTransactionUnsuccessfully() { Console.WriteLine("Execute unsuccessfully :("); throw new AOPNullException(); }
}
public class TransactionInterception : IInterception
{
public void PreInvoke() { Console.WriteLine("☆☆☆Begin transaction☆☆☆"); }
public void PostInvoke() { Console.WriteLine("☆☆☆Commit transaction☆☆☆"); }
public void ExceptionHandle() { Console.WriteLine("☆☆☆Rollback transaction☆☆☆"); }
}

成功路徑:Begin → Commit;失敗路徑:Begin → Rollback + ExceptionHandle。
> 💡 **注意**:Debug 模式下throw 會中斷,按 F5 繼續即可;Release 或直接執行 exe 不受影響。
| 維度 | Proxy Factory | Attribute (ContextBoundObject) |
|---|---|---|
| 使用方式 | 程式碼建立 Proxy 實例 | 聲明式,直接 new |
| 目標類別限制 | 繼承 MarshalByRefObject | 繼承 ContextBoundObject (更重) |
| 效能 | 較輕量 | Context 邊界轉換開銷較大 |
| 適用場景 | 局部、動態決定是否 AOP | 全域、框架級、慣用模式 |
| 現代替代方案 | Castle DynamicProxy / Autofac Interception / PostSharp | 同上 |
AOP 本質是 「橫切邏輯的外部化與注入」。本文展示的輕量框架基於 RealProxy + 訊息攔截,雖非生產級 (受限於 ContextBoundObject 效能與功能),但完整呈現了:
現代 .NET 專案建議採用 Castle DynamicProxy (輕量、無 Context 限制) 或 PostSharp (編織期 IL 修改、零執行期 Proxy 開銷)。理解本文核心機制,有助於深入掌握各類 AOP 框架的底層運作原理。