The Darkside

Shedding light on things and stuff

 
  Home :: Contact :: Syndication  :: Login
  83 Posts :: 0 Stories :: 57 Comments :: 2 Trackbacks

Ads

 

Donate via PayPal...

...if you feel the site helped.

Archives

Post Categories

Open Source Projects

Other Blogs

This is (another) look at implementing a dynamic proxy using Reflection Emit.  Why would you need a dynamic proxy? Well, there are times when functionality needs to be injected into existing code that can not be modified. My dynamic proxy generator allow you to supply two Action parameters the will be executed as pre- and post-call methods. The tests that I have provided with my sample application show an example of injecting timing code into existing methods. It could also be used to insert logging and tracing information into code.

The route that I have followed to create my dynamic proxy has its limitations, as have many other such as the Castle Dynamic Proxy and LinFu – only virtual methods can successfully be “proxied”. For my needs, the dynamic proxy type had to adhere to the Liskov Substitution Principle.  Also, and very importantly, my sample is merely an exercise in academics – I’ve been fiddling with emitting code for the last week, so this post evolved from that. If you need a fully fledged dynamic proxy, try out one of links above.

Download: Darkside.DynamicProxy.zip

I’ve implemented a static method in a generic class to allow for strongly typed implementation.

public static T CreateDynamicProxy<T>(Action preCall, Action postCall) where T:class , new()

First on the list of tasks is to create a dynamic assembly, module and type.

   15 //Do some setup work on the name of the assembly and type
   16 var typeName = typeof (T).FullName;
   17 var dynamicTypeName = typeName + "DynamicProxyType";
   18 var name = new AssemblyName { Name = dynamicTypeName };
   19 
   20 //Define a new dynamic assembly
   21 AssemblyBuilder assembly = AppDomain.CurrentDomain.DefineDynamicAssembly(name, AssemblyBuilderAccess.Run);
   22 //Now define a new module
   23 ModuleBuilder module = assembly.DefineDynamicModule(dynamicTypeName + ".dll");
   24 //And create a new type which inherits the base type you've passed in
   25 TypeBuilder type = module.DefineType(dynamicTypeName, TypeAttributes.Public, typeof(T));

I do some setup work creating a new assembly name, as well as a new name for my dynamic type which I use throughout the method. A point of interest is on line 21, the last parameter of the DefineDynamicAssembly method; The AssemblyBuilderAccess.Run can be changed to AssemblyBuilderAccess.RunAndSave which is useful if you decide to save the new assembly to disk, meaning that in future you wouldn’t have to regenerate the dynamic type, you could simply load it from the assembly.

Next on the list is the constructor – I add a new constructor to the type that takes two parameters; thesewill be the two parameters of type Action that are the actual pre- and post-call methods passed to the method. Before I actually emit the code to assign these parameters to internal fields, I emit code to call the default constructor in the base class.

 Expand Code
   33 //Define a new constructor for the dynamic type, which takes two arguments of type Action
   34 ConstructorBuilder newConstructorBuilder = type.DefineConstructor(MethodAttributes.Public, CallingConventions.Standard, new[]{typeof(Action), typeof(Action)});
   35 var newConstructorGenerator = newConstructorBuilder.GetILGenerator();
   36 //Emit code to call the base default constructor
   37 newConstructorGenerator.Emit(OpCodes.Ldarg_0);
   38 newConstructorGenerator.Emit(OpCodes.Call, typeof(T).GetConstructors()[0]);
   39 
   40 //Emit code to save the first parameter passed to the constructor
   41 newConstructorGenerator.Emit(OpCodes.Ldarg_0);
   42 newConstructorGenerator.Emit(OpCodes.Ldarg_1);
   43 newConstructorGenerator.Emit(OpCodes.Stfld, preCallFieldBuilder);
   44 
   45 //Emit code to save the second parameter passed to the constructor
   46 newConstructorGenerator.Emit(OpCodes.Ldarg_0);
   47 newConstructorGenerator.Emit(OpCodes.Ldarg_2);
   48 newConstructorGenerator.Emit(OpCodes.Stfld, postCallFieldBuilder);
   49 newConstructorGenerator.Emit(OpCodes.Ret);

