Search code examples
c#.netclosuresexpression-trees

How to parse a simple captured closure expression and get the target object and selector


If I have simple class

class Foo {
    int Value { get; set; } = 20
}

class Bar {
    Foo foo { get; set;} = new Foo();
}

and then I write a selector expression

var bar = new Bar();
Expression<Func<int>> fn = () => bar.foo.Value;

I am aware that if I type

var v = fn.Compile()()

v will equal 20 demonstrating that the closure is indeed captured. I infer that inside the expression a reference to the captured target of the expression is stored. I would like to get this back out of the expression.

For example

Bar capturedBar = GetTarget(fn);
Assert(capturedBar == bar);

and another function

string selector = GetSelector(fn)

which should return

"bar.foo.A"

Solution

  • You can accomplish this tasks with using the Expression Visitor technique.

    Here is the simple and dirty demonstration of the main idea:

    var bar = new Bar();
    Expression<Func<int>> fn = () => bar.foo.Value;
    
    BarExtractor be = new BarExtractor();
    be.Visit(fn);
    Bar capturedBar = be.bar;
    

    The BarExtractor class is declared as follows:

    class BarExtractor : ExpressionVisitor {
        public Bar bar;
        protected override Expression VisitConstant(ConstantExpression node) {
            object closureObj = node.Value;
            if(closureObj != null && closureObj.GetType().Name.StartsWith("<>c__DisplayClass")) {
                var barField = closureObj.GetType().GetField("bar");
                if(barField != null && barField.FieldType == typeof(Bar))
                    bar = barField.GetValue(closureObj) as Bar;
            }
            return base.VisitConstant(node);
        }
    }