4

What I want to do is take any class type and create a list of 'get' accessors to all of the properties in the object graph.

The exact format, order, etc of the collection doesn't matter, I just don't quite know how to start off identifying and creating accessors to all of the properties. It might take the form of something like this:

public static List<Func<T,object>> CreateAccessors<T>()
{
   Type t = typeof(T);
   // Identify all properties and properties of properties (etc.) of T
   // Return list of lambda functions to access each one given an instance of T
}

public void MyTest()
{
   MyClass object1;
   var accessors = CreateAccessors<MyClass>();
   var myVal1 = accessors[0](object1);
   var myVal2 = accessors[1](object1);

   // myVal1 might now contain the value of object1.Property1
   // myVal2 might now contain the value of object1.Property4.ThirdValue.Alpha
}
cachance7
  • 903
  • 1
  • 10
  • 23
  • Have a look at this blog post from Jon Skeet - https://msmvps.com/blogs/jon_skeet/archive/2008/08/09/making-reflection-fly-and-exploring-delegates.aspx, it's pretty much all there barring the recursion. – Rich O'Kelly Jan 27 '12 at 16:57
  • You might want to see this too http://stackoverflow.com/questions/16578857/using-nested-collections-of-lambda-expressions-to-create-an-object-graph – nawfal Jun 02 '13 at 15:46
  • Jon's MVP blog is gone, here's a copy of the post on his (personal?) blog: https://codeblog.jonskeet.uk/2008/08/09/making-reflection-fly-and-exploring-delegates/ – Michael Blackburn Apr 14 '16 at 21:49

2 Answers2

5

You can use reflection to extract the properties and expression-trees to help build the delegates targeting the property getters:

                // Start with all public instance properties of type
var accessors = from property in type.GetProperties
                         (BindingFlags.Instance | BindingFlags.Public)

                // Property must be readable
                where property.CanRead

                //Assemble expression tree of the form:
                // foo => (object) foo.Property

                // foo
                let parameter = Expression.Parameter(type, "foo")

                // foo.Property 
                let propertyEx = Expression.Property(parameter, property)

                // (object)foo.Property - We need this conversion
                // because the property may be a value-type.
                let body = Expression.Convert(propertyEx, typeof(object))

                // foo => (object) foo.Property
                let expr = Expression.Lambda<Func<T,object>>(body, parameter)

                // Compile tree to Func<T,object> delegate
                select expr.Compile();

return accessors.ToList();

Note that although Delegate.CreateDelegate seems like an obvious choice, you will have some problems boxing value-type properties. Expression-trees dodge this problem elegantly.

Note that you'll need some more work to be able to get "nested" properties out too, but hopefully I've given ypu enough to get you started (hint: recurse). One final pointer with that: watch out for cycles in the object graph!

Ani
  • 111,048
  • 26
  • 262
  • 307
  • Another consideration is to watch out for `null` in nested objects. What do you expect `a.b.c` to return if `b` is null? – Gabe Jan 27 '12 at 17:34
  • Thanks for the response, @Ani. It's the recursion part that proves the most troublesome for me, though. I'm new to expression trees and don't quite understand how to recurse such that I'm building the expressions that will access the deeper properties. Do you have any examples or links to more information? Also, to throw one more thing into the situation, some of the properties may be indexed arrays. How would I address this? – cachance7 Jan 27 '12 at 22:01
  • `let`? Is this C#...? – Matías Fidemraizer Aug 14 '14 at 12:17
0
public static List<Func<T, object>> CreateAccessors<T>()
{
    var accessors = new List<Func<T, object>>();
    Type t = typeof(T);
    foreach (PropertyInfo prop in t.GetProperties(BindingFlags.Instance | BindingFlags.Public)) {
        if (prop.CanRead) {
            var p = prop;
            accessors.Add(x => p.GetValue(x, null));
        }
    }
    return accessors;
}

EDIT:

Here is a variant which returns compiled expressions and should be much faster than the previous one using reflection

public static List<Func<T, object>> CreateAccessorsCompiled<T>()
{
    var accessors = new List<Func<T, object>>();
    Type t = typeof(T);
    foreach (PropertyInfo prop in t.GetProperties(BindingFlags.Instance | BindingFlags.Public)) {
        if (prop.CanRead) {
            ParameterExpression lambdaParam = Expression.Parameter(t, "instance");
            Expression bodyExpression;
            MemberExpression memberAccessExpression = Expression.MakeMemberAccess(Expression.Convert(lambdaParam, t), prop);
            if (prop.PropertyType == typeof(object)) {  // Create lambda expression:  (instance) => ((T)instance).Member
                bodyExpression = memberAccessExpression;
            } else { // Create lambda expression:  (instance) => (object)((T)instance).Member
                bodyExpression = Expression.Convert(memberAccessExpression, typeof(object));
            }
            var lambda = Expression.Lambda<Func<T, object>>(bodyExpression, lambdaParam);
            accessors.Add(lambda.Compile());
        }
    }
    return accessors;
}
Olivier Jacot-Descombes
  • 104,806
  • 13
  • 138
  • 188