Cook Computing

Static Reflection - Lambdas as Data

October 8, 2009 Written by Charles Cook

When I read the MSDN magazine article Functional Programming for Everyday .NET Development I was puzzled by this code:


public class AddressMap : DomainMap<Address>
{
  public AddressMap()
  {
    Map(a => a.Address1);
    Map(a => a.Address2);
    Map(a => a.AddressType);
    Map(a => a.City);
    Map(a => a.TimeZone);
    Map(a => a.StateOrProvince);
    Map(a => a.Country);
    Map(a => a.PostalCode);
  }
}

Until I read the explanation:

Fluent NHibernate never evaluates the expression a => a.Address1. Instead, it parses the expression to find the name Address1 to use in the underlying NHibernate mapping. This technique has spread widely through many recent open-source projects in the .NET space. Using Lambda expressions just to get at PropertyInfo objects and property names is frequently called static reflection.

So I wrote some sample code to see how it could be implemented:


using System;
using System.Linq.Expressions;
using System.Reflection;
 
class Address
{
  public string Address1 { get; set; }
  public TimeZone TimeZone { get; set; }
}
 
class AddressMap : DomainMap<Address>
{
  public AddressMap()
  {
    Map(a => a.Address1);
    Map(a => a.TimeZone);
  }
}
 
class DomainMap<T>
{
  public void Map(Expression<Func<T, object>> exp)
  {
    MemberExpression mexp = exp.Body as MemberExpression;
    PropertyInfo pinfo = mexp.Member as PropertyInfo;
    Console.WriteLine("Property name: {0} Type: {1}", 
      pinfo.Name, pinfo.PropertyType);
  }
}
 
class Program
{
  static void Main(string[] args)
  {
    AddressMap map = new AddressMap();
  }
}

This outputs:


Property name: Address1 Type: System.String
Property name: TimeZone Type: System.TimeZone

Using reflection it would be necessary to pass in the name of the property, which is not type safe, for example:


  public void Map(string propertyName)
  {
    PropertyInfo pinfo = typeof(T).GetProperty(propertyName);
    Console.WriteLine("Property name: {0} Type: {1}", 
      pinfo.Name, pinfo.PropertyType);
  }

Thinking back to my post on NOptFunc a while ago, where I discussed the hypothetical methodinfo operator, it seems that a propertyinfo operator would be even more useful:


public class AddressMap : DomainMap&lt;Address&gt;
{
  public AddressMap()
  {
    Map(propertyinfo(Address.Address1));
    Map(propertyinfo(Address.Address2));
    Map(propertyinfo(Address.AddressType));
    Map(propertyinfo(Address.City));
    Map(propertyinfo(Address.TimeZone));
    Map(propertyinfo(Address.StateOrProvince));
    Map(propertyinfo(Address.Country));
    Map(propertyinfo(Address.PostalCode));
  }
}