日本免费高清视频-国产福利视频导航-黄色在线播放国产-天天操天天操天天操天天操|www.shdianci.com

學(xué)無(wú)先后,達(dá)者為師

網(wǎng)站首頁(yè) 編程語(yǔ)言 正文

C#編程之AOP編程思想_C#教程

作者:.NET開(kāi)發(fā)菜鳥(niǎo) ? 更新時(shí)間: 2022-05-21 編程語(yǔ)言

一、什么是AOP

AOP:Aspect Oriented Programming的縮寫(xiě),意為面向切面編程,通過(guò)預(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í)提高了開(kāi)發(fā)的效率。

為什么要學(xué)習(xí)AOP呢?

AOP的應(yīng)用場(chǎng)景非常廣泛,在一些高級(jí)工程師或者架構(gòu)師的面試過(guò)程中,頻率出現(xiàn)的比較多。

二、編程思想的發(fā)展路線

1、POP

POP:Procedure Oriented Programming的縮寫(xiě),即面向過(guò)程編程,是一種以過(guò)程為中心的編程思想。

面向過(guò)程是分析出解決問(wèn)題的步驟,然后用函數(shù)或者方法,把這些步驟一步一步的實(shí)現(xiàn),使用的時(shí)候在一個(gè)一個(gè)的一次調(diào)用函數(shù)或者方法,這就是面向過(guò)程編程。最開(kāi)始的時(shí)候都是面向過(guò)程編程。面向過(guò)程是最為實(shí)際的一種思考方式。就算是面向?qū)ο缶幊蹋锩嬉彩前忻嫦蜻^(guò)程的編程思想,因?yàn)槊嫦蜻^(guò)程是一種基礎(chǔ)的編程思考方式,它從實(shí)際出發(fā)來(lái)考慮如何實(shí)現(xiàn)需求。

POP的不足:面向過(guò)程編程,只能處理一些簡(jiǎn)單的問(wèn)題,無(wú)法處理一些復(fù)雜的問(wèn)題。如果問(wèn)題很復(fù)雜,全部以流程來(lái)思考的話,會(huì)發(fā)現(xiàn)流程很混亂,甚至流程都不能進(jìn)行下去。

2、OOP

OOP:Object Oriented Programming的縮寫(xiě),即面向?qū)ο缶幊獭?/p>

早期的計(jì)算機(jī)編程都是面向過(guò)程的,因?yàn)樵缙诘木幊潭急容^簡(jiǎn)單。但是隨著時(shí)間的發(fā)展,需求處理的問(wèn)題越來(lái)越多,問(wèn)題就會(huì)越來(lái)越復(fù)雜,這時(shí)就不能簡(jiǎn)單的使用面向過(guò)程的編程了,就出現(xiàn)了面向?qū)ο缶幊獭T谟?jì)算機(jī)里面,把所有的東西都想象成一種事物。現(xiàn)實(shí)世界中的對(duì)象都有一些屬性和行為,也就對(duì)應(yīng)計(jì)算機(jī)中的屬性和方法。

面向?qū)ο缶幊叹褪前褬?gòu)成問(wèn)題的事物分解成各個(gè)對(duì)象,建立對(duì)象的目的不是為了完成一個(gè)步驟,而是為了描述某個(gè)事物在整個(gè)解決問(wèn)題的步驟中的行為。

我們以一棟大樓為例來(lái)說(shuō)明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)開(kāi)發(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ì)模式都是為了解決這類問(wèn)題的,即在不修改類的前提下去擴(kuò)展功能。

OOP的不足:產(chǎn)生新的需求會(huì)導(dǎo)致程序代碼不斷的進(jìn)行修改,容易造成程序的不穩(wěn)定。

如果非常了解OOP的話,那么我們應(yīng)該知道,從對(duì)象的組織角度來(lái)講,分類方法都是以繼承關(guān)系為主線的,我們稱為縱向。如果只使用OOP思想的話,會(huì)帶來(lái)兩個(gè)問(wèn)題:
1、共性問(wèn)題。
2、擴(kuò)展問(wèn)題,需要對(duì)先有類進(jìn)行擴(kuò)展時(shí)就比較困難了。

