2011-09-23 1 views

risposta

15

Il modo più veloce sarebbe quello di memorizzare nella cache un delegato digitato; se si conosce la firma è sempre:

void PersonInstance.MethodName(string s); 

Quindi è possibile creare un Action<Person,string> via Delegate.CreateDelegate:

var action = (Action<Person,string>)Delegate.CreateDelegate(
    typeof(Action<Person,string>), method); 

Questo può quindi essere memorizzate nella cache contro il nome, e invocato come:

action(personInstance, value); 

Si noti che la cache qui è critica; la riflessione per individuare il metodo e preparare un delegato tipizzato non è banale.

Diventa più difficile se la firma è imprevedibile, poiché DynamicInvoke è relativamente lento. Il modo più veloce sarebbe utilizzare DynamicMethod e ILGenerator per scrivere un metodo shim (al volo) che acquisisce un oggetto [] per i parametri e lo decomprime e lo proietta per corrispondere alla firma; quindi è possibile memorizzare uno Action<object, object[]> o Func<object,object[],object>. Questo è, tuttavia, un argomento avanzato. Potrei fornire un esempio se davvero necessario. In sostanza la scrittura (in fase di esecuzione):

void DummyMethod(object target, object[] args) { 
    ((Person)target).MethodName((int)args[0],(string)args[1]); 
} 

Ecco un esempio di farlo (nota: non gestisce ref/out args in questo momento, e forse un paio di altri scenari - e ho lasciato il " cache" lato delle cose come un esercizio per il lettore):

using System; 
using System.Reflection; 
using System.Reflection.Emit; 

class Program 
{ 
    static void Main() 
    { 
     var method = typeof(Foo).GetMethod("Bar"); 
     var func = Wrap(method); 
     object[] args = { 123, "abc"}; 
     var foo = new Foo(); 
     object result = func(foo, args); 
    } 

    static Func<object, object[], object> Wrap(MethodInfo method) 
    { 
     var dm = new DynamicMethod(method.Name, typeof(object), new Type[] { 
      typeof(object), typeof(object[]) 
     }, method.DeclaringType, true); 
     var il = dm.GetILGenerator(); 

     if (!method.IsStatic) 
     { 
      il.Emit(OpCodes.Ldarg_0); 
      il.Emit(OpCodes.Unbox_Any, method.DeclaringType); 
     } 
     var parameters = method.GetParameters(); 
     for (int i = 0; i < parameters.Length; i++) 
     { 
      il.Emit(OpCodes.Ldarg_1); 
      il.Emit(OpCodes.Ldc_I4, i); 
      il.Emit(OpCodes.Ldelem_Ref); 
      il.Emit(OpCodes.Unbox_Any, parameters[i].ParameterType); 
     } 
     il.EmitCall(method.IsStatic || method.DeclaringType.IsValueType ? 
      OpCodes.Call : OpCodes.Callvirt, method, null); 
     if (method.ReturnType == null || method.ReturnType == typeof(void)) 
     { 
      il.Emit(OpCodes.Ldnull); 
     } 
     else if (method.ReturnType.IsValueType) 
     { 
      il.Emit(OpCodes.Box, method.ReturnType); 
     } 
     il.Emit(OpCodes.Ret); 
     return (Func<object, object[], object>)dm.CreateDelegate(typeof(Func<object, object[], object>)); 
    } 
} 

public class Foo 
{ 
    public string Bar(int x, string y) 
    { 
     return x + y; 
    } 
} 
+0

mi piacerebbe vedere un esempio di utilizzo DynamicMethod e ILGenerator (se non è troppo disturbo). – StriplingWarrior

+2

@StriplingWarrior non è un problema, no - Sono, tuttavia, attualmente su iPod, che non si presta bene alla scrittura di IL-emit code. Posso aggiungere un esempio in poche ore quando sono al PC? –

+0

+1 bello. Ho dovuto provarlo e funziona !. – kenny