Overview
Every new version of C# brings new features that enhance the language's expressiveness and productivity. C# 10 introduced init-only properties, which revolutionized the creation of immutable objects. In this detailed exploration, we'll delve into the concept of init-only properties and how they revolutionize the world of coding.
Immutable Objects and the Need for Initialization
Immutable objects, once created, remain unmodifiable. Creating immutable objects with traditional C# required writing verbose constructors or using object initializers, but they offered many advantages, including thread safety, enhanced security, and simplified code reasoning. Streamlining the initialization process of immutable objects with init-only properties in C# 10 solves this problem elegantly.
Defining Init-Only Properties
Take a classic Person class with FirstName, LastName, DateOfBirth, Email, Phone, Address and Orders properties as an example below:
//The File name is Person.cs inside Models folder
namespace CSharp10_ImmutableObjects.Models;
public class Person
{
public string FirstName { get; }
public string LastName { get; }
public DateTime DateOfBirth { get; }
public string Email { get; }
public string Phone { get; }
public Address Address { get; }
public List<Order> Orders { get; }
public Person(string firstName, string lastName, DateTime dateOfBirth, string email, string phone, Address address, List<Order> orders)
{
FirstName = firstName;
LastName = lastName;
DateOfBirth = dateOfBirth;
Email = email;
Phone = phone;
Address = address;
Orders = orders;
}
}
//The File name is public class Address.cs inside Models folder
namespace CSharp10_ImmutableObjects.Models;
public class Address
{
public Guid AddressID { get; }
public string Street { get; }
public string City { get; }
public string State { get; }
public string ZipCode { get; }
public Address(Guid addressID, string street, string city, string state, string zipCode)
{
AddressID = addressID;
Street = street;
City = city;
State = state;
ZipCode = zipCode;
}
}
//The File name is public class Order.cs inside Models folder
namespace CSharp10_ImmutableObjects.Models;
public class Order
{
public Guid OrderId { get; }
public DateTime OrderDate { get; }
public string ProductName { get; }
public int Quantity { get; }
public decimal Price { get; }
public Order(Guid orderId, DateTime orderDate, string productName, int quantity, decimal price)
{
OrderId = orderId;
OrderDate = orderDate;
ProductName = productName;
Quantity = quantity;
Price = price;
}
}
The conventional way to initialize FirstName, LastName, DateOfBirth, Email, Phone, Address and Orders properties is to define a constructor. C# 10's init-only properties simplify this process significantly:
//The File name is Person.cs inside Models folder
namespace CSharp10_ImmutableObjects.Models;
public class Person
{
public string FirstName { get; init; }
public string LastName { get; init;}
public DateTime DateOfBirth { get; init; }
public string Email { get; init; }
public string Phone { get; init; }
public Address Address { get; init; }
public List<Order> Orders { get; init; }
public Person(string firstName, string lastName, DateTime dateOfBirth, string email, string phone, Address address, List<Order> orders)
{
FirstName = firstName;
LastName = lastName;
DateOfBirth = dateOfBirth;
Email = email;
Phone = phone;
Address = address;
Orders = orders;
}
}
Init specifies that these properties are read-only once the object is created, ensuring their read-only status.
Streamlined Initialization
With init-only properties, creating and initializing instances of immutable objects becomes concise and expressive.
using CSharp10_ImmutableObjects.Models;
Console.WriteLine("Hello, from Ziggy Rafiq!");
var address = new Address(Guid.NewGuid(), "123 Main St", "Anytown", "State", "12345");
var orders = new List<Order>
{
new Order(Guid.NewGuid(), DateTime.Now, "Product1", 2, 20.00m),
new Order(Guid.NewGuid(), DateTime.Now, "Product2", 1, 15.00m)
};
var person = new Person(
firstName:"Lisa",
lastName: "Smith",
dateOfBirth:new DateTime(1980, 10, 10),
email: "[email protected]",
phone: "123-456-7890",
address: address,
orders: orders
);
Console.WriteLine(person.ToString());
It offers a clean and readable way to initialize properties during object creation. Modifying these properties once instantiated will result in a compilation error, enforcing immutability.
Benefits of Init-Only Properties
- Immutability by Design: Init-only properties facilitate the creation of immutable objects, enhancing the predictability and maintainability of code.
- Readability and Conciseness: Using init-only properties to initialize an object leads to more readable and expressive code.
- Compiler-Enforced Immutability: Compilers prevent accidental modification of init-only properties by ensuring they can only be set during object initialization.
Using Init-Only Properties in a Real-World Scenario
As an extension of our Person example, let's consider an Address class with only init properties:
//The File name is public class Address.cs inside Models folder
namespace CSharp10_ImmutableObjects.Models;
public class Address
{
public Guid AddressID { get; init; }
public string Street { get; init; }
public string City { get; init; }
public string State { get; init; }
public string ZipCode { get; init; }
public Address(Guid addressID, string street, string city, string state, string zipCode)
{
AddressID = addressID;
Street = street;
City = city;
State = state;
ZipCode = zipCode;
}
}
The initialization of an Address object is straightforward:
var address = new Address(Guid.NewGuid(), "123 Main St", "Anytown", "State", "12345");
Additional Considerations
Compatibility and Adoption
Though init-only properties were introduced in C# 10, they have been enhanced and optimized in C# 10. As a result, developers should make sure they are using a compatible compiler and runtime environment to take full advantage of init-only properties.
//The File name is Person.cs inside Models folder
namespace CSharp10_ImmutableObjects.Models;
public class Person
{
public string FirstName { get; init; }
public string LastName { get; init; }
public DateTime DateOfBirth { get; init; }
public string Email { get; init; }
public string Phone { get; init; }
public Address Address { get; init; }
public List<Order> Orders { get; init; }
public Person(string firstName, string lastName, DateTime dateOfBirth, string email, string phone, Address address, List<Order> orders)
{
FirstName = firstName;
LastName = lastName;
DateOfBirth = dateOfBirth;
Email = email;
Phone = phone;
Address = address;
Orders = orders;
}
public override string ToString()
{
return $"{FirstName} {LastName}";
}
}

Join the conversation! Your thoughts help the community grow.