Introduction
This article shows how to generate a proxy class runtime that implements a given interface. In our solution, the runtime generated implementor class derives from a base class and every call through the implemented interface will be delegated to it. The base class contains common handler methods for property get, property set, method call, event subscribe/unsubscribe. Our purpose is to generate a class runtime that implements the given interface, inherits from this base class and calls the base class' appropriate method with the appropriate arguments.
The proxy generation for an interface may be useful in such cases when every method call/property access/event handling has the same (proxy) implementation. For example, if there is no concrete implementation available for an interface, then it can be replaced by a proxy implementation. This proxy can log and queue all the calls and process them when the concrete implementation becomes available.
Another useful case may be when you never have the implementation of an interface because it is implemented in a different location. In this case, you can create a proxy that transfers each call with its parameters to the remote location.
Background
We use System.Reflection.Emit
builder classes to generate dynamic assembly, classes and IL code. The abstract
base class function names follow the naming convention of the <a href="http://msdn.microsoft.com/en-us/library/system.dynamic.dynamicobject(v=vs.110).aspx" target="_blank">DynamicObject</a>
class.
Using the Code
You can see the main interfaces and classes in Figure 1.
The abstract
base class for the dynamically generated implementor class is called DynamicProxy
. It has four abstract
methods which must be implemented in a concrete class. The TryGetMember
method will be invoked when a property value is requested, similarly TrySetMember
method when a property value is set. TryInvokeMember
will be invoked whenever a method is called via the implemented interface. And at last, TrySetEvent
method will be called when an event subscription or unsubscription happens. (Similarly to DynamicObject
, we use the same protected
method for property setting and event setting called TrySetMemberInternal
. In this method, the property setting and event subscription is split and the appropriate abstract
method is called.)
public abstract class DynamicProxy : IDisposable
{
....
protected DynamicProxy()
{
}
protected abstract bool TryInvokeMember(Type interfaceType, string name, object[] args, out object result);
protected abstract bool TrySetMember(Type interfaceType, string name, object value);
protected abstract bool TryGetMember(Type interfaceType, string name, out object result);
protected abstract bool TrySetEvent(Type interfaceType, string name, object value);
protected bool TrySetMemberInternal(Type interfaceType, string name, object value)
{
bool ret;
if (TypeHelper.HasEvent(interfaceType, name))
{
ret = TrySetEvent(interfaceType, name, value);
}
else
{
ret = TrySetMember(interfaceType, name, value);
}
return ret;
}
protected virtual void Dispose(bool disposing)
{
}
public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}
~DynamicProxy()
{
Dispose(false);
}
}
Dynamic Interface Implementation
The IDynamicInterfaceImplementor
interface defines a contract to create the type of the runtime generated proxy. It has only one method, CreateType
which takes two Type parameters, the first one represents the interface to implement and the other one indicates the base class' type. The method returns a new Type
which fits the above mentioned requirements.
public interface IDynamicInterfaceImplementor
{
Type CreateType(Type interfaceType, Type dynamicProxyBaseType);
}
The DynamicInterfaceImplementor
class implements this interface and does the hard work for us. To create a new type, it uses the builder classes of the System.Reflection.Emit
namespace. First an AssemblyBuilder
is requested from the current AppDomain
with the help of the DefineDynamicAssembly
method. The newly generated assembly's name has a random part in it so more than one interface imlementors can be used at the same time. Every assembly needs at least one module, so in the next step a dynamic module is added to the assembly. AssemblyBuilder
has a DefineDynamicModule
method which creates a module and returns a reference to its ModuleBuilder
.
private ModuleBuilder moduleBuilder = null;
private void Init()
{
...
Type ownClass = typeof(DynamicInterfaceImplementor);
string guid = Guid.NewGuid().ToString();
AssemblyName assemblyName = new AssemblyName(string.Concat(ownClass.Namespace, ".", ownClass.Name, "_", guid));
AssemblyBuilder ab = AppDomain.CurrentDomain.DefineDynamicAssembly(assemblyName, AssemblyBuilderAccess.RunAndSave);
moduleBuilder = ab.DefineDynamicModule(assemblyName.Name, string.Concat(assemblyName.Name, ".dll"));
...
}
A module is a logical container in an assembly which contains all the classes, so the ModuleBuilder
has a DefineType
method which returns a TypeBuilder
what we can use to construct a new type. It has a lot of straightforward methods that we can use to give the details of the type. For example, SetParent
and AddInterfaceImplementation
help us to specify the base class and the implemented interface(s) for the newly generated class. The dynamicProxyBaseType
is set as the base class. The interfaceType
is added to the type declaration as an implemented interface.
public Type CreateType(Type interfaceType, Type dynamicProxyBaseType)
{
...
string typeName = string.Concat(ownClassName, "+", interfaceType.FullName);
TypeBuilder tb = moduleBuilder.DefineType(typeName, TypeAttributes.Public);
tb.SetParent(dynamicProxyBaseType);
tb.AddInterfaceImplementation(interfaceType);
CreateConstructorBaseCalls(dynamicProxyBaseType, tb);
DynamicImplementInterface(new List<Type> { interfaceType }, new List<string>(), interfaceType, tb);
ret = tb.CreateType();
...
}
Now, we generate and add to the type the implementation of the interface members. This happens in the DynamicImplementImplementation
method which iterates through all the members of the given interface and implements them. It also searches for base interfaces and if it finds any, then recursively implements them too. We store the already implemented member names in the usedNames
variable because identical member names in the interface hierarchy are not yet supported.
private void DynamicImplementInterface(List<Type> implementedInterfaceList, List<string> usedNames, Type interfaceType, TypeBuilder tb)
{
List<MethodInfo> propAccessorList = new List<MethodInfo>();
GenerateProperties(usedNames, interfaceType, tb, propAccessorList);
GenerateEvents(usedNames, interfaceType, tb, propAccessorList);
GenerateMethods(usedNames, interfaceType, tb, propAccessorList);
foreach (Type i in interfaceType.GetInterfaces())
{
if (!implementedInterfaceList.Contains(i))
{
DynamicImplementInterface(implementedInterfaceList, usedNames, i, tb);
implementedInterfaceList.Add(i);
}
}
}
Property Generation
First, we iterate through the properties in the interface definition. Our purpose is that each invocation of the property's get accessor is forwarded to the TryGetMember
method of the base class. The base class is responsible for returning the proper value. For the definition of a new property, we use the DefineProperty
method. This property needs a method that serves as a getter method and contains the implementation details. This method has to be set as the getter method of the property. You can do it with the PropertyBuilder.
SetGetMethod
function. This new getter method is defined with the help of the TypeBuilder.
DefineMethod
. This returns a MethodBuilder
which has a GetILGenerator
method that returns an <a href="http://msdn.microsoft.com/en-us/library/system.reflection.emit.ilgenerator(v=vs.110).aspx" target="_blank">ILGenerator</a>
which can emit IL op codes. With this class, we can write the implementation details of the method in IL code. In our concrete implementation, it contains a method call to the base class' TryGetMember
method. Our IL code implementation in the EmitPropertyGet
is not the part of this article.
private void GenerateProperties(List<string> usedNames, Type interfaceType, TypeBuilder tb, List<MethodInfo> propAccessors)
{
foreach (PropertyInfo propertyInfo in interfaceType.GetProperties(BindingFlags.Instance | BindingFlags.Public))
{
...
PropertyBuilder pb = tb.DefineProperty(propertyInfo.Name, propertyInfo.Attributes, propertyInfo.PropertyType, null);
if (propertyInfo.CanRead)
{
MethodInfo getMethodInfo = propertyInfo.GetGetMethod();
propAccessors.Add(getMethodInfo);
MethodBuilder getMb = tb.DefineMethod(getMethodInfo.Name, MethodAttributes.Public |
MethodAttributes.Virtual, propertyInfo.PropertyType, Type.EmptyTypes);
ILGenerator ilGenerator = getMb.GetILGenerator();
EmitPropertyGet(ilGenerator, propertyInfo);
pb.SetGetMethod(getMb);
tb.DefineMethodOverride(getMb, getMethodInfo);
}
...
}
}
If the property has a setter accessor, then we generate another method for the setter method. You can set this method as the property's setter method with the SetSetMethod
function. The IL code generated in EmitPropertySet
calls the base class' TrySetMemberInternal
method with the property name and the new value.
if (propertyInfo.CanWrite)
{
MethodInfo setMethodInfo = propertyInfo.GetSetMethod();
propAccessors.Add(setMethodInfo);
MethodBuilder setMb = tb.DefineMethod(setMethodInfo.Name, MethodAttributes.Public |
MethodAttributes.Virtual, typeof(void), new Type[] { propertyInfo.PropertyType });
ILGenerator ilGenerator = setMb.GetILGenerator();
EmitPropertySet(ilGenerator, propertyInfo);
pb.SetSetMethod(setMb);
tb.DefineMethodOverride(setMb, setMethodInfo);
}
Event Generation
Then we iterate through all the events. The new events are generated in the proxy class with the help of DefineEvent
method. We also need a field to store the delegate containing the event's invocation list. The generated event's add
and remove
accessors have to be defined as a method. Both methods calls the TrySetMemberInternal
method in the base class. The call has two parameters, the first one contains the name of the event and the second one contains the new Delegate
. Before calling the TrySetMemberInternal
method, the add
function calls Delegate.Combine
to concatenate the old invocation list with the new delegate. Similarly, the remove
function calls Delegate.Remove
to remove the delegate from the invocation list. This behaviour is implemented with IL codes in the EmitEventAdd
and EmitEventRemove
methods which are not detailed in this article.
private void GenerateEvents(List<string> usedNames, Type interfaceType, TypeBuilder tb, List<MethodInfo> propAccessors)
{
foreach (EventInfo eventInfo in interfaceType.GetEvents(BindingFlags.Instance | BindingFlags.Public))
{
...
EventBuilder eb = tb.DefineEvent(eventInfo.Name, eventInfo.Attributes, eventInfo.EventHandlerType);
FieldBuilder ef = tb.DefineField(string.Concat("_", eventInfo.Name),
eventInfo.EventHandlerType, FieldAttributes.Private);
{
MethodInfo addMethodInfo = eventInfo.GetAddMethod();
propAccessors.Add(addMethodInfo);
MethodBuilder getMb = tb.DefineMethod(addMethodInfo.Name, MethodAttributes.Public |
MethodAttributes.Virtual, typeof(void), new Type[] { eventInfo.EventHandlerType });
ILGenerator ilGenerator = getMb.GetILGenerator();
EmitEventAdd(ilGenerator, eventInfo, ef);
tb.DefineMethodOverride(getMb, addMethodInfo);
}
{
MethodInfo removeMethodInfo = eventInfo.GetRemoveMethod();
propAccessors.Add(removeMethodInfo);
MethodBuilder getMb = tb.DefineMethod(removeMethodInfo.Name, MethodAttributes.Public |
MethodAttributes.Virtual, typeof(void), new Type[] { eventInfo.EventHandlerType });
ILGenerator ilGenerator = getMb.GetILGenerator();
EmitEventRemove(ilGenerator, eventInfo, ef);
tb.DefineMethodOverride(getMb, removeMethodInfo);
}
}
}
Method Generation
At last, we generate the methods for the new class. Iterating through all the methods of the interface, we generate an implementation for each method. The implementation contains a call to the base class' TryInvokeMember
method. It passes the method name and all the arguments of the method and gives back the return value of the TryInvokeMember
function. The EmitInvokeMethod
function generates the IL code for this method which is not described in detail here.
private void GenerateMethods(List<string> usedNames, Type interfaceType, TypeBuilder tb, List<MethodInfo> propAccessors)
{
foreach (MethodInfo mi in interfaceType.GetMethods())
{
...
var parameterInfoArray = mi.GetParameters();
var genericArgumentArray = mi.GetGenericArguments();
if (!propAccessors.Contains(mi))
{
MethodBuilder mb = tb.DefineMethod(mi.Name, MethodAttributes.Public | MethodAttributes.Virtual, mi.ReturnType, parameterInfoArray.Select(pi => pi.ParameterType).ToArray());
if (genericArgumentArray.Any())
{
mb.DefineGenericParameters(genericArgumentArray.Select(s => s.Name).ToArray());
}
EmitInvokeMethod(mi, mb);
tb.DefineMethodOverride(mb, mi);
}
}
}
Contructor Creation
So we implemented all the members of the given interface. Next, we need to define the contructors depending on the base class' constructors. For instance, if the base class does not have a default constructor, we have to generate a pass-through constructor. This contructor method gets the same parameters and passes them to the base class’ constructor. We generate the pass-through constructor based on this article.
Instantiation
The CreateType
method creates the new type and gives back a reference to the new Type
class. This Type
can be used to create a new instance with the help of IDynamicProxyFactory
interface. The CreateDynamicProxy
method expects two parameters, first the interface type to implement and second the parameter array to pass to the constructor and it returns the new instance. The CreateDynamicProxy has a generic overload which gets the interface type in the generic parameter so it can return a typed instance. The sequence diagram of creating a new instance looks like below:
The factory interface code shows the signature of these methods:
public interface IDynamicProxyFactory
{
TInterfaceType CreateDynamicProxy<TInterfaceType>(params object[] constructorParameters);
object CreateDynamicProxy(Type interfaceType, params object[] constructorParameters);
}
There is a generic class called DynamicProxyFactory
which implements this interface. This class has a generic parameter which defines the base class for all the generated proxy classes. In the constructor, it gets an IDynamicInterfaceImplementor
instance to use for the proxy class generation. After creating the new class, the method creates a new instance with the help of the Activator
class. The constructor parameters are passed to the CreateInstance
method.
public class DynamicProxyFactory<TDynamicProxyType> : IDynamicProxyFactory where TDynamicProxyType : DynamicProxy
{
private IDynamicInterfaceImplementor interfaceImplementor = null;
public DynamicProxyFactory(IDynamicInterfaceImplementor interfaceImplementor)
{
this.interfaceImplementor = interfaceImplementor;
}
public virtual TInterfaceType CreateDynamicProxy<TInterfaceType>(params object[] constructorParameters)
{
TInterfaceType ret;
ret = (TInterfaceType)CreateDynamicProxy(typeof(TInterfaceType), constructorParameters);
return ret;
}
public virtual object CreateDynamicProxy(Type interfaceType, params object[] constructorParameters)
{
if (interfaceType == null)
{
throw new ArgumentNullException("interfaceType");
}
if (!interfaceType.IsInterface)
{
throw new ArgumentException("interfaceType must be an interface!");
}
object ret = null;
Type t = interfaceImplementor.CreateType(interfaceType, typeof(TDynamicProxyType));
ret = Activator.CreateInstance(t, constructorParameters);
return ret;
}
}
The diagram shows the calling sequence of a new instance creation and interface invocations.
Points of Interest
This architecture provides a solution if you need proxy class instances for interfaces with a customizable common behaviour. In our project, we have a dynamic proxy implementation which makes remote calls between the client and the server passing all the parameters of the call to the remote side. It helps us to hide the service layer. I will show you this architecture in another article.
History
- 28th March, 2014: Initial version