Pregunta Crea un delegado de MethodInfo?


Después de googlear y aterrizar en SO y haber leído esta otra pregunta

¿Es posible construir un delegado correcto desde un MethodInfo? Si no sabía la cantidad o los tipos de parámetros en tiempo de compilación?

Más sobre esto: ¿se puede hacer esto de forma elegante sin el uso de Reflection.Emit o type builders?

Esto es bastante malo para mí porque Delegate.CreateDelegate requiere que especifique el tipo de delegado correcto como el primer parámetro o de lo contrario arrojaría excepciones o invocaría un método incorrecto.

Estoy construyendo algunos engranajes ninja y esto ayudaría mucho ... ¡Gracias!


Aquí hay una solución genérica:

/// <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: Estoy construyendo una aplicación de Silverlight que reemplazaría una aplicación javascript construida hace años en la que tengo múltiples interfaces de Javascript que llama al mismo método de Silverlight [ScriptableMember].

Todas esas interfaces JS heredadas necesitan ser compatibles, así como una nueva interfaz para acceder a nuevas características, por lo que algo que configura automáticamente la interfaz JS y "delega" la llamada al método Silverlight correcto ayudaría a acelerar mucho el trabajo.

No puedo publicar el código aquí, así que ese es el resumen.


32
2017-07-14 10:28


origen


Respuestas:


Para ser honesto, si no conoce el tipo en tiempo de compilación, no hay una gran cantidad de beneficio en la creación de un Delegate. No quieres usar DynamicInvoke; será tan lento como la reflexión. La principal excepción a esto es cuando hay un tipo de delegado al acecho en las sombras, por ejemplo al suscribirse a un evento, en cuyo caso EventInfo lo hace disponible.

Para obtener información, en .NET 3.5 en Expression, Ahi esta:

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

Eso podría ayudar en cierta medida:

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



Si no conoce la cantidad o el tipo de parámetros de antemano, probablemente eso significa que tampoco sabe el tipo de delegado que desea crear.

Si ese es el caso, estás atrapado en el caso absolutamente general.

Sin embargo, para la mayoría común casos (sin parámetros de ref / out, pocos parámetros suficientes para usar uno de los tipos existentes) podría salirse con la suya Func o Action delegados. (.NET 4.0 tiene Func/Action tipos para un gran número de parámetros, así que realmente solo tendría que preocuparse por los parámetros out / ref). Si el método tiene un tipo de retorno no nulo use Funcde lo contrario, use Action. Determine qué tipo usar en función de la cantidad de parámetros, p.

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

Utilizar Type.MakeGenericType utilizando los tipos de parámetros y el tipo de devolución para obtener el tipo de delegado correcto, luego Delegate.CreateDelegate Deberia trabajar.

No tengo tiempo para buscar una muestra en este momento, pero avíseme si quiere que lo haga más adelante.

Una pregunta: ¿cómo piensa utilizar este delegado? Algo más va a necesitar saber cómo ejecutarlo, seguramente ...


7
2017-07-14 10:34



¿Por qué tan complicado?

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: Prefijo este método "Crear ...". "Para ..." es confusa porque te hace pensar que es una conversión.]


6
2017-12-28 08:43