Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Is there anything magic about ReadOnlyCollection

Having this code...

var b = new ReadOnlyCollection<int>(new[] { 2, 4, 2, 2 });
b[2] = 3;

I get a compile error at the second line. I would expect a runtime error since ReadOnlyCollection<T> implements IList<T> and the this[T] have a setter in the IList<T> interface.

I've tried to replicate the functionality of ReadOnlyCollection, but removing the setter from this[T] is a compile error.

like image 319
Esben Skov Pedersen Avatar asked Sep 11 '09 09:09

Esben Skov Pedersen


2 Answers

The indexer is implemented with explicit interface implementation, so you'll only be able to access it if you do:

IList<int> b = new ReadOnlyCollection<int>(new[] { 2, 4, 2, 2 });
b[2] = 3;

or

var b = new ReadOnlyCollection<int>(new[] { 2, 4, 2, 2 });
((IList<int>)b)[2] = 3;

Of course, it'll then fail at execution time...

This is entirely deliberate and helpful - it means that when the compiler knows it's a ReadOnlyCollection, the unsupported bits of functionality aren't available to you, helping to divert you away from execution time failure.

It's an interesting and relatively unusual step though, effectively implementing one half of a property/indexer implicitly, and one half explicitly.

Contrary to my previous thoughts, I believe ReadOnlyCollection<T> actually implements the whole indexer explicitly, but also provides a public readonly indexer. In other words, it's something like this:

T IList<T>.this[int index]
{
    // Delegate interface implementation to "normal" implementation
    get { return this[index]; }
    set { throw new NotSupportedException("Collection is read-only."); }
}

public T this[int index]
{
    get { return ...; }
}
like image 78
Jon Skeet Avatar answered Oct 02 '22 14:10

Jon Skeet


It implements IList.Items explicitly, which makes it non-public, and you'll have to cast to the interface to reach its implementation, and implements a new this[...] indexer, which is used instead, which only has a get-accessor.

If you cast the collection to IList, your code will compile, but will fail at runtime instead.

Unfortunately I don't know how to do this in C#, since writing an indexer in C# involves using the this keyword, and you can't write this:

T IList<T>.this[int index] { get; set; }
like image 43
Lasse V. Karlsen Avatar answered Oct 02 '22 14:10

Lasse V. Karlsen