查找引用特定程序集的所有其他程序集的最有效方法是什么?



由于各种原因,我经常想迭代当前环境中的所有类型,例如查找从特定类继承的所有类型或查找具有特定属性的所有类型。

我可以使用System.AppDomain.CurrentDomain.GetAssemblies()来获取所有程序集,然后在每个程序集上使用Assembly.GetTypes()并迭代所有类型。但这是不必要的低效;在一个包含单个脚本的项目的Unity编辑器中,该方法经过8590种类型,其中大多数都不可能满足我正在搜索的标准。例如,mscorlib.dll或UnityEngine.dll中的任何内容都不会有我的自定义属性或从我的某个类继承,所以我应该跳过这些程序集。

因此,现在,我正在尝试查找引用任何给定程序集的所有程序集,但我很难找到有效的算法来查找它们,因为我只能获得目标引用的程序集数组,而不能获得引用目标的程序集的数组。

还要注意,如果程序集A引用B,而B引用C,那么在搜索引用C的所有内容时,我需要同时获得A和B(以防B中的某些内容从我要查找的类继承,而A中的某些东西从该类继承,但A没有直接引用C)。

我从未使用过Unity 2.0,所以我为它编写了代码。NET框架2.0,希望它能正常工作。

首先,您可能想要创建程序集的树表示:

class DependencyTree
{
    public string AssemblyName;
    public IDictionary<string,DependencyTree> ReferencedAssemblies;
}

现在让我们创建一个类来遍历并生成树

class DependencyWalker
{
    Dictionary<string, DependencyTree> _alreadyProcessed = new Dictionary<string, DependencyTree>();
    public DependencyTree GetDependencyTree(Assembly assembly)
    {
        // Avoid procesing twice same assembly.
        if (_alreadyProcessed.ContainsKey(assembly.FullName)) 
            return _alreadyProcessed[assembly.FullName]; 
        var item = new DependencyTree();
        item.AssemblyName = assembly.FullName;
        item.ReferencedAssemblies = new Dictionary<string, DependencyTree>();
        _alreadyProcessed.Add(item.AssemblyName, item);
        foreach (AssemblyName assemblyName in assembly.GetReferencedAssemblies())
        {
            item.ReferencedAssemblies.Add(assemblyName.FullName, GetDependencyTree(Assembly.Load(assemblyName)));
        }
        return item;
    }
    // To print the tree to the console:
    public void PrintTree(DependencyTree tree)
    {
        PrintTree(tree, 0, new Dictionary<string, bool>()); // Using Dictionary because HashSet is not available on .NET 2.0
    }
    private void PrintTree(DependencyTree tree, int indentationLevel, IDictionary<string,bool> alreadyPrinted)
    {
        Console.WriteLine(new string(' ', indentationLevel) + tree.AssemblyName);
        if (alreadyPrinted.ContainsKey(tree.AssemblyName))
            return;
        alreadyPrinted[tree.AssemblyName] = true;
        foreach (DependencyTree a in tree.ReferencedAssemblies.Values)
            PrintTree(a, indentationLevel + 3, alreadyPrinted);
    }
}

现在你可以很容易地使用这个类:

class Program
{
    static void Main(string[] args)
    {
        new System.Xml.XmlDocument().LoadXml("<xml/>"); // Do whatever to ensure System.Xml assembly is referenced.
        var startingAssembly = typeof(Program).Assembly;
        var walker = new DependencyWalker();
        var tree = walker.GetDependencyTree(startingAssembly);
        walker.PrintTree(tree);
    }
}

哪个输出;

ConsoleApplication1,版本=1.0.0.0,区域性=中性,PublicKeyToken=nullmscorlib,版本=2.0.0.0,区域性=中性,PublicKeyToken=b77a5c561934e089系统Xml,版本=2.0.0.0,区域性=中性,PublicKeyToken=b77a5c561934e089mscorlib,版本=2.0.0.0,区域性=中性,PublicKeyToken=b77a5c561934e089系统,版本=2.0.0.0,区域性=中性,PublicKeyToken=b77a5c561934e089mscorlib,版本=2.0.0.0,区域性=中性,PublicKeyToken=b77a5c561934e089系统配置,版本=2.0.0.0,区域性=中性,PublicKeyToken=b03f5f7f11d50a3amscorlib,版本=2.0.0.0,区域性=中性,PublicKeyToken=b77a5c561934e089系统,版本=2.0.0.0,区域性=中性,PublicKeyToken=b77a5c561934e089系统Xml,版本=2.0.0.0,区域性=中性,PublicKeyToken=b77a5c561934e089系统安全性,版本=2.0.0.0,区域性=中性,PublicKeyToken=b03f5f7f11d50a3amscorlib,版本=2.0.0.0,区域性=中性,PublicKeyToken=b77a5c561934e089系统,版本=2.0.0.0,区域性=中性,PublicKeyToken=b77a5c561934e089系统Xml,版本=2.0.0.0,区域性=中性,PublicKeyToken=b77a5c561934e089系统Xml,版本=2.0.0.0,区域性=中性,PublicKeyToken=b77a5c561934e089系统配置,版本=2.0.0.0,区域性=中性,PublicKeyToken=b03f5f7f11d50a3a系统数据SqlXml,版本=2.0.0.0,区域性=中性,PublicKeyToken=b77a5c561934e089系统,版本=2.0.0.0,区域性=中性,PublicKeyToken=b77a5c561934e089mscorlib,版本=2.0.0.0,区域性=中性,PublicKeyToken=b77a5c561934e089系统Xml,版本=2.0.0.0,区域性=中性,PublicKeyToken=b77a5c561934e089

请注意,生成器输出的树有循环,因此在递归函数上遍历它将是一个无限循环。在PrintTree中,我使用alreadyPrinted列表来避免无限循环。(为了避免循环,我只打印一次子引用列表。)YMMV,所以根据您的需要进行更改。

Dependency Walker将为您施展魔法。

相关内容

  • 没有找到相关文章

最新更新