转摘 https://www.cnblogs.com/niceWk/archive/2010/07/21/1782092.html

偷梁换柱

上一篇我们初试了DynamicAspect这把小刀,如果你已经下了源代码,你可以看看它在后台究竟做了什么手脚。如果你接触过一些动态的AOP组件,你也许已经发现大部分的实现都是在运行时通过反射机制重新创建一个代理对象或者装饰对象和适配器对象,其目的只有一个,把对目标方法的掉用转移到一个新建的方法上,所以DynamicAspect也不例外,那既然这样,你会问为什么还要重新发明车轮,其理由是目前动态编织的Aspect都有一个软肋,要么被编织的类不能是sealed的,也就是可以从它派生一个新的子类,要么需要为这个类定义一个接口,原因无非就是在开发代码的时候,类似下面的代码可以通过编译:

        Bank bank = Factory.Create<Bank>();
        bank.Deposit(100);

或者:

IBank bank = Factory.Create<Bank>();
            bank.Deposit(100);

感谢dynamic,在.NET 4中这个障碍突然之间说消失就消失了。第二个问题是取消这个限制真的很重要吗?很难回答,但个人认为如果任何实体都可以被编织(静态类除外)总是要比受这个限制那个限制要爽多多。其实,自从.NET4发布以来,我一直在看是否有人利用它的动态特性实现一个AOP,我没有看到,想来肯定是有人想到了,但还没有去做或者正在做也未必。

基于笔者多年对AOP的关注,所以决定尝试着开发一个,于是就有了DynamicAspect现在的原型。做一个动态的Aspect,基本上要解决下面的几个问题:

1、如何使调用对象方法的代码通过编译?

2、如何拦截对目标方法的调用?

3、哪些对象需要被编织?

4、对象的哪些方法被执行的时候需要被拦截?

5、如何定义方法中拦截点(Joinpoint)?

6、Aspect必须具备什么样的行为特征,以及它们如何匹配要编制的目标对象?

7、Aspect对象生命期的管理。

