【问题标题】:How can i access the value of a local variable from within an expression tree如何从表达式树中访问局部变量的值
【发布时间】:2016-07-13 19:36:30
【问题描述】:

通过检查表达式树,我可以获得常量、实例字段和属性的值,但不能获得方法中定义的局部变量。

执行以下将输出 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 MemberExpression;

                var @object = memberExpression.Member.DeclaringType == GetType()
                    ? this
                    : null; //Can I do anything here? Instance of the method the variable is defined in?

                if (memberExpression.Member.MemberType == MemberTypes.Field)
                {
                    Console.WriteLine(((FieldInfo)memberExpression.Member).GetValue(@object));
                }
                else if (memberExpression.Member.MemberType == MemberTypes.Property)
                {
                    Console.WriteLine(((PropertyInfo)memberExpression.Member).GetValue(@object));
                }
            }
        }
    }
}

【问题讨论】:

  • 您根本无法做到这一点。除非该方法实际运行,否则该变量甚至都不存在。
  • 你想得到什么?参数名称?我有点困惑,但你总是可以选择在调用方法之前设置一个“全局”变量。
  • @MatthewWatson 问题标题具有误导性。如果你阅读他的代码,你会看到他在 lambda 内部有一个 captured 变量,在这种情况下,闭包很容易在方法停止运行后存在。你肯定能读懂它的价值。
  • 问题标题具有误导性 - 老实说,我不确定我要求的是什么 - 希望代码能证明我在寻找什么。会更新。

标签: c# reflection


【解决方案1】:

已被 lambda 捕获并包含在表达式树中的局部变量,届时将真正成为某个编译器生成的类的字段。这适用于我的 .NET 版本:

void Test<T>(Expression<Func<T>> func)
{
  var body = func.Body;

  if (body.NodeType == ExpressionType.Constant)
  {
    Console.WriteLine(((ConstantExpression)body).Value);
  }
  else
  {
    var memberExpression = (MemberExpression)body;

    var @object = 
      ((ConstantExpression)(memberExpression.Expression)).Value; //HERE!

    if (memberExpression.Member.MemberType == MemberTypes.Field)
    {
      Console.WriteLine(((FieldInfo)memberExpression.Member).GetValue(@object));
    }
    else if (memberExpression.Member.MemberType == MemberTypes.Property)
    {
      Console.WriteLine(((PropertyInfo)memberExpression.Member).GetValue(@object));
    }
  }
}

当然,你也可以“作弊”:

void Test<T>(Expression<Func<T>> func)
{
  Console.WriteLine(func.Compile()());
}

【讨论】:

  • 有趣!但如果这是答案,人们不得不想知道问题是什么(即我认为 OP 发布了 X-Y 问题)。
  • @MatthewWatson 是的,我认为这个问题是“我怎样才能将此分配更改为@object 以找到相关的目标/实例?”。但是,不清楚他是否真的需要编译器生成的类的实例(来自闭包(局部变量捕获))。
  • 这正是我所需要的。这正是我应该问的问题。
【解决方案2】:

不,你不能。

反射不会扩展到读取方法变量的值。

它只处理变量的声明元数据。即便如此,编译器可能已经删除了你认为你声明的变量。您已经可以访问属性、字段了。

【讨论】:

  • 如果你阅读他的代码,你会发现他拥有的是一个包含捕获局部变量的表达式树。这样的事情将变成某个编译器生成的类的字段。当然,您应该能够读取它的值。看我的回答。
【解决方案3】:

不,您不能,因为这些变量在方法范围之外根本不可用。

【讨论】:

  • asker 代码中的局部变量已在 lambda(闭包)中捕获,因此肯定是可用的。看我的回答。提问者没有提供太多解释,所以有必要阅读他的代码。
【解决方案4】:

不,你不能用反射。但是还有其他方法可以做到这一点。作为示例,我将向您展示如何将变量导出到函数的词法范围之外。

假设你有这样的方法:

private void f(int x)
{
    // your code here
}

你也有这样一段代码:

int x_in_f;

f(3);
x_in_f = /* I want to have the value here */;

要从f() 中获取值,您必须导出一个getter 函数。当f() 返回void,你可以返回getter。在一般情况下(当f() 具有非void 返回类型时)您可以利用out 参数。以下是两种变体:

private Func<int> f(int x)
{
    // your code here

    return () => { return x; };
}

或通过out参数:

private void f(int x, out Func<int> g)
{
    // your code here

    g = () => { return x; };
}

代码将是:

int x_in_f;
Func<int> g;

g = f(3);

x_in_f = g(); // this will return the value of x as it was passed to f()

或者通过out参数调用f()如下:

f(3, out g);

此时您可以将g() 传递给其他函数:

private void h(Func<int> getx)
{
    // your code here
    int x = getx();
    // now you have the value of x inside the h() function
}

以及使用h() 调用:

Func<int> g = f(3);

// ...

h(g);

我希望这有助于或至少说明如何使用闭包来规避词法作用域。

对于那里的设计师来说,这是对象能力模型This is a video Douglas Crockford 关于如何在 Javascript 中将其用于安全目的。如上所示,它可以轻松转换为 C# 和其他用途。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-10-18
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多