2008-12-02 3 views
25

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

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

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

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

Что вы будете делать?

ответ

23

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

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

EDIT: (Я бы поставил это в комментарии, но это было бы слишком громоздким.) Отложенное выполнение позволяет вам иметь дело с источниками данных, которые слишком велики для размещения в памяти. Например, если вы обрабатываете файлы журналов - преобразовывая их из одного формата в другой, загружая их в базу данных, разрабатывая некоторые статистические данные или что-то в этом роде - вы вполне можете иметь возможность обрабатывать произвольные объемы данных, передавая их , но вы действительно не хотите сосать все в память. Это не может быть проблемой для вашего конкретного приложения, но это то, что нужно помнить.

+1

Согласен, что ToList оценивает немедленно. Наше мышление заключается в том, что в LINQtoSQL это может повлиять на производительность (особенно, если мы объединяем несколько выражений LINQ), но когда мы находимся в памяти, любой удар по производительности будет небрежным, а последовательность для людей больше важный. – 2008-12-02 16:56:30

+3

Имеются значительные отличия от производительности. В частности, если что-либо о запросе (например, данные в источнике) изменяется, то отложенное выполнение даст вам другой ответ. Иногда это то, что вы хотите, иногда это не так. – 2008-12-02 17:08:33

2

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

16

У нас такой же сценарий - связь 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 будет делать фильтрацию. Намного лучше!

+0

Ваша точка очень отчетливая. Большинство людей, похоже, пропустили его. Я вижу, что действительно плохие примеры, подобные этому, использовались все время. Люди не перестают думать о том, что будет делать при исполнении. – 2008-12-04 04:21:56

7

В случае с Linq-to-Objects возврат List<T> от функции не так хорош, как возвращение IList<T>, так как указывает ВОЗМОЖНЫЙ СКОРОСТЬ. Но часто вы все еще можете сделать лучше. Если вещь, которую вы возвращаете, должна быть неизменной, IList - это плохой выбор, потому что он приглашает вызывающего добавить или удалить вещи.

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

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

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

2

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