C# 使用收益率将代码分解为不同的方法

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

我正在实施一个运行游戏AI脚本的光纤系统,我遇到了一个小问题

我在脚本中使用yield return x表示等待x帧。我想封装计算x帧的逻辑,因为其他事件可以修改等待的帧数。我的理想解决方案如下:

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,而不能嵌套到另一个方法中。如果考虑到编译器在幕后的魔力,这个限制是有意义的。