輕量級AOP架構-移植python的裝飾器(Decorator)到C#(思考篇)

來源:互聯網
上載者:User
一. 從Python說起

    Python是一門強大的語言,它包含了很多神奇的技巧,作為一門動態語言,天生的優勢使得很多特性讓靜態語言難以達到。今天我們展示的就是Python中一個很有用特性:“Decorator”,中文可以譯作“裝飾器”,那麼,Decorator是什嗎?

    在Dr.Dobb’s的文章中有這樣一段描述“Decorators are Python objects that can register,annotate,and/or wrap a Python function or object.”。

    具體來說,Decorator就是一個對函數的封裝,它可以讓你不改變函數本身的情況下對函數的執行進行幹預,比如在執行前進行許可權認證,日誌記錄,甚至修改傳入參數,或者在執行後對返回結果進行預先處理,甚至可以截斷函數的執行等等。

    看到定義是不是有一定的熟悉感,沒錯,本質上來說,它就是我們常說的面向方面編程(Aspect-Oriented Programming),簡稱AOP,相信大家對AOP也是如雷貫耳了,由於動態語言本身上的優勢,使得這類技術在動態語言平台上是如魚得水。

二. Decorator在Python下的例子

    看了很多很玄乎的概念,下面用一個簡單的Python例子來具體說明如何進行Decorator編程。

def logger(name):    def loggerWrapper(fun):        def logging(self):            print "User is %s." % name            print "Start Logging"            result = fun(self)            print "End Logging."            return result        return logging    return loggerWrapperdef debugger(name):    def debuggerWrapper(fun):        def debugging(self):            print "Debug %s" % name            print "Start Debug"            result = fun(self)            print "End Debug"            return result        return debugging    return debuggerWrapperclass MyClass():    @logger("Leven")    @debugger("test")    def Test(self):        print("function MyClass::Test called.")        return "I am Reuslt."        if __name__ == "__main__":    mc = MyClass()    print "Result:%s" % mc.Test()

 

    執行改程式,可以得到如下的結果:

    回過頭看看程式的特點,從源碼可以瞭解到,Decorator的使用很簡單,直接放置在函數定義前即可,通過@xxx的方式放置,系統就可以識別。下面我們研究下Decorator的一些特點。

  1. Decorator的本質是一個函數,它可以有傳入的參數,它需要返回一個函數對象(注意這兒兩個函數的區別),因此,在程式中,名為logger的Decorator返回了一個名為loggerWrapper的函數對象,同理,名為debugger的Decorator返回了一個名為debuggerWrapper的函數對象,這是Decorator定義的第一要點:Decorator函數一定要返回一個函數對象。
  2. Decorator返回的函數對象也是有要求的,因為這個返回的函數對象是系統在調用該函數的時候執行的,因此,系統調用該函數對象的時候會傳入當前被裝飾的函數對象(注意,這裡可能並不是原始定義的函數對象),同時需要返回一個和被修飾的函數定義一致的函數供系統調用。對比代碼中的定義,loggerWrapper函數接收一個fun的參數,很顯然,該參數就是當前被修飾的函數對象,同時,它返回logging函數對象,該函數的定義和被修飾函數完全保持一致。
  3. 系統在調用被修飾方法的時候,實際上是執行了第2點說明中返回的函數對象。也就是說,系統會對函數“偷梁換柱”,實際執行的並不是原來定義的函數體。至於原來定義的函數體是否會執行,那就要看Decorator的具體實現了。
  4. 對於多個Decorator的情況,系統會順序的進行前兩步的動作,同時,系統對Decorator的處理是倒序的。

    下面我們再次順序的對上面例子的執行方式進行說明:

    首先,系統檢查到函數Test有Decorator,因為是倒序處理,所以,首先針對debugger,系統會直接執行debuggerWrapper,並將Test作為參數fun傳入,debuggerWrapper函數執行完畢之後返回了debugging函數對象,於是,系統將Test偷換成debugging,因此,如果執行Test方法,實際執行的debugging方法,繼續,還有一個裝飾器logger,同樣的,系統會直接執行loggerWrapper函數,並將當前的函數(注意,當前的函數不再是Test了,因為經過debuger的裝飾,Test已經被debugging所替代,因此,這兒傳入的是debugging函數對象)作為參數fun傳入,loggerWrapper方法執行之後會返回logging函數對象,該函數定義和Test完全一致,因此,系統又將該位置的debugging函數(Test在先前被debugging替換了)替換成logging函數,因此,該函數在整體執行的時候是直接跑去logging函數執行的。

    在上面的例子中,系統調用Test函數實際執行logging函數,logging函數中調用fun(self)實際調用的是debugging(self),而debugging中調用的fun(self)才是真正的執行了Test方法。

