Есть ли что-нибудь волшебное в ReadOnlyCollection? - PullRequest
11 голосов
/ 11 сентября 2009

Имея этот код ...

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

Я получаю ошибку компиляции во второй строке. Я ожидал бы ошибку времени выполнения, поскольку ReadOnlyCollection<T> реализует IList<T>, а this[T] имеет установщик в интерфейсе IList<T>.

Я пытался воспроизвести функциональность ReadOnlyCollection, но удаление сеттера из this[T] является ошибкой компиляции.

Ответы [ 3 ]

16 голосов
/ 11 сентября 2009

Индексатор реализован с явной реализацией интерфейса, поэтому вы сможете получить к нему доступ только в том случае, если выполните:

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

или

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

Конечно, тогда произойдет сбой во время выполнения ...

Это полностью преднамеренно и полезно - это означает, что когда компилятор знает , что это ReadOnlyCollection, неподдерживаемые биты функциональности вам недоступны, что помогает отвлечь вас от сбоев во время выполнения .

Хотя это интересный и относительно необычный шаг, эффективно неявно реализующий половину свойства / индексатора, а половину явно.

Вопреки моим предыдущим мыслям, я считаю, ReadOnlyCollection<T> на самом деле реализует весь индексатор явно, но также предоставляет общедоступный индексатор только для чтения. Другими словами, это примерно так:

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 ...; }
}
2 голосов
/ 11 сентября 2009

Он явно реализует IList.Items, что делает его закрытым, и вам придется привести его к интерфейсу, чтобы достичь его реализации, и внедрить новый индексатор [...] this, который используется вместо этого, который есть только get-accessor.

Если вы приведете коллекцию к IList, ваш код скомпилируется, но вместо этого потерпит неудачу во время выполнения.

К сожалению, я не знаю, как это сделать в C #, так как написание индексатора в C # предполагает использование ключевого слова this, а вы не можете написать это:

T IList<T>.this[int index] { get; set; }
1 голос
/ 11 сентября 2009

Волшебства нет, ReadOnlyCollection просто имеет разные реализации для своего собственного индексатора и индексатора, который реализует интерфейс IList<T>:

public T Item[int index] { get; }

T IList<T>.Item[int index] { get; set; }

Если вы приведете свой список к IList<int>, вы получите ошибку времени выполнения вместо ошибки компиляции:

((IList<int>)b)[2] = 3;

Edit:
Чтобы реализовать индексатор в своем собственном классе, используйте ключевое слово this:

public T this[int index] { get { ... } }

T IList<T>.this[int index] { get { ... } set { ... } }
...