LINQ + Foreach против Foreach + If - PullRequest
       5

LINQ + Foreach против Foreach + If

54 голосов
/ 31 января 2012

Мне нужно перебрать список объектов, делая что-то только для объектов, для которых булево свойство имеет значение true. Я спорю между этим кодом

foreach (RouteParameter parameter in parameters.Where(p => p.Condition))
{ //do something }

и этот код

foreach (RouteParameter parameter in parameters)
{ 
  if !parameter.Condition
    continue;
  //do something
}

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

Вопрос: Есть ли чистый / красивый способ написать это без циклического повторения дважды?

Ответы [ 4 ]

127 голосов
/ 31 января 2012

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

Парень справа ткнет парня посередине. Парень посередине тычет парню слева. Парень на левой руке, парень посередине карты. Если он черный, парень посередине бросает его на пол и снова тыкает, пока не получит красную карточку, которую он затем вручает парню справа. Затем парень справа снова толкает парня посередине.

Это продолжается до тех пор, пока у парня слева не кончатся карты.

Колода не проходила от начала до конца более одного раза. Однако и парень слева, и парень посередине раздали 52 карты, а парень справа - 26 карт. , Всего было проведено 52 + 52 + 26 операций с картами, но колода проходила только один раз .

Ваша версия "LINQ" и версия "continue" - это одно и то же; если бы у вас было

foreach(var card in deck)
{
    if (card.IsBlack) continue;
    ... use card ...

тогда есть 52 операции, которые выбирают каждую карту из колоды, 52 операции, которые проверяют, является ли каждая карта черной, и 26 операций, которые воздействуют на красную карту. Точно так же.

35 голосов
/ 31 января 2012

Большинство операторов Linq, таких как Where, реализованы для поддержки отложенного и отложенного выполнения . В вашем примере список будет повторяться только один раз, потому что перечислитель, стоящий за IEnumerable, возвращаемым Where, будет перечислять список, пока не найдет элемент, соответствующий предикату, выдаст его и продолжит работу только тогда, когда его попросят о следующем элемент.

С точки зрения кода, я бы предпочел вариант с использованием where, хотя можно утверждать, что вы можете объявить local для parameters.Where(p => p.Condition).

Серия Jon Skeet Edulinq очень рекомендуется, и некоторые ее фрагменты помогут вам понять операторов LINQ.

26 голосов
/ 31 января 2012

На самом деле, это не «цикл дважды».Предложение .Where использует отложенное выполнение.Другими словами, при вызове .Where работа практически не выполняется, но когда вы перебираете результат, он перебирает исходный список и пропускает только те элементы, которые соответствуют вашему условию.Если вы думаете об этом с точки зрения того, как выполняется код, вы фактически делаете это:

Func<Parameter, bool> matchesCondition = p => p.Condition;
foreach(var parameter in parameters)
{
    if(matchesCondition(parameter))
    {
        ...
    }
}

В качестве стиля я лично предпочитаю что-то более похожее на:

var matchingParameters = parameters.Where(p => p.Condition);
foreach(var parameter in matchingParameters)
{
}
0 голосов
/ 12 июня 2012

Я предпочитаю это:

theList.Where(itm => itm.Condition).ToList().ForEach(itmFE => { itmFe.DoSomething(); }); 
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...