获取从特定接口继承的程序集中的类型列表

时间:2013-11-27 21:19:01

标签: c# reflection gettype

请在标记为重复,已经回答,暂停或偏离主题之前阅读。

首先,我知道这里有类似的问题,答案非常好。不幸的是他们没有回答我的问题。

我的目标:创建一个从特定接口继承的程序集中的所有类型的列表。该程序集可以位于本地路径或UNC路径上。

我的问题:首先,我知道如何从UNC和本地路径加载程序集,我知道如何使用GetTypes()。你们中的许多人可能知道GetTypes()有它自己的问题。具体来说,如果任何类型继承自另一个无法加载的类型,它将引发异常。例如How to prevent ReflectionTypeLoadException when calling Assembly.GetTypes()。问题是,我不需要加载任何依赖项。我只需要查看程序集中声明的类型是否继承自其他特定类型。

一些代码作为例子。这只是检索程序集中类型的第一步:

Assembly asm = Assembly.Load(File.ReadAllBytes(assemblyPath));

Type[] myTypes = GetLoadableTypes(asm);

public static Type[] GetLoadableTypes(Assembly assembly)
{
    // TODO: Argument validation
    try
    {
        return assembly.GetTypes();
    }
    catch (ReflectionTypeLoadException e)
    {
        return e.Types.Where(t => t != null);
    }
}

如果程序集内部的任何类型继承自无法加载的类型,则抛出ReflectionTypeLoadException异常,它们将在e.Types中显示为null,因此仍然没有关于它们的信息。所以我想我还是不能使用GetTypes。

为了完整起见,这里是对我原始问题的引用。 How to get Type information when ReflectionTypeLoadException is thrown from Assembly.GetType()

2 个答案:

答案 0 :(得分:0)

好的,在这方面取得了一些进展,除非我想编写自己的元数据解析器,看起来这就是我所困扰的所以我想我会分享。我会尝试给出利弊。

这个答案都围绕着使用事件处理程序 AppDomain.CurrentDomain.ReflectionOnlyAssemblyResolve 事件。应该指出的是,只有在解决程序集的依赖关系时才会引发此事件。我一开始并不明白,但它只是意味着事件不是为你试图加载的程序集触发的,而是该程序集中的任何依赖项,这是有道理的。当你必须首先有一个起点时,为什么要举起那个事件。对于我的解决方案,当我调用.GetTypes()时会引发此事件。

在我发布代码之前,让我解释一下这个想法以及它的优缺点。我的想法是,如果我有一个依赖项,那么将引发该事件。在这种情况下,我将首先检查在原始程序集中在同一文件夹(包括子文件夹)中找到的所有程序集之前创建的字典。如果在该字典中找到了args.Name,那么我读入该程序集并从事件中返回它。如果它不在列表中,我尝试仅按名称加载程序集,这意味着依赖程序集必须是项目的一部分或安装在本地GAC中。

我已经在这里暗示了一些缺点,所以让我们从这些开始。 因为我不只是查看元数据来告诉我主程序集中的依赖项(换句话说必须加载那些依赖项),如果无法在原始程序集中找到依赖项的程序集(在相同的文件夹或子文件夹中),并且依赖项未安装在本地GAC中,或者在项目中引用,GetTypes()仍将抛出ReflectionTypeLoadException异常。对于依赖程序集不在原始程序集中或安装在GAC中的情况而言,这似乎是一种罕见的情况,但我会将PRISM作为可能发生的情况的示例。 PRISMs程序集不一定安装到GAC中,并不总是在本地复制。

那么这种方法有什么好处呢?好吧,主要是它为你提供了至少一些方法来处理99%没有依赖程序集合的情况。

在代码之前的一两个最终想法(实际上是陷阱)。我使用AssemblyName创建一个基于程序集全名的字典,而不实际加载(直接或通过ReflectionOnlyLoad)程序集。有两种方法可以创建AssemblyName实例,可以通过构造函数传递给程序集的路径,也可以使用静态方法GetAssemblyName(path)。构造函数似乎只处理GetAssemblyName()可以处理UNC路径的本地路径。这些都是陷阱:不要仅仅为了获取名称而加载程序集,并确保不限制自己局限于本地路径。

最后,一些代码:

public class TestClass
{
    //This dictionary will hold a list of the full path for an assembly, indexed by the assembly's full name
    private Dictionary<string, string> _allAsms = new Dictionary<string, string>();

    /// <summary>
    /// Tries to list all of the Types inside an assembly and any interfaces those types inherit from.
    /// </summary>
    /// <param name="pathName">The path of the original assembly, without the assembly file</param>
    /// <param name="fileName">The name of the assembly file as it is found on disk.</param>
    public void Search(string pathName, string fileName)
    {

        AppDomain.CurrentDomain.ReflectionOnlyAssemblyResolve += new ResolveEventHandler(CurrentDomain_ReflectionOnlyAssemblyResolve);

        //Getting a list of all possible assembly files that exist in the same location as the original assembly
        string[] filePaths = Directory.GetFiles(pathName, "*.dll", SearchOption.AllDirectories);
        Assembly asm;
        AssemblyName name;

        if (!pathName.EndsWith("\\"))
            pathName += "\\";

        foreach (string path in filePaths)
        {
            name = AssemblyName.GetAssemblyName(path);

            if (!_allAsms.ContainsKey(name.FullName))
                _allAsms.Add(name.FullName, path);
        }

        //This is where we are loading the originaly assembly
        asm = System.Reflection.Assembly.ReflectionOnlyLoad(File.ReadAllBytes(pathName + fileName));

        Console.WriteLine("Opened assembly:{0}", fileName);

        //And this is where the ReflectionOnlyAssemblyResolve will start to be raised
        foreach (Type t in asm.GetTypes())
        {
            Console.WriteLine("  " + t.FullName);
            //Get the interfaces for the type;
            foreach (Type dep in t.GetInterfaces())
            {
                Console.WriteLine("    " + dep.FullName);
            }
        }
    }

    private Assembly CurrentDomain_ReflectionOnlyAssemblyResolve(object sender, ResolveEventArgs args)
    {
        if (_allAsms.ContainsKey(args.Name))
            return Assembly.ReflectionOnlyLoad(File.ReadAllBytes(_allAsms[args.Name]));
        else
            return System.Reflection.Assembly.ReflectionOnlyLoad(args.Name);
    }
}

答案 1 :(得分:-1)

要检查某个类型是否实现了特定的接口,您可以使用它:

typeof(yourInterface).IsAssignableFrom(type)

但是,如果无法加载类型,则没有可用的信息,您也无法确定它是否实现了特定的接口。