C# tutorials > Modern C# Features > C# 6.0 and Later > What are init-only setters in C# 9.0 and when would you use them?

What are init-only setters in C# 9.0 and when would you use them?

C# 9.0 introduced init-only setters, a feature that allows you to initialize a property during object creation but prevents it from being modified afterward. They provide a way to create immutable or semi-immutable objects, ensuring data integrity after initialization.

Understanding Init-Only Setters

In this example, the Person class has three properties: FirstName, LastName, and DateOfBirth. All three properties are defined with the init keyword. This means that these properties can be set during object initialization. However, once the object is created, these properties cannot be modified.

The Example class demonstrates how to initialize a Person object and then attempts to modify one of its init-only properties. The commented-out line will cause a compilation error because you cannot change FirstName after the object has been initialized. This helps create immutable aspects of your object, increasing its reliability.

public class Person
{
    public string FirstName { get; init; }
    public string LastName { get; init; }
    public DateTime DateOfBirth { get; init; }

    public override string ToString()
    {
        return $"Name: {FirstName} {LastName}, Born: {DateOfBirth:d}";
    }
}

public class Example
{
    public static void Main(string[] args)
    {
        var person = new Person
        {
            FirstName = "John",
            LastName = "Doe",
            DateOfBirth = new DateTime(1990, 1, 1)
        };

        Console.WriteLine(person);

        //The following line would cause a compilation error
        //person.FirstName = "Jane";
    }
}

Concepts Behind the Snippet

The core concept is controlled mutability. init-only setters offer a middle ground between fully mutable properties (with standard get; set;) and fully immutable properties (with only get;). They allow initialization during object creation, providing a way to set values that should remain constant throughout the object's lifetime. This promotes the creation of more robust and predictable code.

Real-Life Use Case Section

Consider a configuration class where certain settings need to be defined at startup but should not be changed during runtime. Another practical case is with data transfer objects (DTOs) that represent data from an external source. These DTOs often benefit from having their properties initialized once and then treated as read-only. Also, when loading data from a database where primary keys should not be changed after being loaded into memory.

Best Practices

Use init-only setters for properties that represent the initial state of an object or values that should not change after creation. Avoid using them for properties that are expected to be modified throughout the object's lifecycle. Consider using constructors to initialize properties when initialization logic is complex or requires validation.

Interview Tip

When discussing init-only setters, highlight their role in creating immutable or semi-immutable objects and improving code maintainability. Explain their benefits for data integrity and how they promote defensive programming practices. Be prepared to discuss scenarios where they are most appropriate and how they differ from regular setters and read-only properties.

When to Use Them

Use init-only setters when:

  • You want to enforce that a property is only set during object initialization.
  • You want to create objects with immutable characteristics.
  • You need to ensure data integrity after object creation.
  • Your class represents a record or a value object.

Memory Footprint

init-only setters do not inherently impact the memory footprint compared to regular setters. The memory consumed by the properties themselves remains the same. The primary difference lies in the compile-time enforcement of immutability after initialization, which doesn't directly affect memory allocation.

Alternatives

Alternatives to init-only setters include:

  • Read-Only Properties (get;): Suitable for properties that are calculated or derived and should never be directly set.
  • Private Setters (get; private set;): Allow the property to be modified only within the class itself.
  • Constructors: Use constructors to initialize properties and ensure that certain values are set when the object is created.
  • With keyword (Records): Use the 'with' keyword with records to create immutable copies of an object with updated properties.

Pros

  • Data Integrity: Prevents modification of properties after initialization, ensuring data integrity.
  • Immutability: Facilitates the creation of immutable or semi-immutable objects.
  • Code Clarity: Clearly indicates which properties are intended to be set only during object creation.
  • Thread Safety: Immutable objects are inherently thread-safe.

Cons

  • Limited Flexibility: Properties cannot be changed after initialization, which might not be suitable for all scenarios.
  • Compatibility: Requires C# 9.0 or later.
  • Potential for Verbosity: Can increase the amount of code needed if you need to create variations of an object (use records and 'with' keyword for a more concise approach).

FAQ

  • Can I use init-only setters with structs?

    Yes, you can use init-only setters with structs in C# 9.0 and later. This allows you to create immutable structs, which can be beneficial for performance and thread safety.
  • How do init-only setters differ from private setters?

    init-only setters allow initialization only during object creation using an object initializer. Private setters allow modification of the property only within the class itself, but not from outside the class. init restricts assignments to only object initialization time.
  • Are init-only properties truly immutable?

    init-only properties are effectively immutable from the perspective of external users of the class after initialization. However, you need to consider the immutability of the *type* of the property. If the property is of a mutable reference type (like a List), the *contents* of that referenced object can still be changed, even if the property itself cannot be reassigned to a different list. For true immutability, you would need to use immutable collections or defensive copying techniques.