Skip to content

TypeRegistry

A thin wrapper around ConcurrentDictionary<Type, TValue> with GetOrAdd semantics and a generic key API. Use it as the backing store for plug-in systems, per-type caches, and strategy dispatch.

Usage

csharp
using Servus.Collections;

var registry = new TypeRegistry<Func<object, string>>();

registry.Add<Invoice>(o => $"INV-{((Invoice)o).Id}");
registry.Add<Order>  (o => $"ORD-{((Order)o).Id}");

// Retrieve by generic key
var invoiceFormatter = registry.Get<Invoice>();

// Or by Type
var orderFormatter = registry.Get(typeof(Order));

GetOrAdd — lazy registration

Create-on-demand keeps the registry self-populating:

csharp
var formatter = registry.GetOrAdd<Payment>(() => BuildFormatterFor<Payment>());

API

csharp
public class TypeRegistry<TValue>
{
    protected ConcurrentDictionary<Type, TValue> Dictionary { get; }

    public void Add<TKey>(TValue value);
    public void Add(Type key, TValue value);

    public TValue Get<TKey>();
    public TValue Get(Type key);

    public TValue GetOrAdd<TKey>(Func<TValue> factory);
    public TValue GetOrAdd(Type key, Func<TValue> factory);
}

Get throws when the key is missing. If you want a silent miss, use GetOrAdd with a fallback factory.

Pattern: per-type handlers

TypeRegistry<Action<object>> is a minimal message-type dispatch table:

csharp
var handlers = new TypeRegistry<Action<object>>();

handlers.Add<UserCreated>(e => OnUserCreated((UserCreated)e));
handlers.Add<OrderPlaced>(e => OnOrderPlaced((OrderPlaced)e));

void Dispatch(object evt) => handlers.Get(evt.GetType())(evt);

Servus and happy coding! 🥨🍺