LINQ:何时使用具有筛选条件的SingleOrDefault与FirstOrDefault()


505

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

MSDN文件指出SingleOrDefault

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

FirstOrDefault从MSDN(推测当使用OrderBy()OrderByDescending()在所有或没有),

返回序列的第一个元素

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

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?

在决定使用SingleOrDefault()FirstOrDefault()在LINQ查询中使用或遵循哪些约定

Answers:


465

无论何时使用SingleOrDefault,都应明确指出查询最多只能产生一个结果。另一方面,FirstOrDefault使用时,查询可以返回任意数量的结果,但是您声明只希望第一个结果。

我个人发现语义非常不同,并且根据预期结果使用适当的语义可以提高可读性。


164
一个非常重要的区别是,如果在具有多个元素的序列上使用SingleOrDefault,则会引发异常。
Kamran Bigdely 2014年

17
@kami如果没有抛出异常,则与FirstOrDefault完全一样。唯一的例外是它使它成为SingleOrDefault。提出来的要点是,把差异的棺材钉上钉子。
Fabio S.

17
我必须说,从性能角度来看,使用9,000,000个元素的List <MyClass>,FirstOrDefault的运行速度比SingleOrDefault快10倍,class包含2个整数,而Func包含对这两个整数的搜索。在var v = list上循环搜索200次花费了22秒.SingleOrDefault(x => x.Id1 == i && x.Id2 == i); 和var v = list.FirstOrDefault(x => x.Id1 == i && x.Id2 == i); 大约3秒

6
@BitsandBytesHandyman如果SignleOrDefault在序列包含多个项目时未引发异常,则其行为将与FirstOrDefault完全不同。FirstOrDefault返回第一项;如果序列为空,则返回null。SingleOrDefault应该返回唯一的项目;如果序列为空,或者序列包含多个项目,则返回null,而不会引发任何异常。
Thanasis Ioannidis

2
@RSW是的,我知道这一点。仔细阅读我的评论,我说的是SingleOrDefault应该做什么,而不是它应该做什么。但是,当然,它应该做的是非常主观的。对我而言,“ SomethingOrDefault”模式的意思是:获取“ Something”的值。如果“某事”未能返回值,请返回默认值。这意味着即使在“某事”将引发异常的情况下也应返回默认值。因此,在我看来,Single将引发异常的地方,SingleOrDefault应该返回默认值。
Thanasis Ioannidis

585

如果您的结果集返回0条记录:

  • SingleOrDefault 返回类型的默认值(例如,int的默认值为0)
  • FirstOrDefault 返回类型的默认值

如果结果集返回1条记录:

  • SingleOrDefault 返回该记录
  • FirstOrDefault 返回该记录

如果您的结果集返回许多记录:

  • SingleOrDefault 引发异常
  • FirstOrDefault 返回第一条记录

结论:

如果您希望在结果集包含许多记录时引发异常,请使用SingleOrDefault

如果无论结果集包含什么内容,您总是想要1条记录,请使用 FirstOrDefault


6
我建议实际上很少需要异常,因此在大多数情况下,首选FirstOrDefault。我知道案件并不经常出现。
MikeKulls'5

FirstOrDefault返回第一条记录意味着新记录(最后一条)/旧记录(第一条)?您能澄清一下吗?
2014年

@Duk,这取决于您对记录进行排序的方式。您可以在调用FirstOrDefault之前使用OrderBy()或OrderByDescending()等。请参阅OP的代码示例。

5
我也喜欢这个答案。尤其是在某些情况下,您实际上希望引发异常是因为您打算在其他地方正确处理这种罕见的情况,而不是假装不会发生。当您想要例外时,您要清楚地说出这句话,并迫使其他人来处理,以使整个系统更加强大。
弗朗西斯·罗杰斯

它非常清楚地说明了这一点,以便人们可以轻松理解。
尼拉夫·瓦索亚

243

  • 语义上的差异
  • 性能差异

两者之间。

语义差异:

  • FirstOrDefault 返回可能多个的第一项(如果不存在则返回默认值)。
  • SingleOrDefault假设有一个项目并返回(或默认值,如果不存在)。多个项目违反合同,将引发异常。

性能差异

  • FirstOrDefault通常会更快,它会迭代直到找到元素为止,并且只在没有找到元素时才对整个可枚举进行迭代。在许多情况下,找到项目的可能性很高。

  • SingleOrDefault需要检查是否只有一个元素,因此总是迭代整个可枚举。确切地说,它会进行迭代,直到找到第二个元素并引发异常为止。但是在大多数情况下,没有第二个要素。

