Creating a dynamic proxy generator with c# – Part 4 – Calling the base method
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.
- private static MethodInfo[] GetMethodsToOverride<TBase>() where TBase : class
- {
- return typeof(TBase).GetMethods().Where(x =>
- !methodsToIgnore.Contains(x.Name) &&
- (x.Attributes & MethodAttributes.Final) == 0)
- .ToArray();
- }
- private static StringCollection GetMethodsToIgnore()
- {
- return new StringCollection()
- {
- "ToString",
- "GetHashCode",
- "Equals",
- "GetType"
- };
- }
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.
- private static MethodInfo CreateCallBaseMethodInfo<TBase>(MethodInfo method) where TBase : class
- {
- Type[] baseMethodParameterTypes = ParameterHelper.GetParameterTypes(method, method.GetParameters());
- return typeof(TBase).GetMethod(
- method.Name,
- BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic,
- null,
- baseMethodParameterTypes,
- null
- );
- }
- /// <summary>
- /// Get the parameter types.
- /// </summary>
- /// <param name="method">The method.</param>
- /// <param name="parameters">The parameters.</param>
- public static Type[] GetParameterTypes(MethodInfo method, ParameterInfo[] parameters)
- {
- Type[] parameterTypesList = Type.EmptyTypes;
- if (parameters.Length > 0)
- {
- parameterTypesList = CreateParametersList(parameters);
- }
- return parameterTypesList;
- }
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.
- private static MethodBuilder CreateCallBaseMethodBuilder(TypeBuilder typeBuilder, MethodInfo method)
- {
- string callBaseSuffix = "GetBaseMethod";
- if (method.IsGenericMethod || method.IsGenericMethodDefinition)
- {
- return MethodHelper.SetUpGenericMethod
- (
- typeBuilder,
- method,
- method.Name + callBaseSuffix,
- MethodAttributes.Private | MethodAttributes.HideBySig
- );
- }
- else
- {
- return MethodHelper.SetupNonGenericMethod
- (
- typeBuilder,
- method,
- method.Name + callBaseSuffix,
- MethodAttributes.Private | MethodAttributes.HideBySig
- );
- }
- }
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
- public static MethodBuilder SetupNonGenericMethod(
- TypeBuilder typeBuilder,
- MethodInfo method,
- string methodName,
- MethodAttributes methodAttributes)
- {
- ParameterInfo[] parameters = method.GetParameters();
- Type[] parameterTypes = ParameterHelper.GetParameterTypes(method, parameters);
- Type returnType = method.ReturnType;
- MethodBuilder methodBuilder = CreateMethodBuilder
- (
- typeBuilder,
- method,
- methodName,
- methodAttributes,
- parameterTypes,
- returnType
- );
- ParameterHelper.SetUpParameters(parameterTypes, parameters, methodBuilder);
- return methodBuilder;
- }
- private static MethodBuilder CreateMethodBuilder
- (
- TypeBuilder typeBuilder,
- MethodInfo method,
- string methodName,
- MethodAttributes methodAttributes,
- Type[] parameterTypes,
- Type returnType
- )
- {
- MethodBuilder methodBuilder = typeBuilder.DefineMethod(methodName,
- methodAttributes,
- returnType, parameterTypes);
- return methodBuilder;
- }
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.
- /// <summary>
- /// Sets up generic method.
- /// </summary>
- /// <param name="typeBuilder">The type builder.</param>
- /// <param name="method">The method.</param>
- /// <param name="methodName">Name of the method.</param>
- /// <param name="methodAttributes">The method attributes.</param>
- public static MethodBuilder SetUpGenericMethod
- (
- TypeBuilder typeBuilder,
- MethodInfo method,
- string methodName,
- MethodAttributes methodAttributes
- )
- {
- ParameterInfo[] parameters = method.GetParameters();
- Type[] parameterTypes = ParameterHelper.GetParameterTypes(method, parameters);
- MethodBuilder methodBuilder = typeBuilder.DefineMethod(methodName,
- methodAttributes);
- Type[] genericArguments = method.GetGenericArguments();
- GenericTypeParameterBuilder[] genericTypeParameters =
- GetGenericTypeParameters(methodBuilder, genericArguments);
- ParameterHelper.SetUpParameterConstraints(parameterTypes, genericTypeParameters);
- SetUpReturnType(method, methodBuilder, genericTypeParameters);
- if (method.IsGenericMethod)
- {
- methodBuilder.MakeGenericMethod(genericArguments);
- }
- ParameterHelper.SetUpParameters(parameterTypes, parameters, methodBuilder);
- return methodBuilder;
- }
- private static GenericTypeParameterBuilder[] GetGenericTypeParameters
- (
- MethodBuilder methodBuilder,
- Type[] genericArguments
- )
- {
- return methodBuilder.DefineGenericParameters(GenericsHelper.GetArgumentNames(genericArguments));
- }
- private static void SetUpReturnType(MethodInfo method, MethodBuilder methodBuilder, GenericTypeParameterBuilder[] genericTypeParameters)
- {
- if (method.IsGenericMethodDefinition)
- {
- SetUpGenericDefinitionReturnType(method, methodBuilder, genericTypeParameters);
- }
- else
- {
- methodBuilder.SetReturnType(method.ReturnType);
- }
- }
- private static void SetUpGenericDefinitionReturnType(MethodInfo method, MethodBuilder methodBuilder, GenericTypeParameterBuilder[] genericTypeParameters)
- {
- if (method.ReturnType == null)
- {
- methodBuilder.SetReturnType(typeof(void));
- }
- else if (method.ReturnType.IsGenericType)
- {
- methodBuilder.SetReturnType(genericTypeParameters.Where
- (x => x.Name == method.ReturnType.Name).First());
- }
- else
- {
- methodBuilder.SetReturnType(method.ReturnType);
- }
- }
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.
- ILGenerator il = privateMethodBuilder.GetILGenerator();
- EmitCallBaseMethod(method, callBaseMethod, il);
- private static void EmitCallBaseMethod(MethodInfo method, MethodInfo callBaseMethod, ILGenerator il)
- {
- int privateParameterCount = method.GetParameters().Length;
- il.Emit(OpCodes.Ldarg_0);
- if (privateParameterCount > 0)
- {
- for (int arg = 0; arg < privateParameterCount; arg++)
- {
- il.Emit(OpCodes.Ldarg_S, arg + 1);
- }
- }
- il.Emit(OpCodes.Call, callBaseMethod);
- il.Emit(OpCodes.Ret);
- }
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.