C# 11 Required Members for Classes and Structs
C# 11 introduced the required modifier, a feature
designed to enforce proper object initialization for classes and
structs. This article provides a clear guide on what required members
are, why they were introduced, and how to implement them effectively. By
using this keyword, developers can ensure that callers populate critical
fields or properties during instantiation, reducing boilerplate
constructor code while maintaining type safety and preventing
uninitialized state bugs.
What Are Required Members?
The required modifier can be applied to fields and
properties within class, struct, and
record definitions. When a member is marked as
required, any code creating an instance of that type using
an object initializer must explicitly set that member.
If the consuming code fails to assign a value to a required member during initialization, the C# compiler issues a compile-time error.
public class User
{
public required string FirstName { get; init; }
public required string LastName { get; init; }
public string? MiddleName { get; init; }
}
// Valid instantiation:
var user = new User
{
FirstName = "Jane",
LastName = "Doe"
};
// Compiler Error CS9035: Required member 'User.LastName' must be set.
var invalidUser = new User
{
FirstName = "Jane"
};Why Were Required Members Introduced?
Before C# 11, developers typically faced two suboptimal approaches for enforcing mandatory data:
- Constructors with Parameters: Writing constructors that accept every required field guarantees initialization, but it leads to excessive boilerplate, especially with inheritance, and hampers the readability of object initializers.
- Object Initializers with Nullable Checks: Relying purely on standard properties allows for flexible object initializers, but the compiler cannot guarantee that consumers will assign values, potentially violating non-nullable reference types.
The required modifier bridges this gap. It allows
developers to use clean object initializer syntax while ensuring that
non-nullable and mandatory fields are never omitted.
Rules and Behaviors
1. Visibility Constraints
A required member must be at least as visible as its containing type.
For example, a public class cannot have a
protected or private required property,
because external consumers would be unable to initialize it.
2. Usable with init and
set
The required modifier works with both mutable setters
(set) and immutable init-only setters (init).
It is most commonly paired with init to enforce
immutability after creation.
3. Inheritance
When a class derives from a base class with required members, the derived class inherits those requirements. Callers instantiating the derived class must initialize required members from both the base and the derived classes.
The
[SetsRequiredMembers] Attribute
In scenarios where a class provides a parameterized constructor that
assigns all required members internally, the
[SetsRequiredMembers] attribute can be used. This tells the
compiler that the constructor takes care of initializing the required
properties, exempting the caller from using an object initializer.
using System.Diagnostics.CodeAnalysis;
public class User
{
public required string FirstName { get; init; }
public required string LastName { get; init; }
public User() { }
[SetsRequiredMembers]
public User(string firstName, string lastName)
{
FirstName = firstName;
LastName = lastName;
}
}
// Valid: Constructor fulfills the required contract
var userFromConstructor = new User("Jane", "Doe");
// Valid: Object initializer fulfills the required contract
var userFromInitializer = new User { FirstName = "Jane", LastName = "Doe" };When using [SetsRequiredMembers], any derived class
constructor calling that base constructor must also declare
[SetsRequiredMembers] to maintain initialization
safety.