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

c# - Different types when loading from assembly using reflection

我有一个包含 3 个项目的解决方案: 1) GUI 可执行文件 2) 一个包含 public API 和 public 接口的 Class 库 3) 实现上述接口

的class的Class库

我正在尝试在 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;
}

但后来我把它分解了,所以我可以很容易地调试它。

当我 运行 这些片段中的任何一个时,我确实找到了一种实现接口的类型,但是当我尝试转换它时,我通过 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) 总是错误的。 当我在这些类型上查看调试器时,它们看起来完全一样,所以我不知道是什么问题。

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

此时第二个更重要的是,问题是什么,我该如何解决?

谢谢!!!

看system.component,自从.NET 4 框架MEF 已经集成。 MEF

此框架允许您在接口的每个实现上通过属性 [Export(typeof(interface))] 进行标记,而不管 dll 是什么,并使用目录系统加载它们。 (文件夹 DirectoryCatalog

有一个

这勾起了我的回忆;我 运行 在我编写的第一个 .NET 程序中遇到了同样的问题,那一定是十五年前的事了。

问题在于 .NET 有不同的 "binding contexts" 类型可以加载到其中,并且 "Load" 和 "LoadFrom" 加载到不同的上下文中。两个不同上下文中的 "same" 类型将被运行时视为不同,并且您不能在它们之间转换。

这是 Stack Overflow 上一个相当常见的问题;如果您搜索这些术语,您应该会找到一些解释示例和可能的解决方案。比如这个:

Create object from dynamically load assembly and cast it to interface (.NET 2.0)

此外,这篇来自 .NET 早期的博客文章可能有助于解释设计

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

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