Должен ли я возвращать массив или коллекцию из функции?

StackOverflow https://stackoverflow.com/questions/602860

  •  03-07-2019
  •  | 
  •  

Вопрос

Какой тип контейнера предпочтительнее при возврате нескольких объектов одного типа из функции?

Противоречит ли хорошей практике возвращать простой массив (например, MyType[]), или вы должны обернуть его в какой-то универсальный контейнер (например, ICollection<MyType>)?

Спасибо!

Это было полезно?

Решение

У Эрика Липперта хороший Статья на этом.На случай, если вы не потрудитесь прочитать всю статью целиком, ответ таков:верните интерфейс.

Другие советы

Вернуть IEnumerable<T> используя yield return.

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

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

Это наименее самонадеянный подход, который вы, разработчик API, можете использовать.Не вам решать, как потребитель вашей функции будет использовать то, что они вам отправляют, - вот почему вы должны вернуть IList<T> в данном случае так, чтобы придать им наибольшую гибкость.Также по этой же причине вы никогда не предполагали бы, что знаете, какой тип параметра отправит вам потребитель.Если вам нужно только повторить последовательность, отправленную вам в качестве параметра, то сделайте параметр IEnumerable<T> вместо того, чтобы List<T>.


РЕДАКТИРОВАТЬ (монооксид углерода): Поскольку не похоже, что вопрос будет закрыт, я просто хочу добавить ссылку из другого вопроса по этому поводу: Почему массивы вредны

Почему бы и нет List<T>?

Исходя из сообщения Эрика Липперта, упомянутого другими, я подумал, что выделю это:

Если мне понадобится последовательность, я использую IEnumerable<T>, если мне понадобится сопоставление от смежных чисел к данным, я буду использовать List<T>, если мне понадобится сопоставление для произвольных данных я буду использовать Dictionary<K,V>, если мне понадобится набор, я буду использовать 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 framework - см. это сообщение в блоге Эрика Липперта для этой истории.

Почему бы и нет IList<MyType>?

Он поддерживает прямую индексацию, которая является отличительной чертой массива, не исключая возможности возврата List<MyType> когда-нибудь.Если вы хотите подавить эту функцию, вы, вероятно, захотите вернуть IEnumerable<MyType>.

Это зависит от того, что вы планируете делать с возвращаемой коллекцией.Если вы просто выполняете итерацию или хотите, чтобы пользователь выполнял только итерацию, тогда я согласен с @Daniel, верните IEnumerable<T>.Однако, если вы действительно хотите разрешить операции на основе списков, я бы вернул IList<T>.

Используйте дженерики.Проще взаимодействовать с другими классами коллекций, а система типов в большей степени способна помочь вам с потенциальными ошибками.

Старый стиль возврата массива был опорой перед дженериками.

Что вообще делает ваш код более читабельным, ремонтопригодным и простым для ВАС.Я бы использовал простой массив, в большинстве случаев simpler==better.Хотя мне действительно нужно видеть контекст, чтобы дать правильный ответ.

Предпочтение IEnumerable перед чем-либо другим имеет большие преимущества, поскольку это дает вам наибольшую гибкость реализации и позволяет использовать yield return или операторы Linq для отложенной реализации.

Если вызывающий абонент хочет List<T> вместо этого они могут просто позвонить ToList() на том, что вы вернули, и общая производительность будет примерно такой же, как если бы вы создали и вернули новый List<T> из вашего метода.

Массив вреден, но ICollection<T> это тоже вредно.

ICollection<T> не могу гарантировать, что объект будет неизменяемым.

Моя рекомендация - обернуть возвращаемый объект с помощью ReadOnlyCollection<T>

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top