Интерфейс и реализация в отдельных сборках с использованием отражения - PullRequest
1 голос
/ 16 августа 2011

у меня две сборки; AssemblyWithInterface и AssemblyWithClass. В AssemblyWithInterface у меня есть интерфейс под названием IDoSomething, который реализуется TheClass в AssemblyWithClass. (AssemblyWithClass ссылки AssemblyWithInterface.)

В AssemblyWithInterface Я хочу создать экземпляры класса из AssemblyWithClass, используя отражение:

var theAssembly = Assembly.Load("Company.AssemblyWithClass, { FQN... }");
var theConcreteClass = theAssembly.CreateInstance("Company.AssemblyWithClass.TheClass");

Сборка загружается нормально, и экземпляр создается как TheConcreteClass.

Однако я не могу привести theConcreteClass к его интерфейсу реализации. Я получаю InvalidCastException здесь:

var theConcreteClassInterfaced = (IDoSomething)theConcreteClass;

И

var isAssignable = typeof(IDoSomething).IsAssignableFrom(theConcreteClass.GetType();

Неверно.

Чего мне не хватает? (Цель состоит в том, чтобы иметь возможность в стиле шаблона команд добавлять команды, реализующие IDoSomething к AssemblyWithClass, и иметь возможность выполнять их в AssemblyWithInterface без изменения кода в AssemblyWithInterface.)

Платформа - .NET 3.5 (не может использовать динамический).

Обновление: Предпосылкой для этого вопроса (чтобы объяснить, почему я не соблюдаю DIP) является то, что у меня есть устаревшее ASP.NET-приложение, содержащееся в одной большой сборке. Я хочу создать сборку плагинов, которая может обращаться к различным частям устаревшей сборки, чтобы выполнять мониторинг и некоторые автоматизированные задачи. Я не хочу добавлять какие-либо дополнительные зависимости (ссылки на другие сборки) в устаревшую сборку. Идея состоит в том, чтобы реализовать хук в устаревшей сборке (новая специальная страница и IPlugInOperation), добавить страницу мониторинга с соответствующим кодом позади. Сделайте так, чтобы код выполнял различные IPlugInOperations (рисуя интерфейс, позволяющий администратору указывать параметры, которые будут использоваться для выполнения кода в устаревшей сборке). Сборка подключаемого модуля должна ссылаться на устаревшую сборку, а устаревшая сборка использует отражение, чтобы вывести список и позволить администратору оправдывать различные реализации IPlugInOperation, содержащиеся в сборке подключаемого модуля.

Ответы [ 2 ]

3 голосов
/ 16 августа 2011

Интерфейсы не должны заботиться о реализациях.

Измените и переместите всю логику в третью сборку.

Обновление:

Спецификация сборки

  • открытый интерфейс ICommand
  • открытый интерфейс ICommandFactory

Сборка класса (спецификация ссылок)

  • внутренний класс CreateUserCommand: ICommand
  • открытый класс CommandFactory: ICommandFactory

Сборка приложения (обе ссылки)

public class Program
{
    private ICommandFactory _commandFactory;

    public static void Main(string[] argv)
    {
        // this is the only line that is really dependent of a specific
        // implementation.
        _commandFactory = new TheSpecificImplementationAssembly.CommandFactory();

        ICommand command = _commandFactory.Create("CreateUser");
        command.Execute();
    }
}

Update2

В большинстве современных решений используется инверсия контейнера управления для обеспечения соответствия между интерфейсами и реализациями.

Другое решение состоит в том, чтобы иметь небольшой набор фабрик, который используется для создания реализаций для конкретных интерфейсов.,В этом случае я бы также использовал шаблон фабричного метода, чтобы позволить корням агрегатов создавать дочерние агрегаты.Например, класс Order будет иметь метод с именем CreateOrderLine, который будет возвращать объект IOrderLine.

1 голос
/ 16 августа 2011

Здесь у вас есть своего рода круговая ссылка, которая определенно не идеальна, но должна быть возможность делать то, что вы ищете.Взгляните на AppDomain.AssemblyResolve .Я подозреваю, что происходит то, что рефлексия для создания экземпляра класса заставляет систему загрузить дополнительную копию вашей исходной сборки интерфейса, так что реализуемый тип интерфейса уже не тот тип, на который вы статически ссылались.Реализуя AssemblyResolve, вы можете искать в списке загруженных сборок, используя AppDomain.GetAssemblies , возвращая тот, у которого совпадает имя.

Вот одна из возможных реализаций:

    private static Assembly CurrentDomain_AssemblyResolve(object sender, ResolveEventArgs args)
    {
        return AppDomain.CurrentDomain.GetAssemblies().
                     FirstOrDefault(assembly => assembly.FullName == args.Name);
    }
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...