Understanding Collections β
Collection Interfaces β
The framework contains primarily two kinds of collection concepts(implied by the type name):
- collection: a bucket of items without indexing or, order-agnostic. Inherits
IEnumerbale*as well. - list: a collection with indexing, meaning list-like interfaces are inherited from collection interfaces.
ICollection<T> β
As a order-agnostic collection interface, it requires
- Simple manipulation methods like
AddandRemove - Simple query like
Contains - Basic properties
CountandIsReadOnly
ICollection β
ICollection is the legacy version of ICollection<T>, considered soft-deprecated, it's only reserved for backward-compatibility. It does have some mistaken from the early days of dotnet. For example, ICollection.SyncRoot
IMPORTANT
ICollection<T> is a re-design of ICollection, it does not inherit ICollection like what IEnumerbale<T> does.
The Design Flaw β
ICollection<T>.IsReadOnly is actually kind of impure, because how do you know a collection should have a IsWhatever kind before creating the concrete type? What if I wanted to add more natures like IsWhatever properties? It would just break a lot of codebase around the world. This is an unfortunate design flaw that IReadOnlyCollection<T> is not the base interface of ICollection<T>, the interfacing should do additions from simple to complex, instead of adding hacky properties to remedy.
public interface ICollection<T> : IEnumerable<T> {
int Count { get; }
void Add(T item); // <-- This is the problem
void Clear();
bool Contains(T item);
bool Remove(T item);
bool IsReadOnly { get; } // <-- This tries to "fix" the problem
}
public interface IReadOnlyCollection<out T> : IEnumerable<T> {
int Count { get; }
}2
3
4
5
6
7
8
9
10
11
12
Yet another flaw is the combination of ICollection.SyncRoot and ICollection.IsSynchronized, which was served for thread-safe collection(again, it's impure). You can lock on ICollection.SyncRoot and pretend it as synchronized,
IList<T> β
As a collection with indexing, it has additional index-based querying method like IndexOf and indexer, on the basis of ICollection<T>.
NOTE
Just like ICollection<T>, IList<T> doesn't inherit IList.
IList β
Collections with Readonly-ness β
Readonly Collections β
Readonly collections are simple wrappers around a backing collection field, without any manipulation method. That means, you're not allow to manipulate the backing collection but can read from it, the changes happened on the backing collection are also reflected on the wrapper.
For example, one can expose an api to user with an ReadonlyCollection<T> as a real-time source to query without the capability to affect the internal workflow.
var ids = GetAllCurrentId();
ids.Add(1); // user can't modify it at all
ids.IndexOf(userId); // but it can query in real-time as the backing collection is revealed by the readonly wrapper
public ReadonlyCollection<int> GetAllCurrentId() {
return new ReadonlyCollection<int>(this._currentIds);
}2
3
4
5
6
7
ReadonlyCollection<T> covers any linear and list-like type which implements IList<T>, including Array and List<T>, so there's no need for dedicated types like ReadonlyList<T>.
NOTE
Although ReadonlyCollection<T> has "collection" in its name, it actually implements IList* interfaces.
NOTE
There are also ReadonlySet<T> and ReadonlyDictionary<K, V>.
Immutable Collections β
Frozen Collections β
Something Confusing β
List<T> Implements IReadOnlyList<T> β
It might be confusing that List<T> actually implements IReadOnlyList<T> while it isn't usually expected as a readonly collection. The reason is, a non-readonly collection should be able to be downgraded to a readonly one, so a method can imply that it doesn't modify anything when user pass a List<T> to a parameter targeted IReadOnlyList<T>.
// I don't alter anything, feel safe to pass a non-readonly list!
public void ReadList<T>(IReadOnlyList<T> list);2