Мне не совсем понятно, как возникают экземпляры этих типов и в какой степени они должны быть взаимозаменяемыми.Одним из подходов, который может быть полезен в некоторых контекстах, является использование делегатов.Например, предположим, что некоторый тип объекта должен обеспечивать метод для сохранения его состояния и последующего восстановления.Если предполагается, что состояние будет восстановлено в том же объекте, из которого оно было сохранено, подпрограмма «сохранить контекст» может вернуть делегат, который при выполнении восстановит состояние объекта.Делегат может содержать данные любого типа, но весь код будет безопасным по типу.
Другие варианты - сделать IModuleConfiguration универсальным в отношении типа настраиваемого модуля, а IModuleConfiguration содержит ConfigureModuleили метод InvokeModule (который, в свою очередь, передает себя методу Configure или Invoke модуля), или интерфейс IConfiguredModule, метод вызова которого ничего не говорит о конфигурации (ожидается, что экземпляры класса объединят модуль ссоответствующая конфигурация).
Какой подход будет наиболее подходящим, зависит от того, как создаются и используются различные экземпляры.