使用DynamicInvoke直接调用委托和使用DynamicInvokeImpl有什么区别?

DynamicInvoke和DynamicInvokeImpl的文档都说:

动态调用(后期绑定)当前委托表示的方法。

我注意到DynamicInvoke和DynamicInvokeImpl采用了一个对象数组而不是一个特定的参数列表(这是我猜的后期部分)。 但这是唯一的区别吗? DynamicInvoke和DynamicInvokeImpl之间有什么区别。

直接调用它(这是Invoke(...)简写)和使用DynamicInvoke之间的主要区别在于性能; 我的测量值(下图)超过* 700的因子。

使用直接/ Invoke方法,参数已经通过方法签名预先validation,并且代码已经存在以直接将它们传递给方法(我会说“作为IL”,但我似乎记得运行时提供了这个直接, 没有任何IL)。 使用DynamicInvoke它需要通过reflection从数组中检查它们(即它们是否适合此调用;它们是否需要拆箱等); 这很 (如果你在紧密循环中使用它),应该尽可能避免。

例; 结果第一(我增加了之前编辑的LOOP计数,以进行合理的比较):

 Direct: 53ms Invoke: 53ms DynamicInvoke (re-use args): 37728ms DynamicInvoke (per-cal args): 39911ms 

使用代码:

 static void DoesNothing(int a, string b, float? c) { } static void Main() { Action method = DoesNothing; int a = 23; string b = "abc"; float? c = null; const int LOOP = 5000000; Stopwatch watch = Stopwatch.StartNew(); for (int i = 0; i < LOOP; i++) { method(a, b, c); } watch.Stop(); Console.WriteLine("Direct: " + watch.ElapsedMilliseconds + "ms"); watch = Stopwatch.StartNew(); for (int i = 0; i < LOOP; i++) { method.Invoke(a, b, c); } watch.Stop(); Console.WriteLine("Invoke: " + watch.ElapsedMilliseconds + "ms"); object[] args = new object[] { a, b, c }; watch = Stopwatch.StartNew(); for (int i = 0; i < LOOP; i++) { method.DynamicInvoke(args); } watch.Stop(); Console.WriteLine("DynamicInvoke (re-use args): " + watch.ElapsedMilliseconds + "ms"); watch = Stopwatch.StartNew(); for (int i = 0; i < LOOP; i++) { method.DynamicInvoke(a,b,c); } watch.Stop(); Console.WriteLine("DynamicInvoke (per-cal args): " + watch.ElapsedMilliseconds + "ms"); } 

巧合的是,我发现了另一个不同之处。

如果Invoke抛出exception,它可以被预期的exception类型捕获。 但是, DynamicInvoke会抛出TargetInvokationException 。 这是一个小型演示:

 using System; using System.Collections.Generic; namespace DynamicInvokeVsInvoke { public class StrategiesProvider { private readonly Dictionary strategies; public StrategiesProvider() { strategies = new Dictionary { {StrategyTypes.NoWay, () => { throw new NotSupportedException(); }} // more strategies... }; } public void CallStrategyWithDynamicInvoke(StrategyTypes strategyType) { strategies[strategyType].DynamicInvoke(); } public void CallStrategyWithInvoke(StrategyTypes strategyType) { strategies[strategyType].Invoke(); } } public enum StrategyTypes { NoWay = 0, ThisWay, ThatWay } } 

当第二个测试变为绿色时,第一个测试面临TargetInvokationException。

 using System; using Microsoft.VisualStudio.TestTools.UnitTesting; using SharpTestsEx; namespace DynamicInvokeVsInvoke.Tests { [TestClass] public class DynamicInvokeVsInvokeTests { [TestMethod] public void Call_strategy_with_dynamic_invoke_can_be_catched() { bool catched = false; try { new StrategiesProvider().CallStrategyWithDynamicInvoke(StrategyTypes.NoWay); } catch(NotSupportedException exc) { /* Fails because the NotSupportedException is wrapped * inside a TargetInvokationException! */ catched = true; } catched.Should().Be(true); } [TestMethod] public void Call_strategy_with_invoke_can_be_catched() { bool catched = false; try { new StrategiesProvider().CallStrategyWithInvoke(StrategyTypes.NoWay); } catch(NotSupportedException exc) { catched = true; } catched.Should().Be(true); } } } 

真的,两者之间没有function差异。 如果你在reflection器中提取实现,你会注意到DynamicInvoke只是使用相同的参数集调用DynamicInvokeImpl。 没有进行额外的validation,它是一个非虚方法,所以它的行为不可能被派生类改变。 DynamicInvokeImpl是一个虚拟方法,可以完成所有实际工作。