调用和DynamicInvoke之间的区别


Answers:


206

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

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

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

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

请注意,我已经写了args很长的篇幅以明确指出object[]涉及到了。这里有很多额外费用:

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

基本上,尽可能避免DynamicInvokeInvoke总是可取的,除非您所拥有的都是a Delegate和an object[]

为了进行性能比较,在调试器(控制台exe)之外的释放模式下打印以下内容:

Invoke: 19ms
DynamicInvoke: 3813ms

码:

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
这是否意味着在使用情况下DynamicInvoke编译器会产生更多的IL代码来处理委托调用?
testCoder 2012年

2
@testCoder不,它将使用反射
Marc Gravell

@MarcGravell当我在引发事件的方法中尝试此方法时,我收到的第一个方法调用大约花费0,7766 ms,而第二个方法调用大约花费0,0568 ms。当第一个调用时,它比DynamicInvoke花费更长的时间,反之亦然。当我尝试使用1循环的示例并查看ms时Invoke: 0,0478ms, DynamicInvoke: 0,053ms。您为什么要比较多个通话?为什么第一个调用比第二个调用函数花费更长的时间?
uzay95 '16

4
@ uzay95对该方法的第一次调用将导致CIT进行JIT编译-这适用于在进程启动后首次调用该方法的任何方法。在这种情况下,您可以执行以下三种操作之一:(1)多次运行该方法,以使第一次调用所花费的时间对最终结果无关紧要;(2)在您开始之前不要开始测量已经调用了该方法一次,或(3)使用ngen.exe(过度杀伤)。这篇文章解释得足够好... stackoverflow.com/questions/4446203/…–
Quanta

@ marc-gravell您不需要创建数组即可传递给DynamicInvoke,因为它的方法签名指出了args参数的 params关键字。
zodo
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.