FrenchExDev.Net.FiniteStateMachine.Core
1.0.4
dotnet add package FrenchExDev.Net.FiniteStateMachine.Core --version 1.0.4
NuGet\Install-Package FrenchExDev.Net.FiniteStateMachine.Core -Version 1.0.4
<PackageReference Include="FrenchExDev.Net.FiniteStateMachine.Core" Version="1.0.4" />
<PackageVersion Include="FrenchExDev.Net.FiniteStateMachine.Core" Version="1.0.4" />
<PackageReference Include="FrenchExDev.Net.FiniteStateMachine.Core" />
paket add FrenchExDev.Net.FiniteStateMachine.Core --version 1.0.4
#r "nuget: FrenchExDev.Net.FiniteStateMachine.Core, 1.0.4"
#:package FrenchExDev.Net.FiniteStateMachine.Core@1.0.4
#addin nuget:?package=FrenchExDev.Net.FiniteStateMachine.Core&version=1.0.4
#tool nuget:?package=FrenchExDev.Net.FiniteStateMachine.Core&version=1.0.4
FrenchExDev.Net.FiniteStateMachine.Core
A tiny
& flexible
, enum-typed
generic
Finite State Machine
library
for .NET
, written in C#
.
This library
enables you to separate
modeling
and building
of executable stateful machines
, with support for custom actions
, conditions
, and transition-specific behaviors
.
Features
Strongly-typed FSMs
Use your own types for states, triggers, and context objects.Builder Pattern
Fluent API for defining states, transitions, and actions.Transition Actions & Conditions
Attach logic and guards to transitions.State Entry Actions
Execute logic when entering a state.Unit-tested
Includes comprehensive tests using a typical FSM scenario.
Getting Started
1. Install
dotnet add package FrenchExDev.Net.FiniteStateMachine.Core
2. Define your Class
Create a class that will represent the context of your state machine:
class Door
{
public bool Locked { get; set; }
public void History(DeviceState state, DeviceEvent trigger, DateTime timestamp){ /* ... */ }
public void ResetAvailableOperations(DeviceOperation[] operations) { /* ... */ }
}
3. Define Your Events and States
Define enums or classes for your states and triggers:
enum DoorState { Closed, Open, Locked }
enum DoorEvent { Open, Close, Lock, Unlock }
4. Build a State Machine
Use the builder to define states and transitions:
using FrenchExDev.Net.FiniteStateMachine.Core;
var builder = new FiniteStateMachineBuilder<Door, DoorState, DoorEvent>();
builder
.Transition(fromState: DoorState.Closed, toState: DoorState.Open, on: DoorEvent.Open)
.Transition(fromState: DoorState.Open, toState: DoorState.Closed, on: DoorEvent.Close)
.Transition(fromState: DoorState.Closed, toState: DoorState.Locked, on: DoorEvent.Lock, body: (door, e, fsm) =>
{
door.Locked = true;
}, condition: (door, e, fsm) => !door.Locked)
.Transition(fromState: DoorState.Locked, toState: DoorState.Closed, on: DoorEvent.Unlock, body: (door, e, fsm) =>
{
door.Locked = false;
}, condition: (door, e, fsm) => door.Locked)
.Transition(on: DoorEvent.Open, fromState: DoorState.Locked, toState: DoorState.Open, condition: (door, e, fsm) => door.HasKey)
;
var door = new Door();
var fsm = builder.Build(door, DoorState.Closed);
5. Fire triggers
fsm.Fire(DoorEvent.Open); // Door transitions to Open, returned TransitionResult.Success
fsm.Fire(DoorEvent.Close); // Door transitions to Closed, returned TransitionResult.Success
fsm.Fire(DoorEvent.Lock); // Door transitions to Locked, sets door.Locked = true, returned TransitionResult.Success
fsm.Fire(DoorEvent.Unlock); // Door transitions to Closed, sets door.Locked = false, returned TransitionResult.Success
Advanced: State Entry Actions
imagine the following scenarios:
- You have a device with Available Operations and History
- Your have its State and Event enums
using FrenchExDev.Net.FiniteStateMachine.Core;
public class Device {
public Device History(DeviceState state, DeviceEvent trigger, DateTime timestamp) { /* ... */ }
public Device ResetAvailableOperations(DeviceOperation[] operations) { /* ... */ }
}
public enum DeviceState { NotInitialized, Initing, Available, Connected }
public enum DeviceEvent { Init, Inited, Connect }
You can use the WhenFiniteStateMachineBuilder
to define actions
that run
- when
a specific event
is fired using.On(DeviceEvent trigger, Action<Device, DeviceEvent, WhenFiniteStateMachine<Device, DeviceState, DeviceEvent>> action);
- whenever
a specific state
is entered, regardless of the trigger using.When(DeviceState state, Action<Device, DeviceEvent, WhenFiniteStateMachine<Device, DeviceState, DeviceEvent>> action).);
using FrenchExDev.Net.FiniteStateMachine.Core;
var builder = new WhenFiniteStateMachineBuilder<Device, DeviceState, DeviceEvent>();
builder
.On(DeviceEvent.Open, (device, trigger, fsm) => {
device.History(DeviceState.Open, trigger, DateTime.UtcNow);
})
.When(DeviceState.Connected, (device, trigger, fsm) =>
{
device.ResetAvailableOperations(new[] { DeviceOperation.Disconnect, DeviceOperation.Upload });
});
builder
.Transition(on: DeviceEvent.Init, fromState: DeviceState.NotInitialized, toState: DeviceState.Initing, body: (device, e, fsm) =>
{
device.History(DeviceState.Initing, e, DateTime.UtcNow);
fsm.Fire(DeviceEvent.Inited);
})
.Transition(on: DeviceEvent.Inited, fromState: DeviceState.Initing, toState: DeviceState.Available, body: (device, e, fsm) =>
{
device.History(DeviceState.Available, e, DateTime.UtcNow);
})
.Transition(on: DeviceEvent.Connect, fromState: DeviceState.Available, toState: DeviceState.Connected, body: (device, e, fsm) =>
{
device.History(DeviceState.Connected, e, DateTime.UtcNow);
});
var device = new Device();
var fsm = builder.Build(device, DeviceState.NotInitialized);
fsm.Fire(DeviceEvent.Init); // Device transitions to Initing, then to Available, returned TransitionResult.Success
fsm.Fire(DeviceEvent.Connect); // Device transitions to Connected, returned TransitionResult.Success
Running Tests
Open a Terminal
in the .sln
directory and run:
dotnet test
Which should output something like:
PS C:\code\FSM.Net\FSM.Net_i1\FrenchExDev.FiniteStateMachine.Net> dotnet test
Restauration terminée (0,9s)
FrenchExDev.Net.FiniteStateMachine.Core a réussi (3,7s) → src\FrenchExDev.Net.FiniteStateMachine.Core\bin\Debug\net9.0\FrenchExDev.Net.FiniteStateMachine.Core.dll
FrenchExDev.Net.FiniteStateMachine.Core.Tests a réussi (0,9s) → test\FrenchExDev.Net.FiniteStateMachine.Core.Tests\bin\Debug\net9.0\FrenchExDev.Net.FiniteStateMachine.Core.Tests.dll
Test de FrenchExDev.Net.FiniteStateMachine.Core.Tests : a réussi (0,8 s)
Récapitulatif du test : total : 3; échec : 0; réussi : 3; ignoré : 0; durée : 0,4s
Générer a réussi dans 6,7s
Copyright (c) 2025 FrenchExDev
Stéphane ERARD
email@stephane.erard@gmail.com
Contact for licensing: stephane.erard@gmail.com
.
This project is provided for educational purposes only
.
Product | Versions Compatible and additional computed target framework versions. |
---|---|
.NET | net9.0 is compatible. net9.0-android was computed. net9.0-browser was computed. net9.0-ios was computed. net9.0-maccatalyst was computed. net9.0-macos was computed. net9.0-tvos was computed. net9.0-windows was computed. net10.0 was computed. net10.0-android was computed. net10.0-browser was computed. net10.0-ios was computed. net10.0-maccatalyst was computed. net10.0-macos was computed. net10.0-tvos was computed. net10.0-windows was computed. |
-
net9.0
- No dependencies.
NuGet packages
This package is not used by any NuGet packages.
GitHub repositories
This package is not used by any popular GitHub repositories.