设为首页 收藏本站
查看: 1977|回复: 0

[经验分享] 轻量级AOP框架-移植python的装饰器(Decorator)到C#(思考篇)

[复制链接]
累计签到:2 天
连续签到:1 天
发表于 2015-4-21 09:01:08 | 显示全部楼层 |阅读模式
一. 从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 loggerWrapper
def 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 debuggerWrapper
class 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()

  
  执行改程序,可以得到如下的结果:
DSC0000.png
  回过头看看程序的特点,从源码可以了解到,Decorator的使用很简单,直接放置在函数定义前即可,通过@xxx的方式放置,系统就可以识别。下面我们研究下Decorator的一些特点。


  • Decorator的本质是一个函数,它可以有传入的参数,它需要返回一个函数对象(注意这儿两个函数的区别),因此,在程序中,名为logger的Decorator返回了一个名为loggerWrapper的函数对象,同理,名为debugger的Decorator返回了一个名为debuggerWrapper的函数对象,这是Decorator定义的第一要点:Decorator函数一定要返回一个函数对象。
  • Decorator返回的函数对象也是有要求的,因为这个返回的函数对象是系统在调用该函数的时候执行的,因此,系统调用该函数对象的时候会传入当前被装饰的函数对象(注意,这里可能并不是原始定义的函数对象),同时需要返回一个和被修饰的函数定义一致的函数供系统调用。对比代码中的定义,loggerWrapper函数接收一个fun的参数,很显然,该参数就是当前被修饰的函数对象,同时,它返回logging函数对象,该函数的定义和被修饰函数完全保持一致。
  • 系统在调用被修饰方法的时候,实际上是执行了第2点说明中返回的函数对象。也就是说,系统会对函数“偷梁换柱”,实际执行的并不是原来定义的函数体。至于原来定义的函数体是否会执行,那就要看Decorator的具体实现了。
  • 对于多个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来代表任意方法。

五. 本篇小结
  在本篇中,我们将Python中Decorator的原理进行了透彻的分析,同时评估了在C#实现同样的Decorator的可行性,通过分析可以肯定,在C#上,通过一定的技术手段,我们完全可以实现和Python类似的Decorator功能。
  在下一篇中,文章将从具体实践出发,对Python的Decorator进行一个完整的移植,因此,下一篇将暂定名为“编码篇”。

运维网声明 1、欢迎大家加入本站运维交流群:群②:261659950 群⑤:202807635 群⑦870801961 群⑧679858003
2、本站所有主题由该帖子作者发表,该帖子作者与运维网享有帖子相关版权
3、所有作品的著作权均归原作者享有,请您和我们一样尊重他人的著作权等合法权益。如果您对作品感到满意,请购买正版
4、禁止制作、复制、发布和传播具有反动、淫秽、色情、暴力、凶杀等内容的信息,一经发现立即删除。若您因此触犯法律,一切后果自负,我们对此不承担任何责任
5、所有资源均系网友上传或者通过网络收集,我们仅提供一个展示、介绍、观摩学习的平台,我们不对其内容的准确性、可靠性、正当性、安全性、合法性等负责,亦不承担任何法律责任
6、所有作品仅供您个人学习、研究或欣赏,不得用于商业或者其他用途,否则,一切后果均由您自己承担,我们对此不承担任何法律责任
7、如涉及侵犯版权等问题,请您及时通知我们,我们将立即采取措施予以解决
8、联系人Email:admin@iyunv.com 网址:www.yunweiku.com

所有资源均系网友上传或者通过网络收集,我们仅提供一个展示、介绍、观摩学习的平台,我们不对其承担任何法律责任,如涉及侵犯版权等问题,请您及时通知我们,我们将立即处理,联系人Email:kefu@iyunv.com,QQ:1061981298 本贴地址:https://www.yunweiku.com/thread-59092-1-1.html 上篇帖子: python——连接MySQL数据库 下篇帖子: Python模块学习——optparse
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

扫码加入运维网微信交流群X

扫码加入运维网微信交流群

扫描二维码加入运维网微信交流群,最新一手资源尽在官方微信交流群!快快加入我们吧...

扫描微信二维码查看详情

客服E-mail:kefu@iyunv.com 客服QQ:1061981298


QQ群⑦:运维网交流群⑦ QQ群⑧:运维网交流群⑧ k8s群:运维网kubernetes交流群


提醒:禁止发布任何违反国家法律、法规的言论与图片等内容;本站内容均来自个人观点与网络等信息,非本站认同之观点.


本站大部分资源是网友从网上搜集分享而来,其版权均归原作者及其网站所有,我们尊重他人的合法权益,如有内容侵犯您的合法权益,请及时与我们联系进行核实删除!



合作伙伴: 青云cloud

快速回复 返回顶部 返回列表