В ответ на этот вопрос runefs предположил, что "если у вас нет особой причины для использования IList, вам следует рассмотреть IEnumerable". Какой вы используете и почему?
Через какой интерфейс я должен выставлять List‹T›?
Ответы (6)
IEnumberable<T>
доступен только для чтения, вам необходимо восстановить коллекцию, чтобы внести в нее изменения. С другой стороны, IList<T>
доступен для чтения и записи. Поэтому, если вы ожидаете много изменений в коллекции, откройте IList<T>
, но если можно с уверенностью предположить, что вы не будете ее изменять, используйте IEnumerable<T>
.
Всегда используйте наиболее строгий интерфейс, предоставляющий необходимые вам функции, потому что это дает вам максимальную гибкость для последующего изменения реализации. Итак, если IEnumerable<T>
достаточно, используйте это... если вам нужны функции списка, используйте IList<T>
.
И желательно использовать строго типизированные общие версии.
Я следую принципу, который я прочитал некоторое время назад:
«Потребляй самое простое и выявляй самое сложное»
(Я уверен, что это действительно распространено, и я неправильно цитирую его, если кто-нибудь знает источник, можете ли вы оставить комментарий или отредактировать, пожалуйста...)
(Отредактировано, чтобы добавить - ну, вот я здесь пару недель спустя, и я только что наткнулся на эту цитату в совершенно другом контексте, похоже, она началась как Принцип надежности или закон Постеля -
Будьте консервативны в том, что вы делаете; будьте либеральны в том, что вы принимаете от других.
Первоначальное определение предназначено для сетевого взаимодействия через Интернет, но я уверен, что видел, как оно переназначается для определения контрактов классов в объектно-ориентированном подходе.)
По сути, если вы определяете метод для внешнего потребления, то параметры должны быть самого базового типа, который дает вам требуемую функциональность - в случае вашего примера это может означать использование IEnumerable вместо IList. Это дает клиентскому коду наибольшую гибкость в отношении того, что они могут передать. С другой стороны, если вы предоставляете свойство для внешнего потребления, делайте это с наиболее сложным типом (IList или ICollection вместо IEnumerable), так как это дает клиенту больше возможностей. гибкость в том, как они используют объект.
Я нахожу разговор между собой и DrJokepu в комментариях увлекательным, но я также понимаю, что это не должен быть дискуссионный форум, поэтому я отредактирую свой ответ, чтобы еще больше изложить причины моего выбора, чтобы противостоять тенденции и предложите выставить его как IList (на самом деле это список, как вы увидите). Допустим, это класс, о котором мы говорим:
public class Example
{
private List<int> _internal = new List<int>();
public /*To be decided*/ External
{
get { return _internal; }
}
}
Итак, прежде всего давайте предположим, что мы выставляем External как IEnumerable. Причины, по которым я видел это в других ответах и комментариях, в настоящее время:
- IEnumerable доступен только для чтения
- IEnumerable является стандартом де-факто
- Это уменьшает связь, поэтому вы можете изменить реализацию
- Вы не раскрываете детали реализации
Хотя IEnumerable предоставляет только функциональность только для чтения, это не делает его доступным только для чтения. Реальный тип, который вы возвращаете, легко определяется путем отражения или просто приостановки отладчика и поиска, поэтому тривиально вернуть его обратно в List‹>, то же самое относится к примеру FileStream в комментариях ниже. Если вы пытаетесь защитить члена, то притворяться, что это что-то другое, — не лучший способ сделать это.
Не думаю, что это стандарт де-факто. Я не могу найти код в библиотеке .NET 3.5, где у Microsoft была возможность вернуть конкретную коллекцию и вместо этого вернуть интерфейс. IEnumerable является более распространенным в версии 3.5 благодаря LINQ, но это потому, что они не могут предоставить более производный тип, а не потому, что не хотят.
Теперь, уменьшая связанность, с которой я согласен - до определенного момента - в основном этот аргумент, кажется, заключается в том, что вы говорите клиенту, что, хотя возвращаемый вами объект является списком, который я хочу, чтобы вы обрабатывали, как IEnumerable просто на случай, если вы решите позже изменить внутренний код. Игнорируя проблемы с этим и тот факт, что реальный тип все равно выставлен, остается вопрос «зачем останавливаться на IEnumerable?» если вы вернете его как тип объекта, у вас будет полная свобода изменить реализацию на что угодно! Это означает, что вы должны были принять решение о том, сколько функциональности требуется клиентскому коду, поэтому либо вы пишете клиентский код, либо решение основано на произвольных показателях.
Наконец, как обсуждалось ранее, вы можете предположить, что детали реализации всегда доступны в .NET, особенно если вы публикуете объект публично.
Итак, после этой длинной обличительной речи остался один вопрос - Зачем выставлять это как Список‹>? А почему бы не? Вы ничего не выиграли, представив его как IEnumerable, так зачем же искусственно ограничивать возможности клиентского кода работать с объектом? Представьте, если бы Microsoft решила, что коллекция Controls элемента управления Winforms должна отображаться как IEnumerable вместо ControlCollection — вы больше не сможете передавать ее методам, требующим IList, работать с элементами в произвольном индексе или проверять, содержит определенный элемент управления, если вы не разыграете его. Microsoft на самом деле ничего бы не выиграла, а только доставила бы вам неудобства.
Когда мне нужно только перечислить дочерние элементы, я использую IEnumerable. Если мне нужен Count, я использую ICollection. Однако я стараюсь этого избегать, потому что это раскрывает детали реализации.
IList<T>
действительно очень мощный интерфейс. Я предпочитаю выставлять типы, производные от Collection<T>
. Это в значительной степени соответствует тому, что предлагает сделать Джеффри Рихтер (нет поблизости книги, поэтому нельзя указывать номер страницы/главы): методы должны принимать наиболее распространенные типы в качестве параметров и возвращать наиболее «производные» типы в качестве параметров. возвращаемые значения.
Если коллекция, доступная только для чтения, предоставляется через свойство, то обычный способ сделать это — представить ее как ReadOnlyCollection (или ее производный класс), обернув все, что у вас есть. Он предоставляет клиенту все возможности IList, и в то же время четко дает понять, что он доступен только для чтения.