C#編程之AOP編程思想
一、什么是AOP
AOP:Aspect Oriented Programming的縮寫,意為面向切面編程,通過預(yù)編譯方式和運(yùn)行期間動(dòng)態(tài)代理實(shí)現(xiàn)程序功能的統(tǒng)一維護(hù)的一種技術(shù)。AOP是OOP思想的延續(xù)。利用AOP可以對(duì)業(yè)務(wù)邏輯的各個(gè)部分進(jìn)行隔離,從而使得業(yè)務(wù)邏輯各部分之間的耦合度降低,提高程序的可重用性,同時(shí)提高了開發(fā)的效率。
為什么要學(xué)習(xí)AOP呢?
AOP的應(yīng)用場(chǎng)景非常廣泛,在一些高級(jí)工程師或者架構(gòu)師的面試過程中,頻率出現(xiàn)的比較多。
二、編程思想的發(fā)展路線
1、POP
POP:Procedure Oriented Programming的縮寫,即面向過程編程,是一種以過程為中心的編程思想。
面向過程是分析出解決問題的步驟,然后用函數(shù)或者方法,把這些步驟一步一步的實(shí)現(xiàn),使用的時(shí)候在一個(gè)一個(gè)的一次調(diào)用函數(shù)或者方法,這就是面向過程編程。最開始的時(shí)候都是面向過程編程。面向過程是最為實(shí)際的一種思考方式。就算是面向?qū)ο缶幊蹋锩嬉彩前忻嫦蜻^程的編程思想,因?yàn)槊嫦蜻^程是一種基礎(chǔ)的編程思考方式,它從實(shí)際出發(fā)來考慮如何實(shí)現(xiàn)需求。
POP的不足:面向過程編程,只能處理一些簡單的問題,無法處理一些復(fù)雜的問題。如果問題很復(fù)雜,全部以流程來思考的話,會(huì)發(fā)現(xiàn)流程很混亂,甚至流程都不能進(jìn)行下去。
2、OOP
OOP:Object Oriented Programming的縮寫,即面向?qū)ο缶幊獭?/p>
早期的計(jì)算機(jī)編程都是面向過程的,因?yàn)樵缙诘木幊潭急容^簡單。但是隨著時(shí)間的發(fā)展,需求處理的問題越來越多,問題就會(huì)越來越復(fù)雜,這時(shí)就不能簡單的使用面向過程的編程了,就出現(xiàn)了面向?qū)ο缶幊?。在?jì)算機(jī)里面,把所有的東西都想象成一種事物。現(xiàn)實(shí)世界中的對(duì)象都有一些屬性和行為,也就對(duì)應(yīng)計(jì)算機(jī)中的屬性和方法。
面向?qū)ο缶幊叹褪前褬?gòu)成問題的事物分解成各個(gè)對(duì)象,建立對(duì)象的目的不是為了完成一個(gè)步驟,而是為了描述某個(gè)事物在整個(gè)解決問題的步驟中的行為。
我們以一棟大樓為例來說明OOP的不足。
我們把系統(tǒng)比喻成一棟樓,類或者對(duì)象是磚塊,磚塊組成了一面墻,多面墻構(gòu)成一間房間,多個(gè)房間構(gòu)成一棟樓。
這就好比一個(gè)模塊的功能是由多個(gè)類實(shí)現(xiàn),模塊又組成了某一項(xiàng)服務(wù),多個(gè)服務(wù)構(gòu)成了一個(gè)完整的系統(tǒng)。一個(gè)系統(tǒng)開發(fā)完成并不代表就真的完成了,以后肯定會(huì)有各種需求的變更出現(xiàn),需求變更出現(xiàn)以后就要去修改代碼,代碼都在類里面,這就相當(dāng)于去修改類。如果是小范圍的修改影響還不是很大,如果是大范圍的修改,影響就比較大了。即使每次修改都很小,但是如果經(jīng)常進(jìn)行修改,影響也會(huì)很大。就會(huì)造成系統(tǒng)的不穩(wěn)定。我們得出結(jié)論:類應(yīng)該是固定的,不應(yīng)該頻繁的去修改,甚至是不允許修改。這也是為什么有那么多的設(shè)計(jì)原則和設(shè)計(jì)模式。大部分的設(shè)計(jì)模式都是為了解決這類問題的,即在不修改類的前提下去擴(kuò)展功能。
OOP的不足:產(chǎn)生新的需求會(huì)導(dǎo)致程序代碼不斷的進(jìn)行修改,容易造成程序的不穩(wěn)定。
如果非常了解OOP的話,那么我們應(yīng)該知道,從對(duì)象的組織角度來講,分類方法都是以繼承關(guān)系為主線的,我們稱為縱向。如果只使用OOP思想的話,會(huì)帶來兩個(gè)問題:
1、共性問題。
2、擴(kuò)展問題,需要對(duì)先有類進(jìn)行擴(kuò)展時(shí)就比較困難了。
OOP與POP的區(qū)別:
在對(duì)比面向過程的時(shí)候,面向?qū)ο蟮姆椒ㄊ前咽挛镒钚』癁閷?duì)象,包括屬性和方法。當(dāng)程序的規(guī)模比較小的時(shí)候,面向過程編程還是有一些優(yōu)勢(shì)的,因?yàn)檫@時(shí)候程序的流程是比較容易梳理清楚的。以早上去上班為例,過程就是起床、穿衣、刷牙洗臉、去公司。每一步都是按照順序完成的,我們只需要按照步驟去一步一步的實(shí)現(xiàn)里面的方法就行了,最后在依次調(diào)用實(shí)現(xiàn)的方法即可,這就是面向過程開發(fā)。
如果使用面向?qū)ο缶幊?,我們就需要抽象出來一個(gè)員工類,該員工具有起床、穿衣、刷牙洗臉、去公司的四個(gè)方法。但是,最終要實(shí)現(xiàn)早上去上班的這個(gè)需求的話,還是要按照順序依次來調(diào)用四個(gè)方法。最開始的時(shí)候,我們是按照面向過程的思想來思考該需求,然后在按照面向?qū)ο蟮乃枷雭沓橄蟪鰩讉€(gè)方法,最終要實(shí)現(xiàn)這個(gè)需求,還是要按照面向過程的順序來實(shí)現(xiàn)。
面向?qū)ο蠛兔嫦蜻^程的區(qū)別僅僅是在思考問題方式上面的不同。最終你會(huì)發(fā)現(xiàn),在你實(shí)現(xiàn)這個(gè)需求的時(shí)候,即使使用了面向?qū)ο蟮乃枷氤橄蟪鰜砹藛T工類,但是最后還是要使用面向過程來實(shí)現(xiàn)這個(gè)需求。
3、AOP
AOP:Aspect Oriented Programming的縮寫,即面向切面編程。是對(duì)OOP的一種補(bǔ)充,在不修改原始類的情況下,給程序動(dòng)態(tài)添加統(tǒng)一功能的一種技術(shù)。
OOP關(guān)注的是將需求功能劃分為不同的并且相對(duì)獨(dú)立、封裝良好的類,依靠繼承和多態(tài)來定義彼此的關(guān)系。AOP能夠?qū)⑼ㄓ眯枨蠊δ軓牟幌嚓P(guān)的類中分離出來,很多類共享一個(gè)行為,一旦發(fā)生變化,不需要去修改很多類,只需要去修改這一個(gè)類即可。
AOP中的切面是指什么呢?切面指的是橫切關(guān)注點(diǎn)??聪旅嬉粡垐D:

OOP是為了將狀態(tài)和行為進(jìn)行模塊化。上圖是一個(gè)商場(chǎng)系統(tǒng),我們使用OOP將該系統(tǒng)縱向分為訂單管理、商品管理、庫存管理模塊。在該系統(tǒng)里面,我們要進(jìn)行授權(quán)驗(yàn)證。像訂單、商品、庫存都是業(yè)務(wù)邏輯功能,但是這三個(gè)模塊都需要一些共有的功能,比如說授權(quán)驗(yàn)證、日志記錄等。我們不可能在每個(gè)模塊里面都去寫授權(quán)驗(yàn)證,而且授權(quán)驗(yàn)證也不屬于具體的業(yè)務(wù),它其實(shí)屬于功能性模塊,并且會(huì)橫跨多個(gè)業(yè)務(wù)模塊??梢钥吹竭@里是橫向的,這就是所謂的切面。通俗的將,AOP就是將公用的功能給提取出來,如果以后這些公用的功能發(fā)生了變化,我們只需要修改這些公用功能的代碼即可,其它的地方就不需要去更改了。所謂的切面,就是只關(guān)注通用功能,而不關(guān)注業(yè)務(wù)邏輯,而且不修改原有的類。
AOP優(yōu)勢(shì):
- 將通用功能從業(yè)務(wù)邏輯中抽離出來,提高代碼復(fù)用性,有利于后期的維護(hù)和擴(kuò)展。
- 軟件設(shè)計(jì)時(shí),抽出通用功能(切面),有利于軟件設(shè)計(jì)的模塊化,降低軟件架構(gòu)的復(fù)雜度。
AOP的劣勢(shì):
- AOP的對(duì)OOP思想的一種補(bǔ)充,它無法單獨(dú)存在。如果說單獨(dú)使用AOP去設(shè)計(jì)一套系統(tǒng)是不可能的。在設(shè)計(jì)系統(tǒng)的時(shí)候,如果系統(tǒng)比較簡單,那么可以只使用POP或者OOP來設(shè)計(jì)。如果系統(tǒng)很復(fù)雜,就需要使用AOP思想。首先要使用POP來梳理整個(gè)業(yè)務(wù)流程,然后根據(jù)POP的流程,去整理類和模塊,最后在使用AOP來抽取通用功能。
AOP和OOP的區(qū)別:
- 面向目標(biāo)不同:OOP是面向名詞領(lǐng)域(抽象出來一個(gè)事物,比如學(xué)生、員工,這些都是名詞)。AOP是面向動(dòng)詞領(lǐng)域(比如鑒權(quán)、記錄日志,這些都是動(dòng)作或行為)。
- 思想結(jié)構(gòu)不同:OOP是縱向的(以繼承為主線,所以是縱向的)。AOP是橫向的。
- 注重方面不同:OOP是注重業(yè)務(wù)邏輯單元的劃分,AOP偏重業(yè)務(wù)處理過程中的某個(gè)步驟或階段。
POP、OOP、AOP三種思想是相互補(bǔ)充的。在一個(gè)系統(tǒng)的開發(fā)過程中,這三種編程思想是不可或缺的。
三、實(shí)現(xiàn)AOP
我們?cè)谏厦嬷v解了有關(guān)AOP的一些理論知識(shí),那么如何在代碼里面實(shí)現(xiàn)呢?
實(shí)現(xiàn)AOP有兩種方式:
- 靜態(tài)代理實(shí)現(xiàn)。所謂靜態(tài)代理,就是我們自己來寫代理對(duì)象。
- 動(dòng)態(tài)代理實(shí)現(xiàn)。所謂動(dòng)態(tài)代理,就是在程序運(yùn)行時(shí),去生成一個(gè)代理對(duì)象。
1、靜態(tài)代理
實(shí)現(xiàn)靜態(tài)代理需要使用到兩種設(shè)計(jì)模式:裝飾器模式和代理模式。
裝飾器模式:允許向一個(gè)現(xiàn)有的對(duì)象添加新的功能,同時(shí)又不改變這個(gè)現(xiàn)有對(duì)象的結(jié)構(gòu)。屬于結(jié)構(gòu)型設(shè)計(jì)模式,它是作為現(xiàn)有類的一種包裝。首先會(huì)創(chuàng)建一個(gè)裝飾類,用來包裝原有的類,并在保持類的完整性的前提下,提供額外的功能。看下面的例子。
我們首先創(chuàng)建一個(gè)User類:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace StaticDemo.Model
{
public class User
{
public string Name { get; set; }
public string Password { get; set; }
}
}接著我們創(chuàng)建一個(gè)賬號(hào)服務(wù)的接口,里面有一個(gè)方法,用來注冊(cè)一個(gè)用戶:
using StaticDemo.Model;
namespace StaticDemo.Services
{
/// <summary>
/// 接口
/// </summary>
public interface IAccountService
{
/// <summary>
/// 注冊(cè)用戶
/// </summary>
/// <param name="user"></param>
void Reg(User user);
}
}然后創(chuàng)建一個(gè)類來實(shí)現(xiàn)上面的接口:
using StaticDemo.Model;
using System;
namespace StaticDemo.Services
{
/// <summary>
/// 實(shí)現(xiàn)IAccountService接口
/// </summary>
public class AccountService : IAccountService
{
public void Reg(User user)
{
// 業(yè)務(wù)代碼 之前 或者之后執(zhí)行一些其它的邏輯
Console.WriteLine($"{user.Name}注冊(cè)成功");
}
}
}我們?cè)趧?chuàng)建一個(gè)裝飾器類:
using StaticDemo.Model;
using StaticDemo.Services;
using System;
namespace StaticDemo
{
/// <summary>
/// 裝飾器類
/// </summary>
public class AccountDecorator : IAccountService
{
private readonly IAccountService _accountService;
public AccountDecorator(IAccountService accountService)
{
_accountService = accountService;
}
public void Reg(User user)
{
Before();
// 這里調(diào)用注冊(cè)的方法,原有類里面的邏輯不會(huì)改變
// 在邏輯前面和后面分別添加其他邏輯
_accountService.Reg(user);
After();
}
private void Before()
{
Console.WriteLine("注冊(cè)之前的邏輯");
}
private void After()
{
Console.WriteLine("注冊(cè)之后的邏輯");
}
}
}我們會(huì)發(fā)現(xiàn)裝飾器類同樣實(shí)現(xiàn)了IAccountService接口。最后我們?cè)贛ain方法里面調(diào)用:
using StaticDemo.Model;
using StaticDemo.Services;
using System;
namespace StaticDemo
{
class Program
{
static void Main(string[] args)
{
// 實(shí)例化對(duì)象
IAccountService accountService = new AccountService();
// 實(shí)例化裝飾器類,并用上面的實(shí)例給構(gòu)造方法傳值
var account = new AccountDecorator(accountService);
var user = new User { Name = "Rick", Password = "12345678" };
// 調(diào)用裝飾器類的注冊(cè)方法,相當(dāng)于調(diào)用實(shí)例化對(duì)象的注冊(cè)方法
account.Reg(user);
Console.ReadKey();
}
}
}運(yùn)行結(jié)果:

