WPF 集合视图错误:"集合已修改;枚举操作可能无法执行



我正在覆盖System.Windows.Data.CollectionView的行为。有一种方法应该从数据库中清除并重新填充CollectionView.SourceCollection(在我的情况下是ObservableCollection<object>(。引发的异常:

抛出的异常:mscorlib 中的"System.InvalidOperationException".dll

其他信息:集合已修改;枚举操作 可能无法执行。

它正好在这条线被击中时被抛出SourceObservableCollection.Add(item);.

(注释行描述了我解决问题的失败尝试(:

//...
public ObservableCollection<object> SourceObservableCollection { get { return (ObservableCollection<object>)SourceCollection; } }
//<Part of Attempt7>
protected override void OnCollectionChanged(NotifyCollectionChangedEventArgs args)
{
base.OnCollectionChanged(args);
isCollectionChanging = false;
}
private bool isCollectionChanging = false;
//</Part of Attempt7>
//<Part of Attempt9>
private static readonly object _lock = new object();
//</Part of Attempt9>
//<*async syntax is part of Attempt10*/>
public async void RefreshSource()
{
SourceObservableCollection.Clear();
// refreshSourceFunction retrieves data from Database
IEnumerable result = refreshSourceFunction(/*parameters*/);
////Attempt1:
foreach (object item in result)
{
SourceObservableCollection.Add(item);
}
////Attempt2:
//foreach (object item in result.OfType<object>().ToList())
//{
//    SourceObservableCollection.Add(item);
//}
////Attempt3:
//List<object> lstResult = result.OfType<object>().ToList();
//foreach (object item in lstResult)
//    SourceObservableCollection.Add(item);
////Attempt4:
//List<object> lstResult2 = result.OfType<object>().ToList();
//for (int x = 0; x < lstResult2.Count; x++)
//{
//    SourceObservableCollection.Add(lstResult2[x]);
//}
////Attempt5:
//IEnumerator enumerator = result.GetEnumerator();
//while (enumerator.MoveNext())
//{
//    SourceObservableCollection.Add(enumerator.Current);
//}
////Attempt6:
//IEnumerator enumerator2 = result.GetEnumerator();
//while (enumerator2.MoveNext())
//{
//    Dispatcher.Invoke(() =>
//    {
//        SourceObservableCollection.Add(enumerator2.Current);
//    });
//}
////Attempt7:
//foreach (object item in result)
//{
//    isCollectionChanging = true;
//    Dispatcher.Invoke(() =>
//    {
//        SourceObservableCollection.Add(item);
//    }, System.Windows.Threading.DispatcherPriority.Background);
//    while (isCollectionChanging) ;
//}
////Attempt8:
//foreach (object item in result)
//{
//    SourceObservableCollection.Add(item);
//    Refresh();
//}
////Attempt9:
//foreach (object item in result)
//{
//    lock (_lock)
//    {
//        SourceObservableCollection.Add(item);
//    }
//}
////Attempt10:
//await Dispatcher.InvokeAsync(() => SourceObservableCollection.Clear());
//IEnumerable result2 = await Task.Run(() => refreshSourceFunction(/*parameters*/));
//foreach (object item in result2)
//{
//    await Dispatcher.InvokeAsync(() => SourceObservableCollection.Add(item));
//}
}
//...

例外StackTrace只有这个:

at System.ThrowHelper.ThrowInvalidOperationException(ExceptionResource resource(

但是,调试调用堆栈为:

姆科利布.dll!System.ThrowHelper.ThrowInvalidOperationException(System.ExceptionResource 资源( 未知

姆科利布.dll!System.Collections.Generic.List.Enumerator.MoveNextRare(( 未知

姆科利布.dll!System.Collections.Generic.List.Enumerator.MoveNext(( Unknown

PresentationFramework.dll!MS.Internal.Data.IndexedEnumerable.EnsureEnumerator(( Unknown

PresentationFramework.dll!MS.Internal.Data.IndexedEnumerable.EnsureCacheCurrent(( Unknown

PresentationFramework.dll!MS.Internal.Data.IndexedEnumerable.Count.get(( Unknown

演示框架.dll!System.Windows.Data.CollectionView.Count.get(( 未知

演示框架.dll!System.Windows.Data.CollectionView.AdjustCurrencyForAdd(int 索引(未知

演示框架.dll!System.Windows.Data.CollectionView.ProcessCollectionChanged(System.Collections.Specialized.NotifyCollectionChangedEventArgs 参数( 未知

演示框架.dll!System.Windows.Data.CollectionView.OnCollectionChanged(object 寄件人 System.Collections.Specialized.NotifyCollectionChangedEventArgs 参数( 未知

系统.dll!System.Collections.ObjectModel.ObservableCollection.OnCollectionChanged(System.Collections.Specialized.NotifyCollectionChangedEventArgs e( 未知

系统.dll!System.Collections.ObjectModel.ObservableCollection.InsertItem(int 索引,System.__Canon项( 未知

姆科利布.dll!System.Collections.ObjectModel.Collection.Add(object 项( 未知

我的.dll!MyDll.MyNamespace.MyOverriddenCollectionView.RefreshSource(( 第 105 行 C#

在观察调试堆栈跟踪后,我对MS.Internal.Data.IndexedEnumerable方法产生了怀疑,尤其是在 ReferenceSource 中观察到它之后;如您所见,多线程使用并不安全:

/// <summary>
/// for a collection implementing IEnumerable this offers
/// optimistic indexer, i.e. this[int index] { get; }
/// and cached Count/IsEmpty properties and IndexOf method,
/// assuming that after an initial request to read item[N],
/// the following indices will be a sequence for index N+1, N+2 etc.
/// </summary>
/// <remarks>
/// This class is NOT safe for multi-threaded use.
/// if the source collection implements IList or ICollection, the corresponding
/// properties/methods will be used instead of the cached versions
/// </remarks>
internal class IndexedEnumerable : IEnumerable, IWeakEventListener
{
//...

但是,我仍然无法弄清楚如何解决这个问题,甚至不知道到底出了什么问题。任何帮助将不胜感激。

当前 .Net 框架版本:4.5

事实证明,问题实际上出在ObservableCollection<T>本身,因为它不是线程安全的。似乎在 UI 线程中读取它时仍在修改它,并且问题中描述的与线程相关的解决方法不起作用CollectionChanged因为无论如何都会引发事件。将类型ObservableCollection<T>替换为此处找到的线程安全版本解决了该问题。

当您循环访问集合并尝试修改它时,会发生此异常。通常,当您迭代一个集合时,它会返回一个 IEnumerable,您可以将其假定为按顺序向前移动的指针。假设您更改了集合,然后迭代器变得无效,框架抛出无效操作异常

例如(伪代码(

Foreach(var item in collection)
{
modify collection here; (Do some add , remove or clear operation)
}

在上面的代码中,肯定会抛出异常

为了处理这种场景,您必须迭代收集并希望执行一些修改操作 使用索引

例如(伪代码(

for(int i=0; i< collection.count(); i++)
{
// do anything here
}

最新更新