C#에서 상태 시스템을 구현하는 가장 좋은(성능이 중요한 경우) 방법은 무엇입니까?
-
06-07-2019 - |
문제
나는 다음과 같은 옵션을 생각해 냈습니다.
goto 문 사용:
Start:
goto Data
Data:
goto Finish
Finish:
;
스위치 문을 사용하여:
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 메소드를 사용하면 상태 머신이 다른 모든 것을 제어하는 메인 루프가 되어야 합니다. 왜냐하면 상태를 잃어버리기 때문에 여기서 나갈 수 없기 때문입니다.
스위치 방법을 사용하면 상태 머신이 격리되므로 외부에서 이벤트를 처리하려는 곳 어디든 이동할 수 있습니다.상태 시스템으로 돌아가면 yuu가 중단한 부분부터 계속됩니다.둘 이상의 상태 시스템을 나란히 실행할 수도 있는데, 이는 goto 버전에서는 불가능합니다.
세 번째 대안이 어디로 가고 있는지 잘 모르겠습니다. 주변에 쓸모없는 스위치가 있는 첫 번째 대안처럼 보입니다.
다른 팁
상호 호출/재귀 기능을 선호합니다. 예제를 조정하려면 :
returnvalue Start() {
return Data();
}
returnvalue Data() {
return Finish();
}
returnvalue Finish() {
…
}
이론적으로, 이것 ~할 수 있다 컴파일러 출력이 귀하의 goto
솔루션 (따라서 동일한 속도). 현실적으로, c# compiler /jitter는 아마 그렇게하지 않을 것입니다.. 그러나 솔루션은 훨씬 더 읽기 쉽기 때문에 (음, IMHO), 나는 그것을 goto
솔루션은 매우 신중한 벤치 마크 후이를 입증 한 후 ~이다 실제로 속도 측면에서 열등하거나 스택 오버플로가 발생합니다 (이 간단한 솔루션이 아니라 더 큰 오토마타 가이 문제에 실행됩니다).
그럼에도 불구하고 분명히 고집 goto case
해결책. 왜요? 그렇다면 당신의 모든 지저분하기 때문입니다 goto
파스타는 블록 구조 내부에서 잘 정리되어 있습니다 ( switch
블록) 그리고 당신의 스파게티는 코드의 나머지 부분을 맹글하지 않아 볼로냐를 방지합니다.
결론적으로: 기능 변형은 명확하지만 일반적으로 문제가 발생하기 쉽습니다. 그만큼 goto
해결책은 지저분합니다. 뿐 goto case
반쯤 깨끗하고 효율적인 솔루션을 제공합니다. 성능이 실제로 가장 중요하다면 (그리고 Automaton이 병 목인 경우) 구조화 된 것을 위해 가십시오. goto case
변종.
네 번째 옵션이 있습니다.
반복자를 사용하여 Statemachine을 구현하십시오. 여기에 있습니다 좋은 짧은 기사 방법을 보여줍니다
그래도 몇 가지 단점이 있습니다. 반복자 외부에서 상태를 조작하는 것은 불가능합니다.
나는 그것이 매우 빠른지 확실하지 않습니다. 그러나 항상 테스트를 수행 할 수 있습니다.
상태 머신 전환 로직을 별도의 기능으로 나누고 싶다면 스위치 문을 사용 하여만 수행 할 수 있습니다.
switch(m_state) {
case State.Start:
m_state = State.Data;
break;
case State.Data:
m_state = ComputeNextState();
break;
case State.Finish:
break;
}
또한 더 읽기 쉬우 며 스위치 명령문 (GOTO)의 오버 헤드는 드문 상황에서만 성능 차이를 만듭니다.
편집하다:
"goto case"를 사용하여 성능을 약간 향상시킬 수 있습니다.
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를 가진 두 번째 선호를 선호합니다. 이후로 새로운 상태로 이동하려면 불필요한 루프 단계 (예를 들어)가 필요합니다.