Create Immutable Type In C#


How to create an immutable type in C#.


Create a class with,

  1. Fields - that are private and read-only.
  2. Constructors

    1. Public - that accepts parameters to initialize the class
    2. Private - that accepts parameter for all the fields/properties of the class

  3. Properties - that are read-only.
  4. Methods - that return a new object (of the same type) instead of mutating the state (fields/properties).
  1. public sealed class Email  
  2.  {  
  3.      private readonly List<string> to;  
  4.      private readonly List<string> cc;  
  5.      private readonly List<string> bcc;  
  6.      private readonly List<string> attachments;  
  8.      public Email(string subject, string from, string body, string to)  
  9.      {  
  10.          // set fields values  
  11.      }  
  13.      private Email(string subject, string from, string body,  
  14.          List<string> to, List<string> cc, List<string> bcc,  
  15.          List<string> attachments)  
  16.      {  
  17.          // set fields values  
  18.      }  
  20.      public string Subject { get; }  
  21.      public string From { get; }  
  22.      public string Body { get; }  
  23.      public IReadOnlyList<string> To => to;  
  24.      public IReadOnlyList<string> CC => cc;  
  25.      public IReadOnlyList<string> BCC => bcc;  
  26.      public IReadOnlyList<string> Attachments => attachments;  
  28.      public Email AddTo(string to)  
  29.      {  
  30.          return new Email(  
  31.              subject: this.Subject,  
  32.              from: this.From,  
  33.              to: new List<string>().Concat(,  
  34.              body: this.Body,  
  35.              cc:,  
  36.              bcc: this.bcc,  
  37.              attachments: this.attachments);  
  38.      }  
  39.  } 

The test below shows that the old instance remains unchanged.

  1. [Fact]  
  2.   public void Changing_email_returns_new_email_and_leave_orignal_unchanged()  
  3.   {  
  4.       var email = new Email(  
  5.                       subject: "Hello Immutable Type",  
  6.                       from: "",  
  7.                       body: "Immutable Types are good for value objects",  
  8.                       to: "");  
  10.       var newEmail = email.AddTo("");  
  12.       Assert.Equal(1, email.To.Count);  
  13.       Assert.Equal(2, newEmail.To.Count);  
  14.   } 


The idea behind the immutable types is that once initialized, they don’t mutate their state but instead, create & return a new object.

By making the fields read-only, we ensure that once initialized in a constructor, these can’t be modified, even by the code in the class itself.

For the public constructor that accepts initialization, data is required so that the client can pass-in minimum state for the type to be valid. Private constructor, on the other hand, is used by methods to construct a new object and set all its states.

Properties must be made read-only by using get; only properties or expression-bodied functions. Note also the use of IReadOnlyList; this is to ensure that clients can’t add to lists. Methods will construct a new object based on current state.


Of course creating immutable types is extra work, so why and when should we use them?

I’ve found that Value Objects are good candidates for this. These are abstractions based on their values and therefore it doesn’t make sense for one object to “mutate” into another; e.g., a £5 note can’t become a £10 note, each note (object) is defined by its value and is distinct.

I’ve also found them useful in writing library code that other developers use. For example, if you’re building a library to connect to Azure NoSQL and have a class AzureNoSqlSettings that is being passed to this library, it is useful to make this settings class immutable so that it is clear to all developers (working on library) that settings object must not change once set by the client.

Immutable Types are also useful in concurrency scenarios since multiple threads are not mutating the same fields/state.

Source Code