C# - 使用反射加载程序集时不同的类型

3
我有一个包含3个项目的解决方案: 1)GUI可执行文件 2)包含公共API和公共接口的类库 3)实现上述接口的类的类库
我正在尝试在API中实现资源加载器,以便当GUI调用方法 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:\MyProject\MyAPI.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;
}

实际上,我一开始就使用了这个,结果相同:

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种类型实现了该接口,但是当我尝试使用as运算符进行强制转换时,会得到null值,并且在使用(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)总是为false。当我用调试器查看这些类型时,它们看起来完全一样,所以我不知道问题出在哪里。

首先,这是一个好的实践吗?我希望其他开发者向我提供他们的程序集,如果他们实现了IResource接口,则使用反射创建一个实例并调用所需的方法。

其次,在此时更重要的是,问题出在哪里,我该如何解决?

谢谢!!!


1
你为什么不直接用 MEF 呢? - BradleyDotNET
我不熟悉MEF。我们想要这种设计的原因是可扩展性。将来添加的程序集将是我们自己的,它们代表了计算相同事物的不同方式,当所有方式都返回答案后,我们以某种方式混合结果。 - ZivS
2
好的,这里有一个愚蠢的问题。他们实际上是引用相同的接口(在某个公共程序集中),还是只是拥有文件的副本?不过我会研究MEF,它更容易些。 - BradleyDotNET
我会查看一下,谢谢。它们都在同一个解决方案下,从解决方案内部引用,但我认为它们使用了本地副本 :/ 待会儿会更新。 - ZivS
这有点尴尬...我确实打开了本地复制标志 :| @BradleyDotNet,谢谢你的愚蠢问题 :P - ZivS
2个回答

6
这让我想起了往事;我在写的第一个.NET程序中遇到了同样的问题,那应该是15年前的事了。
问题在于.NET有不同的“绑定上下文”,类型可以在其中加载,“Load”和“LoadFrom”会加载到不同的上下文中。两个不同上下文中的“相同”类型将被运行时视为不同,并且无法在它们之间进行转换。
这是Stack Overflow上经常被问到的问题;如果您搜索这些术语,应该能找到一些解释和可能的解决方案。例如,这个: 从动态加载的程序集创建对象并将其转换为接口(.NET 2.0) 此外,这篇早期的.NET博客文章可能有助于解释设计。

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

最后,另一个答案是正确的;如果您想要构建一个插件系统,我建议不要从头开始。使用MEF或MAF或其他专门设计用于解决您的问题的系统。加载程序集可能是您最不用担心的事情;假设您必须生活在第三方插件可能具有敌意的世界中?解决这个安全问题很困难,所以让别人为您解决它。


1

看一下 system.component,自从 .NET 4 开始,框架 MEF 已经被集成了。MEF

这个框架允许你在接口的每个实现上标记一个属性 [Export(typeof(interface))],而不管 dll,并使用目录系统加载它们。 (有一个用于文件夹的 DirectoryCatalog)


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接