Как правильно создавать объекты, созданные путем отражения

Я пытаюсь обернуть голову вокруг отражения, поэтому я решил добавить возможности плагина к программе, которую я пишу. Единственный способ понять концепцию - замалчивать ваши пальцы и писать код, поэтому я пошел по пути создания простой библиотеки интерфейса, состоящей из интерфейсов IPlugin и IHost, библиотеки реализации плагинов классов, реализующих IPlugin, и простой который создает экземпляр класса реализации IHost, который выполняет простую работу с объектами плагина.

Используя отражение, я хотел повторить типы, содержащиеся в моей DLL-реализации плагина, и создать экземпляры типов. Я смог успешно создать классы с этим кодом, но я не мог отбросить созданный объект к интерфейсу.

Я пробовал этот код, но я не мог бросить объект o, как я ожидал. Я прошел через процесс с помощью отладчика и был вызван правильный конструктор. Объект Quickwatching o показал мне, что он имеет поля и свойства, которые я ожидал увидеть в классе реализации.

loop through assemblies
  loop through types in assembly
    // Filter out unwanted types
    if (!type.IsClass || type.IsNotPublic || type.IsAbstract )
      continue;
    // This successfully created the right object
    object o = Activator.CreateInstance(type);
    // This threw an Invalid Cast Exception or returned null for an "as" cast
    // even though the object implemented IPlugin      
    IPlugin i = (IPlugin) o;

Я сделал код с этим.

using System.Runtime.Remoting;
ObjectHandle oh = Activator.CreateInstance(assembly.FullName, type.FullName);
// This worked as I intended
IPlugin i = (IPlugin) oh.Unwrap();
i.DoStuff();

Вот мои вопросы:

  • Activator.CreateInstance(Тип t) возвращает объект, но я не мог передать объект интерфейсу, который реализовал объект. Почему?
  • Должен ли я использовать другую перегрузку CreateInstance()?
  • Каковы подсказки и трюки, связанные с размышлениями?
  • Есть ли какая-то важная часть отражения, которую я просто не получаю?

Ответ 1

Я просто догадываюсь, потому что из вашего кода это не очевидно, где у вас есть определение интерфейса IPlugin, но если вы не можете использовать его в своем хост-приложении, то вы, вероятно, имеете интерфейс IPlugin в своей сборке хоста, а затем на то же самое время в вашей сборке плагинов. Это не сработает.

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

Ответ 3

@lubos hasko

Ты прибил его к носу. У моего первоначального проекта было три разных сборки с реализацией хоста и плагина, ссылающимися на сборку интерфейса плагина.

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

Вы мне немного подумали, потому что я не совсем понимаю, почему две сборки, ссылающиеся на общую сборку, не получают один и тот же тип из общей сборки.

Ответ 4

Я просто пытался разобраться с этим сам и сумел наткнуться на ответ!

У меня было 3 разных проекта С#

  • Проект интерфейса плагина
  • B - Проект хоста exe → ссылки A
  • C - Проект реализации плагина → ссылки A

Я тоже получал ошибку кастинга, пока я не изменил имя сборки для моего проекта Plugin Interface proj, чтобы соответствовать пространству имен того, что я пытался использовать.

например.

IPluginModule pluginModule = (IPluginModule)Activator.CreateInstance(curType);

терпел неудачу, потому что сборка, в которой был определен интерфейс IPluginModule, называлась "Common", "Тип", который я использовал, был "Blah.Plugins.Common.IPluginModule".

Я изменил имя Assembly, чтобы интерфейс proj был "Blah.Plugins.Common", означало, что приведение выполнено успешно.

Надеюсь, это объяснение кому-то поможет. Вернуться к коду..

Ответ 5

Является ли ваш тип не общедоступным, если это так, вызовите перегрузку, которая принимает значение boolean:

Activator.CreateInstance(type, true);

Также, в вашем первом примере, посмотрите, является ли o нулевым, а если нет, распечатайте o.GetType(). Name, чтобы узнать, что это на самом деле.

Ответ 6

@Haacked

Я попытался сохранить псевдокод простым. foreach занимают много места и брекеты. Я уточнил это.

o.GetType(). FullName возвращает Plugins.Multiply, который является ожидаемым объектом. Plugins.Multiply реализует IPlugin. Несколько раз я проходил процесс в отладчике, пока не сдался на вечер. Не могу понять, почему я не мог его бросить, потому что я наблюдал, как строитель стрелял, пока я не встревожился во всем беспорядке. Вернулся к нему этим вечером и заставил его работать, но я до сих пор не понимаю, почему бросок не прошел в первом блоке кода. Второй блок кода работает, но он чувствует себя подальше от меня.

Ответ 7

Ссылка на egghead выше является основным решением проблемы использования Assembly.LoadFile() вместо .LoadFrom()