Required Members: Compile-Time Guarantees for Object Initialisation

Object initialisers have been in C# since version 3, but they have always had a blind spot: nothing forces a caller to set the properties that matter. You could create a Person with a name but no email, or a Configuration missing its connection string, and the compiler would not complain. C# 11's required modifier finally closes that gap.

The Problem

Consider a simple DTO:

Example.cs
public class UserRegistration
{
    public string Email { get; set; }
    public string DisplayName { get; set; }
    public string? Bio { get; set; }
}

Nothing prevents this:

Example.cs
var registration = new UserRegistration
{
    Bio = "Hello world"
    // Email and DisplayName silently default to null
};

The traditional fix was to add a constructor with required parameters, but that defeats the purpose of object initialisers and creates friction when you have many properties with a mix of required and optional values.

The Solution

The required modifier marks properties and fields that must be set by the caller via an object initialiser or constructor:

Example.cs
public class UserRegistration
{
    public required string Email { get; set; }
    public required string DisplayName { get; set; }
    public string? Bio { get; set; }
}

Now the compiler enforces it:

Example.cs
// Error CS9035: Required member 'Email' must be set
var registration = new UserRegistration
{
    DisplayName = "Alice"
};

// This compiles
var registration = new UserRegistration
{
    Email = "[email protected]",
    DisplayName = "Alice"
};

Required Members and Constructors

If you have a constructor that sets the required properties, you can mark it with [SetsRequiredMembers] to tell the compiler that callers using that constructor do not need to set them again:

Example.cs
public class UserRegistration
{
    public required string Email { get; set; }
    public required string DisplayName { get; set; }
    public string? Bio { get; set; }

    public UserRegistration() { }

    [SetsRequiredMembers]
    public UserRegistration(string email, string displayName)
    {
        Email = email;
        DisplayName = displayName;
    }
}

// Both of these compile:
var a = new UserRegistration("[email protected]", "Alice");
var b = new UserRegistration
{
    Email = "[email protected]",
    DisplayName = "Alice"
};

Note that [SetsRequiredMembers] is a trust-based attribute — the compiler does not verify that the constructor actually assigns the members. It is your responsibility to ensure correctness.

Inheritance

Required members compose well with inheritance. A derived class can add its own required members, and all required members from the entire hierarchy must be set:

Example.cs
public class Entity
{
    public required Guid Id { get; set; }
}

public class AuditableEntity : Entity
{
    public required DateTime CreatedAt { get; set; }
    public required string CreatedBy { get; set; }
}

var entity = new AuditableEntity
{
    Id = Guid.NewGuid(),
    CreatedAt = DateTime.UtcNow,
    CreatedBy = "system"
};

Omitting any of Id, CreatedAt, or CreatedBy produces a compile-time error.

Required Members on Records

Required members work with records, giving you the best of both worlds — positional syntax for some parameters and required initialisers for others:

Example.cs
public record Product(string Sku)
{
    public required string Name { get; init; }
    public required decimal Price { get; init; }
    public string? Description { get; init; }
}

var product = new Product("SKU-001")
{
    Name = "Widget",
    Price = 9.99m
};

Practical Patterns

Required members work particularly well for configuration and options classes:

Example.cs
public class SmtpOptions
{
    public required string Host { get; set; }
    public required int Port { get; set; }
    public string? Username { get; set; }
    public string? Password { get; set; }
    public bool UseTls { get; set; } = true;
}

This makes it impossible to forget the host or port whilst keeping optional settings flexible. It is a pattern that self-documents which values are essential.

Limitations

There are a few things to be aware of. Required members cannot be used with positional record parameters — those are already required by the constructor. The [SetsRequiredMembers] attribute is all-or-nothing; you cannot indicate that a constructor sets only some required members. And serialisation frameworks need to be aware of required members — System.Text.Json handles them correctly from .NET 7 onwards, but third-party libraries may need updates.

When to Adopt

If you have types where certain properties must always be set and you prefer object initialisers over constructors, required is a straightforward improvement. It turns runtime NullReferenceException surprises into compile-time errors, which is always a worthwhile trade.