Search code examples
c#lambdaexpression-trees

Expression to Call a Method on Each Property of a Class


I want to take a class, loop through it's properties, get the property value, and call a method passing that property value in. I think I can get the property values, but what does the lambda expression's body look like? What body is used to call a method on each property?

This is what I have so far...

Action<T> CreateExpression<T>( T obj )
{
 foreach( var property in typeof( T ).GetProperties() )
 {
  Expression value = Expression.Property( Expression.Constant( obj ), property );
  var method = Expression.Call( typeof( SomeType ), "SomeMethod", null, value );
 }

 // What expression body can be used that will call
 // all the method expressions for each property?
 var body = Expression...
 return Expression.Lambda<Action<T>>( body, ... ).Compile();
}

Solution

  • It depends on a few things.

    • does the method return anything? Expression in 3.5 can't do multiple separate "action" operations (a statement body), but you can cheat if you can do something with a fluent API:

      SomeMethod(obj.Prop1).SomeMethod(obj.Prop2).SomeMethod(obj.Prop3);
      

      (perhaps using generics to make it simpler)

    • do you have access to 4.0? In 4.0 there are additional Expression types allowing statement bodies and exactly what you ask for. I discuss some similar examples in an article here (look for Expression.Block, although this is based on a beta a while ago - it may have been renamed by now).

    Alternative; since you are compiling to a delegate, consider that an Action<T> is multicast; you could build a set of simple operations, and combine them in the delegate; this would work in 3.5; for example:

    using System;
    using System.Linq.Expressions;
    static class SomeType
    {
        static void SomeMethod<T>(T value)
        {
            Console.WriteLine(value);
        }
    }
    class Customer
    {
        public int Id { get; set; }
        public string Name { get; set; }
    }
    static class Program
    {
        static readonly Action<Customer> action = CreateAction<Customer>();
        static void Main()
        {
            Customer cust = new Customer { Id = 123, Name = "Abc" };
            action(cust);
        }
        static Action<T> CreateAction<T>()
        {
            Action<T> result = null;
            var param = Expression.Parameter(typeof(T), "obj");
            foreach (var property in typeof(T).GetProperties(
                BindingFlags.Instance | BindingFlags.Public))
            {
                if (property.GetIndexParameters().Length > 0) continue;
                var propVal = Expression.Property(param, property);
                var call = Expression.Call(typeof(SomeType), "SomeMethod", new Type[] {propVal.Type}, propVal);
                result += Expression.Lambda<Action<T>>(call, param).Compile();
            }
            return result;
        }
    }