Каков наилучший тип коллекции для возврата в API

Я всегда думал, что возвращаемые массивы лучше, чем списки при наличии открытого API, но теперь, похоже, все эти функции перечислены в списках, доступных через LINQ и т.д.

Изменилась ли наилучшая практика для возврата коллекций примитивов или объектов?

например:

Order[] GetOrders();
List<Order> GetOrders();
IEnumerable<Order> GetOrders();
IQueryable<Order> Get Orders();

Ответ 1

Я думаю, что наиболее часто используемым типом является IEnumerable<T>

  • С типом возврата IEnumerable<T> вы можете использовать ключевое слово yield, и это позволяет задерживать перечисление и выполнение вашего метода. (Например, общая жалоба заключается в том, что System.IO.Path.GetFiles() НЕ возвращает IEnumerable<T>, но возвращает массив, что означает, что при вызове метода все элементы должны быть перечислены независимо от того, нужны они вам или нет. - У вас такой же недостаток с List<T>)
  • Большинство методов расширения LINQ работают с IEnumerable<T>
  • Возвращаемый тип IEnumerable<T> не предполагает ничего конкретного о вызывающем. Если вызывающему абоненту нужен список или массив, он всегда может создать его.
  • IEnumerable<T> реализуется List<T> и Array, и поэтому легко изменить реализацию вашего метода и по-прежнему поддерживать предыдущий тип возвращаемого значения.

Ответ 2

Вы хотите, чтобы коллекция была неизменной? IEnumerable<T> Mutable? IList<T>

Вам нужны индексы? IList<T>

С помощью списка или массива пользователь API имеет полную возможность добавлять, удалять, удалять, очищать и т.д.

С помощью IEnumerable<T> потребитель API получает "сумку" элементов без определенного порядка, без индекса и методов для изменения коллекции.

Есть некоторые ограниченные обстоятельства, в которых вы можете захотеть вернуть IQueryable<T>, но они часто специфичны для построения запросов по частям.

В общем, я по умолчанию должен был бы IEnumerable<T>. Я мог бы использовать List<T> в качестве поля поддержки, но только хочу разрешить пользователю Add(), например, - не удалять, не очищать или что-либо еще, поэтому я объявляю public void Add<T>(T item), который просто добавляет элемент в список полей поддержки.

Ответ 3

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

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

Если вы используете IEnumerable<T> и освобождаете свой тип в неконтролируемой пустыне, будьте осторожны:

class MyClass {
    private List<int> _list;
    public IEnumerable<int> Numbers {
        get { return _list; }
    }
}

Как пользователь может это сделать и испортить внутреннее состояние вашего класса:

var list = (List<int>)myClass.Numbers;
list.Add(123);

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

    public IEnumerable<int> Numbers {
        get { return new ReadOnlyCollection<int>(_list); }
    }

В качестве альтернативы вы можете позвонить _list.ToReadOnly(). Я написал его полностью, чтобы показать тип.

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

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

Лично я хотел бы включить фреймворк и использовать такой интерфейс:

public interface IReadOnlyCollection<T> : IEnumerable<T>
{
    T this[int index] { get; }
    int Count { get; }
    bool Contains(T item);
    void CopyTo(T[] array, int arrayIndex);
    int IndexOf(T item);
}

Вы можете получить все эти функции с помощью методов расширения поверх IEnumerable<T>, но они не так эффективны.

Ответ 4

В дополнение к тому, что сказали другие, я хочу добавить, что вы никогда не должны возвращать IQueryable если вы не IQueryable объекты из какого-либо удаленного источника данных. IQueryable - это объект запроса, который будет извлекать результаты от имени вызывающей стороны. При использовании LINQ IQueryable обычно принимает дерево выражений, которое будет переведено для использования какой-либо другой системой (например, SQL Server).

См. Http://weblogs.asp.net/fredriknormen/archive/2008/12/01/returning-iqueryable-lt-t-gt.aspx для получения более подробной информации.