Diferença entre Invoke e DynamicInvoke

128

Qual é a diferença entre Invoke e DynamicInvoke nos delegados? Por favor, me dê um exemplo de código que explique a diferença entre esses dois métodos.

testCoder
fonte

Respostas:

206

Quando você tem uma instância delegada, pode saber o tipo exato ou apenas saber que é a Delegate. Se você souber o tipo exato, poderá usar Invoke, o que é muito rápido - tudo já está pré-validado. Por exemplo:

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

Contudo! Se você apenas sabe que é Delegate, ele tem que resolver os parâmetros, etc. manualmente - isso pode envolver descompactação, etc. - muita reflexão está acontecendo. Por exemplo:

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

Observe que escrevi a argsmão longa para deixar claro que um object[]está envolvido. Existem muitos custos extras aqui:

  • a matriz
  • validar os argumentos passados ​​é um "ajuste" para o real MethodInfo
  • unboxing etc, conforme necessário
  • invocar a reflexão
  • então o chamador precisa fazer algo para processar o valor de retorno

Basicamente, evite DynamicInvokesempre que puder. Invokeé sempre preferível, a menos que tudo que você tem é um Delegatee um object[].

Para uma comparação de desempenho, é impresso o seguinte no modo de liberação fora do depurador (um exe do console):

Invoke: 19ms
DynamicInvoke: 3813ms

Código:

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
fonte
3
Isso significa que, em caso de uso, o compilador DynamicInvoke produz mais código IL para manipular a chamada de delegação?
Test
2
@testCoder não, ele vai usar a reflexão
Marc Gravell
@MarcGravell Quando tento isso em um método que está aumentando o evento, estou recebendo a primeira chamada de método que leva cerca de 0,7766 ms, mas a segunda está levando 0,0568 ms. Quando o primeiro é Invoke, leva mais tempo que o DynamicInvoke ou vice-versa. Quando tentei o seu exemplo com 1 loop e olhe ms Invoke: 0,0478ms, DynamicInvoke: 0,053ms. Por que você os está comparando mais de uma ligação? E por que o primeiro demora mais que a segunda chamada de função?
uzay95
4
@ uzay95 A primeira chamada ao método faz com que a compilação JIT ocorra pelo CLR - isso se aplica a qualquer método na primeira vez em que é chamado após o início do processo. Nesse tipo de cenário, você pode fazer uma de três coisas: (1) execute o método várias vezes para que o tempo que levou para a primeira chamada se torne insignificante no resultado final, (2) não comece a medir até depois de você chamou o método uma vez ou (3) use ngen.exe (exagero). Este post explica bem o suficiente ... stackoverflow.com/questions/4446203/...
Quanta
@ marc-gravell Você não precisa criar uma matriz para transmitir ao DynamicInvoke, pois a assinatura do método indica a palavra-chave params para o parâmetro args.
zodo 31/01