Wednesday, January 7, 2015

Passing Property Names with C# Lambdas

How many times have you heard that using magic strings is a bad practice? Imagine you are passing a property name to a function that is going to use reflection to read its value. How do you ensure that the string is typo free and accurately represents the property? You could write a unit test to validate the name being passed, but there's nothing stopping you from copying and pasting the typo; you could create a static class and store the names as static, read-only properties; or maybe even retrieve it from another source that could be internal or external to the project. The problem with all these methods is that if there is any change to the property name itself, they also need to be updated.  

var book = new Book()
{
    Author = "George R. R. Martin",
    Title = "A Game Of Thrones",
    Publisher = "Bantam"
};

Console.WriteLine(ReadProperty(book, "Author"));

public object ReadProperty(object source, string propertyName)
{
    var propertyInfo = source.GetType().GetProperty(propertyName);

    return propertyInfo.GetValue(source);
}

If the property's name has been changed, this will not be detected until the function tries to retrieve the PropertyInfo for the property that has been renamed. As it no longer exists, this will result in a NullException being thrown. 

Miguel Castro introduces a simple solution for this in his Pluralsight course that ensures you are only using valid properties. This solution uses a lambda expression to validate that the property identified in the expression actually exists and if it doesn't, a compile error will result. By passing the name as a strongly typed property instead of as a string, you are rewarded with a safety net against typos and changes.

var book = new Book()
{
    Author = "George R. R. Martin",
    Title = "A Game Of Thrones",
    Publisher = "Bantam"
};

Console.WriteLine(ReadProperty(book, () => book.Author));

public object ReadProperty<T>(object source, Expression<Func<T>> propertyExpression)
{
    var memberExpression = propertyExpression.Body as MemberExpression;
    var propertyName = memberExpression.Member.Name;
    var propertyInfo = source.GetType().GetProperty(propertyName);

    return propertyInfo.GetValue(source);
}

The Expression wrapping the Func of T causes the compiler to "emit code to build an expression tree that represents the lambda expression." This provides the functionality to allow us to very easily interrogate the lambda expression and retrieve the name of the property that was identified.

If you are familiar with the ASP.NET MVC framework and HtmlHelpers, you should recognize the application of the above pattern as this is exactly what is being used to identify a property on the view model:


As the in type of the Func is set as the type of the view model, this limits the scope of the properties that can be identified to only those present on the view model; the out type (labeled as TProperty) still remains as the type of the property being identified.

That's it--now you no longer have to worry about passing property names as strings!

3 comments:

  1. Shopping online arrived into existence extended back again but initially it had been preferred mainly for that overseas level buying, but slowly because of the factors home sales like pc literacy for anyone, cell phones, growth inside business and accessibility of internet made internet commerce really well-known and simple

    ReplyDelete
  2. Shopping online arrived into existence extended back again but initially it had been preferred mainly for that overseas level buying, but slowly because of the factors home sales like pc literacy for anyone, cell phones, growth inside business and accessibility of internet made internet commerce really well-known and simple

    ReplyDelete