【问题标题】:LINQ Single vs FirstLINQ Single vs First
【发布时间】:2010-04-27 18:55:05
【问题描述】:

LINQ:

当我确定查询将返回单条记录时,使用Single() 运算符是否比First() 更有效?

有区别吗?

【问题讨论】:

    标签: .net linq


    【解决方案1】:

    如果您期望一条记录,最好在代码中明确说明。

    我知道其他人写过你为什么使用其中一个,但我想我会说明为什么你不应该使用一个,当你另一个时。

    注意:在我的代码中,我通常会使用 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();
    

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

    在您认为应该明确始终返回 1 条记录时使用 Single() 将帮助您避免逻辑错误。

    【讨论】:

    • Single 和 First 方法都可以使用表达式参数进行过滤,因此不需要 Where 函数。示例:客户 customer = db.Customers.Single( c=> c.ID == 5 );
    • @JoshNoe - 我很好奇,customers.Where(predicate).Single() customers.Single(predicate) 之间有区别吗?
    • @drzaus - 从逻辑上讲,不,它们都根据谓词过滤要返回的值。但是,我检查了反汇编,在我制作的简单案例中, Where(predicate).Single() 有三个额外的指令。所以,虽然我不是 IL 专家,但似乎 customers.Single(predicate) 应该更有效。
    • @JoshNoe 事实证明完全相反。
    【解决方案2】:

    如果找到多个符合条件的记录,Single 将抛出异常。 First 将始终从列表中选择第一条记录。如果查询只返回 1 条记录,您可以使用 First()

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

    【讨论】:

    • "如果查询只返回 1 条记录,您可以使用 First()" 为什么不使用 Single() 来更好地传达您的意图?此外,如果我们以某种方式神奇地确实得到了多件物品,而我们正期待着一件物品,我们难道不想知道这一点吗?否则,我们的程序会在可能已损坏的状态下运行。
    【解决方案3】:

    单()

    返回查询的单个特定元素

    当使用时:如果恰好需要 1 个元素;不为 0 或大于 1。如果列表为空或包含多个元素,则会抛出 Exception "Sequence contains more than one element"

    SingleOrDefault()

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

    当使用时:当需要 0 或 1 个元素时。如果列表有 2 个或更多项目,它将引发异常。

    First()

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

    使用时:当需要 1 个或多个元素并且您只想要第一个时。如果列表不包含任何元素,它会抛出异常。

    FirstOrDefault()

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

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

    【讨论】:

    • 很好的解释。我只会更改您可以在 1 个或更多元素需要时使用First,而不仅仅是“超过 1 个”,FirstOrDefault 可以使用任意数量的元素。
    【解决方案4】:

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

    两者都有效率,取第一项。 First() 的效率稍高一些,因为它不需要检查是否有第二项。

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

    【讨论】:

      【解决方案5】:

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

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

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

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

      【讨论】:

        【解决方案6】:

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

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

        【讨论】:

        • 如果我没记错的话,在他们产生的 SQL 中,First() 排在 TOP 1,Single() 排在 TOP 2。
        【解决方案7】:

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

        我在 Stack Overflow 上阅读了几篇讨论这个问题的帖子。有人说使用 First 而不是 Single 会有小的性能提升。这是因为 First 将简单地返回第一项,而 Single 必须扫描所有结果以确保没有重复项(即:如果它在表的第一行中找到该项目,它仍然会每隔一行扫描一次以确保没有与条件匹配的第二个值会引发错误)。我觉得我在“First”比“Single”更快的情况下站稳了脚跟,所以我开始证明这一点并结束辩论。

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

        我在“Foreign”键字段上尝试了类似的查询,这些查询没有被索引,这证明 First 更快,但 Single 在我的测试中总是稍微快一些。

        【讨论】:

        • 如果它在索引字段上,则数据库不需要进行扫描以确保它是唯一的。它已经知道它是。所以那里没有开销,服务器端唯一的开销是确保只返回一条记录。自己做性能测试,这不是一种或另一种方式的结论
        • 如果您在 IComparer 未使用的字段上进行搜索,我认为这些结果在复杂对象上不会相同。
        • 那应该是另一个问题。确保包含您的 test 的来源。
        【解决方案8】:

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

        【讨论】:

          【解决方案9】:

          您可以尝试简单的示例来获得差异。 第 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);
          

          【讨论】:

            【解决方案10】:

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

            【讨论】:

              【解决方案11】:

              Employee实体中的记录:

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

              Firstname = Robert: 不止一名员工同名

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

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

              单()

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

              First() 从多条记录中返回对应的记录,按照birthdate 升序排列,所以它会返回'Robert',谁是最老的。

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

              以上应返回最旧的罗伯特,根据出生日期。

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

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

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

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

              【讨论】:

              • 请解释你的答案。
              • @MrMaavin 我已经更新了,请告诉我你现在可以理解了吗?
              猜你喜欢
              • 2011-03-29
              • 2012-09-07
              • 1970-01-01
              • 1970-01-01
              • 2019-06-29
              • 2020-12-08
              • 1970-01-01
              • 1970-01-01
              • 2011-06-18
              相关资源
              最近更新 更多