三. 參考C#進行思考

    看到如此簡單實用的Decorator,是不是心動了呢?想辦法也讓C#能享受到這個好處吧。在C#中,Attribute的表現形式和Python很是相似,因此,我們考慮使用Attribute和一系列相關的操作來達到類比Python的執行過程。

    在C#中,函數對象由委託來進行描述,因此,我們考慮,同樣的,讓支援Decorator操作的Attribute實現一個特定的介面,該介面返回一個委託,然後,將實際執行該方法的時候去執行該委託,那麼考慮以下形式:

public class MyClass{    [Logger(“Leven”)]    public string Test(string s) {        xxx        return xxx;    }}public delegate string TestMethodDelegate(string s);public class LoggerAttribute : Attribute {    public string Name { get; private set; }    public LoggerAttribute(string name) {        Name = name;    }    TestMethodDelegate LoggerWrapper(TestMethodDelegate fun) {        return e => {            Console.WriteLine(“User is {0}.”, Name);            Console.WriteLine(“Start Logging”);            var result = Fun(e);            Console.WriteLine(“End Logging”);            return result;        }    }}

    這樣一來形式就和Python的Decorator大致一樣了,然而,在正常情況下,C#是不會像Python一樣自動處理LoggerAttribute和Test的關係的,因此,LoggerWrapper永遠也沒法執行,更不用說替換Test方法了,於是,下面我們需要繼續考慮具體功能的實現。

四. 考慮功能的實現

    要實現Decorator,很重要的一點就是能做到對方法“偷梁換柱”,而C#在運行期是不能修改方法的。當然,Decorator本質上是一種AOP的表現形式,因此,我們大可考慮AOP常見的實現方式。AOP一般有動態代理和靜態織入兩種實現方式,動態代理雖然有一些小小的限制,但是相比靜態織入,實現和使用上都大大簡單化,因此,我們考慮使用動態代理的方式實現該功能。

    對於MyClass類,我們希望能產生一個新的MyClassWrapper類,該類繼承自MyClass,同時,我們要求將Test方法修改成virtual方法,這樣,我們可以在MyClassWrapper類中對Test方法進行改寫,這樣以來就達到了對方法“偷梁換柱”的做法,雖然比起Python不夠利索,但是能滿足我們的要求也是大大的歡迎。那麼,我們的MyClassWrapper類要求如下定義:

public class MyClassWrapper : MyClass {    public override string Test() {        LoggerAttribute attribute = typeof(MyClass).GetMethod(“Test”).GetCustomAttribute(typeof(LoggerAttribute), true)[0] as LoggerAttribute; //擷取基類中的第一個LoggerAttribute        //調用LoggerWrapper方法,擷取新的委託.        var fun = attribute.LoggerWrapper(new TestMethodDelegate(Test));         return fun();    }}

    這樣一來,我們就改寫了Test方法的實現,達到和Python中的Decorator一致的效果。然而,C#的這種處理方式還是和Python有不同的,在Python中,是Decorator函數替換掉被裝飾函數,但是在C#中,因為方法不能被另一個委派物件替換,我們只能在方法內部手動執行裝飾過的方法,不過,效果是完全一致的。當然,上面的例子只是代表了一種實現的方法,在多個Attribute的情況下還需要很多不同的處理,不過,這樣就代表在C#上實現python的Decorator是完全行得通的。

    在上面的例子中,我們針對Test方法做到了裝飾,但是,作為一個架構,我們要求能對所有滿足要求的方法都能實現裝飾,因此,我們需要對上面的實現進行修改。很重要的一點,我們上面的裝飾器僅僅能處理string Test(string)這樣的方法,如果是string Test(object)我們則需要重新定義,使用可是大大的不便,於是,考慮用一種通用的方法簽名來代表所有的方法調用,也就是使用Func<object, object[], object>來代表任意方法。

五. 本篇小結

    在本篇中,我們將Python中Decorator的原理進行了透徹的分析,同時評估了在C#實現同樣的Decorator的可行性,通過分析可以肯定,在C#上,通過一定的技術手段,我們完全可以實現和Python類似的Decorator功能。

    在下一篇中,文章將從具體實踐出發,對Python的Decorator進行一個完整的移植,因此,下一篇將暫訂名為“編碼篇”。

相關文章

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.