Creating a dynamic proxy generator with c# – Part 3 – Creating the constructors

Posted by SeanMcAlinden on ASP.net Weblogs See other posts from ASP.net Weblogs or by SeanMcAlinden
Published on Wed, 24 Mar 2010 23:22:00 GMT Indexed on 2010/03/24 23:43 UTC
Read the original article Hit count: 753

For the latest code go to http://rapidioc.codeplex.com/

When building our proxy type, the first thing we need to do is build the constructors.

There needs to be a corresponding constructor for each constructor on the passed in base type.

We also want to create a field to store the interceptors and construct this list within each constructor.

So assuming the passed in base type is a User<int, IRepository> class, were looking to generate constructor code like the following:

 

Default Constructor
  1. public User`2_RapidDynamicBaseProxy()
  2. {
  3.     this.interceptors = new List<IInterceptor<User<int, IRepository>>>();
  4.     DefaultInterceptor<User<int, IRepository>> item = new DefaultInterceptor<User<int, IRepository>>();
  5.     this.interceptors.Add(item);
  6. }

 

 

Parameterised Constructor
  1. public User`2_RapidDynamicBaseProxy(IRepository repository1) : base(repository1)
  2. {
  3.     this.interceptors = new List<IInterceptor<User<int, IRepository>>>();
  4.     DefaultInterceptor<User<int, IRepository>> item = new DefaultInterceptor<User<int, IRepository>>();
  5.     this.interceptors.Add(item);
  6. }

 

As you can see, we first populate a field on the class with a new list of the passed in base type.

Construct our DefaultInterceptor class.

Add the DefaultInterceptor instance to our interceptor collection.

Although this seems like a relatively small task, there is a fair amount of work require to get this going. Instead of going through every line of code – please download the latest from http://rapidioc.codeplex.com/ and debug through.

In this post I’m going to concentrate on explaining how it works.

TypeBuilder

The TypeBuilder class is the main class used to create the type.

You instantiate a new TypeBuilder using the assembly module we created in part 1.

  1. /// <summary>
  2. /// Creates a type builder.
  3. /// </summary>
  4. /// <typeparam name="TBase">The type of the base class to be proxied.</typeparam>
  5. public static TypeBuilder CreateTypeBuilder<TBase>() where TBase : class
  6. {
  7.     TypeBuilder typeBuilder = DynamicModuleCache.Get.DefineType
  8.         (
  9.             CreateTypeName<TBase>(),
  10.             TypeAttributes.Class | TypeAttributes.Public,
  11.             typeof(TBase),
  12.             new Type[] { typeof(IProxy) }
  13.         );
  14.  
  15.     if (typeof(TBase).IsGenericType)
  16.     {
  17.         GenericsHelper.MakeGenericType(typeof(TBase), typeBuilder);
  18.     }
  19.  
  20.     return typeBuilder;
  21. }
  22.  
  23. private static string CreateTypeName<TBase>() where TBase : class
  24. {
  25.     return string.Format("{0}_RapidDynamicBaseProxy", typeof(TBase).Name);
  26. }

As you can see, I’ve create a new public class derived from TBase which also implements my IProxy interface, this is used later for adding interceptors.

If the base type is generic, the following GenericsHelper.MakeGenericType method is called.

GenericsHelper
  1. using System;
  2. using System.Reflection.Emit;
  3. namespace Rapid.DynamicProxy.Types.Helpers
  4. {
  5.     /// <summary>
  6.     /// Helper class for generic types and methods.
  7.     /// </summary>
  8.     internal static class GenericsHelper
  9.     {
  10.         /// <summary>
  11.         /// Makes the typeBuilder a generic.
  12.         /// </summary>
  13.         /// <param name="concrete">The concrete.</param>
  14.         /// <param name="typeBuilder">The type builder.</param>
  15.         public static void MakeGenericType(Type baseType, TypeBuilder typeBuilder)
  16.         {
  17.             Type[] genericArguments = baseType.GetGenericArguments();
  18.  
  19.             string[] genericArgumentNames = GetArgumentNames(genericArguments);
  20.  
  21.             GenericTypeParameterBuilder[] genericTypeParameterBuilder
  22.                 = typeBuilder.DefineGenericParameters(genericArgumentNames);
  23.  
  24.             typeBuilder.MakeGenericType(genericTypeParameterBuilder);
  25.         }
  26.  
  27.         /// <summary>
  28.         /// Gets the argument names from an array of generic argument types.
  29.         /// </summary>
  30.         /// <param name="genericArguments">The generic arguments.</param>
  31.         public static string[] GetArgumentNames(Type[] genericArguments)
  32.         {
  33.             string[] genericArgumentNames = new string[genericArguments.Length];
  34.  
  35.             for (int i = 0; i < genericArguments.Length; i++)
  36.             {
  37.                 genericArgumentNames[i] = genericArguments[i].Name;
  38.             }
  39.  
  40.             return genericArgumentNames;
  41.         }
  42.     }
  43. }

 

 

 

