繁体   English   中英

在C#中实现状态机的最佳方法(性能何时重要)?

[英]What's the best(when performance matters) way to implement a state machine in 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语句的第一个选项,因为它更快,更简洁。 但我不确定它是不是最好的选择。 表现明智,但是当谈到可读性我不知道。 这就是我问这个问题的原因。 您更喜欢哪个选项?为什么?

我更喜欢相互调用/递归函数。 要适应你的例子:

returnvalue Start() {
    return Data();
}

returnvalue Data() {
    return Finish();
}

returnvalue Finish() {
    …
}

从理论上讲,这可以完全内联,因此编译器输出等同于你的goto解决方案(因此速度相同)。 实际上, C#编译器/ JITter可能不会这样做 但是,由于该解决方案是大大更具可读性(好吧,恕我直言),我只会用替换它goto一个非常仔细的标杆证明它在速度方面的确逊色,或出现堆栈溢出(在此简单的解决方案之后,解决方案但是更大的自动机会遇到这个问题)。

即便如此,我肯定会坚持使用goto case解决方案。 为什么? 因为那么你的整个凌乱goto面食是公包封的嵌段结构(内部switch块)和您的面条不会裂伤的代码的其余部分,从而防止波伦亚。

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

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

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

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

我不确定你在第三种方案中走向何方,它看起来就像是第一种无用的开关。

如果您希望将状态机转换逻辑分解为单独的函数,则只能使用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 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM