приведение объекта к базовому интерфейсу - PullRequest
5 голосов
/ 18 августа 2010

У меня проблема с приведением объекта к одному из его базовых интерфейсов, живущих в другой библиотеке.Вот код для этого:

BaseSDK.dll

public interface IPlugin
{
    void Run();
}

CustomPlugin.Definition.dll:

public interface ICustomPlugin
{
    void DoCustomStuff();
}

CustomPlugin.dll (имеет ссылки на BaseSDK.dll и CustomPlugin.Definition.dll):

public class CustomPlugin: IPlugin, ICustomPlugin
{
    public void Run()
    {

    }

    public void DoCustomStuff()
    {

    }
}

Host.exe (имеет ссылки на BaseSDK.dll и CustomPlugin.Definition.dll):

IPlugin plugin;
public void DoStuff()
{
    plugin = LoadPluginAndCreateAnInstanceSomehow();
    // I know plugin is a CustomPlugin
    ICustomPlugin customPlugin = plugin as ICustomPlugin; //cast fails.
    customPlugin.DoCustomStuff();
}

Я не понимаю;это просто приведение типа к базовому типу.Как я могу это исправить?или какие-либо альтернативы?

Редактировать: Вот краткий обзор того, что LoadPluginAndCreateAnInstanceSomehow() делает:

Assembly ass = Assembly.LoadFrom(filename);
Type t = ass.GetType(ass.FullName + ".CustomPlugin");
plugin = (IPlugin)Activator.CreateInstance(t);

Редактировать 2: Плагины загруженыв другой домен приложения.Мне пришлось добавить IPlugin и ICustomPlugin.Definiton в качестве ссылки во время компиляции, потому что приложение должно иметь представление о том, что такое CustomPlugin.это источник проблемы?если да, то как мне добиться того, что я пытаюсь сделать?

Редактировать 3: Плагины загружаются так:

   public class PluginLoader
    {
        List<IPlugin> Plugins;
        AppDomain Domain;
        string libPath;
        public void PluginLoader(string sLibPath, AppDomain sDomain)
        {

            libPath = sLibPath;
            Plugins = new List<IPlugin>();
            Domain = sDomain;
            if(Domain==null)Domain = AppDomain.CreateDomain("PluginsDomain");
            Domain.AssemblyResolve += new ResolveEventHandler(Domain_AssemblyResolve);
            Domain.TypeResolve += new ResolveEventHandler(Domain_TypeResolve);
            Domain.DoCallBack(new CrossAppDomainDelegate(DomainCallback));
        }

        Assembly Domain_AssemblyResolve(object sender, ResolveEventArgs args)
        {
            return Assembly.LoadFrom(args.Name);
        }
        Assembly Domain_TypeResolve(object sender, ResolveEventArgs args)
        {
            return Assembly.LoadFrom(args.Name);
        }
        void DomainCallback()
        {
            string[] fileNames = Directory.GetFiles(libPath, "*.dll");
            foreach (string filename in fileNames)
            {

                FileInfo fi = new FileInfo(filename);
                Assembly ass = Assembly.LoadFrom(fi.FullName);
                Type t = ass.GetType(ass.FullName + ".CustomPlugin");
                IPlugin p = (IPlugin)Activator.CreateInstance(t);
                Plugins.Add(p);
            }
        }
    }

Ответы [ 3 ]

2 голосов
/ 18 августа 2010

Мне удалось воспроизвести эту проблему.Рассмотрим следующее.

Предположим, что ваш проект имеет следующую структуру времени выполнения (упрощенно, конечно)

C: \ Runtime \ - это ваш основной каталог времени выполнения и имеет Host.exe
C: \ Runtime \ Library \ - этот путь содержит три библиотечные сборки

Проект exe ссылается на две сборки, определяющие интерфейс, поэтому они копируются в папку времени выполнения, но не ссылаются на сборку, содержащую CustomPlugin., поэтому он не копируется.

Что, если старая версия проекта DID ссылалась на CustomPlugin.dll и копировала старую версию в \ Runtime?Затем вы разделили их.Новая версия больше не копируется, но старая версия остается.

Таким образом, когда вы создаете класс в AppDomain из \ Library, все в порядке, но при маршалинге через границу AppDomain первичный AppDomain нуждаетсязагрузить соответствующую сборку, чтобы получить информацию о типе.Сначала он находит старую версию.Если старая версия не реализует ICustomConfig, то приведение не будет выполнено.Ранее вы упоминали, что они не подписаны, поэтому .NET не будет иметь проблем с совершением этой ошибки за вас!

Таким образом, ваш каталог времени выполнения содержит

Host.exe
BaseSDK.dll
CustomPlugin.Definition.dll
CustomPlugin.dll - старая версия, не поддерживающая ICustomPlugin

, а каталог вашей библиотеки содержит

BaseSDK.dll
CustomPlugin.Definition.dll
CustomPlugin.dll - новая версия

РЕДАКТИРОВАТЬ

Вы всегда можете проверить значение plugin.GetType (). Assembly.Location, чтобы увидеть, какую DLL он загружает в каждом AppDomain.

1 голос
/ 18 августа 2010

Я не уверен, почему это происходит, но при использовании типа приведения типа «as» вы не получите никакой информации, когда приведение не удалось.

Документация MSDN гласит: «Оператор as похож на операцию приведения; однако, если преобразование невозможно, as возвращает ноль, а не вызывает исключение.»

Поэтому попробуйте использовать:

ICustomPlugin customPlugin = (ICustomPlugin)plugin;

и посмотрите, есть ли какая-то информация в исключении, которая может помочь объяснить, почему.

0 голосов
/ 18 августа 2010

Я подозреваю, что здесь есть два разных типа ICustomPlugin. Это возможно, например, если вы подписываете строгое имя, а CustomPlugin.dll загружает версию CustomPlugin.Definition.dll, отличную от загружаемой Host.exe.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...