【问题标题】:Can I use reflection to inspect the code in a method?我可以使用反射来检查方法中的代码吗?
【发布时间】:2011-02-11 05:46:41
【问题描述】:

我正在使用 C# 反射 API。我可以在程序集中轻松加载Type 类、方法等信息,但是现在我想知道如何加载和读取方法内的代码?

【问题讨论】:

  • 在什么场景下使它有用?它打破了 OO 的基本租户之一,即封装。但更何况作为 IL 的代码并不完全相同。
  • @DevelopingChris:如果您正在编写调试器或代码分析工具,那么能够加载程序集并分析方法体可能会很有用。事实上,这就是 FxCop 等工具的作用。
  • .NET 的目标受众是美国公司(其次可能是一些小企业)。如果您可以绕过 .NET 混淆,那么(生产成本高昂)软件很容易飞入竞争对手的指尖。

标签: c# reflection


【解决方案1】:

基本答案:

您不能使用反射 API (System.Reflection)。

原因是反射 api 旨在处理元数据(类的类型、方法的名称和签名……),而不是数据级别(即 IL 流本身)。

扩展答案:

您可以使用 System.Reflection.Emit(例如 ILGenerator 类)发出(但不能读取)IL。

通过MethodInfo.GetMethodBody(),您可以获得用于实现方法的二进制IL-stream。但这本身通常是完全没用的。

您可以使用外部库(如Cecil)来读取/修改/添加/删除方法内的代码。

【讨论】:

  • 你不能通过委托引用方法,然后从委托创建一个 ExpressionTree 并检查树吗?
  • @Matt Greer:不。您不能将委托的方法体“逆向工程”成表达式树。
  • @Matt:这不会给你inside方法的代码,你只会得到一个MethodCallExpression,它可以让你访问同一个MethodInfo你可以更直接地使用反射 API。
  • 否,除非它可以解析它不能解析的二进制 IL 流。
  • @Foxfire:嗨。我不知道您是否会看到这一点,但如果您看到...当您说反射 API 旨在处理元数据而不是数据级别时,您能否引用您的消息来源?我相信你;)但我想把它包含在我的硕士论文中,我需要一些参考。非常感谢:)
【解决方案2】:

这取决于您所说的“阅读代码”是什么意思。代码有4种形式。

Code Type Can get with Reflection
The source code, i.e. the original C# or VB.NET No
The symbolic IL code No
The JITed assembly code No
The IL bytes, i.e. the actual bytes that IL is compiled to Yes

看看MethodBase.GetMethodBody() 的最后一个。您可以获取 IL 字节、局部变量、异常帧等。

【讨论】:

  • GetMethodBody() 是你能得到的最接近的,我想。
【解决方案3】:

有点可以。相关函数为MethodBase.GetMethodBody

它并不是最有用的 API。您可以获取有关方法内部内容的一些基本信息,并且可以将 IL 作为字节数组获取。就是这样。

Mono.Cecil 库中有一个稍微好一点的 API,它公开了一个带有自己的 MethodBody 实现的 MethodDefinition 类,其中包含实际的 Instructions,因此您不必解释原始字节码。不过,如果您希望通过 Reflector 从中获取 C# 代码,您将会非常失望。此外,Cecil 的文档记录也不是很好。

如果你还想尝试,那么祝你好运。

【讨论】:

    【解决方案4】:

    如果您不需要实时执行此操作,请查看Reflector。您可以反汇编任何 .NET 程序集(包括 MS 核心 DLL)并以您选择的语言查看代码。这可能非常具有教育意义。

    更新有没有人试过在 Reflector 上使用 Reflector 来弄清楚这是怎么做到的?

    【讨论】:

    • 注意,这不会去优化代码。因此,如果对 IL 进行了编译器优化,它不会返回到其原始构建源,而是返回到人工编写的优化版本。这是一种查看您可以最有效地做些什么的好方法。
    • 您不能在反射器上使用反射器。试试看,你会得到一个惊喜。
    • Reflector 是加密的,所以它不能反射自己,我想他们是用 DotFuscator 来做的。
    • 我有大量的 CIL 代码会导致 Reflector 崩溃,即使根本没有任何混淆。将原本不可能用该语言编写的内容转换为 C# 是不可能的。
    • @hoodaticus 虽然我尊重您的回答,但如果您的目标语言是图灵完备的,则始终可以转换。现在,无论它是简单、快速还是一般的好主意,都是一个完全不同的讨论。 :)
    【解决方案5】:

    我想提供一个示例,说明如何探索方法中的代码。正如其他人所解释的那样,使用本机 .NET 反射 API 无法轻松完成此操作。但是,使用Mono.Reflection API,您可以使用GetInstructions() 方法以编程方式反汇编代码并在运行时检查它。

    例如,以下代码检查一个方法并计算其中的调用次数。作为此类代码的用例,假设我是一名教师(我是)并指导我的同学在不使用任何其他方法的情况下编写给定方法,然后在单元测试中使用此代码我可以验证给定的约束是尊重。

    public static class MethodInfoUtil
    {
        public static int NbOfInnerCalls(this MethodInfo mi)
        {
            return mi.GetInstructions().Count(
                instruction => instruction.OpCode.FlowControl == FlowControl.Call);
        }
    }
    

    示例控制台程序:

    class Program
    {
        static int Add(int a, int b) => a + b;
        static int Doubling(int a) => Add(a, a);
        static int Quadrupling(int a) => Add(Add(a, a), Add(a, a));
    
        static void Main(string[] args)
        {
            Console.WriteLine("Inner method calls");
            Console.WriteLine("        Add: {0}", ((Func<int, int, int>)Add).Method.NbOfInnerCalls());
            Console.WriteLine("   Doubling: {0}", ((Func<int, int>)Doubling).Method.NbOfInnerCalls());
            Console.WriteLine("Quadrupling: {0}", ((Func<int, int>)Quadrupling).Method.NbOfInnerCalls());
        }
    }
    
    // Output:
    // Inner method calls
    //         Add: 0
    //    Doubling: 1
    // Quadrupling: 3
    

    【讨论】:

      【解决方案6】:

      没有
      这是下一个 C# 版本的功能。您可以使用 CodeDom 获取比反射更多的信息,但您还不能询问解析树。

      嗯,总是有单声道的,在单声道中,编译器是一个服务,你可以在运行时获取解析树。

      更好的问题是你为什么要这样做?

      【讨论】:

        【解决方案7】:

        是的,必须有一种方法来实现这一点:.NET Reflector 工具也可以做到这一点。不过,不能告诉你它是如何在那里完成的。

        【讨论】:

        • 你为什么不反射反射器;)
        • Reflector 不使用反射,至少不是 .NET 框架意义上的反射。它实际上解析 PE 文件并从 .TEXT 部分分解 IL 代码。然后做一些非常聪明和酷的模式匹配来将 IL 映射/反编译回目标语言。
        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2013-07-18
        • 2019-09-26
        • 2018-10-13
        • 1970-01-01
        • 2011-08-07
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多