I want to use this post to discuss a risky software engineering pattern, and how to avoid being hurt by this pattern. Particularly in software engineering, there is a tendency to rely on tools to tell us how to improve our code. These tools suggest code style improvements, pattern improvements, as well as potential bug fixes. They are awesome these tools, but they are sharp…so we must use them with awareness.
Let’s use C# properties as the code and ReSharper as the tool. This example is not the only case by any means, but is meant to highlight a risky pattern of doing what our tools tell us to do without fully understanding the ramifications.
As of C# 6.0 there are multiple ways in which to define a property and multiple ways in which to initialize it. The following Person class demonstrates each of these ways.
Person class includes all of the following:
- Field-backed Property
- Auto-implemented Property
- Expression-bodied Property
- Field Initialization
- Constructor Initialization
- Property Initialization
Field-backed properties have been part of the C# language specification from the beginning. Their syntax is straightforward and you get what you see. But just for completeness, the getter block is called every time you read a property’s value. So in the case of Weight, every time you read the property, “return itsWeight;” is processed.
C# 3.0 introduced auto-implemented properties. In the Person class, Alive and LastName are auto-implemented properties. There is no explicit backing field for these properties – they are auto-implemented. Following the language change, ReSharper began preferring auto-implemented properties in various ways. The tool defaulted to generating them in some cases, suggested replacement in others, but in general encouraged the developer to switch from field-backed to auto-implemented properties. There was very little risk involved so I won’t be digging in on this one. I will say that blindly using auto-implemented properties does introduce unwanted complexity around type member reflection, but that is generally an edge case.
C# 6.0 introduced expression-bodied properties. Id and FirstName are expression-bodied properties. The idea here is that the property is a convenience for evaluating whatever expression is associated with it. Just like with auto-implemented properties, ReSharper began preferring this syntax. Unfortunately this syntax gets visually confused by some developers as an initialization. It is NOT an initialization. It is evaluated every single time you get/set the property. This has led to numerous bugs and is a great example of being fully aware of what your tool is doing to Your Code.
Let’s take a look at what all this syntactic sugar equates to.
There are several things you want to be aware of here…and keep that awareness when dealing with ReSharper and properties:
- Id and FirstName, as implicit expression-bodied getters, are expanded to explicit getter syntax.
- Alive and LastName, as auto-implemented properties, are expanded to field-backed syntax.
- Alive, as Property Initializer syntax, is compiled to Field Initializer syntax.
I really hope this post highlights the underlying priciple: Know Your Tool. A good tool is kept sharp…you just don’t want to let it cut you.