Чтобы ответить на ваш актуальный вопрос ...
При перечислении вы получите IEnumerator, который привязан к состоянию списка, как это было, когда вы его запрашивали. Дальнейшие операции работают с перечислителем (MoveNext, Current).
При использовании цикла for вы выполняете последовательность вызовов, чтобы получить определенный элемент по индексу. Нет внешнего контекста, такого как перечислитель, который знает, что вы находитесь в цикле. Как известно всей коллекции, вы запрашиваете только один предмет. Поскольку коллекция никогда не передавала перечислитель, у нее нет возможности узнать, что причина, по которой вы запрашиваете элемент 0, затем элемент 1, затем элемент 2 и т. Д., Заключается в том, что вы просматриваете список.
Если вы гадите со списком в то же время, как ходите по нему, вы получите ошибки в любом случае. Если вы добавляете элементы, цикл for может пропустить некоторые молча, а цикл foreach сработает. Если вы удаляете элементы, цикл for может выкинуть индекс из диапазона, если вам не повезло, но, вероятно, будет работать большую часть времени.
Но я думаю, что вы понимаете все это, ваш вопрос был просто, почему два способа итерации вели себя по-разному. Ответ на это - состояние коллекции известно (коллекции), когда вы вызываете GetEnumerator в одном случае, и когда вы вызываете get_Item в другом случае.