PageRenderTime 24ms CodeModel.GetById 17ms RepoModel.GetById 1ms app.codeStats 0ms

/www/App_Code/DynamicDuck.cs

http://adnug.codeplex.com
C# | 260 lines | 132 code | 31 blank | 97 comment | 9 complexity | 739aa9672633b685ac45772ea2f38ae1 MD5 | raw file
  1. using System;
  2. using System.Collections.Generic;
  3. using System.Diagnostics.CodeAnalysis;
  4. using System.Linq;
  5. using System.Reflection;
  6. using System.Reflection.Emit;
  7. using System.Dynamic;
  8. namespace Dynamic.Duck
  9. {
  10. /// <summary>
  11. /// Extension methods for duck typing and dynamic casts.
  12. /// </summary>
  13. public static class DynamicDuck
  14. {
  15. static MethodInfo GetPropertyMethod = typeof(DynamicProxyBase).GetMethod("GetProperty");
  16. static MethodInfo SetPropertyMethod = typeof(DynamicProxyBase).GetMethod("SetProperty");
  17. static MethodAttributes attributes = MethodAttributes.Public | MethodAttributes.HideBySig | MethodAttributes.NewSlot | MethodAttributes.Virtual | MethodAttributes.Final | MethodAttributes.SpecialName;
  18. #region Private fields
  19. /// <summary>
  20. /// Module builder singleton.
  21. /// </summary>
  22. private static ModuleBuilder s_moduleBuilder;
  23. #endregion
  24. #region Public methods
  25. /// <summary>
  26. /// Creates a wrapper object of type <typeparamref name="T">T</typeparamref> around the specified <paramref name="target">target</paramref> object.
  27. /// </summary>
  28. /// <typeparam name="T">Target type.</typeparam>
  29. /// <param name="target">Object to be wrapped.</param>
  30. /// <returns>Wrapper around the specified object.</returns>
  31. /// <exception cref="InvalidOperationException">Occurs when the specified target type is not an interface.</exception>
  32. /// <remarks>
  33. /// This method allows a form of duck typing where interfaces are used to specify a late-bound contract morphed over an existing object.
  34. /// </remarks>
  35. [SuppressMessage("Microsoft.Design", "CA1004:GenericMethodsShouldProvideTypeParameter", Justification = "By design.")]
  36. public static T AsIf<T>(IDynamicMetaObjectProvider target) where T : class
  37. {
  38. //
  39. // Get the thunk type.
  40. //
  41. var targetType = typeof(T);
  42. string thunkTypeName = "<>__Thunks." + targetType.FullName;
  43. //
  44. // Don't regenerate the thunk type if it already exists for the specified target type.
  45. //
  46. Type thunkType = ModuleBuilder.GetType(thunkTypeName, false, false);
  47. if (thunkType == null)
  48. {
  49. thunkType = BuildThunkType(targetType, thunkTypeName);
  50. }
  51. //
  52. // Create and return the thunk instance.
  53. //
  54. return (T)Activator.CreateInstance(thunkType, target);
  55. }
  56. #endregion
  57. #region Private methods
  58. /// <summary>
  59. /// Builds a thunk type definition with the specified <paramref name="thunkTypeName">name</paramref> for the specified <paramref name="targetType">target type</paramref>.
  60. /// </summary>
  61. /// <param name="targetType">Target type to create a thunk type definition for.</param>
  62. /// <param name="thunkTypeName">Name to be used for the created thunk type definition.</param>
  63. /// <returns>Thunk type definition for the specified <paramref name="targetType">target type</paramref>.</returns>
  64. private static Type BuildThunkType(Type targetType, string thunkTypeName)
  65. {
  66. TypeBuilder typeBuilder = GetTypeBuilder(thunkTypeName);
  67. //
  68. // Set the parent type to Dynamic.
  69. //
  70. typeBuilder.SetParent(typeof(DynamicProxyBase));
  71. //
  72. // Implement constructor for thunked object.
  73. //
  74. ImplementConstructor(typeBuilder);
  75. //
  76. // Implement all interfaces.
  77. //
  78. foreach (Type interfaceType in GetInterfaces(targetType))
  79. {
  80. ImplementInterface(interfaceType, typeBuilder);
  81. }
  82. return typeBuilder.CreateType();
  83. }
  84. /// <summary>
  85. /// Implements the constructor for a thunk type definition.
  86. /// </summary>
  87. /// <param name="typeBuilder">Type builder to emit to.</param>
  88. private static void ImplementConstructor(TypeBuilder typeBuilder)
  89. {
  90. //
  91. // public <class>(object @object) : base(@object)
  92. // {
  93. // }
  94. //
  95. ConstructorBuilder ctorBuilder = typeBuilder.DefineConstructor(MethodAttributes.Public | MethodAttributes.HideBySig | MethodAttributes.SpecialName | MethodAttributes.RTSpecialName, CallingConventions.Standard, new Type[] { typeof(object) });
  96. ILGenerator ctorILGen = ctorBuilder.GetILGenerator();
  97. ctorILGen.Emit(OpCodes.Ldarg_0);
  98. ctorILGen.Emit(OpCodes.Ldarg_1);
  99. ctorILGen.Emit(OpCodes.Call, typeof(DynamicProxyBase).GetConstructor(BindingFlags.Instance | BindingFlags.NonPublic, null, new Type[] { typeof(object) }, null));
  100. ctorILGen.Emit(OpCodes.Ret);
  101. }
  102. /// <summary>
  103. /// Implements the specified <paramref name="interfaceType">interface type</paramref>.
  104. /// </summary>
  105. /// <param name="interfaceType">Interface type to implement.</param>
  106. /// <param name="typeBuilder">Type builder to emit to.</param>
  107. /// <param name="siteCounter">Global counter for site fields used in the thunk type being generated.</param>
  108. private static void ImplementInterface(Type interfaceType, TypeBuilder typeBuilder)
  109. {
  110. //
  111. // Add implements clause.
  112. //
  113. typeBuilder.AddInterfaceImplementation(interfaceType);
  114. //
  115. // Implement all properties.
  116. //
  117. foreach (MemberInfo member in interfaceType.GetMembers())
  118. {
  119. if (member.MemberType == MemberTypes.Property)
  120. {
  121. var propertyInfo = member as PropertyInfo;
  122. Type returnType = propertyInfo.PropertyType;
  123. Type[] parameterTypes = propertyInfo.GetIndexParameters().Select(parameter => parameter.ParameterType).ToArray();
  124. PropertyBuilder propertyBuilder = typeBuilder.DefineProperty(propertyInfo.Name, PropertyAttributes.None, returnType, parameterTypes);
  125. if (propertyInfo.CanRead)
  126. AddGetPropertyMethod(propertyBuilder, typeBuilder, propertyInfo.Name);
  127. if (propertyInfo.CanWrite)
  128. AddSetPropertyMethod(propertyBuilder, typeBuilder, propertyInfo.Name);
  129. }
  130. }
  131. }
  132. private static void AddGetPropertyMethod(PropertyBuilder propertyBuilder, TypeBuilder typeBuilder, string methodName)
  133. {
  134. const string GETPREFIX = "get_";
  135. MethodBuilder methodBuilder = typeBuilder.DefineMethod(GETPREFIX + methodName, attributes, CallingConventions.HasThis, propertyBuilder.PropertyType, null);
  136. ILGenerator methodILGen = methodBuilder.GetILGenerator();
  137. methodILGen.DeclareLocal(propertyBuilder.PropertyType);
  138. methodILGen.Emit(OpCodes.Ldarg_0);
  139. methodILGen.Emit(OpCodes.Ldstr, methodName);
  140. methodILGen.Emit(OpCodes.Call, GetPropertyMethod);
  141. methodILGen.Emit(OpCodes.Stloc_0);
  142. methodILGen.Emit(OpCodes.Ldloc_0);
  143. methodILGen.Emit(OpCodes.Ret);
  144. propertyBuilder.SetGetMethod(methodBuilder);
  145. }
  146. private static void AddSetPropertyMethod(PropertyBuilder propertyBuilder, TypeBuilder typeBuilder, string methodName)
  147. {
  148. const string SETPREFIX = "set_";
  149. MethodBuilder methodBuilder = typeBuilder.DefineMethod(SETPREFIX + methodName, attributes, CallingConventions.HasThis, typeof(void), new Type[] { propertyBuilder.PropertyType });
  150. ILGenerator methodILGen = methodBuilder.GetILGenerator();
  151. methodILGen.Emit(OpCodes.Ldarg_0);
  152. methodILGen.Emit(OpCodes.Ldstr, methodName);
  153. methodILGen.Emit(OpCodes.Ldarg_1);
  154. methodILGen.Emit(OpCodes.Call, SetPropertyMethod);
  155. methodILGen.Emit(OpCodes.Ret);
  156. propertyBuilder.SetSetMethod(methodBuilder);
  157. }
  158. /// <summary>
  159. /// Gets the closure of all interfaces types implemented by the specified <paramref name="interfaceType">interface type</paramref>.
  160. /// </summary>
  161. /// <param name="interfaceType">Interface type to calculate the closure of implemented interface types for.</param>
  162. /// <returns>Closure of implemented interface types.</returns>
  163. /// <remarks>No particular order is guaranteed.</remarks>
  164. /// <example>
  165. /// interface IBar {}
  166. /// interface IFoo1 : IBar {}
  167. /// interface IFoo2 : IBar {}
  168. /// interface ISample : IFoo1, IFoo2 {}
  169. /// |-
  170. /// CollectionAssert.AreEquivalent(GetInterfaces(typeof(ISample)), new Type[] { typeof(ISample), typeof(IFoo1), typeof(IFoo2), typeof(IBar) })
  171. /// </example>
  172. private static Type[] GetInterfaces(Type interfaceType)
  173. {
  174. HashSet<Type> interfaces = new HashSet<Type>();
  175. //
  176. // Call helper function to find closure of all interfaces to implement.
  177. //
  178. GetInterfacesInternal(interfaces, interfaceType);
  179. return interfaces.ToArray();
  180. }
  181. /// <summary>
  182. /// Helper method to calculate the closure of implemented interfaces for the specified <paramref name="interfaceType">interface type</paramref> recursively.
  183. /// </summary>
  184. /// <param name="interfaces">Collected set of interface types.</param>
  185. /// <param name="interfaceType">Interface type to find all implemented interfaces for recursively.</param>
  186. private static void GetInterfacesInternal(HashSet<Type> interfaces, Type interfaceType)
  187. {
  188. //
  189. // Avoid duplication.
  190. //
  191. if (!interfaces.Contains(interfaceType))
  192. {
  193. interfaces.Add(interfaceType);
  194. //
  195. // Recursive search.
  196. //
  197. foreach (Type subInterfaceType in interfaceType.GetInterfaces())
  198. {
  199. GetInterfacesInternal(interfaces, subInterfaceType);
  200. }
  201. }
  202. }
  203. /// <summary>
  204. /// Gets a type builder for a type with the specified <paramref name="thunkTypeName">type name</paramref>.
  205. /// </summary>
  206. /// <param name="thunkTypeName">Name of the type to create a type builder for.</param>
  207. /// <returns>Type builder for the specified <paramref name="thunkTypeName">name</paramref>.</returns>
  208. private static TypeBuilder GetTypeBuilder(string thunkTypeName)
  209. {
  210. return ModuleBuilder.DefineType(thunkTypeName, TypeAttributes.Class | TypeAttributes.Public | TypeAttributes.AutoLayout | TypeAttributes.AnsiClass | TypeAttributes.Sealed | TypeAttributes.BeforeFieldInit);
  211. }
  212. /// <summary>
  213. /// Ensures the module builder singleton is available.
  214. /// </summary>
  215. private static ModuleBuilder ModuleBuilder
  216. {
  217. get
  218. {
  219. if (s_moduleBuilder == null)
  220. {
  221. AssemblyBuilder assemblyBuilder = AppDomain.CurrentDomain.DefineDynamicAssembly(new AssemblyName("DuckTaperGen"), AssemblyBuilderAccess.Run);
  222. s_moduleBuilder = assemblyBuilder.DefineDynamicModule("Thunks");
  223. }
  224. return s_moduleBuilder;
  225. }
  226. }
  227. #endregion
  228. }
  229. }