使用“yield”关键字实现状态机

时间:2009-07-28 15:19:01

标签: c# yield state-machine fsm

使用yield关键字来实现简单的状态机as shown here是否可行。对我来说,看起来C#编译器已经为你完成了艰苦的工作,因为它在内部实现了一个状态机来使yield语句有效。

你可以在编译器已经完成的工作之上捎带并让它为你实现大部分状态机吗?

有没有人这样做,技术上是否可行?

4 个答案:

答案 0 :(得分:47)

这是可行的,但这是一个坏主意。创建迭代器块是为了帮助您为集合编写自定义迭代器,而不是为了解决实现状态机的通用问题。

如果要编写状态机,只需编写状态机即可。这并不难。如果要编写大量状态机,请编写一个有用的辅助方法库,使您可以干净地表示状态机,然后使用您的库。但是,不要滥用意图完全不同的语言结构,而恰好使用状态机作为实现细节。这使得您的状态机代码难以阅读,理解,调试,维护和扩展。

(顺便说一句,我在读你的名字时做了双重考虑。其中一位C#设计师也被命名为Matt Warren!)

答案 1 :(得分:7)

是的,这绝对可行且容易做到。您可以享受使用控制流构造(forforeachwhile,... goto(使用goto特别适合这种情况;)))沿着用yield来构建一个。

IEnumerator<State> StateMachine
             (Func<int> currentInput /* gets current input from IO port */, 
              Func<int> currentOutput) {
    for (;;)  {
       if ((currentInput() & 1) == 0) 
           yield return new State("Ready"); 
       else {
           if (...) {
               yield return new State("Expecting more data");
               SendOutput(currentOutput());
               while ((currentInput() & 2) != 0) // while device busy
                    yield return new State("Busy");
           else if (...) { ... } 
       }
    }
}

// consumer:
int data;
var fsm = StateMachine(ReadFromIOPort, () => data);
// ...
while (fsm.Current != "Expecting more data")
    fsm.MoveNext();
data = 100;
fsm.MoveNext();

答案 2 :(得分:4)

虽然这不是传统意义上的状态机,但有关Iterator-based Micro Threading的文章会创造性地用于基于状态的行为。

IEnumerable Patrol ()
{
    while (alive){
        if (CanSeeTarget ()) {
            yield return Attack ();
        } else if (InReloadStation){
            Signal signal = AnimateReload ();
            yield return signal;
        } else {
            MoveTowardsNextWayPoint ();
            yield return TimeSpan.FromSeconds (1);
        };
    }
    yield break;
}

答案 3 :(得分:3)

迭代器块确实实现了状态机,但是棘手的一点是获得下一个输入。你怎么知道接下来要去哪里?我想你可能会有一些共享的“当前转换”变量,但这有些蠢。

如果您不需要任何输入(例如您的状态机只是在状态之间循环)那么这很容易,但这不是有趣的类型:)

你能描述一下你感兴趣的状态机吗?