32

在谷歌搜索并登陆 SO 并阅读了另一个问题之后

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

更多信息:不使用 Reflection.Emit 或 type builder 可以优雅地完成吗?

这对我来说有点可惜,因为 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 方法的调用将有助于大大加快工作速度。

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

4

3 回答 3

22

老实说,如果您在编译时不知道类型,那么创建Delegate. 你不想使用DynamicInvoke; 它将与反射一样慢。主要的例外是当有一个代理类型潜伏在阴影中时,例如在订阅一个事件时 - 在这种情况下EventInfo,它可以使用。

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

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; }
}
于 2009-07-14T10:36:04.877 回答
9

为什么这么复杂?

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...” 令人困惑,因为它误导您认为这是一种转换。]

于 2010-12-28T08:43:02.320 回答
7

如果你事先不知道参数的个数或类型,大概是说你也不知道要创建的委托类型吧?

如果是这种情况,那么您将陷入绝对一般的情况。

但是,对于最常见的情况(没有 ref/out 参数,使用现有类型之一的参数足够少),您可以使用FuncorAction委托之一。(.NET 4.0 有大量参数的Func/Action类型,所以你真的只需要担心 out/ref 参数。)如果方法有一个非 void 返回类型使用Func,否则使用Action. 根据参数的数量确定使用哪种类型,例如

static readonly Type[] FuncTypes = { typeof(Func), 
    typeof(Func<>), typeof(Func<,>), typeof(Func<,,>), /* etc */ };

使用Type.MakeGenericType参数类型和返回类型来获得正确的委托类型,然后Delegate.CreateDelegate应该可以工作。

我现在没有时间制作样本,但如果您希望我稍后再做,请告诉我。

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

于 2009-07-14T10:34:10.607 回答