FindRefs - 查找源于您的程序集的 Method、Property 和 Field 引用






4.08/5 (6投票s)
一个命令行实用程序,用于分析您的程序集并生成一个 XML 文件,其中包含引用的方法、字段和属性,按程序集和类型分组
引言
本文源于需要找出程序集通过其引用访问的方法、字段和属性。虽然 Visual Studio 显示程序集引用的程序集列表,但它没有显示实际引用的成员。 FindRefs 是一个简单的工具,它使用 Cecil [^] 作为后端来分析程序集中的所有方法和属性,并列出它在引用程序集中类型上进行的调用的方法、字段和属性引用。
背景
编写该工具的动机是为了弄清楚程序集的逻辑依赖关系,这样重构或重新设计其引用的程序集就不会破坏它。仅仅是引用的程序集列表显然是不够的,而且我找不到任何工具,除了 Reflector,它能给我实际引用的方法、属性和字段的列表。但是,我找不到从 Reflector 导出输出的方法,所以我决定自己动手。此外,我刚发现了 Cecil,我认为这将是一个很好的机会来玩一下。
使用 FindRefs
使用 FindRefs 非常简单。 FindRefs 是一个命令行应用程序,它将要分析的程序集作为参数。您可以使用 -include
和 -exclude
标志选择性地过滤要考虑的类型(用于输出)。命令行语法是
findrefs <assembly_path> [-include filterlist] [-exclude filterlist]</assembly_path>
filterlist
是完全限定类型名称的列表。它还可以包含带有星号 (*) 的命名空间,以指示命名空间中的所有类型,就像我们在 using
C# 语句中所做的那样。它还支持使用 * 作为通配符来匹配类型名称。用法示例
// excludes all types in all namespaces under System
findrefs blah.exe -exclude System.*
// excludes all types in all namespaces/types under System except for System.Console
findrefs blah.exe -include System.Console -exclude System.*
// excludes everything except System.Console
findrefs blah.exe -include System.Console -exclude *
// excludes all types except those whose fully qualified names start with Sys
findrefs blah.exe -include Sys* -exclude *
输出将被写入 blah.xml(程序集名称,不带扩展名 + .xml),并且它在当前目录中生成。不指定 -exclude
不会排除任何内容,因此 -include
仅在与 -exclude
结合使用时才有意义。
示例输出
对于一个简单的程序,例如
class Program
{
static void Main(string[] args)
{
Console.WriteLine("Hello");
}
}
在已编译的程序集上运行 findrefs 会生成以下输出
<?xml version="1.0" encoding="utf-8"?>
<References>
<Assembly name="mscorlib, Version=2.0.0.0, Culture=neutral,
PublicKeyToken=b77a5c561934e089">
<Type name="System.Console">
<Reference name="System.Void System.Console::WriteLine(System.String)" />
</Type>
<Type name="System.Object">
<Reference name="System.Void System.Object::.ctor()" />
</Type>
</Assembly>
</References>
工作原理
Cecil 库使遍历程序集中的所有方法变得非常简单。您加载程序集并使用 AssemblyFactory.GetAssembly(assemblyPath)
获取对 AssemblyDefinition
的引用,然后使用 AssemblyDefinition
对象递归地遍历模块、类型和方法。以下代码段显示了如何遍历程序集中的所有方法
public static void FindReferences(string assemblyPath, TypeFilter typeFilter)
{
AssemblyDefinition assemblyDefinition = AssemblyFactory.GetAssembly(assemblyPath);
foreach (ModuleDefinition moduleDefinition in assemblyDefinition.Modules)
{
Console.WriteLine("Processing module [" + moduleDefinition.Name + "]");
foreach (TypeDefinition typeDefinition in moduleDefinition.Types)
{
ProcessType(typeDefinition, typeFilter);
}
}
}
static void ProcessType(TypeDefinition typeDefinition, TypeFilter typeFilter)
{
Console.WriteLine("Processing type [" + typeDefinition.FullName + "]");
foreach (MethodDefinition methodDefinition in typeDefinition.Methods)
{
ProcessMethod(methodDefinition, typeFilter);
}
foreach (MethodDefinition methodDefintion in typeDefinition.Constructors)
{
ProcessMethod(methodDefintion, typeFilter);
}
foreach (PropertyDefinition propertyDefintion in typeDefinition.Properties)
{
ProcessMethod(propertyDefintion.GetMethod, typeFilter);
ProcessMethod(propertyDefintion.SetMethod, typeFilter);
}
}
ProcessMethod
遍历方法的正文中的 IL 指令,查找 MethodReference
或 FieldReference
类型的操作数。但是,MethodDefinition
和 FieldDefinition
派生自 MethodReference
和 FieldReference
,而 XXXDefinition
类型的操作数表示特定的方法/字段位于此程序集中。我们正在寻找当前程序集之外的引用,因此 ProcessMethod
显式检查它们是否为 XXXReference 类型,而不是 XXXDefinition 类型。
其余代码只是将结果按程序集和类型分组的管道。还有代码按调用方法对引用进行分组,但目前未使用该数据。如果提供了包含和排除列表,TypeFilter
是一个巧妙的小类,负责基于包含和排除列表进行过滤,并且它使用简单的字符串匹配来完成其工作。
结论
这是我第一次接触 Mono 项目,我必须说我对 Cecil [^] 印象深刻。弄清楚如何使用它非常容易,而且它似乎很好地处理了 .NET 2.0 的东西,比如泛型。无论如何,我编写这个工具时玩得很开心,我希望它能证明是对 CP 免费工具库的一个有用的补充。欢迎评论、建议(和错误报告:))。
历史
- 初始版本 - 2007 年 4 月 4 日上午 11:23
- 更新 - 2007 年 4 月 5 日上午 8:52
- 修复了阻止正确过滤内部类和泛型类型的错误
- 添加了用于类型名称的通配符匹配