Domanda Costruisce un delegato da MethodInfo?


Dopo aver fatto ricerche su Google e atterrando su SO e dopo aver letto questa altra domanda

È possibile creare un delegato corretto da un MethodInfo Se non conoscevi il numero o i tipi di parametri al momento della compilazione?

Maggiori informazioni su questo: può essere fatto elegantemente senza l'uso di Reflection.Emit o di tipo builder?

Questo per me è un peccato perché Delegate.CreateDelegate mi richiede di specificare il tipo di Delegate corretto come primo parametro, altrimenti genererebbe eccezioni o invocherà un metodo errato.

Sto costruendo alcuni ingranaggi ninja e questo sarebbe di grande aiuto ... Grazie!


Ecco una soluzione generica:

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

Nota: Sto creando un'applicazione Silverlight che sostituirà un'applicazione javascript di anni precedenti in cui dispongo di più interfacce Javascript che chiamano nello stesso metodo [ScriptableMember] di Silverlight.

Tutte le interfacce JS legacy devono essere supportate così come una nuova interfaccia per accedere alle nuove funzionalità, quindi qualcosa che imposta automaticamente l'interfaccia JS e "delega" la chiamata al giusto metodo Silverlight potrebbe velocizzare molto il lavoro.

Non posso pubblicare il codice qui, quindi questo è il riassunto.


32
2017-07-14 10:28


origine


risposte:


Ad essere onesti, se non si conosce il tipo in fase di compilazione, non vi è un enorme vantaggio nel creare un Delegate. Non vuoi usare DynamicInvoke; sarà lento come la riflessione. L'eccezione principale a questo è quando c'è un tipo di delega in agguato nell'ombra, ad esempio quando si sottoscrive un evento - nel qual caso EventInfo lo rende disponibile.

Per informazioni, in .NET 3.5 su Expression, c'è:

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

Ciò potrebbe aiutare in una misura:

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

22
2017-07-14 10:36



Se non conosci il numero o il tipo di parametri in anticipo, presumibilmente significa che non conosci il tipo di delegato che vuoi creare?

Se è così, sei bloccato nel caso assolutamente generale.

Tuttavia, per la maggior parte Comune casi (nessun parametro di ref / out, pochi parametri sufficienti per utilizzare uno dei tipi esistenti) si potrebbe farla franca con uno dei Func o Action delegati. (.NET 4.0 ha Func/Action tipi per un numero enorme di parametri, quindi in realtà dovresti preoccuparti solo dei parametri out / ref.) Se il metodo ha un tipo di ritorno non vuoto usa Func, altrimenti usare Action. Calcola il tipo da utilizzare in base al numero di parametri, ad es.

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

Uso Type.MakeGenericType utilizzando i tipi di parametri e il tipo restituito per ottenere il tipo di delegato corretto, quindi Delegate.CreateDelegate dovrebbe funzionare.

Non ho tempo di elaborare un campione in questo momento, ma fammi sapere se vuoi che lo faccia in seguito.

Una domanda: come intendi usare questo delegato? Qualcos'altro avrà bisogno di sapere come eseguirlo, sicuramente ...


7
2017-07-14 10:34



Perché è complicato?

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

[Nota a margine: ho prefisso questo metodo "Crea ...". "Per ..." è confusionario in quanto ti induce a pensare che sia una conversione.]


6
2017-12-28 08:43