C# Protecting private collections from modification – Part 2

C# Protecting private collections from modification – Part 2

The common object issue

In the last part I wrote about how you can protect a class’s private collections from modification by wrapping them in a ReadOnlyCollection before exposing them. But how about collections that are handed to your class? Consider the following code:

public class MyClass
{
  private readonly List<int> _myInts;

  public MyClass(List<int> ints)
  {
    _myInts = ints;
  }
  ...
}

What happens here is that MyClass takes a variable of reference type in it’s constructor, and creates a variable of reference type of its own, _myInts, that is set to reference the same List object as the constructor variable. In other words, the List of ints on the heap now has at least two references to it.

Now assume the code that instantiates MyClass looks something like this:

var ints = new List<int> { 1, 2, 3 };
var myClass = new MyClass(ints); // Use ints in the MyClass constructor
...
ints.Clear(); // Re-use ints for other purposes
ints.Add(7);
...

This code will clear and then add a 7 to the List that _myInts in MyClass references. Which may lead to unexpected behavior.

Protecting the data

Fortunately there is an easy way to protect the data from any modifications. By using a type from the Systems.Collections.Immutable namespace.

The Immutable namespace contains immutable classes and interfaces, as well as extension methods to create an immutable copy of a mutable collection. The MyClass code can be changed to take advantage of these types:

public class MyClass
{
  private readonly ImmutableList<int> _ints;

  public MyClass(List<int> ints)
  {
    _ints = ints.ToImmutableList();
  }
  ...
}

Note that it would add no protection from external modification by using AsReadOnly like in the previous post. The big difference here is that ToImmutableList creates a new ImmutableList object which is populated by enumerating the ints List and copying the values.

Conclusion

When dealing with references, it is easy to forget to properly protect data that should be private to a class. There are also no compile time warnings or errors for altering a referenced object that have many references to it. However, it is important to be able to trust that data that you don’t expect to change really stays the same. Use the support that is available in the framework in order to achieve this.