Есть ли причина предоставлять внутреннюю коллекцию как ReadOnlyCollection, а не как IEnumerable, если вызывающий код выполняет только итерацию по коллекции?
class Bar
{
private ICollection<Foo> foos;
// Which one is to be preferred?
public IEnumerable<Foo> Foos { ... }
public ReadOnlyCollection<Foo> Foos { ... }
}
// Calling code:
foreach (var f in bar.Foos)
DoSomething(f);
На мой взгляд, IEnumerable - это подмножество интерфейса ReadOnlyCollection, и он не позволяет пользователю изменять коллекцию. Итак, если интерфейса IEnumberable достаточно, то его следует использовать. Это правильный способ рассуждать об этом или я что-то упускаю?
Спасибо / Эрик
c#
.net
collections
ienumerable
readonly-collection
Эрик Оджебо
источник
источник
ReadOnlyCollection
если вы параноик, но теперь вы не привязаны к конкретной реализации.Ответы:
Более современное решение
Если вам не нужно, чтобы внутренняя коллекция была изменяемой, вы можете использовать
System.Collections.Immutable
пакет, изменить тип своего поля на неизменяемую коллекцию, а затем выставить ее напрямую -Foo
конечно, при условии , что она неизменна.Обновленный ответ для более прямого ответа на вопрос
Это зависит от того, насколько вы доверяете вызывающему коду. Если вы полностью контролируете все, что когда-либо будет вызывать этого члена, и гарантируете, что никакой код никогда не будет использовать:
Тогда конечно, ничего страшного не будет, если вы просто вернете коллекцию напрямую. Я обычно стараюсь быть немного более параноиком, чем это.
Точно так же, как вы говорите: если вам только нужно
IEnumerable<T>
, то зачем привязывать себя к чему-то более сильному?Оригинальный ответ
Если вы используете .NET 3.5, вы можете избежать копирования и простого приведения, используя простой вызов Skip:
(Существует множество других вариантов тривиального обертывания - приятная особенность
Skip
над Select / Where заключается в том, что нет делегата, который нужно выполнять бессмысленно для каждой итерации.)Если вы не используете .NET 3.5, вы можете написать очень простую оболочку, которая будет делать то же самое:
источник
ReadOnlyCollection
кICollection
иAdd
успешное выполнение. Насколько я знаю, это невозможно.evil.Add(...)
Должен выдавать ошибку. dotnetfiddle.net/GII2jWReadOnlyCollection
- я бросил ,IEnumerable<T>
который на самом деле не только для чтения ... это вместо того , чтобы разоблачать вReadOnlyCollection
ReadOnlyCollection
вместо aIEnumerable
, чтобы защититься от злых заклинаний.Если вам нужно только перебрать коллекцию:
тогда достаточно возврата IEnumerable .
Если вам нужен произвольный доступ к элементам:
затем оберните его в ReadOnlyCollection .
источник
System.Collections.Immutable
как рекомендует Джон Скит, совершенно справедливо, когда вы раскрываете то, что никогда не изменится после того, как вы получите ссылку на это. С другой стороны, если вы открываете доступное только для чтения представление изменяемой коллекции, тогда этот совет все еще актуален, хотя я, вероятно, представил бы его какIReadOnlyCollection
(которого не существовало, когда я писал это).bar.Foos[17]
сIReadOnlyCollection
. Единственное отличие - дополнительноеCount
имущество.public interface IReadOnlyCollection<out T> : IEnumerable<T>, IEnumerable
bar.Foos[17]
, выставь какReadOnlyCollection<Foo>
. Если вы хотите узнать размер и просмотреть его, выставьте его какIReadOnlyCollection<Foo>
. Если вам не нужно знать размер, используйтеIEnumerable<Foo>
. Есть другие решения и другие детали, но это выходит за рамки обсуждения этого конкретного ответа.Если вы сделаете это, то ничто не помешает вашим вызывающим абонентам вернуть IEnumerable обратно в ICollection и затем изменить его. ReadOnlyCollection устраняет эту возможность, хотя по-прежнему можно получить доступ к базовой доступной для записи коллекции через отражение. Если коллекция небольшая, то безопасный и простой способ обойти эту проблему - вместо этого вернуть копию.
источник
Я стараюсь максимально избегать использования ReadOnlyCollection, на самом деле это значительно медленнее, чем использование обычного списка. См. Этот пример:
источник
Иногда вы можете захотеть использовать интерфейс, возможно, потому что вы хотите имитировать коллекцию во время модульного тестирования. Пожалуйста, прочтите мою запись в блоге о добавлении собственного интерфейса в ReadonlyCollection с помощью адаптера.
источник