A lightweight and mutation-safe event bus (event aggregator) for C# applications and games. Designed for simplicity and ease of use.
TrimKit.EventBus allows different parts of your system to communicate via strongly-typed events without hard dependencies or direct references.
- Lightweight & dependency-free.
 - Thread-safe.
 - Mutation-safe - handlers can safely modify subscriptions even during event publishing.
 - Disposable subscriptions - each Subscribe() returns an IDisposable token for easy unsubscribe.
 - SubscribeOnce() support - automatically unsubscribes after first event.
 - Duplicate protection - prevents duplicate handler registration.
 
Use provided nuget package or download the source.
🔧 dotnet add package TrimKit.EventBus
Define your events:
public sealed class GameStartedEvent
{
    public DateTime StartTime { get; }
    public GameStartedEvent(DateTime startTime) => StartTime = startTime;
}
public sealed class CharacterDamagedEvent
{
    public string Name { get; }
    public int Damage { get; }
    public CharacterDamagedEvent(string name, int damage)
    {
        Name = name;
        Damage = damage;
    }
}Create the event bus itself:
var bus = new EventBus();Add subscribers. You can subscribe normally or use SubscribeOnce() for a one-time handler.
// normal subscription
var token = bus.Subscribe<CharacterDamagedEvent>(e =>
{
    Console.WriteLine($"{e.Name} took {e.Damage} damage!");
});
// subscribe once
bus.SubscribeOnce<GameStartedEvent>(e =>
{
    Console.WriteLine($"[Once] Game started at {e.StartTime:T}");
});Each Subscribe() returns an IDisposable token you can later call Dispose() on to unsubscribe:
token.Dispose();And now you can start publishing events:
bus.Publish(new GameStartedEvent(DateTime.Now));
bus.Publish(new CharacterDamagedEvent("Goblin", 25));All operations (Subscribe, Unsubscribe, Publish, Reset) are protected by a lock and safe for concurrent use. During event dispatch, handlers are called using a snapshot copy, so you can safely modify subscriptions while publishing.
- Dispatch is by exact event type - base or interface subscribers do not receive derived events.
 - Events can be any class or struct (no need for 
EventArgs). - Handlers are simple 
Action<T>delegates without asenderparameter. You can include the sender information inside the payload itself. 
| Method | Description | 
|---|---|
IDisposable Subscribe<T>(Action<T> handler) | 
Subscribe to event T. | 
IDisposable SubscribeOnce<T>(Action<T> handler) | 
Subscribe for one-time delivery. | 
bool Unsubscribe<T>(Action<T> handler) | 
Remove specific subscription. | 
void Publish<T>(T payload) | 
Publish event to all subscribers. | 
int GetSubscriberCount<T>() | 
Get count of current subscribers for a type. | 
bool HasSubscribers<T>() | 
Check if any subscribers exist. | 
void Clear<T>() | 
Remove all subscriptions for a type T. | 
void ClearAll() | 
Remove all subscriptions. | 
- v1.1 - Switched to using Actions and expanded API.
 - v1.0.1 - Fixed namespaces.
 - v1.0 - Initial release.
 
This library is part of the TrimKit collection - a set of small, focused C# libraries that make game development more enjoyable by reducing the need for boilerplate code and providing simple reusable building blocks that can be dropped into any project.
- TrimKit.EventBus - Lightweight, mutation-safe event bus (event aggregator).
 - TrimKit.GameSettings - JSON-based persistent settings manager.
 - TrimKit.VirtualFileSystem - Unified file hierarchy abstraction to enable modding and additional content in games.
 - TrimKit.StatDictionary - Simple character stat container for RPG or other games relying on stat heavy calculations.
 
Each module is independent and can be used standalone or combined with others for a complete lightweight foundation.
Contributions are welcome!
You can start with submitting an issue on GitHub.
This library is released under the MIT License.