我为什么要避免使用Dispatcher?

时间:2022-10-05 08:44:23

I've read many thread posts, articles etc. about binding and the thread affinity of GUI controls. There are some post in which the people don't want to use the Dispatcher.

我已经阅读了很多关于绑定和GUI控件的线程亲和性的帖子,文章等。有些帖子中人们不想使用Dispatcher。

I also have a workmate who avoids using the Dispatcher in his code. I asked him for the reason but his answer didn't satisfy me. He said, he don't like such kind of "magic" hidden in a class.

我还有一个同事避免在他的代码中使用Dispatcher。我问他原因,但他的回答并不能让我满意。他说,他不喜欢隐藏在课堂上的这种“魔法”。

Well I'm a fan of the following class.

好吧,我是下一堂课的粉丝。


public class BindingBase : INotifyPropertyChanged
{
   public event PropertyChangedEventHandler PropertyChanged;

   private Dispatcher Dispatcher
   {
#if SILVERLIGHT
      get { return Deployment.Current.Dispatcher; }
#else
      get { return Application.Current.Dispatcher; }
#endif
   }

   protected void RaisePropertyChanged<T>(Expression<Func<T>> expr)
   {
      var memberExpr = (MemberExpression)expr.Body;
      string property = memberExpr.Member.Name;

      var propertyChanged = PropertyChanged;
      if (propertyChanged == null) return;

      if (Dispatcher.CheckAccess())
         propertyChanged.Invoke(this, new PropertyChangedEventArgs(property));
      else
         Dispatcher.BeginInvoke(() => RaisePropertyChanged(expr));
   }
}

Here is the question. Are there any reasons why some people don't want to use such a class? Perhaps I have to reconsider this approach.

这是个问题。有些人不想使用这样的课程有什么理由吗?也许我必须重新考虑这种方法。

You have to admit, there is one strange thing. Dispatcher.CheckAccess() is excluded from Intellisense. Perhaps they are a bit scary due to this fact.

你不得不承认,有一件奇怪的事情。 Dispatcher.CheckAccess()被排除在Intellisense之外。由于这个事实,也许他们有点可怕。

Regards

问候

EDIT:

编辑:

Ok, another example. Consider a complex object. The collection as example was perhaps not the best idea.

好的,另一个例子。考虑一个复杂的对象。作为例子的集合可能不是最好的主意。


public class ExampleVm : BindingBase
{
   private BigFatObject _someData;
   public BigFatObject SomeData
   {
      get { return _someData; }
      set
      {
         _someData = value;
         RaisePropertyChanged(() => SomeData);
      }
   }

   public ExampleVm()
   {
      new Action(LoadSomeData).BeginInvoke(null, null); //I know - it's quick and dirty
   }

   private void LoadSomeData()
   {
      // loading some data from somewhere ...
      // result is of type BigFatObject

      SomeData = result; // This would not work without the Dispatcher, would it?
   }
}

1 个解决方案

#1


5  

I am also, personally, not against of Dispatcher in view model classes. I haven't seen any significant problems with it, yet it gives the most flexibility to your code.

我个人也不反对视图模型类中的Dispatcher。我没有看到任何重大问题,但它为您的代码提供了最大的灵活性。

