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
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);
}
}
����������������������������������������������
