使用参数名称动态创建委托 [英] Creating delegates dynamically with parameter names

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

问题描述

您好,我正在尝试创建一个函数,该函数动态创建一个委托,该委托具有与作为参数接收的 MethodInfo 相同的返回值和相同的参数,并且这对于相同的参数名称非常重要!

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!

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

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);
        }
    }

有人对我如何做到这一点有任何建议吗?说清楚,我关心参数名称的原因是我可以用参数名称激活委托,所以我可以这样称呼它(cheer="YAY!', height=3)(我的应用程序与 Python 集成,这就是我可以在没有 DynamicInvoke 的情况下完成它的方式,这也是参数名称如此重要的原因还有为什么我写的是'='而不是':')

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 ':')

推荐答案

要动态创建委托,您可以使用 Reflection.Emit.由于委托是 .Net 中的特殊类型,因此创建它们的代码不是很明显.以下是基于 Expression.Lambda() 使用的方法的反射代码.在那里,它用于在没有 ActionFunc 委托可用的情况下创建自定义委托类型(超过 17 个参数,或带有 refout).

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.

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

The only modification in your code will be the line that creates lambdaExpression:

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

但实际上您根本不需要处理Expression.Delegate.CreateDelegate() 就足够了:

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天全站免登陆