在探讨上面的几个问题之前,我想大致介绍一下dynamic这个大功臣,之所以这么说是因为如果没有dynamic也就没有DynamicAspect,甚至你把DynamicAspect看成是Dynamic技术和MEF技术二者结合的结晶一点也不为过,之所以大致介绍,因为完全的解释dynamic需要很大的篇幅,而且也超出了这个系列的主题范围,大家如果有兴趣可以自己阅读msdn文档相关部分和其他网友写的文章(笔者推荐Using the Dynamic Keyword in C# 4.0)。

近年来,动态语言火热流行,使得开发人员对生在静态语言家族的C#也有了支持动态的需求,C#3.0 引入Lambda 表达式,开始向支持动态迈进了一步,C#4.0引入dynamic又向前迈出了一大步,在这之前,.NET支持动态的表现是通过反射(相信有人使用反射来实现类似COM的迟后绑定),那么到底dynamic是什么?使用dynamic有什么好处?还是让代码来告诉我们吧,请看下面的代码:

            dynamic result = DoSomething(1, 2);

            result = DoSomething("Hello,", "Dynamic!");

DoSomething的方法如下:

static dynamic DoSomething(dynamic d1, dynamic d2)
        {
            return d1 + d2;
        }

结果第一个result返回:3

第二个result返回:Hello, Dynamic!

从上面的表达式看dynamic是一个CLR类型,所以几乎所有可以使用类型的地方都可以使用dynamic,但你不能通过new dynamic(); 来创建一个dynamic对象实例。也许你觉得dynamicvar关键字,可是var关键字只能出现在局部变量的声明中,其实dynamic更像object,和object不同的是dynamic可以支持任何操作,也就是在编写代码的时候你可以一个dynamic类型对象可以调用任何方法而不会产生编译错误,但这并不意味着dynamic可以逃脱类型检测,在运行时后,它还是要被检测,也就是说它的类型以及所执行的操作在运行的时候必须存在,正所谓逃了和尚逃不了庙!有的朋友可能会说DoSomething方法可以使用泛型,我很认真的告诉你在上面的例子中,泛型无能为力,不信你试试?所以基本上你可以把dynamic看成一个占位对象,该占位对象可以绕过编译器的检查。

至于使用dynamic的好处,就要看在什么场合下使用了,好吃的东西也不宜多吃,否则会适得其反的。下面我们通过dynamic的一个兄弟ExpandoObject类来看dynamic在动态编程中的用法:

dynamic user = new ExpandoObject();

user.Name = "user";
user.Password = "p@ssw0rd"; user.Validate = (Func<string,string,bool>)
          (( userid,  password) => userid == "user" && password == "p@ssw0rd" );
user.Validate(user.Name, user.Password);
 
user是ExpandoObject类型的动态对象,类似一个匿名类型,我们在代码中动态定义了两个属性NamePassword,并对它们赋值,然后我们为user对象动态定义了一个Validate方法,最后,我们调用这个定义的方法,读出前面定义的NamePassword的属性值作为方法的参数。

如果你需要的功能ExpandoObject不能满足的话,这时有两个定制方法可以使用,方法1:实现

IDynamicMetaObjectProvider接口;方法2:从DynamicObject类派生
我们先来看看从DynamicObject类中重载来的几个方法的定义(其他方法参见MSDN类库文档)
    public class DynamicObject : IDynamicMetaObjectProvider
    {        
    //提供对获取成员值操作的实现,重载该方法可以实现获取对象属性值操作时行为。
    public virtual bool TryGetMember(GetMemberBinder binder, out object result);
 
    //提供调用成员操作的实现。
  public virtual bool TryInvokeMember(InvokeMemberBinder binder, object[] args, out object result);
    //提供对设置成员值得实现。 
    public virtual bool TrySetMember(SetMemberBinder binder, object value);
     }
 
有了上面dynamic的知识,我们的第一个问题得到了解决。在DynamicAspect中,我们设计了一个名为WeavableObject的类,它从DynamicObject派生,通过对TryInvokeMember方法的重载,我们得到拦截方法调用的机会;而通过对TryGetMemberTrySetMember方法的重载,于是就可以拦截对属性的getset的调用。WeavableObject的部分代码如下所示:
    public sealed class WeavableObject<T> : DynamicObject, 
                         IPartImportsSatisfiedNotification 
                         where T: class
    {
        T source;
        public WeavableObject()
        {
            source = Activator.CreateInstance<T>();
        }         public WeavableObject(T source)
        {
            this.source = source;
        }
     
        public override bool TryInvokeMember(InvokeMemberBinder binder, object[] args, out object result)
        {

// 代码暂略

        }

        public override bool TryGetMember(GetMemberBinder binder, out object result)
        {
          // 代码暂略
        }         public override bool TrySetMember(SetMemberBinder binder, object value)
        {
          // 代码暂略
        }
 
我们的第二个问题也就解决了。
当我们决定一个对象要被编织的时候,我们就可以在实例化该对象的是时候把它替换成WeavableObject,比如上一篇的Bank对象,我们就可以把代码写成:
 
dynamic bank = new WeavableObject<Bank>();
 WeavableObject的无参数构造器将根据对象的类型通过反射来创建一个对象实例。为了减少反射带来的性能开销,我们也设计了一个带参构造器,所以上面的代码就可以写成:
 
dynamic bank = new WeavalbeObject<Bank>(new Bank());
 
为了使代码看上去更自然,我们编写了一个扩展方法AsDynamic<T>(),于是最终的代码就变成了:
 
dynamic bank = new Bank().AsDynamic();
 
自此,梁柱就这样被换掉了。下一篇将继续探讨其他未解决的问题。
(未完待续)

使用dynamic和MEF实现轻量级的AOP组件 (2)的更多相关文章

  1. 使用dynamic和MEF实现轻量级的AOP组件 ---- 系列文章

      .NET 4 实践 - 使用dynamic 和MEF实现轻量级的AOP组件(1)   .NET 4 实践 - 使用dynamic和MEF实现轻量级的AOP组件 (2) .NET 4 实践 - 使用 ...

  2. 使用dynamic和MEF实现轻量级的AOP组件 (3)

    转摘 https://www.cnblogs.com/niceWk/archive/2010/07/22/1783068.html 水到渠成 在上一篇的<偷梁换柱>中,介绍了Weavabl ...

  3. 使用dynamic 和MEF实现轻量级的 AOP 组件 (1)

    转载https://www.cnblogs.com/niceWk/archive/2010/07/19/1780843.html AOP魔法 今天你AOP了吗?谈到AOP,总有一种神秘的感觉,人类对于 ...

  4. .NET 4 实践 - 使用dynamic和MEF实现轻量级的AOP组件 (4)

    转摘 https://www.cnblogs.com/niceWk/archive/2010/07/23/1783394.html 借花献佛 前面我们介绍了构成DynamicAspect绝大部分的类, ...

  5. C++11实现一个轻量级的AOP框架

    AOP介绍 AOP(Aspect-Oriented Programming,面向方面编程),可以解决面向对象编程中的一些问题,是OOP的一种有益补充.面向对象编程中的继承是一种从上而下的关系,不适合定 ...

  6. C#轻量级高性能日志组件EasyLogger

    一.课程介绍 本次分享课程属于<C#高级编程实战技能开发宝典课程系列>中的第六部分,阿笨后续会计划将实际项目中的一些比较实用的关于C#高级编程的技巧分享出来给大家进行学习,不断的收集.整理 ...

  7. 基于DispatchProxy打造自定义AOP组件

    DispatchProxy是微软爸爸编写的一个代理类,基于这个,我扩展了一个AOP组件 暂时不支持依赖注入构造方法,感觉属性注入略显麻烦,暂时没打算支持 基于特性的注入流程 [AttributeUsa ...

  8. 使用MEF与Castle实现AOP

    MEF是微软的一个ioc框架,使用非常方便,我们只需要在需要导出的类上标记[Export],在需要使用的地方[import]就可以使用了.现在我们扩展MEF,在其装配生成实例时,使用Castle Dy ...

  9. 基于微软企业库的AOP组件(含源码)

    软件开发,离不开对日志的操作.日志可以帮助我们查找和检测问题,比较传统的日志是在方法执行前或后,手动调用日志代码保存.但自从AOP出现后,我们就可以避免这种繁琐但又必须要实现的方式.本文是在微软企业库 ...

随机推荐

  1. coding++:Spring IOC/DI 实现原理

    什么是 SpringIOC: spring ioc 指的是控制反转,IOC容器负责实例化.定位.配置应用程序中的对象及建立这些对象间的依赖.交由Spring容器统一进行管理,从而实现松耦合. “控制反 ...

  2. Python python 函数参数:关键字参数

    # 关键字参数 '''关键字参数代表传入任意个含参数名的参数,这些关键字参数在函数内部自动组装为一个dict ''' def student(name,sex,**keywords): print(' ...

  3. Jmeter 注册多个用户 之 CSV Data set Config

    1. 打开Jmeter,新建一个测试计划 > 新建线程组> 创建一个Http 请求 2. 创建一个信息头管理器 > content-Type   application/json; ...

  4. 总结:自动将函数对象添加到字典的bug

    介绍 本文以ATM项目为背景,介绍一个比较实用的编程技巧,使用装饰器将项目中的指定函数添加到字典中. 利用字典通过key访问value的特点,实现用户输入编号,通过字典直接获取并调用编号对应的功能函数 ...

  5. B【SDOI2008】Sandy的卡片

    时间限制 : 5000 MS   空间限制 : 128000 KB 问题描述 Sandy和Sue的热衷于收集干脆面中的卡片.然而,Sue收集卡片是因为卡片上漂亮的人物形象,而Sandy则是为了积攒卡片 ...

  6. Scratch2的鸡兔同笼

    解题思路鸡兔同笼新算法:已知共有鸡和兔15只,共有40只脚,问鸡和兔各有几只.算法:假设鸡和兔训练有素,吹一声哨,它们抬起一只脚,(40-15=25) .再吹一声哨,它们又抬起一只脚,(25-15=1 ...

  7. python学习 0 python简介

    一.Python简介 python是一门简单易学又功能强大的编程语言.它具有高效的高级数据结构和简单而有效的面向对象编程的特性.python优雅的语法和动态类型.以及其解释性的性质,使它在许多领域和大 ...

  8. linux之进程管理(二)

    一.查看进程 ps   aux 查看系统所有的进程数据 ps   -lA 查看所有系统的数据 ps   axjf 连同部分进程树状态 ps参数 -A   显示所有进程,等效 -e -a   不与ter ...

  9. C语言移动一个点

    #include"stdio.h"#include"windows.h"#include"conio.h"#define M 3#defin ...

  10. webstorm 永久激活方法

    打开终端,执行: cd /etc/ sudo vim hosts 在最后一行加上: 0.0.0.0 account.jetbrains.com 打开webstorm,选择Activation Code ...