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;
}
}
mi piacerebbe vedere un esempio di utilizzo DynamicMethod e ILGenerator (se non è troppo disturbo). – StriplingWarrior
@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? –
+1 bello. Ho dovuto provarlo e funziona !. – kenny