Постоянные коллекции и стандартные интерфейсы коллекций - PullRequest
2 голосов
/ 16 января 2012

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

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); }

Но у меня есть несколько опасений по поводу этого варианта:

  1. Будет ли это путать пользователей? Я предполагаю сценарии, когда кто-то работает с этим классом и обнаруживает, что вызов Add () для него иногда вызывает исключение, а иногда запускается, но не изменяет список, как это обычно ожидается для ICollection, в зависимости от связанной информации о типе с используемой ссылкой?

  2. Следуя (1), реализация ICollection<T> IsReadOnly должна предположительно возвращать true. Но это может противоречить тому, что подразумевается в других местах, где Add () используется с экземплярами класса.

  3. Разрешается ли (2) неразборчивым путем повторения явного шаблона реализации снова, когда новая версия возвращает false, а явная реализация возвращает true? Или это только усугубляет ложное впечатление, что метод MyList<T> Add() является мутатором?

  4. Или было бы лучше забыть о попытке использовать существующий интерфейс и просто создать отдельный IPersistentCollection<T> интерфейс, который получен вместо IEnumerable<T>?

edit Я изменил название класса и переключился на использование ICollection. Я хотел сосредоточиться на поведении объекта и его связи с интерфейсом. Я просто пошел со списком минусов в качестве простого примера. Я признателен за совет, что если бы мне пришлось реализовать список минусов, я бы попытался придумать менее запутанное имя и не должен реализовывать IList, потому что этот интерфейс предназначен для быстрого произвольного доступа, но это несколько касательные проблемы.

Я хотел спросить о том, что другие думают о напряженности между семантикой коллекций, доступных только для чтения (или неизменяемых), которые запекаются в Framework, и постоянных коллекций, которые реализуют поведение, эквивалентное тому, что описано в интерфейсе, только функционально, а не через мутирующие побочные эффекты.

Ответы [ 2 ]

5 голосов
/ 16 января 2012

Будет ли реализация IList<T> сбивать с толку?

Да. Хотя бывают ситуации, в которых реализация IList<T> выдает - скажем, когда вы пытаетесь изменить размер списка, но его реализация представляет собой массив - я нахожу довольно странным иметь IList<T>, который может быть мутирован в Ни в коем случае и не имели быстрого произвольного доступа.

Должен ли я реализовать новый IPersistentList<T>?

Это зависит от того, будет ли кто-нибудь его использовать. Могут ли потребители вашего класса иметь полдюжины различных реализаций IPL<T> на выбор? Я не вижу смысла в создании интерфейса, который реализуется только одним классом; просто используйте класс.

ItemsControl WPF может получить лучшую производительность, если его ItemsSource равен IList<T> вместо IEnumerable<T>.

Но ваш постоянный связанный список в любом случае не будет иметь быстрого произвольного доступа.

3 голосов
/ 16 января 2012

Для меня было бы более разумно создать новый интерфейс IPersistentList<T> (или IImmutableList<T>, поскольку "постоянный" звучит для меня как данные, сохраненные где-то.), Поскольку на самом деле это поведение отличается от ожидаемогоIList<T>.Классы, которые реализуют IList<T>, должны быть изменяемыми ИМХО.

О, и, конечно, я бы не стал использовать имя класса List<T>, поскольку оно уже является частью фреймворка.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...