Como isso funciona com um "rendimento" em detalhes?
-
27-09-2019 - |
Pergunta
Recebi esse método (dentro de um script de unidade C#), mas não entendo como a parte "rendimento" realmente funciona.
Sei do MSDN que a função retornará um ienumerator que eu poderia iterar, mas esse código aguarda 1,5 segundos e não é iterado porque isso significaria que os objetos criados no interior foram criados várias vezes. Alguém aqui que pode me explicar como esse código funciona?
IEnumerator DestroyShip()
{
// create new gameobject
Instantiate(ExplosionPrefab, transform.position, transform.rotation);
// make current gameobject invisible
gameObject.renderer.enabled = false;
// set new position for the current gameobject
transform.position = new Vector3(0f, transform.position.y, transform.position.z);
// wait for 1,5 seconds
yield return new WaitForSeconds(1.5f);
// make the current gameobject visible again
gameObject.renderer.enabled = true;
}
Solução
O enumerador que o compilador gera para você está sendo iterado. Uma vez.
O compilador gerará uma classe que implementa o iEnumerator, que possui uma função moveNeNext () e uma propriedade atual. A classe terá todos os membros necessários para armazenar o estado da função entre as chamadas. Os detalhes exatos podem ser considerados "mágica do compilador".
O objetivo desta classe gerado será tratado e gerenciado pelo mecanismo Unity3D. O mecanismo Unity3D ligará para o MoveNeNext () em cada coroutina ativa uma vez cada quadro (a menos que seja instruído de outra forma).
Isso permitiu que o programador Unity3D escrevesse scripts que são reproduzidos um quadro de cada vez. Uma combinação de mágica do compilador C# e a mágica do motor Unity3D resulta em scripts muito poderosos, mas fáceis de usar.
Para responder à sua pergunta: o código em sua função será executado uma vez, mas será uma pausa na instrução 'rendimento de rendimento'.
Como afirmado acima, um objeto especial que implementa o iEnumerator é criado pelo compilador C#.
Na primeira chamada para MoveNeNext (), sua função cria uma explosão e define o objeto atual para "New WaitForSeconds (1.5F)".
O mecanismo Unity3D inspeciona esse objeto, vê que é uma instância da classe especial "WaitForSeconds", então coloca o enumerador em alguma fila de espera e não solicitará o segundo elemento até que 1,5 segundos foram aprovados. Enquanto isso, muitos quadros serão renderizados e a explosão será tocada.
Após 1,5 segundos, a unidade antecederá o enumerador da fila e chamará o Movenext () novamente. A segunda parte da sua função será executada agora, mas deixará de gerar um segundo objeto. O MoveNeNext () retornará false para indicar que não conseguiu obter um novo elemento, que é o sinal para o Unity3D para jogar esse enumerador fora. O coletor de lixo recuperará a memória em algum momento.
Como dito: muito compilador e magia do Unity3D estão acontecendo. Desde que você se lembre de que sua função será suspensa até o próximo quadro em cada declaração de retorno de rendimento, você saberá o suficiente para se beneficiar dessas funções especiais.
Outras dicas
Se yield
é usado uma vez, é como se você estivesse retornando um ienumerador com um elemento, é por isso que você tem a impressão de que ele não itera.
É um uso bastante estranho da palavra -chave de rendimento, é difícil entender por que ela foi implementada, sem ver todo o contexto.
É melhor voltar IEnumerable
ao invés de IEnumerator
como é mais chamativo e fácil de usar. É ainda melhor usar IEnumerable<T>
. O retorno do rendimento é apenas açúcar sintático para implementar um bloco de iterador, o compilador gera o código relevante, pois é essencialmente a placa de caldeira padrão que pode ser facilmente gerada programaticamente. Você geralmente usa o rendimento de rendimento quando deseja fazer uma série de ações únicas em repouso como uma coleção, por exemplo:
public IEnumerable<Thing> GetThings()
{
yield return GetNextThing();
}