13

我得到了这个方法(在 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;
}
4

3 回答 3

27

编译器为您生成的 Enumerator 正在被迭代。一次。

编译器将生成一个实现 IEnumerator 的类,该类具有一个 MoveNext() 函数和一个 Current 属性。该类将具有存储调用之间函数状态所需的所有成员。确切的细节可以被认为是“编译器魔术”。

这个生成的类的对象,将由 Unity3d 引擎处理和管理。Unity3d 引擎将在每个活动协程上每帧调用一次 MoveNext()(除非另有说明)。

这使 Unity3d 程序员能够编写一次播放一帧的脚本。C# 编译器魔法和 Unity3d 引擎魔法的组合产生了非常强大但易于使用的脚本。

回答您的问题:您的函数中的代码将执行一次,但它会在“yield return”语句处暂停。

如上所述,实现 IEnumerator 的特殊对象是由 C# 编译器创建的。

在第一次调用 MoveNext() 时,您的函数会创建一个爆炸并将当前对象设置为“new WaitForSeconds(1.5f)”。

Unity3d 引擎检查这个对象,发现它是特殊类“WaitForSeconds”的一个实例,因此将枚举器放在某个等待队列中,并且在 1.5 秒过去之前不会请求第二个元素。同时,将渲染许多帧并播放爆炸。

1.5 秒后,Unity 将从队列中抓取枚举数,并再次调用 MoveNext()。您的函数的第二部分现在将执行,但无法生成第二个对象。MoveNext() 将返回 false 以指示它未能获取新元素,这是向 Unity3d 发送此枚举器的信号。垃圾收集器将在某个时间点回收内存。

如前所述:许多编译器和 Unity3d 魔法正在发生。只要您记得您的函数将在每个 yield return 语句中暂停到下一帧,您就会知道足以从这些特殊函数中受益。

于 2010-08-09T09:54:20.163 回答
4

如果yield使用一次,就好像您返回一个带有一个元素的 IEnumerator,这就是为什么您会得到它不会迭代的印象。

yield 关键字的使用相当奇怪,如果不查看整个上下文,很难理解为什么会这样实现它。

于 2010-08-09T09:22:43.530 回答
1

最好退回IEnumerable而不是IEnumerator因为它更柔软且易于使用。使用它甚至更好IEnumerable<T>。Yield return 只是实现迭代器块的语法糖,编译器生成相关代码,因为它本质上是标准样板,可以很容易地以编程方式生成。当您想让一系列单个动作看起来是一个集合时,您通常会使用 yield return,例如:

public IEnumerable<Thing> GetThings()
{
    yield return GetNextThing();
}
于 2010-08-09T09:27:03.833 回答