Collection was modified; enumeration operation may not execute

后端 未结 16 2580
南旧
南旧 2020-11-21 06:05

I can\'t get to the bottom of this error, because when the debugger is attached, it does not seem to occur.

Collection was modified; enumeration operatio

相关标签:
16条回答
  • 2020-11-21 06:50

    The accepted answer is imprecise and incorrect in the worst case . If changes are made during ToList(), you can still end up with an error. Besides lock, which performance and thread-safety needs to be taken into consideration if you have a public member, a proper solution can be using immutable types.

    In general, an immutable type means that you can't change the state of it once created. So your code should look like:

    public class SubscriptionServer : ISubscriptionServer
    {
        private static ImmutableDictionary<Guid, Subscriber> subscribers = ImmutableDictionary<Guid, Subscriber>.Empty;
        public void SubscribeEvent(string id)
        {
            subscribers = subscribers.Add(Guid.NewGuid(), new Subscriber());
        }
        public void NotifyEvent()
        {
            foreach(var sub in subscribers.Values)
            {
                //.....This is always safe
            }
        }
        //.........
    }
    

    This can be especially useful if you have a public member. Other classes can always foreach on the immutable types without worrying about the collection being modified.

    0 讨论(0)
  • 2020-11-21 06:51

    So a different way to solve this problem would be instead of removing the elements create a new dictionary and only add the elements you didnt want to remove then replace the original dictionary with the new one. I don't think this is too much of an efficiency problem because it does not increase the number of times you iterate over the structure.

    0 讨论(0)
  • 2020-11-21 06:55

    InvalidOperationException- An InvalidOperationException has occurred. It reports a "collection was modified" in a foreach-loop

    Use break statement, Once the object is removed.

    ex:

    ArrayList list = new ArrayList(); 
    
    foreach (var item in list)
    {
        if(condition)
        {
            list.remove(item);
            break;
        }
    }
    
    0 讨论(0)
  • 2020-11-21 06:56

    Here is a specific scenario that warrants a specialized approach:

    1. The Dictionary is enumerated frequently.
    2. The Dictionary is modified infrequently.

    In this scenario creating a copy of the Dictionary (or the Dictionary.Values) before every enumeration can be quite costly. My idea about solving this problem is to reuse the same cached copy in multiple enumerations, and watch an IEnumerator of the original Dictionary for exceptions. The enumerator will be cached along with the copied data, and interrogated before starting a new enumeration. In case of an exception the cached copy will be discarded, and a new one will be created. Here is my implementation of this idea:

    using System;
    using System.Collections;
    using System.Collections.Generic;
    using System.Collections.ObjectModel;
    using System.Linq;
    
    public class EnumerableSnapshot<T> : IEnumerable<T>, IDisposable
    {
        private IEnumerable<T> _source;
        private IEnumerator<T> _enumerator;
        private ReadOnlyCollection<T> _cached;
    
        public EnumerableSnapshot(IEnumerable<T> source)
        {
            _source = source ?? throw new ArgumentNullException(nameof(source));
        }
    
        public IEnumerator<T> GetEnumerator()
        {
            if (_source == null) throw new ObjectDisposedException(this.GetType().Name);
            if (_enumerator == null)
            {
                _enumerator = _source.GetEnumerator();
                _cached = new ReadOnlyCollection<T>(_source.ToArray());
            }
            else
            {
                var modified = false;
                if (_source is ICollection collection) // C# 7 syntax
                {
                    modified = _cached.Count != collection.Count;
                }
                if (!modified)
                {
                    try
                    {
                        _enumerator.MoveNext();
                    }
                    catch (InvalidOperationException)
                    {
                        modified = true;
                    }
                }
                if (modified)
                {
                    _enumerator.Dispose();
                    _enumerator = _source.GetEnumerator();
                    _cached = new ReadOnlyCollection<T>(_source.ToArray());
                }
            }
            return _cached.GetEnumerator();
        }
    
        public void Dispose()
        {
            _enumerator?.Dispose();
            _enumerator = null;
            _cached = null;
            _source = null;
        }
    
        IEnumerator IEnumerable.GetEnumerator() => GetEnumerator();
    }
    
    public static class EnumerableSnapshotExtensions
    {
        public static EnumerableSnapshot<T> ToEnumerableSnapshot<T>(
            this IEnumerable<T> source) => new EnumerableSnapshot<T>(source);
    }
    

    Usage example:

    private static IDictionary<Guid, Subscriber> _subscribers;
    private static EnumerableSnapshot<Subscriber> _subscribersSnapshot;
    
    //...(in the constructor)
    _subscribers = new Dictionary<Guid, Subscriber>();
    _subscribersSnapshot = _subscribers.Values.ToEnumerableSnapshot();
    
    // ...(elsewere)
    foreach (var subscriber in _subscribersSnapshot)
    {
        //...
    }
    

    Unfortunately this idea cannot be used currently with the class Dictionary in .NET Core 3.0, because this class does not throw a Collection was modified exception when enumerated and the methods Remove and Clear are invoked. All other containers I checked are behaving consistently. I checked systematically these classes: List<T>, Collection<T>, ObservableCollection<T>, HashSet<T>, SortedSet<T>, Dictionary<T,V> and SortedDictionary<T,V>. Only the two aforementioned methods of the Dictionary class in .NET Core are not invalidating the enumeration.


    Update: I fixed the above problem by comparing also the lengths of the cached and the original collection. This fix assumes that the dictionary will be passed directly as an argument to the EnumerableSnapshot's constructor, and its identity will not be hidden by (for example) a projection like: dictionary.Select(e => e).ΤοEnumerableSnapshot().


    Important: The above class is not thread safe. It is intended to be used from code running exclusively in a single thread.

    0 讨论(0)
  • 2020-11-21 06:58

    I've seen many options for this but to me this one was the best.

    ListItemCollection collection = new ListItemCollection();
            foreach (ListItem item in ListBox1.Items)
            {
                if (item.Selected)
                    collection.Add(item);
            }
    

    Then simply loop through the collection.

    Be aware that a ListItemCollection can contain duplicates. By default there is nothing preventing duplicates being added to the collection. To avoid duplicates you can do this:

    ListItemCollection collection = new ListItemCollection();
                foreach (ListItem item in ListBox1.Items)
                {
                    if (item.Selected && !collection.Contains(item))
                        collection.Add(item);
                }
    
    0 讨论(0)
  • 2020-11-21 07:01

    There is one link where it elaborated very well & solution is also given. Try it if you got proper solution please post here so other can understand. Given solution is ok then like the post so other can try these solution.

    for you reference original link :- https://bensonxion.wordpress.com/2012/05/07/serializing-an-ienumerable-produces-collection-was-modified-enumeration-operation-may-not-execute/

    When we use .Net Serialization classes to serialize an object where its definition contains an Enumerable type, i.e. collection, you will be easily getting InvalidOperationException saying "Collection was modified; enumeration operation may not execute" where your coding is under multi-thread scenarios. The bottom cause is that serialization classes will iterate through collection via enumerator, as such, problem goes to trying to iterate through a collection while modifying it.

    First solution, we can simply use lock as a synchronization solution to ensure that the operation to the List object can only be executed from one thread at a time. Obviously, you will get performance penalty that if you want to serialize a collection of that object, then for each of them, the lock will be applied.

    Well, .Net 4.0 which makes dealing with multi-threading scenarios handy. for this serializing Collection field problem, I found we can just take benefit from ConcurrentQueue(Check MSDN)class, which is a thread-safe and FIFO collection and makes code lock-free.

    Using this class, in its simplicity, the stuff you need to modify for your code are replacing Collection type with it, use Enqueue to add an element to the end of ConcurrentQueue, remove those lock code. Or, if the scenario you are working on do require collection stuff like List, you will need a few more code to adapt ConcurrentQueue into your fields.

    BTW, ConcurrentQueue doesnât have a Clear method due to underlying algorithm which doesnât permit atomically clearing of the collection. so you have to do it yourself, the fastest way is to re-create a new empty ConcurrentQueue for a replacement.

    0 讨论(0)
提交回复
热议问题