【问题标题】:Getting a list of types in an assembly that inherit from a specific interface获取程序集中从特定接口继承的类型列表
【发布时间】:2013-11-27 21:19:01
【问题描述】:

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

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

我的目标:创建一个程序集中从特定接口继承的所有类型的列表。此程序集可能位于本地路径或 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()

【问题讨论】:

  • 对我来说,这看起来与您链接的问题完全相同。它看起来也正是您之前发布的被搁置的问题。这个问题有什么不同?
  • 虽然我同意这两种情况都非常接近,但这个问题有一个非常具体的点不同:如果也无法加载类型的依赖项,您仍然无法获得有关类型的任何信息。我真的不确定如何更清楚地说明这一点,或者我所链接的问题在哪里涉及到这方面。如果我在那里遗漏了什么,请指出。至于我的问题,我认为我必须以不同的方式提出问题并提供更多信息,所以我创建了这个问题。然后我意识到我可以只编辑上一个问题。

标签: c# reflection gettype


【解决方案1】:

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

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

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

我已经在这里暗示了一些缺点,所以让我们从这些开始。 因为我不只是查看元数据来告诉我主程序集中有哪些依赖项(换句话说,必须加载这些依赖项),如果在原始程序集中(在同一文件夹或子文件夹中)找不到依赖项的程序集,并且该依赖没有安装在本地 GAC 中,或者在项目中引用,GetTypes() 仍然会抛出 ReflectionTypeLoadException 异常。遇到依赖程序集不位于原始程序集或未安装在 GAC 中的情况似乎很少见,但我会以 PRISM 作为可能发生这种情况的示例。 PRISM 程序集不一定会安装到 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);
    }
}

【讨论】:

    【解决方案2】:

    要检查一个类型是否实现了一个特定的接口,你可以使用这个:

    typeof(yourInterface).IsAssignableFrom(type)
    

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

    【讨论】:

    • 由于 OP 明确指出他们的问题是无法加载相关类型,我不确定您的答案有什么价值。
    • 这可能有助于避免添加另一个需要引发 ReflectionOnlyAssemblyResolve 的实例,但仅此而已。
    猜你喜欢
    • 2019-01-31
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-05-14
    • 2022-01-04
    • 2014-03-05
    • 1970-01-01
    相关资源
    最近更新 更多