c# - 使用反射从装配加载时的不同类型

Ziv*_*ivS 3 .net c# reflection .net-assembly

我有一个包含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;
}
Run Code Online (Sandbox Code Playgroud)

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

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

但后来我将其分解,以便我可以轻松调试.

当我运行任何这些片段时,我确实找到了一种实现接口的类型,但是当我尝试as强制转换它时,我通过运算符获取null,并在使用时获取异常(IResource).例外是:

{System.InvalidCastException: Unable to cast object of type 'MyProject.MyFirstResource' to type 'MyProject.IResource'.
Run Code Online (Sandbox Code Playgroud)

在 ...

问题看起来像是来自类型,所以我尝试更换

var typeOfIResource = IResourceAssambly.GetType("MyProject.IResource");
Run Code Online (Sandbox Code Playgroud)

var typeOfIResource = typeof(MyProject.IResource);
Run Code Online (Sandbox Code Playgroud)

结果是现在它根本找不到任何东西,即interfaceType.IsEquivalentTo(typeOfIResource)总是假的.当我用调试器查看这些类型时,它们看起来完全一样,所以我不知道是什么问题.

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

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

谢谢!!!

Eri*_*ert 6

这带回了回忆; 我在我编写的第一个.NET程序中遇到了同样的问题,这个程序一定是十五年前的.

问题是.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或专门设计的其他系统来解决您的问题.装卸组件可能是您最不担心的; 假设您必须生活在第三方插件可能存在敌意的世界中?解决安全问题很困难,所以让别人为你做.