Get Property Names Using Lambda Expressions in C#

Get Property Names Using Lambda Expressions in C#

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: https://automatetheplanet.com/generic-properties-validator-csharp-code/”>**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> 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);

Improve PropertiesAsserter Configuration by Usage of Property Lambda Expressions

public class ObjectToAssertValidator : PropertiesValidator<ObjectToAssertValidator, ObjectToAssert>
{
    public void Validate(ObjectToAssert expected, ObjectToAssert actual)
    {
        this.Validate(expected, actual, "FirstName");
    }
}
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);
    }
}

����������������������������������������������

Related Articles

Development

Neat Tricks for Effortlessly Formatting Currency in C#

In the article, I am going to present to you a few ways how to create a handy class that effortlessly formatting currency in C#. Initially, I came up with the i

Neat Tricks for Effortlessly Formatting Currency in C#

Development

Types Of Code Coverage- Examples In C#

Code coverage analysis is used to measure the quality of software testing, usually using dynamic execution flow analysis. There are many different types of code

Types Of Code Coverage- Examples In C#

Development

Which Works Faster- Null Coalescing Operator or GetValueOrDefault or Conditional Operator

After my article Top 15 Underutilized Features of .NET provoked an interesting discussion. I was curious to learn which method is faster- ?? (null coalescing op

Which Works Faster- Null Coalescing Operator or GetValueOrDefault or Conditional Operator

Development

Embracing Non-Nullable Reference Types in C# 8

Microsoft launched its latest version of the C# language – C# 8.0 – in September 2019. It came with many new features or improvements like readonly struct membe

Embracing Non-Nullable Reference Types in C# 8

Development, Resources

Most Complete MSTest Unit Testing Framework Cheat Sheet

An essential part of every UI test framework is the usage of a unit testing framework. One of the most popular ones in the .NET world is MSTest. However, you ca

Most Complete MSTest Unit Testing Framework Cheat Sheet

Development

Top 15 Underutilized Features of .NET Part 2

In this article, I'm going to share with you even more underutilized features of C# language (underutilized). If you missed the first publication you should che

Top 15 Underutilized Features of .NET Part 2
Anton Angelov

About the author

Anton Angelov is Managing Director, Co-Founder, and Chief Test Automation Architect at Automate The Planet — a boutique consulting firm specializing in AI-augmented test automation strategy, implementation, and enablement. He is the creator of BELLATRIX, a cross-platform framework for web, mobile, desktop, and API testing, and the author of 8 bestselling books on test automation. A speaker at 60+ international conferences and researcher in AI-driven testing and LLM-based automation, he has been recognized as QA of the Decade and Webit Changemaker 2025.