Click here to Skip to main content
13,898,182 members
Click here to Skip to main content
Add your own
alternative version

Tagged as

Stats

28.1K views
31 bookmarked
Posted 10 Jan 2016
Licenced Ms-PL

Get Property Names Using Lambda Expressions in C#

, 25 Feb 2016
Rate this:
Please Sign up or sign in to vote.
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, 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
Anton Angelov is an IT Consultant and Quality Assurance Architect at Innovative Lab. He is passionate about automation testing and designing test harness and tools, having the best industry development practices in mind. In addition, he is an active blogger and the founder of Automate The Planet. He strives to make the site one of the leading authorities in Automation Testing by presenting compelling articles, inspiring ardent discussions amongst the community. He is also one of the most-rated-answer authors of questions about Test Automation Frameworks (WebDriver) on Stack Overflow.

You may also be interested in...

Pro

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

Permalink | Advertise | Privacy | Cookies | Terms of Use | Mobile
Web05 | 2.8.190306.1 | Last Updated 25 Feb 2016
Article Copyright 2016 by Anton Angelov
Everything else Copyright © CodeProject, 1999-2019
Layout: fixed | fluid