Comment créer un délégué à partir D'un MethodInfo lorsque la signature de la méthode ne peut pas être connue à l'avance?

j'ai besoin d'une méthode qui prend un MethodInfo instance représentant une méthode statique non-générique avec une signature arbitraire et renvoie un délégué lié à cette méthode qui pourrait ensuite être invoqué en utilisant Delegate.DynamicInvoke méthode. Mon premier naïf essayer ressemblait à ceci:

using System;
using System.Reflection;

class Program
{
    static void Main()
    {
        var method = CreateDelegate(typeof (Console).GetMethod("WriteLine", new[] {typeof (string)}));
        method.DynamicInvoke("Hello world");
    }

    static Delegate CreateDelegate(MethodInfo method)
    {
        if (method == null)
        {
            throw new ArgumentNullException("method");
        }

        if (!method.IsStatic)
        {
            throw new ArgumentNullException("method", "The provided method is not static.");
        }

        if (method.ContainsGenericParameters)
        {
            throw new ArgumentException("The provided method contains unassigned generic type parameters.");
        }

        return method.CreateDelegate(typeof(Delegate)); // This does not work: System.ArgumentException: Type must derive from Delegate.
    }
}

j'espère que le MethodInfo.CreateDelegate la méthode pourrait comprendre le type de délégué correct lui-même. Bon, évidemment, il ne le peut pas. Alors, comment créer une instance de System.Type représentant un délégué avec une signature correspondant à la fourni MethodInfo exemple?

35
demandé sur nawfal 2013-05-03 21:04:24

2 réponses

Vous pouvez utiliser Système.Linq.Expression.Expression.GetDelegateType méthode:

using System;
using System.Linq;
using System.Linq.Expressions;
using System.Reflection;

class Program
{
    static void Main()
    {
        var writeLine = CreateDelegate(typeof(Console).GetMethod("WriteLine", new[] { typeof(string) }));
        writeLine.DynamicInvoke("Hello world");

        var readLine = CreateDelegate(typeof(Console).GetMethod("ReadLine", Type.EmptyTypes));
        writeLine.DynamicInvoke(readLine.DynamicInvoke());
    }

    static Delegate CreateDelegate(MethodInfo method)
    {
        if (method == null)
        {
            throw new ArgumentNullException("method");
        }

        if (!method.IsStatic)
        {
            throw new ArgumentException("The provided method must be static.", "method");
        }

        if (method.IsGenericMethod)
        {
            throw new ArgumentException("The provided method must not be generic.", "method");
        }

        return method.CreateDelegate(Expression.GetDelegateType(
            (from parameter in method.GetParameters() select parameter.ParameterType)
            .Concat(new[] { method.ReturnType })
            .ToArray()));
    }
}

il y a probablement une erreur de copier-coller dans la 2e vérification pour !method.IsStatic - vous ne devriez pas utiliser ArgumentNullException il n'. Et c'est un bon style fournir un nom de paramètre, comme un argument de ArgumentException.

Utiliser method.IsGenericMethod si vous souhaitez rejeter toutes les méthodes génériques et method.ContainsGenericParameters si vous voulez rejeter seulement les méthodes génériques ayant des paramètres de type non substitués.

30
répondu Oksana Gimmel 2013-05-03 17:17:21

Vous pouvez essayer le Système.LinQ.Les Expressions

...
using System.Linq.Expressions;
...

static Delegate CreateMethod(MethodInfo method)
{
    if (method == null)
    {
        throw new ArgumentNullException("method");
    }

    if (!method.IsStatic)
    {
        throw new ArgumentException("The provided method must be static.", "method");
    }

    if (method.IsGenericMethod)
    {
        throw new ArgumentException("The provided method must not be generic.", "method");
    }

    var parameters = method.GetParameters()
                           .Select(p => Expression.Parameter(p.ParameterType, p.Name))
                           .ToArray();
    var call = Expression.Call(null, method, parameters);
    return Expression.Lambda(call, parameters).Compile();
}

et l'utiliser plus tard comme suit

var method = CreateMethod(typeof (Console).GetMethod("WriteLine", new[] {typeof (string)}));
method.DynamicInvoke("Test Test");
3
répondu Khoa Nguyen 2013-05-03 17:16:55