DEV Community

Cover image for C# Init Only and Required Properties
Anton Martyniuk
Anton Martyniuk

Posted on • Originally published at antondevtips.com on

C# Init Only and Required Properties

Init only properties

C# 9.0 introduced a new type of a property accessor called init.

The init only property can be assigned only during object creation and can’t be changed further. This enforces immutability.

Let’s see how to define init only properties:

public record Product
{
    public int Id { get; init; }

    public string Name { get; init; }

    public decimal Price { get; init; }
}

// Create an instance of Product's record
var product = new Product
{
    Id = 1,
    Name = "Phone",
    Price = 500.00m
};
Enter fullscreen mode Exit fullscreen mode

The best practise when creating a record is declaring all of its properties as init only. This ensures that properties can’t be changed, ensuring the immutability of the record’s data. Although you can define init properties in a class too.

init only properties have the same behaviour as set properties. You can use a backing field for the property if needed:

public record Product
{
    private readonly decimal _price;

    public required decimal Price
    {
        get => _price;
        init => _price = value;
    }

    // Other properties...
}
Enter fullscreen mode Exit fullscreen mode

Required properties

A new keyword called required was introduced in C# 11, which is a great addition to init only properties:

public record Product
{
    public required int Id { get; init; }

    public required string Name { get; init; }

    public required decimal Price { get; init; }
}
Enter fullscreen mode Exit fullscreen mode

This ensures that all properties marked with required keyword, should be assigned when creating an object, otherwise a compilation error is raised:

// This code doesn't compile as Price property is not assigned
var product = new Product
{
    Id = 1,
    Name = "Phone"
};
Enter fullscreen mode Exit fullscreen mode

init and required keywords make a great pair together. required ensures that init only properties are assigned during objection creation, as these properties can’t be changed further.

Positional Records

C# also provides a shortened form of record declaration:

public record Product(int Id, string Name, decimal Price);

var product = new Product(1, "PC", 1000.00m);
Enter fullscreen mode Exit fullscreen mode

A single line of code! We assign all the properties using a record’s constructor which is called a primary constructor. Under the hood this code is translated to a classic form of a record declaration with all properties being init only. This form of record is also called a positional record as all properties under the hood are created in the exact order as their position in the primary constructor. Positional form of declaration looks really elegant and concise when a record doesn’t have a lot of fields.

When creating a record’s object using a primary constructor — you are forced to assign all the properties. Though the required keyword is not used here.

Init Only and Required Properties Important Notes

1. Required keyword only works at compile time

When an object is created at the runtime, required keyword has no effect, it doesn’t throw exceptions even if the field is not assigned with a value. required works only at compile time. Although there is one exception: objects serialized and deserialized using System.Text.Json in NET 8 can throw exceptions when required properties are not assigned with a value.

2. Nullable properties can be marked as required

It is perfectly fine to mark nullable properties as required. It forces you to assign it a real value or null when creating an object. Let’s add a nullable Description property to the Product record:

public record Product
{
    public required int Id { get; init; }

    public required string Name { get; init; }

    public required string? Description { get; init; }

    public required decimal Price { get; init; }
}

var product = new Product
{
    Id = 1,
    Name = "PC",
    Price = 1000.00m,
    Description = "Some amazing PC" // Assign some value
};

var product2 = new Product
{
    Id = 1,
    Name = "PC",
    Price = 1000.00m,
    Description = null // Explicitly assign null here
};
Enter fullscreen mode Exit fullscreen mode

You should really consider using required keyword for all types of properties whenever appropriate.

3. Using constructor with required properties

Let’s see the following code where we create a class with required properties and a constructor:

public class User
{
    public User(string name, int age)
    {
        Name = name;
        Age = age;
    }

    public required string Name { get; init; }

    public required int Age { get; init; }
}

var user = new User("Anton", 30); // This doesn't compile
Enter fullscreen mode Exit fullscreen mode

This code doesn’t compile as compiler tells that required properties are not assigned, even if they are assigned from the constructor. To fix the code you need to add property assignments along with the constructor:

var user = new User("Anton", 30)
{
    Name = "Anton",
    Age = 30
}; // Now this compiles
Enter fullscreen mode Exit fullscreen mode

This one is a real caveat and you should consider this strange behaviour when modelling classes.

Hope you find this blog post useful. Happy coding!

Originally published at https://antondevtips.com.

After reading the post consider the following:

  • Subscribe to receive newsletters with the latest blog posts
  • Download the source code for this post from my github (available for my sponsors on BuyMeACoffee and Patreon)

If you like my content —  consider supporting me

Unlock exclusive access to the source code from the blog posts by joining my Patreon and Buy Me A Coffee communities!


Top comments (0)