通过检查表达式树,我可以获得常量,实例字段和属性的值,但不能获取方法中定义的局部变量.
执行以下操作将输出1,2,3(来自常量,实例字段和属性)然后是异常,因为我不知道如何获取声明FieldInfo的实例以便为本地调用GetValue()变量.
using System;
using System.Linq.Expressions;
using System.Reflection;
namespace Example
{
class Program
{
private int _intField = 2;
static void Main()
{
new Program().Run();
Console.ReadLine();
}
private void Run()
{
IntProp = 3;
var intVariable = 4;
Test(() => 1);
Test(() => _intField);
Test(() => IntProp);
Test(() => intVariable);
}
public int IntProp { get; set; }
void Test<T>(Expression<Func<T>> func)
{
var body = func.Body;
if (body.NodeType == ExpressionType.Constant)
{
Console.WriteLine(((ConstantExpression)body).Value);
}
else
{
var memberExpression = body as …Run Code Online (Sandbox Code Playgroud) 问题似乎是一个你不应该做的肮脏的黑客,但让我先解释一下.最终目标是在C++中使用方法局部静态.
void Func()
{
static methodLocalObject = new ExpensiveThing();
// use methodlocal Object
}
Run Code Online (Sandbox Code Playgroud)
这与指令指针有什么关系?我想根据我的调用者缓存数据.为了使这个快速,我回到堆栈中以获取我的调用者的地址,并将其用作字典的唯一键来存储数据.这将允许创建一个基于反射的跟踪器,它不会每次都使用Reflection来获取当前方法和类型的名称,但只有一次,并将反射信息存储在哈希表中.
到目前为止,答案仅以单声道为基础.我想尝试一个适用于.NET 3.5/4.0 32/64位的通用解决方案.我知道64位的调用约定是完全不同的,因此获得可靠的东西可能会遇到挑战.但另一方面,我在我的方法中完全控制堆栈的外观.在.NET 3.5和4.0之间,堆栈确实看起来非常不同,当然它在发布版本之间也有所不同.我仍然需要检查NGen是否确实创建了具有不同堆栈布局的代码.一种可能性是使用C++帮助器方法,该方法需要5个魔术整数参数(在x64上只有第5个将在堆栈上)并检查我在堆栈中可以找到它们的位置.另一种可能性是简单地使用整个堆栈,直到我在堆栈上找到我的魔术标记作为键并使用堆栈的这一部分作为唯一足够的密钥.但我不确定这种方法是否可行,或者是否有更好的选择.我知道我可以通过分析或调试apis以安全的方式走栈,但它们都不是很快.
对于跟踪库,通常的方法是使用反射来遍历堆栈以获取当前方法名称和类型.
class Tracer
{
[MethodImpl(MethodImplOptions.NoInlining)]
public Tracer()
{
StackFrame frame = new StackTrace().GetFrame(1); // get caller
Console.WriteLine("Entered method {0}.{1}", frame.GetMethod().DeclaringType.FullName, frame.GetMethod().Name);
}
}
Run Code Online (Sandbox Code Playgroud)
但这很慢.另一个解决方案是直接通过字符串传递数据,速度要快得多,但需要更多的输入.另一种解决方案是使用调用函数的指令指针(如果可以以非常快的方式确定)来绕过昂贵的反射调用.那么这是可能的:
class Tracer
{
static Dictionary<Int64, string> _CachedMethods = new Dictionary<Int64, string>();
[MethodImpl(MethodImplOptions.NoInlining)]
public Tracer()
{
Int64 eip = GetEIpOfParentFrame();
string name;
lock (_CachedMethods)
{
if (!_CachedMethods.TryGetValue(eip, out name))
{
var callingMethod = new StackTrace().GetFrame(1).GetMethod();
name …Run Code Online (Sandbox Code Playgroud)