Static Reflection of property names
From .Net 3.5 you can use Expressions for "static reflection". You can get compile-time checking when you refer to properties by name. The most widely quoted example is Fluent NHibernate, and this small helper steals from it too.
Example
//property names as strings- look out for typos
DropDownList1.DataTextField = "Name";
DropDownList1.DataValueField = "Id";
//expression gives intellisense and strong typing
DropDownList1.DataTextField = Property.Name<Person>(x => x.Name);
DropDownList1.DataValueField = Property.Name<Person>(x => x.Id);
Code
using System;
using System.Linq.Expressions;
using System.Reflection;
namespace Library.Reflection
{
/// <summary>
/// Get the string name and type of a property or field. Eg <c>string name = Property.Name<string>(x => x.Length);</c>
/// </summary>
public static class Property
{
/// <summary>
/// Gets the type for the specified entity property or field. Eg <c>string name = Property.Name<string>(x => x.Length);</c>
/// </summary>
/// <typeparam name="TEntity">The type of the entity (interface or class).</typeparam>
/// <param name="expression">The expression returning the entity property, in the form x => x.Id</param>
/// <returns>The name of the property as a string</returns>
[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1006:DoNotNestGenericTypesInMemberSignatures")]
public static string Name<TEntity>(Expression<Func<TEntity, object>> expression)
{
var memberExpression = GetMemberExpression(expression);
var propertyInfo = memberExpression.Member as MemberInfo;
if (propertyInfo != null)
return propertyInfo.Name;
//unknown
return null;
}
/// <summary>
/// Gets the type for the specified entity property or field. Eg Type<string>(x => x.Length) == typeof(int)
/// </summary>
/// <typeparam name="TEntity">The type of the entity (interface or class).</typeparam>
/// <param name="expression">The expression returning the entity property, in the form x => x.Id</param>
/// <returns>A type.</returns>
[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1006:DoNotNestGenericTypesInMemberSignatures")]
public static Type Type<TEntity>(Expression<Func<TEntity, object>> expression)
{
var memberExpression = GetMemberExpression(expression);
var propertyInfo = memberExpression.Member as PropertyInfo;
if (propertyInfo != null)
return propertyInfo.PropertyType;
//not a property, maybe a public field
var fieldInfo = memberExpression.Member as FieldInfo;
if (fieldInfo != null)
return fieldInfo.FieldType;
//unknown
return typeof(object);
}
private static MemberExpression GetMemberExpression<TEntity, T>(Expression<Func<TEntity, T>> expression)
{
//originally from Fluent NHibernate
MemberExpression memberExpression = null;
if (expression.Body.NodeType == ExpressionType.Convert)
{
var body = (UnaryExpression)expression.Body;
memberExpression = body.Operand as MemberExpression;
}
else if (expression.Body.NodeType == ExpressionType.MemberAccess)
{
memberExpression = expression.Body as MemberExpression;
}
//runtime exception if not a member
if (memberExpression == null)
throw new ArgumentException("Not a property or field", "expression");
return memberExpression;
}
}
}