views:

831

answers:

2

I am parsing an Expression Tree. Given a NodeType of ExpressionType.MemberAccess, how do I get the value of that Field?

From C# MSDN docs: MemberAccess is A node that represents reading from a field or property.

A code snippet would be incredibly, incredibly helpful. Thanks in advance!!!

My code looks something like this:

public static List<T> Filter(Expression<Func<T, bool>> filterExp) 
{
//the expression is indeed a binary expression in this case
BinaryExpression expBody = filterExp.Body as BinaryExpression;

if (expBody.Left.NodeType == ExpressionType.MemberAccess) 
  //do something with ((MemberExpressionexpBody.Left).Name

//right hand side is indeed member access. in fact, the value comes from //aspdroplist.selectedvalue            
if (expBody.Right.NodeType == ExpressionType.MemberAccess)
{
   //how do i get the value of aspdroplist.selected value?? note: it's non-static                        
}

//return a list
}
+12  A: 

[updated for clarity]

First; cast the Expression to a MemberExpression.

A MemberExpression has two things of interest:

  • .Member - the PropertyInfo / FieldInfo to the member
  • .Expression - the expression to evaluate to get the "obj" for the .Member

i.e. if you can evaluate the .Expression to "obj", and the .Member is a FieldInfo, then you can get the actual value via .GetValue(obj) on the FieldInfo (and PropertyInfo is very similar).

The problem is that evaluating the .Expression is very tricky ;-p

Obviously you get lucky if it turns out to be a ConstantExpression - but in most cases it isn't; it could be a ParameterExpression (in which case you'll need to know the actual parameter value that you want to evaluate), or any other combination of Expressions.

In many cases, a simple (perhaps lazy) option is to use .Compile() to get the .NET framework to do the heavy lifting; you can then evaluate the lambda as a typed delegate (passing in any parameters that the lambda requires). This isn't always an option, however.

To show how complex this is; consider this trivial example (where I've hard-coded at every step, rather than testing etc):

using System;
using System.Linq.Expressions;
using System.Reflection;
class Foo
{
    public string Bar { get; set; }
}

static class Program
{
    static void Main()
    {
        Foo foo = new Foo {Bar = "abc"};
        Expression<Func<string>> func = () => foo.Bar;

        MemberExpression outerMember = (MemberExpression)func.Body;
        PropertyInfo outerProp = (PropertyInfo) outerMember.Member;
        MemberExpression innerMember = (MemberExpression)outerMember.Expression;
        FieldInfo innerField = (FieldInfo)innerMember.Member;
        ConstantExpression ce = (ConstantExpression) innerMember.Expression;
        object innerObj = ce.Value;
        object outerObj = innerField.GetValue(innerObj);
        string value = (string) outerProp.GetValue(outerObj, null);    
    }

}
Marc Gravell
Thank you so much Marc. THe value of .Expression property is ... something more interesting: {value(ASP.usercontrols_mycontro_ascx).controlname}
Keith Fitzgerald
i'm doing a lot with generics and reflection so retrieving the value via propertyinfo/fieldinfo isn't working because i'm not sure where to pull the referencing object from ... can i pull that from memberexpression or methodinfo?
Keith Fitzgerald
It will work... but the problem is you need to evaluate the .Expression as a value to feed in as the "obj" for FieldInfo/PropertyInfo. Can you not just use .Compile() and execute the lambda as a delegate? A lot easier than parsing...
Marc Gravell
Note that if the lambda is parameterised, but you don't have the specific parameter - then you will struggle... but things like ConstantExpression should work themselves out OK.
Marc Gravell
Thank you so much Marc!!! really ... above and beyond here. ultimately the problem is the left hand side is a binaryexpression ... and the right hand side isn't a constantexpression. i'm going to update my question with some code
Keith Fitzgerald
+6  A: 

thank you so so much to Marc Gravell above. I really appreciated his help.

It turns out, in my case. the problem can be solved via:

object value = Expression.Lambda(expBody.Right).Compile().DynamicInvoke();

Thanks again Mark!

Keith Fitzgerald
just what i needed!
Andrew Bullock