Как эта функция с "доходностью" работает в деталях?

Я получил этот метод (внутри Unity С# Script), но я не понимаю, как работает "выход".

Я знаю из MSDN, что функция вернет IEnumerator, который я мог бы перебирать через tt, но этот код ждет 1,5 секунды и не получает итерации, потому что это будет означать, что объекты, созданные внутри, были созданы несколько раз. Кто-нибудь здесь, кто может объяснить мне, как работает этот код?

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;
}

Ответ 1

Перечислитель, созданный для вас, переименовывается. Один раз.

Компилятор будет генерировать класс, который реализует IEnumerator, который имеет функцию MoveNext() и свойство Current. Класс будет иметь все члены, необходимые для хранения состояния функции между вызовами. Точные детали можно рассматривать как "Compiler Magic".

Объект этого сгенерированного класса будет обрабатываться и управляться Unity3d Engine. Unity3d Engine будет вызывать MoveNext() для каждой активной сопрограммы после каждого кадра (если не указано иное).

Это позволило программисту Unity3d писать сценарии, которые воспроизводятся по одному кадру за раз. Комбинация магии компилятора С# и магии Unity3d Engine приводит к очень мощным, но простым в использовании скриптам.

Чтобы ответить на ваш вопрос: код в вашей функции будет выполнен один раз, но он будет приостановлен в инструкции return return.

Как указано выше, специальный компилятор, реализующий IEnumerator, создается компилятором С#.

При первом вызове MoveNext() ваша функция создает взрыв и устанавливает текущий объект в "новый WaitForSeconds (1.5f)".

Механизм Unity3d проверяет этот объект, видит, что он является экземпляром специального класса "WaitForSeconds", поэтому помещает перечислитель в очередь ожидания и не будет запрашивать второй элемент до 1,5 секунд. Тем временем будет отображаться много кадров и будет воспроизведен взрыв.

Через 1,5 секунды Unity выберет счетчик из очереди и снова вызовет MoveNext(). Вторая часть вашей функции будет выполняться сейчас, но не сможет создать второй объект. MoveNext() вернет false, чтобы указать, что ему не удалось получить новый элемент, который является сигналом Unity3d, чтобы выбросить этот счетчик. Сборщик мусора вернет память в определенный момент времени.

Как сказано: много компилятора и магия Unity3d продолжаются. До тех пор, пока вы помните, что ваша функция будет переведена на удержание до следующего кадра в каждом операторе возврата доходности, вы будете достаточно знать, чтобы воспользоваться этими специальными функциями.

Ответ 2

Если yield используется один раз, это как если бы вы возвращали IEnumerator с одним элементом, поэтому вы получаете впечатление, что он не выполняет итерацию.

Это довольно странное использование ключевого слова yield, трудно понять, почему оно было реализовано так, не видя всего контекста.

Ответ 3

Лучше вернуть IEnumerable, а не IEnumerator, поскольку он более гибкий и простой в использовании. Еще лучше использовать IEnumerable<T>. Возврат доходности - это просто синтаксический сахар для реализации блока итератора, компилятор генерирует соответствующий код, поскольку он по существу является стандартной плитой котла, которая может быть легко сгенерирована программно. Обычно вы используете возврат доходности, когда хотите сделать серию одиночных действий, представляющих собой коллекцию, например:

public IEnumerable<Thing> GetThings()
{
    yield return GetNextThing();
}