How to get property name from lambda

The whole idea is simple, we want this test to pass:

[Test]
void CanGetPropertyName_UsingLambda()
{
  Assert.AreEqual("Name", PropertyName.For<person>(x => x.Name));
}

It seams nice and convenient way of getting property name.
Whole test fixture looks as follows:

[TestFixture]
public class PropertyNameTests
{
    public string NameForTest { get; set; }

    [Test]
    public void CanGetPropertyName_SameType_UsingLambda()
    {
        Assert.AreEqual("NameForTest",
            PropertyName.For(() => NameForTest));
    }

    [Test]
    public void CanGetPropertyName_UsingLambda()
    {
        Assert.AreEqual("Name",
            PropertyName.For<person>(x => x.Name));
        Assert.AreEqual("Age",
            PropertyName.For<person>(x => x.Age));
    }

    [Test]
    public void CanGetPropertyName_Composite_UsingLambda()
    {
        Assert.AreEqual("Home.City",
            PropertyName.For<person>(x => x.Home.City));
        Assert.AreEqual("Home.FlatNumber",
            PropertyName.For<person>(x => x.Home.FlatNumber));
    }
}

public class Person
{
    public string Name { get; set; }
    public int Age { get; set; }
    public Home Home { get; set; }
}

public class Home
{
    public string City { get; set; }
    public string FlatNumber { get; set; }
}

Implementation uses .NET 3.5 feature called expression trees. Expression trees represent language-level code in the form of data. The data is stored in a tree-shaped structure.

/// <summary>
/// Gets property name using lambda expressions.
/// </summary>
internal class PropertyName
{
    public static string For<t>(
        Expression<func<t, object>> expression)
    {
        Expression body = expression.Body;
        return GetMemberName(body);
    }

    public static string For(
        Expression<func<object>> expression)
    {
        Expression body = expression.Body;
        return GetMemberName(body);
    }

    public static string GetMemberName(
        Expression expression)
    {
        if (expression is MemberExpression)
        {
            var memberExpression = (MemberExpression)expression;

            if (memberExpression.Expression.NodeType ==
                ExpressionType.MemberAccess)
            {
                return GetMemberName(memberExpression.Expression)
                    + "."
                    + memberExpression.Member.Name;
            }
            return memberExpression.Member.Name;
        }

        if (expression is UnaryExpression)
        {
            var unaryExpression = (UnaryExpression)expression;

            if (unaryExpression.NodeType != ExpressionType.Convert)
                throw new Exception(string.Format(
                    "Cannot interpret member from {0}",
                    expression));

            return GetMemberName(unaryExpression.Operand);
        }

        throw new Exception(string.Format(
            "Could not determine member from {0}",
            expression));
    }
}

UnaryExpression part is needed for value types to work.

Tags:

Questions?

Consider using our Q&A forum for asking questions.

6 Responses to “How to get property name from lambda”

  1. Victor Says:

    I think you have to modify GetMemberName at least a little, and put the following code in the begining

    if (expression is LambdaExpression)
    {
    var lambdaExpression = (LambdaExpression)expression;
    var compiled = lambdaExpression.Compile();

    if (lambdaExpression.Body.NodeType == ExpressionType.MemberAccess)
    {
    return GetMemberName(lambdaExpression.Body);
    }
    }

  2. Limilabs support Says:

    Why?

  3. Victor Says:

    Actually i maid a small mistake , you shoud pass expression.body in case of LambdaExpression, because LambdaExpression itself is not member expression and you’re code will throw an exception otherwise

  4. Limilabs support Says:

    Could you write a unit test for this situation?

  5. INotifyPropertyChanged with lambdas Says:

    […]   ProtocolExHow to get property name from lambda Email template engine […]

  6. Stephane Says:

    Thanks, works perfectly. Just what I was looking for.