使用 IEnumerable 检测修改

发布于 2024-12-04 09:23:25 字数 223 浏览 2 评论 0原文

我有一个问题,令我惊讶的是还没有以这种格式提出。

如果我有一个基于迭代数据源(并使用yield return语句)生成的IEnumerable,那么在通过通过生成的枚举器进行访问后,如何检测源何时发生了修改GetEnumerator 调用?

这是奇怪的部分:我不是多线程的。我认为我的问题在某个地方有缺陷,因为这应该很简单。 。 。我只想知道源何时更改并且迭代器何时过时。

太感谢了。

I have a question that I am surprised hasn't already been asked in exactly this format.

If I have an IEnumerable that is generated based on iterating through a source of data, (and using a yield return statement), how can I detect when there has been a modification to the source after an access via an Enumerator that was generated via a GetEnumerator call?

Here is the strange part: I'm not multi-threading. I think my question has a flaw in it somewhere, because this should be simple. . . I just want to know when the source has changed and the iterator is out of date.

Thank you so much.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(4

双马尾 2024-12-11 09:23:25

您需要自己处理创建枚举器才能跟踪此信息,或者至少使用 yield return; 以及您自己类型的修改跟踪。

例如,大多数框架集合类都保留“版本”号。当他们创建枚举器时,他们会保留该版本号的快照,并在 MoveNext() 期间检查它。您可以在调用 yield return XXX; 之前进行相同的检查

You would need to handle creating the enumerator yourself in order to track this information, or, at a minimum use yield return; with your own type of modification tracking in place.

Most of the framework collection classes, for example, keep a "version" number. When they make an enumerator, they keep a snapshot of that version number, and check it during MoveNext(). You could make the same check before calling yield return XXX;

俯瞰星空 2024-12-11 09:23:25

.NET BCL 中的大多数集合类都使用版本属性来跟踪更改。即:枚举器是用版本号(整数)构造的,并在每次迭代时检查版本号的原始来源是否仍然相同(当调用 movenext 时)。每次进行修改时,集合都会依次递增版本属性。这种跟踪机制简单而有效。

我见过的其他两种方法是:

让集合保存一个内部集合,其中包含对未完成枚举器的弱引用。每次对集合进行修改时,都会使每个仍然存在的枚举器无效。

或者在集合 ( INotifyCollectionChanged ) 中实现事件并简单地在枚举器中注册该事件。如果引发,则将枚举器标记为无效。此方法相对容易实现、通用,并且没有太多开销,但需要您的集合支持事件

Most collection classes in the .NET BCL uses a version attribute for change tracking. That is: the enumerator is constructed with a version number (integer) and checks the original source of the version number is still the same each iteration (when movenext is called). The collection in turn increments the version attribute each time a modification is made. This tracking mechanism is simple and effective.

2 other ways i've seen are:

Having the collection hold an internal collection containing weak references to outstanding enumerators. and each time a modification is made to the collection, it makes each enumerator which is still alive invalid.

Or implementing events in the collection ( INotifyCollectionChanged ) and simply register on that event in the enumerator. And if raised, mark the enumerator as invalid. This method is relatively easy to implement, generic and comes without to much overhead but requires your collection to support events

浅笑依然 2024-12-11 09:23:25

Microsoft 建议对 IEnumerable 集合的任何修改都应使任何现有 IEnumerator 对象无效,但该策略很少特别有用,有时可能会造成麻烦。如果集合的修改方式不会阻止 IEnumerator 返回与未进行此类修改时返回的数据相同的数据,则 IEnumerable/IEnumerator 的作者没有理由认为需要引发异常。我会进一步建议,在可能的情况下,如果枚举器能够遵守以下约束,那么应该认为它保持功能:

  1. 在整个枚举期间集合中的项目必须恰好返回一次。
  2. 枚举期间添加或删除的每个项目可以返回零次或一次,但最多返回一次。如果一个对象从集合中删除并重新添加,则它可能被视为最初位于一个项目中,但放入了一个新项目中,因此枚举可以合法地返回旧项目、新项目、两者或两者都不返回。

VisualBasic.Collection 类的行为符合上述约束;这种行为非常有用,可以枚举整个类并删除满足特定标准的项目。

当然,设计一个在枚举过程中被修改时表现得明智的集合可能并不一定比抛出异常更容易,但对于合理大小的集合,可以通过让枚举器将集合转换为列表并枚举其内容来获得此类语义。名单。如果需要,特别是不需要线程安全时,让集合保留对其枚举器返回的列表的强引用或弱引用,并在每次修改此类引用时将其作废,可能会有所帮助。另一种选择是对集合的“真实”引用保存在包装类中,并让内部类保留存在的枚举数的计数(枚举数将获得对真实集合的引用)。如果在枚举数存在时尝试修改集合,请用副本替换集合实例,然后对其进行修改(副本将以零引用计数开始)。这样的设计将避免制作列表的冗余副本,除非 IEnumerator 未经处理而被放弃的情况;即使在这种情况下,与涉及弱引用或事件的情况不同,任何对象都不会保持活动状态超过必要的时间。

Microsoft suggests any modification to an IEnumerable collection should void any existing IEnumerator objects, but that policy is seldom particularly helpful and can sometimes be a nuisance. There is no reason why the author of an IEnumerable/IEnumerator should feel a need to throw an exception if a collection is modified in a way that will not prevent the IEnumerator from returning the same data as it would have returned without such modification. I would go further and suggest that it should be considered desirable, when possible, to have an enumerator remain functional if it can obey the following constraints:

  1. Items which are in the collection throughout the duration of enumeration must be returned exactly once.
  2. Each items which is added or deleted during enumeration may be returned zero or one times, but no more than one. If an object is removed from the collection and re-added, it may be regarded as having been originally housed in one item but put into a new one, so enumeration may legitimately return the old one, the new one, both, or neither.

The VisualBasic.Collection class behaves according to the above constraints; such behavior can be very useful, making it possible to enumerate through the class and remove items meeting a particular criterion.

Of course, designing a collection to behave sensibly if it's modified during enumeration may not necessarily be easier than throwing an exception, but for collections of reasonable size such semantics may be obtained by having the enumerator convert the collection to a list and enumerate the contents of the list. If desired, and especially if thread safety is not required, it may be helpful to have the collection keep a strong or weak reference to the list returned by its enumerator, and void such reference any time it is modified. Another option would be to have a "real" reference to the collection be held in a wrapper class, and have the inner class keep a count of how many enumerators exist (enumerators would get a reference to the real collection). If an attempt is made to modify the collection while enumerators exist, replace the collection instance with a copy and then make the modifications on that (the copy would start with a reference count of zero). Such a design would avoid making redundant copies of the list except in the scenario where an IEnumerator is abandoned without being Dispose'd; even in that scenario, unlike scenarios involving WeakReferences or events, no objects would be kept alive any longer than necessary.

双马尾 2024-12-11 09:23:25

我还没有找到答案,但作为一种解决方法,我刚刚捕获了这样的异常(WPF 示例):

            while (slideShowOn)
            {

                if (this.Model.Images.Count < 1)
                {
                    break;
                }

                var _bitmapsEnumerator = this.Model.Images.GetEnumerator();

                try
                {
                    while (_bitmapsEnumerator.MoveNext())
                    {
                        this.Model.SelectedImage = _bitmapsEnumerator.Current;


                        Dispatcher.Invoke(new Action(() => { }), DispatcherPriority.ContextIdle, null);
                        Thread.Sleep(41);
                    }
                }
                catch (System.InvalidOperationException ex)
                {
// Scratch this bit: the error message isn't restricted to English
//                     if (ex.Message == "Collection was modified; enumeration operation may not execute.")
//                        {
//
//                        }
//                        else throw ex;
                }
            }

I haven't found an answer, but as a work around I have just been catching the exception like this (WPF example):

            while (slideShowOn)
            {

                if (this.Model.Images.Count < 1)
                {
                    break;
                }

                var _bitmapsEnumerator = this.Model.Images.GetEnumerator();

                try
                {
                    while (_bitmapsEnumerator.MoveNext())
                    {
                        this.Model.SelectedImage = _bitmapsEnumerator.Current;


                        Dispatcher.Invoke(new Action(() => { }), DispatcherPriority.ContextIdle, null);
                        Thread.Sleep(41);
                    }
                }
                catch (System.InvalidOperationException ex)
                {
// Scratch this bit: the error message isn't restricted to English
//                     if (ex.Message == "Collection was modified; enumeration operation may not execute.")
//                        {
//
//                        }
//                        else throw ex;
                }
            }
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文