【问题标题】:Why should I avoid using the Dispatcher?为什么我应该避免使用 Dispatcher?
【发布时间】:2011-07-10 04:53:32
【问题描述】:

我已经阅读了很多关于绑定和 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?
   }
}

【问题讨论】:

    标签: wpf silverlight data-binding


    【解决方案1】:

    我个人也不反对视图模型类中的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.Currentnull。其次,你将如何嘲笑它?你会创建一个特殊的线程来模仿 UI 线程并使用该线程的 Dispatcher 吗?所以,这种事情。

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

    更新:

    您提供的第二个示例在没有Dispatcher 的情况下也可以工作(绑定就可以了)。

    【讨论】:

    • "(顺便说一句,在 RaisePropertyChanged 的​​情况下,您不必发送任何东西 - 绑定已经为您完成了;您只需发送对集合的更改)" 如果这是真的,那么有些东西已经自 .Net 3.5 起已更改。考虑一个集合,该集合在单独的线程中加载,然后由该线程传递给 ViewModel 中的属性。我认为没有 DispatcherBackGroundWorker 是不可能的。
    • 我不明白为什么在单元测试期间会有缺点。请你再解释一下好吗?
    • @DHN - 有关单元测试问题的示例,请参阅我的更新。至于“考虑一个在单独的线程中加载然后由该线程传递给 ViewModel 中的属性的集合”,我明确提到“您只需将更改发送到集合”,所以,是的,您需要使用 @ 987654333@ 修改集合,但不修改通常的属性(即您不必在 UI 线程上引发 INotifyPropertyChanged.PropertyChanged' event on the UI thread, but you have to raise INotifyCollectionChanged.CollectionChanged`)。
    • 请看我的编辑。我认为您对这些系列的看法是正确的。
    • +1 我同意。据我回忆,我在我的 VM 基类中添加了一个 (Application.Current != null) 测试作为单元测试的解决方法。
    猜你喜欢
    • 2012-07-15
    • 1970-01-01
    • 2011-03-10
    • 2011-04-16
    • 2018-06-26
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多