[英]What is the difference between calling a delegate directly, using DynamicInvoke, and using DynamicInvokeImpl?
The docs for both DynamicInvoke and DynamicInvokeImpl say: DynamicInvoke和DynamicInvokeImpl的文档都说:
Dynamically invokes (late-bound) the method represented by the current delegate.
动态调用(后期绑定)当前委托表示的方法。
I notice that DynamicInvoke and DynamicInvokeImpl take an array of objects instead of a specific list of arguments (which is the late-bound part I'm guessing). 我注意到DynamicInvoke和DynamicInvokeImpl采用了一个对象数组而不是一个特定的参数列表(这是我猜的后期绑定部分)。 But is that the only difference?
但这是唯一的区别吗? And what is the difference between DynamicInvoke and DynamicInvokeImpl.
DynamicInvoke和DynamicInvokeImpl之间有什么区别。
The main difference between calling it directly (which is short-hand for Invoke(...)
) and using DynamicInvoke
is performance; 直接调用它(这是
Invoke(...)
简写)和使用DynamicInvoke
之间的主要区别在于性能; a factor of more than *700 by my measure (below). 我的测量值(下图)超过* 700的因子。
With the direct/ Invoke
approach, the arguments are already pre-validated via the method signature, and the code already exists to pass those into the method directly (I would say "as IL", but I seem to recall that the runtime provides this directly, without any IL). 使用直接/
Invoke
方法,参数已经通过方法签名预先验证,并且代码已经存在以直接将它们传递给方法(我会说“作为IL”,但我似乎记得运行时提供了这个直接, 没有任何IL)。 With DynamicInvoke
it needs to check them from the array via reflection (ie are they all appropriate for this call; do they need unboxing, etc); 使用
DynamicInvoke
它需要通过反射从数组中检查它们(即它们是否适合此调用;它们是否需要拆箱等); this is slow (if you are using it in a tight loop), and should be avoided where possible. 这很慢 (如果你在紧密循环中使用它),应该尽可能避免。
Example; 例; results first (I increased the
LOOP
count from the previous edit, to give a sensible comparison): 结果首先(我增加了上一次编辑的
LOOP
计数,以进行合理的比较):
Direct: 53ms
Invoke: 53ms
DynamicInvoke (re-use args): 37728ms
DynamicInvoke (per-cal args): 39911ms
With code: 使用代码:
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");
}
Coincidentally I have found another difference. 巧合的是,我发现了另一个不同之处。
If Invoke
throws an exception it can be caught by the expected exception type. 如果
Invoke
抛出异常,它可以被预期的异常类型捕获。 However DynamicInvoke
throws a TargetInvokationException
. 但是,
DynamicInvoke
会抛出TargetInvokationException
。 Here is a small demo: 这是一个小型演示:
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
}
}
While the second test goes green, the first one faces a TargetInvokationException. 当第二个测试变为绿色时,第一个测试面临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);
}
}
}
Really there is no functional difference between the two. 真的,两者之间没有功能差异。 if you pull up the implementation in reflector, you'll notice that DynamicInvoke just calls DynamicInvokeImpl with the same set of arguments.
如果你在反射器中提取实现,你会注意到DynamicInvoke只是使用相同的参数集调用DynamicInvokeImpl。 No extra validation is done and it's a non-virtual method so there is no chance for it's behavior to be changed by a derived class.
没有进行额外的验证,它是一个非虚方法,所以它的行为不可能被派生类改变。 DynamicInvokeImpl is a virtual method where all of the actual work is done.
DynamicInvokeImpl是一个虚拟方法,可以完成所有实际工作。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.