实现.net下的动态代理 一文使用动态代理实现了 Ducking Typing,解决了文中的第一个问题,下面,向第二个问题进军——mixin。
一、应用场景
(1) 假定有一个接口TInterface,一个类B,类C。类B与类C分别实现了部分TInterface:
1 public interface TInterface
2 {
3 String A { get; }
4 String B(int a, int b);
5 }
6
7 public class ClassB
8 {
9 public String A
10 {
11 get
12 {
13 return String.Empty;
14 }
15 set
16 { }
17 }
18 }
19
20 public class ClassC
21 {
22 public String B(int a, int b)
23 {
24 return (a + b).ToString();
25 }
26 }
假设b和c分别是B和C的一个实例,通过 TypeTemplate.Create<TInterface>(b,c) 方法将b和c包装一下,即可生成一个TInterface实例:
1 ClassB b = new ClassB();
2 ClassC c = new ClassC();
3 TInterface i2 = TypeTemplate.Create<TInterface>(b, c);
4
(2) 通过Mixin,我们还可以在运行时动态替换接口的指定方法或属性:
1 public interface Human
2 {
3 String Heart();
4 String Lung();
5 String Name();
6 }
7
8 public class ZhouBapi : Human
9 {
10 public String Heart() { return "好心"; }
11 public String Lung() { return "好肺"; }
12 public String Name() { return "周扒皮"; }
13 }
14
15 public class Huaidan
16 {
17 public String Heart() { return "狼心"; }
18 public String Lung() { return "狗肺"; }
19 }
20
21 class Program
22 {
23 static void Show(Human body)
24 {
25 Console.WriteLine(String.Format("我叫{0},我是{1}{2}", body.Name(), body.Heart(), body.Lung()));
26 }
27
28 static void ShowHuaidan(Human body)
29 {
30 Console.WriteLine(String.Format("{0},我看你是{1}{2}", body.Name(), body.Heart(), body.Lung()));
31 }
32
33 static void Main(string[] args)
34 {
35 Human zhou = new ZhouBapi();
36 Console.Write("周扒皮: ");
37 Show(zhou);
38 zhou = TypeTemplate.Create<Human>(new Huaidan(), zhou);
39 Console.Write("人民群众:");
40 ShowHuaidan(zhou);
41 Console.Read();
42 }
43 }
输出为:
周扒皮: 我叫周扒皮,我是好心好肺
人民群众:周扒皮,我看你是狼心狗肺
二、实现
以下是详细实现代码:
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 class ObjectContainer
12 {
13 private List<Object> Container = new List<object>();
14 private List<Type> TypeContainer = new List<Type>();
15
16 public ObjectContainer(Object[] objs, Type[] tlist)
17 {
18 Container.AddRange(objs);
19 TypeContainer.AddRange(tlist);
20 }
21
22 public Object Get(Int32 index)
23 {
24 if (index >= 0 && index < Container.Count)
25 return Container[index];
26 else
27 return null;
28 }
29
30 public Type GetType(Int32 index)
31 {
32 if (index >= 0 && index < TypeContainer.Count)
33 return TypeContainer[index];
34 else
35 return null;
36 }
37
38 public static MethodInfo GetGetMethodInfo()
39 {
40 MethodInfo[] mis = typeof(ObjectContainer).GetMethods(BindingFlags.Public | BindingFlags.Instance);
41 foreach (var mi in mis)
42 {
43 if (mi.Name == "Get") return mi;
44 }
45 return null;
46 }
47 }
48
49 private class MethodInfoHolder
50 {
51 public Int32 ObjectIndex { get; private set; }
52 public MethodInfo MethodInfo { get; private set; }
53 public Type ObjectType { get; private set; }
54 public MethodInfoHolder(MethodInfo pi, Int32 index, Type type)
55 {
56 MethodInfo = pi;
57 ObjectIndex = index;
58 ObjectType = type;
59 }
60 }
61
62 public delegate void Handler<TImple>(TImple imple) where TImple: class;
63
64 public static TInterface Create<TInterface, TImple>(TImple instance)
65 where TInterface : class
66 where TImple : class
67 {
68 Type type = DynamicTypeGen<TInterface>( new Object[] { instance }, new Type[] { instance.GetType()});
69 return Activator.CreateInstance(type, new ObjectContainer(new Object[] { instance }, new Type[] { instance.GetType() })) as TInterface;
70 }
71
72 public static TInterface Create<TInterface>(params Object[] impleInstances)
73 where TInterface : class
74 {
75 List<Type> tlist = new List<Type>();
76 foreach (var item in impleInstances)
77 {
78 tlist.Add(item.GetType());
79 }
80 Type type = DynamicTypeGen<TInterface>(impleInstances, tlist.ToArray());
81 return Activator.CreateInstance(type, new ObjectContainer( impleInstances, tlist.ToArray())) as TInterface;
82 }
83
84 public static TInterface CreateIntercepted<TInterface, TImple>(TImple instance, Handler<TImple> before, Handler<TImple> after)
85 where TInterface : class
86 where TImple : class
87 {
88 throw new NotImplementedException();
89 }
90
91 public static Type DynamicTypeGen<TInterface>(Object[] instances, Type[] typeList)
92 where TInterface: class
93 {
94 Type tInterface = typeof(TInterface);
95
96 PropertyInfo[] pisInterface = tInterface.GetProperties(BindingFlags.Public | BindingFlags.Instance);
97 MethodInfo[] misInterface = tInterface.GetMethods(BindingFlags.Public | BindingFlags.Instance);
98 List<MethodInfo> misInterfaceList = new List<MethodInfo>();
99
100 List<Type> tList = new List<Type>();
101 foreach (var obj in instances)
102 {
103 tList.Add(obj.GetType());
104 }
105
106 Type[] tArray = tList.ToArray();
107
108 foreach (var item in misInterface)
109 {
110 if (item.IsSpecialName == false) misInterfaceList.Add(item);
111 }
112
113 List<MethodInfoHolder> miHolderList = new List<MethodInfoHolder>();
114 for (int i = 0; i < tArray.Length; i++)
115 {
116 MethodInfo[] misImple = tArray[i].GetMethods(BindingFlags.Public | BindingFlags.Instance);
117 foreach (var item in misImple)
118 {
119 MethodInfoHolder h = new MethodInfoHolder(item, i, typeList[i]);
120 miHolderList.Add(h);
121 }
122 }
123
124 AssemblyName aName = new AssemblyName("Orc.Generics.DynamicTypes");
125 AssemblyBuilder ab =
126 AppDomain.CurrentDomain.DefineDynamicAssembly(
127 aName,
128 AssemblyBuilderAccess.RunAndSave);
129
130 ModuleBuilder mb =
131 ab.DefineDynamicModule(aName.Name, aName.Name + ".dll");
132
133 TypeBuilder tb = mb.DefineType(GetDynamicTypeName<TInterface>(instances),
134 TypeAttributes.Public, null, new Type[] { tInterface });
135 FieldBuilder fbInstances = tb.DefineField(
136 "_container",
137 typeof(TypeTemplate.ObjectContainer),
138 FieldAttributes.Private);
139
140 ConstructorBuilder ctor1 = tb.DefineConstructor(
141 MethodAttributes.Public | MethodAttributes.HideBySig | MethodAttributes.RTSpecialName,
142 CallingConventions.Standard,
143 new Type[1] { typeof(TypeTemplate.ObjectContainer) });
144
145 ILGenerator ctor1IL = ctor1.GetILGenerator();
146 ctor1IL.Emit(OpCodes.Ldarg_0);
147 ctor1IL.Emit(OpCodes.Call, typeof(object).GetConstructor(Type.EmptyTypes));
148 ctor1IL.Emit(OpCodes.Ldarg_0);
149 ctor1IL.Emit(OpCodes.Ldarg_1);
150 ctor1IL.Emit(OpCodes.Stfld, fbInstances);
151 ctor1IL.Emit(OpCodes.Ret);
152
153 foreach (var item in pisInterface)
154 {
155 MethodInfoHolder getMi = FindGetMethodInfo(miHolderList, item);
156 MethodInfoHolder setMi = FindSetMethodInfo(miHolderList, item);
157 CreateProperty(tb, fbInstances, item, getMi, setMi);
158 }
159
160 foreach (var item in misInterfaceList)
161 {
162 MethodInfoHolder instanceMi = FindMethodInfo(miHolderList, item);
163 CreateMethod(tb, fbInstances, item, instanceMi);
164 }
165 Type type = tb.CreateType();
166 ab.Save(aName.Name + ".dll");
167 return type;
168 }
169
170 private static MethodInfoHolder FindGetMethodInfo(IEnumerable<MethodInfoHolder> miList, PropertyInfo pi)
171 {
172 foreach (var item in miList)
173 {
174 if (item.MethodInfo.Name.Equals("get_" + pi.Name) && item.MethodInfo.IsSpecialName) return item;
175 }
176
177 return null;
178 }
179
180 private static MethodInfoHolder FindSetMethodInfo(IEnumerable<MethodInfoHolder> miList, PropertyInfo pi)
181 {
182 foreach (var item in miList)
183 {
184 if (item.MethodInfo.Name.Equals("set_" + pi.Name) && item.MethodInfo.IsSpecialName) return item;
185 }
186
187 return null;
188 }
189
190 private static MethodInfoHolder FindMethodInfo(IEnumerable<MethodInfoHolder> miList, MethodInfo mi)
191 {
192 foreach (var item in miList)
193 {
194 if (MethodInfoEqual(item.MethodInfo,mi)) return item;
195 }
196
197 return null;
198 }
199
200 private static Boolean MethodInfoEqual(MethodInfo mi1, MethodInfo mi2)
201 {
202 if (mi1.IsSpecialName == true || mi2.IsSpecialName == true) return false;
203 if (mi1.Name != mi2.Name) return false;
204 if (mi1.ReturnType != mi2.ReturnType) return false;
205 ParameterInfo[] pis1 = mi1.GetParameters();
206 ParameterInfo[] pis2 = mi2.GetParameters();
207 if (pis1.Length != pis2.Length) return false;
208 for (int i = 0; i < pis1.Length; i++)
209 {
210 ParameterInfo pi1 = pis1[i];
211 ParameterInfo pi2 = pis2[i];
212 if (pi1.ParameterType != pi2.ParameterType) return false;
213 }
214 return true;
215 }
216
217 private static void CreateProperty(TypeBuilder tb, FieldBuilder fbInstance, PropertyInfo pi, MethodInfoHolder getMi, MethodInfoHolder setMi)
218 {
219 String name = pi.Name;
220 Type type = pi.PropertyType;
221
222 PropertyBuilder pb = tb.DefineProperty(
223 name,
224 PropertyAttributes.HasDefault,
225 type,
226 null);
227
228 MethodAttributes getSetAttr = MethodAttributes.Public |
229 MethodAttributes.SpecialName | MethodAttributes.HideBySig | MethodAttributes.NewSlot | MethodAttributes.Virtual | MethodAttributes.Final ;
230 MethodBuilder mbGetAccessor = tb.DefineMethod(
231 "get_" + name,
232 getSetAttr,
233 type,
234 Type.EmptyTypes);
235
236 ILGenerator getIL = mbGetAccessor.GetILGenerator();
237
238 if (getMi == null)
239 {
240 getIL.Emit(OpCodes.Newobj, typeof(NotImplementedException).GetConstructor(new Type[]{}));
241 getIL.Emit(OpCodes.Throw);
242 }
243 else
244 {
245 getIL.Emit(OpCodes.Ldarg_0);
246 getIL.Emit(OpCodes.Ldfld, fbInstance);
247 getIL.Emit(OpCodes.Ldc_I4, getMi.ObjectIndex);
248 getIL.Emit(OpCodes.Callvirt, ObjectContainer.GetGetMethodInfo());
249 getIL.Emit(OpCodes.Isinst, getMi.ObjectType);
250 getIL.Emit(OpCodes.Callvirt, getMi.MethodInfo);
251 getIL.Emit(OpCodes.Ret);
252 }
253
254 MethodBuilder mbSetAccessor = tb.DefineMethod(
255 "set_"+ name,
256 getSetAttr,
257 null,
258 new Type[] { type });
259
260 ILGenerator setIL = mbSetAccessor.GetILGenerator();
261 if (setMi == null)
262 {
263 setIL.Emit(OpCodes.Newobj, typeof(NotImplementedException).GetConstructor(new Type[] { }));
264 setIL.Emit(OpCodes.Throw);
265 }
266 else
267 {
268 setIL.Emit(OpCodes.Ldarg_0);
269 setIL.Emit(OpCodes.Ldfld, fbInstance);
270 setIL.Emit(OpCodes.Ldc_I4, setMi.ObjectIndex);
271 setIL.Emit(OpCodes.Callvirt, ObjectContainer.GetGetMethodInfo());
272 setIL.Emit(OpCodes.Isinst, setMi.ObjectType);
273 setIL.Emit(OpCodes.Ldarg_1);
274 setIL.Emit(OpCodes.Callvirt, setMi.MethodInfo);
275 setIL.Emit(OpCodes.Ret);
276 }
277
278 pb.SetGetMethod(mbGetAccessor);
279 pb.SetSetMethod(mbSetAccessor);
280 }
281
282 private static void CreateMethod(TypeBuilder tb, FieldBuilder fbInstance, MethodInfo mi, MethodInfoHolder instanceMi)
283 {
284 List<Type> paramTyleList = new List<Type>();
285 foreach(var item in mi.GetParameters())
286 paramTyleList.Add(item.ParameterType);
287
288 MethodBuilder mb = tb.DefineMethod(
289 mi.Name,
290 MethodAttributes.Public | MethodAttributes.HideBySig | MethodAttributes.NewSlot | MethodAttributes.Virtual | MethodAttributes.Final,
291 mi.ReturnType,
292 paramTyleList.ToArray());
293
294 ILGenerator il = mb.GetILGenerator();
295 if (instanceMi == null)
296 {
297 il.Emit(OpCodes.Newobj, typeof(NotImplementedException).GetConstructor(new Type[] { }));
298 il.Emit(OpCodes.Throw);
299 }
300 else
301 {
302 il.Emit(OpCodes.Ldarg_0);
303 il.Emit(OpCodes.Ldfld, fbInstance);
304 il.Emit(OpCodes.Ldc_I4, instanceMi.ObjectIndex);
305 il.Emit(OpCodes.Callvirt, ObjectContainer.GetGetMethodInfo());
306 il.Emit(OpCodes.Isinst, instanceMi.ObjectType);
307 switch (paramTyleList.Count)
308 {
309 case 0:
310 break;
311 case 1:
312 il.Emit(OpCodes.Ldarg_1);
313 break;
314 case 2:
315 il.Emit(OpCodes.Ldarg_1);
316 il.Emit(OpCodes.Ldarg_2);
317 break;
318 case 3:
319 il.Emit(OpCodes.Ldarg_1);
320 il.Emit(OpCodes.Ldarg_2);
321 il.Emit(OpCodes.Ldarg_3);
322 break;
323 default:
324 il.Emit(OpCodes.Ldarg_1);
325 il.Emit(OpCodes.Ldarg_2);
326 il.Emit(OpCodes.Ldarg_3);
327
328 Int32 sCount = Math.Min(paramTyleList.Count, 127);
329 for (int i = 4; i <= sCount; i++)
330 {
331 il.Emit(OpCodes.Ldarg_S, i);
332 }
333
334 for (int i = 128; i <= paramTyleList.Count; i++)
335 {
336 il.Emit(OpCodes.Ldarg, i);
337 }
338
339 break;
340 }
341
342 il.Emit(OpCodes.Callvirt, instanceMi.MethodInfo);
343 il.Emit(OpCodes.Ret);
344 }
345 }
346
347 private static String GetDynamicTypeName<TInterface>(params Object[] instances)
348 where TInterface : class
349 {
350 StringBuilder sb = new StringBuilder();
351 sb.Append("_DynamicTypes");
352 sb.Append(typeof(TInterface).ToString());
353 foreach (var obj in instances)
354 {
355 sb.Append("_");
356 sb.Append(obj.GetType().ToString());
357 }
358 return sb.ToString();
359 }
360 }
361
362 public interface TInterface
363 {
364 String A { get; }
365 String B(int a, int b);
366 }
367
368 public class ClassB
369 {
370 public String A
371 {
372 get
373 {
374 return String.Empty;
375 }
376 set
377 { }
378 }
379 }
380
381 public class ClassC
382 {
383 public String B(int a, int b)
384 {
385 return (a + b).ToString();
386 }
387 }
388
389 public class ClassA
390 {
391 public String A
392 {
393 get
394 {
395 return String.Empty;
396 }
397 set
398 { }
399 }
400
401 public String B(int a, int b)
402 {
403 return (a+b).ToString();
404 }
405 }
406
407 public class InterfaceImple_ClassA : TInterface
408 {
409 private TypeTemplate.ObjectContainer __container;
410
411 public InterfaceImple_ClassA(TypeTemplate.ObjectContainer container)
412 {
413 __container = container;
414 }
415
416 public String A
417 {
418 get { return (__container.Get(1) as ClassA).A; }
419 set { (__container.Get(1) as ClassA).A = value; }
420 }
421
422 public String B(int a, int b)
423 {
424 return (__container.Get(1) as ClassA).B(a, b);
425 }
426 }
427
428 public class InterfaceImple_ClassA_Factory
429 {
430 public readonly String Name = "InterfaceImple_ClassA_Factory";
431 public InterfaceImple_ClassA Create(ClassA a)
432 {
433 return null;
434 // return new InterfaceImple_ClassA(new Object[] {a});
435 }
436 }
437}
438
测试代码:
1 [TestMethod]
2 public void TestCreate()
3 {
4 ClassA a = new ClassA();
5 TInterface i1 = TypeTemplate.Create<TInterface, ClassA>(a);
6 Assert.AreNotEqual(null, i1);
7 Assert.AreEqual("3", i1.B(1, 2));
8 Assert.AreEqual(String.Empty, i1.A);
9
10 ClassB b = new ClassB();
11 ClassC c = new ClassC();
12 TInterface i2 = TypeTemplate.Create<TInterface>(b, c);
13 Assert.AreNotEqual(null, i2);
14 Assert.AreEqual("3", i2.B(1, 2));
15 Assert.AreEqual(String.Empty, i2.A);
16 }
17
三、说明:
(1)通过上述方法可以很方便的创建对现有实例的Wrapper,使其适应一个新的接口;
(2)TypeTemplate.Create<TInterface>(params Object[] impleInstances) 排位在前的 impleInstance 的接口方法/属性将掩盖它后面的 impleInstance 的接口方法/属性,这样可以在运行时动态改变接口的行为。
(3)这种MixIn方案比不上动态语言的MixIn,主要是必须要有一个interface,才能MixIn。如果能将interface也根据被代理的对象的行为动态生成,那就很爽了。怎么实现,是一个问题。