Skip to content

Instantly share code, notes, and snippets.

@ciniml
Created January 17, 2018 18:49
Show Gist options
  • Save ciniml/cbbee0043bef0e485f3fb9064a18bb91 to your computer and use it in GitHub Desktop.
Save ciniml/cbbee0043bef0e485f3fb9064a18bb91 to your computer and use it in GitHub Desktop.
C# Simple State Machine
Initial State: Initial
Initial -> Connecting
Connecting...
Connecting -> Connected
Connected
Connected -> Disconnecting
Disconnecting -> Disconnected
Connecting...
Connecting -> Connected
Connected
XXX Connected -> Initial
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace ConsoleApp5
{
class Program
{
enum ProgramState
{
Initial,
Connecting,
Connected,
Disconnecting,
Disconnected,
}
static void ValidTransition(ProgramState stateBefore, ProgramState stateAfter)
{
Console.WriteLine($"{stateBefore} -> {stateAfter}");
}
static void InvalidTransition(ProgramState stateBefore, ProgramState stateAfter)
{
Console.WriteLine($"XXX {stateBefore} -> {stateAfter}");
}
static void Main(string[] args)
{
var stateMachine = new StateMachine<ProgramState>(ProgramState.Initial);
stateMachine.Register(ProgramState.Initial, ProgramState.Connecting, ValidTransition);
stateMachine.Register(ProgramState.Connecting, ProgramState.Connected, ValidTransition);
stateMachine.Register(ProgramState.Connected, ProgramState.Disconnecting, ValidTransition);
stateMachine.Register(ProgramState.Disconnecting, ProgramState.Disconnected, ValidTransition);
stateMachine.Register(@".+", nameof(ProgramState.Connecting), (_, __) => Console.WriteLine("Connecting..."));
stateMachine.Register(@".+", nameof(ProgramState.Connected), (_, __) => Console.WriteLine("Connected"));
stateMachine.Register(@".+", nameof(ProgramState.Initial), InvalidTransition);
Console.WriteLine($"Initial State: {stateMachine.CurrentState}");
stateMachine.Transit(ProgramState.Connecting);
stateMachine.Transit(ProgramState.Connected);
stateMachine.Transit(ProgramState.Disconnecting);
stateMachine.Transit(ProgramState.Disconnected);
stateMachine.Transit(ProgramState.Connecting);
stateMachine.Transit(ProgramState.Connected);
stateMachine.Transit(ProgramState.Initial);
}
}
}
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Text.RegularExpressions;
using System.Threading.Tasks;
namespace ConsoleApp5
{
public struct TransitionAction<TState>
{
public IReadOnlyList<TState> StatesBefore { get; }
public IReadOnlyList<TState> StatesAfter { get; }
public Action<TState, TState> Action { get; }
public TransitionAction(IEnumerable<TState> statesBefore, IEnumerable<TState> statesAfter, Action<TState, TState> action)
{
this.StatesBefore = statesBefore.ToArray();
this.StatesAfter = statesAfter.ToArray();
this.Action = action;
}
}
public class StateMachine<TState>
{
private readonly List<TransitionAction<TState>> transitionActions = new List<TransitionAction<TState>>();
public TState CurrentState { get; private set; }
public void Register(TransitionAction<TState> action)
{
this.transitionActions.Add(action);
}
public void Register(TState stateBefore, TState stateAfter, Action<TState, TState> action)
{
this.Register(new[] { stateBefore }, new[] { stateAfter }, action);
}
public void Register(IEnumerable<TState> statesBefore, IEnumerable<TState> statesAfter, Action<TState, TState> action)
{
this.Register(new TransitionAction<TState>(statesBefore, statesAfter, action));
}
public void Register(string statesBeforePattern, string statesAfterPattern, Action<TState, TState> action)
{
var beforeRegex = new Regex(statesBeforePattern);
var afterRegex = new Regex(statesAfterPattern);
var statesBefore = Enum.GetNames(typeof(TState))
.Where(name => beforeRegex.IsMatch(name))
.Select(name => (TState)Enum.Parse(typeof(TState), name));
var statesAfter = Enum.GetNames(typeof(TState))
.Where(name => afterRegex.IsMatch(name))
.Select(name => (TState)Enum.Parse(typeof(TState), name));
this.Register(statesBefore, statesAfter, action);
}
public StateMachine(TState initialState, IEnumerable<TransitionAction<TState>> actions)
{
this.CurrentState = initialState;
this.transitionActions.AddRange(actions);
}
public StateMachine(TState initialState)
{
this.CurrentState = initialState;
}
public StateMachine()
{
}
public void Transit(TState newState)
{
foreach(var transitionAction in this.transitionActions)
{
if( transitionAction.StatesBefore.Contains(this.CurrentState) && transitionAction.StatesAfter.Contains(newState) )
{
transitionAction.Action(this.CurrentState, newState);
}
}
this.CurrentState = newState;
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment