C# 我可以使用反射来检查方法中的代码吗?

C# 我可以使用反射来检查方法中的代码吗?,c#,reflection,C#,Reflection,我正在玩C#反射API。我可以轻松地加载程序集中类、方法等的类型信息,但现在我想知道如何加载和读取方法中的代码?基本答案: 您不能使用反射API(System.reflection) 原因是反射api设计用于元数据(类的类型、方法的名称和签名等),而不是数据级别(即IL流本身) 扩展答案: 您可以使用System.Reflection.emit(例如ILGenerator类)发射(但不读取)IL 通过MethodInfo.GetMethodBody()可以获得用于实现方法的二进制IL流。但这本身

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

基本答案:

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

原因是反射api设计用于元数据(类的类型、方法的名称和签名等),而不是数据级别(即IL流本身)

扩展答案:

您可以使用System.Reflection.emit(例如ILGenerator类)发射(但不读取)IL

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

有一些外部库(如)可用于读取/修改/添加/删除方法内部的代码。


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

mono总是存在的,在mono中,编译器是一个服务,您可以在运行时获得解析树


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

是的,必须有一种方法来实现这一点:使用.NET Reflector工具也可以做到这一点。但是,无法告诉您是如何完成的。

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


更新是否有人尝试过在反射器上使用反射器来了解这是如何做到的?

这取决于您阅读代码的意思。代码有4种形式

1-源代码,如原始C#或VB.NET-不,您无法通过反射获得此信息
2-符号IL代码-不,您无法通过反射获得此代码
3-JIT程序集代码-不,您无法通过反射获得此代码

4-IL字节,即IL编译到的实际字节,您可以得到它。

以MethodBase.GetMethodBody()为例,您可以获取IL字节、局部变量、异常帧等。 你可以。相关功能为

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

库中有一个稍好一点的API,它公开了一个
MethodDefinition
类及其自己的
MethodBody
实现,该实现包含实际的
指令
,因此您不必解释原始字节码。不过,如果你想从反射器中获得C代码,你会非常失望。而且,塞西尔的记录也不是很好


如果您仍然想尝试,那么祝您好运。

我想提供一个示例,说明如何探索方法中的代码。正如其他人所解释的,使用本机.NET反射API很难做到这一点。但是,使用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
类程序
{
静态整数相加(整数a,整数b)=>a+b;
静态整数倍增(整数a)=>加法(a,a);
静态整数四倍(整数a)=>Add(添加(a,a),添加(a,a));
静态void Main(字符串[]参数)
{
WriteLine(“内部方法调用”);
WriteLine(“Add:{0}”,((Func)Add.Method.nbofinerCalls());
WriteLine(“Doubling:{0}”,((Func)Doubling.Method.nbofinerCalls());
WriteLine(“四联:{0}”,((Func)四联).Method.nbofinerCalls());
}
}
//输出:
//内部方法调用
//地址:0
//倍增:1
//翻两番:3

是什么样的场景使此功能变得有用?它打破了OO的一个基本租户,即封装。但是,与IL的代码实际上并不相同。@DevelopingChris:如果您正在编写调试器或代码分析工具,那么能够加载程序集并分析方法体可能会很有用。事实上,像FxCop这样的工具就是这样做的。NET的目标受众是美国公司(其次可能是一些小企业)。如果你能绕过.NET模糊处理,这个(生产成本昂贵的)软件很容易就会飞到竞争对手的指尖上。难道你不能通过委托引用该方法,然后从委托创建一个ExpressionTree并检查该树吗?@Matt Greer:不。你不能“反向工程”将委托的方法体转换到表达式树中。@Matt:这不会给出方法中的代码,您只需获得一个
MethodCallExpression
,它允许您访问与反射API相同的
MethodInfo
。除非它能够解析一个无法解析的二进制IL流。@Foxfire:Hi。我不知道你是否会看到这个,但如果你看到。。。当您说反射API设计用于元数据而不是数据级别时,您能引用您的来源吗?我相信你;)但我想在我的硕士论文中包括这一点,我需要一些参考资料。非常感谢:)注意,这不会对代码进行去优化。因此,如果对IL进行了编译器优化,它不会返回到其原始构建源代码,而是返回到一个稍微不那么人工编写的优化版本。这是一个很好的方式来看看你能做什么最有效