Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/.net/21.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C# 为什么Dispatcher.Invoke在本例中不执行委托参数?_C#_.net_Wpf_Unit Testing - Fatal编程技术网

C# 为什么Dispatcher.Invoke在本例中不执行委托参数?

C# 为什么Dispatcher.Invoke在本例中不执行委托参数?,c#,.net,wpf,unit-testing,C#,.net,Wpf,Unit Testing,这段代码不会引发异常。同时,Dispatcher.Invoke不执行任何操作。我觉得很奇怪 我将一个助手方法提取到一个基本ViewModel中。工作线程使用此方法DoOnUIThread()来避免线程关联问题。 然而,在我的单元测试中,我发现由于上述问题,尝试测试视图模型对象会导致失败 我可以将整个行为转移到一个可插入的依赖项中,我可以在测试中替换它。e、 ViewModelBase依赖于UIThreadExecutor.Execute(Action),我在测试中使用了一个只调用该操作的伪函数。

这段代码不会引发异常。同时,Dispatcher.Invoke不执行任何操作。我觉得很奇怪

我将一个助手方法提取到一个基本ViewModel中。工作线程使用此方法DoOnUIThread()来避免线程关联问题。 然而,在我的单元测试中,我发现由于上述问题,尝试测试视图模型对象会导致失败


我可以将整个行为转移到一个可插入的依赖项中,我可以在测试中替换它。e、 ViewModelBase依赖于UIThreadExecutor.Execute(Action),我在测试中使用了一个只调用该操作的伪函数。但是,我很好奇为什么Dispatcher的行为如此..

看起来您只是将其作为参数传递给
BackgroundWorker
。尝试将此添加到
SomeWork
函数中:

    [Test]
    public void A()
    {
        var d = Dispatcher.CurrentDispatcher;

        Action action = () => Console.WriteLine("Dispatcher invoked me!");

        var worker = new BackgroundWorker();
        worker.DoWork += SomeWork;

        //worker.RunWorkerAsync( (Action) delegate { Console.WriteLine("This works!"); } );
        worker.RunWorkerAsync((Action) delegate { d.Invoke(action); } );

        System.Threading.Thread.Sleep(2500);
    }

    private void SomeWork(object sender, DoWorkEventArgs e)
    {
        (e.Argument as Action)();
    }
您可以尝试以下方法,而不是直接的
睡眠:

  public void SomeWork(object sender, DoWorkEventArgs e)
  {
     ((MulticastDelegate)e.Argument).DynamicInvoke();
  }

看起来您只是将其作为参数传递给
BackgroundWorker
。尝试将此添加到
SomeWork
函数中:

    [Test]
    public void A()
    {
        var d = Dispatcher.CurrentDispatcher;

        Action action = () => Console.WriteLine("Dispatcher invoked me!");

        var worker = new BackgroundWorker();
        worker.DoWork += SomeWork;

        //worker.RunWorkerAsync( (Action) delegate { Console.WriteLine("This works!"); } );
        worker.RunWorkerAsync((Action) delegate { d.Invoke(action); } );

        System.Threading.Thread.Sleep(2500);
    }

    private void SomeWork(object sender, DoWorkEventArgs e)
    {
        (e.Argument as Action)();
    }
您可以尝试以下方法,而不是直接的
睡眠:

  public void SomeWork(object sender, DoWorkEventArgs e)
  {
     ((MulticastDelegate)e.Argument).DynamicInvoke();
  }

当主线程空闲并重新进入分派循环时,分派器只能执行其Begin/Invoke()职责。此时,主线程处于静止状态,可以安全地执行已调度的请求。以及Windows发送给它的任何通知


在您的情况下,它不是空闲的,它被困在睡眠(2500)中。

只有当主线程空闲并重新进入调度循环时,调度程序才能执行其Begin/Invoke()任务。此时,主线程处于静止状态,可以安全地执行已调度的请求。以及Windows发送给它的任何通知


在你的情况下,它不是空闲的,它被困在睡眠中(2500)。

这是我使用的,它对我有用,YMMV:

  while (worker.IsBusy)
  {
     Dispatcher.CurrentDispatcher.Invoke(DispatcherPriority.Background,new EmptyDelegate(delegate{}));
     //Application.DoEvents();
     System.Threading.Thread.Sleep(10);
  }
