LINQ单对第一

LINQ:

当我确定查询将返回单张唱片时,使用Single()操作符是否比First()更有效?

有区别吗?

114928 次浏览

如果我回忆,Single()检查在第一个元素之后是否有另一个元素(如果是这样,则抛出异常),而first()在得到它之后停止。如果序列为空,两者都会抛出异常。

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

Single如果发现符合条件的记录超过一条,将抛出异常。 First将始终从列表中选择第一条记录。如果查询只返回一条记录,你可以使用First()。< / p > 如果集合为空,两者都会抛出InvalidOperationException异常。 或者你也可以使用SingleOrDefault()。如果列表为空

,则不会抛出异常

如果您不特别希望在有多个项的事件中抛出异常,使用First()

两者都很有效率,就拿第一项来说。First()稍微更有效,因为它不需要检查是否有第二个项。

唯一的区别是Single()期望开始时枚举中只有一个项,如果有多个项则会抛出异常。在这种情况下你使用 .Single() 如果您特别希望抛出异常

他们是不同的。它们都断言结果集不是空的,但single也断言结果集不超过1个。我个人在只希望有1个结果的情况下使用Single,因为返回超过1个结果是一个错误,可能应该这样对待。

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

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

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

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

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

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

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

例如,一个表使用复合键(IDLang)在不同语言中存储Customers:

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条记录时,使用它将帮助你避免逻辑错误。

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

你可以尝试简单的例子来得到不同。 在第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);

单()

返回查询的单个特定元素

When Use:如果恰好需要1个元素;不是0或大于1。如果列表为空或包含多个元素,则会抛出异常“序列包含多个元素”。

SingleOrDefault ()

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

When Use:当需要0或1个元素时。如果列表有2个或更多项,它将抛出异常。

第()

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

When Use:当期望有1个或多个元素,而您只需要第一个元素时。如果列表中不包含元素,它将抛出异常。

FirstOrDefault ()

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

When Use:当需要多个元素而你只想要第一个元素时。或者列表为空,你想要指定类型的默认值,就像default(MyObjectType)一样。例如:如果列表类型是list<int>,它将返回列表中的第一个数字,如果列表为空则返回0。如果它是list<string>,它将返回列表中的第一个字符串,如果列表为空则返回null。

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

我在Stack Overflow上读过几篇讨论这个问题的文章。有人说使用First而不是Single会有小的性能提升。这是因为First只会返回第一项,而Single则必须扫描所有结果以确保没有重复的结果(即:如果它在表的第一行中找到了该项,它仍然会扫描每一行以确保没有第二个值与条件匹配,从而抛出错误)。我觉得“First”比“Single”快是有根据的,所以我开始证明这一点,把争论平息下来。

我在我的数据库中设置了一个测试,并添加了1,000,000行 ID UniqueIdentifier 外国UniqueIdentifier Info 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)

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

员工实体中的记录:

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

Firstname = Robert:具有此名称的多个员工

Employeeid = 10:没有这个ID的员工

现在有必要详细理解Single()First()的含义。

单()

Single()用于返回表中唯一存在的单个记录,因此下面的查询将返回employeed =1的Employee,因为我们只有一个Employeed为1的Employee。如果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。

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

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

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

在两个函数中都使用SingleOrDefault() OR FirstOrDefault()来处理 一个空异常,在没有找到记录的情况下,它将返回null