2

我最近在使用 datacontext changed 事件时偶然发现了 Silverlight 中的一个问题。

如果您订阅了更改的事件,然后立即取消订阅,它将引发异常,

DataContextChanged += MainPage_DataContextChanged;
void MainPage_DataContextChanged(object sender, DependencyPropertyChangedEventArgs e)
{
  var vm = e.NewValue as VM;
  if(vm != null)
  {
     DataContextChange-= MainPage_DataContextChanged;//throws invalidoperationexception for collection modified
  }
}

为了解决这个问题,我只是稍后取消订阅该事件,在这种情况下,要求是尽早取消订阅,这样才能正常工作。

DataContextChanged += MainPage_DataContextChanged;
void MainPage_DataContextChanged(object sender, DependencyPropertyChangedEventArgs e)
{
  var vm = e.NewValue as VM;
  if(vm != null)
  {
      //forces item onto the dispatcher queue so anything needing to happen with 'collections' happens first
      Dispatcher.BeginInvoke(()=>
        {
     DataContextChange-= MainPage_DataContextChanged;//throws invalidoperationexception for collection modified
         });
  }
}

我猜这些集合是可视化树中所有不同控件的子元素,我猜他们的更新可能发生在调度程序队列上,所以我的问题是:

为什么在触发后取消订阅的事件会影响将在此之后修改或更新的集合?

编辑:经过一番思考,这与事件处理程序调用列表在完成之前被修改有什么关系吗?

4

1 回答 1

3

您对正在修改的调用列表的怀疑是正确的。

根据 dotPeek 的反编译,这是触发 DataContextChanged 事件的代码:

private void RaisePublicDataContextChanged()
{
  if (this._dataContextChangedInfo == null)
    return;
  object oldValue = this._dataContextChangedInfo.OldValue;
  object dataContext = this.DataContext;
  if (oldValue == dataContext)
    return;
  this._dataContextChangedInfo.OldValue = dataContext;
  List<DependencyPropertyChangedEventHandler>.Enumerator enumerator = this._dataContextChangedInfo.ChangedHandlers.GetEnumerator();
  try
  {
    // ISSUE: explicit reference operation
    while (((List<DependencyPropertyChangedEventHandler>.Enumerator) @enumerator).MoveNext())
    {
      // ISSUE: explicit reference operation
      ((List<DependencyPropertyChangedEventHandler>.Enumerator) @enumerator).get_Current()((object) this, new DependencyPropertyChangedEventArgs(FrameworkElement.DataContextProperty, oldValue, dataContext));
    }
  }
  finally
  {
    enumerator.Dispose();
  }
}

如您所见,代码使用枚举器遍历处理程序集合。因此,当您在调用处理程序期间取消订阅事件时,您会使枚举数无效,从而导致您看到的异常。

于 2012-09-26T16:58:08.570 回答