使用参数名动态创建代理 [英] Creating delegates dynamically with parameter names

查看:199
本文介绍了使用参数名动态创建代理的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我试图创建一个函数,它动态地创建一个具有相同返回值的代理和与它作为参数接收的MethodInfo相同的参数,同样参数名称也是非常重要的!



到目前为止我所做的是创建一个函数,返回一个接收相同参数类型的lambda,并且具有与MethodInfo相同的返回值,但它没有参数名: p>

  static void Example()
{
Person adam = new Person();
MethodInfo method = typeof(Person).GetMethod(Jump);
委托结果= CreateDelegate(adam,方法);
result.DynamicInvoke((uint)4,Yeahaa);
}

private static委托CreateDelegate(对象实例,MethodInfo方法)
{
var parametersInfo = method.GetParameters();
表达式[] expArgs = new Expression [parametersInfo.Length];
列表< ParameterExpression> lstParamExpressions = new List< ParameterExpression>(); (int i = 0; i< expArgs.Length; i ++)
{
expArgs [i] = Expression.Parameter(parametersInfo [i] .ParameterType,parametersInfo [i]。名称);
lstParamExpressions.Add((ParameterExpression)expArgs [i]);
}

MethodCallExpression callExpression = Expression.Call(Expression.Constant(instance),method,expArgs);
LambdaExpression lambdaExpression = Expression.Lambda(callExpression,lstParamExpressions);

返回lambdaExpression.Compile();
}

private class Person
{
public void Jump(uint height,string cheer)
{
Console.WriteLine(Person跳高+高++欢呼)
}
}

有没有人有什么建议,我该怎么做?
为了清楚起见,我关心参数名称的原因是所以我可以使用参数名激活委托,所以我可以这样调用它(cheer =YAY!,height = 3 )
(我的应用程序与Python集成在我没有DynamicInvoke的情况下我可以做到这一点,这也是为什么参数名称如此重要的原因
,为什么我写了'='和不是':')

解决方案

要动态创建一个委托,可以使用Reflection.Emit,因为委托是特殊类型。 Net,创建代码不是很明显,以下是基于 Expression.Lambda()使用的方法的反映代码,它用于创建自定义委托在没有 Action Func 可用的代理(超过17个参数或带有$的参数c $ c> ref 或 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); (int i = 0; i< parameters.Length; i ++)


{
var parameter = parameters [i];
invokeMethod.DefineParameter(i + 1,ParameterAttributes.None,parameter.Name);
}

return typeBuilder.CreateType();
}

私有字符串GetUniqueName(string nameBase)
{
int number = 2;
string name = nameBase;
while(m_module.GetType(name)!= null)
name = nameBase + number ++;
返回名称;
}
}

如果你关心性能,你可能想创建某种缓存,以便您不要一遍一遍地创建相同的代理类型。



代码中唯一的修改将是创建 lambdaExpression

  LambdaExpression lambdaExpression = Expression.Lambda(
s_delegateTypeFactory。 CreateDelegateType(method),
callExpression,lstParamExpressions);

但实际上你不需要处理 Expression s。 Delegate.CreateDelegate() 是足够的:

  private static Delegate CreateDelegate(object instance,MethodInfo method)
{
return Delegate.CreateDelegate(
s_delegateTypeFactory.CreateDelegateType(method),instance,method);
}


Hi I'm trying to create a function that dynamically creates a delegate with the same return value and the same parameters as a MethodInfo it receives as parameter and also and this is very important the same parameter names!

What I did so far is create a function that returns a lambda that receives the same parameter types and has the same return value as the MethodInfo but it doesn't have the parameter names:

    static void Example()
    {
        Person adam = new Person();
        MethodInfo method = typeof(Person).GetMethod("Jump");
        Delegate result = CreateDelegate(adam, method);
        result.DynamicInvoke((uint)4, "Yeahaa");
    }

    private static Delegate CreateDelegate(object instance, MethodInfo method)
    {
        var parametersInfo = method.GetParameters();
        Expression[] expArgs = new Expression[parametersInfo.Length];
        List<ParameterExpression> lstParamExpressions = new List<ParameterExpression>();
        for (int i = 0; i < expArgs.Length; i++)
        {
            expArgs[i] = Expression.Parameter(parametersInfo[i].ParameterType, parametersInfo[i].Name);
            lstParamExpressions.Add((ParameterExpression)expArgs[i]);
        }

        MethodCallExpression callExpression = Expression.Call(Expression.Constant(instance), method, expArgs);
        LambdaExpression lambdaExpression = Expression.Lambda(callExpression, lstParamExpressions);

        return lambdaExpression.Compile();
    }

    private class Person
    {
        public void Jump(uint height, string cheer)
        {
            Console.WriteLine("Person jumped " + height + " "+ cheer);
        }
    }

Does anyone have any suggestions how I can do that? To make it clear, the reason I care about the parameter names is so I would be able to activate the delegate with the parameter names, so I could call it like this (cheer="YAY!', height=3) (My application is integrated with Python that's how I'll be able to do it without DynamicInvoke and this is also the reason why the parameter names are so important and also why I wrote '=' and not ':')

解决方案

To dynamically create a delegate, you can use Reflection.Emit. Since delegates are special types in .Net, the code to create it 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 Expressions at all. Delegate.CreateDelegate() is enough:

private static Delegate CreateDelegate(object instance, MethodInfo method)
{
    return Delegate.CreateDelegate(
        s_delegateTypeFactory.CreateDelegateType(method), instance, method);
}

这篇关于使用参数名动态创建代理的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