Unterschied zwischen Invoke und DynamicInvoke

128

Was ist der Unterschied zwischen Invoke und DynamicInvoke bei Delegierten? Bitte geben Sie mir ein Codebeispiel, das den Unterschied zwischen diesen beiden Methoden erklärt.

testCoder
quelle

Antworten:

206

Wenn Sie eine Delegateninstanz haben, kennen Sie möglicherweise den genauen Typ oder wissen nur, dass es sich um eine handelt Delegate. Wenn Sie den genauen Typ kennen, können Sie ihn verwenden Invoke, was sehr schnell geht - alles ist bereits vorab validiert. Beispielsweise:

Func<int,int> twice = x => x * 2;
int i = 3;
int j = twice.Invoke(i);
// or just:
int j = twice(i);

Jedoch! Wenn Sie nur wissen, dass Delegatedies der Fall ist, müssen die Parameter usw. manuell aufgelöst werden - dies kann das Auspacken usw. beinhalten - es wird viel nachgedacht. Beispielsweise:

Delegate slowTwice = twice; // this is still the same delegate instance
object[] args = { i };
object result = slowTwice.DynamicInvoke(args);

Hinweis: Ich habe die argslange Hand geschrieben, um zu verdeutlichen, dass es sich um eine object[]handelt. Hier fallen viele zusätzliche Kosten an:

  • das Array
  • Das Überprüfen der übergebenen Argumente ist eine "Anpassung" an das tatsächliche MethodInfo
  • Unboxing usw. nach Bedarf
  • Reflexionsaufruf
  • Dann muss der Anrufer etwas tun, um den Rückgabewert zu verarbeiten

Vermeiden DynamicInvokeSie grundsätzlich, wann immer Sie können. Invokeist immer vorzuziehen, es sei denn, Sie haben nur ein Delegateund einobject[] .

Für einen Leistungsvergleich wird im Release-Modus außerhalb des Debuggers (einer Konsolen-Exe) Folgendes gedruckt:

Invoke: 19ms
DynamicInvoke: 3813ms

Code:

Func<int,int> twice = x => x * 2;
const int LOOP = 5000000; // 5M
var watch = Stopwatch.StartNew();
for (int i = 0; i < LOOP; i++)
{
    twice.Invoke(3);
}
watch.Stop();
Console.WriteLine("Invoke: {0}ms", watch.ElapsedMilliseconds);
watch = Stopwatch.StartNew();
for (int i = 0; i < LOOP; i++)
{
    twice.DynamicInvoke(3);
}
watch.Stop();
Console.WriteLine("DynamicInvoke: {0}ms", watch.ElapsedMilliseconds);
Marc Gravell
quelle
3
Bedeutet dies, dass der DynamicInvoke-Compiler im Falle einer Verwendung mehr IL-Code erzeugt, um den Aufruf von Delegaten zu verarbeiten?
TestCoder
2
@ TestCoder nein, es wird Reflexion verwenden
Marc Gravell
@MarcGravell Wenn ich dies in einer Methode versuche, die ein Ereignis auslöst, erhalte ich einen ersten Methodenaufruf, der ungefähr 0,7766 ms dauert, während der zweite ungefähr 0,0568 ms dauert. Wenn das erste Mal Invoke ist, dauert es länger als DynamicInvoke oder umgekehrt. Als ich dein Beispiel mit 1 Schleife ausprobiert habe und dir ms anschaue Invoke: 0,0478ms, DynamicInvoke: 0,053ms. Warum vergleichen Sie sie mehr als 1 Anruf? Und warum dauert der erste länger als der zweite Funktionsaufruf?
uzay95
4
@ uzay95 Der erste Aufruf der Methode bewirkt, dass die JIT-Kompilierung von der CLR durchgeführt wird. Dies gilt für jede Methode beim ersten Aufruf nach dem Start des Prozesses. In einem solchen Szenario können Sie eines von drei Dingen ausführen: (1) Führen Sie die Methode mehrmals aus, damit die Zeit, die für den ersten Aufruf benötigt wurde, im Endergebnis unbedeutend wird. (2) Beginnen Sie erst nach Ihnen mit der Messung Ich habe die Methode einmal aufgerufen oder (3) benutze ngen.exe (Overkill). Dieser Beitrag erklärt es gut genug ... stackoverflow.com/questions/4446203/…
Quanta
@ marc-gravell Sie müssen kein Array erstellen, um es an DynamicInvoke zu übergeben, da die Methodensignatur das Schlüsselwort params für den Parameter args angibt .
Zodo