Adição de elementos numa lista dentro de um laço sobre a mesma

Participei da seleção para uma empresa da Bélgica que desenvolve jogos para cassinos. Na primeira fase do processo foram feitas várias pequenas questões para verificar se o o candidato conhece os conceitos da linguagem. Quatro destas questões eram sobre listas ou listas e Linq. Abaixo uma delas, sobre a adição de novos elementos numa lista dentro de um laço foreach sobre a mesma lista:

Qual será a saída ao executar este código?

class Program {
   static void Main() {
      var ints = new List<int>(3) { 1, 2 };

      foreach (int i in ints) {
           ints.Add(i + 1);
      }
      Console.WriteLine("{0}", ints[2]);
   }
}

a) A saída será 2 e a lista conterá 4 elementos.
b) A saída será 1 e a lista conterá 6 elementos.
c) A saída será 2 e a lista conterá 6 elementos.
d) Não irá compilar.
e) Haverá uma exceção do tipo “fora do intervalo” em tempo de execução.
f) Haverá uma exceção do tipo “operação inválida” em tempo de execução.

O programa é bem simples: cria uma lista com três posições e preenche as duas primeiras com 1 e 2. Depois disto, para cada elemento da lista adiciona um novo elemento, cujo valor é o elemento atual mais 1. Por fim, mostra o terceiro elemento da lista.

Ou seja:

A lista começa com os elementos 1 e 2. Na primeira iteração adiciona um novo elemento com valor 2 (1 + 1) e lista passa a ter três elementos (1, 2, 2). Na segunda interação adiciona o elemento 3 (2 + 1) e lista passa a ter 4 elementos (1, 2, 2, 3). Resultado, letra  A: saída será 2 e a lista conterá 4 elementos. Certo? Não, errado. O sistema gera uma exceção em tempo de execução.

Mas por que ocorre uma exceção?

A exceção ocorre porque o código está tentando alterar a coleção dentro de um laço que percorre a própria coleção. Quando o programa é executado ele roda normalmente até inserir o primeiro novo elemento na lista, entretanto quando o cursor volta para o laço foreach ocorre o erro:

System.InvalidOperationException: ‘Collection was modified; enumeration operation may not execute.’

Ou seja, a coleção foi modifica e a operação sobre ela não pode ser executada. Resposta correta letra F: haverá uma exceção do tipo “operação inválida” em tempo de execução.

Execute o programa passo a passo usando o debug para ver o ponto exato onde a exceção é gerada

Este comportamento faz todo sentido, pois o laço foreach está preparado para percorrer toda a lista. Se a lista é modifica, seja por adição ou exclusão de elementos, como o compilador irá controlar quando o laço deve ser finalizado?

Mais uma dúvida….

Caso fosse possível adicionar novos elementos à lista dentro do foreach, surge uma nova questão: a lista foi declarada com tamanho três e iniciada com dois elementos, seria possível adicionar mais do que um elemento a ela ou neste caso teria uma exceção do tipo out of range (fora do intervalo)?

Isto pode ser testado modificando ligeiramente o código da questão, alterando o laço foreach para um laço for:

class Program {
   static void Main() {
      var ints = new List<int>(3) { 1, 2 };

      for(int i = 1; i < 3; i++) {
           ints.Add(i + 1);
      }
      Console.WriteLine("{0}", ints[2]);
   }
}

No código acima dois novos elementos (2 e 3) são adicionados a lista, que passa a ter quatro elementos (1, 2, 2, 3). Nenhuma exceção é levantada, pois o tamanho da lista é alterado automaticamento de 3 para 4. O “3” usado no momento que a lista é instanciada em memória determina o tamanho inicial alocado e não o tamanho máximo.

Origem da questão
País: Bélgica Tipo: Conceitos Assunto: List
Ramo de negócio da empresa: Games Grau de Dificuldade: fácil