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 :)