Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

What are immutable alternatives for an array in C#?

What are immutable alternatives for an array for production code in C# (at least while ImmutableArray is in beta)?

like image 270
thomius Avatar asked Mar 26 '15 09:03

thomius


2 Answers

The disadvantage to the just using ReadOnlyCollection or IReadOnlyList is that these provide much weaker guarantees than an immutable array. These objects only guarantee that the holder of the read only collection will not modify it. They do not guarantee that the collection won't be modified by code that holds a reference to the underlying collection.

Specifically, the consumer of such an object must still employ thread synchronization while reading from the collection, because there is no guarantee that the owner of the underlying collection is not busy adding/removing items from the collection when you are reading it through your read-only lense. Or you have to have a documented agreement between the consumer and producer that the collection won't be modified.

If you really want an immutable array and you cannot use the Immutable Collections package for some reason, then just make sure the source never modifies the array, but instead clones it whenever it wants to make a change. Combine this with handing out "read only" handles to implement immutable arrays.

LINQ makes it pretty easy to implement your own, though it is not terribly smart. An example of removing one item in an array with another in an immutable way:

private int[] _array;

public void RemoveItem(int item)
{
    _array = _array.Where(x => x != item).ToArray();
}

/// <summary>Gets immutable array</summary>
public IReadOnlyList<int> Data { get { return _array; } }
like image 41
Brandon Avatar answered Oct 17 '22 14:10

Brandon


You should give more details, but one option is System.Collections.ObjectModel.ReadOnlyCollection<> which acts as a list/array. It can be created like this:

  var immutable1 = Array.AsReadOnly(new[] { 7, 9, 13, });
  var immutable2 = (new List<int> { 7, 9, 13, }).AsReadOnly();

It is a wrapper around an IList<>. It cannot be cast to anything that allows altering the underlying list/array. But the underlying object can be reached with reflection, of course.

If someone somewhere else mutates the "inner" list/array, that will be refelcted in the ReadOnlyCollection<> wrapper. No copy is performed.

This is a "shallow" form of immutability. If the individual items can mutate (which an int cannot), the wrapper does not prohibit that.


A cheaper thing to do is to just use the interface IReadOnlyList<out T> which is covariant in T (an extra advantage). Both a List<T> and a T[] implement this interface. So:

  IReadOnlyList<int> immutable3 = new[] { 7, 9, 13, };
  IReadOnlyList<int> immutable4 = new List<int> { 7, 9, 13, };

In that case, however, the consumer may test immutable3 as IList<int>, for example, or immutable3 as int[], and get a "mutable-type reference" to the mutable object (without reflection).


However, there are immutable types (such as ImmutableList<>) that are not "in beta" in Microsoft.Immutable.Collections. So you could consider installing that if you really need it. It is not part of the standard .NET library, so you will have to install it separately.


To elaborate on what I mean by "shallow", suppose you have an array whose element type is in itself mutable. Then no matter which of the suggestions above you use, people can still modyfy the entries. For example:

StringBuilder[] yourArray = { 
    new StringBuilder("All"),
    new StringBuilder("is"),
    new StringBuilder("good"),
    };

var immutable = ImmutableList<StringBuilder>.Empty.AddRange(yourArray);

immutable[0].Insert(0, "NOT ");
immutable[1].Clear();
immutable[2].Append(new string('!', 10000));
like image 100
Jeppe Stig Nielsen Avatar answered Oct 17 '22 14:10

Jeppe Stig Nielsen