Я реализую постоянную коллекцию - в качестве аргумента, скажем, это односвязный список стиля, общего в функциональных языках.
class MyList<T>
{
public T Head { get; }
public MyList<T> Tail { get; }
// other various stuff
// . . .
}
Кажется естественным, что этот класс реализует ICollection<T>
, поскольку он может реализовывать все нормальное поведение, которое можно ожидать от ICollection<T>
, по крайней мере в широких штрихах. Но существует большое несоответствие между поведением этого класса и ICollection<T>
. Например, подпись метода Add()
void Add(T item); // ICollection<T> version
предполагает, что добавление будет выполнено как побочный эффект, который изменяет коллекцию. Но это постоянная структура данных, поэтому вместо этого Add () должен создать новый список и вернуть его.
MyList<T> Add(T item); // what we really want
Кажется, лучший способ решить эту проблему - просто создать нужную нам версию, а также сгенерировать нефункциональную явную реализацию версии, определенной в интерфейсе.
void ICollection<T>.Add(T item) { throw new NotSupportedException(); }
public MyList<T> Add(T item) { return new MyList<T>(item, this); }
Но у меня есть несколько опасений по поводу этого варианта:
Будет ли это путать пользователей? Я предполагаю сценарии, когда кто-то работает с этим классом и обнаруживает, что вызов Add () для него иногда вызывает исключение, а иногда запускается, но не изменяет список, как это обычно ожидается для ICollection, в зависимости от связанной информации о типе с используемой ссылкой?
Следуя (1), реализация ICollection<T>
IsReadOnly
должна предположительно возвращать true. Но это может противоречить тому, что подразумевается в других местах, где Add () используется с экземплярами класса.
Разрешается ли (2) неразборчивым путем повторения явного шаблона реализации снова, когда новая версия возвращает false
, а явная реализация возвращает true
? Или это только усугубляет ложное впечатление, что метод MyList<T>
Add()
является мутатором?
Или было бы лучше забыть о попытке использовать существующий интерфейс и просто создать отдельный IPersistentCollection<T>
интерфейс, который получен вместо IEnumerable<T>
?
edit Я изменил название класса и переключился на использование ICollection. Я хотел сосредоточиться на поведении объекта и его связи с интерфейсом. Я просто пошел со списком минусов в качестве простого примера. Я признателен за совет, что если бы мне пришлось реализовать список минусов, я бы попытался придумать менее запутанное имя и не должен реализовывать IList, потому что этот интерфейс предназначен для быстрого произвольного доступа, но это несколько касательные проблемы.
Я хотел спросить о том, что другие думают о напряженности между семантикой коллекций, доступных только для чтения (или неизменяемых), которые запекаются в Framework, и постоянных коллекций, которые реализуют поведение, эквивалентное тому, что описано в интерфейсе, только функционально, а не через мутирующие побочные эффекты.