126

I am working on creating an immutable class.
I have marked all the properties as read-only.

I have a list of items in the class.
Although if the property is read-only the list can be modified.

Exposing the IEnumerable of the list makes it immutable.
I wanted to know what is the basic rules one has to follow to make a class immutable ?

usefulBee
  • 9,250
  • 10
  • 51
  • 89
Biswanath
  • 9,075
  • 12
  • 44
  • 58
  • 3
    I strongly recommend that you read [Eric Lippert's blog series on immutability](http://blogs.msdn.com/ericlippert/archive/tags/Immutability/default.aspx), in particular the entry on ["kinds of immutability"](http://blogs.msdn.com/ericlippert/archive/2007/11/13/immutability-in-c-part-one-kinds-of-immutability.aspx). Your comment that "Exposing the IEnumerable of the list makes it immutable" seems somewhat strange to me. What do you mean by it? – Jon Skeet Dec 09 '08 at 11:49
  • What I meant was, rather than allowing to access the list( If the object have a list of some other objects ), allowing the user to access the members with IEnumerable. Talking list here as in a specific example, but it can be any data structure. – Biswanath May 18 '09 at 16:27
  • 3
    Jon Skeet's links to Eric Lippert's blog series have broken when MSDN archived these blogs. Please see ["kinds of immutability"](https://learn.microsoft.com/en-us/archive/blogs/ericlippert/immutability-in-c-part-one-kinds-of-immutability). The rest of the series seems to be under December 2007 + January 2008 in the left-hand panel. – John Doe Jan 24 '20 at 15:41
  • Please see Eric Lippert's blog series on how people commonly confuse the terms `atomicity`, `volatility`, and `immutability`: [Part One](https://ericlippert.com/2011/05/26/atomicity-volatility-and-immutability-are-different-part-one/), [Part Two](https://ericlippert.com/2011/05/31/atomicity-volatility-and-immutability-are-different-part-two/), and [Part Three](https://ericlippert.com/2011/06/16/atomicity-volatility-and-immutability-are-different-part-three/) . These are from his personal blog and, I believe, more newbie-friendly than his MSDN posts. – John Doe Jan 24 '20 at 15:46

8 Answers8

130

I think you're on the right track -

  • all information injected into the class should be supplied in the constructor
  • all properties should be getters only
  • if a collection (or Array) is passed into the constructor, it should be copied to keep the caller from modifying it later
  • if you're going to return your collection, either return a copy or a read-only version (for example, using ArrayList.ReadOnly or similar - you can combine this with the previous point and store a read-only copy to be returned when callers access it), return an enumerator, or use some other method/property that allows read-only access into the collection
  • keep in mind that you still may have the appearance of a mutable class if any of your members are mutable - if this is the case, you should copy away whatever state you will want to retain and avoid returning entire mutable objects, unless you copy them before giving them back to the caller - another option is to return only immutable "sections" of the mutable object - thanks to @Brian Rasmussen for encouraging me to expand this point
Blair Conrad
  • 233,004
  • 25
  • 132
  • 111
  • Should I write an wrapper look up property , which lets you only do the look up ? And thanks for the answer. – Biswanath Dec 09 '08 at 11:57
  • 6
    Any mutable reference type passed as an argument to the constructor should be copied. Otherwise the caller will still hold a reference to the state. – Brian Rasmussen Dec 09 '08 at 12:02
  • @Brian Rasmussen, you're right, but even if it's copied, may be possible for any caller to access the mutable object, depending on the acces.sors provided. In the case of being passed a mutable object, the class is best off always returning a different copy, or immutable sections of the object – Blair Conrad Dec 09 '08 at 12:04
  • @Blair - If I have dictionary in the class which I just want to use it for the look up. Is a read-only property which does a look up should be fine ? – Biswanath Dec 09 '08 at 12:16
  • @Biswanath - yes, with the caveat that if the values in the dictionary are mutable, you will need to protect them before returning, if you don't want them mutated – Blair Conrad Dec 09 '08 at 14:06
  • @Biswanah @Blair: For *"returning your collection"*, see http://stackoverflow.com/questions/352471 – BlueRaja - Danny Pflughoeft Feb 01 '11 at 22:01
  • Just an FYI - simply returning an IEnumerable<> doesn't necessarily make the class immutable, as the IEnumerable<> could still change (by other code, or even by the *user* of your immutable class. See http://stackoverflow.com/questions/15760870/code-for-adding-to-ienumerable/15858794#15858794 – Steve Jun 24 '13 at 20:35
  • You should mention not to return a `Span` object anywhere as you can write to private data from a `Span` object. – JAlex Nov 05 '20 at 22:02
21

To be immutable, all your properties and fields should be readonly. And the items in any list should themselves be immutable.

You can make a readonly list property as follows:

public class MyClass
{
    public MyClass(..., IList<MyType> items)
    {
        ...
        _myReadOnlyList = new List<MyType>(items).AsReadOnly();
    }

    public IList<MyType> MyReadOnlyList
    {
        get { return _myReadOnlyList; }
    }
    private IList<MyType> _myReadOnlyList

}
Joe
  • 122,218
  • 32
  • 205
  • 338
  • 1
    I think ImmutableList would be better. – Kevin Wong Nov 11 '16 at 14:13
  • use ImmutableList, also consider sealing the class – kofifus Dec 13 '18 at 00:11
  • I'm not convinced that ImmutableList is a good fit for this use case: `basic rules to make a class (that contains a list) immutable`. ImmutableList semantics enable more efficient memory usage when adding items to a copy of the list, but that seems to me to be a more specific use case. – Joe Dec 13 '18 at 07:11
12

Also, keep in mind that:

public readonly object[] MyObjects;

is not immutable even if it's marked with readonly keyword. You can still change individual array references/values by index accessor.

lubos hasko
  • 24,752
  • 10
  • 56
  • 61
7

All you need is L... Ehm record and C# 9.0 or newer.

public record Customer(string FirstName, string LastName, IEnumerable<string> Items);

//...

var person = new Customer("Test", "test", new List<string>() { "Test1", "Test2", "Test3" });
// you can't change anything within person variable
// person.FirstName = "NewName";

This gets translated into immutable class called Customer with three properties, FirstName, LastName and Items.

If you need an immutable (a read-only) collection as a property of the class, it is better to expose it as IEnumerable<T> or ReadOnlyCollection<T> than something from System.Collections.Immutable

KUTlime
  • 5,889
  • 1
  • 18
  • 29
5

Use the ReadOnlyCollection class. It's situated in the System.Collections.ObjectModel namespace.

On anything that returns your list (or in the constructor), set the list as a read-only collection.

using System.Collections.ObjectModel;

...

public MyClass(..., List<ListItemType> theList, ...)
{
    ...
    this.myListItemCollection= theList.AsReadOnly();
    ...
}

public ReadOnlyCollection<ListItemType> ListItems
{
     get { return this.myListItemCollection; }
}
mbillard
  • 38,386
  • 18
  • 74
  • 98
2

Another option would be to use a visitor pattern instead of exposing any internal collections at all.

Andrew
  • 11,894
  • 12
  • 69
  • 85
2

Using ReadOnlyCollection will restrict client from modifying it.

Imad
  • 7,126
  • 12
  • 55
  • 112
1

The original question and answer date from the end of 2008, there is now a System.Collections.Immutable namespace that I believe dates from the earliest .NET Core (1.0) . The namespace is still not available in .NET Standard (current version 2.1) and .NET framework (current version 4.8). This namespace has lots of immutable collections including the ImmutableList, which is asked about in the original question. However, I believe the System.Collections.Immutable namespace may appear in .NET 5 which is at release candidate 2 at the moment.

Also, starting with C# 6 you can have immutable auto-implemented properties using just { get; } .

Eric Wood
  • 331
  • 1
  • 3
  • 14