下面我們?cè)趤砜纯慈绾问褂么砟J綄?shí)現(xiàn)。
代理模式:即一個(gè)類代表另一個(gè)類的功能。我們會(huì)創(chuàng)建一個(gè)代理類,這個(gè)代理類和裝飾器類基本一樣。看一下代碼:
using StaticDemo.Model;
using StaticDemo.Services;
using System;
namespace StaticDemo
{
/// <summary>
/// 代理類
/// </summary>
public class ProxyAccount : IAccountService
{
private readonly IAccountService _accountService;
/// <summary>
/// 構(gòu)造函數(shù)沒有參數(shù)
/// 直接在里面創(chuàng)建了AccountService類
/// </summary>
public ProxyAccount()
{
_accountService = new AccountService();
}
public void Reg(User user)
{
before();
_accountService.Reg(user);
after();
}
private void before()
{
Console.WriteLine("代理:注冊(cè)之前的邏輯");
}
private void after()
{
Console.WriteLine("代理:注冊(cè)之后的邏輯");
}
}
}Main方法里面調(diào)用:
using StaticDemo.Model;
using StaticDemo.Services;
using System;
namespace StaticDemo
{
class Program
{
static void Main(string[] args)
{
#region 裝飾器模式
//// 實(shí)例化對(duì)象
//IAccountService accountService = new AccountService();
//// 實(shí)例化裝飾器類,并用上面的實(shí)例給構(gòu)造方法傳值
//var account = new AccountDecorator(accountService);
//var user = new User { Name = "Rick", Password = "12345678" };
//// 調(diào)用裝飾器類的注冊(cè)方法,相當(dāng)于調(diào)用實(shí)例化對(duì)象的注冊(cè)方法
//account.Reg(user);
#endregion
#region 代理模式
var account = new ProxyAccount();
var user = new User { Name = "Tom", Password = "12345678" };
account.Reg(user);
#endregion
Console.ReadKey();
}
}
}運(yùn)行結(jié)果:

