← Open in the full interactive course (progress tracking, search & more)

A constructor forces every caller down one fixed path. required keeps object initializers flexible, while still refusing to let anyone skip the fields that actually matter.

Object initializer syntax — new Customer { Name = "Alice", Email = "alice@example.com" } — is genuinely pleasant to read and write. You can set exactly the properties you care about, in any order, without threading them all through a constructor's parameter list. There was just one problem: nothing stopped you from forgetting one.

var customer = new CustomerDto { Name = "Alice" }; // compiles fine — Email is just... missing

If Email was supposed to be mandatory, nothing here caught that mistake. You'd either add a constructor (losing the nice named-property syntax) or find out at runtime, possibly much later, that Email was null when it never should have been.

In this lesson, you'll learn the required modifier, how it interacts with constructors and object initializers, and why it exists as an alternative — and a complement — to constructor-mandated initialization.

What Is It?

The Simple Explanation

The required modifier marks a property (or field) as something every caller must set when creating the object — using object initializer syntax — or the code simply won't compile. It's a way to keep the flexible, named, any-order style of new Thing { A = 1, B = 2 }, while still guaranteeing that certain properties are never left unset by accident.

The Technical Definition

The required modifier (C# 11) can be applied to an instance property or field that has (at minimum) an init or set accessor. When present, the compiler enforces — as a compile-time error, not a warning — that every object-creation expression for that type explicitly assigns that member via object-initializer syntax, unless the constructor being used is itself annotated with [SetsRequiredMembers] to promise the compiler it already handles the assignment internally.

Why Does It Exist?

The Problem

Before C# 11, there were exactly two ways to guarantee a property was always set, and both cost you something:

Option 1 — a constructor

Option 2 — an object initializer, unenforced

The Need

Developers needed a third option: the readability of object initializers, combined with the same enforced guarantee a constructor gives you — verified at compile time, so a missing required value is caught the moment you write the code, not discovered later.

The Solution

The required modifier:

public class CustomerDto
{
    public required string Name { get; init; }
    public required string Email { get; init; }
    public string? PhoneNumber { get; init; } // genuinely optional
}

var c1 = new CustomerDto { Name = "Alice", Email = "alice@example.com" }; //  compiles
var c2 = new CustomerDto { Name = "Bob" }; //  CS9035: required member 'Email' must be set

You keep the named, any-order initializer syntax, and the compiler now refuses to let anyone forget Email.

Big Picture

THREE WAYS TO GUARANTEE A VALUE IS SET
Constructor
Enforced, but positional and rigid
Plain initializer
Flexible, but not enforced
required + init
Flexible and enforced

How It Works

required — STEP BY STEP
1. MARK THE PROPERTY required
public class Product
{
    public required string Sku { get; init; }
    public required decimal Price { get; init; }
}
2. THE COMPILER ENFORCES IT AT EVERY CREATION SITE
var p1 = new Product { Sku = "ABC-1", Price = 19.99m }; // 
var p2 = new Product { Sku = "ABC-1" };                   //  missing required member 'Price'
3. A CONSTRUCTOR CAN SATISFY required MEMBERS TOO — WITH [SetsRequiredMembers]
public class Product
{
    public required string Sku { get; init; }
    public required decimal Price { get; init; }

    public Product() { } // still usable with an object initializer

    [SetsRequiredMembers]
    public Product(string sku, decimal price)
    {
        Sku = sku;
        Price = price;
    }
}

var p3 = new Product("ABC-1", 19.99m); //  the constructor is trusted to have set both
4. required WORKS ALONGSIDE NULLABLE REFERENCE TYPES

Simple Example

public class UserProfile
{
    public required string Username { get; init; }
    public required string Email { get; init; }
    public string? Bio { get; init; }        // truly optional
    public bool IsVerified { get; init; }     // has a sensible default (false), no need to require it
}

var profile = new UserProfile
{
    Username = "alice_codes",
    Email = "alice@example.com"
    // Bio and IsVerified are left at their defaults — that's fine, they're not required
};

// var broken = new UserProfile { Bio = "Hi there!" };
//  CS9035: Required member 'UserProfile.Username' must be set.
//  CS9035: Required member 'UserProfile.Email' must be set.

Meaning: Username and Email are non-negotiable — every single construction of UserProfile, anywhere in the codebase, is guaranteed by the compiler to have set them. Bio and IsVerified stay genuinely optional, with sensible defaults.

Real-World Example

An API layer deserializing incoming requests into a DTO is exactly where required earns its keep — it turns "the client forgot to send an ID" into a build-time guarantee for your own code, and a clear deserialization failure for missing client data:

public class CreateOrderRequest
{
    public required string CustomerId { get; init; }
    public required List<OrderLineDto> Lines { get; init; }
    public string? PromoCode { get; init; } // optional — not every order has one
}

public class OrderLineDto
{
    public required string ProductSku { get; init; }
    public required int Quantity { get; init; }
}

// Anywhere in your OWN code that builds a CreateOrderRequest, the compiler
// guarantees CustomerId and Lines are always provided:
var request = new CreateOrderRequest
{
    CustomerId = "CUST-42",
    Lines = [ new OrderLineDto { ProductSku = "SKU-1", Quantity = 2 } ]
};

// System.Text.Json also understands `required` when deserializing:
// if incoming JSON is missing "customerId" or "lines", deserialization
// throws a JsonException immediately, instead of silently leaving a null.

Notice the two layers of protection working together: required stops your own code from ever constructing an incomplete CreateOrderRequest, and the same annotation is honored by System.Text.Json, so a malformed request from an external client fails fast during deserialization instead of quietly producing an object with missing data that only breaks somewhere further downstream.

Under the Hood

WHAT required ACTUALLY IS
A COMPILE-TIME CHECK, BACKED BY A RUNTIME ATTRIBUTE

Common Confusion

1. required vs a non-nullable type — related, but not the same guarantee

A non-nullable property (string Name) tells the compiler "this should never hold null" — but without required or constructor initialization, nothing actually stops you from constructing the object and leaving it unset (you just get a warning). required is what closes that gap for object-initializer construction specifically, turning "should never be null" into "genuinely cannot be skipped."

2. required doesn't replace constructors — it complements them

You can absolutely still write a full constructor for a class with required members — you just need [SetsRequiredMembers] on it if you want callers to be able to use that constructor without also supplying an object initializer. Many types offer both: a constructor for the common, positional case, and required properties so the same type also works safely with a plain object initializer.

3. required only needs set or init — not necessarily init specifically

A required property can use a regular set accessor instead of init, meaning it can still be reassigned after construction. Most designs pair required with init because they usually go together conceptually (mandatory and immutable once set) — but the two are independent features, and combining them is a choice, not a requirement of the language.

Common Mistakes

Mistake 1 — Marking everything required, even properties with sensible defaults

public class Settings
{
    public required bool DarkMode { get; init; }     // does every caller REALLY need to think about this?
    public required int RetryCount { get; init; }    // ...or is 3 a perfectly good default?
}

Reserve required for properties that genuinely have no safe default — where forcing every caller to think about the value is the correct behavior, not busywork. Give the rest a sensible default instead.

Mistake 2 — Adding a positional constructor and forgetting [SetsRequiredMembers]

public class Product
{
    public required string Sku { get; init; }

    public Product(string sku) { Sku = sku; } // missing [SetsRequiredMembers]
}

var p = new Product("ABC-1"); //  still requires an object initializer too — surprising!

Add [SetsRequiredMembers] to any constructor you want to trust as fully satisfying the required members on its own.

Mistake 3 — Assuming required validates the value, not just its presence

required only guarantees that some value was assigned — it says nothing about whether that value is meaningful. new CustomerDto { Name = "", Email = "" } compiles perfectly fine; both required members were technically "set," just to empty strings. For actual data validation (non-empty, correctly formatted, within range), you still need real validation logic — required solves "was this forgotten," not "is this correct."

When Should I Use It?

Rule of thumb: If a property has no reasonable default and forgetting it would produce a broken or meaningless object, mark it required. If it has a sensible default most callers would choose anyway, leave it optional.

Mental Model

required = "You may set my properties in any order you like — but you may not skip this one."

Remember:
· required needs at least init or set.
· A constructor can satisfy required members too, but only if marked [SetsRequiredMembers].
· required guarantees a value was assigned — not that it's a valid value.

Key Takeaway


Check Your Understanding

You've seen how required keeps object initializers flexible while still enforcing the properties that matter. Let's check your understanding.

1. What happens if you write new CustomerDto { Name = "Bob" } when CustomerDto.Email is marked required and isn't set here?

Show answer

Correct: B

Why B is correct: required is enforced at compile time, as a genuine error (not a warning). Any object-initializer construction that omits a required member simply fails to build.

Why A is incorrect: This is exactly the behavior required exists to prevent — without it, this would compile and quietly leave Email unset.

Why C is incorrect: The check happens well before runtime — the code never even builds, so there's no chance to run and throw later.

Why D is incorrect: required doesn't supply any automatic default value — it forces the caller to explicitly provide one.

Reinforcement: required converts "might forget to set this" into "the code won't even compile if you forget."

2. A class has a required property and also defines a full constructor that sets it. Callers report they still can't call the constructor alone without also using an object initializer. What's the most likely cause?

Show answer

Correct: B

Why B is correct: A constructor can satisfy required members, but only if it's explicitly marked [SetsRequiredMembers] — that attribute is the promise to the compiler that this particular constructor already handles the assignment, so an additional object initializer isn't needed.

Why A is incorrect: Constructors absolutely can satisfy required members — that's exactly what [SetsRequiredMembers] is for.

Why C is incorrect: static has nothing to do with this — required members are instance members by nature, since they're set per-object.

Why D is incorrect: The two features are explicitly designed to work together via [SetsRequiredMembers] — they are not incompatible.

Reinforcement: Without [SetsRequiredMembers], the compiler still insists on an object initializer for required properties, even alongside a constructor that already sets them.

3. new UserProfile { Username = "", Email = "" } compiles successfully even though both are marked required. What does this tell you about what required actually guarantees?

Show answer

Correct: A

Why A is correct: required checks only whether a member was explicitly assigned during construction — it has no concept of "valid" data. An empty string is still a real, assigned value, so the requirement is satisfied even though the data itself may not be meaningful for your application's rules.

Why B is incorrect: required is working exactly as designed here — it caught the "was something assigned" question, which is all it was ever meant to answer.

Why C is incorrect: There's no automatic rejection of empty strings — required has no built-in data validation logic at all.

Why D is incorrect: required works on both reference and value type properties equally; this isn't a type-category limitation.

Reinforcement: For real data validation (non-empty, correctly formatted, within range), you still need explicit validation logic — required only solves the "was this forgotten" problem.

4. A Settings class has a RetryCount property that works perfectly well with a default of 3 for almost every caller. Should RetryCount be marked required?

Show answer

Correct: B

Why B is correct: required is meant for properties with no safe default, where forgetting them would produce a broken or meaningless object. A property with a genuinely sensible default doesn't benefit from being required — it just forces unnecessary repetition on every caller.

Why A is incorrect: Overusing required adds friction without adding meaningful safety, and works against the goal of keeping object initializers pleasant to use.

Why C is incorrect: Whether a constructor exists is unrelated to whether a property deserves to be required — that decision is about whether a safe default exists.

Why D is incorrect: required very much affects usability — it forces every caller to explicitly state a value, which is only worth the cost when there's no reasonable default.

Reinforcement: Reserve required for properties with no safe default — use it deliberately, not as a blanket default for every property.

You now know how to keep object initializers flexible while guaranteeing the properties that genuinely can't be skipped.


dotnetmadeeasy.com — Learn C# and .NET, the right way.