实现.net下的动态代理

一、动态代理可以解决哪些问题

 

本文描述的动态代理可以解决以下问题:

 

问题1:接口约束问题

 

场景AComboBox类与ToolStripComboBox类的行为大部分相似,它们却不共享某个粒度较大的接口,以至于对这两个类的操作代码难以公用。

 

场景B:在泛型程序中,我们必需为泛型类型声明一个接口约束,才能使用该类型所对应接口约束的方法与属性。这样以来有一个问题:存在接口A,类型BA中的所有属性和方法签名B都有,但B不是A的实现,不能由B转换为A。而由于某些原因,我们无法获得B的代码或者虽然能够获得B的代码,却由于种种考虑不能更改B的代码;

大多数情况下,可以新写一个类C,使类C继承自B,再让C实现接口A。然而,其一,代码量较大;其二,当BSealed时不能继承;其三:不方便使用继承(如:继承后,IDE中的设计器无法给新类提供支持)。

 

此时,我们希望动态代理能解决这个问题:

 

假定我们有一个接口:

 

1    public interface TInterface
2    {
3        String A get; }
4        String B(Object a, Object b, Object c, Object d, Object e);
5    }

6

 

有一个类:

 

 1    public class ClassA
 2    {
 3        public String A 
 4        {
 5            get
 6            {
 7                return String.Empty;
 8            }

 9        }

10
11        public String B(Object a, Object b, Object c, Object d, Object e)
12        {
13            return String.Empty;
14        }

15    }

16
17

 

我们需要这个动态代理类(假定为 TypeTemplate)提供一个方法,能够方便的生成一个代理类实例,将ClassA的实例包装一下,转变为 TInterface 的实例:

 

1        public static TInterface Create<TInterface, TImple>(TImple instance) 
2            where TInterface : class
3            where TImple : class
4        {
5            ……
6        }

7

 

该方法生成一个实现TInterface的动态类的实例。对于TInterface中的每一个属性或方法,该实例直接调用instance实例中具有同样签名的属性或方法,如果没有匹配的属性或方法,则抛出NotImplementedException异常。

 

问题2:快速生成现有实例类的Wrapper

 

场景C:假定接口TInterface10个方法,类TImple中有9个方法与TInterface对应,为了实现TInterface,需要新写一个Wrapper类。最方便的写法是令Wrapper继承自TImple且实现接口TInterfaceWrapper只需新添实现那个未实现的方法即可。然而,如前面所述,很多情况下使用继承不是最佳选择。不用继承的话,则需要Wrapper类实现TInterface的全部10个方法,枯燥又乏味。

 

此时,我们期待存在这样一种动态代理:它能够将几个实例BCD一起打包,使它适合接口A。这样一来,针对场景C,我们只需要写一个简单的Wrapper类,实现那个未实现的方法,然后与TImple实例一起由动态代理工厂打包生成一个新的代理类实例即可。

 

即:我们需要动态代理工厂TypeTemplate能够提供以下方法:

 

1        public static TInterface Create<TInterface>(TInterface instance, params Object[] impleInstances)
2            where TInterface : class
3        {
4            …
5        }

6

 

该方法生成一个实现TInterface的类实例。对于TInterface中的每一个属性或方法,该实例依次从impleInstances中寻找具有同样签名的属性或方法,如果没有匹配的属性或方法,则抛出NotImplementedException异常。

 

问题3AOP应用

 

场景D:当需要对方法进行拦截时我们需要动态代理。

 

AOP是动态代理最经典的应用,无需赘述。

 

这种情况下,我们需要 TypeTemplate 类提供以下的方法向代理类中加入钩子:

 

1        public delegate void Handler<TImple>(TImple imple) where TImple: class;
2
3        public static TInterface CreateIntercepted<TInterface, TImple>(TImple instance, Handler<TImple> before, Handler<TImple> after)
4            where TInterface : class
5            where TImple : class
6        {
7            …
8        }

9

 

二、实现

 

 

下面来实现动态代理类工厂TypeTemplate。由于时间有限,只实现 static TInterface Create<TInterface, TImple>(TImple instance) 方法原型。其它几个静态方法可以用类似的方式实现。

 

实现思路:

 

对于问题1中的接口TInterface和类ClassA,通过Emit生成如下类型InterfaceImple_ClassAIL代码。

 

 

 1    public class InterfaceImple_ClassA : TInterface
 2    {
 3        private ClassA __wrappedInstance;
 4
 5        public InterfaceImple_ClassA(ClassA instance)
 6        {
 7            __wrappedInstance = instance;
 8        }

 9
10        public String A
11        {
12            get return __wrappedInstance.A; }
13            set throw new NotImplementedException(); }
14        }

15
16        public String B(Object a, Object b, Object c, Object d, Object e)
17        {
18            return __wrappedInstance.B(a,b,c,d,e);
19        }

20    }

21
22

 

假定生成上面这个类型的方法为:

 

1        public static Type DynamicTypeGen<TInterface, TImple>()
2            where TInterface: class where TImple : class
3
4

 

则可用Activator轻松生成动态代理类实例:

 

1        public static TInterface Create<TInterface, TImple>(TImple instance) 
2            where TInterface : class
3            where TImple : class
4        {
5            Type type = DynamicTypeGen<TInterface, TImple>();
6            return Activator.CreateInstance(type, instance) as TInterface;
7        }

8

 

下面是具体代码:

 

 

Code

 

单元测试代码:

 

 1        [TestMethod]
 2        public void TestCreate()
 3        {
 4            ClassA a = new ClassA();
 5            TInterface i = TypeTemplate.Create<TInterface, ClassA>(a);
 6            Assert.AreNotEqual(null, i);
 7            Assert.AreEqual(String.Empty, i.A);
 8            Assert.AreEqual("3", i.B(12));
 9        }

10

 

 

三、说明

 

1         上文只是 TypeTemplate的原型实现,我只进行了简单的单元测试,没有针对全部可能遇见的情况进行测试;在功能上,未生成event的动态代理;

2         static TInterface Create<TInterface>(TInterface instance, params Object[] impleInstances) 这个方法生成的代理类最好继承一个特殊接口,通过该接口能够接入被代理的实例;

3         如果第一部分所述的三个静态方法全部实现了的话,将是一个非常强大的代理工厂;

4         代理类实例的性能接近于直接调用相关方法或属性的性能,性能极佳;

5         代理类实例的生成是通过反射生成的,在性能上有很大的提升空间。可以通过emit动态生成一个工厂类。如,针对上面的ClassA,生成如下工厂类:

 

1    public class InterfaceImple_ClassA_Factory
2    {
3        public readonly String Name = "InterfaceImple_ClassA_Factory";
4        public InterfaceImple_ClassA Create(ClassA a)
5        {
6            return new InterfaceImple_ClassA(a);
7        }

8    }

9

 

         然后通过反射生成以上工厂类的实例,缓存住。当需要生成新的动态代理实例时,从缓存中查找对应的工厂,生成具体的代理类。

若标题中有“转载”字样,则本文版权归原作者所有。若无转载字样,本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利.
posted @ 2009-02-01 08:47 xiaotie 阅读(3066) 评论(13) 编辑 收藏

 回复 引用   
#1楼 2009-02-01 09:08 www.***[未注册用户]
好文章,顶一下,伯乐族 www.*** 收藏了,更多人能看到了。
 回复 引用 查看   
#2楼[楼主] 2009-02-01 09:09 xiaotie      
很多好功能,微软不给我们,只好自己hack了。

本文初衷是通过动态代理来实现泛型中的接口约束,这样一来,可大大扩展c#下的泛型应用。

另外一个需要hack的地方是C#下的lambda表达式的类型推演问题,这个留待以后吧。

 回复 引用 查看   
#3楼 2009-02-01 09:43 Steven Chen      
看了半天才明白什么是动态代理工厂,首先标签是"工厂模式",恩......问题12应该是介个样子比较好啊 class C : A { private B b;....... } ------------------------------------------------------------- “大多数情况下,可以新写一个类C,使类C继承自B,再让C实现接口A。” 感觉适配比工厂的说法更好一些,至于问题3,如果系统中很多地方使用C,那么封装一个算了,如果仅仅是少数地方,就干脆直接用B来写算了,还是不要走入“理论面向对象的泥潭”。 至于适配的实现方式,园子里很多人都谈过,现在的C# 和.netFramework,不好实现,或许到4以后,加上很多动态特性会好实现一些。 俄 或许赵妞妞的这片咚咚lz你可以弄过来用以下哈 http://www.cnblogs.com/JeffreyZhao/archive/2009/01/31/fast-reflection-library.html
 回复 引用 查看   
#4楼[楼主] 2009-02-01 09:48 xiaotie      
@Steven Chen
有 public static TInterface Create<TInterface>(TInterface instance, params Object[] impleInstances)
适配就很简单了。

 回复 引用 查看   
#5楼[楼主] 2009-02-01 09:49 xiaotie      
@Steven Chen
不一样的,用不了。

 回复 引用 查看   
#6楼[楼主] 2009-02-01 10:02 xiaotie      
对于问题1,使用起来很简单:
ClassA a = new ClassA();
TInterface i = TypeTemplate.Create<TInterface, ClassA>(a);
对于问题2:我在开发中大量遇到——UI层调用实体Object时很多情况需要Wrapper一下,很烦人;
对于问题3:不喜欢AOP,不用之。

技术方案依赖的.net版本越低越好。.net 4.*还不知得多久,且不说到时候的软件包有多大。

 回复 引用 查看   
#7楼 2009-02-01 10:08 xiao_p      
问题1和duck typing的应用场景很像
 回复 引用 查看   
#8楼[楼主] 2009-02-01 10:16 xiaotie      
@xiao_p 老轮子和新轮子的区别 :)
以前没研究过duck typing。刚看了会duck typing,发现是和问题1很像。问题1衍生一下,不用多少代码就能够解决问题2和问题3。
更进一步,我想用泛型实现伪动态类型的定义,用emit实现,折腾了一会没搞定。
再进一步,能搞定lambda表达式的推演,方便撰写高阶类型就更好了。

 回复 引用 查看   
#9楼 2009-02-01 12:40 Leepy      
我想请问下Emit动态生成类的效率如何?主要能用在哪些方面?
 回复 引用 查看   
#10楼[楼主] 2009-02-01 15:10 xiaotie      
@Leepy
第一次生成时较慢,第二次生成(如果缓存了工厂类)以及生成之后的调用和源生类相差无几。

 回复 引用 查看   
#11楼 2009-02-02 08:49 毁于随      
留个名.
在网上找了一周关于C#版的“动态代理模式”,没有结果,看了您了文章也没有看明白,是不是.net就不能实现动态代理模式呢?希望如果楼主有能力和时间话,请一篇关于C#下的动态代理模式的文章,谢谢
 回复 引用   
#13楼 2009-04-24 20:08 yeah[未注册用户]
让我想起了 Remoting 神奇的 TransparentProxy,但貌似不同。