Ответ 1
Эрик Липперт имеет хорошую статью об этом. Если вы не можете почитать всю статью, ответ будет следующим: верните интерфейс.
Какой предпочтительный тип контейнера при возвращении нескольких объектов одного и того же типа из функции?
Не рекомендуется ли возвращать простой массив (например, MyType []) или его следует обернуть в какой-нибудь общий контейнер (например, ICollection <MyType> )?
Спасибо!
Эрик Липперт имеет хорошую статью об этом. Если вы не можете почитать всю статью, ответ будет следующим: верните интерфейс.
Верните IEnumerable<T>
с помощью yield return
.
Я бы вернул IList<T>
, поскольку это дает потребителю вашей функции наибольшую гибкость. Таким образом, если потребителю вашей функции нужно только перечислить последовательность, которую они могут сделать, но если они хотят использовать эту последовательность в качестве списка, они могут это сделать.
Мое общее правило - принять наименее ограничительный тип в качестве параметра и вернуть самый богатый тип, который я могу. Это, конечно, балансирующий акт, так как вы не хотите блокировать себя каким-либо конкретным интерфейсом или реализацией (но всегда всегда старайтесь использовать интерфейс).
Это наименее самонадеянный подход, который вы, разработчик API, можете принять. Вам не решать, как потребитель вашей функции будет использовать то, что они вам отправляют, поэтому вы должны вернуть IList<T>
в этом случае, чтобы предоставить им максимальную гибкость. Кроме того, по этой же причине вы никогда не захотите узнать, какой тип параметра вы отправите потребителю. Если вам нужно только повторить последовательность, отправленную вам в качестве параметра, тогда сделайте параметр a IEnumerable<T>
, а не List<T>
.
EDIT (монооксид): Поскольку это не похоже, что вопрос будет закрыт, я просто хочу добавить ссылку из другого вопроса об этом: Почему массивы вредны
Почему бы не List<T>
?
Из сообщения Эрика Липперта, упомянутого другими, я подумал, что я остановлюсь на этом:
Если мне нужна последовательность Ill use
IEnumerable<T>
, если мне нужно отображение от смежных чисел до данных Ill используйтеList<T>
, если мне нужно отображение по произвольным даннымDictionary<K,V>
, если мне нужен набор Ill используйтеHashSet<T>
. Мне просто не нужно массивов для чего угодно, поэтому я почти никогда используй их. Они не решают проблему, я лучше, чем другие инструменты на моем удаление.
Если возвращаемая коллекция доступна только для чтения, это означает, что вы никогда не хотите, чтобы элементы в коллекции были изменены, затем используйте IEnumerable<T>
. Это самое основное представление последовательности только для чтения неизменяемых (по крайней мере, с точки зрения самих элементов перечисления).
Если вы хотите, чтобы это была автономная коллекция, которую можно изменить, используйте ICollection<T>
или IList<T>
.
Например, если вы хотите вернуть результаты поиска для определенного набора файлов, верните IEnumerable<FileInfo>
.
Однако, если вы хотите разоблачить файлы в каталоге, вы можете открыть IList/ICollection<FileInfo>
, поскольку имеет смысл, что вы захотите изменить содержимое коллекции.
Хороший совет, который я уже слышал, таков:
Будьте либеральны в том, что вы принимаете, точным в том, что вы предоставляете.
Что касается проектирования вашего API, я бы предложил вам вернуть интерфейс, а не конкретный тип.
Взяв ваш примерный метод, я переписал его следующим образом:
public IList<object> Foo()
{
List<object> retList = new List<object>();
// Blah, blah, [snip]
return retList;
}
Ключ в том, что ваш внутренний выбор реализации - использование списка - не отображается вызывающему, но вы возвращаете соответствующий интерфейс.
Руководства Microsoft по разработке рамок рекомендуют не возвращать определенные типы, предпочитая интерфейсы. (Извините, я не смог найти ссылку для этого)
Аналогично, ваши параметры должны быть как можно более общими - вместо принятия массива принять IEnumerable соответствующего типа. Это совместимо с массивами, а также списками и другими полезными типами.
Повторное использование вашего примера:
public IList<object> Foo(IEnumerable<object> bar)
{
List<object> retList = new List<object>();
// Blah, blah, [snip]
return retList;
}
return ICollection<type>
Преимущество типичных типов возврата заключается в том, что вы можете изменить базовую реализацию, не меняя код, который ее использует. Преимущество возврата определенного типа состоит в том, что вы можете использовать более специфичные для типа методы.
Всегда возвращайте тип интерфейса, который предоставляет наибольшую функциональность вызывающему. Поэтому в вашем случае ICollection<YourType>
следует использовать.
Интересно отметить, что разработчики BCL действительно ошибались в некотором месте платформы .NET - см. это сообщение в блоге Eric Lippert для этой истории.
Почему бы не IList<MyType>
?
Он поддерживает прямую индексацию, которая является признаком для массива, не удаляя возможности вернуть List<MyType>
в один прекрасный день. Если вы хотите подавить эту функцию, вы, вероятно, захотите вернуться IEnumerable<MyType>
.
Это зависит от того, что вы планируете делать с возвращаемой вами коллекцией. Если вы просто выполняете итерацию или хотите только, чтобы пользователь выполнял итерацию, то я согласен с @Daniel, вернем IEnumerable<T>
. Однако, если вы действительно хотите разрешить операции на основе списков, я бы вернул IList<T>
.
Используйте generics. Легче взаимодействовать с другими классами коллекций, и система типов более способна помочь вам с потенциальными ошибками.
Старый стиль возврата массива был костылем перед дженериками.
Что делает ваш код более удобочитаемым, удобным и легким для ВАС. Я бы использовал простой массив, более простой - лучше в большинстве случаев. Хотя мне действительно нужно увидеть контекст, чтобы дать правильный ответ.
Есть большие преимущества в пользу IEnumerable над чем-либо еще, поскольку это дает вам максимальную гибкость реализации и позволяет использовать операторы yield return
или Linq для ленивой реализации.
Если вызывающий абонент хочет List<T>
вместо этого, он может просто вызвать ToList()
на все, что вы вернули, а общая производительность будет примерно такой же, как если бы вы создали и вернули новый List<T>
из вашего метода.
Массив вреден, но ICollection<T>
также вреден.
ICollection<T>
не может гарантировать, что объект будет неизменным.
Моя рекомендация - обернуть возвращаемый объект ReadOnlyCollection<T>