OOP與POP的區(qū)別:

在對(duì)比面向過(guò)程的時(shí)候,面向?qū)ο蟮姆椒ㄊ前咽挛镒钚』癁閷?duì)象,包括屬性和方法。當(dāng)程序的規(guī)模比較小的時(shí)候,面向過(guò)程編程還是有一些優(yōu)勢(shì)的,因?yàn)檫@時(shí)候程序的流程是比較容易梳理清楚的。以早上去上班為例,過(guò)程就是起床、穿衣、刷牙洗臉、去公司。每一步都是按照順序完成的,我們只需要按照步驟去一步一步的實(shí)現(xiàn)里面的方法就行了,最后在依次調(diào)用實(shí)現(xiàn)的方法即可,這就是面向過(guò)程開(kāi)發(fā)。

如果使用面向?qū)ο缶幊蹋覀兙托枰橄蟪鰜?lái)一個(gè)員工類,該員工具有起床、穿衣、刷牙洗臉、去公司的四個(gè)方法。但是,最終要實(shí)現(xiàn)早上去上班的這個(gè)需求的話,還是要按照順序依次來(lái)調(diào)用四個(gè)方法。最開(kāi)始的時(shí)候,我們是按照面向過(guò)程的思想來(lái)思考該需求,然后在按照面向?qū)ο蟮乃枷雭?lái)抽象出幾個(gè)方法,最終要實(shí)現(xiàn)這個(gè)需求,還是要按照面向過(guò)程的順序來(lái)實(shí)現(xiàn)。

面向?qū)ο蠛兔嫦蜻^(guò)程的區(qū)別僅僅是在思考問(wèn)題方式上面的不同。最終你會(huì)發(fā)現(xiàn),在你實(shí)現(xiàn)這個(gè)需求的時(shí)候,即使使用了面向?qū)ο蟮乃枷氤橄蟪鰜?lái)了員工類,但是最后還是要使用面向過(guò)程來(lái)實(shí)現(xiàn)這個(gè)需求。

3、AOP

AOP:Aspect Oriented Programming的縮寫(xiě),即面向切面編程。是對(duì)OOP的一種補(bǔ)充,在不修改原始類的情況下,給程序動(dòng)態(tài)添加統(tǒng)一功能的一種技術(shù)。

OOP關(guān)注的是將需求功能劃分為不同的并且相對(duì)獨(dú)立、封裝良好的類,依靠繼承和多態(tài)來(lái)定義彼此的關(guān)系。AOP能夠?qū)⑼ㄓ眯枨蠊δ軓牟幌嚓P(guān)的類中分離出來(lái),很多類共享一個(gè)行為,一旦發(fā)生變化,不需要去修改很多類,只需要去修改這一個(gè)類即可。

AOP中的切面是指什么呢?切面指的是橫切關(guān)注點(diǎn)。看下面一張圖:

OOP是為了將狀態(tài)和行為進(jìn)行模塊化。上圖是一個(gè)商場(chǎng)系統(tǒng),我們使用OOP將該系統(tǒng)縱向分為訂單管理、商品管理、庫(kù)存管理模塊。在該系統(tǒng)里面,我們要進(jìn)行授權(quán)驗(yàn)證。像訂單、商品、庫(kù)存都是業(yè)務(wù)邏輯功能,但是這三個(gè)模塊都需要一些共有的功能,比如說(shuō)授權(quán)驗(yàn)證、日志記錄等。我們不可能在每個(gè)模塊里面都去寫(xiě)授權(quán)驗(yàn)證,而且授權(quán)驗(yàn)證也不屬于具體的業(yè)務(wù),它其實(shí)屬于功能性模塊,并且會(huì)橫跨多個(gè)業(yè)務(wù)模塊。可以看到這里是橫向的,這就是所謂的切面。通俗的將,AOP就是將公用的功能給提取出來(lái),如果以后這些公用的功能發(fā)生了變化,我們只需要修改這些公用功能的代碼即可,其它的地方就不需要去更改了。所謂的切面,就是只關(guān)注通用功能,而不關(guān)注業(yè)務(wù)邏輯,而且不修改原有的類。

