Вы ToList ()? - PullRequest
       50

Вы ToList ()?

25 голосов
/ 02 декабря 2008

У вас есть тип по умолчанию, который вы предпочитаете использовать в своих отношениях с результатами запросов LINQ?

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

Единственное исключение из этого было в LINQ to SQL, где вызов .ToList() мог бы преждевременно перечислить IEnumerable.

Мы также широко используем WCF, тип коллекции по умолчанию которого System.Array. Мы всегда изменяем это значение на System.Collections.Generic.List в диалоговом окне «Настройки справочника услуг» в VS2008 для согласованности с остальной частью нашей кодовой базы.

Что вы делаете?

Ответы [ 5 ]

23 голосов
/ 02 декабря 2008

ToList всегда оценивает последовательность сразу, а не только в LINQ to SQL. Если ты этого хочешь, это нормально - но это не всегда уместно.

Лично я бы постарался не объявлять, что вы возвращаете List<T> напрямую - обычно IList<T> более уместно и позволяет позже перейти к другой реализации. Конечно, есть некоторые операции, которые указаны только на самом List<T> ... такого рода решения всегда сложно.

РЕДАКТИРОВАТЬ: (Я бы поставил это в комментарии, но это было бы слишком громоздко.) Отложенное выполнение позволяет вам иметь дело с источниками данных, которые слишком велики, чтобы поместиться в памяти. Например, если вы обрабатываете файлы журналов - преобразуете их из одного формата в другой, загружаете их в базу данных, определяете некоторую статистику или что-то в этом роде - вы вполне можете обрабатывать произвольные объемы данных путем их потоковой передачи. , но вы действительно не хотите высосать все в память. Это может не беспокоить ваше конкретное приложение, но об этом следует помнить.

16 голосов
/ 03 декабря 2008

У нас тот же сценарий - связь WCF с сервером, сервер использует LINQtoSQL.

Мы используем .ToArray () при запросе объектов с сервера, потому что для клиента «запрещено» изменять список. (Это означает, что нет цели поддерживать ".Add", ".Remove" и т. Д.).

Однако, оставаясь на сервере, я бы порекомендовал оставить его по умолчанию (это не IEnumerable, а скорее IQueryable). Таким образом, если вы хотите фильтровать еще больше на основе некоторых критериев, фильтрация будет STILL на стороне SQL до оценки.

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

Пример:

// This is just an example... imagine this is on the server only. It's the
// basic method that gets the list of clients.
private IEnumerable<Client> GetClients()
{
    var result = MyDataContext.Clients;  

    return result.AsEnumerable();
}

// This method here is actually called by the user...
public Client[] GetClientsForLoggedInUser()
{
    var clients = GetClients().Where(client=> client.Owner == currentUser);

    return clients.ToArray();
}

Вы видите, что там происходит? Метод «GetClients» будет принудительно загружать ВСЕХ «клиентов» из базы данных ... ТОГДА предложение Where будет происходить в методе GetClientsForLoogedInUser для его фильтрации.

Теперь обратите внимание на небольшое изменение:

private IQueryable<Client> GetClients()
{
    var result = MyDataContext.Clients;  

    return result.AsQueryable();
}

Теперь фактическая оценка не произойдет, пока не будет вызван ".ToArray" ... и SQL выполнит фильтрацию. НАМНОГО лучше!

7 голосов
/ 03 декабря 2008

В случае Linq-to-Objects возврат List<T> из функции не так хорош, как возврат IList<T>, как указывает VENERABLE SKEET. Но часто вы все еще можете сделать лучше, чем это. Если вещь, которую вы возвращаете, должна быть неизменной, IList - плохой выбор, поскольку он предлагает вызывающей стороне добавлять или удалять вещи.

Например, иногда у вас есть метод или свойство, которое возвращает результат запроса Linq или использует yield return для ленивой генерации списка, и тогда вы понимаете, что было бы лучше сделать это в первый раз, когда вы вызвано, кэшируйте результат в List<T> и затем возвращайте кешированную версию. Вот почему возврат IList может быть плохой идеей, потому что вызывающая сторона может изменить список для своих собственных целей, что затем повредит ваш кеш, делая их изменения видимыми для всех остальных вызывающих абонентов.

Лучше вернуть IEnumerable<T>, поэтому все, что у них есть, это прямая итерация. И если вызывающий хочет получить быстрый произвольный доступ, т. Е. Он желает, чтобы он мог использовать [] для доступа по индексу, он может использовать ElementAt, который определяет Linq, так что он спокойно анализирует IList и использует это, если доступно, а если нет он делает тупой линейный поиск.

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

2 голосов
/ 04 декабря 2008

Если вам не нужны дополнительные функции List <>, почему бы просто не использовать IQueryable <>?!?!?! Наименьший общий знаменатель - лучшее решение (особенно когда вы видите ответ Тимоти).

2 голосов
/ 02 декабря 2008

Зависит от того, нужно ли вам модифицировать коллекцию. Мне нравится использовать массив, когда я знаю, что никто не собирается добавлять / удалять элементы. Я использую список, когда мне нужно отсортировать / добавить / удалить элементы. Но обычно я просто оставляю это как можно больше, пока я могу.

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