Introduction

This article presents a C# implementation of the Gang of Four State Pattern using features like Generics, enumeration, and Reflection to make life easier. The reader is assumed to be familiar with UML State Charts and the Gang of Four State Pattern; this article is not intended as a tutorial on those subjects.

Motivation

I regularly use UML State Charts to design state behavior. UML State Charts are very useful in discussions with users. When it comes to implementing, I like to use the Gang of Four State Pattern. The pattern enables a clear translation of a State Chart into code. But a couple of things annoyed me with the standard implementations:

StateContext needs to be updated in several places when adding concrete states.

Intellisense/auto-complete is not really helpful when setting a property of abstract type.

Concrete states need constructors with a reference to the StateContext. This contradicts with the clear translation of State Chart into code, and is distracting for the casual reader, especially while state classes can be very small.

These issues are addressed in a neat little package called fsm4net. Let's have a look.

Example

Let's dive in. Suppose we need to implement the following State Chart. We have a light that we need to turn on and off with a toggle event. And furthermore, the light should be turned off after a timeout. This simple example uses states, transitions, triggers, actions, and timeout.

Using fsm4net, the implementation of our concrete states looks like this (actual code):

What I really like about this code is that it translates very well to the State Chart and is also understandable for non-programmers. If you like it so far, then keep reading.

Overview

We'll take a look at the big picture first and then we'll get back to the example.

This class diagram shows what you get when using fsm4net, i.e., inherit the abstract base classes and interface of the fsm4net assembly. The classes will look familiar if you've seen the State Pattern before. Let's go over them:

StatesEnum is an enumeration that identifies the concrete states in your machine and the empty or final state that is used for termination. It is very convenient while entering state change code to be able to select values from an enum using intellisense/autocomplete.

enum StatesEnum { ConcreteState1, ConcreteState2, Empty }

StateBase is the abstract base class for all your concrete states. The simplest form is:

abstractclass StateBase : State<statecontext,>
{
}

StateBase is typically expanded with virtual handlers for specific triggers, as we'll see in the example later on.

StateContext is the bookkeeper. The base constructor takes two parameters: the enum values of the first state and the final state. The simplest form is:

StateContext can be reached from every state using their inherited Context property. Typically, StateContext is expanded with properties that allow for either persisting data across states or accessing interfaces from states (Light, in our example).

IstateContext provides an interface for the thread that runs the state machine. The simplest implementation looks like:

Details

The base constructor of StateContext performs all the magic, and uses Reflection to find out which states are implemented; instantiates the concrete states and matches them to StatesEnum. Some rules for the definition of states apply:

Every concrete state needs a corresponding enum value of the exact same name.

One extra enum value needs to be defined for the exit or terminating state, and cannot have an associated class.

The enqueue method has the EventHandler signature, and can therefore directly subscribe to events. The sender object will be discarded.

StateContext contains a Timeout property of type TimeSpan that can be used for state timeouts. Timeouts are typically set in the EntryAction of a concrete state. Timeouts are reset when state is changed. When a timeout occurs, the TimeoutHandler() on the current state is called. The default action is to throw a NotImplementedException("Unhandled timeout").

Back to the example

We have already seen the concrete state implementations of our example state chart. Now, let's go over the other classes.

IStateContext is the interface for the thread that runs the state machine. All members are inherited:

Our example is very simple. Typically, TriggerHandler will check the type of the argument and possibly its properties to distribute the events to separate method calls.

Conclusion

fsm4net provides a neat way to implement UML State Charts in C# using the GoF State Pattern. There is a good separation between functionality and implementation details, and intellisense / auto-complete really helps with the coding of the states. I hope it will be useful to you too.

Ok, I liked the idea of having a generic implementation of a FSM, but...

1) I really dislike the idea of having a process that continually 'polls' the status... it feels like it should be more event driven. ('dislike' isn't much of an objection)2) Most of my projects would have dynamic states with static events, with the information saved in the database.

So, I drew up a draft of how I would do it... but I'm not sure that I'm keeping everything in mind. Can you send me an email to discuss?

Your example is for an object which behaves differently based on its state and how to cause it to follow an FSM when moving between states! Of course, you wouldn't have dynamic states in this kind of situation!

My example was for something different, but because you're using FSM, I got confused.

Its not so much that Handle() polls the status; The idea is that all actions and decisions are atomic and that Handle() serializes these. The machine accepts asynchronous events from multiple threads and orderly handles them on a first come first served base in a single thread. If you need priority levels you can easily add queues. The continuous calling of DoAction() of a state when idle is exactly what is required by the State Pattern.

The situation you describe with 'dynamic states and database driven' calls for a totally different approach then what is described in this article. I looked around for prior work before posting my solution and came across implementations that where XML driven and others that support 'add state transition' operations. I suggest you look for those. My embedded machine control projects are usually about limited sets of static states.