Creating delegates dynamically with parameter names
To dynamically create a delegate, you can use Reflection.Emit. Since delegates are special types in .Net, the code to create them is not quite obvious. The following is based on reflected code of methods used by Expression.Lambda()
. There, it's used to create custom delegate types in situations, where there is no Action
or Func
delegate available (more than 17 parameters, or parameters with ref
or out
).
class DelegateTypeFactory
{
private readonly ModuleBuilder m_module;
public DelegateTypeFactory()
{
var assembly = AppDomain.CurrentDomain.DefineDynamicAssembly(
new AssemblyName("DelegateTypeFactory"), AssemblyBuilderAccess.RunAndCollect);
m_module = assembly.DefineDynamicModule("DelegateTypeFactory");
}
public Type CreateDelegateType(MethodInfo method)
{
string nameBase = string.Format("{0}{1}", method.DeclaringType.Name, method.Name);
string name = GetUniqueName(nameBase);
var typeBuilder = m_module.DefineType(
name, TypeAttributes.Sealed | TypeAttributes.Public, typeof(MulticastDelegate));
var constructor = typeBuilder.DefineConstructor(
MethodAttributes.RTSpecialName | MethodAttributes.HideBySig | MethodAttributes.Public,
CallingConventions.Standard, new[] { typeof(object), typeof(IntPtr) });
constructor.SetImplementationFlags(MethodImplAttributes.CodeTypeMask);
var parameters = method.GetParameters();
var invokeMethod = typeBuilder.DefineMethod(
"Invoke", MethodAttributes.HideBySig | MethodAttributes.Virtual | MethodAttributes.Public,
method.ReturnType, parameters.Select(p => p.ParameterType).ToArray());
invokeMethod.SetImplementationFlags(MethodImplAttributes.CodeTypeMask);
for (int i = 0; i < parameters.Length; i++)
{
var parameter = parameters[i];
invokeMethod.DefineParameter(i + 1, ParameterAttributes.None, parameter.Name);
}
return typeBuilder.CreateType();
}
private string GetUniqueName(string nameBase)
{
int number = 2;
string name = nameBase;
while (m_module.GetType(name) != null)
name = nameBase + number++;
return name;
}
}
If you care about performance, you might want to create a cache of some sort, so that you don't create the same delegate type over and over.
The only modification in your code will be the line that creates lambdaExpression
:
LambdaExpression lambdaExpression = Expression.Lambda(
s_delegateTypeFactory.CreateDelegateType(method),
callExpression, lstParamExpressions);
But you actually don't need to deal with Expression
s at all. Delegate.CreateDelegate()
is enough:
private static Delegate CreateDelegate(object instance, MethodInfo method)
{
return Delegate.CreateDelegate(
s_delegateTypeFactory.CreateDelegateType(method), instance, method);
}
I have just stumbled upon a nice way to solve this issue, it looks like this for delegates to a static method:
private static Delegate CreateDelegate(MethodInfo method) {
var paramTypes = method.GetParameters().Select(p => p.ParameterType);
Type delegateType = Expression.GetDelegateType(paramTypes.Append(method.ReturnType).ToArray());
return Delegate.CreateDelegate(delegateType, method, true);
}
It uses this extension method:
public static IEnumerable<TSource> Append<TSource>(this IEnumerable<TSource> collection, TSource element) {
if (collection == null) throw new ArgumentNullException("collection");
foreach (TSource element1 in collection) yield return element1;
yield return element;
}