ZeroAlloc.StateMachine 1.1.1

dotnet add package ZeroAlloc.StateMachine --version 1.1.1
                    
NuGet\Install-Package ZeroAlloc.StateMachine -Version 1.1.1
                    
This command is intended to be used within the Package Manager Console in Visual Studio, as it uses the NuGet module's version of Install-Package.
<PackageReference Include="ZeroAlloc.StateMachine" Version="1.1.1" />
                    
For projects that support PackageReference, copy this XML node into the project file to reference the package.
<PackageVersion Include="ZeroAlloc.StateMachine" Version="1.1.1" />
                    
Directory.Packages.props
<PackageReference Include="ZeroAlloc.StateMachine" />
                    
Project file
For projects that support Central Package Management (CPM), copy this XML node into the solution Directory.Packages.props file to version the package.
paket add ZeroAlloc.StateMachine --version 1.1.1
                    
#r "nuget: ZeroAlloc.StateMachine, 1.1.1"
                    
#r directive can be used in F# Interactive and Polyglot Notebooks. Copy this into the interactive tool or source code of the script to reference the package.
#:package ZeroAlloc.StateMachine@1.1.1
                    
#:package directive can be used in C# file-based apps starting in .NET 10 preview 4. Copy this into a .cs file before any lines of code to reference the package.
#addin nuget:?package=ZeroAlloc.StateMachine&version=1.1.1
                    
Install as a Cake Addin
#tool nuget:?package=ZeroAlloc.StateMachine&version=1.1.1
                    
Install as a Cake Tool

ZeroAlloc.StateMachine

NuGet Build License: MIT AOT GitHub Sponsors

Source-generated, zero-allocation finite state machines for .NET.

Add [StateMachine] and [Transition<TState, TTrigger>] attributes to a partial class or struct. A Roslyn source 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. AOT-safe.


Quick start

dotnet add package ZeroAlloc.StateMachine
public enum State   { Idle, Pending, Done }
public enum Trigger { Submit, Pay }

[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;                 // State.Pending
machine.TryFire(Trigger.Pay);    // true — Pending → Done
machine.TryFire(Trigger.Submit); // false — Done has no outgoing transitions

Features

Feature Notes
Zero allocation on happy path TryFire allocates 0 bytes — the switch is a compile-time constant
AOT / trimmer safe Generator emits concrete switch arms; no reflection at runtime
Concurrent mode Interlocked.CompareExchange CAS loop, Volatile.Read for Current
Guards partial bool Guard{Trigger}(TState, TTrigger) — block a transition at runtime
Entry / exit hooks partial void OnEnter{State} / partial void OnExit{State} — observe every crossing
Terminal states [Terminal<TState>] silences the "no outgoing transitions" diagnostic
Struct support partial struct machines eliminate even the instance heap allocation
Diagnostics ZSM0001–ZSM0004: unreachable state, sink state, concurrent + guard, concurrent + struct

Attribute overview

[StateMachine]

[StateMachine(InitialState = nameof(State.Idle), Concurrent = false)]
public partial class MyMachine { }
Property Type Default Description
InitialState string required Name of the initial state enum value. Use nameof(...).
Concurrent bool false Enables thread-safe transitions via Interlocked.CompareExchange.

[Transition<TState, TTrigger>]

[Transition<State, Trigger>(From = State.Idle, On = Trigger.Submit, To = State.Pending, When = false)]
Property Type Default Description
From TState required Source state.
On TTrigger required Trigger that fires the transition.
To TState required Destination state.
When bool false Emit a Guard{Trigger} partial stub and add a when clause.

[Terminal<TState>]

[Terminal<State>(State = State.Done)]

Marks a state as an intentional sink (no outgoing transitions). Silences ZSM0002.


Generated code

For each annotated type the generator emits one file alongside the user's source:

// <auto-generated />
partial class OrderMachine
{
    private State _state = State.Idle;
    public State Current => _state;

    public bool TryFire(Trigger trigger)
        => (Current, trigger) switch
        {
            (State.Idle,    Trigger.Submit) => Fire(State.Idle,    State.Pending, trigger),
            (State.Pending, Trigger.Pay)    => Fire(State.Pending, State.Done,    trigger),
            _ => false
        };

    private bool Fire(State from, State to, Trigger trigger) { ... }

    // Partial hook stubs — implement what you need, leave the rest
    partial void OnExitIdle(Trigger on);
    partial void OnExitPending(Trigger on);
    partial void OnEnterPending(State from);
    partial void OnEnterDone(State from);
}

Hooks

public partial class OrderMachine
{
    partial void OnExitIdle(Trigger on)
        => Console.WriteLine($"Leaving Idle via {on}");

    partial void OnEnterDone(State from)
        => Console.WriteLine($"Order complete, came from {from}");
}

Guards

[Transition<State, Trigger>(From = State.Pending, On = Trigger.Pay, To = State.Done, When = true)]
public partial class OrderMachine
{
    public bool HasBalance { get; set; }

    // Generator emits: private partial bool GuardPay(State from, Trigger on);
    private partial bool GuardPay(State from, Trigger on) => HasBalance;
}

Concurrent mode

[StateMachine(InitialState = nameof(State.Idle), Concurrent = true)]
[Transition<State, Trigger>(From = State.Idle, On = Trigger.Start, To = State.Running)]
public partial class WorkerMachine { }

State is stored as volatile long. TryFire uses a CAS loop — safe for concurrent callers. Guards are not generated in concurrent mode (TOCTOU risk).


Diagnostics

ID Severity Description
ZSM0001 Warning State is unreachable (no transition leads to it, not InitialState)
ZSM0002 Warning State has no outgoing transitions (use [Terminal] to acknowledge)
ZSM0003 Warning Trigger appears in only one transition (possible typo)
ZSM0004 Error Concurrent = true on a partial struct (not supported)

Documentation

Full docs live in docs/:


License

MIT

Product Compatible and additional computed target framework versions.
.NET net8.0 is compatible.  net8.0-android was computed.  net8.0-browser was computed.  net8.0-ios was computed.  net8.0-maccatalyst was computed.  net8.0-macos was computed.  net8.0-tvos was computed.  net8.0-windows was computed.  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 is compatible.  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. 
Compatible target framework(s)
Included target framework(s) (in package)
Learn more about Target Frameworks and .NET Standard.
  • net10.0

    • No dependencies.
  • net8.0

    • No dependencies.
  • net9.0

    • No dependencies.

NuGet packages (4)

Showing the top 4 NuGet packages that depend on ZeroAlloc.StateMachine:

Package Downloads
ZeroAlloc.Resilience

Source-generated, zero-allocation resilience policies for .NET. Add [Retry], [Timeout], [RateLimit], and [CircuitBreaker] to an interface; the generator emits a proxy composing all policies in declaration order. AOT-safe.

ZeroAlloc.Scheduling

Source-generated zero-allocation background job scheduling for .NET.

ZeroAlloc.Outbox

Source-generated transactional outbox for .NET.

ZeroAlloc.EventSourcing.Aggregates

Aggregate base + generator-emitted Apply dispatch for ZeroAlloc.EventSourcing.

GitHub repositories

This package is not used by any popular GitHub repositories.

Version Downloads Last Updated
1.1.1 0 5/3/2026
1.1.0 48 5/1/2026
1.0.3 128 4/29/2026
1.0.1 632 4/19/2026
1.0.0 2,039 4/18/2026