在 C# 中实现状态机的最佳(当性能很重要时)方法是什么?
我提出了以下选项:
使用 goto 语句:
Start:
goto Data
Data:
goto Finish
Finish:
;
使用 switch 语句:
switch(m_state) {
case State.Start:
m_state = State.Data;
break;
case State.Data:
m_state = State.Finish;
break;
case State.Finish:
break;
}
一起使用 goto 和 switch:
switch(m_state) {
case State.Start:
goto case State.Data2;
case State.Data1:
goto case State.Finish;
case State.Data2:
m_state = State.Data1;
//call to a function outside the state machine
//that could possibly change the state
break;
case State.Finish:
break;
}
我更喜欢使用 goto 语句的第一个选项,因为它更快且更简洁。但我不确定这是否是最好的选择。也许在性能方面,但在可读性方面我不知道。这就是我问这个问题的原因。您更喜欢哪个选项,为什么?
I came up with the following options:
Using the goto statement:
Start:
goto Data
Data:
goto Finish
Finish:
;
using the switch statement:
switch(m_state) {
case State.Start:
m_state = State.Data;
break;
case State.Data:
m_state = State.Finish;
break;
case State.Finish:
break;
}
using goto and switch together:
switch(m_state) {
case State.Start:
goto case State.Data2;
case State.Data1:
goto case State.Finish;
case State.Data2:
m_state = State.Data1;
//call to a function outside the state machine
//that could possibly change the state
break;
case State.Finish:
break;
}
I prefer the first option using the goto statement, because it is faster and less verbose. But i'm not sure if it's the best option. Performance wise maybe, but when it comes to readability i don't know. That's why i ask this question. Which option do you prefer and why?
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(5)
我更喜欢相互调用/递归函数。调整您的示例:
理论上,这个可以完全内联,以便编译器输出相当于您的
goto
解决方案(因此速度相同)。实际上,C# 编译器 /JITter 可能不会这样做< /a>.但由于该解决方案的可读性要高得多(嗯,恕我直言),只有在经过非常仔细的基准测试证明它在方面确实较差之后,我才会将其替换为 goto 解决方案速度,或者发生堆栈溢出(不是在这个简单的解决方案中,而是更大的自动机遇到这个问题)。即便如此,我肯定会坚持
goto case
解决方案。为什么?因为这样你的整个凌乱的goto
意大利面就会被很好地封装在块结构(switch
块)中,并且你的意大利面不会破坏其余的代码,从而阻止博洛尼亚语。结论:功能变体很明确,但通常容易出现问题。
goto
解决方案很混乱。只有goto case
提供了一半干净、高效的解决方案。如果性能确实是最重要的(并且自动机是瓶颈),请选择结构化的goto case
变体。I prefer mutually calling/recursive functions. To adapt your example:
Theoretically, this can be completely inlined so that the compiler output is equivalent to your
goto
solution (hence, same speed). Realistically, the C# compiler /JITter probably won’t do it. But since the solution is vastly more readable (well, IMHO), I would only replace it with thegoto
solution after a very careful benchmark proving that it is indeed inferior in terms of speed, or that stack overflows occur (not in this simple solution but larger automata run into this problem).Even then, I would definitely stick to the
goto case
solution. Why? Because then your whole messygoto
pasta is well-encased inside a block structure (theswitch
block) and your spaghetti won’t mangle the rest of the code, preventing Bolognese.In conclusion: the functional variant is clear but in general prone to problems. The
goto
solution is messy. Onlygoto case
offers a halfway clean, efficient solution. If performance is indeed paramount (and the automaton is the bottle neck), go for the structuredgoto case
variant.与 goto 相比,switch 的优点是您可以将状态保存在变量中,而不仅仅是指令指针中。
使用 goto 方法,状态机必须是控制其他一切的主循环,因为您无法退出它,因为您会丢失状态。
使用 switch 方法,状态机是隔离的,您可以去任何您想要处理来自外部的事件。当您返回状态机时,它只会从 yuu 中断的地方继续。您甚至可以让多个状态机并行运行,这是 goto 版本不可能实现的。
我不确定你对第三种选择的去向,它看起来就像第一种选择,但周围有一个无用的开关。
The advantage with the switch over the goto is that you have the state in a variable, not just in the instruction pointer.
With the goto method the state machine has to be the main loop that controls everything else, because you can't step out of it because you would lose the state.
With the switch method the state machine is isolated, and you can go anywhere you want to handle events from the outside. When you return to the state machine, it just continues where yuu left off. You can even have more than one state machine running side by side, something that is not possible with the goto version.
I'm not sure where you are going with the third alternative, it looks just like the first alternative with a useless switch around it.
还有第四个选项。
使用迭代器来实现状态机。这是一个 不错的简短内容文章向您展示了
它是如何实现的,但它也有一些缺点。从迭代器外部操作状态是不可能的。
我也不确定它是否很快。但您始终可以进行测试。
There is a 4th option.
Use an iterator to implement a statemachine. Here is a nice short article showing you how
It has some disadvantages though. Manipulating the state from outside of the iterator is not possible.
I'm also not sure if it is very quick. But you can always do a test.
如果您想将状态机转换逻辑分解为单独的函数,则只能使用 switch 语句来完成。
它也更具可读性,并且 switch 语句(相对于 Goto)的开销只会在极少数情况下产生性能差异。
编辑:
您可以使用“goto case”来进行小的性能改进:
但是您可能会面临忘记更新状态变量的风险。这可能会导致稍后出现微妙的错误(因为您假设设置了“m_state”),所以我建议避免它。
If you ever want to break your state machine transition logic into separate functions, you can only do it using switch statements.
It is also more readable, and the overhead of the switch statement (versus Goto) will only make a performance difference in rare circumstances.
EDIT:
You can use "goto case" to make small performance improvement:
However you run the risk of forgetting to update the state variable. Which might cause subtle bugs later on (because you assumed that "m_state" was set), so I would suggest avoiding it.
就我个人而言,我更喜欢带有 goto 的第二个,因为第一个将需要不必要的循环步骤(例如)才能进入新状态
Personally i prefer second one with goto since first one will require unnecessary loop step (for example) to go to the new state