A const list in C#

.NET supports truly immutable collections, read-only views of mutable collections, and read-only interfaces implemented by mutable collections.

One such immutable collection is ImmutableArray<> which you can create as a.ToImmutableArray() in your example. Make sure to take a look at the other options MSDN lists because you may be better served by a different immutable collection. If you want to make copies of the original sequence with slight modifications, ImmutableList<> might be faster, for instance (the array is cheaper to create and access, though). Note that a.Add(...); is valid, but returns a new collection rather than changing a. If you have resharper, that will warn you if you ignore the return value of a pure method like Add (and there may be a roslyn extension to do something similar I’m unaware of). If you’re going this route – consider skipping List<> entirely and going straight to immutable collections.

Read-only views of mutable collections are a little less safe but supported on older versions of .NET. The wrapping type is called ReadOnlyCollection<>, which in your example you might construct as a.AsReadOnly(). This collection does not guarantee immutability; it only guarrantees you can’t change it. Some other bit of code that shares a reference to the underlying List<> can still change it. Also, ReadOnlyCollection also imposes some additional overhead; so you may not be winning much by avoiding immutable collections for performance reasons (TODO: benchmark this claim). You can use a read-only wrapper such as this even in a public API safely – there’s no (non-reflection) way of getting the underlying list. However, since it’s often no faster than immutable collections, and it’s also not entirely safe, I recommend to avoid ReadOnlyCollection<> – I never use this anymore, personally.

Read-only interfaces implemented by mutable collections are even further down the scale of safety, but fast. You can simply cast List<> as IReadOnlyList<>, which you might do in your example as IReadOnlyList<int> lst = a. This is my preferences for internal code – you still get static type safety, you’re simply not protected from malicious code or code that uses type-checks and casts unwisely (but those are avoidable via code-reviews in my experience). I’ve never been bitten by this choice, but it is less safe than the above two options. On the upside, it incurs no allocations and is faster. If you commonly do this, you may want to define an extension method to do the upcast for you (casts can be unsafe in C# because they not only do safe upcasts, but possibly failing downcasts, and user-defined conversions – so it’s a good idea to avoid explicit casts wherever you can).

Note that in all cases, only the sequence itself is read-only. Underlying objects aren’t affected (e.g. an int or string are immutable, but more complicated objects may or may not be).


  • For safety: Use a.ToImmutableArray() to create an immutable copy in an ImmutableArray<int>.
  • For performance: Use IReadOnlyList<int> to help prevent accidental mutation in internal code with minimal performance overhead. Be aware that somebody can cast it back to List<> (don’t do that), making this less “safe” for a public api.
  • Avoid a.AsReadOnly() which creates a ReadOnlyCollection<int> unless you’re working on a legacy code base that doesn’t support the newer alternatives, or if you really know what you’re doing and have special needs (e.g. really do want to mutate the list elsewhere and have a read-only view).

Leave a Comment