Click here to Skip to main content
14,837,089 members
Articles / Programming Languages / C#
Technical Blog
Posted 10 Jan 2016

Tagged as

Stats

37.3K views
31 bookmarked

Get Property Names Using Lambda Expressions in C#

Rate me:
Please Sign up or sign in to vote.
4.76/5 (17 votes)
25 Feb 2016Ms-PL3 min read
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.

Image 1

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.

C#
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.

C#
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.

C#
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

C#
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.

Image 2

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.

C#
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.

C#
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.

C#
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, along with any associated source code and files, is licensed under The Microsoft Public License (Ms-PL)

Share

About the Author

Anton Angelov
CEO Automate The Planet
Bulgaria Bulgaria
CTO and Co-founder of Automate The Planet Ltd, inventor of BELLATRIX Test Automation Framework, author of "Design Patterns for High-Quality Automated Tests: High-Quality Test Attributes and Best Practices" in C# and Java. Nowadays, he leads a team of passionate engineers helping companies succeed with their test automation. Additionally, he consults companies and leads automated testing trainings, writes books, and gives conference talks. You can find him on LinkedIn every day.

Comments and Discussions

 
QuestionBetter give example how to use GetMemberName with nested properties and as extension method. Pin
AlexNaiden7-Dec-17 9:55
MemberAlexNaiden7-Dec-17 9:55 
QuestionCode Pin
Madmaximus24-Jul-17 3:37
MemberMadmaximus24-Jul-17 3:37 
AnswerC# 6 nameof operator Pin
Anandpatel2321-Jun-16 15:06
MemberAnandpatel2321-Jun-16 15:06 
GeneralRe: C# 6 nameof operator Pin
Anton Angelov21-Jun-16 23:00
MemberAnton Angelov21-Jun-16 23:00 
You are right. However, you can use it only with Visual Studio 2015. Which might be a problem for most people.
QuestionPicture... Pin
Nelek7-Feb-16 0:04
protectorNelek7-Feb-16 0:04 
AnswerRe: Picture... Pin
Anton Angelov7-Feb-16 0:51
MemberAnton Angelov7-Feb-16 0:51 
GeneralRe: Picture... Pin
Nelek7-Feb-16 22:10
protectorNelek7-Feb-16 22:10 
Questionunsafe Pin
dmjm-h12-Jan-16 9:42
Memberdmjm-h12-Jan-16 9:42 
AnswerRe: unsafe Pin
Anton Angelov12-Jan-16 21:26
MemberAnton Angelov12-Jan-16 21:26 
SuggestionNew in C#6... Pin
Frederico Barbosa12-Jan-16 1:58
MemberFrederico Barbosa12-Jan-16 1:58 
GeneralRe: New in C#6... Pin
Anton Angelov12-Jan-16 3:47
MemberAnton Angelov12-Jan-16 3:47 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.