Плавный дизайн API на C# с вложенными общими ограничениями

В настоящее время я реализую 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.

Я могу (почти) свободно менять дизайн всего.

Любое предложение приветствуется.

Какой тип возвращаемого значения у register.Module()?

Mat J 14.12.2018 07:10

Это ExtraModulesRegister<TModule>. Обратите внимание на это в комментариях в коде метода WithConfig.

Ondřej Fischer 14.12.2018 08:38
Стоит ли изучать PHP в 2023-2024 годах?
Стоит ли изучать PHP в 2023-2024 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
3
2
368
1

Ответы 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()); 

Мне нравится такой подход. В настоящее время я поддерживаю модули без конфигурации, но думаю, что смогу справиться с ними отдельно. Скоро попробую.

A. Chiesa 14.12.2018 10:16

Другие вопросы по теме