LINQ:
如果我确定查询将返回单条记录,那么使用Single()
运算符优于First()
是否更有效?
有区别吗?
答案 0 :(得分:292)
我知道其他人已经写过为什么你使用其中一个,但我想我会说明为什么你不应该使用一个,当你意味着另一个时。
注意:在我的代码中,我通常会使用FirstOrDefault()
和SingleOrDefault()
,但这是一个不同的问题。
例如,使用复合键(Customers
,ID
)以不同语言存储Lang
的表:
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()
将帮助您避免出现逻辑错误。
答案 1 :(得分:63)
如果找到符合条件的多条记录,则会抛出异常。
首先将始终从列表中选择第一条记录。如果查询只返回1条记录,则可以使用First()
。
如果集合为空,两者都将抛出InvalidOperationException
异常。
或者,您可以使用SingleOrDefault()
。如果列表为空,则不会抛出异常
答案 2 :(得分:24)
<强>单()强>
返回查询的单个特定元素
使用时 :如果恰好有1个元素;不是0或大于1.如果列表为空或具有多个元素,则会抛出异常“序列包含多个元素”
<强>的SingleOrDefault()强>
返回查询的单个特定元素,如果未找到结果,则返回默认值
使用时 :当需要0或1个元素时。如果列表包含2个或更多项,则会抛出异常。
<强>首先()强>
返回具有多个结果的查询的第一个元素。
使用时 :当预期有1个或多个元素时,您只需要第一个元素。如果列表中没有元素,它将抛出异常。
<强> FirstOrDefault()强>
返回包含任意数量元素的列表的第一个元素,如果列表为空,则返回默认值。
使用时 :当需要多个元素且您只想要第一个元素时。或者列表为空,您需要指定类型的默认值,与
default(MyObjectType)
相同。例如:如果列表类型为list<int>
,则它将从列表中返回第一个数字,如果列表为空,则返回0。如果是list<string>
,它将从列表中返回第一个字符串,如果列表为空,则返回null。
答案 3 :(得分:17)
这两种方法之间存在细微的语义差异。
使用Single
从序列中检索第一个(也是唯一的)元素,该序列应该包含一个元素,而不是更多元素。如果序列具有多个on元素,则调用Single
将导致抛出异常,因为您指出应该只有一个元素。
使用First
从可包含任意数量元素的序列中检索第一个元素。如果序列具有多于on元素,则调用First
将不会导致抛出异常,因为您指示您只需要序列中的第一个元素而不关心是否存在更多元素。
如果序列不包含任何元素,则两个方法调用都将导致抛出异常,因为两个方法都期望至少存在一个元素。
答案 4 :(得分:15)
如果你没有特别想要在有多个项目的情况下抛出异常,使用First()
。
两者都很有效率,拿第一项。 First()
效率稍高,因为它不会检查是否有第二项。
唯一的区别是Single()
期望枚举中只有一个项目开始,并且如果有多个项目将抛出异常。在这种情况下,如果您特别想要抛出异常,使用 .Single()
答案 5 :(得分:14)
如果我记得,Single()检查第一个元素之后是否有另一个元素(如果是这种情况则抛出异常),而First()在获取之后停止。如果序列为空,则抛出异常。
个人而言,我总是使用First()。
答案 6 :(得分:7)
关于绩效:一位同事和我正在讨论Single vs First(或SingleOrDefault vs FirstOrDefault)的表现,我正在争论First(或FirstOrDefault)会更快并提高性能(我是关于让我们的应用程序运行得更快。)
我已经阅读了有关Stack Overflow的几篇文章。有人说使用First而不是Single会有很小的性能提升。这是因为First只返回第一项,而Single必须扫描所有结果以确保没有重复(即:如果它在表的第一行中找到该项,它仍然会扫描每隔一行确保没有第二个值匹配条件然后会抛出错误)。我觉得自己处于坚实的基础上,“第一”比“单身”更快,所以我开始证明这一点并让辩论得以休息。
我在我的数据库中设置了一个测试并添加了1,000,000行 ID UniqueIdentifier 外国UniqueIdentifier 信息nvarchar(50)(填充数字字符串“0”到“999,9999”
我加载了数据并将ID设置为主键字段。
使用LinqPad,我的目标是表明如果你使用Single搜索'Foreign'或'Info'的值,那将比使用First更糟糕。
我无法解释我得到的结果。几乎在所有情况下,使用Single或SingleOrDefault稍微快一些。这对我来说没有任何合理意义,但我想分享一下。
Ex:我使用了以下查询:
var q = TestTables.First(x=>x.Info == "314638") ;
//Vs.
Var q = TestTables.Single(x=>x.Info =="314638") ; //(this was slightly faster to my surprise)
我在“外部”关键字段上尝试了类似的查询,这个字段没有编入索引,认为First会更快,但Single在我的测试中总是稍快一些。
答案 7 :(得分:5)
他们是不同的。它们都断言结果集不为空,但是单个也断言结果不超过1。我个人使用Single,我只希望得到1个结果只是因为得到多于1个结果是一个错误,可能应该这样对待。
答案 8 :(得分:4)
您可以尝试简单的示例来获得差异。 第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);
答案 9 :(得分:3)
我认识的很多人都使用FirstOrDefault(),但我更倾向于使用SingleOrDefault(),因为如果有多个人,通常会出现某种数据不一致的情况。不过,这是在处理LINQ-to-Objects。
答案 10 :(得分:-1)
Lets below are the records in Employee entity
Employeeid = 1: Only one employee with this ID
Firstname = Robert: More than one employee with this name
Employeeid = 10: No employee with this ID
Now understand what Single() and First() in detail
**Single()**
Single() is use to return single record which exists only one in a table, so below query will return Employee whose employee id =1 because we have Employee is one whose Employee id is 1. If we have two records for EmployeeId = 1 then it give the error, kindly see error below in second query where we are taking example of name.
Employee.Single(e => e.Employeeid == 1)
Above will return single record, which have 1 employeeId
Employee.Single(e => e.Firstname == "Robert")
Above will give exception because multilple records are in table for FirstName='Robert'
And exception will be
InvalidOperationException: Sequence contains more than one element
Employee.Single(e => e.Employeeid == 10)
Above again will throw exception because no record exists for id=10 and exception will be
InvalidOperationException: Sequence contains no elements.
For EmployeeId = 10 it will return null but we are using only Single() then it will give error to handle null error we should use SingleOrDefault().
**First()**
First() return from multiple same records according to order as we using ascending order on birthdate so it will return 'Rober' who is oldest.
Employee.OrderBy(e => e. Birthdate)
.First(e => e.Firstname == "Robert")
Above should return the oldest one robert as per DOB
Employee.OrderBy(e => e. Birthdate)
.First(e => e.Employeeid == 10)
Above will give the exception as no record for id =10
To avoid null exception we should use FirstOrDefault() not only First().
Note: We can use only First()/Single() when we damn sure that it must going to return not null value.
In both function **SingleOrDefault() OR FirstOrDefault()**
It will handled null exception, in case of no record found it will give null.