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

时间:2015-01-21 00:46:23

标签: c# .net reflection .net-assembly

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

我试图在API中实现资源加载器,这样当GUI调用方法API.Foo()时,我会遍历特定文件夹(位于:。\ resources)中的每个程序集,其中包含< strong>我编译的程序集的副本(#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;
}

Iv&#39; e实际上最初使用了这个,结果相同:

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'.

at ...

问题看起来像来自类型所以我试图替换

var typeOfIResource = IResourceAssambly.GetType("MyProject.IResource");

var typeOfIResource = typeof(MyProject.IResource);

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

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

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

感谢!!!

2 个答案:

答案 0 :(得分:6)

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

问题在于.NET具有不同的“绑定上下文”,其中可以加载类型,并且“Load”和“LoadFrom”加载到不同的上下文中。两个不同上下文中的“相同”类型将被运行时视为不同,并且您无法在它们之间进行转换。

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

答案 1 :(得分:1)

查看system.component,因为.NET 4已经集成了框架MEF。 MEF

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

有一个