结论

  • 使用FirstOrDefault,如果你不关心有多少项目有当你不能检查的独特性(例如,在一个非常大的集合)。当您检查将项目添加到集合中的唯一性时,在搜索这些项目时再次进行检查可能会太昂贵。

  • 使用SingleOrDefault如果你没有在乎太多性能,并希望确保单个项目的前提是明确的读者,并在运行时检查。

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


16
+1 “或者在您负担不起检查唯一性的情况下(例如,在非常大的收藏集中)。” 。我一直在寻找。我还将在插入时或/和/或通过设计而不是在进行查询添加强制唯一性
纳瓦兹

我可以想象SingleOrDefault使用Linq to Objects时会迭代很多对象,但是SingleOrDefault例如,如果Linq与数据库对话,则不必迭代最多两个项目吗?只是想知道..
Memet Olsen

3
@memetolsen考虑使用LINQ to SQL两者的代码吐痰-FirstOrDefault使用前1名。SingleOrDefault使用前2名。–
Jim Wooley

@JimWooley我想我误解了“难以计数”这个词。我以为Stefan是指C#Enumerable
Memet Olsen 2013年

1
@memetolsen在原始答案方面是正确的,您的评论是指向数据库的,所以我提供了提供者的信息。尽管.Net代码仅迭代2个值,但数据库访问所需数量的记录,直到命中满足条件的第二个记录为止。
Jim Wooley 2013年

76

没有人提到用SQL转换的FirstOrDefault做TOP 1记录,而SingleOrDefault做TOP 2记录,因为它需要知道是否有多于1条记录。


3
当我跑到通过LinqPad和VS一个的SingleOrDefault,我从来没有SELECT TOP 2,用FirstOrDefault我能得到SELECT TOP 1,但据我可以告诉你没有得到SELECT TOP 2
杰米- [R Rytlewski

嘿,我也曾在linqpad中尝试过,sql查询让我感到害怕,因为它完全获取了所有行。我不确定怎么会这样?
AnyOne 2012年

1
这完全取决于所使用的LINQ提供程序。例如,LINQ to SQL和LINQ to Entities可以通过不同的方式转换为SQL。我刚刚使用IQ MySql提供程序尝试了LINQPad,并在FirstOrDefault()添加LIMIT 0,1时不SingleOrDefault()添加任何内容。
卢卡斯

1
EF Core 2.1将FirstOrDefault转换为SELECT TOP(1),SingleOrDefault转换为SELECT TOP(2)
camainc

19

对于LINQ-> SQL:

单一或默认

  • 将生成查询,例如“从userid = 1的用户中选择*”
  • 选择匹配的记录,如果找到多个记录,则引发异常
  • 如果您要基于主键/唯一键列获取数据,请使用

第一或默认

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

我认为您应该从SingleOrDefault中删除“选择所有匹配的行”
Saim Abdullah,

10

SingleOrDefault在逻辑上要求将结果为零或一个的情况下使用。如果还有更多,那就是错误情况,这很有帮助。


3
我经常发现SingleOrDefault()会突出显示以下情况:我没有对结果集应用正确的过滤,或者底层数据重复出现问题。我经常发现自己在First()方法上使用Single()和SingleOrDefault()。
TimS

如果您有大量枚举,则LINQ to Objects上的Single()和SingleOrDefault()会对性能产生影响,但是与数据库(例如SQL Server)通信时,它将进行前2个调用,并且如果您正确设置了索引,调用不应该很昂贵,我宁愿快速失败并查找数据问题,而不是通过在调用First()或FirstOrDefault()时进行错误的重复来引入其他数据问题。
heartlandcoder

5

SingleOrDefault:您说的是“至多”项,与查询或默认值匹配。FirstOrDefault:您说的是“至少”项,与查询或默认值匹配

下次大声说出来时,您需要选择,您可能会明智地选择。:)


