LINQ Single与First的区别

252
LINQ:
当我确定查询只会返回一个记录时,使用 Single() 操作符比 First() 操作符更有效率吗?
这两个操作符有什么区别吗?
11个回答

353

如果你期望只获取一条记录,那在你的代码中明确表达这个意图总是一个好习惯。

我知道其他人已经写过为什么会使用其中之一,但我认为我可以说明一下当你想要使用其中一个时为什么不应该使用另一个。

注意:在我的代码中,我通常会使用FirstOrDefault()SingleOrDefault(),但那是另一个问题。

例如,考虑一个使用复合键(IDLang)存储不同语言客户的表:

DBContext db = new DBContext();
Customer customer = db.Customers.Where( c=> c.ID == 5 ).First();

以上代码可能存在一个潜在的逻辑错误(难以追踪)。如果您有多种语言的客户记录,它将返回多个记录,但始终仅返回第一个记录... 这可能有时有效... 但有时无效。它是不可预测的。

由于您的意图是返回单个 Customer,请使用 Single()

以下代码将抛出异常(在此情况下这正是您想要的):

DBContext db = new DBContext();
Customer customer = db.Customers.Where( c=> c.ID == 5 ).Single();

然后,你只需要轻轻地拍打自己的额头并对自己说... 呜呼!我忘记了语言字段!以下是正确的版本:

DBContext db = new DBContext();
Customer customer = db.Customers.Where( c=> c.ID == 5 && c.Lang == "en" ).Single();

First()在以下情况下非常有用:

DBContext db = new DBContext();
NewsItem newsitem = db.NewsItems.OrderByDescending( n => n.AddedDate ).First();

它会返回一个对象,由于您使用了排序,因此将返回最近的记录。

当您需要显式地始终返回1条记录时,使用Single()可以帮助避免逻辑错误。


91
Single 和 First 方法都可以接受一个表达式参数来进行过滤,因此 Where 函数是不必要的。例如:Customer customer = db.Customers.Single(c => c.ID == 5); - Josh Noe
6
两者都是在顾客集合中查找符合条件的单个顾客对象,但前者使用了 LINQ 查询语句,而后者直接调用了 Single 方法并传递了谓词参数。两者的结果是一样的,只是实现方式不同。 - drzaus
9
从逻辑上讲,它们都基于谓词过滤要返回的值。然而,我检查了反汇编代码,在我做的简单情况下,Where(predicate).Single()有三个额外的指令。因此,虽然我不是IL专家,但似乎customers.Single(predicate)应该更有效率。 - Josh Noe
6
@JoshNoe,事实证明相反。 - AgentFire
11
为了支持你的说法,请参考此链接:https://dev59.com/PGEi5IYBdhLWcg3whcri - M. Mimpen
显示剩余8条评论

86

如果Single方法找到多于一个匹配标准的记录,它将抛出异常。 First方法总是从列表中选择第一条记录。如果查询返回的只有一条记录,您可以使用 First()

如果集合为空,Both方法将抛出InvalidOperationException异常。 或者,您可以使用SingleOrDefault()。如果列表为空,它不会抛出异常。


5
如果查询只返回1条记录,您可以使用First()。为什么不使用Single()更好地传达您的意图呢?此外,如果我们期望恰好获得一个项目,但某种神奇的方式确实获得了多个项目,难道我们不想知道吗?否则,我们的程序将在可能已经被损坏的状态下运行。 - user2051770

43

Single()

返回查询结果中的单个元素。

使用场景: 当期望只有一个元素时,而不是0个或多个元素。如果列表为空或包含多个元素,则会抛出异常“序列包含多个元素”。

SingleOrDefault()

返回查询结果中的单个元素,如果没有结果则返回默认值。

使用场景: 当期望0个或1个元素时。如果列表包含2个或更多项,则会抛出异常。

First()

返回查询结果中的第一个元素。

使用场景: 当期望有1个或多个元素时,但只需要第一个元素时。如果列表不包含元素,则会抛出异常。

FirstOrDefault()

返回列表中的第一个元素,如果列表为空则返回默认值。

使用场景: 当期望有多个元素时,但只需要第一个元素时。或者当列表为空时,需要指定类型的默认值,与 default(MyObjectType) 相同。例如:如果列表类型是 list<int>,则返回列表中的第一个数字,如果列表为空,则返回0。如果是 list<string>,则返回列表中的第一个字符串,如果列表为空,则返回null。


2
很好的解释。我只想更改一下,当期望1个或多个元素时,可以使用First,而不仅仅是“多于1个”,而FirstOrDefault则适用于任意数量的元素。 - Andrew

20

