Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles
(untagged)

Get Property Names Using Lambda Expressions in C#

0.00/5 (No votes)
25 Feb 2016 1  
Explains how to pass properties as methods' parameters via lambda expressions. Build utility to get the properties names from the lambda expressions. The post Get Property Names Using Lambda Expressions in C# appeared first on Automate The Planet.

Introduction

In this article, I am going to present to you how to get property and method names from lambda expressions. You can pass properties and methods as methods’ parameters. It is particularly useful for configuration purposes. In the last example, I am going refactor a little bit my PropertiesAssert utility so that you can set the properties not to be asserted as lambda expression instead of plain text. You can read more about the PropertiesAsserter utility here: Generic Properties Validator C# Code.

How to Pass Property as Lambda Expression Parameter?

First, you need to write a code that enables you to pass properties as parameters. It looks a little bit scary but don’t worry.

public static string GetMemberName<T>(Expression<Func<T, object>> expression)
{
    return GetMemberName(expression.Body);
}

Through the type Expression<Func<T, object>>, you pass the lambda expression for the property. T is the type of the class that holds the property.

The next step of the process is to create a utility method to get the name of the property from the lambda expression.

Build Lambda Expressions Reader

You will need two methods for the job. The main method contains logic to get the name of a property, a void method or a value type method. If you pass null expression or not supported expression, an ArgumentException is thrown.

private static string GetMemberName(Expression expression)
{
    if (expression == null)
    {
        throw new ArgumentException(expressionCannotBeNullMessage);
    }

    if (expression is MemberExpression)
    {
        // Reference type property or field
        var memberExpression = (MemberExpression) expression;
        return memberExpression.Member.Name;
    }

    if (expression is MethodCallExpression)
    {
        // Reference type method
        var methodCallExpression = (MethodCallExpression) expression;
        return methodCallExpression.Method.Name;
    }

    if (expression is UnaryExpression)
    {
        // Property, field of method returning value type
        var unaryExpression = (UnaryExpression) expression;
        return GetMemberName(unaryExpression);
    }

    throw new ArgumentException(invalidExpressionMessage);
}

private static string GetMemberName(UnaryExpression unaryExpression)
{
    if (unaryExpression.Operand is MethodCallExpression)
    {
        var methodExpression = (MethodCallExpression) unaryExpression.Operand;
        return methodExpression.Method.Name;
    }

    return ((MemberExpression) unaryExpression.Operand).Member.Name;
}

In my opinion, these methods are easier to use if they are built as extension methods. Also additionally, I added a method that accepts multiple properties’ lambda expressions. It is implemented through params operator. To pass a method as a lambda expression, use Expression<Action<T>> type for method’s parameter.

public static class NameReaderExtensions
{
    private static readonly string expressionCannotBeNullMessage = "The expression cannot be null.";
    private static readonly string invalidExpressionMessage = "Invalid expression.";

    public static string GetMemberName<T>
    (this T instance, Expression<Func<T, object>> expression)
    {
        return GetMemberName(expression.Body);
    }

    public static List<string> GetMemberNames<T>
    (this T instance, params Expression<Func<T, object>>[] expressions)
    {
        List<string> memberNames = new List<string>();
        foreach (var cExpression in expressions)
        {
            memberNames.Add(GetMemberName(cExpression.Body));
        }

        return memberNames;
    }

    public static string GetMemberName<T>
    (this T instance, Expression<Action<T>> expression)
    {
        return GetMemberName(expression.Body);
    }

    private static string GetMemberName(Expression expression)
    {
        if (expression == null)
        {
            throw new ArgumentException(expressionCannotBeNullMessage);
        }

        if (expression is MemberExpression)
        {
            // Reference type property or field
            var memberExpression = (MemberExpression) expression;
            return memberExpression.Member.Name;
        }

        if (expression is MethodCallExpression)
        {
            // Reference type method
            var methodCallExpression = (MethodCallExpression) expression;
            return methodCallExpression.Method.Name;
        }

        if (expression is UnaryExpression)
        {
            // Property, field of method returning value type
            var unaryExpression = (UnaryExpression) expression;
            return GetMemberName(unaryExpression);
        }

        throw new ArgumentException(invalidExpressionMessage);
    }

    private static string GetMemberName(UnaryExpression unaryExpression)
    {
        if (unaryExpression.Operand is MethodCallExpression)
        {
            var methodExpression = (MethodCallExpression) unaryExpression.Operand;
            return methodExpression.Method.Name;
        }

        return ((MemberExpression) unaryExpression.Operand).Member.Name;
    }
}

Test Drive of the NameExtensions Utility Class

