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.