Real readonly lists in C#

In C# there is the readonly keyword that enforced the rule that the variable must be initialised as it's declared or in the constructor. This works as expected for simple types, but for objects and lists it's not quite like that. With a list, you can still add, remove and change items in the list. You may also expose a list as a property with a public get and a private set. You want the owner class to be able to modify the list items, but you don't want anything outside the class to modify the list items.

Here is an example with an almost readonly list, but as you can see it's not as readonly as you might think:

public class MyClass
{
	public readonly List<int> AlmostReadonlyInts;

	public MyClass()
	{
		AlmostReadonlyInts = new List<int>{1, 2, 3, 4};
	}
}

public class MainClass
{
	public void Method1()
	{
		var myObject = new MyClass();

		// Not allowed - The list is read only
		myObject.AlmostReadonlyInts = new List<int>();

		// Allowed - The collection itself is not read only
		myObject.AlmostReadonlyInts.Add(666);
	}
}

You can make a list completely readonly to the outside world by only exposing it as an IEnumerable<T>. Like this:

public class MyClass
{
	public IEnumerable<int> ReadonlyInts
	{
		get { return _readonlyInts; }
	}
	private readonly IList<int> _readonlyInts;

	public MyClass()
	{
		_readonlyInts = new List<int>{1, 2, 3, 4};
	}

	// You may still want to be able to
	//  access list elements without enumerating
	public int this[int index]
	{
		get { return _readonlyInts[index]; }
	}
}

public class MainClass
{
	public void Method1()
	{
		var myObject = new MyClass();

		// Not allowed - No public setter
		myObject.ReadonlyInts = new List<int>();

		// Not allowed - IEnumerable does
		//  not allow you to change the collection
		myObject.ReadonlyInts.Add(666);
	}
}

If you want a completely readonly list that not even the owner class can change then you can use a ReadOnlyCollection<T> like this:

public class MyClass
{
	public readonly ReadOnlyCollection<int> VeryReadonlyInts;

	public MyClass()
	{
		VeryReadonlyInts = new ReadOnlyCollection<int>(
			new [] {1,2,3,4}
		);
	}
}

public class MainClass
{
	public void Method1()
	{
		var myObject = new MyClass();

		// Not allowed - Is read only
		myObject.VeryReadonlyInts = new List<int>();

		// Not allowed - Is very readonly
		myObject.VeryReadonlyInts.Add(666);
	}
}

So, when you want the items in a collection to be readonly, you might find one of these techniques useful.

.NET 4.5 update

If you're using .NET 4.5, List<T> now implements IReadOnlyList<T> and IReadOnlyCollection<T> which gives us a neater solution:

public class MyClass
{
	public readonly IReadOnlyList<int> VeryReadonlyInts;

	public MyClass()
	{
		VeryReadonlyInts = new List<int> {1,2,3,4};
	}
}

public class MainClass
{
	public void Method1()
	{
		var myObject = new MyClass();

		// Not allowed - Is read only
		myObject.VeryReadonlyInts = new List<int>();

		// Not allowed - Is very readonly
		myObject.VeryReadonlyInts.Add(666);
	}
}

Thanks go to Dan Rigby for letting me know about these 4.5 updates. Also Dictionary<TKey, TValue> now implements IReadOnlyDictionary<TKey, TValue>.

No Comments