|
using System;
using System.Collections.Generic;
using System.Reflection;
using System.Reflection.Emit;
namespace ThreadBound
{
/// <summary>
/// Creates a proxy class that wraps an interface inside a ContextBoundObject derived class.
/// </summary>
class InterfaceProxyGenerator
{
/// <summary>
/// Global Cache for all created wrapper types. This prevents creating the same wrapper over and
/// over again.
/// </summary>
private static readonly Dictionary<Type, Type> Wrappers = new Dictionary<Type, Type>();
/// <summary>
/// Module-Builder that is used for the creation of the wrappers.
/// </summary>
private static readonly ModuleBuilder ModBuilder;
/// <summary>
/// Static c'tor that creates the dynamic assembly and the dynamic module used for generating the proxy classes.
/// </summary>
static InterfaceProxyGenerator()
{
//-- There's only one assembly and one module used for all dynamic wrappers.
AssemblyBuilder AssBuilder = AppDomain.CurrentDomain.DefineDynamicAssembly(new AssemblyName("ProxyGeneratorAssembly"), AssemblyBuilderAccess.Run);
ModBuilder = AssBuilder.DefineDynamicModule("ProxyGenerator");
}
/// <summary>
/// Creates a c'tor that takes a reference to a real interface as an input parameter and saves this reference within the supplied realObject-Field.
/// </summary>
/// <param name="newType">The type-builder that should be used to create the new type.</param>
/// <param name="realObjectField">Information of the field that recieves the reference to the real interface.</param>
/// <param name="interfaceType">Type of the interface that should be used for the c'tors parameter and is also the type of the realObject field.</param>
static private void GenerateProxyCtor(TypeBuilder newType, FieldInfo realObjectField, Type interfaceType)
{
//-- Create the c'tor with one parameter. It takes a reference to the interface that should be called by the wrapper.
Type[] ctorArgs = { interfaceType };
ConstructorBuilder cBuilder = newType.DefineConstructor(MethodAttributes.Public | MethodAttributes.HideBySig | MethodAttributes.SpecialName | MethodAttributes.RTSpecialName, CallingConventions.Standard, ctorArgs);
//-- Now emit the method body.
ILGenerator ilGen = cBuilder.GetILGenerator();
//-- Push the this reference and call the default c'tor of the base class
ilGen.Emit(OpCodes.Ldarg_0);
ilGen.Emit(OpCodes.Call, newType.BaseType.GetConstructor(BindingFlags.NonPublic | BindingFlags.Instance, null, System.Type.EmptyTypes, null));
//-- Now push this and the first parameter (the reference to the real interface) onto the stack.
// The interface reference is then written to the realObjectReference via the stfld op-code.
ilGen.Emit(OpCodes.Ldarg_0);
ilGen.Emit(OpCodes.Ldarg_1);
ilGen.Emit(OpCodes.Stfld, realObjectField);
ilGen.Emit(OpCodes.Ret);
}
/// <summary>
/// Creates a proxy method that forwards all calls and all call parameters to the real object.
/// </summary>
/// <param name="newType">Type builder instance that is used to create the new method.</param>
/// <param name="realObjectFieldInfo">FieldInfo for the field that contains the reference to the real interface.</param>
/// <param name="interfaceType">Type of the interface for which to implement the proxy method. It is used to create the explicit decleration of the method name.</param>
/// <param name="method">Information about the method to proxy. This defines all properties of the proxy method.</param>
static private void GenerateProxyMethod(TypeBuilder newType, FieldInfo realObjectFieldInfo, Type interfaceType, MethodInfo method)
{
System.Byte paramCount = 0;
List<Type> paramTypes = new List<Type>();
//-- Create a list of all parameters of the interface.
// Increment a count because we need the number of parameters later.
foreach (ParameterInfo parameter in method.GetParameters())
{
paramTypes.Add(parameter.ParameterType);
paramCount++;
}
//-- Define the method for the interface.
MethodBuilder mBuilder = newType.DefineMethod(interfaceType.Name + "." + method.Name, MethodAttributes.Public | MethodAttributes.HideBySig | MethodAttributes.NewSlot | MethodAttributes.Virtual | MethodAttributes.Final, method.ReturnType, paramTypes.ToArray());
//-- Now emit the method body.
ILGenerator ilGen = mBuilder.GetILGenerator();
//-- Push this onto the stack and then use ldfld to get the real interface's reference out of the
// field referenced by realObjectFieldInfo.
// After this operation the this reference has been removed and the real interface's reference is onto the stack.
ilGen.Emit(OpCodes.Ldarg_0);
ilGen.Emit(OpCodes.Ldfld, realObjectFieldInfo);
//-- Now push all parameters onto the stack. For this the ldarg_S op-code is used
// because it is the most generic opcode. Eventho it uses a little more memory.
for (System.Byte i = 1; i <= paramCount; i++)
{
ilGen.Emit(OpCodes.Ldarg_S, i);
}
//-- Now call the original method. The this reference on the stack is the reference
// to the real interface.
ilGen.Emit(OpCodes.Callvirt, method);
ilGen.Emit(OpCodes.Ret);
//-- Now override the interface's method decleration with the real implementation.
newType.DefineMethodOverride(mBuilder, method);
}
/// <summary>
/// Creates a wrapper of an interface. This wrapper will be derived from ContextBoundObject so it can be
/// intercepted as needed. The proxy will just call the original implementation for each of the interface's
/// methods. This allows intercepting any interface regardless of the dependencies of the real class.
/// </summary>
/// <typeparam name="T">Type of the interface.</typeparam>
/// <param name="realObject">Reference to the real object that implementes the interface that should be proxied.</param>
/// <returns>An instance of the proxy class just created. This class will be derived from ContextBoundObject and MarshalByRefObject.</returns>
static public MarshalByRefObject CreateProxy<T>(T realObject) where T : class
{
Type wrapperType;
if (!typeof(T).IsInterface)
throw new ArgumentException("T is not an interface.");
//-- Check if the type is already cached. If it is, don't create a new one.
if (!Wrappers.ContainsKey(typeof(T)))
{
AssemblyName assName = new AssemblyName("ProxyGeneratorAssembly");
AssemblyBuilder assBuilder = AppDomain.CurrentDomain.DefineDynamicAssembly(assName, AssemblyBuilderAccess.Run);
ModuleBuilder modBuilder = assBuilder.DefineDynamicModule("Module" + typeof(T).Name);
//-- Create the proxy type.
TypeBuilder newType = modBuilder.DefineType("DynProxy" + typeof(T).Name, TypeAttributes.Public | TypeAttributes.AutoClass | TypeAttributes.BeforeFieldInit, typeof(ContextBoundObject));
//-- Now add the interface type to the class's definition. This way we can cast the proxy to the real interface.
newType.AddInterfaceImplementation(typeof(T));
//-- Create the realObject-field that stores the reference to the real interface.
FieldBuilder realObjectField = newType.DefineField("realObject", typeof(T), FieldAttributes.Private);
//-- Create a c'tor that accepts a reference to the real interface.
GenerateProxyCtor(newType, realObjectField, typeof(T));
//-- Now create a proxy method for each method of the interface.
foreach (MethodInfo method in typeof(T).GetMethods())
{
GenerateProxyMethod(newType, realObjectField, typeof(T), method);
}
//-- Create the new type and add it to the cache
wrapperType = newType.CreateType();
Wrappers.Add(typeof(T), wrapperType);
}
else
{
//-- If the type is already in the cache, get it.
wrapperType=Wrappers[typeof(T)];
}
return (MarshalByRefObject)Activator.CreateInstance(wrapperType, realObject);
}
}
}
|
By viewing downloads associated with this article you agree to the Terms of Service and the article's licence.
If a file you wish to view isn't highlighted, and is a text file (not binary), please
let us know and we'll add colourisation support for it.
This member has not yet provided a Biography. Assume it's interesting and varied, and probably something to do with programming.