One of the most interesting C# 11 features which was introduced with Visual Studio 2022 17.3 is the required modifier. The required modifier enforces the consuming code to initialize the fields/properties either via constructors (when the constructor is decorated with SetsRequiredMembersAttribute) or using Object initializers every time the type is initialized.
These are extremely useful when you want to ensure the consumers of your class initialize the mandatory fields or properties.
required modifier
Consider the following code.
public class Foo{
public required string Name{ get; init; }
}
The required modifier for the property Name ensures that the client needs to initialize the property when creating an instance of Foo. If you attempt to create an instance of the Foo class without initializing the Name property, the compiler would warn you against it.
var foo = new Foo();
// Compile Error
// Error CS9035 Required member 'Foo.Name' must be set in the object initializer or attribute constructor. RequiredMembers (net7.0)
In the absence of a constructor without the SetsRequiredMembersAttribute (which we will discuss in a short while), the consuming code needs to use the object initializers. Following is a valid code.
var foo = new Foo() { Name = "John Doe" };
SetsRequiredMembersAttribute
The SetsRequiredMembersAttribute allows the developers to mark the constructors which initializes all the required members. Consider the following code.
public class Bar{
[SetsRequiredMembers]
public Bar() => Name = String.Empty;
public Bar(string name) => Name = name;
public required string Name{ get; set; }
}
The Bar class has a single property Name, which is marked with the required modifier. It also has two constructors - a Parameterless Constructor and a constructor that accepts a single parameter_.
In the above example, the parameterless constructor is marked with the SetsRequiredMembersAttribute. This tells the compiler that if the consuming client code initializes the object instance using the particular constructor, then it can safely assume that all the required properties in the class are initialized and do not need to explicitly initialize the properties using object initializers.
// Following compiles
var bar = new Bar();
On other hand, in the above code, the constructor that accepts a single parameter is not decorated with the SetsRequiredMembersAttribute . Consequently, if the consuming code uses the mentioned constructor to initialize objects without explicitly initializing the properties using object initializer, the compiler would throw error.
var bar = new Bar("John Doe");
// Compiler throws following error
// Error CS9035 Required member 'Bar.Name' must be set in the object initializer or attribute constructor. RequiredMembers (net7.0)
If you observe the Bar class, the single parameter constructor in fact does initialize the required member. However, due to the lack of SetsRequiredMembersAttribute, the compiler is unable to detect that the constructor has successfully initialized all the required members.
There are few rules which govern the behavior.
- The required member modified cannot be used with interface.
- The required member should be at least as visible as the containing type.
- Derived class cannot hide a required member and if overriden, should always include the required modifier.
- If a constructor is chained (using base() or this())to another constructor which is decorated by the SetsRequiredMembersAttribute, then it should also be decorated with the SetsRequiredMembersAttribute.
For more rules on required modifier, refer to the official documentation. The source referred to in this post can be accessed from my Github.