Created
January 17, 2018 18:49
-
-
Save ciniml/cbbee0043bef0e485f3fb9064a18bb91 to your computer and use it in GitHub Desktop.
C# Simple State Machine
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Initial State: Initial | |
Initial -> Connecting | |
Connecting... | |
Connecting -> Connected | |
Connected | |
Connected -> Disconnecting | |
Disconnecting -> Disconnected | |
Connecting... | |
Connecting -> Connected | |
Connected | |
XXX Connected -> Initial |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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); | |
} | |
} | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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