DynamicInvoke和DynamicInvokeImpl的文档都说:
动态调用(后期绑定)当前委托表示的方法.
我注意到DynamicInvoke和DynamicInvokeImpl采用了一个对象数组而不是一个特定的参数列表(这是我猜的后期绑定部分).但这是唯一的区别吗?DynamicInvoke和DynamicInvokeImpl之间有什么区别.
Mar*_*ell 33
直接调用(简称Invoke(...))和使用它的主要区别在于DynamicInvoke性能; 我的测量值(下图)超过*700的因子.
使用direct/Invokeapproach,参数已经通过方法签名预先验证,并且代码已经存在以直接将它们传递给方法(我会说"作为IL",但我似乎记得运行时直接提供了这个,没有任何IL).有了DynamicInvoke它需要通过反射从阵列检查他们(即是它们都适用于这个呼叫;他们需要拆箱等); 这很慢(如果你在紧密循环中使用它),应该尽可能避免.
例; 结果第一(我LOOP从上一次编辑增加了计数,给出了明智的比较):
Direct: 53ms
Invoke: 53ms
DynamicInvoke (re-use args): 37728ms
DynamicInvoke (per-cal args): 39911ms
Run Code Online (Sandbox Code Playgroud)
使用代码:
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");
}
Run Code Online (Sandbox Code Playgroud)
小智 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
}
}
Run Code Online (Sandbox Code Playgroud)
当第二个测试变为绿色时,第一个测试面临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);
}
}
}
Run Code Online (Sandbox Code Playgroud)
真的,两者之间没有功能差异.如果你在反射器中提取实现,你会注意到DynamicInvoke只是使用相同的参数集调用DynamicInvokeImpl.没有进行额外的验证,它是一个非虚方法,所以它的行为不可能被派生类改变.DynamicInvokeImpl是一个虚拟方法,可以完成所有实际工作.