Reputation: 952
I would like to create a list in C# that after its creation I won't be able to add or remove items from it. For example, I will create the list;
List<int> lst = a;
(a is an existing list), but after I won't be able to write the code (it will mark it as an error):
lst.Add(2);
Upvotes: 49
Views: 101380
Reputation: 64
public readonly List<string> constList = new(){"value1", "value",...};
By adding readonly create a const equivalent list.
Upvotes: 2
Reputation: 2975
Why not just:
readonly IEnumerable<int> lst = new List<int>() { a }
Upvotes: 2
Reputation: 31833
I recommend using a System.Collections.Immutable.ImmutableList<T>
instance but referenced by a variable or property of type System.Collections.Generic.IReadOnlyList<T>
. If you just use a naked immutable list, you won't get errors for adding to it, as you desire.
System.Collections.Generic.IReadOnlyList<int> list = a.ToImmutableList();
Upvotes: 6
Reputation: 48066
.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).
TL;DR:
a.ToImmutableArray()
to create an immutable copy in an ImmutableArray<int>
.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.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).Upvotes: 61
Reputation: 81
Why not just use an IEnumerable?
IEnumerable<string> myList = new List<string> { "value1", "value2" };
Upvotes: 8
Reputation: 186748
It could be IReadOnlyList<int>
, e.g.
IReadOnlyList<int> lst = a;
So the initial list (a
) is mutable while lst
is not. Often we use IReadOnlyList<T>
for public properties and IList<T>
for private ones, e.g.
public class MyClass {
// class itself can modify m_MyList
private IList<int> m_MyList = new List{1, 2, 3};
...
// ... while code outside can't
public IReadOnlyList<int> MyList {
get {
return m_MyList;
}
}
}
Upvotes: 5
Reputation: 109607
Your best bet here is to use an IReadOnlyList<int>
.
The advantage of using IReadOnlyList<int>
compared to List.AsReadOnly()
is that a ReadOnlyCollection<T>
can be assigned to an IList<T>
, which can then be accessed via a writable indexer.
Example to clarify:
var original = new List<int> { 1, 2, 3 };
IReadOnlyList<int> readOnlyList = original;
Console.WriteLine(readOnlyList[0]); // Compiles.
readOnlyList[0] = 0; // Does not compile.
var readOnlyCollection = original.AsReadOnly();
readOnlyCollection[0] = 1; // Does not compile.
IList<int> collection = readOnlyCollection; // Compiles.
collection[0] = 1; // Compiles, but throws runtime exception.
Using an IReadOnlyList<int>
avoids the possibility of accidentally passing the read-only list to a method which accepts an IList<>
and which then tries to change an element - which would result in a runtime exception.
Upvotes: 5
Reputation: 1821
As an alternative to the already posted answers, you can wrap a readonly
regular List<T>
into an object that exposes it as IReadOnlyList
.
class ROList<T>
{
public ROList(IEnumerable<T> argEnumerable)
{
m_list = new List<T>(argEnumerable);
}
private readonly List<T> m_list;
public IReadOnlyList<T> List { get { return m_list; } }
}
void Main()
{
var list = new List<int> {1, 2, 3};
var rolist = new ROList<int>(list);
foreach(var i in rolist.List)
Console.WriteLine(i);
//rolist.List.Add(4); // Uncomment this and it won't compile: Add() is not allowed
}
Upvotes: 5
Reputation: 149548
You can use ImmutableList<T>
/ ImmutableArray<T>
from System.Collections.Immutable
NuGet:
var immutable = ImmutableList<int>.Create(1, 2, 3);
Or using the ToImmutableList
extension method:
var immutable = mutableList.ToImmutableList();
In-case Add
is invoked, *a new copy * is returned and doesn't modify the original list. This won't cause a compile time error though.
Upvotes: 12
Reputation: 1832
You need a ReadonlyCollection
. You can create one from a list by calling List.AsReadOnly()
Reference: https://msdn.microsoft.com/en-us/library/ms132474.aspx
Upvotes: 9