J4N
J4N

Reputation: 20707

Extract property chain from a lambda expression

Imagine I've the following code:

public void DoSomething<TU,TV>(Func<TU,TV> valueGetter)
{
    TV extractedValue = valueGetter(_someFields);
    //Here, how to get from  valueGetter the properties used to get the value.
    PropertyInfo[] properties = ....;//["SubObject", "SubSubObject", "Property"]

}
DoSomething((x)=>x.SubObject.SubSubObject.Property)//Let admit that this path always contains only properties, never methods

I'm looking too extract an array of PropertyInfo, containing each property in the path to get the object through reflection.

Is that possible?

Upvotes: 1

Views: 113

Answers (2)

Jeppe Stig Nielsen
Jeppe Stig Nielsen

Reputation: 61942

The very cheap way is like this:

  public void DoSomething<TU, TV>(Expression<Func<TU, TV>> valueGetterExpr)
  {
    var str = valueGetterExpr.ToString();
    // now 'str' holds string representation of the lambda expression
  }

A more structured approach is similar to this:

  public void DoSomething<TU, TV>(Expression<Func<TU, TV>> valueGetterExpr)
  {
    var expr = valueGetterExpr.Body;
    var li = new List<PropertyInfo>();
    while (!(expr is ParameterExpression))
    {
      if (!(expr is MemberExpression me))
        throw new Exception("Unexpected kind");

      if (!(me.Member is PropertyInfo pi))
        throw new Exception("Unexpected kind");

      li.Add(pi);
      expr = me.Expression;
    }
    // now 'li' holds all the properties
  }

The order is reversed compared to what you said. You may reverse the List<> in place with li.Reverse();, or you can use a new Stack<...>() instead of a new List<...>(), "push" instead of "add", and do .ToArray() on your stack after the loop.

In either case, call my method with e.g. DoSomething((DateTime x) => x.TimeOfDay.TotalHours);.

Longer example call: DoSomething((System.Threading.Thread x) => x.CurrentCulture.NumberFormat.NumberDecimalSeparator.Length);

If you want to get the plain Func<,> back, you use:

var func = valueGetterExpr.Compile();
var extractedValue = func(_someFields);

I am sure this can be improved in several ways.

Upvotes: 1

Zazaeil
Zazaeil

Reputation: 4109

It is possible, however you'd need to proceed on the ExpressionTree rather than just a func. Check official documentation from our beloved Microsoft. That would require a lot of reflection, actually, so don't do if performance is a must; in such a case you have to ship more info into your method, like tuple (function, metadata).

Upvotes: 0

Related Questions