Подтвердить что ты не робот

LINQ + Foreach vs Foreach + If

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

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

и этот код

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

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

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

4b9b3361

Ответ 1

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

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

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

Палуба не прошла от начала до конца более одного раза.. Однако, как парень слева, так и парень в середине обработали 52 карты, а парень по правой руке 26 карты. На карточках было всего 52 + 52 + 26 операций, но колода была только зациклирована.

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

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

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

Ответ 2

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

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

Jon Skeet Edulinq серия настоятельно рекомендуется, чтение некоторых бит этого должно помочь вам с пониманием операторов LINQ.

Ответ 3

Собственно, это не "цикл дважды". Предложение .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)
{
}

Ответ 4

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

theList.Where(itm => itm.Condition).ToList().ForEach(itmFE => { itmFe.DoSomething(); });