2015-11-17 5 views
3

я оказался в this посте при поиске решения моей проблемы - что привело меня предложить новый ответ есть - и - столкнуться со следующим вопросом:Почему я не всегда должен использовать ICollection вместо IEnumerable?

Учитывая ICollection реализует IEnumerable, и все расширения LINQ применяются для обоих интерфейсов существует ли какой-либо сценарий, когда мне бы хотелось воспользоваться работой с IEnumerable вместо ICollection?

Некоммерческий IEnumerable, например, не предоставляет расширение Count. Оба ICollection интерфейсов делать.

Учитывая все ICollection, в любом случае, обеспечивают все функциональные возможности IEnumerable реализовать - так как он сам реализует его - почему тогда я должен выбрать IEnumerable вместо ICollection?

Обратная совместимость с предыдущими системами, где ICollection не был доступен?

+3

'IEnumerable' - родительский интерфейс' ICollection'. Поэтому, если ваш метод принимает только ICollection ', он не может быть повторно использован, как если бы он принял' IEnumerable '. Также обратите внимание, что многие методы расширения LINQ пытаются использовать для 'IList' или' ICollection' для использования свойства вместо перечисления последовательности. 'Enumerable.Count' оптимизирован, поскольку вы можете видеть [здесь] (http://referencesource.microsoft.com/#System.Core/System/Linq/Enumerable.cs,41ef9e39e54d0d0b.references). –

+0

@TimSchmelter: thanks Tim. Утверждение ** Итак, если вы говорите, что ваш метод принимает только ICollection , он не может быть повторно использован, как если бы вы принимали IEnumerable ** однако отвечает «почему бы не использовать A вместо B, если A есть B +». Если по каким-либо причинам IEnumerable более широко используется, все же не является причиной того, что нельзя использовать ICollection, поскольку ICollection является IEnumerable (если только это не так). – Veverke

+0

@Veverke, если нет конкретной причины не делать этого, вы всегда должны предпочитать более высокий класс в иерархии, который имеет все, что вам нужно. Это дает большую гибкость: если в «ICollection» нет ничего конкретного, использование «IEnumerable» дает большую гибкость. – Jcl

ответ

3

Я думаю, что на самом деле есть два вопроса, чтобы ответить здесь.

Когда бы я хотел IEnumerable<T>?

В отличие от других типов коллекций и языка в целом запросы на IEnumerable s выполняются с ленивой оценкой.Это означает, что вы можете выполнить несколько связанных запросов только в перечислении.

Стоит отметить, что ленивая оценка не очень хорошо взаимодействует с побочными эффектами, потому что многократное перечисление может дать разные результаты, вы должны помнить об этом при использовании. На языке, подобном C#, ленивая оценка может быть очень мощным инструментом, но также источником необъяснимого поведения, если вы не будете осторожны.

Когда я не хочу ICollection<T>?

ICollection<T> - это изменяемый интерфейс, он предоставляет, среди прочего, добавлять и удалять методы. Если вы не хотите, чтобы внешние вещи изменяли содержимое вашего объекта, вы не хотите его возвращать. Аналогично, вы, как правило, не хотите передавать его в качестве аргумента по той же причине.

Если вам нужна явная изменчивость коллекции, обязательно используйте ICollection<T>.

Кроме того, в отличие от IEnumerable<T> или IReadOnlyCollection<T>, ICollection<T> не является ковариантным, что снижает гибкость типа в определенных случаях использования.

Non-дженерики

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

Я лично хотел бы избежать необоснованных версий из-за отсутствия безопасности типов и низкой производительности при боксировании/распаковке в случае типов значений.

Резюме

Если вы хотите ленивые вычисления, используйте IEnumerable<T>. Для нетерпеливой оценки и неизменности используйте IReadOnlyCollection<T>. Для использования явной изменчивости ICollection<T>.

+0

Благодарим за добавление аспекта изменчивости ICollection - и его отсутствия в IEnumerable. – Veverke

4

IEnumerable предоставляет интерфейс для чтения только для чтения, а ICollection допускает модификацию. Также IEnumerable нужно просто знать, как перебирать элементы. ICollection должен предоставить дополнительную информацию.

Это семантически отличается. Вы не всегда хотите предоставить функциональность для изменения коллекции.

Существует IReadOnlyCollection, но не используется ICollection. Это дизайн C#, который ReadOnly - это другой урезанный интерфейс.

Вопрос, сделанный Тимом, весьма важен. Внутренняя работа для Count может быть существенно иной. IEnumerable не должен знать, сколько элементов он охватывает. В коллекции есть свойство, поэтому он должен знать, сколько элементов он содержит. Это еще одна важная разница.

+0

О, вот разница. Благодаря ! – Veverke

+0

Обратите внимание, что это верно для универсального 'ICollection ', но не для не-generic 'ICollection'. Для не-генерической версии единственное отличие от 'IEnumerable' - это синхронизация (с корневым объектом sync) и прямым подсчетом. – Jcl

+0

О да, я пацифист, поэтому я вообще не касался не-дженериков, так как мне не нравится бокс. Я должен, вероятно, расширить ответ. – luk32

2

Идея заключается в использовании простейшего контракта (интерфейса), который отвечает требованиям: ICollection - это коллекция, IEnumerable - это последовательность. Последовательность может иметь отсроченное выполнение, она может быть бесконечной и т. Д. Интерфейс IEnumerable просто сообщает вам, что вы можете перечислить последовательность, то есть все. Это отличается от ICollection, который представляет собой фактическую коллекцию, содержащую конечное количество элементов.

Как вы можете видеть, это совсем другое. Вы не можете игнорировать семантику этих контрактов и просто сосредоточиться на том, какой интерфейс наследует другой.

Если ваш алгоритм включает только перечисление входных данных, тогда он должен принимать IEnumerable. Если вы, по контракту, имеете дело с коллекциями (т. Е. Ожидаете коллекции и ничего больше), то вы должны использовать ICollection.

+0

Может ли последовательность быть бесконечной *? Я думаю, что бесконечность - это абстрактная концепция без реальной реализации, которая сохраняет свою бесконечную природу. Если мы проводим какую-либо последовательность под рукой в ​​любой момент времени, она не является конечной, потому что я могу ее перечислить. – Veverke

+1

@Veverke Возможно, существует бесконечное 'IEnumerable' да, все, что нам нужно из' IEnumerable', является перечислителем, описывающим, как перейти к следующему элементу. Это описание того, как 'MoveNext()' может расширяться до бесконечности. Разумеется, вы не можете оценить бесконечный «IEnumerable», но вы можете оценить его подмножество, и в некоторых случаях это может быть полезным. – TheInnerLight

+0

@Veverke: контракт требует выполнения только следующего запроса при запросе. Вопреки тому, что вы написали, вы не «держите последовательность под рукой». Вот бесконечная последовательность: 'IEnumerable InfiniteSequence() {while (true) return return 0; } '. Попробуйте перечислить эту последовательность. Если вам нужны практические приложения: предоставление показаний датчиков в последовательности. –