简体   繁体   English

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

[英]Expression tree - how to get at declaring instance?

I'm a newbie when it comes to expression trees, so I'm not sure how to ask this question or what terminology to use. 当涉及到表达树时,我是新手,所以我不确定如何提出这个问题或使用什么术语。 Here's an overly-simplifed version of what I'm trying to do: 这是我正在尝试做的过于简化的版本:

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.
}

How can I get to bar inside the Zap method? 如何在Zap方法中使用bar?

Since the expression passed into your Zap method is a tree, you just need to walk the tree using an Expression Tree Visitor and look for the first ConstantExpression in the expression. 由于传递到Zap方法的表达式是树,因此您只需使用表达式树访问者遍历树,并在表达式中查找第一个ConstantExpression It will likely be in the following sequence: 它可能按以下顺序排列:

(((source.Body as MemberExpression).Expression as MemberExpression).Expression as ConstantExpression).Value

Note that the bar instance is captured by a closure, which is implemented as an internal class with the instance as a member, which is where the 2nd MemberExpression comes from. 请注意, bar实例由闭包捕获,闭包实现为内部类,实例作为成员,这是第二个MemberExpression的来源。

EDIT 编辑

Then you have to get the field from the generated closure like so: 然后你必须从生成的闭包中获取字段,如下所示:

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

If you know the type of "bar", you can do this (I'm reusing some bits from the codekaizen's answer here): 如果你知道“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);
    } 

In most cases, you can find an expression representing your object within an expression tree, and then compile and execute this expression and get the object (but this is not a very fast operation, by the way). 在大多数情况下,您可以在表达式树中找到表示对象的表达式,然后编译并执行此表达式并获取对象(但顺便说一下,这不是一个非常快速的操作)。 So, the bit you were missing is the Compile() method. 所以,你缺少的是Compile()方法。 You can find a little bit more info here: How to: Execute Expression Trees . 您可以在这里找到更多信息: 如何:执行表达式树

In this code, I assume that you always pass an expression like "() => object.Member". 在这段代码中,我假设你总是传递一个像“()=> object.Member”这样的表达式。 For a real-world scenario you will need either to analyze that you have an expression you need (eg just throw an exception if it's not a MemberExpression). 对于真实场景,您需要分析您是否拥有所需的表达式(例如,如果它不是MemberExpression则抛出异常)。 Or use ExpressionVisitor, which is kind of tricky. 或者使用ExpressionVisitor,这有点棘手。

I have recently answered a very similar question here: How do I subscribe to an event of an object inside an expression tree? 我最近在这里回答了一个非常类似的问题: 如何在表达式树中订阅对象的事件?

Standing on the shoulders of giants above, my final extension method to extract the instance of the class that represented the source of the expression looks as follows: 站在上面的巨人的肩膀上,我提取表示表达式来源的类的实例的最终扩展方法如下所示:

public static TIn GetSource<TIn, TOut>(this Expression<Func<TIn, TOut>> property)
        where TIn: class
{
    MemberExpression memberExpression = (MemberExpression)property.Body;
    TIn instance = Expression.Lambda<Func<TIn>>(memberExpression.Expression).Compile()();
    return instance;
}

I built on all of the answers above, thanks to all. 我建立在上面的所有答案之上,感谢所有人。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM