Как эта функция с «доходностью» работает в деталях? - PullRequest
13 голосов
/ 09 августа 2010

Я получил этот метод (внутри скрипта Unity C #), но я не понимаю, как на самом деле работает часть «yield».

Я знаю из MSDN, что функция вернет IEnumerator, который я мог бы перебрать, но этот код ждет 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;
}

Ответы [ 3 ]

27 голосов
/ 09 августа 2010

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

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

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

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

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

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

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

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

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

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

5 голосов
/ 09 августа 2010

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

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

1 голос
/ 09 августа 2010

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

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