I need to iterate over a List of objects, doing something only for the objects that have a boolean property set to true. I\'m debating between this code
for
Actually, it's not "looping twice." The .Where
clause uses deferred execution. In other words, practically no work is performed when you call .Where
, but when you iterate over the result, it will iterate over the original list and only pass through items that match your condition. If you think of it in terms of how the code gets executed, you're effectively doing this:
Func<Parameter, bool> matchesCondition = p => p.Condition;
foreach(var parameter in parameters)
{
if(matchesCondition(parameter))
{
...
}
}
As a matter of style, I personally prefer something more like:
var matchingParameters = parameters.Where(p => p.Condition);
foreach(var parameter in matchingParameters)
{
}
I prefer this:
theList.Where(itm => itm.Condition).ToList().ForEach(itmFE => { itmFe.DoSomething(); });
Most Linq operators such as Where
are implemented to support deferred and lazy execution. In your example, the list will be iterated over only once because the enumerator sitting behind the IEnumerable returned by Where
will enumerate the list until it finds an item matching the predicate, yields it and will only continue when it is asked for the next element.
From a code perspective, I'd prefer the variant using where, although it could be argued you could declare a local for parameters.Where(p => p.Condition)
.
Jon Skeet's Edulinq series comes highly recommended, reading some bits of this should help you with your understanding of LINQ operators.
Jon Skeet sometimes does a live-action LINQ demo to explain how this works. Imagine you have three people on stage. On the left we have one guy who has a shuffled deck of cards. In the middle we have one guy who only passes along red cards, and on the right, we have a guy who wants cards.
The guy on the right pokes the guy in the middle. The guy in the middle pokes the guy on the left. The guy on the left hands the guy in the middle a card. If it is black, the guy in the middle throws it on the floor and pokes again until he gets a red card, which he then hands to the guy on the right. Then the guy on the right pokes the guy in the middle again.
This continues until the guy on the left runs out of cards.
The deck was not gone through from start to finish more than once. However, both the guy on the left and the guy in the middle handled 52 cards, and the guy on the right handled 26 cards. There were a total of 52 + 52 + 26 operations on cards, but the deck was only looped through once.
Your "LINQ" version and the "continue" version are the same thing; if you had
foreach(var card in deck)
{
if (card.IsBlack) continue;
... use card ...
then there are 52 operations that fetch each card from the deck, 52 operations that test to see if each card is black, and 26 operations that act on the red card. Same thing exactly.