Learn the importance of immutability and how to take advantage of it in your C# applications Credit: Thinkstock Immutability is a feature of functional programming languages that makes programs easier to write, test, and maintain. However, immutability is not supported by many imperative programming languages. Until recently, C# did not support immutability out-of-the-box. That changes with the introduction of records in C# 9, which is available for preview in .NET 5. However, we can implement immutability in earlier versions of C# by using the System.Collections.Immutable namespace, which is available as a NuGet package. An immutable object is defined as an object that cannot be changed after it has been created. For many use cases, such as Data Transfer Objects, immutability is a desirable feature. This article discusses why we might want to take advantage of immutability and how we can implement immutability in C#. To work with the code examples provided in this article, you should have Visual Studio 2019 installed in your system. If you don’t already have a copy, you can download Visual Studio 2019 here. Create a .NET Core console application project in Visual Studio First off, let’s create a .NET Core console application project in Visual Studio. Assuming Visual Studio 2019 is installed in your system, follow the steps outlined below to create a new .NET Core console application project in Visual Studio. Launch the Visual Studio IDE. Click “Create new project.” In the “Create new project” window, select “Console App (.NET Core)” from the list of templates displayed. Click Next. In the “Configure your new project” window shown next, specify the name and location for the new project. Click Create. This will create a new .NET Core console application project in Visual Studio 2019. We’ll use this project to illustrate immutability in the subsequent sections of this article. Install the System.Collection.Immutable NuGet package To work with immutable types, you should install the System.Collections.Immutable package from NuGet. You can do this either via the NuGet package manager inside the Visual Studio 2019 IDE, or by executing the following command in the NuGet package manager console: Install-Package System.Collections.Immutable This package comprises a collection of thread-safe classes, also known as immutable collections. Understand immutability and records in C# 9 A Data Transfer Object is a classic example of when you want immutability. An instance of a DTO is often serialized so that it can be independent of the technology used at the consumer end. Naturally, when transferring a data object between a database and a client, you would like to ensure that the object cannot be changed — and that is exactly the purpose of a DTO. You can read more about the use of Data Transfer Objects in C# from my earlier article here. To create immutable DTOs, you can take advantage of a ReadOnlyCollection or the thread-safe immutable collection types in the System.Collections.Immutable namespace. Alternatively, you could take advantage of record types in C# 9 to implement immutable DTOs. A record type in C# 9 is a lightweight, immutable data type (or a lightweight class) that has read-only properties only. Since a record type is immutable, it is thread-safe and cannot mutate or change after it has been created. You can initialize a record type only inside a constructor. Creating a record type for a class (Author in this example) is as simple as the following code snippet. class data Author(int Id, string firstName, string lastName, string address); You also could write the Author record type as shown in the code snippet given below: public data class Author { public int Id { get; init; } public string firstName { get; init; } public string lastName { get; init; } public string address { get; init; } } Note the usage of the data keyword when declaring the record type. The data keyword when used in the declaration of a class marks the type as a record. You can take advantage of an instance of record type to pass data across the layers while at the same time ensuring immutability of the DTOs. The System.Collections.Immutable namespace Immutable collections are those whose members cannot change once they have been created. The System.Collections.Immutable namespace comprises several immutable collections. This namespace contains immutable versions of Lists, Dictionaries, Arrays, Hashes, Stacks, and Queues. The ImmutableStack can be used to push and pop elements much the same way we do with mutable stacks. However, since ImmutableStack is an immutable collection, its elements cannot be altered. So, when you make a call to the pop method to pop an element from the stack, a new stack is created for you and the original stack remains unaltered. Let’s illustrate this with an example. The following code snippet shows how you can push elements onto an immutable stack. var stack = ImmutableStack<int>.Empty; for(int i = 0; i < 10; i++) { stack = stack.Push(i); } The following program demonstrates that the elements of an immutable stack cannot be altered. class Program { static void Main(string[] args) { var stack = ImmutableStack<int>.Empty; for(int i = 0; i < 10; i++) { stack = stack.Push(i); } Console.WriteLine("No of elements in original stack: "+stack.Count()); var newStack = stack.Pop(); Console.WriteLine("No of elements in new stack: " + newStack.Count()); Console.ReadKey(); } } When you execute the above program, here’s how the output should appear in the console window. IDG Figure 1 As you can see in Figure 1, the original immutable stack (containing 10 elements) is unchanged after a call to the Pop() method. Rather, a new immutable stack is created with 9 elements. Immutable collections don’t offer constructors but you can take advantage of the static factory method called Create as shown in the code snippet given below. var list = ImmutableList.Create(1, 2, 3, 4, 5); If you wanted to add or remove an element from this collection, a new immutable list would be created and the original immutable list would remain unchanged. Immutability is a design choice; it means that an instance of a type cannot be changed after it has been created. Except for immutable stacks and immutable queues, all immutable collections are based on AVL trees. Hence you can insert elements at any position of the collection, i.e., the beginning, middle, or end, without needing to copy the tree in its entirety. How to do more in C#: How to use data annotations in C# How to work with GUIDs in C# 8 When to use an abstract class vs. interface in C# How to work with AutoMapper in C# How to use lambda expressions in C# How to work with Action, Func, and Predicate delegates in C# How to work with delegates in C# How to implement a simple logger in C# How to work with attributes in C# How to work with log4net in C# How to implement the repository design pattern in C# How to work with reflection in C# How to work with filesystemwatcher in C# How to perform lazy initialization in C# How to work with MSMQ in C# How to work with extension methods in C# How to us lambda expressions in C# When to use the volatile keyword in C# How to use the yield keyword in C# How to implement polymorphism in C# How to build your own task scheduler in C# How to work with RabbitMQ in C# How to work with a tuple in C# Exploring virtual and abstract methods in C# How to use the Dapper ORM in C# How to use the flyweight design pattern in C# Related content feature 14 great preprocessors for developers who love to code Sometimes it seems like the rules of programming are designed to make coding a chore. Here are 14 ways preprocessors can help make software development fun again. By Peter Wayner Nov 18, 2024 10 mins Development Tools Software Development feature Designing the APIs that accidentally power businesses Well-designed APIs, even those often-neglected internal APIs, make developers more productive and businesses more agile. By Jean Yang Nov 18, 2024 6 mins APIs Software Development news Spin 3.0 supports polyglot development using Wasm components Fermyon’s open source framework for building server-side WebAssembly apps allows developers to compose apps from components created with different languages. By Paul Krill Nov 18, 2024 2 mins Microservices Serverless Computing Development Libraries and Frameworks news Go language evolving for future hardware, AI workloads The Go team is working to adapt Go to large multicore systems, the latest hardware instructions, and the needs of developers of large-scale AI systems. By Paul Krill Nov 15, 2024 3 mins Google Go Generative AI Programming Languages Resources Videos