State Pattern in C#: Definition, Examples, Pros, Cons, and Use Cases
The State pattern in C# is a behavioral design pattern that allows an object to change its behavior dynamically when its internal state changes.
The State pattern encapsulates different behaviors into separate state classes and delegates behavior execution to the current state object. Instead of using large conditional statements, the object changes its behavior by switching between state objects. Each state class represents a specific state and defines behavior appropriate for that state. The context object maintains a reference to the current state and forwards requests to it. In C#, the State pattern is commonly used in workflow systems, game development, UI controls, media players, and finite state machines.
Why We Use State Pattern in C#?
We use the State pattern in C# to:
• Eliminate large if-else or switch statements
• Manage complex state-dependent behavior
• Improve maintainability
• Encapsulate state-specific logic
• Support dynamic behavior changes
• Simplify workflow management
• Follow the Open/Closed Principle
• Improve readability in state-driven systems
When Should We Use State Pattern in C#?
The State pattern should be used when:
• Object behavior changes based on internal state
• Applications contain many state-based conditions
• State transitions are frequent
• Workflow systems require clear state management
• Finite state machine behavior is needed
• State-specific logic should be isolated
• You want cleaner and more extensible code
Common Programming Problems Solved by State Pattern
• Workflow engines
• Media player states
• Traffic light systems
• ATM machine operations
• Document approval processes
• Order processing systems
• Character behavior in games
• UI component state handling
Structure of State Pattern in C#
Typical participants:
• Context
• State Interface
• Concrete States
• Client
Basic workflow:
• Context maintains current state
• Client sends request to context
• Context delegates behavior to current state
• State may change context to another state
State Pattern Examples in C#
Example 1: Traffic Light System
A traffic light should behave differently depending on its current color state.
using System;
public interface ITrafficLightState
{
void Handle(TrafficLight light);
}
public class RedState : ITrafficLightState
{
public void Handle(TrafficLight light)
{
Console.WriteLine("Red Light - Stop");
light.State = new GreenState();
}
}
public class GreenState : ITrafficLightState
{
public void Handle(TrafficLight light)
{
Console.WriteLine("Green Light - Go");
light.State = new YellowState();
}
}
public class YellowState : ITrafficLightState
{
public void Handle(TrafficLight light)
{
Console.WriteLine("Yellow Light - Wait");
light.State = new RedState();
}
}
public class TrafficLight
{
public ITrafficLightState State { get; set; }
public TrafficLight()
{
State = new RedState();
}
public void Change()
{
State.Handle(this);
}
}
public class Program
{
public static void Main()
{
TrafficLight light = new TrafficLight();
light.Change();
light.Change();
light.Change();
}
}
Example 2: Media Player States
A media player should behave differently when playing, paused, or stopped.
using System;
public interface IPlayerState
{
void PressButton(MediaPlayer player);
}
public class PlayingState : IPlayerState
{
public void PressButton(MediaPlayer player)
{
Console.WriteLine("Pausing media");
player.State = new PausedState();
}
}
public class PausedState : IPlayerState
{
public void PressButton(MediaPlayer player)
{
Console.WriteLine("Resuming media");
player.State = new PlayingState();
}
}
public class StoppedState : IPlayerState
{
public void PressButton(MediaPlayer player)
{
Console.WriteLine("Starting media");
player.State = new PlayingState();
}
}
public class MediaPlayer
{
public IPlayerState State { get; set; }
public MediaPlayer()
{
State = new StoppedState();
}
public void PressPlayPause()
{
State.PressButton(this);
}
}
public class Program
{
public static void Main()
{
MediaPlayer player = new MediaPlayer();
player.PressPlayPause();
player.PressPlayPause();
player.PressPlayPause();
}
}
Example 3: Order Processing Workflow
An order should transition through different processing states.
using System;
public interface IOrderState
{
void Next(Order order);
}
public class NewOrderState : IOrderState
{
public void Next(Order order)
{
Console.WriteLine("Order confirmed");
order.State = new ShippedState();
}
}
public class ShippedState : IOrderState
{
public void Next(Order order)
{
Console.WriteLine("Order delivered");
order.State = new DeliveredState();
}
}
public class DeliveredState : IOrderState
{
public void Next(Order order)
{
Console.WriteLine("Order already delivered");
}
}
public class Order
{
public IOrderState State { get; set; }
public Order()
{
State = new NewOrderState();
}
public void Process()
{
State.Next(this);
}
}
public class Program
{
public static void Main()
{
Order order = new Order();
order.Process();
order.Process();
order.Process();
}
}
Most Known Real-World Use Cases in C#
• Workflow engines
• Game character behavior systems
• Media player controls
• ATM systems
• UI state management
• Document lifecycle systems
• Order processing systems
• Authentication state management
• Network connection state handling
Advantages of State Pattern in C#
• Eliminates complex conditional statements
• Improves code organization
• Encapsulates state-specific behavior
• Simplifies adding new states
• Supports Open/Closed Principle
• Improves readability
• Makes transitions explicit
• Enhances maintainability
Disadvantages (Weak Points) of State Pattern in C#
• Increases number of classes
• Can become complex with many states
• State transitions may be difficult to track
• Adds extra abstraction
• Overkill for simple state logic
• Context-state coupling may grow
• Debugging transitions can become harder
Comparison with Similar Patterns
| Pattern | Main Purpose | Behavior Change | State Awareness | Typical Usage | Main Difference from State |
|---|---|---|---|---|---|
| State | Change behavior by internal state | Dynamic | High | Workflows, FSMs, UI states | Behavior changes automatically based on state |
| Strategy | Choose algorithm dynamically | Manual selection | Low | Sorting, payment processing | Strategies are selected externally rather than state-driven |
| Memento | Save and restore object state | No | Moderate | Undo/redo systems | Focuses on snapshot restoration instead of behavior changes |
| Command | Encapsulate operations | No | Low | Task queues, undo systems | Encapsulates requests instead of state behavior |
| Template Method | Define algorithm skeleton | Partial | Low | Framework base classes | Uses inheritance instead of runtime state switching |
Simplified UML-Style Structure
| Component | Responsibility |
|---|---|
| Context | Maintains current state and delegates requests |
| State Interface | Defines state-specific operations |
| ConcreteState | Implements behavior for a specific state |
| Client | Interacts with context object |