Click here to Skip to main content
14,029,616 members
Click here to Skip to main content
Add your own
alternative version

Tagged as

Stats

36.2K views
932 downloads
32 bookmarked
Posted 28 Mar 2014
Licenced CPOL

Dynamic Interface Implementation

, 3 Apr 2014
Rate this:
Please Sign up or sign in to vote.
Generate proxy class dynamically which implements interface

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
{
    /// <summary>
    /// Create a type which implements the given interface and inherits from the given base type.
    /// Every call can be handled in the base class proper method.
    /// </summary>
    /// <param name="interfaceType">Interface to implement</param>
    /// <param name="dynamicProxyBaseType">Base class, which should inherit from <see cref="DynamicProxy"/></param>
    /// <returns>The new type</returns>
    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);

        //add
        {
            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);
        }
        //remove
        {
            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
{
    /// <summary>
    /// Creates an instance of a class which implements the given interface
    /// </summary>
    /// <typeparam name="TInterfaceType">Interface to be implemented</typeparam>
    /// <param name="constructorParameters">ctor parameters for creating the new instance</param>
    /// <returns>The new instance</returns>
    TInterfaceType CreateDynamicProxy<TInterfaceType>(params object[] constructorParameters);

    /// <summary>
    /// Creates an instance of a class which implements the given interface
    /// </summary>
    /// <param name="interfaceType">Interface to be implemented</param>
    /// <param name="constructorParameters">ctor parameters for creating the new instance</param>
    /// <returns>The new instance</returns>
    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!"); //LOCSTR
        }

        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

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)

Share

About the Author

Kemeny Attila
Architect Ardinsys Zrt.
Hungary Hungary
No Biography provided

You may also be interested in...

Comments and Discussions

 
SuggestionDynamicProxy TryInvokeMember with MethodInfo Pin
davideciarm21-May-18 2:16
memberdavideciarm21-May-18 2:16 
Questioncan create a no public interface? Pin
grart00110-Jan-18 20:53
membergrart00110-Jan-18 20:53 
QuestionDifferences with Castle Dynamic Proxy ? Pin
JSFAURE5-Oct-14 21:26
memberJSFAURE5-Oct-14 21:26 
QuestionProxy hide call Pin
Member 102150024-Apr-14 9:41
memberMember 102150024-Apr-14 9:41 
AnswerRe: Proxy hide call Pin
Kemeny Attila7-Apr-14 5:24
memberKemeny Attila7-Apr-14 5:24 
QuestionSource code Pin
Caleb1-Apr-14 8:06
memberCaleb1-Apr-14 8:06 
AnswerRe: Source code Pin
Kemeny Attila1-Apr-14 20:43
memberKemeny Attila1-Apr-14 20:43 
GeneralMy vote of 5 Pin
Volynsky Alex31-Mar-14 0:57
professionalVolynsky Alex31-Mar-14 0:57 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.

Permalink | Advertise | Privacy | Cookies | Terms of Use | Mobile
Web02 | 2.8.190419.4 | Last Updated 3 Apr 2014
Article Copyright 2014 by Kemeny Attila
Everything else Copyright © CodeProject, 1999-2019
Layout: fixed | fluid