I then emit two private methods that actually make the calls. The methods are what are added to the overriden methods which I emit later on.

 Expand Code
   51 //Build a method for the pre call 
   52 //Define a new private method. Once again, the name is just an attempt at being unique in the code
   53 MethodBuilder preCallMethodBuilder = type.DefineMethod("__dyn_PreCall", MethodAttributes.Private);
   54 var genPreCall = preCallMethodBuilder.GetILGenerator();
   55 genPreCall.Emit(OpCodes.Ldarg_0);
   56 genPreCall.Emit(OpCodes.Ldfld, preCallFieldBuilder);
   57 genPreCall.Emit(OpCodes.Callvirt, typeof (Action).GetMethod("Invoke"));
   58 genPreCall.Emit(OpCodes.Ret);
   59 
   60 //Build a method for the post call 
   61 MethodBuilder postCallMethodBuilder = type.DefineMethod("__dyn_PostCall", MethodAttributes.Private);
   62 var genPostCall = postCallMethodBuilder.GetILGenerator();
   63 genPostCall.Emit(OpCodes.Ldarg_0);
   64 genPostCall.Emit(OpCodes.Ldfld, postCallFieldBuilder);
   65 genPostCall.Emit(OpCodes.Callvirt, typeof (Action).GetMethod("Invoke"));
   66 genPostCall.Emit(OpCodes.Ret);

I could have called the fields directly, but added the methods in so that I could modify to do other things. Just in case :) After that, all that’s left to do in constructing the dynamic is to loop through the methods from the original/base type, and emit new method calls for them.

 Expand Code
   68 //Loop through all the methods that are public, virtual and instance methods
   69 foreach (var methodInfo in typeof(T).GetMethods(BindingFlags.Public | BindingFlags.Instance).Where(mi => mi.IsVirtual))
   70 {
   71     //Define a new method
   72     MethodBuilder mb = type.DefineMethod(methodInfo.Name,
   73                                          MethodAttributes.Public | MethodAttributes.HideBySig | MethodAttributes.Virtual,
   74                                          methodInfo.ReturnType,
   75                                          methodInfo.GetParameters().Select(pi => pi.ParameterType).ToArray());
   76 
   77     var mbGen = mb.GetILGenerator();
   78 
   79     //Only emit the code that makes a call to the PreCall if it is defined
   80     mbGen.Emit(OpCodes.Ldarg_0);
   81     mbGen.Emit(OpCodes.Call, preCallMethodBuilder);
   82 
   83     //Setup up the parameters that were passed into this call for the base class call
   84     mbGen.Emit(OpCodes.Ldarg_0);
   85     //Loop through all the parameters and emit statements
   86     for (int i = 1; i <= methodInfo.GetParameters().Count(); i++)
   87         mbGen.Emit(OpCodes.Ldarg_S, i);
   88     //Emit the code that makes the base call
   89     mbGen.Emit(OpCodes.Call, methodInfo);
   90 
   91     //Only emit the code that makes a call to the PostCall method if it is defined
   92     mbGen.Emit(OpCodes.Ldarg_0);
   93     mbGen.Emit(OpCodes.Call, postCallMethodBuilder);
   94     mbGen.Emit(OpCodes.Ret);
   95 }
   96 //Create the type
   97 type.CreateType();

The final act of the factory method is to instantiate a new instance of the dynamic type, passing to it the two action parameters from the method call. I do a null check on the parameters and substitute it with a dummy call if it is.

 Expand Code
  103 //Return an instance of the type
  104 return assembly.CreateInstance(dynamicTypeName, false,
  105                                BindingFlags.CreateInstance,
  106                                null,
  107                                new[]
  108                                    {
  109                                        preCall ??
  110                                        new Action(delegate {
  111                                                                return;
  112                                        }),
  113                                        postCall ??
  114                                        new Action(delegate {
  115                                                                return;
  116                                        })
  117                                    },
  118                                CultureInfo.CurrentCulture, null) as T;

In the first test method, my code looks as follows:

var fb = new FooBar();
 
fb.Foo();
fb.Foo(42);
int x = fb.Bar();
Assert.AreEqual(42,x);

and when using the dynamic proxy creation method, my code changes to this:

var fb = DynamicProxyFactory.CreateDynamicProxy<FooBar>(null, null);
 
fb.Foo();
fb.Foo(42);
int x = fb.Bar();
Assert.AreEqual(42, x);

You’ll notice that, barring the instantiation of the class, the rest of the code stays the same. The next example shows much the same, except demonstrates how to add timing to the methods passed as parameters:

var sw = new Stopwatch();
Action preCall = sw.Start;
Action postCall = sw.Stop;
 
var fb = DynamicProxyFactory.CreateDynamicProxy<FooBar>(preCall, postCall);
 
fb.Foo();
fb.Foo(42);
int x = fb.Bar();
 
Assert.AreEqual(42, x);
Console.WriteLine("Time taken: " + sw.ElapsedMilliseconds); //Should be 100ms or more :)
posted on Thursday, August 13, 2009 12:00 AM
Comments have been closed on this topic.