注意事项:

  • 在VS 2012的.NET 4.0控制台应用程序中进行了测试——具体取决于您的单元测试运行程序YMMV

  • 在DoEvents扩展方法中,您可以注释掉一个或另一个try/catch块(即,仅使用WPF方式,或仅使用WinForms方式)——它仍然可以工作——我希望两者都可以使用,以防万一——如果你想用WinForms的方式做这件事:你需要在你的项目中添加对System.Windows.Forms的引用

  • Thread.Yield/Thread.Sleep不是必需的,并且不会为解决问题增加价值(Sleep和Yield都不会运行任何排队的调度程序事件)——但它们会减少该线程上的CPU使用量(即,笔记本电脑电池寿命更长,CPU风扇更安静,等等)如果你只是在一个永远忙碌的循环中等待,那么在windows上玩得会更好。它还将增加一些开销,因为这是本来可以用来运行排队调度程序事件的时间

  • 调用
    dispatcher.Invoke
    ,从与dispatcher相同的线程调用,似乎只是直接调用方法,即没有理由调用
    dispatcher.DoEvents()
    ——但是,从同一线程调用
    dispatcher.BeginInvoke
    ,将不会立即执行调用,它将等待您调用
    dispatcher.DoEvents()


    • 这是我使用的,它对我有用,YMMV:

        while (worker.IsBusy)
        {
           Dispatcher.CurrentDispatcher.Invoke(DispatcherPriority.Background,new EmptyDelegate(delegate{}));
           //Application.DoEvents();
           System.Threading.Thread.Sleep(10);
        }
      
      注意事项:

      • 在VS 2012的.NET 4.0控制台应用程序中进行了测试——具体取决于您的单元测试运行程序YMMV

      • 在DoEvents扩展方法中,您可以注释掉一个或另一个try/catch块(即,仅使用WPF方式,或仅使用WinForms方式)——它仍然可以工作——我希望两者都可以使用,以防万一——如果你想用WinForms的方式做这件事:你需要在你的项目中添加对System.Windows.Forms的引用

      • Thread.Yield/Thread.Sleep不是必需的,并且不会为解决问题增加价值(Sleep和Yield都不会运行任何排队的调度程序事件)——但它们会减少该线程上的CPU使用量(即,笔记本电脑电池寿命更长,CPU风扇更安静,等等)如果你只是在一个永远忙碌的循环中等待,那么在windows上玩得会更好。它还将增加一些开销,因为这是本来可以用来运行排队调度程序事件的时间

      • 调用
        dispatcher.Invoke
        ,从与dispatcher相同的线程调用,似乎只是直接调用方法,即没有理由调用
        dispatcher.DoEvents()
        ——但是,从同一线程调用
        dispatcher.BeginInvoke
        ,将不会立即执行调用,它将等待您调用
        dispatcher.DoEvents()


      那么,有没有一种方法可以启动单元测试的消息循环—Wpf EXE的Application.Run()的对应项?调用不是应该是同步/立即的吗?@Gishu-您可以循环BackgroundWorker的IsBusy属性。这不是一个理想的解决方案,但应该可行。请参阅“我的编辑”。@SwDev-如果BGW有RunWorkerCompleted事件,这将是一个保证死锁。在事件运行之前,它无法停止IsBusy,而在循环中测试IsBusy时,事件无法运行。@Gishu:为什么不使用Application.run?是的,它与UI线程同步。不,它不是即时的,因为UI线程必须是空闲的。这对于ViewModel对象的单元测试来说是一个很大的负担,它与UI无关。此外,App.Run()还会阻止测试运行程序线程。我想我必须将这种行为转移到依赖项上。那么,有没有一种方法可以启动单元测试的消息循环——Wpf EXE的Application.Run()的对应项?调用不是应该是同步/立即的吗?@Gishu-您可以循环BackgroundWorker的IsBusy属性。这不是一个理想的解决方案,但应该可行。请参阅“我的编辑”。@SwDev-如果BGW有RunWorkerCompleted事件,这将是一个保证死锁。在事件运行之前,它无法停止处于忙碌状态。当您处于循环中时,事件无法运行