使用 foreach 循环和 LINQ 的操作有什么区别?



我正在解决一个问题,我知道这些代码以不同的方式工作,但我无法理解为什么第二个不正确以及有什么区别。

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;
}
  • LINQ 针对可读性进行了优化;它易于编写、读取和理解
  • 但它并不总是那么高效;它不能利用自定义迭代器之类的东西,并且可能涉及更多对象。
  • 外观简单的操作(例如OrderBy)可能非常昂贵
  • 但是...在大多数情况下,这并不重要,可读性是赢家
  • 但是...在某些情况下,这确实很重要:)

如果我们假设这是内存中的数据(LINQ-Objects) - 类似于IEnumerable<Person>List<Person>Person[]等:

请注意,排序是一项相对昂贵的操作,在使用 LINQ 时,这通常也意味着创建数据的副本(以免更改源)。有一些外部扩展方法可以在 LINQ 概念中更有效地执行此操作,即

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

同样,效率不如循环,但比OrderByDescending+FirstOrDefault效率高得多......只需注意这对于空输入的行为(它可能会抛出而不是返回null)。


然而,正如Dzyann在评论中观察到的那样:这里的people可能是一个IQueryable<Person>- 类似于EF或LINQ-to-SQL(等)的DbSet<Person>,在这种情况下,一切都会改变:现在我们谈论的是被推送到外部资源的查询,在这种情况下,OrderByDescending+FirstOrDefault可以变成SQL,如下所示:

SELECT TOP 1 *
FROM People
ORDER BY Age DESC

我们成了英雄。如果我们通过IQueryable<Person>foreach做到这一点,我们将发布:

SELECT *
FROM People

然后在我们在本地迭代时通过网络获取所有内容,以查看哪个是最古老的。

关于 LINQ 查询的一个不太明显的问题是它们往往会"快速失败"。也就是说,一旦满足指定的条件,它们就会退出循环,而不是遍历整个序列。

例如,FirstOrDefault()从序列中获取第一项,如果序列中有任何项,则立即退出。如果没有,它会立即返回 null。它不会循环访问序列。

但是,for循环会遍历序列中的每个元素。如果序列非常大,这可能很耗时。此外,如果退出条件错误或完全缺失,则可能会返回错误的元素(尽管在 LINQ 表达式中也可能发生这种情况)。

LINQ 表达式非常高效,仅检索尽快完成请求所需的数据。你不太可能编写更有效的for循环(尽管并非不可能)。

最新更新