可能有的人會(huì)發(fā)現(xiàn),裝飾器類和代理類很相像,功能也一模一樣,僅僅是構(gòu)造函數(shù)不同。那么裝飾器模式和代理模式有區(qū)別嗎?有些東西,形式上看起來區(qū)別很小,但實(shí)際上他們區(qū)別很大。它們?cè)谛问缴洗_實(shí)一樣,不管是裝飾器類還是代理類,它們都要實(shí)現(xiàn)相同的接口,但是它們?cè)谶\(yùn)用的時(shí)候還是有區(qū)別的。
裝飾器模式關(guān)注于在一個(gè)對(duì)象上動(dòng)態(tài)添加方法,而代理模式關(guān)注于控制對(duì)象的訪問。簡單來說,使用代理模式,我們的代理類可以隱藏一個(gè)類的具體信息。var account = new ProxyAccount();僅看這段代碼不看源碼,不知道里面代理的是誰。
當(dāng)使用代理模式的時(shí)候,我們常常是在代理類中去創(chuàng)建一個(gè)對(duì)象的實(shí)例:_accountService = new AccountService()。而當(dāng)我們使用裝飾器模式的時(shí)候,我們通常是將原始對(duì)象作為一個(gè)參數(shù)傳遞給裝飾器的構(gòu)造函數(shù)。簡單來說,在使用裝飾器模式的時(shí)候,我們可以明確地知道裝飾的是誰,而且更重要的是,代理類里面是寫死的,在編譯的時(shí)候就確定了關(guān)系。而裝飾器是在運(yùn)行時(shí)來確定的。
2、動(dòng)態(tài)代理
動(dòng)態(tài)代理實(shí)現(xiàn)也有兩種方式;
- 通過代碼織入的方式。例如PostSharp第三方插件。我們知道.NET程序最終會(huì)編譯成IL中間語言,在編譯程序的時(shí)候,PostSharp會(huì)動(dòng)態(tài)的去修改IL,在IL里面添加代碼,這就是代碼織入的方式。
- 通過反射的方式實(shí)現(xiàn)。通過反射實(shí)現(xiàn)的方法非常多,也有很多實(shí)現(xiàn)了AOP的框架,例如Unity、MVC過濾器、Autofac等。
我們先來看看如何使用PostSharp實(shí)現(xiàn)動(dòng)態(tài)代理。PostSharp是一款收費(fèi)的第三方插件。
首先新創(chuàng)建一個(gè)控制臺(tái)應(yīng)用程序,然后創(chuàng)建一個(gè)訂單業(yè)務(wù)類:
using System;
namespace PostSharpDemo
{
/// <summary>
/// 訂單業(yè)務(wù)類
/// </summary>
public class OrderBusiness
{
public void DoWork()
{
Console.WriteLine("執(zhí)行訂單業(yè)務(wù)");
}
}
}接著在Main方法里面調(diào)用:
using System;
namespace PostSharpDemo
{
class Program
{
static void Main(string[] args)
{
OrderBusiness order = new OrderBusiness();
// 調(diào)用方法
order.DoWork();
Console.ReadKey();
}
}
}運(yùn)行結(jié)果:

