表达式树 - 如何获取声明实例?

Jud*_*ngo 4 c# lambda expression-trees

当涉及到表达树时,我是新手,所以我不确定如何提出这个问题或使用什么术语.这是我正在尝试做的过于简化的版本:

Bar bar = new Bar();
Zap(() => bar.Foo);

public static void Zap<T>(Expression<Func<T>> source)
{
   // HELP HERE:
   // I want to get the bar instance and call bar.Zim() or some other method.
}
Run Code Online (Sandbox Code Playgroud)

如何在Zap方法中使用bar?

cod*_*zen 6

由于传递给Zap方法的表达式是树,因此您只需使用表达式树访问者遍历树并查找ConstantExpression表达式中的第一个.它可能按以下顺序排列:

(((source.Body as MemberExpression).Expression as MemberExpression).Expression as ConstantExpression).Value
Run Code Online (Sandbox Code Playgroud)

请注意,bar实例由闭包捕获,闭包实现为内部类,实例作为成员,这是第二个MemberExpression的来源.

编辑

然后你必须从生成的闭包中获取字段,如下所示:

    static void Main(string[] args)
    {
        var bar = new Bar();
        bar.Foo = "Hello, Zap";
        Zap(() => bar.Foo);
    }

    private class Bar
    {
        public String Foo { get; set; }    
    }

    public static void Zap<T>(Expression<Func<T>> source)
    {
        var param = (((source.Body as MemberExpression).Expression as MemberExpression).Expression as ConstantExpression).Value;
        var type = param.GetType();
        // Note that the C# compiler creates the field of the closure class 
        // with the name of local variable that was captured in Main()
        var field = type.GetField("bar");
        var bar = field.GetValue(param) as Bar;
        Debug.Assert(bar != null);
        Console.WriteLine(bar.Foo);
    }
Run Code Online (Sandbox Code Playgroud)


Ale*_*ina 5

如果你知道"bar"的类型,你可以这样做(我在这里重复使用codekaizen答案中的一些内容):

    static void Main(string[] args)
    {
        var bar = new Bar();
        bar.Foo = "Hello, Zap";
        Zap(() => bar.Foo);

        Console.ReadLine();
    }

    private class Bar
    {
        public String Foo { get; set; }
    }

    public static void Zap<T>(Expression<Func<T>> source)
    {
        var body = source.Body as MemberExpression;
        Bar test = Expression.Lambda<Func<Bar>>(body.Expression).Compile()();
        Console.WriteLine(test.Foo);
    } 
Run Code Online (Sandbox Code Playgroud)

在大多数情况下,您可以在表达式树中找到表示对象的表达式,然后编译并执行此表达式并获取对象(但顺便说一下,这不是一个非常快速的操作).所以,你缺少的是Compile()方法.您可以在这里找到更多信息:如何:执行表达式树.

在这段代码中,我假设你总是传递一个像"()=> object.Member"这样的表达式.对于真实场景,您需要分析您是否拥有所需的表达式(例如,如果它不是MemberExpression则抛出异常).或者使用ExpressionVisitor,这有点棘手.

我最近在这里回答了一个非常类似的问题: 如何在表达式树中订阅对象的事件?