SharpFsm 1.1.0

dotnet add package SharpFsm --version 1.1.0
                    
NuGet\Install-Package SharpFsm -Version 1.1.0
                    
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="SharpFsm" Version="1.1.0" />
                    
For projects that support PackageReference, copy this XML node into the project file to reference the package.
<PackageVersion Include="SharpFsm" Version="1.1.0" />
                    
Directory.Packages.props
<PackageReference Include="SharpFsm" />
                    
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 SharpFsm --version 1.1.0
                    
#r "nuget: SharpFsm, 1.1.0"
                    
#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 SharpFsm@1.1.0
                    
#: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=SharpFsm&version=1.1.0
                    
Install as a Cake Addin
#tool nuget:?package=SharpFsm&version=1.1.0
                    
Install as a Cake Tool

SharpFsm

A flexible finite state machine (FSM) library for .NET written in C#, it is designed to create and manage Finite State Machines (FSMs) in a simple and efficient way. It allows developers to define states, transitions, and events, enabling the modeling of complex behaviors in a structured manner.

Quick Start

Install SharpFsm via NuGet Package Manager or dotnet.

Install-Package SharpFsm
dotnet add package SharpFsm
public enum SwitchState { Off, On }
public class SwitchContext { }
var builder = FiniteStateMachineBuilder<SwitchState, SwitchContext>.Create("Switch")
    .WithInitialState(SwitchState.Off)
    .AddTransition(SwitchState.Off, SwitchState.On).Done()
    .AddTransition(SwitchState.On, SwitchState.Off).Done();
var fsm = new FiniteStateMachine<SwitchState, SwitchContext>(builder.Build());
var context = new SwitchContext();
Console.WriteLine(fsm.Current); // Off
fsm.TryTransitionTo(SwitchState.On, context);
Console.WriteLine(fsm.Current); // On

Features

  • State Management: Easily define and manage states in your FSM. Strongly-typed FSMs using enums for states
  • Transition Handling: Transition conditions and side effects.
  • Serialization: Serialization to/from JSON and YAML.
  • Builder Pattern: Builder pattern for easy FSM construction.
  • Extensible: Easily extend the library to fit specific needs or integrate with other systems.
  • Cross-Platform: Multi-targeted for broad .NET compatibility

Concepts

State

A state represents a distinct condition or situation in the lifecycle of an object or process. In SharpFsm, states are typically defined using an enum (e.g., Open, Closed, InProgress).

Transition

A transition is a rule that defines how the FSM moves from one state to another. Each transition specifies:

  • The source state (where the transition starts)
  • The target state (where the transition ends)
  • An optional condition (a function that must return true for the transition to occur)
  • An optional side effect (an action to execute when the transition happens)

Condition

A condition is a predicate (function) that determines if a transition is allowed, based on the current context. Example: ctx => ctx.IsAgentAssigned

Side Effect

A side effect is an action that is executed when a transition occurs. Example: ctx => Console.WriteLine("Customer notified")

Context

The context is an object that holds data relevant to the FSM’s operation and is passed to conditions and side effects. Example: a TicketContext with properties like IsAgentAssigned.

State Machine Definition

A state machine definition describes all possible states, transitions, the initial state, and associated logic for an FSM.

Advantage of using FSM

Using a finite state machine (FSM) for managing state offers several advantages over ad-hoc approaches (like scattered conditionals, flags, or event-driven code) and even over some object-oriented state patterns. Here are the key benefits:

  1. Clarity and Explicitness
    • All possible states and transitions are explicitly defined.
    • The system’s behavior is easy to visualize, reason about, and document.
    • Reduces ambiguity and hidden state changes.
  2. Predictability and Robustness
    • Transitions are controlled and validated.
    • Only allowed transitions can occur, preventing invalid or unexpected state changes.
    • Makes it easier to handle edge cases and errors.
  3. Maintainability
    • Adding or modifying states and transitions is straightforward.
    • Changes are localized to the FSM definition, not scattered across the codebase.
    • Reduces the risk of introducing bugs when requirements change.
  4. Testability
    • FSMs are easy to test.
    • You can systematically test all states and transitions.
    • Makes it easier to write unit tests for state-dependent logic.
  5. Separation of Concerns
    • State logic is separated from business logic.
    • Conditions and side effects are encapsulated, making the codebase cleaner and more modular.
  6. Scalability
    • FSMs scale well as complexity grows.
    • Adding new states or transitions does not exponentially increase code complexity, unlike nested if/else or switch statements.
  7. Visualization and Documentation
    • FSMs can be visualized as state diagrams.
    • This aids in communication with stakeholders and helps onboard new developers.
Approach Pros of FSM over this approach
If/else, switch Avoids spaghetti code, centralizes state logic
Flags/booleans Prevents invalid state combinations
Event-driven Makes allowed transitions explicit and predictable
State pattern FSM is more declarative and easier to visualize

Use Cases

Here are some common use cases for implementing a finite state machine (FSM) in software development:

  1. Workflow and Process Management
    • Example: Ticketing systems, order processing, approval workflows.
    • Why: Each item moves through a series of well-defined states (e.g., Open → In Progress → Resolved).
  2. User Interface (UI) Navigation
    • Example: Wizard dialogs, multi-step forms, menu navigation.
    • Why: UI components often have distinct states and transitions based on user actions.
  3. Game Development
    • Example: Character states (Idle, Walking, Jumping, Attacking), enemy AI behaviors.
    • Why: Game entities often have clear, rule-based state transitions.
  4. Protocol and Communication Handling
    • Example: Network protocol implementations (TCP handshake, HTTP request/response), parsers.
    • Why: Protocols are defined by sequences of states and transitions based on received data.
  5. Device and Hardware Control
    • Example: Embedded systems, robotics, IoT devices (e.g., a washing machine: Idle → Washing → Rinsing → Spinning → Done).
    • Why: Devices operate in modes with strict rules for moving between them.
  6. Authentication and Authorization Flows
    • Example: Login processes, multi-factor authentication, session management.
    • Why: Security flows require strict control over allowed transitions.
  7. Error Handling and Recovery
    • Example: Retry logic, circuit breakers, transaction management.
    • Why: Systems need to move between normal, error, and recovery states in a controlled way.
  8. Text Parsing and Lexical Analysis
    • Example: Tokenizers, interpreters, compilers.
    • Why: Parsing often involves moving through states based on input characters or tokens.

Example

Let's define a state machine for order management.

stateDiagram-v2
    [*] --> Created
    Created --> Paid: PaymentReceived
    Paid --> Packed: PackingComplete
    Packed --> Shipped: Shipped
    Shipped --> Delivered: Delivered
    Delivered --> Returned: ReturnRequested

    Created --> Cancelled: CancelRequested
    Paid --> Cancelled: CancelRequested
    Packed --> Cancelled: CancelRequested
    Shipped --> Cancelled: CancelRequested

    Cancelled --> [*]
    Delivered --> [*]
    Returned --> [*]

  1. Define States and Context
public enum OrderState
{
    Created,
    Paid,
    Packed,
    Shipped,
    Delivered,
    Cancelled,
    Returned
}

public class OrderContext
{
    // This class can contain direct access to your data stores 
    // ex: connect to your database to do complex calculations
    public bool PaymentReceived { get; set; }
    public bool PackingComplete { get; set; }
    public bool Shipped { get; set; }
    public bool Delivered { get; set; }
    public bool CancelRequested { get; set; }
    public bool ReturnRequested { get; set; }
}
  1. Register Conditions and Side Effects
using SharpFsm;

var registry = new TransitionRegistry<OrderContext>();
registry.RegisterCondition("PaymentReceived", ctx => ctx.PaymentReceived);
registry.RegisterCondition("PackingComplete", ctx => ctx.PackingComplete);
registry.RegisterCondition("Shipped", ctx => ctx.Shipped);
registry.RegisterCondition("Delivered", ctx => ctx.Delivered);
registry.RegisterCondition("CancelRequested", ctx => ctx.CancelRequested);
registry.RegisterCondition("ReturnRequested", ctx => ctx.ReturnRequested);

registry.RegisterSideEffect("NotifyShipment", ctx => Console.WriteLine("Customer notified: Order shipped"));
registry.RegisterSideEffect("NotifyDelivery", ctx => Console.WriteLine("Customer notified: Order delivered"));
registry.RegisterSideEffect("NotifyCancel", ctx => Console.WriteLine("Customer notified: Order cancelled"));
registry.RegisterSideEffect("NotifyReturn", ctx => Console.WriteLine("Customer notified: Order returned"));
  1. Build a State Machine
var builder = FiniteStateMachineBuilder<OrderState, OrderContext>.Create("Order")
    .WithInitialState(OrderState.Created)
    .WithRegistry(registry)
    .AddTransition(OrderState.Created, OrderState.Paid)
        .When("PaymentReceived")
        .Done()
    .AddTransition(OrderState.Paid, OrderState.Packed)
        .When("PackingComplete")
        .Done()
    .AddTransition(OrderState.Packed, OrderState.Shipped)
        .When("Shipped")
        .WithSideEffect("NotifyShipment")
        .Done()
    .AddTransition(OrderState.Shipped, OrderState.Delivered)
        .When("Delivered")
        .WithSideEffect("NotifyDelivery")
        .Done()
    .AddTransition(OrderState.Created, OrderState.Cancelled)
        .When("CancelRequested")
        .WithSideEffect("NotifyCancel")
        .Done()
    .AddTransition(OrderState.Paid, OrderState.Cancelled)
        .When("CancelRequested")
        .WithSideEffect("NotifyCancel")
        .Done()
    .AddTransition(OrderState.Packed, OrderState.Cancelled)
        .When("CancelRequested")
        .WithSideEffect("NotifyCancel")
        .Done()
    .AddTransition(OrderState.Shipped, OrderState.Cancelled)
        .When("CancelRequested")
        .WithSideEffect("NotifyCancel")
        .Done()
    .AddTransition(OrderState.Delivered, OrderState.Returned)
        .When("ReturnRequested")
        .WithSideEffect("NotifyReturn")
        .Done();