As you can see, I’m getting all of the generic argument types and names, creating a GenericTypeParameterBuilder and then using the typeBuilder to make the new type generic.

InterceptorsField

The interceptors field will store a List<IInterceptor<TBase>>.

Fields are simple made using the FieldBuilder class.

The following code demonstrates how to create the interceptor field.

  1. FieldBuilder interceptorsField = typeBuilder.DefineField(
  2.     "interceptors",
  3.     typeof(System.Collections.Generic.List<>).MakeGenericType(typeof(IInterceptor<TBase>)),
  4.       FieldAttributes.Private
  5.     );

The field will now exist with the new Type although it currently has no data – we’ll deal with this in the constructor.

Add method for interceptorsField

To enable us to add to the interceptorsField list, we are going to utilise the Add method that already exists within the System.Collections.Generic.List class.

We still however have to create the methodInfo necessary to call the add method.

This can be done similar to the following:

Add Interceptor Field
  1. MethodInfo addInterceptor = typeof(List<>)
  2.     .MakeGenericType(new Type[] { typeof(IInterceptor<>).MakeGenericType(typeof(TBase)) })
  3.     .GetMethod
  4.     (
  5.        "Add",
  6.        BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic,
  7.        null,
  8.        new Type[] { typeof(IInterceptor<>).MakeGenericType(typeof(TBase)) },
  9.        null
  10.     );

So we’ve create a List<IInterceptor<TBase>> type, then using the type created a method info called Add which accepts an IInterceptor<TBase>.

