C# required, record and init-only
Explore the benefits of C# features like required, record, and init-only properties. Learn how these tools enhance code clarity.

Class
Before required you had to create a constructor to guarantee that all properties you wanted were actually set:
public class EmailPdfToUserCommand
{
public EmailPdfToUserCommand(string invoiceId, string userFirstName, string userLanguage, string emailAddress)
{
InvoiceId = invoiceId;
UserFirstName = userFirstName;
UserLanguage = userLanguage;
EmailAddress = emailAddress;
}
public string InvoiceId { get; private set; }
public string UserFirstName { get; private set; }
public string UserLanguage { get; private set; }
public string EmailAddress { get; private set; }
}
public class SendEmails
{
public void SendPdf(string invoiceId, Customer customer)
{
var command = new EmailPdfToUserCommand(
invoiceId,
customer.FirstName,
customer.Language,
customer.Email
);
// execute the command
}
}
The constructor syntax though is not very readable, since you don’t immediately know which properties you’re setting:
var command = new EmailPdfToUserCommand(
invoiceId,
customer.FirstName,
customer.Language,
customer.Email
);
You can specify the argument names of course, but that’s still not exactly “it”:
var command = new EmailPdfToUserCommand(
invoiceId: invoiceId,
userFirstName: customer.FirstName,
userLanguage: customer.Language,
emailAddress: customer.Email
);
Required
required to the rescue! If you mark the properties as required, you don’t need the constructor:
public class EmailPdfToUserCommandWithRequired
{
public required string InvoiceId { get; set; }
public required string UserFirstName { get; set; }
public required string UserLanguage { get; set; }
public required string EmailAddress { get; set; }
}
Now, when you’re instantiating this command, you can use the object initializer syntax:
var command = new EmailPdfToUserCommandWithRequired
{
InvoiceId = invoiceId,
UserFirstName = customer.FirstName,
UserLanguage = customer.Language,
EmailAddress = customer.Email
};
It’s also guaranteed that all the properties will be set.
This, for example, will not compile:
var command = new EmailPdfToUserCommandWithRequired
{
InvoiceId = invoiceId,
UserFirstName = customer.FirstName,
UserLanguage = customer.Language,
// compilation error
};
because you didn’t specify the value for EmailAddress:

Record
In case of DTOs or commands and any other plain (no logic) objects, it’s actually better to use a record instead of a class.
Since in our example the values are not going to change (once we’ve created a command we’re not going to reuse it with different data in it), we can also make the properties init-only:
public record EmailPdfToUserCommandWithRequired
{
public required string InvoiceId { get; init; }
public required string UserFirstName { get; init; }
public required string UserLanguage { get; init; }
public required string EmailAddress { get; init; }
}
Now, this won’t compile:
public void SendPdfWithRequired(string invoiceId, Customer customer)
{
var command = new EmailPdfToUserCommandWithRequired
{
InvoiceId = invoiceId,
UserFirstName = customer.FirstName,
UserLanguage = customer.Language,
EmailAddress = customer.Email
};
command.UserLanguage = "en"; // compilation error
}
because we’re trying to modify an init-only property.

Positional record
We can make the definition of the record more concise by using the positional record syntax:
public record EmailPdfToUserCommandPositional(
string InvoiceId,
string UserFirstName,
string UserLanguage,
string EmailAddress);
but then the initialization syntax becomes this again:
public void SendPdfWithRequired(string invoiceId, Customer customer)
{
var command = new EmailPdfToUserCommandPositional(
invoiceId,
customer.FirstName,
customer.Language,
customer.Email
);
}
so I’m not a big fan of that :)
Required, record and init-only are great
Since the introduction of required in C# 11, I've really appreciated its utility. The record and init-only features, which were introduced earlier in C# 9, are also excellent. However, I'm not particularly fond of the positional record syntax.