var fsm = new FiniteStateMachine<OrderState, OrderContext>(builder.Build());

  1. Use the State Machine
var context = new OrderContext { PaymentReceived = true };
fsm.TryTransitionTo(OrderState.Paid, context); // Moves to Paid

context.PackingComplete = true;
fsm.TryTransitionTo(OrderState.Packed, context); // Moves to Packed

context.Shipped = true;
fsm.TryTransitionTo(OrderState.Shipped, context); // Moves to Shipped, notifies shipment

context.Delivered = true;
fsm.TryTransitionTo(OrderState.Delivered, context); // Moves to Delivered, notifies delivery

context.ReturnRequested = true;
fsm.TryTransitionTo(OrderState.Returned, context); // Moves to Returned, notifies return

Serialization/Deserialization Example

  1. JSON
using System.Text.Json;

// Serialization
var definition = builder.Build(); // Ensure build is called before calling ToSerializable
var serializedObject = builder.ToSerializable(); // returns SerializableStateMachine object
string json = JsonSerializer.Serialize(serializedObject);

// Deserialization
var jsonDto = JsonSerializer.Deserialize<SerializableStateMachine>(json);
var loadedBuilder = FiniteStateMachineBuilder<TicketState, TicketContext>.Create(jsonDto.EntityType)
    .LoadFrom(jsonDto, registry);
var loadedFsm = loadedBuilder.Build();
var sm = new FiniteStateMachine<TicketState, TicketContext>(loadedFsm);
  1. YAML
using YamlDotNet.Serialization;
using YamlDotNet.Serialization.NamingConventions;

// Serialization
var definition = builder.Build(); // Ensure build is called before calling ToSerializable
var serializedObject = builder.ToSerializable(); // returns SerializableStateMachine object
var yamlSerializer = new SerializerBuilder()
	.WithNamingConvention(CamelCaseNamingConvention.Instance)
	.Build();
var yaml = yamlSerializer.Serialize(serializedObject);

// Deserialization
var yamlDeserializer = new DeserializerBuilder()
	.WithNamingConvention(CamelCaseNamingConvention.Instance)
	.IgnoreUnmatchedProperties()
	.Build();
var deserializedDto = yamlDeserializer.Deserialize<SerializableStateMachine>(new StringReader(yaml));
var loadedBuilder = FiniteStateMachineBuilder<TicketState, TicketContext>.Create("Ticket")
    .LoadFrom(deserializedDto!, registry);
var sm = new FiniteStateMachine<TicketState, TicketContext>(loadedBuilder.Build());

API Overview

Namespaces

  • SharpFsm
  • SharpFsm.Serialization

