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