C# 使用收益率将代码分解为不同的方法
我正在实施一个运行游戏AI脚本的光纤系统,我遇到了一个小问题 我在脚本中使用yield return x表示等待x帧。我想封装计算x帧的逻辑,因为其他事件可以修改等待的帧数。我的理想解决方案如下:C# 使用收益率将代码分解为不同的方法,c#,yield-return,C#,Yield Return,我正在实施一个运行游戏AI脚本的光纤系统,我遇到了一个小问题 我在脚本中使用yield return x表示等待x帧。我想封装计算x帧的逻辑,因为其他事件可以修改等待的帧数。我的理想解决方案如下: public Boolean removeScript = false; public IEnumerable update() { while(true) { shootAtPlayer() wait(30) } } private IEn
public Boolean removeScript = false;
public IEnumerable update()
{
while(true)
{
shootAtPlayer()
wait(30)
}
}
private IEnumerable wait(int x)
{
yield return removeScript ? -1 : x;
}
wait()指示返回30,如果不再需要脚本,则返回-1。迭代器将通过删除脚本来处理-1返回。如果x>0,迭代器将递减每帧返回的值,直到0,然后再次调用update()
但是,这不起作用,因为wait()的yield返回当然不会传播到update方法。这会导致代码重复、模块性变差和代码可读性变差:
public IEnumerable update()
{
while(true)
{
shootAtPlayer()
yield return removeScript ? -1 : x;
}
}
我想知道是否有更好的方法来组织这个?或者,在这种情况下,我缺少的语言功能是否有用?在这种情况下,解决方案相对简单:使
Wait()
仅返回整数:
public IEnumerable Update()
{
while (true)
{
ShootAtPlayer();
yield return Wait(30);
}
}
private int Wait(int x)
{
return removeScript ? -1 : x;
}
在更复杂的情况下,您可以使用foreach
,尽管这会使语法更加冗长:
public IEnumerable Update()
{
while (true)
{
ShootAtPlayer();
foreach (var x in Wait(30))
yield return x;
}
}
private IEnumerable Wait(int x)
{
yield return removeScript ? -1 : x;
}
如您所见,您可以(ab)使用yield-return
来实现光纤,但是yield-return
从来就不是这个意思,所以它不会工作得那么好
为这种异步延续所做的是新的async
-wait
。这样,您的代码可以如下所示:
public async Task Update()
{
while (true)
{
ShootAtPlayer();
await Wait(30);
}
}
private async Task Wait(int x)
{
await fiber.Wait(removeScript ? -1 : x);
}
最后,我认为您使用
removeScript
的方式不是一个好主意。脚本的结束应该通过实际完成的Update()
方法来表示(枚举项没有更多的项,或者任务
完成),而不是返回一个神奇的值。这里使用yield return
的确切意义是什么?谁调用了update
,为什么它不能仅仅是一个普通的旧函数呢?调用方有一个更新循环(每帧调用一次),如果当前枚举数为0,该循环调用上述脚本上的更新。如果当前枚举数大于0,则每帧递减一次枚举数,直到0。采用这种方式实现的原因是允许脚本以这样的方式写入,然后等待60帧,然后以这种方式执行,而不是在第0帧执行此操作,然后在第60帧执行此操作。它使脚本更容易跟踪,因为它们的操作有点像单独的子例程。最好使用yield return,而不是为每个脚本生成一个线程并使用sleep e.t.c。您正在尝试使用yield创建联合例程。不幸的是,对您来说,yield是co例程的一个稍微有限的版本。正如您所发现的,您只能直接在IEnumerable函数体内部拥有yield,而不能嵌套到另一个方法中。如果考虑到编译器在幕后的魔力,这个限制是有意义的。