Valeri
Valeri

Reputation: 97

What is the difference between an operation with foreach loop and LINQ?

I'm solving a problem, and I understood that these codes work in different ways, but I can't understand why the second one isn't correct and what's the difference.

public Person GetOldestMember()
{
    Person oldestPerson = people.OrderByDescending(x => x.Age).FirstOrDefault();

    return oldestPerson;
}

.

public Person GetOldestMember()
{
    Person oldestPerson = new Person(-1); //this is a constructor with parameter age

    foreach (Person person in people)
    {
        if (person.Age > oldestPerson.Age)
        {
            oldestPerson = person;
        }
    }

    return oldestPerson;
}

Upvotes: 0

Views: 114

Answers (2)

Marc Gravell
Marc Gravell

Reputation: 1062685

  • LINQ is optimized for readability; it is easy to write, read, and understand
  • but it isn't always quite as efficient; it can't exploit things like custom iterators, and may involve more objects
  • simple looking operations (OrderBy, for example) can be really very expensive
  • but then... in most cases, that doesn't matter, and the readability is the winner
  • but then... in some cases, that does matter :)

IF WE ASSUME THAT THIS IS IN-MEMORY DATA (LINQ-Objects) - something like IEnumerable<Person>, List<Person>, Person[], etc:

Note that sorting is a relatively expensive operation, and when using LINQ that also usually means creating a copy of the data (so as not to change the source). There are external extension methods available that do this more efficiently within the LINQ concept, i.e.

Person oldestPerson = people.MaxBy(x => x.Age);

Again, not quite as efficient as the loop, but tons more efficient than OrderByDescending+FirstOrDefault... just watch out how this behaves for empty inputs (it might throw rather than returning null).


However, as Dzyann observes in the comments: people here could be an IQueryable<Person> - something like a DbSet<Person> from EF or LINQ-to-SQL (etc), in which case everything changes: now we're talking about queries that get pushed down to an external resource, in which case the OrderByDescending + FirstOrDefault could become SQL like:

SELECT TOP 1 *
FROM People
ORDER BY Age DESC

and we've become a hero. If we did that via foreach over an IQueryable<Person>, we would have issued:

SELECT *
FROM People

then fetched everything over the network as we iterate locally to see which is oldest.

Upvotes: 6

Mike Hofer
Mike Hofer

Reputation: 17002

One of the things about LINQ queries that isn't immediately obvious is that they tend to "fail fast". That is, they exit the loop as soon as the specified criteria is met, rather than iterating over the entire sequence.

For example, FirstOrDefault() gets the first item from the sequence and exits immediately, if there are any items in the sequence. If there aren't any, it immediately returns null. It does not iterate over the sequence.

Your for loop, however, iterates over each element in the sequence. If the sequence is very large, this can be time consuming. Further, if your exit condition is wrong or missing altogether, you may return the wrong element (although that can happen in LINQ expressions as well).

LINQ expressions are very efficient, retrieving only the data that is necessary to fulfill the request as fast as possible. It's unlikely you'll write a more efficient for loop (though not impossible).

Upvotes: -2

Related Questions