如何从表达式树中访问局部变量的值

时间:2016-07-13 07:47:57

标签: c# reflection

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

执行以下操作将输出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));
                }
            }
        }
    }
}

4 个答案:

答案 0 :(得分:4)

由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()());
}

答案 1 :(得分:1)

不,你不能。

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

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

答案 2 :(得分:0)

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

答案 3 :(得分:0)

不,你不能反思。但还有其他方法可以做到这一点。作为一个例子,我向您展示了如何从函数中导出变量&#39;词汇范围。

假设您有这样的方法:

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时,您就可以返回获取者。在一般情况下(当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);

我希望这有助于或至少说明如何使用闭包来绕过词汇范围。

对于那里的设计师,这是对象能力模型。 Douglas Crockford的This is a video关于如何在Javascript中将其用于安全目的。正如我上面所示,它很容易转化为C#和其他目的。