使用"yield"关键字实现状态机

Mat*_*ren 25 c# yield state-machine fsm

使用yield关键字来实现这里所示的简单状态机是否可行.对我来说,看起来C#编译器为您完成了艰苦的工作,因为它在内部实现了状态机以使yield语句有效.

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

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

Eri*_*ert 49

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

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

(顺便说一句,我在读你的名字时做了一个双重考虑.其中一位C#的设计师也被命名为马特沃伦!)

  • 我认为将FSM视为从一系列输入到一系列状态的映射的一般想法并不是一个可怕的想法.如果这就是你如何设想FSM,那么使用迭代器块并不是一个糟糕的主意.但是如果你要这样做,那么捕获某些对象中的"规则"是有意义的,这些对象可以有效地从(状态,输入) - >状态进行映射.这样你就可以在一个对象中捕获FSM逻辑,你可以在调试器中检查它,而不是在很难看到的IL中捕获它. (3认同)
  • 是的,我发现他的博客不久前,当你找到一个同名的人时,总是很奇怪!我确实认为这可能是对迭代器块的滥用,这就是我想先检查的原因. (2认同)

Meh*_*ari 7

是的,这绝对可行且容易做到.您可以尽情使用控制流结构(for,foreach,while,... goto(使用goto特别适合这个场景;)))连同yields到建立一个.

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();
Run Code Online (Sandbox Code Playgroud)

  • 我会有点惊讶地看到现实生活中的情况,这提供了比显式状态机更干净的代码。 (2认同)