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

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, увеличивая его на каждую модификацию. Поэтому, если вы сделали ровно несколько кратных 2 32 изменений в списке между итерациями, это сделает эти изменения невидимыми в отношении обнаружения. (Он будет переполняться от int.MaxValue до int.MinValue и, в конце концов, вернется к его начальному значению.)

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

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