какой тип коллекции лучше всего возвращать в API - PullRequest
10 голосов
/ 28 июня 2010

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

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

например:

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

Ответы [ 4 ]

6 голосов
/ 28 июня 2010

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

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

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

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

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

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> но они не такие совершенные.

6 голосов
/ 28 июня 2010

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

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

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

При IEnumerable<T> потребитель API получает «пакет» предметов без определенного порядка, без индекса и без методов для изменения коллекции.

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

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

5 голосов
/ 28 июня 2010

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

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

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

Подробнее см. http://weblogs.asp.net/fredriknormen/archive/2008/12/01/returning-iqueryable-lt-t-gt.aspx.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...