若不需要在有多个项的情况下抛出异常,请使用 First()

二者都是高效的,都取第一项。因为 First() 不会检查是否有第二项,所以它略微更加高效。

唯一的区别是,Single() 假设枚举中只有一个项,并在存在多个项时抛出异常。在这种情况下,您可以使用.Single()指定抛出异常。


18

这两种方法之间有微妙的语义差别。

使用 Single 从只包含一个元素的序列中检索第一个(且唯一)元素。如果序列有多个元素,则会抛出异常,因为你指定了应该只有一个元素。

使用 First 从可以包含任意数量元素的序列中检索第一个元素。如果序列有多个元素,则不会抛出异常,因为你指定了只需要序列中的第一个元素,而不关心是否存在更多元素。

如果序列不包含任何元素,则这两种方法调用都会引发异常,因为两种方法都期望至少有一个元素存在。


16

如果我没记错的话,Single()会检查第一个元素后是否还有其他元素(如果是,则抛出异常),而First()则在获取第一个元素后停止。如果序列为空,两者都会抛出异常。

个人而言,我总是使用First()。


3
在 SQL 中,它们产生的 First() 相当于 TOP 1,而 Single() 相当于 TOP 2(如果我没记错的话)。 - Matthijs Wessels

15

关于性能:我和一位同事正在讨论 Single vs First(或者SingleOrDefault vs FirstOrDefault)的性能, 我辩称使用 First (或者FirstOrDefault)可以更快、提高性能 (因为我非常注重让我们的应用程序运行更快)。

我在 Stack Overflow 上读了几篇有争议的帖子。有些人说使用 First 代替 Single 可以获得轻微的性能提升。这是因为 First 只会返回第一个项目,而 Single 必须扫描所有结果以确保没有重复项(例如:如果它在表的第一行找到该项,则仍将扫描每一行以确保没有第二个满足条件的值,否则将引发错误)。我认为使用 "First" 比 "Single" 更快,所以我开始尝试证明它,并结束这场辩论。

我在数据库中设置了一个测试,并添加了 1,000,000 行数据

ID UniqueIdentifier Foreign UniqueIdentifier Info nvarchar(50)(填充数字“0”到“999,9999”的字符串)

我加载了数据,并将 ID 设置为主键字段。

通过 LinqPad,我的目标是展示,如果你使用 Single 在 'Foreign' 或 'Info'上搜索值,那么它比使用 First 要慢得多。

我无法解释我得到的结果。几乎在所有情况下,使用 Single 或者SingleOrDefault 都比使用 First 稍微快一点。这对我来说毫无逻辑意义,但是我想分享一下。

例如:我使用了以下查询:

var q = TestTables.First(x=>x.Info == "314638") ;
//Vs.
Var q = TestTables.Single(x=>x.Info =="314638") ; //(this was slightly faster to my surprise)

我尝试对未被索引的“Foreign”键字段进行类似的查询,认为这样可以证明First更快,但在我的测试中Single始终略微更快。


3
如果数据存储在一个索引字段上,数据库就不需要扫描以确保它是唯一的,因为它已经知道了。所以这里没有额外的开销,在服务器端唯一的开销就是确保只有一个记录被返回。我自己进行了性能测试,但并没有得出确定的结论。 - mirhagk
1
如果您在一个未被IComparer使用的字段上进行搜索,我认为这些结果在复杂对象上可能不会相同。 - Anthony Nichols
这应该是另一个问题。请确保包含您的测试来源。 - Sinatr
如果是内存中的数据,由于缓存的原因可能会有性能差异。如果您先测试First,然后再测试Single,那么一切可能已经在缓存中,因此计算所需的时间更少。对于转换为SQL的查询,请注意First将转换为SELECT TOP 1,而Single将转换为SELECT TOP 2,以验证是否只找到一个。这可能会对查询规划器决定查询计划以及复杂查询产生微妙的影响。我曾经看到过这种情况,其中一个查询需要几秒钟,而另一个查询则需要很长时间。 - Dave Van den Eynde

5
你可以尝试简单的示例来了解差异。第3行将抛出异常;
        List<int> records = new List<int>{1,1,3,4,5,6};
        var record = records.First(x => x == 1);
        record = records.Single(x => x == 1);

5

它们是不同的。两者都断言结果集不为空,但Single还断言结果不超过1个。在我看来,当我只期望有一个结果时,我会使用Single,因为返回多个结果是错误的,可能应该被视为这样。


3

我认识很多人都使用FirstOrDefault(),但我更倾向于使用SingleOrDefault(),因为如果存在多个元素,则通常会出现某种数据不一致性。这是处理LINQ-to-Objects的情况。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接