我经常发现自己想要在当前环境中迭代所有类型,原因有多种,例如查找从特定类继承的所有内容或查找具有特定属性的所有类型。
我可以使用System.AppDomain.CurrentDomain.GetAssemblies()
来获取所有程序集,然后在每个程序集上Assembly.GetTypes()
并迭代所有类型。但这是不必要的低效率;在包含单个脚本的项目的Unity编辑器中,此方法可以使用8590种类型,其中大部分都可能符合我无论如何都要搜索的标准。例如,mscorlib.dll或UnityEngine.dll中的任何内容都不会有我的自定义属性或从我的某个类继承,所以我应该跳过这些程序集。
所以现在,我试图找到所有引用任何给定程序集的程序集,但是我很难找到一个有效的算法来查找它们,因为我只能得到一个程序集数组目标引用,但不是引用目标的程序集数组。
另请注意,如果程序集A引用B和B引用C,则在搜索引用CI的所有内容时,需要同时获取A和B(如果B中的某些内容继承自类I' m正在寻找和某些内容在A中继承自该类而没有A直接引用C)。
答案 0 :(得分:1)
我从未使用过Unity 2.0,因此我将其编写为.NET framework 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, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null mscorlib, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 System.Xml, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 mscorlib, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 System, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 mscorlib, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 System.Configuration, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a mscorlib, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 System, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 System.Xml, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 System.Security, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a mscorlib, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 System, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 System.Xml, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 System.Xml, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 System.Configuration, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a System.Data.SqlXml, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 System, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 mscorlib, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 System.Xml, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
请注意,生成器输出一个具有循环的树,因此在递归函数上遍历它将是一个无限循环。在PrintTree中,我使用alreadyPrinted
列表来避免无限循环。 (我只打印一次子引用列表以避免循环。)YMMV,所以根据您的需要进行更改。
答案 1 :(得分:0)
Dependency Walker会为你做那个魔术。