Invoke和DynamicInvoke之间的区别

tes*_*der 123 .net c# delegates invoke dynamic-invoke

委托中的Invoke和DynamicInvoke有什么区别?请给我一些代码示例来解释这两种方法之间的区别.

Mar*_*ell 198

当你有一个委托实例时,你可能知道确切的类型,或者你可能只知道它是一个Delegate.如果你知道确切的类型,你可以使用Invoke,这是非常快的 - 一切都已预先验证.例如:

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

然而!如果你只是知道它是Delegate,它必须手动解决参数等 - 这可能涉及拆箱等 - 很多反思正在进行.例如:

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

注意我已经写了args很长的一句话,以明确表示object[]涉及到.这里有很多额外的费用:

  • 数组
  • 验证传递的参数是实际的"适合" MethodInfo
  • 必要时拆箱等
  • 反射调用
  • 然后调用者需要做一些事情来处理返回值

基本上,尽可能避免DynamicInvoke.Invoke总是更好,除非你拥有的只是一个Delegate和一个object[].

对于性能比较,调试器外部的发布模式(控制台exe)中的以下内容将打印:

Invoke: 19ms
DynamicInvoke: 3813ms
Run Code Online (Sandbox Code Playgroud)

码:

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);
Run Code Online (Sandbox Code Playgroud)

  • @ uzay95对方法的第一次调用导致CLR进行JIT编译 - 这适用于在进程启动后第一次调用它时的任何方法.在这种情况下,您可以执行以下三种操作之一:(1)多次运行该方法,以便第一次调用所花费的时间在最终结果中变得微不足道,(2)直到您之后才开始测量已经调用过该方法一次,或者(3)使用ngen.exe(overkill).这篇文章解释得很好...... /sf/ask/311234241/ (4认同)
  • 这是否意味着在使用的情况下,DynamicInvoke编译器会产生更多的IL代码来处理委托调用? (3认同)
  • 谢谢,非常充分的解释. (3认同)
  • @testCoder不,它会使用反射 (2认同)