考虑IEnumerable扩展方法SingleOrDefault()和FirstOrDefault()

MSDN文档SingleOrDefault:

返回序列中唯一的元素,如果序列为空则返回默认值;如果序列中有多个元素,此方法将引发异常。

而来自MSDN的FirstOrDefault(假设在使用OrderBy()或orderbydescent()或根本不使用时),

返回序列的第一个元素

考虑一些示例查询,并不总是清楚何时使用这两个方法:

var someCust = db.Customers
.SingleOrDefault(c=>c.ID == 5); //unlikely(?) to be more than one, but technically COULD BE

var bobbyCust = db.Customers
.FirstOrDefault(c=>c.FirstName == "Bobby"); //clearly could be one or many, so use First?

var latestCust = db.Customers
.OrderByDescending(x=> x.CreatedOn)
.FirstOrDefault();//Single or First, or does it matter?

问题

当你决定在LINQ查询中使用SingleOrDefault()和FirstOrDefault()时,你遵循或建议什么约定?


当前回答

我使用SingleOrDefault的情况下,我的逻辑将是零或一个结果。如果有更多,这是一个错误的情况,这是有帮助的。

其他回答

在上一个例子中:

var latestCust = db.Customers
.OrderByDescending(x=> x.CreatedOn)
.FirstOrDefault();//Single or First, or doesn't matter?

是的。如果您尝试使用SingleOrDefault()并且查询结果超过记录,您将得到一个异常。你唯一可以安全使用SingleOrDefault()的时候是当你只期望1和只有1个结果…

我使用SingleOrDefault的情况下,我的逻辑将是零或一个结果。如果有更多,这是一个错误的情况,这是有帮助的。

我不明白为什么你要使用FirstOrDefault(x=> x.ID == key),当你使用Find(key)可以更快地检索结果时。如果使用表的Primary键进行查询,经验法则是始终使用Find(key)。FirstOrDefault应该用于谓词,如(x=> x. username == username)等。

这个问题的标题并不专门针对DB上的linq或linq到List/IEnumerable等,所以不应该被否决。

对于LINQ -> SQL:

SingleOrDefault

将生成查询,如“select TOP 2 * from users where userid = 1” 选择匹配的记录,如果发现多个记录则抛出异常 如果基于主键/唯一键列获取数据,则使用

FirstOrDefault

将生成类似于“select userid = 1的用户中的top 1 *”的查询 选择第一个匹配的行 如果基于非主键/唯一键列获取数据,则使用

语义上的差异 性能差异

两者之间。

语义的区别:

FirstOrDefault返回可能有多个的第一项(如果不存在则返回default)。 SingleOrDefault假设有一个单项并返回它(如果不存在则返回default)。多个项目违反合同,抛出异常。

性能差异

FirstOrDefault通常更快,它迭代直到找到元素,只有在找不到元素时才迭代整个枚举对象。在许多情况下,有很大的可能性找到一个项目。 SingleOrDefault需要检查是否只有一个元素,因此总是迭代整个枚举对象。准确地说,它迭代直到找到第二个元素并抛出异常。但在大多数情况下,没有第二个元素。

结论

如果你不关心有多少项,或者当你无法负担检查唯一性的费用时(例如在一个非常大的集合中),请使用FirstOrDefault。当您在将项添加到集合时检查唯一性时,在搜索这些项时再次检查它可能代价太大。 如果您不需要太关心性能,并且希望确保单个项的假设对读者来说是清楚的,并在运行时进行检查,则使用SingleOrDefault。

在实践中,即使在假设单个项的情况下,也经常使用First / FirstOrDefault来提高性能。您仍然应该记住,Single / SingleOrDefault可以提高可读性(因为它声明了单个项的假设)和稳定性(因为它检查了它),并适当地使用它。