Почему этот код выдает "Коллекция была изменена", но когда я что-то повторяю перед ней, это не так?

var ints = new List< int >( new[ ] {
    1,
    2,
    3,
    4,
    5
} );
var first = true;
foreach( var v in ints ) {
    if ( first ) {
        for ( long i = 0 ; i < int.MaxValue ; ++i ) { //<-- The thing I iterate
            ints.Add( 1 );
            ints.RemoveAt( ints.Count - 1 );
        }
        ints.Add( 6 );
        ints.Add( 7 );
    }
    Console.WriteLine( v );
    first = false;
}

Если вы закомментируете внутренний for цикл, это бросает, это очевидно, потому что мы сделали изменения в коллекции.

Теперь, если вы раскомментируете это, почему этот цикл позволяет нам добавлять эти два элемента? Требуется некоторое время, чтобы запустить его как полминуты (на процессоре Pentium), но он не выдает, и самое забавное, что он выдает:

Образ

Это было немного ожидаемым, но это указывает на то, что мы можем измениться, и это фактически меняет коллекцию. Есть идеи, почему такое поведение происходит?

1 ответ

Решение

Проблема в том, что способ, которым List<T> обнаруживает изменения, сохраняя поле версии, типа intувеличивая его на каждую модификацию. Поэтому, если вы сделали ровно несколько кратных из32 модификаций в списке между итерациями, он сделает эти модификации невидимыми в отношении обнаружения. (Это будет переполнено из int.MaxValue в int.MinValue и в конечном итоге вернуться к своему первоначальному значению.)

Если вы в значительной степени измените свой код - добавьте 1 или 3 значения вместо 2 или уменьшите число итераций вашего внутреннего цикла на 1, тогда это приведет к ожидаемому исключению.

(Это деталь реализации, а не указанное поведение - и это деталь реализации, которая может быть замечена как ошибка в очень редком случае. Однако было бы очень необычно видеть, что это вызывает проблему в реальной программе.)

Другие вопросы по тегам