当前位置: 首页 > 编程语言 > C#

为什么我不应该在迭代时修改一个集合分享

时间:2023-04-10 20:16:33 C#

为什么我不应该在迭代时修改一个集合例如在List类中有这样的代码:if(this.version!=this.list._version)ThrowHelper.ThrowInvalidOperationException(ExceptionResource.InvalidOperation_EnumFailedVersion);但显然这是设计迭代器类的开发人员的决定,因为我可以提供IEnumerable的一些实现,至少在修改底层集合时,不要抛出任何异常。然后我有一些疑问:为什么我不应该在迭代时修改集合?有些集合可以在迭代时修改,所以它不是全局的。在大多数情况下,编写一个即使底层集合被修改也能工作的高效迭代器是非常困难的。许多情况下的例外是迭代器编写者,他们说他们只是不想处理它。在某些情况下,不清楚当基础集合发生变化时迭代器应该做什么。有些情况是明确的,但对于其他情况,不同的人会期望不同的行为。每当您遇到这种情况时,都表明存在更深层次的问题(您不应该改变正在迭代的序列)是否有可能创建一个支持迭代时修改的集合,而没有任何其他问题?(注:第一个答案也可以回答这个问题)当然可以。考虑这个迭代器列表:publicstaticIEnumerableIterateWhileMutating(thisIListlist){for(inti=0;i如果当前索引处或之前的项目从基础列表中删除,则迭代时将跳过项目。如果在添加在当前索引或之前的项目,项目将被复制。但是如果在当前索引之后的迭代中添加/删除项目,则没有问题。我们可以尝试想象并尝试查看项目是否已被复制从列表中移除/相应地添加和调整索引,但它不能一直工作,所以我们无法处理所有情况。如果我们有类似ObservableCollection的东西,那么我们可以收到添??加/移除及其索引的通知,并相应地调整索引,从而允许迭代器处理基础集合的突变(只要它不在另一个线程中)。由于ObservableCollection的迭代器知道任何项目何时添加/删除以及它们在哪里,它可以相应地调整其位置。我不确定内置迭代器是否正确处理了突变,但这里有一个可以处理基础集合的任何突变:publicstaticIEnumerableIterateWhileMutating(thisObservableCollectionlist){inti=0;NotifyCollectionChangedEventHandlerhandler=(_,args)=>{switch(args.Action){caseNotifyCollectionChangedAction.Add:if(args.NewStartingIndex请注意,此迭代器不处理具有多个线程的情况。如果另一个线程更改集合,而另一个threadisiterate,anerrormayoccurthings(skippedorduplicateditems,orevenexceptions,suchasanindexoutofboundsexception).这允许在迭代期间发生突变,其中只有一个线程,或者只有一个线程执行代码来移动一个迭代器或改变一个集合。当C#编译器在生成Enumerator接口的实现时是否考虑这样的事情?编译器不生成接口实现;它将丢弃迭代。(插入或删除了一个元素迭代在集合中工作的位置;现在下一个元素是什么?新的停止条件是什么?)一个原因是线程安全。如果另一个线程正在添加到List,则无法保证迭代器将从List的以正确的方式支持数组,这可能会导致重新分配到新数组。值得注意的是,即使使用for循环枚举List也会表现出缺乏线程安全性。在JaredPar的博客文章中,他创建了一个ThreadSafeList类:ThiscollectionnolongerimplementsIEnumerable。IEnumerable仅在集合未在后台更改时才有效。以这种方式构建的集合无法轻松实现此保证,因此已将其删除。值得一提的是,并非所有IEnumerable的实现都不允许在枚举期间进行修改。并发集合这样做是因为它们可以提供线程安全保证。使用yield语句加载要修改的元素,如果在迭代时必须修改集合(如果它可以被索引),则在事后执行它,使用for循环并将对象与循环声明解除关联...但是您想确保在循环之前执行此操作使用lock语句来确保您是唯一一个操作该对象的人……并且您还记得自己在下一次循环中的操作……也许您可以这样做,但这可能是意外行为,超出IEnumerable和IEnumerator接口的意图。IEnumerable.GetEnumerator只要集合保持不变,枚举数就保持有效。如果对集合进行了更改(例如添加、修改或删除元素),则无法恢复枚举器,并且其行为未定义。这避免了像LinkedList这样的集合的问题。假设您有一个包含4个节点的链表,然后迭代到第二个节点。然后更改链表,其中将第二个节点移到链表的头部,将第三个节点移到链表的尾部。您的人口普查员接下来会做什么,甚至是什么意思?可能的行为将是模棱两可的,不容易猜到。当您通过其接口处理一个对象时,您不必考虑底层类是什么,以及该类及其枚举器是否允许修改。该界面表示修改会使枚举器无效,因此事情应该是这样的。以上是C#学习教程:迭代时为什么不修改集合共享的所有内容?如果对大家有用,需要进一步了解C#学习教程,希望大家多多关注。本文收集自网络,不代表立场。如涉及侵权,请点击右侧联系管理员删除。如需转载请注明出处: