Why add required members and what problem do they solve?
Properties have always been part of the C# language. In the first version (released in 2002) you had to write a lot of boiler plate code because you had to implement the getter and the setter of the property and most of the time also define a private field (backing field) that you used to store the value of the property. Back in that day a class with a single public property looked like this.
Then with the release of C# 3.0 in 2007 a major feature auto-implemented-properties was added to the language. This meant that you did not have to specify your own backing field for the property and also the getter and setter where automatically implemented. A property on a class since then can look as the following.
In C# 3.0 another feature Object-initializers was added. This feature made it possible to set properties of an object upon creation. This made the following code possible.
When developing code and making a clean object model it is always a good practice to make sure an object can only exists when it has it’s properties correctly filled. To accomplish this you most of the time add properties or other dependencies that are required to the constructor parameters. In that way an object can only be created when you pass in the required values. This could for example look as the following.
The example above is very simple and has just a few parameters. But in case your class becomes larger the number of parameters to your constructor can increase. In that way it can occur that your code becomes less maintainable and readable. This will even be more the case when you have properties in a base class. In that case all the classes that inherit the base class have to pass all those arguments to the base constructor. For example:
Because of the features described before (auto-generated-properties and object-initializer) a lot of people (including myself sometimes) stopped using constructor parameters for some fields and initialized them using the object-initializer. But by doing this there are 2 major side effects:
- The properties are mutable. This means a property can be set upon creation of the object but also be changed after the object is created.
- It is not required that you set a property. This means you can create the object without setting a value. This can lead to objects that are not always correctly filled.
For the first side effect there was a new feature added with C# 9.0. In that release init-only setters where added. Those where added so that a property can only be set while creating the object. In that way a property becomes immutable because it’s value can not be changed after the object is created. An init only property looks as follows.
The second side effect however is not solved with the init-only-property feature. It is still possible to create an object without setting the values of all properties. This is where the new feature required members comes in place.
How is this solved with required members?
The required members feature adds a new modifier required to the language that can be used in front of a property. It will look as the following.
In the example above the FirstName property is marked as required. This means that when you create an instance of Person you have to set the FirstName property using the object-initializer. In that way the second side effect described above is solved. Just by adding the required modifier it is forced that the property is always set. For example:
I think this feature is a great addition to the language because it removes a lot of unnecessary code (especially when you have a lot of required members that also need to be passed in to a base class) and replaces it with this simple keyword. Using this keyword makes it easier to make your object model more valid without the need to create a constructor that takes a lot of arguments. I hope that this feature really makes it to the final release of C# 11 so we can start using it next November.
Tip: Check out SharpLab and select the required members branch to play around with this new feature. You can then also see what low-level C# code is generated to implement this functionality (using attributes).