/www/App_Code/DynamicDuck.cs
C# | 260 lines | 132 code | 31 blank | 97 comment | 9 complexity | 739aa9672633b685ac45772ea2f38ae1 MD5 | raw file
- using System;
- using System.Collections.Generic;
- using System.Diagnostics.CodeAnalysis;
- using System.Linq;
- using System.Reflection;
- using System.Reflection.Emit;
- using System.Dynamic;
-
- namespace Dynamic.Duck
- {
- /// <summary>
- /// Extension methods for duck typing and dynamic casts.
- /// </summary>
- public static class DynamicDuck
- {
- static MethodInfo GetPropertyMethod = typeof(DynamicProxyBase).GetMethod("GetProperty");
- static MethodInfo SetPropertyMethod = typeof(DynamicProxyBase).GetMethod("SetProperty");
- static MethodAttributes attributes = MethodAttributes.Public | MethodAttributes.HideBySig | MethodAttributes.NewSlot | MethodAttributes.Virtual | MethodAttributes.Final | MethodAttributes.SpecialName;
-
- #region Private fields
-
- /// <summary>
- /// Module builder singleton.
- /// </summary>
- private static ModuleBuilder s_moduleBuilder;
-
- #endregion
-
- #region Public methods
-
- /// <summary>
- /// Creates a wrapper object of type <typeparamref name="T">T</typeparamref> around the specified <paramref name="target">target</paramref> object.
- /// </summary>
- /// <typeparam name="T">Target type.</typeparam>
- /// <param name="target">Object to be wrapped.</param>
- /// <returns>Wrapper around the specified object.</returns>
- /// <exception cref="InvalidOperationException">Occurs when the specified target type is not an interface.</exception>
- /// <remarks>
- /// This method allows a form of duck typing where interfaces are used to specify a late-bound contract morphed over an existing object.
- /// </remarks>
- [SuppressMessage("Microsoft.Design", "CA1004:GenericMethodsShouldProvideTypeParameter", Justification = "By design.")]
- public static T AsIf<T>(IDynamicMetaObjectProvider target) where T : class
- {
- //
- // Get the thunk type.
- //
- var targetType = typeof(T);
- string thunkTypeName = "<>__Thunks." + targetType.FullName;
-
- //
- // Don't regenerate the thunk type if it already exists for the specified target type.
- //
- Type thunkType = ModuleBuilder.GetType(thunkTypeName, false, false);
- if (thunkType == null)
- {
- thunkType = BuildThunkType(targetType, thunkTypeName);
- }
-
- //
- // Create and return the thunk instance.
- //
- return (T)Activator.CreateInstance(thunkType, target);
- }
-
- #endregion
-
- #region Private methods
-
- /// <summary>
- /// Builds a thunk type definition with the specified <paramref name="thunkTypeName">name</paramref> for the specified <paramref name="targetType">target type</paramref>.
- /// </summary>
- /// <param name="targetType">Target type to create a thunk type definition for.</param>
- /// <param name="thunkTypeName">Name to be used for the created thunk type definition.</param>
- /// <returns>Thunk type definition for the specified <paramref name="targetType">target type</paramref>.</returns>
- private static Type BuildThunkType(Type targetType, string thunkTypeName)
- {
- TypeBuilder typeBuilder = GetTypeBuilder(thunkTypeName);
-
- //
- // Set the parent type to Dynamic.
- //
- typeBuilder.SetParent(typeof(DynamicProxyBase));
-
- //
- // Implement constructor for thunked object.
- //
- ImplementConstructor(typeBuilder);
-
- //
- // Implement all interfaces.
- //
- foreach (Type interfaceType in GetInterfaces(targetType))
- {
- ImplementInterface(interfaceType, typeBuilder);
- }
-
- return typeBuilder.CreateType();
- }
-
- /// <summary>
- /// Implements the constructor for a thunk type definition.
- /// </summary>
- /// <param name="typeBuilder">Type builder to emit to.</param>
- private static void ImplementConstructor(TypeBuilder typeBuilder)
- {
- //
- // public <class>(object @object) : base(@object)
- // {
- // }
- //
- ConstructorBuilder ctorBuilder = typeBuilder.DefineConstructor(MethodAttributes.Public | MethodAttributes.HideBySig | MethodAttributes.SpecialName | MethodAttributes.RTSpecialName, CallingConventions.Standard, new Type[] { typeof(object) });
- ILGenerator ctorILGen = ctorBuilder.GetILGenerator();
- ctorILGen.Emit(OpCodes.Ldarg_0);
- ctorILGen.Emit(OpCodes.Ldarg_1);
- ctorILGen.Emit(OpCodes.Call, typeof(DynamicProxyBase).GetConstructor(BindingFlags.Instance | BindingFlags.NonPublic, null, new Type[] { typeof(object) }, null));
- ctorILGen.Emit(OpCodes.Ret);
- }
-
- /// <summary>
- /// Implements the specified <paramref name="interfaceType">interface type</paramref>.
- /// </summary>
- /// <param name="interfaceType">Interface type to implement.</param>
- /// <param name="typeBuilder">Type builder to emit to.</param>
- /// <param name="siteCounter">Global counter for site fields used in the thunk type being generated.</param>
- private static void ImplementInterface(Type interfaceType, TypeBuilder typeBuilder)
- {
- //
- // Add implements clause.
- //
- typeBuilder.AddInterfaceImplementation(interfaceType);
-
- //
- // Implement all properties.
- //
- foreach (MemberInfo member in interfaceType.GetMembers())
- {
- if (member.MemberType == MemberTypes.Property)
- {
- var propertyInfo = member as PropertyInfo;
- Type returnType = propertyInfo.PropertyType;
- Type[] parameterTypes = propertyInfo.GetIndexParameters().Select(parameter => parameter.ParameterType).ToArray();
- PropertyBuilder propertyBuilder = typeBuilder.DefineProperty(propertyInfo.Name, PropertyAttributes.None, returnType, parameterTypes);
- if (propertyInfo.CanRead)
- AddGetPropertyMethod(propertyBuilder, typeBuilder, propertyInfo.Name);
- if (propertyInfo.CanWrite)
- AddSetPropertyMethod(propertyBuilder, typeBuilder, propertyInfo.Name);
- }
- }
- }
-
- private static void AddGetPropertyMethod(PropertyBuilder propertyBuilder, TypeBuilder typeBuilder, string methodName)
- {
- const string GETPREFIX = "get_";
- MethodBuilder methodBuilder = typeBuilder.DefineMethod(GETPREFIX + methodName, attributes, CallingConventions.HasThis, propertyBuilder.PropertyType, null);
-
- ILGenerator methodILGen = methodBuilder.GetILGenerator();
- methodILGen.DeclareLocal(propertyBuilder.PropertyType);
- methodILGen.Emit(OpCodes.Ldarg_0);
- methodILGen.Emit(OpCodes.Ldstr, methodName);
- methodILGen.Emit(OpCodes.Call, GetPropertyMethod);
- methodILGen.Emit(OpCodes.Stloc_0);
- methodILGen.Emit(OpCodes.Ldloc_0);
- methodILGen.Emit(OpCodes.Ret);
- propertyBuilder.SetGetMethod(methodBuilder);
- }
-
- private static void AddSetPropertyMethod(PropertyBuilder propertyBuilder, TypeBuilder typeBuilder, string methodName)
- {
- const string SETPREFIX = "set_";
- MethodBuilder methodBuilder = typeBuilder.DefineMethod(SETPREFIX + methodName, attributes, CallingConventions.HasThis, typeof(void), new Type[] { propertyBuilder.PropertyType });
-
- ILGenerator methodILGen = methodBuilder.GetILGenerator();
- methodILGen.Emit(OpCodes.Ldarg_0);
- methodILGen.Emit(OpCodes.Ldstr, methodName);
- methodILGen.Emit(OpCodes.Ldarg_1);
- methodILGen.Emit(OpCodes.Call, SetPropertyMethod);
- methodILGen.Emit(OpCodes.Ret);
- propertyBuilder.SetSetMethod(methodBuilder);
- }
-
- /// <summary>
- /// Gets the closure of all interfaces types implemented by the specified <paramref name="interfaceType">interface type</paramref>.
- /// </summary>
- /// <param name="interfaceType">Interface type to calculate the closure of implemented interface types for.</param>
- /// <returns>Closure of implemented interface types.</returns>
- /// <remarks>No particular order is guaranteed.</remarks>
- /// <example>
- /// interface IBar {}
- /// interface IFoo1 : IBar {}
- /// interface IFoo2 : IBar {}
- /// interface ISample : IFoo1, IFoo2 {}
- /// |-
- /// CollectionAssert.AreEquivalent(GetInterfaces(typeof(ISample)), new Type[] { typeof(ISample), typeof(IFoo1), typeof(IFoo2), typeof(IBar) })
- /// </example>
- private static Type[] GetInterfaces(Type interfaceType)
- {
- HashSet<Type> interfaces = new HashSet<Type>();
-
- //
- // Call helper function to find closure of all interfaces to implement.
- //
- GetInterfacesInternal(interfaces, interfaceType);
-
- return interfaces.ToArray();
- }
-
- /// <summary>
- /// Helper method to calculate the closure of implemented interfaces for the specified <paramref name="interfaceType">interface type</paramref> recursively.
- /// </summary>
- /// <param name="interfaces">Collected set of interface types.</param>
- /// <param name="interfaceType">Interface type to find all implemented interfaces for recursively.</param>
- private static void GetInterfacesInternal(HashSet<Type> interfaces, Type interfaceType)
- {
- //
- // Avoid duplication.
- //
- if (!interfaces.Contains(interfaceType))
- {
- interfaces.Add(interfaceType);
-
- //
- // Recursive search.
- //
- foreach (Type subInterfaceType in interfaceType.GetInterfaces())
- {
- GetInterfacesInternal(interfaces, subInterfaceType);
- }
- }
- }
-
- /// <summary>
- /// Gets a type builder for a type with the specified <paramref name="thunkTypeName">type name</paramref>.
- /// </summary>
- /// <param name="thunkTypeName">Name of the type to create a type builder for.</param>
- /// <returns>Type builder for the specified <paramref name="thunkTypeName">name</paramref>.</returns>
- private static TypeBuilder GetTypeBuilder(string thunkTypeName)
- {
- return ModuleBuilder.DefineType(thunkTypeName, TypeAttributes.Class | TypeAttributes.Public | TypeAttributes.AutoLayout | TypeAttributes.AnsiClass | TypeAttributes.Sealed | TypeAttributes.BeforeFieldInit);
- }
-
- /// <summary>
- /// Ensures the module builder singleton is available.
- /// </summary>
- private static ModuleBuilder ModuleBuilder
- {
- get
- {
- if (s_moduleBuilder == null)
- {
- AssemblyBuilder assemblyBuilder = AppDomain.CurrentDomain.DefineDynamicAssembly(new AssemblyName("DuckTaperGen"), AssemblyBuilderAccess.Run);
- s_moduleBuilder = assemblyBuilder.DefineDynamicModule("Thunks");
- }
- return s_moduleBuilder;
- }
- }
-
-
- #endregion
- }
- }