我提出了以下选项:

使用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(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语句的第一个选项,因为它更快,更简洁。但我不确定它是不是最好的选择。表现明智,但是当谈到可读性我不知道。这就是我问这个问题的原因。您更喜欢哪个选项?为什么?

有帮助吗?

解决方案

切换goto的优点是你在变量中有状态,而不仅仅是在指令指针中。

使用goto方法,状态机必须是控制其他所有内容的主循环,因为你不会因为失去状态而退出它。

使用switch方法,状态机是隔离的,您可以到任何想要处理外部事件的地方。当你返回状态机时,它会继续在yuu停止的地方继续。您甚至可以并排运行多个状态机,这是goto版本无法实现的。

我不确定你在第三种选择的位置,它看起来就像第一种选择,它周围有一个无用的开关。

其他提示

我更喜欢相互调用/递归函数。为了适应你的榜样:

returnvalue Start() {
    return Data();
}

returnvalue Data() {
    return Finish();
}

returnvalue Finish() {
    …
}

理论上,此可以完全内联,以便编译器输出等同于 goto 解决方案(因此速度相同)。实际上, C#编译器/ JITter可能赢得了’ t它的。但是由于解决方案的可读性更高(嗯,恕我直言),我只会在经过非常仔细的基准测试后用 goto 解决方案替换它, 确实是次要的速度,或堆栈溢出发生(不是在这个简单的解决方案,但更大的自动机遇到这个问题)。

即使这样,我也肯定坚持 goto case 解决方案。为什么?因为那时你的整个凌乱的 goto 面食被很好地封装在一个块结构( switch 块)中,你的意大利面条会破坏其余的代码,防止波伦亚。

总之:功能变体很明显,但通常容易出问题。 goto 解决方案很乱。只有 goto case 提供了一个干净,高效的解决方案。如果性能确实是最重要的(并且自动机是瓶颈),那么请选择结构化的 goto case 变体。

有第四种选择。

使用迭代器实现状态机。这是 nice short文章向您展示如何

但它有一些缺点。无法从迭代器外部操纵状态。

我也不确定它是否很快。但你总能做一个测试。

如果您想将状态机转换逻辑分解为单独的函数,则只能使用switch语句来执行此操作。

switch(m_state) {
        case State.Start:
                m_state = State.Data;
                break;
        case State.Data:                        
                m_state = ComputeNextState();
                break;
        case State.Finish:
                break;
} 

它也更具可读性,并且switch语句(与Goto相比)的开销只会在极少数情况下产生性能差异。

编辑:

您可以使用“转到案例”提高性能:

switch(m_state) {
        case State.Start:
                m_state = State.Data; // Don't forget this line!
                goto case State.Data;
        case State.Data:                        
                m_state = ComputeNextState();
                break;
        case State.Finish:
                break;
} 

但是你冒着忘记更新状态变量的风险。这可能会导致稍后的细微错误(因为你假设设置了“m_state”),所以我建议避免它。

我个人更喜欢第二个使用goto,因为第一个需要不必要的循环步骤(例如)才能进入新状态

许可以下: CC-BY-SA归因
不隶属于 StackOverflow
scroll top