我正在玩C#反射API。我可以轻松地在程序集中加载类,方法等的Type
信息,但是,现在我想知道如何加载和读取方法中的代码?
答案 0 :(得分:33)
基本答案:
您不能使用反射API(System.Reflection)。
原因是反射api设计用于元数据(类的类型,名称和方法签名,......),但不适用于数据级别(IL-stream本身)。
扩展答案:
您可以使用System.Reflection.Emit(例如ILGenerator Class)发出(但不读取)IL。
通过MethodInfo.GetMethodBody()
,您可以获得用于实现方法的二进制IL流。但这本身通常完全没用。
您可以使用外部库(如Cecil)来读取/修改/添加/删除方法中的代码。
答案 1 :(得分:16)
这取决于您阅读代码的含义。有4种形式的代码。
1-源代码,例如。原始的C#或VB.NET - 不,你不能用反射来得到这个 2-符号IL代码 - 不,你不能用反射得到这个 3- JITed汇编代码 - 不,你不能用反射来得到这个
4- IL字节,IL编译到的实际字节,你可以得到。
例如,看看MethodBase.GetMethodBody(),你可以得到IL字节,局部变量,异常帧等。 http://msdn.microsoft.com/en-us/library/system.reflection.methodbase.getmethodbody.aspx
答案 2 :(得分:9)
你有点可以。相关功能是MethodBase.GetMethodBody。
这不是最有用的API。您可以获得有关方法内部内容的一些基本信息,并且可以将IL作为字节数组获取。就是这样。
Mono.Cecil库中的API稍微好一点,它会为MethodDefinition
类提供自己的MethodBody
实现,其中包含实际的Instructions
,所以你没有解释原始字节代码。尽管如此,如果你想从反射器中获取C#代码,你将会非常失望。此外,塞西尔没有很好的文件记录。
如果你还想尝试,那么祝你好运。
答案 3 :(得分:2)
如果您不需要实时执行此操作,请查看Reflector。您可以反汇编任何.NET程序集(包括MS核心DLL),并以您选择的语言查看代码。这可以非常教育。
更新有没有人尝试在Reflector上使用Reflector来弄清楚如何做到这一点?
答案 4 :(得分:1)
没有
这是下一版C#的功能。您可以使用CodeDom获取比反射更多的信息,但是您无法查询解析树。
总是单声道,单声道编译器是一个服务,你可以在运行时获得解析树。
更好的问题是你想要的原因?
答案 5 :(得分:1)
是的,必须有办法实现这一点:.NET Reflector工具也是如此。但不能告诉你它是如何在那里完成的。
答案 6 :(得分:0)
我想提供一个示例,说明如何在方法内部探索代码。正如其他人所解释的那样,使用本机.NET Reflection 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