将对象转换为基接口

Sla*_*oph 5 c# inheritance plugins casting interface

我有一个问题,将对象转换为生成在另一个库中的其中一个基本接口.这是它的代码:

BaseSDK.dll

public interface IPlugin
{
    void Run();
}
Run Code Online (Sandbox Code Playgroud)

CustomPlugin.Definition.dll:

public interface ICustomPlugin
{
    void DoCustomStuff();
}
Run Code Online (Sandbox Code Playgroud)

CustomPlugin.dll(引用BaseSDK.dll和CustomPlugin.Definition.dll):

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

    }

    public void DoCustomStuff()
    {

    }
}
Run Code Online (Sandbox Code Playgroud)

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();
}
Run Code Online (Sandbox Code Playgroud)

我不明白; 这只是普通的类型转换类型到它的基类型.我怎样才能解决这个问题?还是其他选择?

编辑:这是一个总结LoadPluginAndCreateAnInstanceSomehow():

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

编辑2:插件加载到另一个AppDomain中.我必须在编译时添加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);
            }
        }
    }
Run Code Online (Sandbox Code Playgroud)

Sam*_*Sam 2

我设法重现了这个问题。考虑以下。

假设您的项目具有以下运行时结构(当然是简化的)

C:\Runtime\ - 这是您的主运行时目录,并包含 Host.exe
C:\Runtime\Library\ - 此路径包含您的三个库程序集

exe项目引用了定义接口的两个程序集,因此它们被复制到运行时文件夹中,但没有引用包含CustomPlugin的程序集,因此不被复制。

如果项目的旧版本确实引用了 CustomPlugin.dll 并将旧版本复制到 \Runtime 会怎样?后来你把它们分开了。新版本不再复制,但旧版本保留。

因此,当您从 \Library 在 AppDomain 中创建类时,一切正常,但是当在 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 的值,以查看它在每个AppDomain 中加载哪个dll。