La principale différence entre l'appelant directement (ce qui est à court de main pour Invoke(...)
) et à l'aide de DynamicInvoke
est le rendement; un facteur de plus de *700 par ma mesure (ci-dessous).
Avec le direct/Invoke
approche, les arguments sont déjà pré-validé par la signature de la méthode, et le code existe déjà à passer à celles de la méthode directement (je dirais "qu'IL", mais il me semble que le runtime fournit directement, sans aucun IL). Avec DynamicInvoke
il a besoin de les vérifier à partir de la matrice par la réflexion (c'est à dire sont-ils appropriés pour cet appel, ont-ils besoin d'unboxing, etc); c'est lent (si vous l'utilisez dans une boucle), et doit être évitée autant que possible.
Exemple; les résultats de la première (j'ai augmenté les LOOP
comptent à partir de la précédente édition, pour donner un bon de comparaison):
Direct: 53ms
Invoke: 53ms
DynamicInvoke (re-use args): 37728ms
DynamicInvoke (per-cal args): 39911ms
Avec le code:
static void DoesNothing(int a, string b, float? c) { }
static void Main() {
Action<int, string, float?> method = DoesNothing;
int a = 23;
string b = "abc";
float? c = null;
const int LOOP = 5000000;
Stopwatch watch = Stopwatch.StartNew();
for (int i = 0; i < LOOP; i++) {
method(a, b, c);
}
watch.Stop();
Console.WriteLine("Direct: " + watch.ElapsedMilliseconds + "ms");
watch = Stopwatch.StartNew();
for (int i = 0; i < LOOP; i++) {
method.Invoke(a, b, c);
}
watch.Stop();
Console.WriteLine("Invoke: " + watch.ElapsedMilliseconds + "ms");
object[] args = new object[] { a, b, c };
watch = Stopwatch.StartNew();
for (int i = 0; i < LOOP; i++) {
method.DynamicInvoke(args);
}
watch.Stop();
Console.WriteLine("DynamicInvoke (re-use args): "
+ watch.ElapsedMilliseconds + "ms");
watch = Stopwatch.StartNew();
for (int i = 0; i < LOOP; i++) {
method.DynamicInvoke(a,b,c);
}
watch.Stop();
Console.WriteLine("DynamicInvoke (per-cal args): "
+ watch.ElapsedMilliseconds + "ms");
}