views:

167

answers:

2

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?

+2  A: 

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);
    }
codekaizen
Thanks! I'll give that a try.
Judah Himango
I've tried it. It's close: the above expression gives me the internal, compiler-generated class. I need to get to the bar instance stored in that class. I played around and couldn't figure it out. What am I missing?
Judah Himango
Ok, I updated the answer.
codekaizen
Thanks. Of course, I can't rely on "bar" being the real name. However, I should be able to extract this information from the MemberField property of the nested expression. Thanks.
Judah Himango
Right on. Good call on further reflecting on the MemberField property.
codekaizen
+2  A: 

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: http://stackoverflow.com/questions/1936852/how-do-i-subscribe-to-an-event-of-an-object-inside-an-expression-tree/1942176#1942176

Alexandra Rusina