即使可以在运行时编译C#代码,也不可能在当前范围中包含和运行生成的代码。相反,所有变量都必须作为显式参数传递。
与Python等动态编程语言相比,人们永远无法真正复制eval
的完整行为(如本例所示)。
x = 42
print(eval("x + 1")) # Prints 43
所以我的问题是(无论它是否真的有用;))是否可以通过使用 reflection 在.NET中模仿动态范围。
由于.NET为我们提供了允许我们检查调用方法的Diagnostics.StackTrace
类,因此该问题归结为以下内容:(如何)可以可靠地访问调用方法的本地
堆栈跟踪是否为我们提供了足够的信息来计算内存偏移量,或者托管代码中是否禁止这样做?
这样的代码是否有可能?
void Foo() {
int x = 42;
Console.WriteLine(Bar());
}
int Bar() {
return (int)(DynamicScope.Resolve("x")); // Will access Foo's x = 42
}
答案 0 :(得分:8)
所以我的问题是,是否可以通过使用反射来模拟.NET中的动态范围。
Reflection支持对程序集的元数据中表示的项目进行运行时操作。
局部变量不是程序集元数据中表示的项。
因此,你的问题的答案是“不”。
(如何)可以可靠地访问调用方法的本地?
访问调用方法的本地的设备称为“调试器”。所以你的问题的答案是“自己写一个调试器”。
请注意,调试器不会可靠地访问具有代码优化的世界中的本地人。本地可以被优化掉,抖动可以生成代码,这些代码对两个不同的本地使用相同的寄存器,堆栈帧可以在尾调用期间重复使用,等等。当然,您最好使用PDB文件告诉调试器与每个堆栈帧位置关联的名称是什么。
您的方法必须做的是将自定义调试器作为新进程启动,然后等待调试器向其发送消息。然后调试器将挂起原始进程的线程,查询堆栈帧,然后恢复进程的线程。然后它会向调试对象发送包含您发现的信息的消息。由于调试对象正处于等待状态等待消息,因此它将恢复其业务。
如果您想要的是进程内“eval”功能,请考虑JScript.NET,这是为这类事物设计的语言。
答案 1 :(得分:5)
这是不可能的。在编译的.NET代码(中间语言)中,变量简单地表示为堆栈上的索引。例如the ldloc
instruction,它加载变量的值只需unsigned int16
值作为参数。对于在调试模式下编译的应用程序,可能有某种方法可以执行此操作(毕竟,在调试应用程序时,Visual Studio会这样做),但它通常无法正常工作。
在Phalanger(.NET编译器,我部分参与其中),这必须以某种方式解决,因为PHP语言有eval
(它不需要提供动态范围,但它需要按名称访问变量)。因此,Phalanger检测函数是否包含eval
的任何使用,如果是,它将所有变量存储在Dictionary<string, object>
中,然后传递给eval
函数(以便它可以读取)变量按名称)。我担心这是唯一的方法......