Last active
July 5, 2024 09:10
-
-
Save W4RH4WK/5dea8f55532e0526da8b6e60c566c259 to your computer and use it in GitHub Desktop.
C# Generic Finite 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
using System; | |
using System.Collections.Generic; | |
using System.Reflection; | |
namespace GenericFSM { | |
public class FSM<T> where T : struct, IConvertible { | |
public T State { get; private set; } | |
private const BindingFlags FLAGS = BindingFlags.NonPublic | BindingFlags.Instance; | |
private IDictionary<T, MethodInfo> states = new Dictionary<T, MethodInfo>(); | |
private IDictionary<T, MethodInfo> transitions = new Dictionary<T, MethodInfo>(); | |
public FSM(T init) { | |
if(!typeof(T).IsEnum) { | |
throw new ArgumentException("T must be an enumeration"); | |
} | |
// Cache state and transition functions | |
foreach(T value in typeof(T).GetEnumValues()) { | |
var s = GetType().GetMethod(value.ToString() + "State", FLAGS); | |
if(s != null) { | |
states.Add(value, s); | |
} | |
var t = GetType().GetMethod(value.ToString() + "Transition", FLAGS); | |
if(t != null) { | |
transitions.Add(value, t); | |
} | |
} | |
State = init; | |
} | |
public void Transition(T next) { | |
MethodInfo method; | |
if(transitions.TryGetValue(next, out method)) { | |
method.Invoke(this, new object[] { State }); | |
} | |
State = next; | |
} | |
public void StateDo() { | |
MethodInfo method; | |
if(states.TryGetValue(State, out method)) { | |
method.Invoke(this, null); | |
} | |
} | |
} | |
} |
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; | |
namespace GenericFSM { | |
enum MyStates { Init, Foo, Bar, Empty, Finish }; | |
class MyFSM : FSM<MyStates> { | |
public MyFSM() : base(MyStates.Init) { } | |
void InitTransition(MyStates prev) { | |
Console.Out.WriteLine(prev.ToString() + " -> Init"); | |
} | |
void InitState() { | |
Console.Out.WriteLine("Init State"); | |
Transition(MyStates.Foo); | |
} | |
void FooTransition(MyStates prev) { | |
Console.Out.WriteLine(prev.ToString() + " -> Foo"); | |
} | |
void FooState() { | |
Console.Out.WriteLine("Foo State"); | |
Transition(MyStates.Bar); | |
} | |
// Simply omit empty transitions | |
void BarState() { | |
Console.Out.WriteLine("Bar State"); | |
Transition(MyStates.Finish); | |
} | |
// Simply omit empty states | |
void FinishTransition(MyStates prev) { | |
Console.Out.WriteLine(prev.ToString() + " -> Finish"); | |
Transition(MyStates.Bar); | |
} | |
void FinishState() { | |
Console.Out.WriteLine("Finish State"); | |
Environment.Exit(0); | |
} | |
} | |
class Program { | |
static void Main(string[] args) { | |
MyFSM fsm = new MyFSM(); | |
// Trigger first transition manually (if needed) | |
fsm.Transition(MyStates.Init); | |
// Have Fun | |
while(true) { | |
fsm.StateDo(); | |
} | |
} | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
It's so simple it's beautiful