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.
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.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:
That's it--now you no longer have to worry about passing property names as strings!