DynamicInvoke和DynamicInvokeImpl的文档都说:
动态调用(后期绑定) 当前所代表的方法 委派。
我注意到DynamicInvoke和DynamicInvokeImpl采用了一个对象数组而不是一个特定的参数列表(这是我猜的后期部分)。但这是唯一的区别吗? DynamicInvoke和DynamicInvokeImpl之间有什么区别。
答案 0 :(得分:33)
直接调用它(Invoke(...)
的简称)和使用DynamicInvoke
之间的主要区别在于性能;根据我的衡量标准(下图)超过* 700的因素。
使用直接/ Invoke
方法,参数已经通过方法签名预先验证,并且代码已经存在以直接将这些参数传递给方法(我会说“作为IL”,但我似乎回想一下,运行时直接提供了这个,没有任何IL)。对于DynamicInvoke
,它需要通过反射从数组中检查它们(即它们是否都适用于此调用;它们是否需要拆箱等);这是慢(如果您在紧密循环中使用它),应尽可能避免使用。
实施例;结果首先(我增加了前一次编辑的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<int, string, float?> 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");
}
答案 1 :(得分:10)
巧合的是,我发现了另一个不同之处。
如果Invoke
抛出异常,则可以通过预期的异常类型捕获它。
但是DynamicInvoke
会引发TargetInvokationException
。这是一个小型演示:
using System;
using System.Collections.Generic;
namespace DynamicInvokeVsInvoke
{
public class StrategiesProvider
{
private readonly Dictionary<StrategyTypes, Action> strategies;
public StrategiesProvider()
{
strategies = new Dictionary<StrategyTypes, Action>
{
{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);
}
}
}
答案 2 :(得分:9)
两者之间确实没有功能差异。如果你在反射器中提取实现,你会注意到DynamicInvoke只是使用相同的参数集调用DynamicInvokeImpl。没有进行额外的验证,它是一个非虚方法,所以它的行为不可能被派生类改变。 DynamicInvokeImpl是一个虚拟方法,可以完成所有实际工作。