ChatGPT解决这个技术问题 Extra ChatGPT

调用和动态调用之间的区别

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

如果您有多播委托(调用调用列表中包含的多个委托),则只有 .DynamicInvoke(args) 可用。

M
Marc Gravell

当您有一个委托实例时,您可能知道确切的类型,或者您可能只知道它是一个 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 总是更可取的,除非您只有一个 Delegate 和一个 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);

这是否意味着如果使用 DynamicInvoke 编译器会产生更多的 IL 代码来处理委托调用?
@testCoder 不,它会使用反射
@MarcGravell 当我在引发事件的方法中尝试此操作时,我收到第一个方法调用大约需要 0,7766 毫秒,但第二个方法调用大约需要 0,0568 毫秒。当第一个是 Invoke 时,它比 DynamicInvoke 需要更长的时间,反之亦然。当我用 1 个循环尝试您的示例并查看 ms Invoke: 0,0478ms, DynamicInvoke: 0,053ms 时。你为什么要比较他们超过 1 个电话?为什么第一个函数比第二个函数调用花费更长的时间?
@uzay95 对该方法的第一次调用会导致 CLR 进行 JIT 编译——这适用于在进程启动后第一次调用它的任何方法。在这种情况下,您可以执行以下三件事之一:(1)多次运行该方法,以便第一次调用所花费的时间在最终结果中变得微不足道,(2)直到您之后才开始测量已经调用了一次方法,或者 (3) 使用 ngen.exe (overkill)。这篇文章解释得很好...... stackoverflow.com/questions/4446203/…
@marc-gravell 您不需要创建要传递给 DynamicInvoke 的数组,因为它的方法签名声明了 args 参数的 params 关键字。