這時(shí)又提出了一個(gè)新的需求,要去添加一個(gè)日志功能,記錄業(yè)務(wù)的執(zhí)行情況,按照以前的辦法,需要定義一個(gè)日志幫助類:
using System;
using System.IO;
namespace PostSharpDemo
{
public class LgoHelper
{
public static void RecoreLog(string message)
{
string strPath = AppDomain.CurrentDomain.BaseDirectory+"\\log.txt";
using(StreamWriter sw=new StreamWriter(strPath,true))
{
sw.WriteLine(message);
sw.Close();
}
}
}
}如果不使用AOP,我們就需要在記錄日志的地方實(shí)例化Loghelper對(duì)象,然后記錄日志:
using System;
namespace PostSharpDemo
{
/// <summary>
/// 訂單業(yè)務(wù)類
/// </summary>
public class OrderBusiness
{
public void DoWork()
{
// 記錄日志
LgoHelper.RecoreLog("執(zhí)行業(yè)務(wù)前");
Console.WriteLine("執(zhí)行訂單業(yè)務(wù)");
LgoHelper.RecoreLog("執(zhí)行業(yè)務(wù)后");
}
}
}我們?cè)俅芜\(yùn)行程序,查看結(jié)果:

我們看看日志內(nèi)容:

這樣修改可以實(shí)現(xiàn)記錄日志的功能。但是上面的方法會(huì)修改原先已有的代碼,這就違反了開閉原則。而且添加日志也不是業(yè)務(wù)需求的變動(dòng),不應(yīng)該去修改業(yè)務(wù)代碼。下面使用AOP來實(shí)現(xiàn)。首先安裝PostSharp,直接在NuGet里面搜索,然后安裝即可:

然后定義一個(gè)LogAttribute類,繼承自O(shè)nMethodBoundaryAspect。這個(gè)Aspect提供了進(jìn)入、退出函數(shù)等連接點(diǎn)方法。另外,Aspect上必須設(shè)置“[Serializable] ”,這與PostSharp內(nèi)部對(duì)Aspect的生命周期管理有關(guān):
using PostSharp.Aspects;
using System;
namespace PostSharpDemo
{
[Serializable]
[AttributeUsage(AttributeTargets.Method, AllowMultiple = false, Inherited = true)]
public class LogAttribute: OnMethodBoundaryAspect
{
public string ActionName { get; set; }
public override void OnEntry(MethodExecutionArgs eventArgs)
{
LgoHelper.RecoreLog(ActionName + "開始執(zhí)行業(yè)務(wù)前");
}
public override void OnExit(MethodExecutionArgs eventArgs)
{
LgoHelper.RecoreLog(ActionName + "業(yè)務(wù)執(zhí)行完成后");
}
}
}然后Log特性應(yīng)用到DoWork函數(shù)上面:
using System;
namespace PostSharpDemo
{
/// <summary>
/// 訂單業(yè)務(wù)類
/// </summary>
public class OrderBusiness
{
[Log(ActionName ="DoWork")]
public void DoWork()
{
// 記錄日志
// LgoHelper.RecoreLog("執(zhí)行業(yè)務(wù)前");
Console.WriteLine("執(zhí)行訂單業(yè)務(wù)");
// LgoHelper.RecoreLog("執(zhí)行業(yè)務(wù)后");
}
}
}這樣修改以后,只需要在方法上面添加一個(gè)特性,以前記錄日志的代碼就可以注釋掉了,這樣就不會(huì)再修改業(yè)務(wù)邏輯代碼了,運(yùn)行程序:

在看看日志:

這樣就實(shí)現(xiàn)了AOP功能。
我們?cè)诳纯词褂肦emoting來實(shí)現(xiàn)動(dòng)態(tài)代理。
首先還是創(chuàng)建一個(gè)User實(shí)體類:
namespace DynamicProxy.Model
{
public class User
{
public string Name { get; set; }
public string Password { get; set; }
}
}然后創(chuàng)建一個(gè)接口,里面有一個(gè)注冊(cè)方法:
using DynamicProxy.Model;
namespace DynamicProxy.Services
{
public interface IAccountService
{
void Reg(User user);
}
}然后創(chuàng)建接口的實(shí)現(xiàn)類:
using DynamicProxy.Model;
using System;
namespace DynamicProxy.Services
{
public class AccountService : MarshalByRefObject, IAccountService
{
public void Reg(User user)
{
Console.WriteLine($"{user.Name}注冊(cè)成功");
}
}
}然后創(chuàng)建一個(gè)泛型的動(dòng)態(tài)代理類:
using System;
using System.Runtime.Remoting;
using System.Runtime.Remoting.Messaging;
using System.Runtime.Remoting.Proxies;
namespace DynamicProxy
{
public class DynamicProxy<T> : RealProxy
{
private readonly T _target;
// 執(zhí)行之前
public Action BeforeAction { get; set; }
// 執(zhí)行之后
public Action AfterAction { get; set; }
// 被代理泛型類
public DynamicProxy(T target) : base(typeof(T))
{
_target = target;
}
// 代理類調(diào)用方法
public override IMessage Invoke(IMessage msg)
{
var reqMsg = msg as IMethodCallMessage;
var target = _target as MarshalByRefObject;
BeforeAction();
// 這里才真正去執(zhí)行代理類里面的方法
// target表示被代理的對(duì)象,reqMsg表示要執(zhí)行的方法
var result = RemotingServices.ExecuteMessage(target, reqMsg);
AfterAction();
return result;
}
}
}我們看到,這個(gè)泛型動(dòng)態(tài)代理類里面有兩個(gè)泛型委托:BeforeAction、AfterAction。通過構(gòu)造函數(shù)把代理泛型類傳遞進(jìn)去。最后調(diào)用Invoke方法執(zhí)行代理類的方法。
最后我們還要?jiǎng)?chuàng)建一個(gè)代理工廠類,用來創(chuàng)建代理對(duì)象,通過調(diào)用動(dòng)態(tài)代理來創(chuàng)建動(dòng)態(tài)代理對(duì)象:
using System;
namespace DynamicProxy
{
/// <summary>
/// 動(dòng)態(tài)代理工廠類
/// </summary>
public static class ProxyFactory
{
public static T Create<T>(Action before, Action after)
{
// 實(shí)例化被代理泛型對(duì)象
T instance = Activator.CreateInstance<T>();
// 實(shí)例化動(dòng)態(tài)代理,創(chuàng)建動(dòng)態(tài)代理對(duì)象
var proxy = new DynamicProxy<T>(instance) { BeforeAction = before, AfterAction = after };
// 返回透明代理對(duì)象
return (T)proxy.GetTransparentProxy();
}
}
}我們最后在Main方法里面調(diào)用:
using DynamicProxy.Model;
using DynamicProxy.Services;
using System;
namespace DynamicProxy
{
class Program
{
static void Main(string[] args)
{
// 調(diào)用動(dòng)態(tài)代理工廠類創(chuàng)建動(dòng)態(tài)代理對(duì)象,傳遞AccountService,并且傳遞兩個(gè)委托
var acount = ProxyFactory.Create<AccountService>(before:() =>
{
Console.WriteLine("注冊(cè)之前");
}, after:() =>
{
Console.WriteLine("注冊(cè)之后");
});
User user = new User()
{
Name="張三",
Password="123456"
};
// 調(diào)用注冊(cè)方法
acount.Reg(user);
Console.ReadKey();
}
}
}程序運(yùn)行結(jié)果:

這樣就利用Remoting實(shí)現(xiàn)了動(dòng)態(tài)代理。
GitHub地址:https://github.com/jxl1024/AOP
到此這篇關(guān)于C#編程之AOP編程思想的文章就介紹到這了。希望對(duì)大家的學(xué)習(xí)有所幫助,也希望大家多多支持腳本之家。
相關(guān)文章
基于C#實(shí)現(xiàn)一個(gè)溫濕度監(jiān)測(cè)小工具
這篇文章主要為大家詳細(xì)介紹了如何基于C#實(shí)現(xiàn)一個(gè)溫濕度監(jiān)測(cè)小工具,文中的示例代碼講解詳細(xì),具有一定的借鑒價(jià)值,需要的可以參考一下2023-01-01
adonet基礎(chǔ)示例分享(adonet連接數(shù)據(jù)庫)
這篇文章主要介紹了adonet基礎(chǔ)示例分享(adonet連接數(shù)據(jù)庫),需要的朋友可以參考下2014-04-04
基于NET?Core?的Nuget包制作、發(fā)布和運(yùn)用流程解析(完整過程)
這篇文章主要介紹了基于NET?Core?的Nuget包制作、發(fā)布和運(yùn)用流程,本文通過圖文并茂的形式給大家介紹了Nuget包制作過程,感興趣的朋友跟隨小編一起看看吧2022-02-02
C# WinForm創(chuàng)建Excel文件的實(shí)例
下面小編就為大家?guī)硪黄狢# WinForm創(chuàng)建Excel文件的實(shí)例。小編覺得挺不錯(cuò)的,現(xiàn)在就分享給大家,也給大家做個(gè)參考。一起跟隨小編過來看看吧2017-01-01
通過C#實(shí)現(xiàn)自動(dòng)售貨機(jī)接口
這篇文章主要介紹了通過C#實(shí)現(xiàn)自動(dòng)售貨機(jī)接口,需要的朋友可以參考下2015-07-07
C#分析URL參數(shù)并獲取參數(shù)和值對(duì)應(yīng)列表的方法
這篇文章主要介紹了C#分析URL參數(shù)獲取參數(shù)和值對(duì)應(yīng)列表的方法,涉及C#進(jìn)行URL分析及正則表達(dá)式的使用技巧,具有一定參考借鑒價(jià)值,需要的朋友可以參考下2015-03-03
c#中SAPI使用總結(jié)——SpVoice的使用方法
最近使用C#重做了點(diǎn)名系統(tǒng)(要用到TTS,讓計(jì)算機(jī)點(diǎn)名)使用了SAPI,在這里總結(jié)一下SpVoice的使用方法。2011-10-10
C# FileStream實(shí)現(xiàn)多線程斷點(diǎn)續(xù)傳
這篇文章主要為大家詳細(xì)介紹了C# FileStream實(shí)現(xiàn)多線程斷點(diǎn)續(xù)傳,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2018-03-03

