Creating a dynamic proxy generator with c# – Part 4 – Calling the base method

Posted by SeanMcAlinden on ASP.net Weblogs See other posts from ASP.net Weblogs or by SeanMcAlinden
Published on Mon, 29 Mar 2010 20:16:01 GMT Indexed on 2010/03/29 20:23 UTC
Read the original article Hit count: 1146

  • Creating a dynamic proxy generator with c# – Part 1 – Creating the Assembly builder, Module builder and caching mechanism
  • Creating a dynamic proxy generator with c# – Part 2 – Interceptor Design
  • Creating a dynamic proxy generator with c# – Part 3 – Creating the constructors

     

    The plan for calling the base methods from the proxy is to create a private method for each overridden proxy method, this will allow the proxy to use a delegate to simply invoke the private method when required.

    Quite a few helper classes have been created to make this possible so as usual I would suggest download or viewing the code at http://rapidioc.codeplex.com/.

    In this post I’m just going to cover the main points for when creating methods.

    Getting the methods to override

    The first two notable methods are for getting the methods.

    1. private static MethodInfo[] GetMethodsToOverride<TBase>() where TBase : class
    2. {
    3.     return typeof(TBase).GetMethods().Where(x =>
    4.         !methodsToIgnore.Contains(x.Name) &&                     
    5.         (x.Attributes & MethodAttributes.Final) == 0)
    6.         .ToArray();
    7. }
    8. private static StringCollection GetMethodsToIgnore()
    9. {
    10.     return new StringCollection()
    11.     {
    12.         "ToString",
    13.         "GetHashCode",
    14.         "Equals",
    15.         "GetType"
    16.     };
    17. }

    The GetMethodsToIgnore method string collection contains an array of methods that I don’t want to override.

    In the GetMethodsToOverride method, you’ll notice a binary AND which is basically saying not to include any methods marked final i.e. not virtual.

    Creating the MethodInfo for calling the base method

    This method should hopefully be fairly easy to follow, it’s only function is to create a MethodInfo which points to the correct base method, and with the correct parameters.

    1. private static MethodInfo CreateCallBaseMethodInfo<TBase>(MethodInfo method) where TBase : class
    2. {
    3.     Type[] baseMethodParameterTypes = ParameterHelper.GetParameterTypes(method, method.GetParameters());
    4.  
    5.     return typeof(TBase).GetMethod(
    6.        method.Name,
    7.        BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic,
    8.        null,
    9.        baseMethodParameterTypes,
    10.        null
    11.     );
    12. }
    13.  
    14. /// <summary>
    15. /// Get the parameter types.
    16. /// </summary>
    17. /// <param name="method">The method.</param>
    18. /// <param name="parameters">The parameters.</param>
    19. public static Type[] GetParameterTypes(MethodInfo method, ParameterInfo[] parameters)
    20. {
    21.     Type[] parameterTypesList = Type.EmptyTypes;
    22.  
    23.     if (parameters.Length > 0)
    24.     {
    25.         parameterTypesList = CreateParametersList(parameters);
    26.     }
    27.     return parameterTypesList;
    28. }

     

    Creating the new private methods for calling the base method

    The following method outline how I’ve created the private methods for calling the base class method.

    1. private static MethodBuilder CreateCallBaseMethodBuilder(TypeBuilder typeBuilder, MethodInfo method)
    2. {
    3.     string callBaseSuffix = "GetBaseMethod";
    4.  
    5.     if (method.IsGenericMethod || method.IsGenericMethodDefinition)
    6.     {                
    7.         return MethodHelper.SetUpGenericMethod
    8.             (
    9.                 typeBuilder,
    10.                 method,
    11.                 method.Name + callBaseSuffix,
    12.                 MethodAttributes.Private | MethodAttributes.HideBySig
    13.             );
    14.     }
    15.     else
    16.     {
    17.         return MethodHelper.SetupNonGenericMethod
    18.             (
    19.                 typeBuilder,
    20.                 method,
    21.                 method.Name + callBaseSuffix,
    22.                 MethodAttributes.Private | MethodAttributes.HideBySig
    23.             );
    24.     }
    25. }

    The CreateCallBaseMethodBuilder is the entry point method for creating the call base method. I’ve added a suffix to the base classes method name to keep it unique.

    Non Generic Methods

    Creating a non generic method is fairly simple

    1. public static MethodBuilder SetupNonGenericMethod(
    2.     TypeBuilder typeBuilder,
    3.     MethodInfo method,
    4.     string methodName,
    5.     MethodAttributes methodAttributes)
    6. {
    7.     ParameterInfo[] parameters = method.GetParameters();
    8.  
    9.     Type[] parameterTypes = ParameterHelper.GetParameterTypes(method, parameters);
    10.  
    11.     Type returnType = method.ReturnType;
    12.  
    13.     MethodBuilder methodBuilder = CreateMethodBuilder
    14.         (
    15.             typeBuilder,
    16.             method,
    17.             methodName,
    18.             methodAttributes,
    19.             parameterTypes,
    20.             returnType
    21.         );
    22.  
    23.     ParameterHelper.SetUpParameters(parameterTypes, parameters, methodBuilder);
    24.  
    25.     return methodBuilder;
    26. }
    27.  
    28. private static MethodBuilder CreateMethodBuilder
    29. (
    30.     TypeBuilder typeBuilder,
    31.     MethodInfo method,
    32.     string methodName,
    33.     MethodAttributes methodAttributes,
    34.     Type[] parameterTypes,
    35.     Type returnType
    36. )
    37. {
    38. MethodBuilder methodBuilder = typeBuilder.DefineMethod(methodName,
    39. methodAttributes,
    40. returnType, parameterTypes);
    41. return methodBuilder;
    42. }

    As you can see, you simply have to declare a method builder, get the parameter types, and set the method attributes you want.

     

    Generic Methods

    Creating generic methods takes a little bit more work.

    1. /// <summary>
    2. /// Sets up generic method.
    3. /// </summary>
    4. /// <param name="typeBuilder">The type builder.</param>
    5. /// <param name="method">The method.</param>
    6. /// <param name="methodName">Name of the method.</param>
    7. /// <param name="methodAttributes">The method attributes.</param>
    8. public static MethodBuilder SetUpGenericMethod
    9.     (
    10.         TypeBuilder typeBuilder,
    11.         MethodInfo method,
    12.         string methodName,
    13.         MethodAttributes methodAttributes
    14.     )
    15. {
    16.     ParameterInfo[] parameters = method.GetParameters();
    17.  
    18.     Type[] parameterTypes = ParameterHelper.GetParameterTypes(method, parameters);
    19.  
    20.     MethodBuilder methodBuilder = typeBuilder.DefineMethod(methodName,
    21.         methodAttributes);
    22.  
    23.     Type[] genericArguments = method.GetGenericArguments();
    24.  
    25.     GenericTypeParameterBuilder[] genericTypeParameters =
    26.         GetGenericTypeParameters(methodBuilder, genericArguments);
    27.  
    28.     ParameterHelper.SetUpParameterConstraints(parameterTypes, genericTypeParameters);
    29.  
    30.     SetUpReturnType(method, methodBuilder, genericTypeParameters);
    31.  
    32.     if (method.IsGenericMethod)
    33.     {
    34.         methodBuilder.MakeGenericMethod(genericArguments);
    35.     }
    36.  
    37.     ParameterHelper.SetUpParameters(parameterTypes, parameters, methodBuilder);
    38.  
    39.     return methodBuilder;
    40. }
    41.  
    42. private static GenericTypeParameterBuilder[] GetGenericTypeParameters
    43.     (
    44.         MethodBuilder methodBuilder,
    45.         Type[] genericArguments
    46.     )
    47. {
    48.     return methodBuilder.DefineGenericParameters(GenericsHelper.GetArgumentNames(genericArguments));
    49. }
    50.  
    51. private static void SetUpReturnType(MethodInfo method, MethodBuilder methodBuilder, GenericTypeParameterBuilder[] genericTypeParameters)
    52. {
    53.     if (method.IsGenericMethodDefinition)
    54.     {
    55.         SetUpGenericDefinitionReturnType(method, methodBuilder, genericTypeParameters);
    56.     }
    57.     else
    58.     {
    59.         methodBuilder.SetReturnType(method.ReturnType);
    60.     }
    61. }
    62.  
    63. private static void SetUpGenericDefinitionReturnType(MethodInfo method, MethodBuilder methodBuilder, GenericTypeParameterBuilder[] genericTypeParameters)
    64. {
    65.     if (method.ReturnType == null)
    66.     {
    67.         methodBuilder.SetReturnType(typeof(void));
    68.     }
    69.     else if (method.ReturnType.IsGenericType)
    70.     {
    71.         methodBuilder.SetReturnType(genericTypeParameters.Where
    72.             (x => x.Name == method.ReturnType.Name).First());
    73.     }
    74.     else
    75.     {
    76.         methodBuilder.SetReturnType(method.ReturnType);
    77.     }            
    78. }

    Ok, there are a few helper methods missing, basically there is way to much code to put in this post, take a look at the code at http://rapidioc.codeplex.com/ to follow it through completely.

    Basically though, when dealing with generics there is extra work to do in terms of

    • getting the generic argument types
    • setting up any generic parameter constraints
    • setting up the return type
    • setting up the method as a generic

    All of the information is easy to get via reflection from the MethodInfo.

     

    Emitting the new private method

    Emitting the new private method is relatively simple as it’s only function is calling the base method and returning a result if the return type is not void.

    1. ILGenerator il = privateMethodBuilder.GetILGenerator();
    2.  
    3. EmitCallBaseMethod(method, callBaseMethod, il);
    4.  
    5. private static void EmitCallBaseMethod(MethodInfo method, MethodInfo callBaseMethod, ILGenerator il)
    6. {
    7.     int privateParameterCount = method.GetParameters().Length;
    8.  
    9.     il.Emit(OpCodes.Ldarg_0);
    10.  
    11.     if (privateParameterCount > 0)
    12.     {
    13.         for (int arg = 0; arg < privateParameterCount; arg++)
    14.         {
    15.             il.Emit(OpCodes.Ldarg_S, arg + 1);
    16.         }
    17.     }
    18.  
    19.     il.Emit(OpCodes.Call, callBaseMethod);
    20.  
    21.     il.Emit(OpCodes.Ret);
    22. }

    So in the main method building method, an ILGenerator is created from the method builder.

  • The ILGenerator performs the following actions:

    • Load the class (this) onto the stack using the hidden argument Ldarg_0.
    • Create an argument on the stack for each of the method parameters (starting at 1 because 0 is the hidden argument)
    • Call the base method using the Opcodes.Call code and the MethodInfo we created earlier.
    • Call return on the method

     

    Conclusion

    Now we have the private methods prepared for calling the base method, we have reached the last of the relatively easy part of the proxy building.

    Hopefully, it hasn’t been too hard to follow so far, there is a lot of code so I haven’t been able to post it all so please check it out at http://rapidioc.codeplex.com/.

    The next section should be up fairly soon, it’s going to cover creating the delegates for calling the private methods created in this post.

     

    Kind Regards,

    Sean.

    © ASP.net Weblogs or respective owner

    Related posts about General Software Developm

    Related posts about ASP.NET