2024-11-09 05:00:06

LINQ单对第一

LINQ:

当我确定查询将返回一条记录时,使用Single()操作符比First()更有效吗?

有区别吗?


当前回答

这两种方法在语义上有细微的差别。

使用Single从一个序列中检索第一个(也是唯一一个)元素,该序列应该只包含一个元素。如果序列中有多个元素,则调用Single将导致抛出异常,因为您指示了应该只有一个元素。

使用First从可以包含任意数量元素的序列中检索第一个元素。如果序列中有多个元素,则调用First不会引发异常,因为您表示只需要序列中的第一个元素,而不关心是否存在更多元素。

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

其他回答

如果发现匹配条件的记录超过一条,Single将抛出异常。 First将始终从列表中选择第一条记录。如果查询只返回一条记录,您可以使用First()。

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

如果你期望一个单一的记录,在你的代码中显式总是好的。

我知道其他人已经写了为什么你要使用其中一个或另一个,但我认为我应该说明为什么你不应该使用一个,当你指的是另一个。

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

以一个表为例,它使用一个组合键(ID, Lang)存储不同语言的客户:

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

上面的代码引入了一个可能的逻辑错误(难以跟踪)。它将返回多个记录(假设您有多种语言的客户记录),但它总是只返回第一个记录……有时候可能有用……但其他人不是。它是不可预测的。

由于您的意图是返回一个单一客户使用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条记录时,使用它将帮助您避免逻辑错误。

单()

返回查询的单个特定元素 When Use:如果恰好需要1个元素;不是0或大于1。如果列表为空或包含多个元素,则会抛出异常"Sequence contains more than one element"

SingleOrDefault ()

返回查询的单个特定元素,如果没有找到结果,则返回默认值 当使用:当需要0或1个元素时。如果列表有2个或更多项,它将抛出异常。

第()

返回带有多个结果的查询的第一个元素。 When Use:当需要1个或多个元素,而您只需要第一个元素时。如果列表中不包含元素,它将抛出异常。

FirstOrDefault ()

Returns the first element of a list with any amount of elements, or a default value if the list is empty. When Use: When multiple elements are expected and you want only the first. Or the list is empty and you want a default value for the specified type, the same as default(MyObjectType). For example: if the list type is list<int> it will return the first number from the list or 0 if the list is empty. If it is list<string>, it will return the first string from the list or null if the list is empty.

这两种方法在语义上有细微的差别。

使用Single从一个序列中检索第一个(也是唯一一个)元素,该序列应该只包含一个元素。如果序列中有多个元素,则调用Single将导致抛出异常,因为您指示了应该只有一个元素。

使用First从可以包含任意数量元素的序列中检索第一个元素。如果序列中有多个元素,则调用First不会引发异常,因为您表示只需要序列中的第一个元素,而不关心是否存在更多元素。

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

关于性能:我和一个同事正在讨论Single vs First(或SingleOrDefault vs FirstOrDefault)的性能,我认为First(或FirstOrDefault)会更快并提高性能(我都是关于让我们的应用程序运行得更快)。

I’ve read several posts on Stack Overflow that debate this. Some say there are small performance gains using First instead of Single. This is because First would simply return the first item while Single must scan all the results to make sure there isn’t a duplicate (ie: if it found the item in the first row of the table, it still would scan every other row to make sure there isn’t a second value matching the condition which would then throw an error). I felt like I was on solid ground with “First” being faster than “Single” so I set out to prove it and put the debate to rest.

我在我的数据库中设置了一个测试,并添加了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)

我在“外”键字段上尝试了类似的查询,没有索引,认为这将证明第一是更快的,但在我的测试中,单一总是略快。