简体   繁体   English

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

[英]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.

相关问题 不使用DynamicInvoke()的快速代理访问器? - Fast Delegate Accessors without using DynamicInvoke()? 在多播委托上使用慢 DynamicInvoke 的替代方法 - alternative for using slow DynamicInvoke on muticast delegate 使用或不使用委托有什么区别 - What's the difference between using or not using delegate Invoke和DynamicInvoke之间的区别 - Difference Between Invoke and DynamicInvoke 如何比使用DynamicInvoke更快地调用委托? - How do I invoke a Delegate faster than by using DynamicInvoke? 调用Delegate.DynamicInvoke与Func <object> () - Calling Delegate.DynamicInvoke vs Func<object>() 使用“绑定与StaticResource”和在WPF中使用“StaticResource”之间的区别是什么 - What is the difference between using “Binding with StaticResource” and using “StaticResource directly” in WPF 直接调用匿名异步Func与使用Task.Factory之间的区别? - Difference between calling calling anon async Func directly vs using Task.Factory? 调用Stream.Write和使用StreamWriter有什么区别? - What is the difference between calling Stream.Write and using a StreamWriter? 使用委托和使用Func有什么区别 <T> /行动 <T> 在方法签名中? - What is the difference between using a delegate and using Func<T>/Action<T> in a method signature?
 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM