Есть ли что-нибудь волшебство в ReadOnlyCollection
Наличие этого кода...
var b = new ReadOnlyCollection<int>(new[] { 2, 4, 2, 2 });
b[2] = 3;
Я получаю ошибку компиляции во второй строке. Я ожидал бы ошибки во время выполнения, поскольку ReadOnlyCollection<T>
реализует IList<T>
, а this[T]
- сеттер в интерфейсе IList<T>
.
Я попытался реплицировать функциональность ReadOnlyCollection, но удаление setter из this[T]
является ошибкой компиляции.
Ответы
Ответ 1
indexer реализуется с явной реализацией интерфейса, поэтому вы сможете получить доступ к нему, если вы это сделаете:
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>
на самом деле реализует весь указатель явно, но также предоставляет публичный индексатор readonly. Другими словами, это примерно так:
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
Он явно реализует IList.Items, что делает его непубличным, и вам нужно будет передать его в интерфейс, чтобы достичь его реализации, и внедряет новый этот [...] indexer, который используется вместо этого, который только имеет get-accessor.
Если вы передадите коллекцию IList, ваш код будет компилироваться, но вместо этого будет работать во время выполнения.
К сожалению, я не знаю, как это сделать на С#, поскольку запись индексатора в С# включает в себя использование ключевого слова this
, и вы не можете записать это:
T IList<T>.this[int index] { get; set; }
Ответ 3
Нет волшебства, 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 { ... } }