ZeroAlloc.StateMachine
Source-generated, zero-allocation finite state machines for .NET.
Add [StateMachine] and [Transition<TState, TTrigger>] to a partial class or struct. The generator emits a TryFire(TTrigger) method as a switch expression over (TState, TTrigger) tuples — no dictionary, no delegate dispatch, no heap allocation on the transition path.
Quick Example
[StateMachine(InitialState = nameof(State.Idle))]
[Transition<State, Trigger>(From = State.Idle, On = Trigger.Submit, To = State.Pending)]
[Transition<State, Trigger>(From = State.Pending, On = Trigger.Pay, To = State.Done)]
[Terminal<State>(State = State.Done)]
public partial class OrderMachine { }
var machine = new OrderMachine();
machine.TryFire(Trigger.Submit); // true — Idle → Pending
machine.Current; // Pending
Contents
| Page | Description |
|---|---|
| Getting Started | Install and define your first machine |
| Attributes | [StateMachine], [Transition], [Terminal] reference |
| Source Generator | What the generator emits — input/output examples |
| Testing | Unit-test state machines without mocking |
| AOT & Trimming | Native AOT compatibility |
| Performance | Benchmark results and allocation profile |
Core Concepts
| Page | Description |
|---|---|
| States and Triggers | Enums as states and triggers, naming conventions |
| Transitions | Directed edges, TryFire, ordering, entry/exit contract |
| Concurrent Mode | CAS loop, Volatile.Read, hook ordering, guard restrictions |
| Composite States | Hierarchical sub-FSMs, dispatch order, shallow history |
| Timeout Transitions | AfterMs edges, lazy timers, race-safe CAS, IDisposable |
| Concurrent Parts | Multiple independent FSMs in one class via [StateMachineGroup] |
| Persistence | Restore(state) rebuilds a persisted state: validated, no hooks, timers armed |
| Diagram Export | Diagram = true emits a Mermaid stateDiagram-v2 const string |
Guides
| Page | Description |
|---|---|
| Guards | Block transitions at runtime with When = true |
| Entry and Exit Actions | React to state crossings with partial void hooks |
| Terminal States | Intentional sinks and the [Terminal] attribute |
| Circuit Breaker Example | Real-world use: thread-safe circuit breaker |
Diagnostics
| ID | Severity | Description |
|---|---|---|
| ZSM0001 | Warning | Unreachable state |
| ZSM0002 | Warning | Unintentional sink state |
| ZSM0003 | Warning | Single-use trigger (possible typo) |
| ZSM0004 | Error | Concurrent mode on a struct |
| ZSM0005 | Error | Composite state on a concurrent machine |
| ZSM0006 | Error | Sub-machine is not a [StateMachine] |
| ZSM0007 | Error | Sub-machine trigger type mismatch |
| ZSM0008 | Error | Composite state value not in TState |
| ZSM0009 | Error | Duplicate composite state |
| ZSM0010 | Error | History state without composite |
| ZSM0011 | Error | Composite state cannot also be terminal |
| ZSM0012 | Error | Timeout transition requires concurrent mode |
| ZSM0013 | Error | AfterMs must be positive |
| ZSM0014 | Error | [StateMachine] and [StateMachineGroup] are mutually exclusive |
| ZSM0015 | Error | Duplicate state machine part name |
| ZSM0016 | Error | Transition Part mismatch |
| ZSM0017 | Error | [StateMachineGroup] declared with no parts |
| ZSM0018 | Error | Composite state inside a group |
| ZSM0019 | Error | Incompatible user-supplied Dispose |
| ZSM0020 | Warning | Diagram = true on a class with no transitions |
| ZSM0021 | Error | User constructor must call HookConstructor() |
| ZSM0022 | Warning | When = true is ignored on a concurrent machine |
| ZSM0023 | Warning | Containing type is not partial |
| ZSM0024 | Error | File-local state machine |
| ZSM0025 | Error | Name differs only in case from another state machine |
| ZSM0026 | Warning | Copies of a timed record share its timers |
| ZSM0027 | Info | Restore not generated: no parameterless constructor |
| ZSM0028 | Info | Restore not generated: conflicting member |