Задача: Утилиты
Исходник: Неявная типизация, язык: C# [code #577, hits: 7952]
автор: - [добавлен: 12.01.2009]
  1. public static class ProxyGenerator
  2. {
  3. public static T GetProxy<T>(object instance) where T : class
  4. {
  5. Type ifaceType = typeof(T);
  6. AssemblyBuilder asmBuilder = AppDomain.CurrentDomain.DefineDynamicAssembly(
  7. new AssemblyName("TMP"), AssemblyBuilderAccess.Run);
  8. ModuleBuilder modBuilder = asmBuilder.DefineDynamicModule("TMP");
  9. TypeBuilder typeBuilder = modBuilder.DefineType(ifaceType.Name + "_Impl", TypeAttributes.Public);
  10. typeBuilder.AddInterfaceImplementation(ifaceType);
  11. FieldBuilder instanceField = typeBuilder.DefineField("instance", typeof(Object), FieldAttributes.Private);
  12. FieldBuilder instanceTypeField = typeBuilder.DefineField("instanceType", typeof(Type), FieldAttributes.Private);
  13. ConstructorBuilder conBuilder = typeBuilder.DefineConstructor(
  14. MethodAttributes.Public, CallingConventions.Standard, new Type[] { typeof(Object), typeof(Type) });
  15. ILGenerator cgen = conBuilder.GetILGenerator();
  16. cgen.Emit(OpCodes.Ldarg, 0);
  17. cgen.Emit(OpCodes.Call, typeof(Object).GetConstructor(new Type[0]));
  18. cgen.Emit(OpCodes.Ldarg, 0);
  19. cgen.Emit(OpCodes.Ldarg, 1);
  20. cgen.Emit(OpCodes.Stfld, instanceField);
  21. cgen.Emit(OpCodes.Ldarg, 0);
  22. cgen.Emit(OpCodes.Ldarg, 2);
  23. cgen.Emit(OpCodes.Stfld, instanceTypeField);
  24. cgen.Emit(OpCodes.Ret);
  25.  
  26. foreach (MethodInfo mi in ifaceType.GetMethods())
  27. {
  28. if (!mi.Name.StartsWith("get_") && !mi.Name.StartsWith("set_"))
  29. GenerateMethod(typeBuilder, mi, instanceField, instanceTypeField, false);
  30. }
  31.  
  32. foreach (PropertyInfo pi in ifaceType.GetProperties())
  33. {
  34. PropertyBuilder propBuilder = typeBuilder.DefineProperty(pi.Name, pi.Attributes,
  35. pi.PropertyType, null);
  36.  
  37. if (pi.CanRead)
  38. {
  39. MethodInfo mi = ifaceType.GetMethod("get_" + pi.Name);
  40. propBuilder.SetGetMethod(GenerateMethod(typeBuilder, mi, instanceField, instanceTypeField, true));
  41. }
  42.  
  43. if (pi.CanWrite)
  44. {
  45. MethodInfo mi = ifaceType.GetMethod("set_" + pi.Name);
  46. propBuilder.SetSetMethod(GenerateMethod(typeBuilder, mi, instanceField, instanceTypeField, true));
  47. }
  48. }
  49.  
  50. Type type = typeBuilder.CreateType();
  51. return Activator.CreateInstance(type, new object[] { instance, instance.GetType() }) as T;
  52. }
  53.  
  54.  
  55. private static MethodBuilder GenerateMethod(TypeBuilder typeBuilder, MethodInfo mi,
  56. FieldBuilder instanceField, FieldBuilder instanceTypeField, bool specialName)
  57. {
  58. ParameterInfo[] pars = mi.GetParameters();
  59. Type[] parTypes = pars.Length > 0 ? new Type[pars.Length] : null;
  60.  
  61. for (int i = 0; i < pars.Length; i++)
  62. parTypes[i] = pars[i].ParameterType;
  63.  
  64. MethodAttributes ma = MethodAttributes.Public | MethodAttributes.Virtual |
  65. ( specialName ? MethodAttributes.HideBySig | MethodAttributes.SpecialName : MethodAttributes.HideBySig);
  66.  
  67. MethodBuilder methBuilder = typeBuilder.DefineMethod(mi.Name, ma,
  68. mi.ReturnType != typeof(void) ? mi.ReturnType : null, parTypes);
  69.  
  70. for (int i = 0; i < pars.Length; i++)
  71. methBuilder.DefineParameter(i + 1, pars[i].Attributes, pars[i].Name);
  72. ILGenerator gen = methBuilder.GetILGenerator();
  73. LocalBuilder loc = gen.DeclareLocal(typeof(MethodInfo));
  74. int offset = 0;
  75.  
  76. if (pars.Length > 0)
  77. {
  78. gen.DeclareLocal(typeof(object[]));
  79. gen.DeclareLocal(typeof(object[]));
  80. offset += 2;
  81. }
  82.  
  83. if (mi.ReturnType != typeof(void))
  84. {
  85. gen.DeclareLocal(methBuilder.ReturnType);
  86. offset++;
  87. }
  88.  
  89. gen.Emit(OpCodes.Ldarg, 0);
  90. gen.Emit(OpCodes.Ldfld, instanceTypeField);
  91. gen.Emit(OpCodes.Ldstr, methBuilder.Name);
  92. gen.EmitCall(OpCodes.Callvirt, typeof(Type).GetMethod("GetMethod", new Type[] { typeof(String) }), new Type[] { typeof(String) });
  93. gen.Emit(OpCodes.Stloc, 0);
  94.  
  95. if (pars.Length > 0)
  96. {
  97. gen.Emit(OpCodes.Ldc_I4, pars.Length);
  98. gen.Emit(OpCodes.Newarr, typeof(Object));
  99. gen.Emit(OpCodes.Stloc, 2);
  100. gen.Emit(OpCodes.Ldloc, 2);
  101.  
  102. for (int i = 0; i < pars.Length; i++)
  103. {
  104. ParameterInfo p = pars[i];
  105. gen.Emit(OpCodes.Ldc_I4, i);
  106. gen.Emit(OpCodes.Ldarg, i + 1);
  107.  
  108. if (p.ParameterType.IsValueType)
  109. gen.Emit(OpCodes.Box, p.ParameterType);
  110.  
  111. gen.Emit(OpCodes.Stelem_Ref);
  112. gen.Emit(OpCodes.Ldloc, 2);
  113. }
  114.  
  115. gen.Emit(OpCodes.Stloc, 1);
  116. gen.Emit(OpCodes.Ldloc, 0);
  117. gen.Emit(OpCodes.Ldarg, 0);
  118. gen.Emit(OpCodes.Ldfld, instanceField);
  119. gen.Emit(OpCodes.Ldloc, 1);
  120. }
  121. else
  122. {
  123. gen.Emit(OpCodes.Ldloc, 0);
  124. gen.Emit(OpCodes.Ldarg, 0);
  125. gen.Emit(OpCodes.Ldfld, instanceField);
  126. gen.Emit(OpCodes.Ldnull);
  127. }
  128.  
  129. Type[] methParams = new Type[] { typeof(Object), typeof(Object[]) };
  130. gen.EmitCall(OpCodes.Callvirt, typeof(MethodBase).GetMethod("Invoke", methParams), methParams);
  131.  
  132. if (mi.ReturnType != typeof(void))
  133. {
  134. if (methBuilder.ReturnType.IsValueType)
  135. gen.Emit(OpCodes.Unbox_Any, methBuilder.ReturnType);
  136.  
  137. gen.Emit(OpCodes.Stloc, offset);
  138. gen.Emit(OpCodes.Ldloc, offset);
  139. }
  140. else
  141. gen.Emit(OpCodes.Pop);
  142.  
  143. gen.Emit(OpCodes.Ret);
  144. return methBuilder;
  145. }
  146. }
  147.  
  148.  
  149.  
  150. // *********************************
  151. // Как использовать
  152.  
  153. public interface IDuck
  154. {
  155. void Swim();
  156. }
  157.  
  158. public class SwimmingAnimal
  159. {
  160. public void Swim()
  161. {
  162. Console.WriteLine("Hey, we are swimming!");
  163. }
  164. }
  165.  
  166. SwimmingAnimal animal = new SwimmingAnimal();
  167. IDuck duck = ProxyGenerator.GetProxy<IDuck>(animal);
  168. duck.Swim();
Реализация duck typing(неявная типизация): если некий класс не реализует нужный нам интерфейс, но содержит все определенные в интерфейсе мемберы, то его можно привести к этому интерфейсу.

Данная версия "понимает" интерфейсы с методами и свойствами. Методы могут иметь/не иметь параметры, возвращать значение или void.
Не поддерживает ref/out и индексеры. Последнее добавить несложно, по поводу первого — не уверен.

http://www.rsdn.ru//Forum/message/3165397.1.aspx
Тестировалось на: MS Visual Studio 2005, .NET Framework 2.0

+добавить реализацию