通过动态代理可以解决接口约束问题;可以快速生成Wrapper类;可以实现AOP功能。本文提供了这样一个动态代理工厂的原型实现(因为时间有限,只是实现了部分功能,其它功能可参考文中方法实现)。
一、动态代理可以解决哪些问题
本文描述的动态代理可以解决以下问题:
问题1:接口约束问题
场景A:ComboBox类与ToolStripComboBox类的行为大部分相似,它们却不共享某个粒度较大的接口,以至于对这两个类的操作代码难以公用。
场景B:在泛型程序中,我们必需为泛型类型声明一个接口约束,才能使用该类型所对应接口约束的方法与属性。这样以来有一个问题:存在接口A,类型B,A中的所有属性和方法签名B都有,但B不是A的实现,不能由B转换为A。而由于某些原因,我们无法获得B的代码或者虽然能够获得B的代码,却由于种种考虑不能更改B的代码;
大多数情况下,可以新写一个类C,使类C继承自B,再让C实现接口A。然而,其一,代码量较大;其二,当B为Sealed时不能继承;其三:不方便使用继承(如:继承后,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:假定接口TInterface有10个方法,类TImple中有9个方法与TInterface对应,为了实现TInterface,需要新写一个Wrapper类。最方便的写法是令Wrapper继承自TImple且实现接口TInterface,Wrapper只需新添实现那个未实现的方法即可。然而,如前面所述,很多情况下使用继承不是最佳选择。不用继承的话,则需要Wrapper类实现TInterface的全部10个方法,枯燥又乏味。
此时,我们期待存在这样一种动态代理:它能够将几个实例B,C,D一起打包,使它适合接口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异常。
问题3:AOP应用
场景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_ClassA的IL代码。
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
1using System;
2using System.Collections.Generic;
3using System.Text;
4using System.Reflection;
5using System.Reflection.Emit;
6
7namespace Orc.Generics
8{
9 public sealed class TypeTemplate
10 {
11 public delegate void Handler<TImple>(TImple imple) where TImple: class;
12
13 public static TInterface Create<TInterface, TImple>(TImple instance)
14 where TInterface : class
15 where TImple : class
16 {
17 Type type = DynamicTypeGen<TInterface, TImple>();
18 return Activator.CreateInstance(type, instance) as TInterface;
19 }
20
21 public static TInterface Create<TInterface>(TInterface instance, params Object[] impleInstances)
22 where TInterface : class
23 {
24 throw new NotImplementedException();
25 }
26
27 public static TInterface CreateIntercepted<TInterface, TImple>(TImple instance, Handler<TImple> before, Handler<TImple> after)
28 where TInterface : class
29 where TImple : class
30 {
31 throw new NotImplementedException();
32 }
33
34 public static Type DynamicTypeGen<TInterface, TImple>()
35 where TInterface: class where TImple : class
36 {
37 Type tInterface = typeof(TInterface);
38 Type tImple = typeof(TImple);
39
40 PropertyInfo[] pisInterface = tInterface.GetProperties(BindingFlags.Public | BindingFlags.Instance);
41 MethodInfo[] misInterface = tInterface.GetMethods(BindingFlags.Public | BindingFlags.Instance);
42 List<MethodInfo> misInterfaceList = new List<MethodInfo>();
43 foreach (var item in misInterface)
44 {
45 if (item.IsSpecialName == false) misInterfaceList.Add(item);
46 }
47
48 MethodInfo[] misImple = tImple.GetMethods(BindingFlags.Public | BindingFlags.Instance);
49 AssemblyName aName = new AssemblyName("Orc.Generics.DynamicTypes");
50 AssemblyBuilder ab =
51 AppDomain.CurrentDomain.DefineDynamicAssembly(
52 aName,
53 AssemblyBuilderAccess.RunAndSave);
54 ModuleBuilder mb =
55 ab.DefineDynamicModule(aName.Name, aName.Name + ".dll");
56 TypeBuilder tb = mb.DefineType(GetDynamicTypeName<TInterface, TImple>(),
57 TypeAttributes.Public, null, new Type[] { tInterface });
58 FieldBuilder fbInstance = tb.DefineField(
59 "__wrappedInstance",
60 tImple,
61 FieldAttributes.Private);
62
63 ConstructorBuilder ctor1 = tb.DefineConstructor(
64 MethodAttributes.Public,
65 CallingConventions.Standard,
66 new Type[] {tImple});
67
68 ILGenerator ctor1IL = ctor1.GetILGenerator();
69 ctor1IL.Emit(OpCodes.Ldarg_0);
70 ctor1IL.Emit(OpCodes.Call,
71 typeof(object).GetConstructor(Type.EmptyTypes));
72 ctor1IL.Emit(OpCodes.Ldarg_0);
73 ctor1IL.Emit(OpCodes.Ldarg_1);
74 ctor1IL.Emit(OpCodes.Stfld, fbInstance);
75 ctor1IL.Emit(OpCodes.Ret);
76
77 foreach (var item in pisInterface)
78 {
79 MethodInfo getMi = FindGetMethodInfo(misImple, item);
80 MethodInfo setMi = FindSetMethodInfo(misImple, item);
81 CreateProperty(tb, fbInstance, item, getMi, setMi);
82 }
83
84 foreach (var item in misInterfaceList)
85 {
86 MethodInfo instanceMi = FindMethodInfo(misImple, item);
87 CreateMethod(tb, fbInstance, item, instanceMi);
88 }
89
90 return tb.CreateType();
91 }
92
93 private static MethodInfo FindGetMethodInfo(MethodInfo[] miList, PropertyInfo pi)
94 {
95 foreach (var item in miList)
96 {
97 if (item.Name.Equals("get_" + pi.Name) && item.IsSpecialName) return item;
98 }
99
100 return null;
101 }
102
103 private static MethodInfo FindSetMethodInfo(MethodInfo[] miList, PropertyInfo pi)
104 {
105 foreach (var item in miList)
106 {
107 if (item.Name.Equals("set_" + pi.Name) && item.IsSpecialName) return item;
108 }
109
110 return null;
111 }
112
113 private static MethodInfo FindMethodInfo(MethodInfo[] miList, MethodInfo mi)
114 {
115 foreach (var item in miList)
116 {
117 if (MethodInfoEqual(item,mi)) return item;
118 }
119
120 return null;
121 }
122
123 private static Boolean MethodInfoEqual(MethodInfo mi1, MethodInfo mi2)
124 {
125 if (mi1.IsSpecialName == true || mi2.IsSpecialName == true) return false;
126 if (mi1.Name != mi2.Name) return false;
127 if (mi1.ReturnType != mi2.ReturnType) return false;
128 ParameterInfo[] pis1 = mi1.GetParameters();
129 ParameterInfo[] pis2 = mi2.GetParameters();
130 if (pis1.Length != pis2.Length) return false;
131 for (int i = 0; i < pis1.Length; i++)
132 {
133 ParameterInfo pi1 = pis1[i];
134 ParameterInfo pi2 = pis2[i];
135 if (pi1.ParameterType != pi2.ParameterType) return false;
136 }
137 return true;
138 }
139
140 private static void CreateProperty(TypeBuilder tb, FieldBuilder fbInstance, PropertyInfo pi, MethodInfo getMi, MethodInfo setMi)
141 {
142 String name = pi.Name;
143 Type type = pi.PropertyType;
144
145 PropertyBuilder pb = tb.DefineProperty(
146 name,
147 PropertyAttributes.HasDefault,
148 type,
149 null);
150
151 MethodAttributes getSetAttr = MethodAttributes.Public |
152 MethodAttributes.SpecialName | MethodAttributes.HideBySig | MethodAttributes.NewSlot | MethodAttributes.Virtual | MethodAttributes.Final ;
153 MethodBuilder mbGetAccessor = tb.DefineMethod(
154 "get_" + name,
155 getSetAttr,
156 type,
157 Type.EmptyTypes);
158
159 ILGenerator getIL = mbGetAccessor.GetILGenerator();
160 if (getMi == null)
161 {
162 getIL.Emit(OpCodes.Newobj, typeof(NotImplementedException).GetConstructor(new Type[]{}));
163 getIL.Emit(OpCodes.Throw);
164 }
165 else
166 {
167 getIL.Emit(OpCodes.Ldarg_0);
168 getIL.Emit(OpCodes.Ldfld, fbInstance);
169 getIL.Emit(OpCodes.Callvirt, getMi);
170 getIL.Emit(OpCodes.Ret);
171 }
172
173 MethodBuilder mbSetAccessor = tb.DefineMethod(
174 "set_"+ name,
175 getSetAttr,
176 null,
177 new Type[] { type });
178
179 ILGenerator setIL = mbSetAccessor.GetILGenerator();
180 if (setMi == null)
181 {
182 setIL.Emit(OpCodes.Newobj, typeof(NotImplementedException).GetConstructor(new Type[] { }));
183 setIL.Emit(OpCodes.Throw);
184 }
185 else
186 {
187 setIL.Emit(OpCodes.Ldarg_0);
188 setIL.Emit(OpCodes.Ldfld, fbInstance);
189 setIL.Emit(OpCodes.Ldarg_1);
190 setIL.Emit(OpCodes.Callvirt, setMi);
191 setIL.Emit(OpCodes.Ret);
192 }
193
194 pb.SetGetMethod(mbGetAccessor);
195 pb.SetSetMethod(mbSetAccessor);
196 }
197
198 private static void CreateMethod(TypeBuilder tb, FieldBuilder fbInstance, MethodInfo mi, MethodInfo instanceMi)
199 {
200 List<Type> paramTyleList = new List<Type>();
201 foreach(var item in mi.GetParameters())
202 paramTyleList.Add(item.ParameterType);
203
204 MethodBuilder mb = tb.DefineMethod(
205 mi.Name,
206 MethodAttributes.Public | MethodAttributes.HideBySig | MethodAttributes.NewSlot | MethodAttributes.Virtual | MethodAttributes.Final,
207 mi.ReturnType,
208 paramTyleList.ToArray());
209
210 ILGenerator il = mb.GetILGenerator();
211 if (instanceMi == null)
212 {
213 il.Emit(OpCodes.Newobj, typeof(NotImplementedException).GetConstructor(new Type[] { }));
214 il.Emit(OpCodes.Throw);
215 }
216 else
217 {
218 il.Emit(OpCodes.Ldarg_0);
219 il.Emit(OpCodes.Ldfld, fbInstance);
220 switch (paramTyleList.Count)
221 {
222 case 0:
223 break;
224 case 1:
225 il.Emit(OpCodes.Ldarg_1);
226 break;
227 case 2:
228 il.Emit(OpCodes.Ldarg_1);
229 il.Emit(OpCodes.Ldarg_2);
230 break;
231 case 3:
232 il.Emit(OpCodes.Ldarg_1);
233 il.Emit(OpCodes.Ldarg_2);
234 il.Emit(OpCodes.Ldarg_3);
235 break;
236 default:
237 il.Emit(OpCodes.Ldarg_1);
238 il.Emit(OpCodes.Ldarg_2);
239 il.Emit(OpCodes.Ldarg_3);
240
241 Int32 sCount = Math.Min(paramTyleList.Count, 127);
242 for (int i = 4; i <= sCount; i++)
243 {
244 il.Emit(OpCodes.Ldarg_S, i);
245 }
246
247 for (int i = 128; i <= paramTyleList.Count; i++)
248 {
249 il.Emit(OpCodes.Ldarg, i);
250 }
251
252 break;
253 }
254
255 il.Emit(OpCodes.Callvirt, instanceMi);
256 il.Emit(OpCodes.Ret);
257 }
258 }
259
260 private static String GetDynamicTypeName<TInterface, TImple>()
261 where TInterface : class
262 where TImple : class
263 {
264 return "_DynamicTypes" + typeof(TInterface).ToString() + "_" + typeof(TImple);
265 }
266 }
267
268 public interface TInterface
269 {
270 String A { get; }
271 String B(int a, int b);
272 }
273
274 public class ClassA
275 {
276 public String A
277 {
278 get
279 {
280 return String.Empty;
281 }
282 }
283
284 public String B(int a, int b)
285 {
286 return (a+b).ToString();
287 }
288 }
289
290 public class InterfaceImple_ClassA : TInterface
291 {
292 private ClassA __wrappedInstance;
293
294 public InterfaceImple_ClassA(ClassA instance)
295 {
296 __wrappedInstance = instance;
297 }
298
299 public String A
300 {
301 get { return __wrappedInstance.A; }
302 set { throw new NotImplementedException(); }
303 }
304
305 public String B(int a, int b)
306 {
307 return __wrappedInstance.B(a,b);
308 }
309 }
310
311 public class InterfaceImple_ClassA_Factory
312 {
313 public readonly String Name = "InterfaceImple_ClassA_Factory";
314 public InterfaceImple_ClassA Create(ClassA a)
315 {
316 return new InterfaceImple_ClassA(a);
317 }
318 }
319}
320
单元测试代码:
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(1, 2));
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
然后通过反射生成以上工厂类的实例,缓存住。当需要生成新的动态代理实例时,从缓存中查找对应的工厂,生成具体的代理类。