c# - различные типы при загрузке из сборки с использованием отражения



У меня есть решение с 3 проектами:
1) графический исполняемый файл
2) библиотека классов, содержащая открытый API и открытый интерфейс.
3) библиотека классов класса, реализующего указанный выше интерфейс



Я пытаюсь реализовать загрузчик ресурсов в API, так что когда графический интерфейс вызывает метод API.Foo(), я просматриваю каждую сборку в определенной папке (найдено по адресу: . resources), который содержит копию сборок, которые я скомпилировал (#3).
Затем я хочу добавить ресурс в список и используйте этот список для вызова функции, которая является частью интерфейса (который реализует каждый ресурс)



Итак, что я сделал:



private List<IResource> m_resources;

public void Foo()
{
string resourceDir = Directory.GetCurrentDirectory() + @"Resources";
m_resources= new List<IResource>();
foreach (var dllFile in Directory.EnumerateFiles(resourceDir))
{
IResource dllInstance;
if (TryLoadingDLL(Path.Combine(resourceDir, dllFile), out dllInstance))
{
resources.Add(dllInstance);
}
}
}

private static bool TryLoadingDLL(string dllFullPath, out IResource instanceOfDll)
{
instanceOfDll = null;
try
{
Assembly assembly = Assembly.LoadFrom(dllFullPath);
Assembly IResourceAssambly = Assembly.LoadFrom(@"C:MyProjectMyAPI.dll");
Type[] types = assembly.GetTypes();
foreach (Type type in types)
{
var interfaces = type.GetInterfaces();
var typeOfIResource = IResourceAssambly.GetType("MyProject.IResource");
if (interfaces.Any())
{
var interfaceType = interfaces[0]; //In debuger they have the same GUID
if (interfaceType.IsEquivalentTo(typeOfIResource)) //also tried ==
{
instanceOfDll = Activator.CreateInstance(type) as IResource;
return true;
}
}
}
}
catch (Exception e)
{
Console.Error.WriteLine("Failed to load dll {0}, exception: {1}",dllFullPath, e.Message);
return false;
}
return false;
}


Iv'e фактически использовал это сначала, что дало тот же результат:



List<Type> derivedTypesList = typses.Where(type => type.GetInterfaces().Contains(IWNAssambly.GetType("MyProject.IResource"))).ToList();
if (derivedTypesList.Count > 0)
{
instanceOfDll = (IResource)Activator.CreateInstance(derivedTypesList[0]);
return true;
}


Но потом я сломал его, так что я могу легко отладить его.

Когда я запускаю любой из этих фрагментов, я действительно нахожу 1 тип, который реализует интерфейс, но когда я пытаюсь привести его, я получаю null через оператор as и исключение при приведении с (IResource). То исключение составляет:



{System.InvalidCastException: Unable to cast object of type 'MyProject.MyFirstResource' to type 'MyProject.IResource'.


Ат ...



Проблема выглядела так, как будто она исходила от типов, поэтому я попытался заменить



var typeOfIResource = IResourceAssambly.GetType("MyProject.IResource");


С



var typeOfIResource = typeof(MyProject.IResource);


И в результате оказалось, что теперь он вообще ничего не нашел, то есть interfaceType.IsEquivalentTo(typeOfIResource) всегда ложно.
Когда я смотрел с отладчиком на эти типы, они выглядели точно так же, поэтому я не знаю, в чем проблема.

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

Второе и более важное на данный момент-что это за проблема и как я могу ее решить?

Спасибо!!!

691   2  

2 ответов:

Это возвращает воспоминания; я столкнулся с той же проблемой в самой первой программе .NET, которую я когда-либо написал, которая, должно быть, была пятнадцать лет назад.

Проблема заключается в том, что .NET имеет разные "контексты привязки", в которых тип может быть загружен, а "Load" и "LoadFrom" загружаются в разные контексты. Один и тот же тип в двух разных контекстах будет рассматриваться средой выполнения как разный, и вы не сможете выполнять приведение между ними.

Это довольно часто задаваемый вопрос в стеке Переполнение; если вы ищете эти термины, вы должны найти некоторые примеры объяснений и возможных решений. Например, вот это:

Создание объекта из динамически загружаемой сборки и приведение его к интерфейсу (.NET 2.0)

Кроме того, эта статья блога из первых дней .NET может помочь объяснить дизайн

Http://blogs.msdn.com/b/suzcook/archive/2003/09/19/loadfile-vs-loadfrom.aspx

Наконец, другой ответ верен; если вы глядя на создание системы плагинов, я рекомендую не делать это с нуля. Используйте MEF или MAF или другую систему, разработанную специально для решения вашей проблемы. Загрузка сборок может быть наименьшей из ваших забот; предположим, вы должны жить в мире, где сторонние плагины могут быть враждебными? Решить эту проблему безопасности сложно, поэтому пусть кто-то другой сделает это за вас.

Посмотрите систему.компонент, начиная с .NET 4 были интегрированы фреймворк MEF. MEF

Эта платформа позволяет помечать атрибутом [Export (typeof (interface))] каждую реализацию интерфейса независимо от библиотеки dll и загружать их с помощью системы каталогов. (Есть один для папки DirectoryCatalog

Comments

    Ничего не найдено.