LINQ Single与First


Answers:


311

如果您希望获得一条记录,那么在代码中明确显示始终是一件好事。

我知道其他人已经写了为什么您使用其中一个的理由,但是我想我想说明为什么当您指的是另一个时不应该使用其中一个。

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

举个例子来说,该存储表Customers使用不同语言的组合键(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();

它将返回一个对象,并且由于您正在使用排序,因此它将是返回的最新记录。

使用Single()时感觉它应该明确,始终返回1条记录将有助于你避免逻辑错误。


76
Single方法和First方法都可以使用表达式参数进行过滤,因此没有必要使用Where函数。示例:客户customer = db.Customers.Single(c => c.ID == 5);
乔什·诺

6
@JoshNoe-我很好奇,两者之间有区别customers.Where(predicate).Single() customers.Single(predicate)吗?
drzaus13年

9
@drzaus-从逻辑上讲,不,它们都基于谓词过滤要返回的值。但是,我检查了反汇编,在我制作的简单案例中,Where(predicate).Single()还有三条额外的指令。因此,尽管我不是IL专家,但是似乎customers.Single(谓词)应该更有效。
2013年

5
@JoshNoe事实与之相反。
AgentFire 2014年


72

如果发现多个符合条件的记录,则Single将引发异常。First将始终从列表中选择第一条记录。如果查询仅返回1条记录,则可以使用First()

InvalidOperationException如果集合为空,则两者都将引发异常。或者,您可以使用SingleOrDefault()。如果列表为空,则不会抛出异常


29

单()

返回查询的单个特定元素

使用时:如果期望精确地为1个元素,则为0。不为0或大于1。如果列表为空或具有多个元素,则将引发异常“序列包含多个元素”

SingleOrDefault()

返回查询的单个特定元素,如果找不到结果,则返回默认值

当使用时:当期望0或1个元素时。如果列表包含2个或更多项,则将引发异常。

第一()

返回具有多个结果的查询的第一个元素。

当使用时:当期望1个或多个元素并且您只想要第一个时。如果列表不包含任何元素,它将引发异常。

FirstOrDefault()

返回具有任意数量元素的列表的第一个元素,如果列表为空,则返回默认值。

当使用时:当需要多个元素并且只需要第一个元素时。或列表为空,并且您想要指定类型的默认值,与相同default(MyObjectType)。例如:如果列表类型是list<int>,它将返回列表的第一个数字;如果列表为空,则返回0。如果为list<string>,它将返回列表中的第一个字符串;如果列表为空,则返回null。


1
很好的解释。我只更改,您可以使用First,预计1个以上的元素,不仅是“超过1”,并FirstOrDefault与元素的任何量。
安德鲁

18

这两种方法之间存在细微的语义差异。

用于Single从应仅包含一个元素且不再包含其他元素的序列中检索第一个(也是唯一的)元素。如果序列中的元素多于on元素,则您的调用Single将引发异常,因为您指示仅应有一个元素。

用于First从可以包含任意数量元素的序列中检索第一个元素。如果序列中的元素多于on元素,则您的调用First不会导致引发异常,因为您指示仅需要序列中的第一个元素,并且不关心是否存在更多元素。

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


17

如果您不希望在有多个项目的情况下引发异常,请使用First()

两者都是有效的,把第一项。 First()效率更高一点,因为它不会打扰检查是否还有第二个项目。

唯一的区别是,Single()期望枚举中只有一个项目开始,如果有多个项目,则将引发异常。如果您特别想在这种情况下引发异常可以使用.Single()


14

如果我还记得,Single()检查第一个元素之后是否还有另一个元素(如果是,则抛出异常),而First()在获取它之后停止。如果序列为空,则两者都引发异常。

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


2
在SQL中,如果我没有记错的话,它们将产生First()进行TOP 1,而Single()进行TOP 2。
Matthijs Wessels

10

关于性能:我和一位同事正在讨论Single与First(或SingleOrDefault与FirstOrDefault)的性能,我在争辩说First(或FirstOrDefault)会更快并提高性能(我的全部目的是制作应用程序)跑得更快)。

