Modern C# · Lesson 14

Required members are a construction contract

Separate compiler-enforced initialization from runtime validation.

How it works

A required member asks callers to initialize a property or field when they construct an object. Profile uses this rule to make a missing Name an obvious compilation problem in ordinary C# construction. The object initializer supplies the member, so the first instance can be created and its name printed.

The second instance demonstrates the boundary of that guarantee. An empty string is still an assigned value. The compiler sees an initialization and accepts it, even though a person's display name may need additional business validation. Required initialization, nullable annotations, and rules such as nonblank text solve different problems.

An init accessor also restricts when callers can assign the property. The object initializer is a construction context; a later ordinary assignment to user.Name would not compile. That restriction helps communicate intended ownership, but it does not make every object reachable from the profile immutable. A mutable collection property could still be edited.

Do not demonstrate this feature by leaving the required member out of the runnable sample: that would prevent the rest of the exercise from executing. Instead, temporarily remove Name from the first initializer and use Check to inspect the compiler diagnostic. Restore it before exploring the runtime behavior of an empty name.

Practice by adding a required CountryCode and a validation function returning all detected problems. Check a complete valid profile, a blank name, and an unsupported code. Keep the initializer requirements in the type and the domain rules in the validation boundary, so each error explains which contract was violated.

When reading data from outside your program, inspect the serializer's own required-member behavior rather than assuming ordinary object-initializer checks cover it. A wire-format boundary has its own failure modes. The practical question is which component guarantees presence, which guarantees a nonnull value, and which guarantees meaningful content.

A working example

using System;
using System.Linq;
using System.Collections.Generic;
using System.Globalization;
using System.Text;
using System.Text.Json;
using System.Text.Json.Serialization;
using System.Threading;
using System.Threading.Tasks;
using System.Runtime.CompilerServices;

var user = new Profile { Name = "Ada" };
Console.WriteLine(user.Name);
Console.WriteLine(string.IsNullOrWhiteSpace(new Profile { Name = "" }.Name));
class Profile { public required string Name { get; init; } }

Expected output

Ada
True
Try this
Required means the member must be supplied, not that its contents are valid.