Keyed / Named Services¶
When a single contract has multiple implementations, Skirnir lets you distinguish them by a string key. The container can then resolve the correct one either explicitly (by calling GetKeyedService<T>(key)) or implicitly (by declaring a Keyed<T, "key"> constructor parameter).
Registering keyed services¶
Use the AddKeyed* overloads on ServiceCollection. They mirror the un-keyed AddSingleton / AddScoped / AddTransient family:
A key can be any std::string. Empty keys are reserved for non-keyed registrations.
Resolving by key¶
ServiceProvider::GetKeyedService<T>(key) returns the unique registration whose contract is T and key matches. It throws std::runtime_error if no such registration exists. The non-throwing variant TryGetKeyedService<T>(key) returns std::optional<Arc<T>> instead.
auto a = sp->GetKeyedService<IFoo>("a");
auto b = sp->GetKeyedService<IFoo>("b");
auto maybe = sp->TryGetKeyedService<IFoo>("c");
if (maybe) { /* use *maybe */ }
Injecting keyed services¶
Declare a constructor parameter of type Keyed<T, "key">. The container resolves the keyed registration and fills in the wrapper's ptr field for you.
inline constexpr char keyA[] = "a";
class Consumer {
public:
explicit Consumer(skr::Keyed<IFoo, keyA> k) : mFoo(k.ptr) {}
// ...
};
The key must be a constant expression pointing to a static character array. Wrap your keys in inline constexpr char name[] = "..."; at namespace scope and reference them in the Keyed<...> template argument.
Note: The first template argument of
Keyedis the contract type, the second is the NTTP key. The wrapper'sptris aArc<T>set by the container.
Multi-registration interaction¶
Keyed and un-keyed registrations share the same contract id. GetService<T>() returns the first registration (matching .NET semantics). To pick a specific implementation, always use GetKeyedService or the Keyed<T, "..."> ctor wrapper.