AOP優(yōu)勢(shì):

  • ?將通用功能從業(yè)務(wù)邏輯中抽離出來(lái),提高代碼復(fù)用性,有利于后期的維護(hù)和擴(kuò)展。
  • 軟件設(shè)計(jì)時(shí),抽出通用功能(切面),有利于軟件設(shè)計(jì)的模塊化,降低軟件架構(gòu)的復(fù)雜度。

AOP的劣勢(shì):

  • AOP的對(duì)OOP思想的一種補(bǔ)充,它無(wú)法單獨(dú)存在。如果說(shuō)單獨(dú)使用AOP去設(shè)計(jì)一套系統(tǒng)是不可能的。在設(shè)計(jì)系統(tǒng)的時(shí)候,如果系統(tǒng)比較簡(jiǎn)單,那么可以只使用POP或者OOP來(lái)設(shè)計(jì)。如果系統(tǒng)很復(fù)雜,就需要使用AOP思想。首先要使用POP來(lái)梳理整個(gè)業(yè)務(wù)流程,然后根據(jù)POP的流程,去整理類和模塊,最后在使用AOP來(lái)抽取通用功能。

AOP和OOP的區(qū)別:

  • 面向目標(biāo)不同:OOP是面向名詞領(lǐng)域(抽象出來(lái)一個(gè)事物,比如學(xué)生、員工,這些都是名詞)。AOP是面向動(dòng)詞領(lǐng)域(比如鑒權(quán)、記錄日志,這些都是動(dòng)作或行為)。
  • 思想結(jié)構(gòu)不同:OOP是縱向的(以繼承為主線,所以是縱向的)。AOP是橫向的。
  • 注重方面不同:OOP是注重業(yè)務(wù)邏輯單元的劃分,AOP偏重業(yè)務(wù)處理過(guò)程中的某個(gè)步驟或階段。

POP、OOP、AOP三種思想是相互補(bǔ)充的。在一個(gè)系統(tǒng)的開(kāi)發(fā)過(guò)程中,這三種編程思想是不可或缺的。

三、實(shí)現(xiàn)AOP

我們?cè)谏厦嬷v解了有關(guān)AOP的一些理論知識(shí),那么如何在代碼里面實(shí)現(xiàn)呢?

實(shí)現(xiàn)AOP有兩種方式:

  • 靜態(tài)代理實(shí)現(xiàn)。所謂靜態(tài)代理,就是我們自己來(lái)寫(xiě)代理對(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è)裝飾類,用來(lái)包裝原有的類,并在保持類的完整性的前提下,提供額外的功能。看下面的例子。

我們首先創(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è)方法,用來(lái)注冊(cè)一個(gè)用戶:

using StaticDemo.Model;

namespace StaticDemo.Services
{
    /// 
    /// 接口
    /// 
    public interface IAccountService
    {
        /// 
        /// 注冊(cè)用戶
        /// 
        /// 
        void Reg(User user);
    }
}

然后創(chuàng)建一個(gè)類來(lái)實(shí)現(xiàn)上面的接口:

using StaticDemo.Model;
using System;

namespace StaticDemo.Services
{
    /// 
    /// 實(shí)現(xiàn)IAccountService接口
    /// 
    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
{
    /// 
    /// 裝飾器類
    /// 
    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è)趤?lái)看看如何使用代理模式實(shí)現(xiàn)。

代理模式:即一個(gè)類代表另一個(gè)類的功能。我們會(huì)創(chuàng)建一個(gè)代理類,這個(gè)代理類和裝飾器類基本一樣。看一下代碼:

using StaticDemo.Model;
using StaticDemo.Services;
using System;

namespace StaticDemo
{
    /// 
    /// 代理類
    /// 
    public class ProxyAccount : IAccountService
    {
        private readonly IAccountService _accountService;

        /// 
        /// 構(gòu)造函數(shù)沒(méi)有參數(shù)
        /// 直接在里面創(chuàng)建了AccountService類
        /// 
        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ū)別嗎?有些東西,形式上看起來(lái)區(qū)別很小,但實(shí)際上他們區(qū)別很大。它們?cè)谛问缴洗_實(shí)一樣,不管是裝飾器類還是代理類,它們都要實(shí)現(xiàn)相同的接口,但是它們?cè)谶\(yùn)用的時(shí)候還是有區(qū)別的。