But I like the idea of encapsulating the usage of Dispatcher in the infrastructure code as much as possible. Just like you did with RaisePropertyChanged method (BTW, in case of RaisePropertyChanged you don't have to dispatch anything - binding already does it for you; you only have to dispatch changes to collections).

但我喜欢尽可能地将Dispatcher的使用封装在基础架构代码中的想法。就像你使用RaisePropertyChanged方法一样(BTW,如果是RaisePropertyChanged,你不需要发送任何东西 - 绑定已经为你做了;你只需要对集合发送更改)。

The biggest and the only disadvantage that I see here is unit testing. Things can get tricky when you try to test your logic that involves usage of Dispatcher. Imagine if you had code something like this in a view model:

我在这里看到的最大和唯一的缺点是单元测试。当您尝试测试涉及使用Dispatcher的逻辑时,事情会变得棘手。想象一下,如果你在视图模型中有这样的代码:

private void UpdateMyCollection() 
{
   IList<ModelData> dataItems = DataService.GetItems();

   // Update data on UI
   Dispatcher.BeginInvoke(new Action(() => {
      foreach (ModelData dataItem in dataItems)
      {
         MyObservableCollection.Add(new DataItemViewModel(dataItem));
      }
   }));
}

This kind of code is quite typical when it comes to updating collections from a non-UI thread. Now, how would you write a unit test that tests the logic of adding items to the observable collection? First of all, you will need to mock the Dispatcher property because Application.Current is null during unit test execution. Second of all, how will you mock it? Will you create a special thread that will mimic a UI thread and use the Dispatcher of that thread? So, this kind of things.

当从非UI线程更新集合时,这种代码非常典型。现在,您将如何编写单元测试来测试向可观察集合添加项目的逻辑?首先,您需要模拟Dispatcher属性,因为在单元测试执行期间Application.Current为null。第二,你将如何嘲笑它?你会创建一个特殊的线程来模仿UI线程并使用该线程的Dispatcher吗?所以,这种事情。

The bottom line is that if you want your code to be unit-test-friendly, you need to think of the way how you will mock the Dispatcher. This is the only concern.

最重要的是,如果您希望代码对单元测试友好,则需要考虑如何模拟Dispatcher的方式。这是唯一的问题。

Update:

更新:

The second example you provided WILL work without Dispatcher (binding will do the trick).

您提供的第二个示例将在没有Dispatcher的情况下工作(绑定将起作用)。

#1


5  

I am also, personally, not against of Dispatcher in view model classes. I haven't seen any significant problems with it, yet it gives the most flexibility to your code.

我个人也不反对视图模型类中的Dispatcher。我没有看到任何重大问题,但它为您的代码提供了最大的灵活性。

But I like the idea of encapsulating the usage of Dispatcher in the infrastructure code as much as possible. Just like you did with RaisePropertyChanged method (BTW, in case of RaisePropertyChanged you don't have to dispatch anything - binding already does it for you; you only have to dispatch changes to collections).

但我喜欢尽可能地将Dispatcher的使用封装在基础架构代码中的想法。就像你使用RaisePropertyChanged方法一样(BTW,如果是RaisePropertyChanged,你不需要发送任何东西 - 绑定已经为你做了;你只需要对集合发送更改)。

The biggest and the only disadvantage that I see here is unit testing. Things can get tricky when you try to test your logic that involves usage of Dispatcher. Imagine if you had code something like this in a view model:

我在这里看到的最大和唯一的缺点是单元测试。当您尝试测试涉及使用Dispatcher的逻辑时,事情会变得棘手。想象一下,如果你在视图模型中有这样的代码:

private void UpdateMyCollection() 
{
   IList<ModelData> dataItems = DataService.GetItems();

   // Update data on UI
   Dispatcher.BeginInvoke(new Action(() => {
      foreach (ModelData dataItem in dataItems)
      {
         MyObservableCollection.Add(new DataItemViewModel(dataItem));
      }
   }));
}

This kind of code is quite typical when it comes to updating collections from a non-UI thread. Now, how would you write a unit test that tests the logic of adding items to the observable collection? First of all, you will need to mock the Dispatcher property because Application.Current is null during unit test execution. Second of all, how will you mock it? Will you create a special thread that will mimic a UI thread and use the Dispatcher of that thread? So, this kind of things.

当从非UI线程更新集合时,这种代码非常典型。现在,您将如何编写单元测试来测试向可观察集合添加项目的逻辑?首先,您需要模拟Dispatcher属性,因为在单元测试执行期间Application.Current为null。第二,你将如何嘲笑它?你会创建一个特殊的线程来模仿UI线程并使用该线程的Dispatcher吗?所以,这种事情。

The bottom line is that if you want your code to be unit-test-friendly, you need to think of the way how you will mock the Dispatcher. This is the only concern.

最重要的是,如果您希望代码对单元测试友好,则需要考虑如何模拟Dispatcher的方式。这是唯一的问题。

Update:

更新:

The second example you provided WILL work without Dispatcher (binding will do the trick).

您提供的第二个示例将在没有Dispatcher的情况下工作(绑定将起作用)。