Now in our constructor we can use this to call this.interceptors.Add(// interceptor);

Building the Constructors

This will be the first hard-core part of the proxy building process so I’m going to show the class and then try to explain what everything is doing.

For a clear view, download the source from http://rapidioc.codeplex.com/, go to the test project and debug through the constructor building section.

Anyway, here it is:

DynamicConstructorBuilder
  1. using System;
  2. using System.Collections.Generic;
  3. using System.Reflection;
  4. using System.Reflection.Emit;
  5. using Rapid.DynamicProxy.Interception;
  6. using Rapid.DynamicProxy.Types.Helpers;
  7. namespace Rapid.DynamicProxy.Types.Constructors
  8. {
  9.     /// <summary>
  10.     /// Class for creating the proxy constructors.
  11.     /// </summary>
  12.     internal static class DynamicConstructorBuilder
  13.     {
  14.         /// <summary>
  15.         /// Builds the constructors.
  16.         /// </summary>
  17.         /// <typeparam name="TBase">The base type.</typeparam>
  18.         /// <param name="typeBuilder">The type builder.</param>
  19.         /// <param name="interceptorsField">The interceptors field.</param>
  20.         public static void BuildConstructors<TBase>
  21.             (
  22.                 TypeBuilder typeBuilder,
  23.                 FieldBuilder interceptorsField,
  24.                 MethodInfo addInterceptor
  25.             )
  26.             where TBase : class
  27.         {
  28.             ConstructorInfo interceptorsFieldConstructor = CreateInterceptorsFieldConstructor<TBase>();
  29.  
  30.             ConstructorInfo defaultInterceptorConstructor = CreateDefaultInterceptorConstructor<TBase>();
  31.  
  32.             ConstructorInfo[] constructors = typeof(TBase).GetConstructors();
  33.  
  34.             foreach (ConstructorInfo constructorInfo in constructors)
  35.             {
  36.                 CreateConstructor<TBase>
  37.                     (
  38.                         typeBuilder,
  39.                         interceptorsField,
  40.                         interceptorsFieldConstructor,
  41.                         defaultInterceptorConstructor,
  42.                         addInterceptor,
  43.                         constructorInfo
  44.                     );
  45.             }
  46.         }
  47.  
  48.         #region Private Methods
  49.  
  50.         private static void CreateConstructor<TBase>
  51.             (
  52.                 TypeBuilder typeBuilder,
  53.                 FieldBuilder interceptorsField,
  54.                 ConstructorInfo interceptorsFieldConstructor,
  55.                 ConstructorInfo defaultInterceptorConstructor,
  56.                 MethodInfo AddDefaultInterceptor,
  57.                 ConstructorInfo constructorInfo
  58.             ) where TBase : class
  59.         {
  60.             Type[] parameterTypes = GetParameterTypes(constructorInfo);
  61.  
  62.             ConstructorBuilder constructorBuilder = CreateConstructorBuilder(typeBuilder, parameterTypes);
  63.  
  64.             ILGenerator cIL = constructorBuilder.GetILGenerator();
  65.  
  66.             LocalBuilder defaultInterceptorMethodVariable =
  67.                 cIL.DeclareLocal(typeof(DefaultInterceptor<>).MakeGenericType(typeof(TBase)));
  68.  
  69.             ConstructInterceptorsField(interceptorsField, interceptorsFieldConstructor, cIL);
  70.  
  71.             ConstructDefaultInterceptor(defaultInterceptorConstructor, cIL, defaultInterceptorMethodVariable);
  72.  
  73.             AddDefaultInterceptorToInterceptorsList
  74.                 (
  75.                     interceptorsField,
  76.                     AddDefaultInterceptor,
  77.                     cIL,
  78.                     defaultInterceptorMethodVariable
  79.                 );
  80.  
  81.             CreateConstructor(constructorInfo, parameterTypes, cIL);
  82.         }
  83.  
  84.         private static void CreateConstructor(ConstructorInfo constructorInfo, Type[] parameterTypes, ILGenerator cIL)
  85.         {
  86.             cIL.Emit(OpCodes.Ldarg_0);
  87.  
  88.             if (parameterTypes.Length > 0)
  89.             {
  90.                 LoadParameterTypes(parameterTypes, cIL);
  91.             }
  92.  
  93.             cIL.Emit(OpCodes.Call, constructorInfo);
  94.             cIL.Emit(OpCodes.Ret);
  95.         }
  96.  
  97.         private static void LoadParameterTypes(Type[] parameterTypes, ILGenerator cIL)
  98.         {
  99.             for (int i = 1; i <= parameterTypes.Length; i++)
  100.             {
  101.                 cIL.Emit(OpCodes.Ldarg_S, i);
  102.             }
  103.         }
  104.  
  105.         private static void AddDefaultInterceptorToInterceptorsList
  106.             (
  107.                 FieldBuilder interceptorsField,
  108.                 MethodInfo AddDefaultInterceptor,
  109.                 ILGenerator cIL,
  110.                 LocalBuilder defaultInterceptorMethodVariable
  111.             )
  112.         {
  113.             cIL.Emit(OpCodes.Ldarg_0);
  114.             cIL.Emit(OpCodes.Ldfld, interceptorsField);
  115.             cIL.Emit(OpCodes.Ldloc, defaultInterceptorMethodVariable);
  116.             cIL.Emit(OpCodes.Callvirt, AddDefaultInterceptor);
  117.         }
  118.  
  119.         private static void ConstructDefaultInterceptor
  120.             (
  121.                 ConstructorInfo defaultInterceptorConstructor,
  122.                 ILGenerator cIL,
  123.                 LocalBuilder defaultInterceptorMethodVariable
  124.             )
  125.         {
  126.             cIL.Emit(OpCodes.Newobj, defaultInterceptorConstructor);
  127.             cIL.Emit(OpCodes.Stloc, defaultInterceptorMethodVariable);
  128.         }
  129.  
  130.         private static void ConstructInterceptorsField
  131.             (
  132.                 FieldBuilder interceptorsField,
  133.                 ConstructorInfo interceptorsFieldConstructor,
  134.                 ILGenerator cIL
  135.             )
  136.         {
  137.             cIL.Emit(OpCodes.Ldarg_0);
  138.             cIL.Emit(OpCodes.Newobj, interceptorsFieldConstructor);
  139.             cIL.Emit(OpCodes.Stfld, interceptorsField);
  140.         }
  141.  
  142.         private static ConstructorBuilder CreateConstructorBuilder(TypeBuilder typeBuilder, Type[] parameterTypes)
  143.         {
  144.             return typeBuilder.DefineConstructor
  145.                 (
  146.                     MethodAttributes.Public | MethodAttributes.SpecialName | MethodAttributes.RTSpecialName
  147.                     | MethodAttributes.HideBySig, CallingConventions.Standard, parameterTypes
  148.                 );
  149.         }
  150.  
  151.         private static Type[] GetParameterTypes(ConstructorInfo constructorInfo)
  152.         {
  153.             ParameterInfo[] parameterInfoArray = constructorInfo.GetParameters();
  154.  
  155.             Type[] parameterTypes = new Type[parameterInfoArray.Length];
  156.  
  157.             for (int p = 0; p < parameterInfoArray.Length; p++)
  158.             {
  159.                 parameterTypes[p] = parameterInfoArray[p].ParameterType;
  160.             }
  161.  
  162.             return parameterTypes;
  163.         }
  164.  
  165.         private static ConstructorInfo CreateInterceptorsFieldConstructor<TBase>() where TBase : class
  166.         {
  167.             return ConstructorHelper.CreateGenericConstructorInfo
  168.                 (
  169.                     typeof(List<>),
  170.                     new Type[] { typeof(IInterceptor<TBase>) },
  171.                     BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic
  172.                 );
  173.         }
  174.  
  175.         private static ConstructorInfo CreateDefaultInterceptorConstructor<TBase>() where TBase : class
  176.         {
  177.             return ConstructorHelper.CreateGenericConstructorInfo
  178.                 (
  179.                     typeof(DefaultInterceptor<>),
  180.                     new Type[] { typeof(TBase) },
  181.                     BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic
  182.                 );
  183.         }
  184.  
  185.         #endregion
  186.     }
  187. }

So, the first two tasks within the class should be fairly clear, we are creating a ConstructorInfo for the interceptorField list and a ConstructorInfo for the DefaultConstructor, this is for instantiating them in each contructor.

We then using Reflection get an array of all of the constructors in the base class, we then loop through the array and create a corresponding proxy contructor.

Hopefully, the code is fairly easy to follow other than some new types and the dreaded Opcodes.

ConstructorBuilder

This class defines a new constructor on the type.

ILGenerator

The ILGenerator allows the use of Reflection.Emit to create the method body.

LocalBuilder

The local builder allows the storage of data in local variables within a method, in this case it’s the constructed DefaultInterceptor.

Constructing the interceptors field

The first bit of IL you’ll come across as you follow through the code is the following private method used for constructing the field list of interceptors.

  1. private static void ConstructInterceptorsField
  2.             (
  3.                 FieldBuilder interceptorsField,
  4.                 ConstructorInfo interceptorsFieldConstructor,
  5.                 ILGenerator cIL
  6.             )
  7.         {
  8.             cIL.Emit(OpCodes.Ldarg_0);
  9.             cIL.Emit(OpCodes.Newobj, interceptorsFieldConstructor);
  10.             cIL.Emit(OpCodes.Stfld, interceptorsField);
  11.         }

The first thing to know about generating code using IL is that you are using a stack, if you want to use something, you need to push it up the stack etc. etc.

OpCodes.ldArg_0

This opcode is a really interesting one, basically each method has a hidden first argument of the containing class instance (apart from static classes), constructors are no different.

This is the reason you can use syntax like this.myField.

So back to the method, as we want to instantiate the List in the interceptorsField, first we need to load the class instance onto the stack, we then load the new object (new List<TBase>) and finally we store it in the interceptorsField.

Hopefully, that should follow easily enough in the method.

In each constructor you would now have

this.interceptors = new List<User<int, IRepository>>();

Constructing and storing the DefaultInterceptor

The next bit of code we need to create is the constructed DefaultInterceptor.

Firstly, we create a local builder to store the constructed type.

Create a local builder
  1. LocalBuilder defaultInterceptorMethodVariable =
  2.     cIL.DeclareLocal(typeof(DefaultInterceptor<>).MakeGenericType(typeof(TBase)));

Once our local builder is ready, we then need to construct the DefaultInterceptor<TBase> and store it in the variable.

Connstruct DefaultInterceptor
  1. private static void ConstructDefaultInterceptor
  2.     (
  3.         ConstructorInfo defaultInterceptorConstructor,
  4.         ILGenerator cIL,
  5.         LocalBuilder defaultInterceptorMethodVariable
  6.     )
  7. {
  8.     cIL.Emit(OpCodes.Newobj, defaultInterceptorConstructor);
  9.     cIL.Emit(OpCodes.Stloc, defaultInterceptorMethodVariable);
  10. }

As you can see, using the ConstructorInfo named defaultInterceptorConstructor, we load the new object onto the stack.

Then using the store local opcode (OpCodes.Stloc), we store the new object in the local builder named defaultInterceptorMethodVariable.

Add the constructed DefaultInterceptor to the interceptors field collection

Using the add method created earlier in this post, we are going to add the new DefaultInterceptor object to the interceptors field collection.

Add Default Interceptor
  1. private static void AddDefaultInterceptorToInterceptorsList
  2.     (
  3.         FieldBuilder interceptorsField,
  4.         MethodInfo AddDefaultInterceptor,
  5.         ILGenerator cIL,
  6.         LocalBuilder defaultInterceptorMethodVariable
  7.     )
  8. {
  9.     cIL.Emit(OpCodes.Ldarg_0);
  10.     cIL.Emit(OpCodes.Ldfld, interceptorsField);
  11.     cIL.Emit(OpCodes.Ldloc, defaultInterceptorMethodVariable);
  12.     cIL.Emit(OpCodes.Callvirt, AddDefaultInterceptor);
  13. }

So, here’s whats going on.

The class instance is first loaded onto the stack using the load argument at index 0 opcode (OpCodes.Ldarg_0) (remember the first arg is the hidden class instance).

The interceptorsField is then loaded onto the stack using the load field opcode (OpCodes.Ldfld).

We then load the DefaultInterceptor object we stored locally using the load local opcode (OpCodes.Ldloc).

Then finally we call the AddDefaultInterceptor method using the call virtual opcode (Opcodes.Callvirt).

Completing the constructor

The last thing we need to do is complete the constructor.

Complete the constructor
  1. private static void CreateConstructor(ConstructorInfo constructorInfo, Type[] parameterTypes, ILGenerator cIL)
  2.         {
  3.             cIL.Emit(OpCodes.Ldarg_0);
  4.  
  5.             if (parameterTypes.Length > 0)
  6.             {
  7.                 LoadParameterTypes(parameterTypes, cIL);
  8.             }
  9.  
  10.             cIL.Emit(OpCodes.Call, constructorInfo);
  11.             cIL.Emit(OpCodes.Ret);
  12.         }
  13.  
  14.         private static void LoadParameterTypes(Type[] parameterTypes, ILGenerator cIL)
  15.         {
  16.             for (int i = 1; i <= parameterTypes.Length; i++)
  17.             {
  18.                 cIL.Emit(OpCodes.Ldarg_S, i);
  19.             }
  20.         }

So, the first thing we do again is load the class instance using the load argument at index 0 opcode (OpCodes.Ldarg_0).

We then load each parameter using OpCode.Ldarg_S, this opcode allows us to specify an index position for each argument.

We then setup calling the base constructor using OpCodes.Call and the base constructors ConstructorInfo.

Finally, all methods are required to return, even when they have a void return.

As there are no values on the stack after the OpCodes.Call line, we can safely call the OpCode.Ret to give the constructor a void return.

If there was a value, we would have to pop the value of the stack before calling return otherwise, the method would try and return a value.

Conclusion

This was a slightly hardcore post but hopefully it hasn’t been too hard to follow. The main thing is that a number of the really useful opcodes have been used and now the dynamic proxy is capable of being constructed.

If you download the code and debug through the tests at http://rapidioc.codeplex.com/, you’ll be able to create proxies at this point, they cannon do anything in terms of interception but you can happily run the tests, call base methods and properties and also take a look at the created assembly in Reflector.

Hope this is useful.

The next post should be up soon, it will be covering creating the private methods for calling the base class methods and properties.

Kind Regards,

Sean.

© ASP.net Weblogs or respective owner

Related posts about General Software Developm

Related posts about ASP.NET