В настоящее время я реализую API настройки IoC для внутреннего использования (в значительной степени вдохновленный модульной системой Autofac
).
У нас есть Module
, которые можно настроить с помощью строго типизированной конфигурации, и
Я хочу, чтобы модуль мог требовать другие модули, чтобы я мог иметь основной модуль, подобный «корню композиции», который будет загружать все приложение.
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
.
Я могу (почти) свободно менять дизайн всего.
Любое предложение приветствуется.
Это ExtraModulesRegister<TModule>
. Обратите внимание на это в комментариях в коде метода WithConfig
.
Я попытался параметризовать обоими параметрами сам класс 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());
Мне нравится такой подход. В настоящее время я поддерживаю модули без конфигурации, но думаю, что смогу справиться с ними отдельно. Скоро попробую.
Какой тип возвращаемого значения у
register.Module()
?