2004-03-17
| Table of Contents: |
| Rate This Article: | Add This Article To: |
( Page 1 of 3 )
Maybe you haven't worked with state machines since your college computer science courses. Jon Shemitz offers a reason to dust off the technique with .NET: object-oriented state machines can be easier to read and debug than their enum and
State machines are old technology, but they have many uses even for modern programmers. Probably the most common reason to build a state machine on .NET is emulating coroutines in recursive IEnumerator, such as one that must return a FileSystemInfo for every file and directory under a starting directory. Since this only takes three states, that's exactly what the sample code for this article (
The basic idea is that you execute a state machine iteratively, when you need to extract the next value from an input stream, or when a new input comes in. The Current state controls which code is executed on each iteration; the Current state's handler may or may not change the Current state. If the Current state handler doesn't change the Current state, the same handler is invoked each time the state machine is iterated, until something changes.
The canonical way to implement a state machine is via an enum containing the name of every state, and a giant switch statement containing the handler for each enum. This compiles to a nice efficient jump table, but it's hard to maintain. When you add a new state, you have to change both the enum and the switch statement; plus, tracing execution means lots of jumping around the switch statement.
After writing an IEnumerator as an enum and switch state machine, I got to wondering: if an object is just a state packet, could you swap state by changing the CurrentState object, instead of the CurrentEnum value? Each state's handler would stand by itself as a standard method, which is easier to read than a long switch statement. Adding a new state just means adding a new class, which is easier than editing both the enum and the switch. And when a handler changes the CurrentState, you can use F12 to jump to the implementation of the next state, which is easier than having to find it by hand in a long switch statement.
The interface between IEnumerator and a state machine is pretty obvious: Reset() sets CurrentState to an initial state. MoveNext() iterates the state machine, either setting an internal CurrentValue field and returning true, or returning false to indicate that there are no (more) items. And the Current property exposes the internal CurrentValue field.
Each state handler needs to be able to change the enumerator's private CurrentState field. That is, we need a circular reference where the state machine refers to the current state object, and the state handler has a reference back to the state machine.
![]() |
|


