C# 14 New Feature: Field-Backed Properties
.NET 10
In this talk, Ian Griffiths explains how C# 14's new field-backed properties feature can save you from metaphorically falling off a cliff when you need more flexibility beyond automatic properties' basic functionality.
He demonstrates the use of this feature to customize property setters without losing the simplicity and support of automatic properties. By allowing you to refer to the compiler-generated field inside get or set methods, C# 14 reduces verbosity and maintains code clarity and organization.
Learn how this small but impactful enhancement can improve your C# coding experience.
- 00:00 Introduction to C# 14's New Feature
- 00:30 Understanding Automatic Properties
- 01:11 Customizing Property Behavior
- 03:06 Introducing C# 14's New Syntax
- 04:21 Benefits of the New Feature
- 05:33 Conclusion
Transcript
C# 14 can save you from falling off a cliff with its new field-backed properties feature. Admittedly, the cliff is metaphorical. Sometimes when you're using a language or library feature, you can find yourself wanting to go beyond what that feature is able to support. And by moving outside the bounds of its support, you, so to speak, walk off the cliff and that sudden loss of support makes your life a lot harder.
Let me show you what I mean. I've got a very simple class here with a couple of properties. As you may know, this syntax where we use just the get and set keywords and optionally an accessibility modifier makes the compiler generate some code for us. It'll define a hidden field to hold the value and it supplies bodies for the get and set that use that field. The proper name for this is an automatically implemented property, but we typically shorten that to just auto property. This saves us from the tedious business of declaring a field and writing the obvious code to read and write the value in that field. It's not a huge deal, but if you're writing lots of properties, this offers worthwhile improvements in clarity and reduces work.
But what if we want slightly more than what C# generates for us? Notice this type defines an IsModified property. What if I want to set that anytime the Value property changes? Before C# 14, the only way to do that was to write a full property instead of an automatic property. Visual Studio can make that change for me. As you can see, this means declaring the field explicitly and having get and set accessors that use that field. Actually, Visual Studio doesn't quite get it right in this case because it hasn't noticed that the field name collides with the contextual keyword value inside the getter. So I need to qualify the field with a this reference.
And now this is almost identical to what the compiler was generating for me. I can now add in the extra feature that I wanted. So I'm just gonna adjust the layout and then use the full block syntax for the setter. And that gives me a place to put the code that sets the IsModified flag. Let's just run that and check that it worked.
And you can see that after I've set the Value property, the IsModified flag reflects that change as required. The obvious downside is that this is more verbose. It's not terrible. I can't complain about the fact that I've had to write the setter explicitly. The goal here was to customize that, but I've also got an explicitly implemented getter, which is effectively identical to what the compiler was generating, and I've also now got this field.
It's only a slight increase in clutter, but perhaps more concerning is the fact that it would be possible for other code in this class to use this field directly, bypassing my change detection. So the cliff wasn't a big one, it's a bit jarring, but this isn't a major problem. However, it comes up often enough that the C# team decided to support scenarios like this without forcing you to stop using automatic properties entirely.
In C# 14, I can leave the automatic get exactly as it is because I didn't actually wanna change that. Here I can customize just the setter. I can add this extra feature setting the IsModified flag. But how do I modify the value? Well, this is where I use the new syntax. Inside a get or set method, I can use the field keyword to refer to the compiler-generated field.
Let me just change the startup project and running that again, we get the required behavior. Comparing this to what we had to do before, you can see that this is a relatively small change. Before, I needed to declare my own field if I wanted to customize my property behavior, but now I can still get the compiler to generate that field for me.
Before, I had to write a custom getter, even though it was only the setter that I wanted to change. Now I can continue to use the compiler-generated getter. So this new feature has a fairly small impact, but what I prefer about the new code is that it removes clutter. I can see immediately that the getter does nothing out of the ordinary, so it's easy to see the one thing that makes this property slightly unusual.
The other benefit is I've not had to introduce a field. And while that's just one line of code, it's a line that doesn't sit all that well with some widely used .NET style guidelines that require fields to be declared in a separate part of the code from properties. Often the field and property could end up being quite distant, and when conceptually closely related code gets scattered across a file, it increases the work required to understand the code.
Arguably that's a flaw in the coding style guidelines, but for better or worse, it's a very common style in .NET that does provide benefits in some scenarios. Also, by using the compiler-generated field, I can be sure that the only code that modifies the field directly is this line here, and with the old approach, I'd need to search for other uses of the field to understand whether any code elsewhere in the class might be bypassing this change detection.
So in conclusion, C# 14 enables us to continue to enjoy the benefits of automatic properties, even when we move beyond their basic capabilities. So automatic properties have always given us a concise way to get basic property behavior, but now if we want to extend beyond that behavior, we can do so without having to fall off the cliff of support.
My name's Ian Griffiths. Thanks for listening.