Static Reflection - Lambdas as Data
October 8, 2009 Written by Charles CookWhen 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<Address>
{
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));
}
}