Правила большого пальца для вызова ToList при возврате результатов LINQ

Я ищу эмпирические правила для вызова ToList/ToArray/MemoizeAll(Rx) on IEnumerables, а не возврата самого запроса при возврате IEnumerable чего-то.

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

Я хочу собрать рекомендации, такие как:

Вызов ToList, если:

  • вы создаете новые объекты (например, при выборе)
  • у вас есть побочные эффекты в вашем запросе

В противном случае верните запрос

Ответ 1

Во-первых, вы никогда не должны иметь побочных эффектов в запросе. Это худшая практика. Запросы должны отвечать на вопрос, а не давать эффект.

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

Рассматривая, хочет ли вызывающий запрос или список, подумайте о различиях между запросами и списками:

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

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

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

  • запросы крошечные. Списки большие. Многие запросы могут быть повторены по n элементам в O (1) пространстве; список с n элементами занимает пространство O (n). Если набор результатов огромен, то помещать его в список, вероятно, неэффективно.

  • и т.д.

Нет простого ответа. Ответ такой же, как и ответ на любые другие проблемы с дизайном: Рассмотрите все плюсы и минусы каждого возможного решения в контексте того, что, скорее всего, хочет пользователь функции, а затем выберите разумное компромиссное решение.

Ответ 2

Возврат ToList, если:

  • Вы не хотите или не заботитесь о ленивой оценке запросов.

Edit:

Кроме того, верните ToList, если:

  • Вы используете какую-то структуру Linq to SQL (LLBLGen, EF и т.д.), и вам нужно выполнить операцию в списке, который не может быть преобразован в SQL с помощью фреймворка.

Ответ 3

Используйте ToList, если вам нужно запустить пользовательские функции для данных, возвращаемых LINQ to SQL.

Ответ 4

Используйте ToList перед тем, как выйти из блока using, который содержит ваш DataContext.

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

Ответ 5

вы ToList(), когда вам нужен список объектов для вашего результата.