Свободный дизайн API в C# с вложенными общими ограничениями
В настоящее время я реализую API настройки IoC для внутреннего использования (в значительной степени Autofac
модульная система).
У нас есть Module
s, которые настраиваются через строго типизированную конфигурацию, и я хочу, чтобы модуль мог требовать других модулей, поэтому у меня может быть основной модуль, подобный "составному корню", который будет загружать все приложение.
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 ответов
Я попытался параметризовать оба параметра 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());