Client client = new Client();
var propertyNames = client.GetMemberNames
(c => c.FistName, c => c.LastName, c => c.City);
foreach (var cPropertyName in propertyNames)
{
    Console.WriteLine(cPropertyName);
}
string nameOfTheMethod = client.GetMemberName(c => c.ToString());
Console.WriteLine(nameOfTheMethod);

As expected on the console is displayed - FirstName, LastName, City and ToString.

Improve PropertiesAsserter Configuration by Usage of Property Lambda Expressions

In summary, the main purpose of the PropertiesAsserter is to assert all properties of an object against an expected version. However, you should be able to configure it to skip the verification of some of the properties because you may not know what the expected value is - such as DateTime properties, history fields and so on.

In the first implementation of the utility, skip configuration of the properties not to be asserted, their names were passed as strings.

public class ObjectToAssertValidator : PropertiesValidator<ObjectToAssertValidator, ObjectToAssert>
{
    public void Validate(ObjectToAssert expected, ObjectToAssert actual)
    {
        this.Validate(expected, actual, "FirstName");
    }
}

As you can assume, the later approach is error-prompt because of the possibility of typo errors and renaming of the properties.

The primary method of the PropertiesAsserter can be refactored to accept params of properties’ lambda expressions. Internally, it will use the new lambda expressions’ utility to get the properties names.

public void Assert<T>(T expectedObject, T realObject, 
params Expression<Func<T, object>>[] propertiesNotToCompareExpressions)
{
    PropertyInfo[] properties = realObject.GetType().GetProperties();
    List<string> propertiesNotToCompare = 
		expectedObject.GetMemberNames(propertiesNotToCompareExpressions);
    foreach (PropertyInfo currentRealProperty in properties)
    {
        if (!propertiesNotToCompare.Contains(currentRealProperty.Name))
        {
            PropertyInfo currentExpectedProperty = 
		expectedObject.GetType().GetProperty(currentRealProperty.Name);
            string exceptionMessage =
                string.Format("The property {0} of class {1} was not as expected.", 
                currentRealProperty.Name, currentRealProperty.DeclaringType.Name);

            if (currentRealProperty.PropertyType != typeof(DateTime) && 
            currentRealProperty.PropertyType != typeof(DateTime?))
            {
                MSU.Assert.AreEqual(currentExpectedProperty.GetValue(expectedObject, null), 
                currentRealProperty.GetValue(realObject, null), exceptionMessage);
            }
            else
            {
                DateTimeAssert.AreEqual(
                    currentExpectedProperty.GetValue(expectedObject, null) as DateTime?,
                    currentRealProperty.GetValue(realObject, null) as DateTime?,
                    DateTimeDeltaType.Minutes,
                    5);
            }
        }
    }
}

Here is the improved configuration code.

public class ObjectToAssertAsserter : PropertiesAsserter<ObjectToAssertAsserter, ObjectToAssert>
{
    public void Assert(ObjectToAssert expected, ObjectToAssert actual)
    {
        this.Assert(expected,
                    actual,
                    e => e.LastName,
                    e => e.FirstName, 
                    e => e.PoNumber);
    }
}

So Far in the C# Series

1. Implement Copy Paste C# Code
2. MSBuild TCP IP Logger C# Code
3. Windows Registry Read Write C# Code
4. Change .config File at Runtime C# Code
5. Generic Properties Validator C# Code
6. Reduced AutoMapper- Auto-Map Objects 180% Faster
7. 7 New Cool Features in C# 6.0
8. Types Of Code Coverage- Examples In C#
9. MSTest Rerun Failed Tests Through MSTest.exe Wrapper Application
10. Hints For Arranging Usings in Visual Studio Efficiently
11. 19 Must-Know Visual Studio Keyboard Shortcuts – Part 1
12. 19 Must-Know Visual Studio Keyboard Shortcuts – Part 2
13. Specify Assembly References Based On Build Configuration in Visual Studio
14. Top 15 Underutilized Features of .NET
15. Top 15 Underutilized Features of .NET Part 2
16. Neat Tricks for Effortlessly Format Currency in C#
17. Assert DateTime the Right Way MSTest NUnit C# Code
18. Which Works Faster- Null Coalescing Operator or GetValueOrDefault or Conditional Operator
19. Specification-based Test Design Techniques for Enhancing Unit Tests
20. Get Property Names Using Lambda Expressions in C#
21. Top 9 Windows Event Log Tips Using C#

 

If you enjoy my publications, feel free to SUBSCRIBE
Also, hit these share buttons. Thank you!

Source Code

Reference

The post Get Property Names Using Lambda Expressions in C# appeared first on Automate The Planet.

All images are purchased from DepositPhotos.com and cannot be downloaded and used for free.
License Agreement

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here