为什么应该避免使用Dispatcher?

7

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

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

好吧,我是以下这个类的粉丝。


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));
   }
}

这里有一个问题。是否有任何原因,使一些人不想使用这样的类?也许我需要重新考虑这种方法。
你必须承认,有一件奇怪的事情。Dispatcher.CheckAccess()在Intellisense中被排除了。也许由于这个事实它们有点可怕。
问候
编辑:
好的,再举个例子。考虑一个复杂的对象。以集合为例可能不是最好的主意。

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个回答

5

个人而言,我并不反对在视图模型类中使用Dispatcher。我还没有遇到什么重大问题,但它确实为您的代码提供了最大的灵活性。

但我喜欢尽可能地将Dispatcher的使用封装在基础设施代码中。就像你用RaisePropertyChanged方法做的一样(顺便说一句,在RaisePropertyChanged的情况下,您不必分派任何东西 - 绑定已经为您完成了;您只需向集合分派更改即可)。

我看到的最大的唯一缺点就是单元测试。当您尝试测试涉及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));
      }
   }));
}

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

归根结底,如果您希望您的代码适用于单元测试,则需要考虑如何模拟 Dispatcher 的方式。这是唯一的问题。

更新:

您提供的第二个示例将无需 Dispatcher 即可正常工作(绑定会起到作用)。


(顺便说一句,在 RaisePropertyChanged 的情况下,您不必调度任何内容 - 绑定已经为您完成了;您只需要将更改调度到集合即可)如果这是真的,那么自 .Net 3.5 以来就发生了一些变化。考虑一个在单独的线程中加载并由该线程传递到 ViewModel 中的属性的集合。我认为这是不可能的,除非使用 Dispatcher 或 BackGroundWorker。 - DHN
我不明白为什么在单元测试期间会有缺点。你能再解释一下吗? - DHN
@DHN - 请看我的更新,其中有一个单元测试问题的示例。至于“考虑一个在单独线程中加载并由该线程传递到ViewModel属性的集合”,我明确提到“您只需要调度对集合的更改”,因此,是的,您需要使用Dispatcher来修改集合,但不是普通属性(即您不必在UI线程上引发INotifyPropertyChanged.PropertyChanged'事件,但您必须在UI线程上引发INotifyCollectionChanged.CollectionChanged)。 - Pavlo Glazkov
请看我的编辑。我认为你在集合方面是正确的。 - DHN
感谢讨论。不幸的是,我没有时间测试您关于属性中 Dispatcher 的说法。我会更新...很遗憾无法给予您应得的投票。 - DHN
显示剩余2条评论

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接