我目前正在实现内部使用的IoC设置API(极大地启发了Autofac
的模块系统)。
我们有Module
个可以通过强类型配置进行配置,并且
我希望一个模块能够需要其他模块,所以我可以有一个类似于“ composition-root”的主模块,它将引导整个应用程序。
public interface IModule<TConfig>
{
TConfig Config { get; }
void Load(ContainerBuilder builder);
void LoadExtraModules(ModuleRegister register);
}
我目前正在设计ModuleRegister
类。我想要做的就是这样:
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.
}
}
我想让Intellisense以某种方式建议SqlConfiguration
是SqlModule
的正确配置类型,但我没有做到这一点:我想表达一个类似于
// ... inside an helper ExtraModulesRegister<TModule> class
public void WithConfig<TConfig>(TConfig configuration)
where TModule : IModule<TConfig>
{
...
}
但是显然我只能表达对TConfig的约束,不能表达对TModule的约束。
我发现的唯一解决方案是使用扩展方法,如下所示:
public static void WithConfig<TConfig, TModule>(this ExtraModulesRegister<TModule> register,
TConfig configuration)
where TModule : IModule<TConfig>, new()
{
register.LoadModule<TModule, TConfig>(configuration);
}
所以我可以表达两种类型约束,其中一种是在已经定义的通用参数TModule
上。
(几乎)我可以自由更改所有内容的设计。
任何建议都值得赞赏。
答案 0 :(得分:1)
我尝试使用ExtraModulesRegister
类本身的两个参数进行参数化:
public class ExtraModulesRegister<TModule, TConfig> wher TModule : IModule<TConfig> {
void WithConfig(TConfig config) {
}
}
但是现在您可能需要一些技巧让TConfig
推断SqlConfig
,所以您不需要传递两个参数。我想像辅助类型这样的东西可以帮助您,因此您调用register.Module(X<SqlModule>())
,因此通过传递类似X<TModule>
的参数,可以使Module()
方法同时推断出TModule
和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>();
}
不幸的是,看起来C#无法推断类型。在Java中,相同的模式有效,并且编译器可以从一个参数和它们之间的定义关系推断两种类型。
编辑: 您可以在C#中使用它,但是语法可能不是,
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());