【问题标题】:C# reflection and finding all referencesC# 反射和查找所有引用
【发布时间】:2011-07-26 06:58:55
【问题描述】:

给定一个 DLL 文件,我希望能够在该 DLL 文件中找到对该方法的所有调用。我该怎么做?

基本上,我怎样才能以编程方式完成 Visual Studio 已经完成的工作?

我不想使用.NET Reflector 之类的工具来执行此操作,但反射很好并且可能是必要的。

【问题讨论】:

  • 是否只需要检测对方法的静态调用?是否也应该检测到具有反射和反射发射的调用?
  • 这应该适用于非托管代码吗?并非所有 DLL 都支持反射。
  • @Darin:这些只是锦上添花,但没有必要。 @James:我认为我不必担心非托管代码。我怎么能在不支持反射的 dll 上做到这一点?

标签: c# reflection reference reflector


【解决方案1】:

要找出使用方法MyClass.Foo() 的位置,您必须分析引用包含MyClass 的程序集的所有程序集的所有类。我写了一个简单的概念证明,说明这段代码的外观。在我的示例中,我使用了 Jb Evain 编写的 this library(只是 a single .cs file):

我写了一个小测试类来分析:

public class TestClass
{
    public void Test()
    {
        Console.WriteLine("Test");
        Console.Write(10);
        DateTime date = DateTime.Now;
        Console.WriteLine(date);
    }
}

我写了这段代码来打印出TestClass.Test()中使用的所有方法:

MethodBase methodBase = typeof(TestClass).GetMethod("Test");
var instructions = MethodBodyReader.GetInstructions(methodBase);

foreach (Instruction instruction in instructions)
{
    MethodInfo methodInfo = instruction.Operand as MethodInfo;

    if(methodInfo != null)
    {
        Type type = methodInfo.DeclaringType;
        ParameterInfo[] parameters = methodInfo.GetParameters();

        Console.WriteLine("{0}.{1}({2});",
            type.FullName,
            methodInfo.Name,
            String.Join(", ", parameters.Select(p => p.ParameterType.FullName + " " + p.Name).ToArray())
        );
    }
}

它给了我以下输出:

System.Console.WriteLine(System.String value);
System.Console.Write(System.Int32 value);
System.DateTime.get_Now();
System.Console.WriteLine(System.Object value);

这个例子显然还远未完成,因为它不处理 ref 和 out 参数,也不处理泛型参数。我敢肯定,我也忘记了其他细节。它只是表明它可以做到。

【讨论】:

  • 不错。这也将 Linq 用于 select 语句。是时候为我构建一些依赖图了。
  • 我收到此错误消息:“值不在预期范围内。”从这段代码:this.body = method.GetMethodBody(); if (this.body == null) throw new ArgumentException();,我想知道怎么可能有无主体的方法(或者还有什么可能发生的)。有什么想法吗?
  • @ekkis:(抽象类的)抽象方法是无体的。您可以通过检查MethodBase.IsAbstract来检查方法是否抽象。
  • @ElianEbbing,显然还有其他种类的无形方法。看看System.GetType() - 我不确定这个有什么特别之处,但.GetMethodBody() 也出现了空值。我想知道我们是否应该在这些情况下抛出 ArgumentExceptions 或者只是默默地失败
  • Nuget 替代 MethodBodyReader:nuget 包 mono.reflectionvar instructions = Disassembler.GetInstructions(methodBase);
【解决方案2】:

您可以查看 MSDN 杂志文章 Determining .NET Assembly and Method References

【讨论】:

  • +1:请注意,您必须在某个地方搜索 IL 阅读器 - 据我所知,Reflector 的阅读器并不直接可用...
  • 好文章。但它建议对方法依赖项使用反射器。我猜他们都归结为解析 IL。
【解决方案3】:

仅靠反射不足以在给定程序集中找到对方法的所有引用。反射为您提供任何特定方法的主体的字节数组 (MethodInfo.GetMethodBody.GetILAsByteArray),您必须自己解析它以引用其他方法。有几个公开可用的“CIL reader”库(我没有使用它们 - 希望有人会在上面发布更多信息)。

添加FxCop 选项 - 根据您的场景,您可以重用 FxCop(Visual Studio 代码分析)提供的 CIL 解析逻辑,并添加您的自定义规则(如果您可以将其作为代码分析的一部分运行)。

【讨论】:

    【解决方案4】:

    我会考虑反映 Visual Studio 程序集,看看您是否可以在反向工程代码库中找到它。我相信 VS 实际上是在导航代码而不是反映。正如迈克尔所发布的,反射非常适合确定装配的位,但不能确定这些位的消费者。但是,我没有重新检查反射来证实我的怀疑。

    【讨论】:

      【解决方案5】:

      嘿,这是一个假设您要搜索当前程序集中的所有调用的示例。我对此进行了编码,试图获取一个参数值,以便为具有一些默认值的方法设置一些约束。但我无法获取参数值,我只有类型和默认值。

      var currentAssembly = Assembly.GetExecutingAssembly();
      
      foreach (var method in currentAssembly.GetTypes().Where(type => type.IsClass)
                                            .SelectMany(cl => cl.GetMethods())
                                            .OfType<MethodBase>()
                                            .SelectMany(mb => mb.GetInstructions())
                                            .Select(inst => inst.Operand).OfType<MethodInfo>()
                                            .Where(mi => mi.Name == "<YourMethodName>"))
      {
          //here are your calls.
      }
      

      希望对你有帮助。

      【讨论】:

        【解决方案6】:

        查看堆栈溢出问题Get a list of functions for a DLL

        从上面摘录(感谢 Jon Skeet):

        对于特定的程序集,您可以使用 Assembly.GetTypes 来获取类型,然后为每种类型调用 Type.GetMethods()、Type.GetProperties() 等,或者只使用 Type.GetMembers()。

        但是,对于插件功能来说,拥有一个插件必须实现的通用接口通常是个好主意——这可以减少您需要使用的反射量。使用 Type.IsAssignableFrom() 检查类型是否与特定接口兼容。

        您可能还想查看 Managed Extensibility Framework,它可以更轻松地实现扩展系统。

        【讨论】:

        • 我看不出这个答案如何有助于查找对给定方法的调用。它有助于列出目标程序集中的类型、方法、属性等,但 OP 正在寻找一种方法来检测给定方法是否正在调用另一个方法。
        • 这不是 OP 所要求的。
        • 这不是 OP 想要的。这列出了方法,但没有列出它们的调用。
        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2010-11-23
        • 2010-12-05
        相关资源
        最近更新 更多