从MethodInfo构建委托?

时间:2009-07-14 10:28:02

标签: .net reflection delegates methodinfo

在Google上搜索并登陆后阅读this other question

  

是否可以从MethodInfo 构建正确的委托,如果您在编译时不知道参数的数量或类型?

更多相关内容:如果不使用Reflection.Emit或类型构建器,这可以优雅地完成吗?

这对我来说有点令人失望,因为Delegate.CreateDelegate要求我指定正确的Delegate类型作为第一个参数,否则它会抛出异常或调用不正确的方法。

我正在建造一些忍者装备,这会有很大帮助......谢谢!


以下是一般解决方案:

/// <summary>
/// Builds a Delegate instance from the supplied MethodInfo object and a target to invoke against.
/// </summary>
public static Delegate ToDelegate(MethodInfo mi, object target)
{
    if (mi == null) throw new ArgumentNullException("mi");

    Type delegateType;

    var typeArgs = mi.GetParameters()
        .Select(p => p.ParameterType)
        .ToList();

    // builds a delegate type
    if (mi.ReturnType == typeof(void)) {
        delegateType = Expression.GetActionType(typeArgs.ToArray());

    } else {
        typeArgs.Add(mi.ReturnType);
        delegateType = Expression.GetFuncType(typeArgs.ToArray());
    }

    // creates a binded delegate if target is supplied
    var result = (target == null)
        ? Delegate.CreateDelegate(delegateType, mi)
        : Delegate.CreateDelegate(delegateType, target, mi);

    return result;
}

注意:我正在构建一个Silverlight应用程序,它将取代之前的javascript应用程序,其中我有多个调用相同Silverlight [ScriptableMember]方法的Javascript接口。

需要支持所有这些遗留的JS接口以及用于访问新功能的新接口,因此自动设置JS接口并将调用“委托”到正确的Silverlight方法将有助于加快工作量。< / p>

我不能在这里发布代码,所以这是摘要。

3 个答案:

答案 0 :(得分:22)

老实说,如果您在编译时不知道类型,那么创建Delegate并没有太大的好处。您不想使用DynamicInvoke;它会和反射一样慢。这种情况的主要例外是在阴影中潜伏着一个委托类型,例如订阅某个事件时 - EventInfo使其可用。

有关信息,在Expression上的.NET 3.5中,有:

Expression.GetActionType(params Type[] typeArgs);
Expression.GetFuncType(params Type[] typeArgs)

这可能有所帮助:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Linq.Expressions;
using System.Reflection;
static class Program {
    static void Main() {
        DoStuff("Test1");
        DoStuff("Test2");
    }
    static void DoStuff(string methodName) {
        MethodInfo method = typeof(Program).GetMethod(methodName);
        List<Type> args = new List<Type>(
            method.GetParameters().Select(p => p.ParameterType));
        Type delegateType;
        if (method.ReturnType == typeof(void)) {
            delegateType = Expression.GetActionType(args.ToArray());
        } else {
            args.Add(method.ReturnType);
            delegateType = Expression.GetFuncType(args.ToArray());
        }
        Delegate d = Delegate.CreateDelegate(delegateType, null, method);
        Console.WriteLine(d);
    }
    public static void Test1(int i, DateTime when) { }
    public static float Test2(string x) { return 0; }
}

答案 1 :(得分:7)

如果您事先不知道参数的数量或类型,可能这意味着您不知道要创建的委托类型?

如果是这种情况,你就会陷入绝对普遍的情况。

但是,对于大多数常见的情况(没有参考/输出参数,没有足够的参数可以使用其中一种现有类型),您可以使用其中一个Func或{{ 1}}代表。 (.NET 4.0对于大量参数有Action / Func类型,所以实际上你只需要担心out / ref参数。)如果方法有非void返回类型使用Action,否则请使用Func。根据参数的数量确定要使用的类型,例如

Action

使用static readonly Type[] FuncTypes = { typeof(Func), typeof(Func<>), typeof(Func<,>), typeof(Func<,,>), /* etc */ }; 使用参数类型和返回类型来获取正确的委托类型,然后Type.MakeGenericType应该有效。

我现在没有时间处理样本,但如果您希望我以后再告诉我。

一个问题:你打算如何使用这个代表?其他东西需要知道如何执行它,当然......

答案 2 :(得分:6)

为什么那么复杂?

public static Delegate CreateDelegate(this MethodInfo method)
{
    return Delegate.CreateDelegate
    (
        Expression.GetDelegateType
        (
            method.GetParameters()
                .Select(p => p.ParameterType)
                .Concat(new Type[] { method.ReturnType })
                .ToArray()
        ),
        null,
        method
    );   
}

[旁注:我给这个方法加了前缀“创建......”。 “To ......”令人困惑,因为它误导你认为这是转换。]