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
orIEnumerator.Reset
throws anInvalidOperationException
.
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.