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);
}
与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…