Differenza tra Invoke e DynamicInvoke


128

Qual è la differenza tra Invoke e DynamicInvoke nei delegati? Per favore, datemi un esempio di codice che spieghi la differenza tra questi due metodi.

Risposte:


206

Quando si dispone di un'istanza delegata, è possibile conoscere il tipo esatto oppure si può semplicemente sapere che si tratta di un Delegate. Se conosci il tipo esatto, puoi usare Invoke, che è molto veloce , tutto è già pre-validato. Per esempio:

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

Però! Se sai solo che lo è Delegate, deve risolvere manualmente i parametri ecc. - Ciò potrebbe comportare unboxing, ecc. - Sta succedendo molta riflessione. Per esempio:

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

Nota ho scritto la argsmano lunga per chiarire che object[]è coinvolto un. Ci sono molti costi extra qui:

  • l'array
  • la convalida degli argomenti passati è un "adattamento" per l'effettivo MethodInfo
  • unboxing ecc. se necessario
  • riflessione-invoke
  • quindi il chiamante deve fare qualcosa per elaborare il valore restituito

Fondamentalmente, evita DynamicInvokequando puoi. Invokeè sempre preferibile, a meno che tutto ciò che hai sia a Delegatee an object[].

Per un confronto delle prestazioni, viene stampato quanto segue in modalità di rilascio all'esterno del debugger (un exe console):

Invoke: 19ms
DynamicInvoke: 3813ms

Codice:

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);

3
Significa che in caso di utilizzo il compilatore DynamicInvoke produce più codice IL per gestire l'invocazione dei delegati?
testCoder

2
@testCoder no, userà la riflessione
Marc Gravell

@MarcGravell quando provo questo in un metodo che sta generando un evento, sto ricevendo la prima chiamata del metodo che impiega circa 0,7766 ms ma la seconda impiega circa 0,0568 ms. Quando il primo è Invoke, impiega più tempo di DynamicInvoke o viceversa. Quando ho provato il tuo esempio con 1 loop e guardo ms Invoke: 0,0478ms, DynamicInvoke: 0,053ms. Perché li stai confrontando più di 1 chiamata? E perché il primo impiega più tempo della seconda chiamata di funzione?
uzay95,

4
@ uzay95 La prima chiamata al metodo provoca la compilazione JIT da parte del CLR - questo vale per qualsiasi metodo al primo richiamo dopo l'avvio del processo. In questo tipo di scenario, puoi fare una delle tre cose: (1) eseguire il metodo più volte in modo che il tempo impiegato per la prima chiamata diventi insignificante nel risultato finale, (2) non iniziare a misurare fino a dopo hai chiamato il metodo una volta, oppure (3) usa ngen.exe (overkill). Questo post lo spiega abbastanza bene ... stackoverflow.com/questions/4446203/…
Quanta

@ marc-gravell Non è necessario creare un array per passare a DynamicInvoke poiché la sua firma del metodo indica la parola chiave params per il parametro args.
zodo
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.