Immutability Through Interfaces in C#

In C#, immutability is a useful concept. It is easy to image a collection that, under some circumstances, should act as an immutable type – take for example a custom method that is designed to print collection’s values after some fancy formatting. Such a method should deal with the collection in a read-only manner. Also, I think it is a good programming practice to provide users with the ability for a class to be passed around and treated as an immutable entity, if the user of the class has a need to do so.

The perils of using reference types is that a complex external method may accidentally modify the object that is passed to it. There are several methods for safeguarding against this. For example, you may want to store a separate copy of the original object and ensure it is unchanged after some method call. Alternatively, you can define two version of the same object, one for use as a constant, and one for use as a writable object. There is also a way to achieve this through the use of interfaces. This idea is not mine, and I first read about in “Accelerated C# 2008”, 2nd edition.

Ok, so the concept is simple. Define three interfaces, where one of them declares the methods for read-only actions; another interfaces declares methods for writable-actions only; and finally, the last interface combines the two. The class for which the controlled immutability is desired, would implement the combined interface. The users of the class would have a choice of, firstly, how to use it themselves (as they can restrict their own access), and secondly, how to pass it to some methods. Note that the methods would need to explicitly accept the object as the ‘agreed by design’ interface type.

In my simple example below I would like to have a control over who has the ability to add items to a generic collection. I do this via making GenericCollection to implement IMaintainable interface, which derives from IImutable and IModifiable.

using System;
using System.Collections.Generic;

namespace ImmutabilityThroughInterfaces
{
    public interface IImmutable<T>
    {
        T GetItem(int p);
        int CollectionSize();
    }

    public interface IModifiable<T>
    {
        void AddItem(T t, int p);
    }

    public interface IMaintainable<T> : IModifiable<T>, IImmutable<T> { }

    public sealed class GenericCollection<T> : IMaintainable<T>
    {
        public GenericCollection(int size)
        {
            Collection = new T[size];
        }

        public void AddItem(T t, int p)
        {
            if (p>=0 && p < Collection.Length)
              Collection[p] = t;
        }

        public T GetItem(int p)
        {
            if (p >= 0 && p < Collection.Length)
                return Collection[p];
            else
                return default(T);
        }

        public IEnumerator<T> GetEnumerator()
        {
            foreach (T item in Collection)
                yield return item;
        }

        public int CollectionSize()
        {
            return Collection.Length;
        }

        private T[] Collection;
    }
}

The Main method shows a rather simplified use example. Notice that NoAttemptToModify method accepts IImmutable collection. Inside this method, it is not possible to call AddItem, so this method is read-only with respect to the collection.

namespace UseExample
{
    using ImmutabilityThroughInterfaces;
    class EntryPoint
    {
        static void Main(string[] args)
        {

            GenericCollection<int> intColl = new GenericCollection<int>(3);
            intColl.AddItem(1, 0);
            intColl.AddItem(2, 1);
            intColl.AddItem(3, 2);

            var immutableCollection = intColl as IImmutable<int>;
            var maintainableCollection = intColl as IMaintainable<int>;

            AttemptToModify(maintainableCollection, 2);
            NoAttemptToModify(immutableCollection, 1);

            Console.ReadLine();
        }

            public static void AttemptToModify(IMaintainable<int> collection, int pos)
           {
                collection.AddItem(5, pos);
           }

            public static void NoAttemptToModify(IImmutable<int> collection, int pos)
            {
                  collection.GetItem(pos);
            }
    }
}

In this post I gave an example of a simple, but rather elegant way to ensure controlled immutability for custom types.