Home > Mobile >  Strange `Collection was modified` exception though it never gets modified
Strange `Collection was modified` exception though it never gets modified

Time:01-23

Why do I get Collection was modified exception if I have a predefined collection and its values are accessed and updated by index only. No thread deletes or adds items to the collection. The exception happens on the line var values = new List<double> { amount.Values[0], amount.Values[1] };

System.InvalidOperationException: Collection was modified; enumeration operation may not execute.
   at System.ThrowHelper.ThrowInvalidOperationException(ExceptionResource resource)
   at System.Collections.Generic.List`1.Enumerator.MoveNextRare()
   at System.Collections.Generic.List`1.Enumerator.MoveNext()
   at System.Linq.Enumerable.<SkipIterator>d__31`1.MoveNext()
   at System.Collections.Generic.List`1.InsertRange(Int32 index, IEnumerable`1 collection)
   at MyProject.Exensions.ToFixed(Amount amount) in ...\Amount.cs:line 43 ...

CODE

private List<double> _currentAmount = new List<double>(...);
public async Task<Amount> GetAmount(bool fromCache = true)
{
   if(!fromCache)
   {
       _currentAmount = await _service.GetAmount(); 
   }
   
   return new Amount(_currentAmount).ToFixed(); 
}

// Called every 100ms by other thread
private void OnAmountChanged(AmountChangedEventArgs args)
{
    _currentAmount[args.Index] = args.Value;
    AmountChanged?.Invoke(new Amount(_currentAmount));
}

public static Amount ToFixed(this Amount amount)
{
    var values = new List<double> { amount.Values[0], amount.Values[1] };
    values.Add(amount.Values[2] * 0.1);
    values.AddRange(amount.Values.Skip(3));

    return new Amount(values);
}

public sealed class Amount
{
    public ReadOnlyCollection<double> Values { get; }

    public Amount(IList<double> values)
    {
        Values = new ReadOnlyCollection<double>(values);
    }
    
    ...
}

CodePudding user response:

You're invalidating the iterator by modifying the list. In comments, you said:

but "changes collection" only means when an item added or removed, not when a value is modified by index, doesn't it?

But no, that's not what it means. From the documentation of List<T>.GetEnumerator (emphasis mine):

An enumerator remains valid as long as the collection remains unchanged. If changes are made to the collection, such as adding, modifying, or deleting elements, the enumerator is irrecoverably invalidated and the next call to MoveNext or IEnumerator.Reset throws an InvalidOperationException.

This is easy to demonstrate in a minimal example:

using System;
using System.Collections.Generic;
using System.Linq;

var list = new List<int> { 0, 1, 2 };
foreach (var element in list)
{
    list[1] = 5;
}

That throws an InvalidOperationException exception, contrary to your expectations.

  • Related