Expression tree - how to get at declaring instance?

吃可爱长大的小学妹 提交于 2019-12-05 12:20:17

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

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

If you know the type of "bar", you can do this (I'm reusing some bits from the codekaizen's answer here):

    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. 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". For a real-world scenario you will need either to analyze that you have an expression you need (e.g. just throw an exception if it's not a MemberExpression). Or use ExpressionVisitor, which is kind of tricky.

I have recently answered a very similar question here: How do I subscribe to an event of an object inside an expression tree?

The Senator

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.

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!