C# 11 proposes the new keyword required that can apply to an instance property or an instance field declaration within a class, a record or a struct.
|
1 2 3 4 |
class Foo { internal required int _RequiredField; internal required int RequiredProp { get; set; } } |
This keyword forces the caller to initialize the required field and property in the object initializer scope.
required init Property
This is quite a welcomed addition to improve object initialization scenario, especially concerning properties with an init setter that can only be initialized in the constructor and in the object initializer scope.
Limitations of Required Members
The keyword required comes with a few limitations illustrated by the screenshot below:
requiredcannot apply to a private member since the keyword’s restriction acts outside of the parent class, when a caller instantiates it. Notice that a field like_RequiredFieldin the first code sample should not be mutable publicly, as explained into this NDepend rule Fields should be declared as private. Thusrequiredfield is a code smell.requiredcannot apply to a static member since it is designed to be used only at object instantiation time.requiredcannot apply to a read-only member that can only be assigned from within a constructor.
Why required members?
Object initializer scope was introduced to overcome some constructor well-known limitations:
- Positional: With constructor, position of parameters must be respected by the caller.
- Lack of flexibility: if the client only wants a few states to be initialized (quite a common need in tests) he/she has to provide default values for other constructor parameters…
- Overloading: …unless the constructor provides many overloads or default parameters which leads to confusion.
- Breaking change: When introducing a new property, either all constructors must provide a default parameter or clients get broken.
- Redundancy: in a hierarchy of classes, when one add a new property to the base class, its constructor(s) has a new parameter that must be added in the constructors of all derived classes.
Until now it was not possible to require object initializer scopes to set a member which led to favor constructor in such scenario.
Required Member and Constructor
The keyword required is actually part of the public contract of all constructor(s) of the class. The compiler doesn’t attempt to detect if a constructor actually assigns it:
However the new .NET 7.0 attribute System.Diagnostics.CodeAnalysis.SetsRequiredMembersAttribute can tag a constructor to relieve this contract on the tagged constructor only. Thus this code below compiles properly:
|
1 2 3 4 5 6 7 8 9 10 11 12 |
using System.Diagnostics.CodeAnalysis; var foo = new Foo(); class Foo { [SetsRequiredMembers] internal Foo() { this.Prop = "hello"; } internal required string Prop { get; init; } } |
If in the code above if we comment this.Prop = "hello"; a CS8618 warning on the Foo() constructor explains that the non-nullable property Prop must contain a non-null value when exiting the constructor.
required and Property Overriding
There are a few limitations when it comes to required and property overriding:
- The keyword cannot be used in the context of an interface. As we saw the keyword
requiredinfluences the public contract of constructor(s) and an interface doesn’t have any constructor. requiredcannot be used in the context of explicit implementation overriding.- When overriding a
requiredvirtual or abstract property, the keywordrequiredmust be precised again.
required and record C# 9.0 positional property syntax
Since there is no parameterless constructor generated by the C# 9.0 positional property syntax, there is no need for the required keyword in this situation.
required decompiled
The keyword required leads the C# compiler to tag the parent class and required properties and fields with the new .NET 7.0 attribute System.Runtime.CompilerServices.RequiredMemberAttribute.
Conclusion
With constructors and object initializer scopes, C# has two syntaxes to deal with object instantiation. We saw that the new required keyword is a welcomed addition for object initializer situations. However this new keyword is not well suited when there is a mix of constructors overloading and init properties because of the need to tag constructors with [SetsRequiredMembers] which is not smooth syntax.







I find a lot wrong with your argument on the problems that required fixes.
“Positional: With constructor, position of parameters must be respected by the caller.”
This is not true, you can always choose to name your arguments so your constructor will look almost identical to your property initialization.
“Lack of flexibility: if the client only wants a few states to be initialized (quite a common need in tests) he/she has to provide default values for other constructor parameters…”
You shouldn’t be listing out every possible argument in the constructor. Simply reserve that for ones that are required and let users set the properties, by other means, on those they wish to set.
“Overloading: …unless the constructor provides many overloads or default parameters which leads to confusion.
“Breaking change: When introducing a new property, either all constructors must provide a default parameter or clients get broken.”
Once again, only add arguments to the constructor that you need to take. Also, you’d break all clients by adding a required property just as you would by adding a new argument to the constructor.
At the end of the day, required is a way to try and replace constructors…something that I don’t understand. There is nothing wrong with constructors and this syntax really just goes to prove it. It’s yet another junk feature added in that only furthers bloating the language.