I'm currently implementing an IoC setup API for internal use (heavily inspired to Autofac's module system).
We have Modules which are configurable via a strongly typed configuration, and
I want a module to be able to require other modules, so I can have a "composition-root"-like main module, that is going to bootstrap the whole application.
public interface IModule<TConfig>
{
TConfig Config { get; }
void Load(ContainerBuilder builder);
void LoadExtraModules(ModuleRegister register);
}
I'm currently designing the ModuleRegisterclass. What I want to be able to do is similar to this:
public class MyModule : ModuleBase<ApplicationConfiguration>
{
public void LoadExtraModules(ModuleRegister register)
{
register.Module<SqlModule>().WithConfig(new SqlConfiguration() { ... });
}
}
public class SqlModule : ModuleBase<SqlConfiguration>
{
public void Load(ContainerBuilder builder)
{
// configuration code.
}
}
What I would like is to have Intellisense somehow suggest that SqlConfiguration is the right configuration type for SqlModule, but I'm failing to do that: I would like to express a type parameter akin to
// ... inside an helper ExtraModulesRegister<TModule> class
public void WithConfig<TConfig>(TConfig configuration)
where TModule : IModule<TConfig>
{
...
}
but obviously I can only express constraints to TConfig, not to TModule.
The only solution I found is to use an Extension Method like the following:
public static void WithConfig<TConfig, TModule>(this ExtraModulesRegister<TModule> register,
TConfig configuration)
where TModule : IModule<TConfig>, new()
{
register.LoadModule<TModule, TConfig>(configuration);
}
so I can express two type constraints, one of which on the already defined generic parameter TModule.
I can (almost) freely change the design of everything.
Any suggestion is appreciated.
I tried to parametrize with both parameters the ExtraModulesRegister class itself:
public class ExtraModulesRegister<TModule, TConfig> wher TModule : IModule<TConfig> {
void WithConfig(TConfig config) {
}
}
But now you may need some trick let the TConfig get inferred from SqlConfig, so you dont need to pass both parameters. I guess something like helper type can help, so you call register.Module(X<SqlModule>()) so by passing parameter of something like X<TModule> makes Module() method infer both, TModule and TConfig.
public ExtraModulesRegister<TModule, TConfig> Module<TModule, TConfig>(X<TModule> module) where TModule : IModule<TConfig> {
...
}
class X<T> {}
public static X<T> X<T>() {
return new X<T>();
}
Unfortunately it looks like C# is not able to infer the types. In java the same pattern works, and compiler can infer both types from one argument and defined relation between them.
Edit: This works in C#, but it`s maybe not the syntax, you'd like:
public class ExtraModulesRegister<TConfig> {
void WithConfig(TConfig config) {}
}
// Module method
public ExtraModulesRegister<TConfig> Module<TConfig>(IModule<TConfig> fakeModule) {
Return new ExtraModulesRegister<TConfig>();
}
// Usage
register.Module(default(SqlModule)).WithConfig(new SqlConfig());
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With