Core Types

  1. FiniteStateMachine<TState, TContext>: Represents a runtime finite state machine.

    • Type Parameters:
      • TState: Enum type representing states.
      • TContext: Type for contextual data passed to conditions and side effects.
    • Properties:
      • TState Current The current state of the FSM.
    • Methods:
      • bool CanTransitionTo(TState target, TContext context) Checks if a transition to the target state is possible given the context.
      • bool TryTransitionTo(TState target, TContext context) Attempts to transition to the target state. Returns true if successful.
  2. FiniteStateMachineBuilder<TState, TContext>: Fluent builder for defining FSMs

    • Type Parameters:
      • TState: Enum type representing states.
      • TContext: Type for contextual data passed to conditions and side effects.
    • Static Methods:
      • FiniteStateMachineBuilder<TState, TContext> Create(string entityType): Creates a new instance of the FiniteStateMachineBuilder for the specified entity type.
    • Instance Methods:
      • FiniteStateMachineBuilder<TState, TContext> WithInitialState(TState state): Sets the initial state of the finite state machine. This method also adds the state to the list of states if it is not already present.
      • TransitionBuilder AddTransition(TState from, TState to): Adds a transition between two states in the finite state machine. This method also adds both states to the list of states if they are not already present.
      • EnumStateMachineDefinition<TState, TContext> Build(): Builds the finite state machine definition. This method will throw an exception if the initial state has not been set.
      • SerializableStateMachine ToSerializable(): Converts the current state machine builder to a serializable state machine definition.
      • FiniteStateMachineBuilder<TState, TContext> LoadFrom(SerializableStateMachine dto, TransitionRegistry<TContext> registry): Loads a finite state machine from a serializable state machine definition.
    • Nested Type:
      • TransitionBuilder: Builder for defining transitions in the finite state machine.
  3. EnumStateMachineDefinition<TState, TContext>: Describes the structure of an FSM (states, transitions, initial state).

    • Type Parameters:
      • TState: Enum type representing states.
      • TContext: Type for contextual data passed to conditions and side effects.
    • Properties:
      • string EntityType: The type of work item this state machine is associated with.
      • IEnumerable<IState> States: A collection of all states in the state machine, represented as IState instances.
      • IEnumerable<ITransition<TContext>> Transitions: A collection of transitions between states, represented as ITransition instances.
      • IState InitialState: The initial state of the state machine, represented as an IState instance.
  4. TransitionRegistry<TContext>: Registry for reusable conditions and side effects.

    • Type Parameters:
      • TContext: Type for contextual data passed to conditions and side effects.
    • Properties:
      • Dictionary<string, Func<TContext, bool>> Conditions: Stores conditions that can be used to evaluate whether a transition can occur.
      • Dictionary<string, Action<TContext>> SideEffects: Stores side effects that can be executed when a transition occurs.
    • Methods:
      • void RegisterCondition(string name, Func<TContext, bool> fn): Registers a condition with a name that can be used to evaluate transitions.
      • void RegisterSideEffect(string name, Action<TContext> fn): Registers a side effect with a name that can be executed when a transition occurs.
  5. ITransition<TContext>: Represents a transition between two states.

    • Type Parameters:
      • TContext: Type for contextual data passed to conditions and side effects.
    • Properties:
      • IState From: Gets the source state of the transition.
      • IState To: Gets the target state of the transition.
      • Func<TContext, bool> Condition: Gets the condition that must be met for this transition to occur.
      • Action<TContext> SideEffect: Gets an optional side effect that occurs when the transition is taken.
      • string ConditionName: Gets the name of the condition that triggers this transition, if any.
      • string SideEffectName: Gets the name of the side effect that occurs when this transition is taken, if any.
  6. IState: Represents a state in the FSM.

    • Property:
      • string Name: Gets the name of the state.
  7. SerializableStateMachine:

    • Properties:
      • string EntityType: The type of entity this state machine is associated with.
      • string InitialState: The initial state of the state machine.
      • List<string> States: A list of all states in the state machine.
      • List<SerializableTransition> Transitions: A list of transitions between states.
  8. SerializableTransition:

    • Properties:
      • string From: The state this transition is from.
      • string To: The state this transition is to.
      • string ConditionName: The name of the condition that triggers this transition.
      • string SideEffectName: The name of the side effect that occurs when this transition is taken.
Product Compatible and additional computed target framework versions.
.NET net5.0 was computed.  net5.0-windows was computed.  net6.0 is compatible.  net6.0-android was computed.  net6.0-ios was computed.  net6.0-maccatalyst was computed.  net6.0-macos was computed.  net6.0-tvos was computed.  net6.0-windows was computed.  net7.0 was computed.  net7.0-android was computed.  net7.0-ios was computed.  net7.0-maccatalyst was computed.  net7.0-macos was computed.  net7.0-tvos was computed.  net7.0-windows was computed.  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 was computed.  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. 
.NET Core netcoreapp2.0 was computed.  netcoreapp2.1 was computed.  netcoreapp2.2 was computed.  netcoreapp3.0 was computed.  netcoreapp3.1 was computed. 
.NET Standard netstandard2.0 is compatible.  netstandard2.1 is compatible. 
.NET Framework net461 was computed.  net462 was computed.  net463 was computed.  net47 was computed.  net471 was computed.  net472 was computed.  net48 was computed.  net481 was computed. 
MonoAndroid monoandroid was computed. 
MonoMac monomac was computed. 
MonoTouch monotouch was computed. 
Tizen tizen40 was computed.  tizen60 was computed. 
Xamarin.iOS xamarinios was computed. 
Xamarin.Mac xamarinmac was computed. 
Xamarin.TVOS xamarintvos was computed. 
Xamarin.WatchOS xamarinwatchos was computed. 
Compatible target framework(s)
Included target framework(s) (in package)
Learn more about Target Frameworks and .NET Standard.
  • .NETStandard 2.0

    • No dependencies.
  • .NETStandard 2.1

    • No dependencies.
  • net6.0

    • No dependencies.
  • net8.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.

Version Downloads Last Updated
1.1.0 84 6/7/2025
1.0.1 90 6/6/2025
1.0.0 101 6/6/2025