Is there a way to turn fixed length arguments in to an __arglist when
using IL?
This is my first time using IL, I'm creating a kind of transparent proxy.
The code works but there are a few bits that I would like to improve. The
first is that when I intercept a method it can have any number or type of
arguments. Ideally I'd like to use __arglist or something similar. I tried
using EmitCall but it resulted in an empty __arglist. So instead I'm just
passing in a placeholder string and using that to recognize the end of the
arguments like this:
private object ProxyMethod(object methodName, object arg1, object arg2,
object arg3, object arg4, object arg5, object arg6, object arg7, object
arg8, object arg9, object arg10, object arg11, object arg12, object arg13,
object arg14, object arg15)
{
var args = new List<object>(new[] { arg1, arg2, arg3, arg4, arg5,
arg6, arg7, arg8, arg9, arg10, arg11, arg12, arg13, arg14, arg15,
EmptyArgPlaceHolder })
.TakeWhile(i => i.ToString() != EmptyArgPlaceHolder).ToArray();
// etc
Which is horrible.
Also I've tried to use reflection as little as possible but I still have
had to use MethodInfo. An instance of MethodInfo is created every time a
method on the proxy is called and then the MethodInfo is Invoked using the
supplied parameters. What I was wondering is it the actual creation of the
MethodInfo that is slow or is it the Invoke call? If it is the creation
then would it be worth maintaining a dictionary of method names against
MethodInfos so that I only have to create each MethodInfo once?
Also if you spot anything else that could be improved then please let me
know, as I said this is the first time I've used dynamic methods.
Thanks,
Joe
Here is the full code:
public class WcfProxy
{
private const string EmptyArgPlaceHolder = "EMPTY\u0007";
private readonly Type _interfaceType = typeof(ICmsDataServiceWcf);
private readonly AssemblyBuilder _assemblyBuilder;
private readonly ModuleBuilder _moduleBuilder;
private TypeBuilder _typeBuilder;
private WcfProxy()
{
// Get the app domain and initialize our own assembly with it
var appDomain = Thread.GetDomain();
var assemblyName = new AssemblyName { Name = "ReflectionHelperAsm" };
// All shared types get initiated on construction
_assemblyBuilder = appDomain.DefineDynamicAssembly(assemblyName,
AssemblyBuilderAccess.RunAndSave);
_moduleBuilder =
_assemblyBuilder.DefineDynamicModule("ReflectionHelperDynDll",
"WcfProxy.dll", true);
}
private static WcfProxy _instance;
public static WcfProxy Instance
{
get
{
if (_instance == null)
{
_instance = new WcfProxy();
}
return _instance;
}
}
#region Type Building Code
// Some of this code might be slow but it only gets run once
private Type CreateType()
{
_typeBuilder = _moduleBuilder.DefineType("ICmsDataServiceWcfProxy",
TypeAttributes.Public |
TypeAttributes.Class);
_typeBuilder.AddInterfaceImplementation(_interfaceType);
CreateConstructor();
CreateMethods();
return _typeBuilder.CreateType();
}
private void CreateConstructor()
{
var constructor = typeof(object).GetConstructor(new Type[0]);
var constructorBuilder =
_typeBuilder.DefineConstructor(MethodAttributes.Public,
CallingConventions.Standard, Type.EmptyTypes);
ILGenerator ilGenerator = constructorBuilder.GetILGenerator();
ilGenerator.Emit(OpCodes.Ldarg_0); // Load this
ilGenerator.Emit(OpCodes.Call, constructor); // Call object's
constructor
ilGenerator.Emit(OpCodes.Ret);
}
private void CreateMethods()
{
var proxyMethod = this.GetType().GetMethod("ProxyMethod");
// Find all of the methods that need to be implemented (we have no
properties etc) and implement them
foreach (var mi in _interfaceType.GetMethods())
{
var paramTypes = mi.GetParameters().Select(p => p.ParameterType);
// Define the method and copy the properties from the
interface method
var methodBuilder = _typeBuilder.DefineMethod(mi.Name,
MethodAttributes.Virtual | MethodAttributes.Public,
mi.ReturnType, paramTypes.ToArray());
var il = methodBuilder.GetILGenerator();
// In the body of this method we call the proxy method
EmitProxyMethodCall(il, proxyMethod, mi);
if (mi.ReturnType.IsValueType)
{
// If it is a primitive type then unbox it to the correct
type? Is that right?
il.Emit(OpCodes.Unbox_Any, mi.ReturnType);
}
else
{
// If it is a class then cast it to the correct type
il.Emit(OpCodes.Castclass, mi.ReturnType);
}
il.Emit(OpCodes.Ret); // End of method
_typeBuilder.DefineMethodOverride(methodBuilder, mi); //
Override the interface mthod
}
}
private void EmitProxyMethodCall(ILGenerator il, MethodInfo
proxyMethod, MethodInfo realMethod)
{
il.Emit(OpCodes.Ldarg_0); // Load the class's pointer
var realParams = realMethod.GetParameters();
var proxyParams = proxyMethod.GetParameters();
// Setup ProxyMethod's paramaters
il.Emit(OpCodes.Ldstr, realMethod.Name); // First param is always
the name of the real method
for (var i = 0; i < proxyParams.Length - 1; i++) // We -1 because
we have already populated one above
{
if (i < realParams.Length)
{
il.Emit(OpCodes.Ldarg, i + 1);
// Load the argument passed in to this method on to the
stack to be passed in to the proxy method. +1 because
Ldarg_0 is the class itself
il.Emit(OpCodes.Box, realParams[i].ParameterType); // Set
the type
}
else
{
il.Emit(OpCodes.Ldstr, EmptyArgPlaceHolder); //TODO: This
is ugly as hell - need to fix it.
//We use the bell character because it is seldom used in
other strings
}
}
il.Emit(OpCodes.Call, proxyMethod); // Call proxy method with the
paramaters above
}
No comments:
Post a Comment