2011-09-23 6 views

Odpowiedz

15

Najszybszym sposobem jest buforowanie wpisanego delegata; jeśli wiesz, że podpis jest zawsze:

void PersonInstance.MethodName(string s); 

Następnie można utworzyć Action<Person,string> poprzez Delegate.CreateDelegate:

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

ten może być następnie buforowane przed nazwą, a wywołany jako:

action(personInstance, value); 

Należy pamiętać, że pamięć podręczna jest tutaj krytyczna; odbicie, aby zlokalizować metodę i przygotować wpisanego delegata, nie jest banalne.

Robi się trudniej, jeśli podpis jest nieprzewidywalny, ponieważ DynamicInvoke jest stosunkowo wolny. Najprostszym sposobem byłoby użycie DynamicMethod i ILGenerator do napisania metody shim (w locie), która pobiera obiekt [] dla parametrów i rozpakowuje i rzuca go w celu dopasowania do podpisu - wtedy można zapisać Action<object, object[]> lub Func<object,object[],object>. Jest to jednak zaawansowany temat. Mogłabym podać przykład, gdyby naprawdę był potrzebny. Zasadniczo piśmie (przy starcie):

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

Oto przykład robi to (uwaga: nie obsługuje ref/out args w tej chwili, i ewentualnie kilka innych scenariuszy - a ja opuścił " cache "strona rzeczy jako ćwiczenie dla czytelnika):

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

Chciałbym zobaczyć przykład użycia DynamicMethod i ILGenerator (jeśli to nie jest zbyt trudne). – StriplingWarrior

+2

@StriplingWarrior nie jest problemem, nie - jednak obecnie jestem na iPodzie, który nie nadaje się dobrze do pisania kodu emitującego IL. Czy mogę dodać przykład w ciągu kilku godzin, kiedy jestem na komputerze? –

+0

+1 ładny. Musiałem spróbować i to działa! – kenny

Powiązane problemy