5
实际上,没有结果是使用FirstOrDefault. More correctly: FirstOrDefault` 完全可以接受的,它可以使用任意数量的结果,但我只关心第一个结果,因此可能也没有结果。 SingleOrDefault=结果为1或0,如果有更多结果,则表明某个地方有错误。 First=至少有一个结果,我想要。 Single=仅有1个结果,不多也不少,我想要那个。
Davy8 2011年

4

在您的情况下,我将使用以下内容:

按ID == 5进行选择:在这里使用SingleOrDefault是可以的,因为您期望一个[或不]实体,如果您拥有多个ID为5的实体,那肯定是有问题的,肯定值得例外。

当搜索名字等于“ Bobby”的人时,可以有多个(我可能会这么想),因此您既不应使用Single或First,而应使用Where运算进行选择(如果“ Bobby”返回太多)实体,则用户必须优化其搜索或选择返回的结果之一)

按创建日期排序还应该使用Where-操作执行(不太可能只有一个实体,排序没有太大用处;)但这意味着您希望对所有实体进行排序-如果您只想一个实体,请使用FirstOrDefault,如果您拥有多个实体,则每次都会抛出Single。


3
我不同意。如果您的数据库ID是主键,则该数据库已经在强制执行唯一性。浪费大量的CPU周期来检查数据库是否在每个查询中都完成工作是愚蠢的。
约翰·亨克尔

4

两者都是元素运算符,它们用于从序列中选择单个元素。但是它们之间有细微的差别。如果满足多个元素的条件,则FirstOrDefault()运算符将引发异常,因为FirstOrDefault()不会为此引发任何异常。这是例子。

List<int> items = new List<int>() {9,10,9};
//Returns the first element of a sequence after satisfied the condition more than one elements
int result1 = items.Where(item => item == 9).FirstOrDefault();
//Throw the exception after satisfied the condition more than one elements
int result3 = items.Where(item => item == 9).SingleOrDefault();

2
“它们之间有微小的区别”-太重要了!
Nikhil Vartak

3

在最后一个示例中:

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

是的,它确实。如果您尝试使用SingleOrDefault()并且查询的结果超过记录,那么您将得到和例外。唯一可以安全使用的时间SingleOrDefault()是期望只有1个结果且只有1个结果时...


没错 如果结果为0,也会出现异常。
Dennis Rongo

1

因此,据我所知,SingleOrDefault如果您要查询保证唯一的数据(即由数据库约束(例如主键)强制执行),那将是很好的。

还是有更好的查询主键的方法。

假设我的TableAcc有

AccountNumber - Primary Key, integer
AccountName
AccountOpenedDate
AccountIsActive
etc.

我想查询的AccountNumber 987654,我使用

var data = datacontext.TableAcc.FirstOrDefault(obj => obj.AccountNumber == 987654);

1

我认为FirstOrDefault这被滥用了很多。在大多数情况下,当您过滤数据时,您要么期望返回匹配逻辑条件的元素集合,要么期望通过其唯一标识符(例如,用户,书籍,帖子等)来获得单个唯一元素。为什么我们甚至可以说那FirstOrDefault()是代码的味道,不是因为它有什么问题,而是因为它使用得太频繁了。这篇博客文章详细探讨了该主题。在大多数情况下,IMO都是SingleOrDefault()更好的选择,因此请小心此错误,并确保您使用最能代表您的合同和期望的最适当方法。


-1

回应中遗漏的一件事...

如果有多个结果,则无需排序的FirstOrDefault可以根据服务器曾经使用过的索引策略来带回不同的结果。

我个人不能在代码中看到FirstOrDefault,因为对我而言,它说开发人员不在乎结果。但是,通过订购可以将其作为执行最新/最早的一种方式。我不得不纠正由于使用FirstOrDefault的粗心开发人员而导致的许多问题。


-2

我询问Google在GitHub上使用不同方法的情况。这是通过针对每种方法运行Google搜索查询并将查询限制为github.com域和.cs文件扩展名来完成的,方法是使用查询“ site:github.com file:cs ...”

似乎First *方法比Single *方法更常用。

| Method               | Results |
|----------------------|---------|
| FirstAsync           |     315 |
| SingleAsync          |     166 |
| FirstOrDefaultAsync  |     357 |
| SingleOrDefaultAsync |     237 |
| FirstOrDefault       |   17400 |
| SingleOrDefault      |    2950 |

-8

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

由于问题的标题不是特定于DB上的linq或不是针对List / IEnumerable的Linq等,因此这不应该被否决。


1
哪个名称空间Find()在?
p.campbell 2015年

你能告诉我们吗?仍在等待答案。
丹尼


单词“ IEnumerable”在问题正文的第一行。如果您只阅读标题而不是实际问题,并因此发布了错误的答案,那是您的错误也是对IMO否决的完全正当理由。
F1Krazy
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.