裝飾器模式關(guān)注于在一個(gè)對(duì)象上動(dòng)態(tài)添加方法,而代理模式關(guān)注于控制對(duì)象的訪問(wèn)。簡(jiǎn)單來(lái)說(shuō),使用代理模式,我們的代理類可以隱藏一個(gè)類的具體信息。var account = new ProxyAccount();僅看這段代碼不看源碼,不知道里面代理的是誰(shuí)。

當(dāng)使用代理模式的時(shí)候,我們常常是在代理類中去創(chuàng)建一個(gè)對(duì)象的實(shí)例:_accountService = new AccountService()。而當(dāng)我們使用裝飾器模式的時(shí)候,我們通常是將原始對(duì)象作為一個(gè)參數(shù)傳遞給裝飾器的構(gòu)造函數(shù)。簡(jiǎn)單來(lái)說(shuō),在使用裝飾器模式的時(shí)候,我們可以明確地知道裝飾的是誰(shuí),而且更重要的是,代理類里面是寫(xiě)死的,在編譯的時(shí)候就確定了關(guān)系。而裝飾器是在運(yùn)行時(shí)來(lái)確定的。

2、動(dòng)態(tài)代理

動(dòng)態(tài)代理實(shí)現(xiàn)也有兩種方式;

  • 通過(guò)代碼織入的方式。例如PostSharp第三方插件。我們知道.NET程序最終會(huì)編譯成IL中間語(yǔ)言,在編譯程序的時(shí)候,PostSharp會(huì)動(dòng)態(tài)的去修改IL,在IL里面添加代碼,這就是代碼織入的方式。
  • 通過(guò)反射的方式實(shí)現(xiàn)。通過(guò)反射實(shí)現(xiàn)的方法非常多,也有很多實(shí)現(xiàn)了AOP的框架,例如Unity、MVC過(guò)濾器、Autofac等。

我們先來(lái)看看如何使用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
{
    /// 
    /// 訂單業(yè)務(wù)類
    /// 
    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
{
    /// 
    /// 訂單業(yè)務(wù)類
    /// 
    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ì)修改原先已有的代碼,這就違反了開(kāi)閉原則。而且添加日志也不是業(yè)務(wù)需求的變動(dòng),不應(yīng)該去修改業(yè)務(wù)代碼。下面使用AOP來(lái)實(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 + "開(kāi)始執(zhí)行業(yè)務(wù)前");
        }

        public override void OnExit(MethodExecutionArgs eventArgs)
        {
            LgoHelper.RecoreLog(ActionName + "業(yè)務(wù)執(zhí)行完成后");
        }
    }
}

然后Log特性應(yīng)用到DoWork函數(shù)上面:

using System;

namespace PostSharpDemo
{
    /// 
    /// 訂單業(yè)務(wù)類
    /// 
    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來(lái)實(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 : 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。通過(guò)構(gòu)造函數(shù)把代理泛型類傳遞進(jìn)去。最后調(diào)用Invoke方法執(zhí)行代理類的方法。

最后我們還要?jiǎng)?chuàng)建一個(gè)代理工廠類,用來(lái)創(chuàng)建代理對(duì)象,通過(guò)調(diào)用動(dòng)態(tài)代理來(lái)創(chuàng)建動(dòng)態(tài)代理對(duì)象:

using System;

namespace DynamicProxy
{
    /// 
    /// 動(dòng)態(tài)代理工廠類
    /// 
    public static class ProxyFactory
    {
        public static T Create(Action before, Action after)
        {
            // 實(shí)例化被代理泛型對(duì)象
            T instance = Activator.CreateInstance();
            // 實(shí)例化動(dòng)態(tài)代理,創(chuàng)建動(dòng)態(tài)代理對(duì)象
            var proxy = new DynamicProxy(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(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

原文鏈接:https://www.cnblogs.com/dotnet261010/p/12285867.html

欄目分類
最近更新