Как правильно создавать объекты, созданные путем отражения
Я пытаюсь обернуть голову вокруг отражения, поэтому я решил добавить возможности плагина к программе, которую я пишу. Единственный способ понять концепцию - замалчивать ваши пальцы и писать код, поэтому я пошел по пути создания простой библиотеки интерфейса, состоящей из интерфейсов 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 был помечен как общедоступный в вашей сборке хоста, а затем иметь сборку сборки плагинов ссылки, поэтому обе сборки имеют доступ к тот же интерфейс.
Ответ 2
hmmm... Если вы используете Assembly.LoadFrom для загрузки сборки, попробуйте вместо этого заменить Assembly.LoadFile.
Работал для меня
Отсюда: http://www.eggheadcafe.com/community/aspnet/2/10036776/solution-found.aspx
Ответ 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()