Um loop foreach ostensivamente cria um objeto IEnumerator a partir da coleção que você passa e, em seguida, passa por ele. Então, um loop como este:
foreach(var entry in collection)
{
entry.doThing();
}
traduz para algo mais ou menos assim:
(elimina alguma complexidade sobre como o enumerador é descartado posteriormente)
var enumerator = collection.GetEnumerator();
while(enumerator.MoveNext()) {
var entry = enumerator.Current;
entry.doThing();
}
Se isso for compilado de forma ingênua / direta, esse objeto enumerador será alocado no heap (o que leva um pouco de tempo), mesmo que seu tipo subjacente seja uma estrutura - algo chamado boxe. Após a conclusão do loop, essa alocação se torna lixo para limpeza posterior, e seu jogo pode gaguejar por um momento, se houver lixo suficiente para o coletor executar uma varredura completa. Por fim, o MoveNextmétodo e a Currentpropriedade podem envolver mais etapas de chamada de função do que simplesmente pegar um item diretamente collection[i], se o compilador não incorporar essa ação.
Os links Hellium dos documentos do Unity acima indicam que essa forma ingênua é exatamente o que acontece no Unity antes da versão 5.5 , se iterar sobre algo diferente de uma matriz nativa.
Na versão 5.6+ (incluindo as versões de 2017), esse boxe desnecessário desapareceu e você não deve ter alocação desnecessária se estiver usando algum tipo concreto (por exemplo, int[]ou List<GameObject>ou Queue<T>).
Você ainda receberá uma alocação se o método em questão souber apenas que está trabalhando com algum tipo de IList ou outra interface - nesses casos, ele não sabe com qual enumerador específico está trabalhando, portanto, deve colocá-lo em um objeto IEnumerator primeiro .
Portanto, há uma boa chance de que este seja um conselho antigo que não é tão importante no desenvolvimento moderno do Unity. Os desenvolvedores de unidade como nós podem ser um pouco supersticiosos em relação às coisas que costumavam ser lentas / peludas em versões antigas; portanto, aceite qualquer conselho dessa forma com um pouco de sal. ;) O mecanismo evolui mais rapidamente do que nossas crenças sobre ele.
Na prática, eu nunca tive um jogo exibindo lentidão perceptível ou soluços de coleta de lixo usando loops de foreach, mas sua milhagem pode variar. Faça o perfil do seu jogo no hardware escolhido para ver se vale a pena se preocupar. Se você precisar, é bastante trivial substituir loops foreach por loops explícitos mais tarde no desenvolvimento, caso um problema se manifeste, para que eu não sacrifique a legibilidade e a facilidade de codificação no início do desenvolvimento.