我已经阅读了有关Stack Overflow的几篇文章,对此进行了辩论。有人说使用First而不是Single可以带来较小的性能提升。这是因为First将仅返回第一项,而Single必须扫描所有结果以确保没有重复项(即:如果它在表的第一行中找到了该项,则它仍将每隔一行扫描到确保没有匹配条件的第二个值,然后会抛出错误)。我觉得自己在“ First”比“ Single”要快的基础上扎根,所以我着手证明这一点,并搁置了辩论。

我在数据库中设置了一个测试,并添加了1,000,000行ID UniqueIdentifier外部UniqueIdentifier信息nvarchar(50)(由数字字符串“ 0”填充为“ 999,9999”

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

我的目标是使用LinqPad显示,如果您使用Single搜索“ Foreign”或“ Info”上的值,那将比使用First差得多。

我无法解释我得到的结果。在几乎每种情况下,使用Single或SingleOrDefault都会稍微快一些。这对我来说没有任何逻辑意义,但我想分享一下。

例如:我使用以下查询:

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在我的测试中总是稍快。


2
如果位于索引字段上,则数据库无需进行扫描以确保其唯一性。它已经知道了。因此,那里没有开销,而服务器端的唯一开销是确保仅返回一条记录。自己进行性能测试并不是一种决定性的方式
mirhagk 2015年

1
如果您在IComparer未使用的字段上进行搜索,我认为这些结果在复杂对象上不会是相同的。
安东尼·尼科尔斯

那应该是另一个问题。确保包括测试源。
Sinatr

5

它们不一样。他们两个都断言结果集不为空,但是单人也断言结果不超过1个。我个人仅在希望获得1个结果的情况下使用Single,因为仅返回1个以上的结果是一个错误,可能应该这样处理。


5

您可以尝试简单的示例以获取差异。第三行将引发异常;

        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);

3

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


-1

员工实体中的记录:

Employeeid = 1:只有一名具有此ID的员工

Firstname = Robert:不止一位具有此名称的员工

Employeeid = 10:没有具有此ID的员工

现在有必要了解Single()First()详细的意思。

单()

Single()用于返回表中唯一存在的一条记录,因此下面的查询将返回Employee,employeed =1因为我们只有一个Employee,其Employeed值为1。如果我们有两个记录,EmployeeId = 1则会抛出错误(请参见下面的错误在第二个查询中,其中我们使用的示例Firstname

Employee.Single(e => e.Employeeid == 1)

上面将返回一个记录,其中包含1 employeeId

Employee.Single(e => e.Firstname == "Robert")

上面将引发异常,因为的表中有多个记录FirstName='Robert'。例外是

InvalidOperationException:序列包含多个元素

Employee.Single(e => e.Employeeid == 10)

这将再次引发异常,因为不存在id = 10的记录。例外是

InvalidOperationException:序列不包含任何元素。

因为EmployeeId = 10它将返回null,但是在我们使用Single()它时将引发错误。为了处理空错误,我们应该使用SingleOrDefault()

第一()

First()从多个记录返回相应的记录,这些记录按照升序排序,birthdate因此它将返回最早的'Robert'。

Employee.OrderBy(e => e. Birthdate)
.First(e => e.Firstname == "Robert")

上面应该返回最早的一个,按照DOB的Robert。

Employee.OrderBy(e => e. Birthdate)
.First(e => e.Employeeid == 10)

上面将抛出异常,因为不存在id = 10的记录。为了避免null异常,我们应该使用FirstOrDefault()而不是First()

注意:当我们绝对确定它不能返回空值时,我们只能使用First()/ Single()

在这两个函数中,都使用SingleOrDefault()或FirstOrDefault()来处理null异常,如果找不到记录,它将返回null。


请解释您的答案。
MrMaavin

1
@MrMaavin我已经更新了,请告诉我现在对您来说可以理解吗?
治安官
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.