A simple Dynamic Proxy
- by Abhijeet Patel
Frameworks such as EF4 and MOQ do what most developers consider "dark magic". For instance in EF4, when you use a POCO for an entity you can opt-in to get behaviors such as "lazy-loading" and "change tracking" at runtime merely by ensuring that your type has the following characteristics:
The class must be public and not sealed.
The class must have a public or protected parameter-less constructor.
The class must have public or protected properties
Adhere to this and your type is magically endowed with these behaviors without any additional programming on your part.
Behind the scenes the framework subclasses your type at runtime and creates a "dynamic proxy" which has these additional behaviors and when you navigate properties of your POCO, the framework replaces the POCO type with derived type instances.
The MOQ framework does simlar magic. Let's say you have a simple interface:
public interface IFoo
{
int GetNum();
}
We can verify that the GetNum() was invoked on a mock like so:
var mock = new Mock<IFoo>(MockBehavior.Default);
mock.Setup(f => f.GetNum());
var num = mock.Object.GetNum();
mock.Verify(f => f.GetNum());
Beind the scenes the MOQ framework is generating a dynamic proxy by implementing IFoo at runtime. the call to moq.Object returns the dynamic proxy on which we then call "GetNum" and then verify that this method was invoked.
No dark magic at all, just clever programming is what's going on here, just not visible and hence appears magical!
Let's create a simple dynamic proxy generator which accepts an interface type and dynamically creates a proxy implementing the interface type specified at runtime.
public static class DynamicProxyGenerator
{
public static T GetInstanceFor<T>()
{
Type typeOfT = typeof(T);
var methodInfos = typeOfT.GetMethods();
AssemblyName assName = new AssemblyName("testAssembly");
var assBuilder = AppDomain.CurrentDomain.DefineDynamicAssembly(assName, AssemblyBuilderAccess.RunAndSave);
var moduleBuilder = assBuilder.DefineDynamicModule("testModule", "test.dll");
var typeBuilder = moduleBuilder.DefineType(typeOfT.Name + "Proxy", TypeAttributes.Public);
typeBuilder.AddInterfaceImplementation(typeOfT);
var ctorBuilder = typeBuilder.DefineConstructor(
MethodAttributes.Public,
CallingConventions.Standard,
new Type[] { });
var ilGenerator = ctorBuilder.GetILGenerator();
ilGenerator.EmitWriteLine("Creating Proxy instance");
ilGenerator.Emit(OpCodes.Ret);
foreach (var methodInfo in methodInfos)
{
var methodBuilder = typeBuilder.DefineMethod(
methodInfo.Name,
MethodAttributes.Public | MethodAttributes.Virtual,
methodInfo.ReturnType,
methodInfo.GetParameters().Select(p => p.GetType()).ToArray()
);
var methodILGen = methodBuilder.GetILGenerator();
methodILGen.EmitWriteLine("I'm a proxy");
if (methodInfo.ReturnType == typeof(void))
{
methodILGen.Emit(OpCodes.Ret);
}
else
{
if (methodInfo.ReturnType.IsValueType || methodInfo.ReturnType.IsEnum)
{
MethodInfo getMethod = typeof(Activator).GetMethod(/span>"CreateInstance",new Type[]{typeof((Type)});
LocalBuilder lb = methodILGen.DeclareLocal(methodInfo.ReturnType);
methodILGen.Emit(OpCodes.Ldtoken, lb.LocalType);
methodILGen.Emit(OpCodes.Call, typeofype).GetMethod("GetTypeFromHandle")); ));
methodILGen.Emit(OpCodes.Callvirt, getMethod);
methodILGen.Emit(OpCodes.Unbox_Any, lb.LocalType);
}
else
{
methodILGen.Emit(OpCodes.Ldnull);
}
methodILGen.Emit(OpCodes.Ret);
}
typeBuilder.DefineMethodOverride(methodBuilder, methodInfo);
}
Type constructedType = typeBuilder.CreateType();
var instance = Activator.CreateInstance(constructedType);
return (T)instance;
}
}
Dynamic proxies are created by calling into the following main types: AssemblyBuilder, TypeBuilder, Modulebuilder and ILGenerator. These types enable dynamically creating an assembly and emitting .NET modules and types in that assembly, all using IL instructions.
Let's break down the code above a bit and examine it piece by piece
Type typeOfT = typeof(T);
var methodInfos = typeOfT.GetMethods();
AssemblyName assName = new AssemblyName("testAssembly");
var assBuilder = AppDomain.CurrentDomain.DefineDynamicAssembly(assName, AssemblyBuilderAccess.RunAndSave);
var moduleBuilder = assBuilder.DefineDynamicModule("testModule", "test.dll");
var typeBuilder = moduleBuilder.DefineType(typeOfT.Name + "Proxy", TypeAttributes.Public);
We are instructing the runtime to create an assembly caled "test.dll"and in this assembly we then emit a new module called "testModule". We then emit a new type definition of name "typeName"Proxy into this new module. This is the definition for the "dynamic proxy" for type T
typeBuilder.AddInterfaceImplementation(typeOfT);
var ctorBuilder = typeBuilder.DefineConstructor(
MethodAttributes.Public,
CallingConventions.Standard,
new Type[] { });
var ilGenerator = ctorBuilder.GetILGenerator();
ilGenerator.EmitWriteLine("Creating Proxy instance");
ilGenerator.Emit(OpCodes.Ret);
The newly created type implements type T and defines a default parameterless constructor in which we emit a call to Console.WriteLine. This call is not necessary but we do this so that we can see first hand that when the proxy is constructed, when our default constructor is invoked.
var methodBuilder = typeBuilder.DefineMethod(
methodInfo.Name,
MethodAttributes.Public | MethodAttributes.Virtual,
methodInfo.ReturnType,
methodInfo.GetParameters().Select(p => p.GetType()).ToArray()
);
We then iterate over each method declared on type T and add a method definition of the same name into our "dynamic proxy" definition
if (methodInfo.ReturnType == typeof(void))
{
methodILGen.Emit(OpCodes.Ret);
}
If the return type specified in the method declaration of T is void we simply return.
if (methodInfo.ReturnType.IsValueType || methodInfo.ReturnType.IsEnum)
{
MethodInfo getMethod = typeof(Activator).GetMethod("CreateInstance",
new Type[]{typeof(Type)});
LocalBuilder lb = methodILGen.DeclareLocal(methodInfo.ReturnType);
methodILGen.Emit(OpCodes.Ldtoken, lb.LocalType);
methodILGen.Emit(OpCodes.Call, typeof(Type).GetMethod("GetTypeFromHandle"));
methodILGen.Emit(OpCodes.Callvirt, getMethod);
methodILGen.Emit(OpCodes.Unbox_Any, lb.LocalType);
}
If the return type in the method declaration of T is either a value type or an enum, then we need to create an instance of the value type and return that instance the caller. In order to accomplish that we need to do the following:
1) Get a handle to the Activator.CreateInstance method
2) Declare a local variable which represents the Type of the return type(i.e the type object of the return type) specified on the method declaration of T(obtained from the MethodInfo) and push this Type object onto the evaluation stack.
In reality a RuntimeTypeHandle is what is pushed onto the stack.
3) Invoke the "GetTypeFromHandle" method(a static method in the Type class) passing in the RuntimeTypeHandle pushed onto the stack previously as an argument, the result of this invocation is a Type object (representing the method's return type) which is pushed onto the top of the evaluation stack.
4) Invoke Activator.CreateInstance passing in the Type object from step 3, the result of this invocation is an instance of the value type boxed as a reference type and pushed onto the top of the evaluation stack.
5) Unbox the result and place it into the local variable of the return type defined in step 2
methodILGen.Emit(OpCodes.Ldnull);
If the return type is a reference type then we just load a null onto the evaluation stack
methodILGen.Emit(OpCodes.Ret);
Emit a a return statement to return whatever is on top of the evaluation stack(null or an instance of a value type) back to the caller
Type constructedType = typeBuilder.CreateType();
var instance = Activator.CreateInstance(constructedType);
return (T)instance;
Now that we have a definition of the "dynamic proxy" implementing all the methods declared on T, we can now create an instance of the proxy type and return that out typed as T.
The caller can now invoke the generator and request a dynamic proxy for any type T.
In our example when the client invokes GetNum() we get back "0". Lets add a new method on the interface called DayOfWeek GetDay()
public interface IFoo
{
int GetNum();
DayOfWeek GetDay();
}
When GetDay() is invoked, the "dynamic proxy" returns "Sunday" since that is the default value for the DayOfWeek enum
This is a very trivial example of dynammic proxies, frameworks like MOQ have a way more sophisticated implementation of this paradigm where in you can instruct the framework to create proxies which return specified values for a method implementation.