65.9K
CodeProject 正在变化。 阅读更多。
Home

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

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.08/5 (6投票s)

2007年4月4日

CPOL

3分钟阅读

viewsIcon

28568

downloadIcon

467

一个命令行实用程序,用于分析您的程序集并生成一个 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 指令,查找 MethodReferenceFieldReference 类型的操作数。但是,MethodDefinitionFieldDefinition 派生自 MethodReferenceFieldReference,而 XXXDefinition 类型的操作数表示特定的方法/字段位于此程序集中。我们正在寻找当前程序集之外的引用,因此 ProcessMethod 显式检查它们是否为 XXXReference 类型,而不是 XXXDefinition 类型。

其余代码只是将结果按程序集和类型分组的管道。还有代码按调用方法对引用进行分组,但目前未使用该数据。如果提供了包含和排除列表,TypeFilter 是一个巧妙的小类,负责基于包含和排除列表进行过滤,并且它使用简单的字符串匹配来完成其工作。

结论

这是我第一次接触 Mono 项目,我必须说我对 Cecil [^] 印象深刻。弄清楚如何使用它非常容易,而且它似乎很好地处理了 .NET 2.0 的东西,比如泛型。无论如何,我编写这个工具时玩得很开心,我希望它能证明是对 CP 免费工具库的一个有用的补充。欢迎评论、建议(和错误报告:))。

历史

  • 初始版本 - 2007 年 4 月 4 日上午 11:23
  • 更新 - 2007 年 4 月 5 日上午 8:52
    • 修复了阻止正确过滤内部类和泛型类型的错误
    • 添加了用于类型名称的通配符匹配
© . All rights reserved.