我正在解决一个问题,我知道这些代码以不同的方式工作,但我无法理解为什么第二个不正确以及有什么区别。
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
循环(尽管并非不可能)。