init accessors were introduced in C# 9.0. They sit between classic mutable setters and fully constructor-bound immutable types.
Why it matters
init gives you a clean object initializer experience without opening the property up for change later. That is ideal for DTOs, options objects, and other data models that should be set once.
It pairs especially well with records, but it also works on regular classes and structs.
Cautions
An init property can still be assigned from code running during construction or object initialization, so it is not the same thing as a truly frozen object.
Keep constructors and initializers aligned. If you need required data, use required or a constructor instead of assuming callers will remember every field.
Creating an object that can only be set during initialization
Init-only setters keep object construction flexible while preventing mutation after the initializer finishes.
Valid since C# 9.0
public sealed class Customer
{
public string Name { get; init; } = string.Empty;
public int LoyaltyPoints { get; init; }
}