Должен ли я всегда возвращать IEnumerable <T> вместо IList <T>?

Когда я пишу свой DAL или другой код, который возвращает набор элементов, должен ли я всегда делать оператор return:

public IEnumerable<FooBar> GetRecentItems()

или

public IList<FooBar> GetRecentItems()

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

Ответ 1

Это действительно зависит от того, почему вы используете этот конкретный интерфейс.

Например, IList<T> имеет несколько методов, отсутствующих в IEnumerable<T>:

  • IndexOf(T item)
  • Insert(int index, T item)
  • RemoveAt(int index)

и Свойства:

  • T this[int index] { get; set; }

Если вам нужны эти методы каким-либо образом, то обязательно верните IList<T>.

Кроме того, если метод, который потребляет ваш результат IEnumerable<T>, ожидает IList<T>, он сохранит CLR от рассмотрения любых необходимых преобразований, тем самым оптимизируя скомпилированный код.

Ответ 2

Рекомендации по дизайну каркаса рекомендуют использовать класс Collection, когда вам нужно вернуть коллекцию, которая может быть изменена вызывающим или ReadOnlyCollection для коллекций только для чтения.

Причина, по которой это предпочтение относится к простому IList, заключается в том, что IList не сообщает вызывающему абоненту, если его только чтение или нет.

Если вы вернете IEnumerable<T> вместо этого, некоторые операции могут быть немного сложнее для вызывающего абонента. Кроме того, вы больше не дадите вызывающему абоненту гибкость в изменении коллекции, что вам может или не нужно.

Имейте в виду, что LINQ содержит несколько трюков в рукаве и оптимизирует определенные вызовы в зависимости от типа, в котором они выполняются. Так, например, если вы выполняете граф, а базовая коллекция - это список, он НЕ будет проходить через все элементы.

Лично для ORM я, вероятно, придерживаюсь Collection<T> как мое возвращаемое значение.

Ответ 3

Это зависит...

Возврат наименее производного типа (IEnumerable) предоставит вам максимальную свободу действий, чтобы изменить базовую реализацию по пути.

Возврат более производного типа (IList) предоставляет пользователям вашего API больше операций с результатом.

Я всегда предлагаю вернуть наименее производный тип, который имеет все операции, которые вам понадобятся вашим пользователям... так что вначале вам нужно определить, какие операции с результатом имеют смысл в контексте API, который вы определяете.

Ответ 4

В общем, вы должны требовать самого общего и возвращать самую конкретную вещь, которую вы можете. Поэтому, если у вас есть метод, который принимает параметр, и вам действительно нужно только то, что доступно в IEnumerable, тогда это должен быть ваш тип параметра. Если ваш метод может вернуть IList или IEnumerable, предпочитайте возвращать IList. Это гарантирует, что он может использоваться широким кругом потребителей.

Быть свободным в том, что вам нужно, и явно в том, что вы предоставляете.

Ответ 5

List<T> предлагает код вызова еще много функций, таких как изменение возвращаемого объекта и доступ по индексу. Таким образом, вопрос сводится к следующему: в вашем конкретном случае использования вашего приложения вы хотите поддерживать такое использование (предположительно, возвращая недавно собранную коллекцию!), Для удобства звонящего - или вы хотите скорость для простого случая, когда все потребности вызывающего абонента - это цикл через коллекцию, и вы можете спокойно вернуть ссылку на реальную базовую коллекцию, не опасаясь, что это приведет к ее ошибочному изменению и т.д.?

Только вы можете ответить на этот вопрос и только, хорошо понимая, что ваши абоненты захотят делать с возвращаемым значением, и насколько важна производительность здесь (насколько велики будут ваши коллекции, насколько вероятно это будет узкое место и т.д.).

Ответ 6

Следует учитывать, что если вы используете оператор LINQ с отсроченным исполнением для генерации вашего IEnumerable<T>, вызов .ToList(), прежде чем вы вернетесь из своего метода, означает, что ваши объекты могут повторяться дважды - один раз, чтобы создать Список и один раз, когда вызывающий объект прокручивает, фильтрует или преобразует возвращаемое значение. Когда это практично, мне нравится избегать преобразования результатов LINQ-to-Objects в конкретный Список или Словарь, пока я не должен. Если моему абоненту нужен список, то один простой метод отзывается - мне не нужно принимать это решение для них, и это делает мой код немного более эффективным в тех случаях, когда вызывающий абонент просто делает foreach.

Ответ 7

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

IEnumerable не эффективен для подсчета элементов

Если сборка предназначена для чтения только, или изменение коллекции управляется Parent, то возврат IList только для Count не является хорошей идеей.

В Linq существует метод расширения Count() на IEnumerable<T>, который внутри CLR будет сокращен до .Count, если базовый тип имеет значение IList, поэтому разница в производительности незначительна.

В целом я считаю (мнение), что лучше, если возможно, возвращать IEnumerable, если вам нужно делать дополнения, тогда добавьте эти методы в родительский класс, иначе потребитель будет управлять коллекцией в Модели, которая нарушает принципы, например. manufacturer.Models.Add(model) нарушает закон деметатора. Конечно, это всего лишь рекомендации, а не жесткие и быстрые правила, но до тех пор, пока у вас не будет полного понимания применимости, слепо лучше, чем вообще не следует.

public interface IManufacturer 
{
     IEnumerable<Model> Models {get;}
     void AddModel(Model model);
}

(Примечание. Если вы используете nNHibernate, вам может понадобиться сопоставить личный IList с использованием разных аксессуаров.)

Ответ 8

Это не так просто, когда вы говорите о возвращаемых значениях вместо входных параметров. Когда это входной параметр, вы точно знаете, что вам нужно делать. Итак, если вам нужно иметь возможность перебирать коллекцию, вы берете IEnumberable, тогда как если вам нужно добавить или удалить, вы берете IList.

В случае возвращаемого значения он становится более жестким. Что ожидает ваш абонент? Если вы вернете IEnumerable, то он не будет знать априори, что он может сделать IList из него. Но, если вы вернете IList, он будет знать, что он может перебирать его. Таким образом, вы должны учитывать, что ваш абонент собирается делать с данными. Функциональность, которую требует/ожидает ваш вызывающий абонент, должна регулироваться при принятии решения о возврате.

Ответ 9

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

IEnumerable неэффективен для подсчета элементов или получения определенного элемента в коллекции.

List - это коллекция, которая идеально подходит для поиска определенных элементов, легко добавлять элементы или удалять их.

Обычно я стараюсь использовать List, где это возможно, поскольку это дает мне большую гибкость.

Использование   List<FooBar> getRecentItems() скорее, чем   IList<FooBar> GetRecentItems()

Ответ 10

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

Ответ 11

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

Однако, если вам нужно количество элементов, не забывайте, что между IEnumerable и IList существует другой слой - ICollection.

Ответ 12

Я мог бы быть немного отсюда, видя, что никто еще не предложил его до сих пор, но почему бы вам не вернуть (I)Collection<T>?

Из того, что я помню, Collection<T> был предпочтительным типом возвращаемого значения над List<T>, потому что он абстрагирует реализацию. Все они реализуют IEnumerable, но это звучит для меня слишком низкоуровневым для работы.

Ответ 13

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

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

Помните, что перемещение UP в коллекцию из IEnumerable в интерфейсе будет работать, перемещение вниз до IEnumerable из коллекции приведет к поломке существующего кода.

Если эти мнения кажутся противоречивыми, это потому, что решение субъективно.