想要用實現有限狀態機的功能,看了一下網路上的解決方案以及 State Pattern,覺得都不怎麼適用,因此利用 Tuple 與 Dictionary 去實作了一個簡易又可重複使用的 State Machine:

public sealed class StateMachine
{
    #region  Fields
    Dictionary, TState> _transitions;
    #endregion  Fields

    #region  Properties
    /// 
    /// Gets the m_ transitions.
    /// 
    /// 
    /// The m_ transitions.
    /// 
    private Dictionary, TState> m_Transitions
    {
        get
        {
            return _transitions ?? (_transitions = new Dictionary, TState>());
        }
    }
    /// 
    /// Gets the state of the current.
    /// 
    /// 
    /// The state of the current.
    /// 
    public TState CurrentState { get; private set; }
    #endregion  Properties

    #region  Constructors
    /// 
    /// Initializes a new instance of the  class.
    /// 
    ///  The state.
    public StateMachine(TState state)
    {
        this.CurrentState = state;
    }
    #endregion  Constructors

    #region  Methods
    /// 
    /// Adds the translation.
    /// 
    ///  State of the base.
    ///  The command.
    ///  State of the target.
    /// 
    public StateMachine AddTranslation(TState baseState, TCommand command, TState targetState)
    {
        var key = new Tuple(baseState, command);

        if (m_Transitions.ContainsKey(key))
            throw new Exception("Duplicated translation!!");

        m_Transitions.Add(key, targetState);

        return this;
    }

    /// 
    /// Triggers the command.
    /// 
    ///  The command.
    public void Trigger(TCommand command)
    {
        var key = new Tuple(this.CurrentState, command);

        if (!m_Transitions.ContainsKey(key))
            throw new Exception("Translation not found!!");

        var state = m_Transitions[key];

        this.CurrentState = state;
    }
    #endregion  Methods
}

使用時只要宣告狀態的列舉以及用來觸發狀態轉換的命令列舉,然後帶入初始狀態去建立狀態機的物件實體。接著設定狀態機內所內含的狀態轉換,設定時需指定觸發時的狀態、用來觸發的命令、以及轉換後的狀態。最後在程式中適當的時機點插入觸發適當的命令即可,程式寫起來會像下面這樣:

public enum Command
{
    Next,
    Reset
}

public enum State
{
    State1,
    State2,
    State3
}

...
var stateMachine = new StateMachine(State.State1)
    .AddTranslation(State.State1, Command.Reset, State.State1)
    .AddTranslation(State.State2, Command.Reset, State.State1)
    .AddTranslation(State.State3, Command.Reset, State.State1)
    .AddTranslation(State.State1, Command.Next, State.State2)
    .AddTranslation(State.State2, Command.Next, State.State3);

Console.WriteLine(stateMachine.CurrentState);

stateMachine.Trigger(Command.Next);
Console.WriteLine(stateMachine.CurrentState);

stateMachine.Trigger(Command.Reset);
Console.WriteLine(stateMachine.CurrentState);
...