将对象转换为基接口

时间:2010-08-17 23:42:13

标签: c# inheritance plugins interface casting

我在将对象转换为其中一个生成在另一个库中的基本接口时遇到了问题。这是代码:

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:插件已加载到另一个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);
            }
        }
    }

3 个答案:

答案 0 :(得分:2)

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

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

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

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

如果项目的旧版本DID引用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。

答案 1 :(得分:1)

我不确定为什么会发生这种情况,但是当使用'as'类型的投射时,你不会在投射失败时得到任何信息。

MSDN文档说: “as运算符类似于强制转换操作;但是,如果无法进行转换,则返回null而不是引发异常。”

所以尝试使用:

ICustomPlugin customPlugin = (ICustomPlugin)plugin;

并查看异常中是否有某些信息可以帮助解释原因。

答案 2 :(得分:0)

我怀疑这里有两种不同的ICustomPlugin类型。例如,如果您正在进行强名称签名,并且CustomPlugin.dll正在加载与Host.exe正在加载的不同版本的CustomPlugin.Definition.dll,则这是可能的。