web-dev-qa-db-fra.com

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 une instance de MethodInfo 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 être invoqué plus tard à l'aide de Delegate.DynamicInvoke méthode. Mon premier essai naïf 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érais que le MethodInfo.CreateDelegate la méthode pourrait déterminer le type de délégué correct lui-même. Eh bien, évidemment, cela ne peut pas. Alors, comment créer une instance de System.Type représentant un délégué avec une signature correspondant à l'instance MethodInfo fournie?

40

Vous pouvez utiliser la méthode System.Linq.Expressions.Expression.GetDelegateType :

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 de !method.IsStatic - vous ne devez pas y utiliser ArgumentNullException. Et c'est un bon style de fournir un nom de paramètre comme argument à ArgumentException.

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

35
Oksana Gimmel

Vous voudrez peut-être essayer System.LinQ.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
Khoa Nguyen