何时使用 .First 以及何时将 .FirstOrDefault 与 LINQ 一起使用?
Posted
技术标签:
【中文标题】何时使用 .First 以及何时将 .FirstOrDefault 与 LINQ 一起使用?【英文标题】:When to use .First and when to use .FirstOrDefault with LINQ? 【发布时间】:2010-11-04 16:54:19 【问题描述】:我四处搜索,并没有真正找到一个明确的答案,即您何时想要使用 .First
以及何时想要将 .FirstOrDefault
与 LINQ 一起使用。
您想在什么时候使用.First
?只有在没有返回结果的情况下才想捕获异常?
var result = List.Where(x => x == "foo").First();
你想什么时候使用.FirstOrDefault
?如果没有结果,您何时总是想要默认类型?
var result = List.Where(x => x == "foo").FirstOrDefault();
那么,Take 呢?
var result = List.Where(x => x == "foo").Take(1);
【问题讨论】:
.First
和 .FirstOrDefault
都将谓词作为参数,因此 var result = List.Where(x => x == "foo").First();
可以重写为 var result = List.First(x => x == "foo");
别忘了考虑Single
和SingleOrDefault
。我讨厌人们使用First
,而他们真正的意思是Single
; )
如果返回多个元素,Single 或 SingleOrDefault 会抛出异常!我认为 FirstOrDefault 在大多数情况下更好!
关键是当你期望一个单一的结果时你应该这样说,异常表明你的逻辑失败了。
还要考虑使用.FirstOrDefault()
总是给你机会抛出一个更有意义的异常。如果在一个方法中引发了一个序列异常并且有多个.First()
,则可能很难辨别哪个语句是问题所在。
【参考方案1】:
当我知道或期望序列至少有一个元素时,我会使用First()
。换句话说,当序列为空是一种例外情况。
当您知道需要检查是否存在某个元素时,请使用FirstOrDefault()
。换句话说,什么时候序列为空是合法的。您不应该依赖异常处理来进行检查。 (这是不好的做法,可能会损害性能)。
最后,First()
和Take(1)
之间的区别在于First()
返回元素本身,而Take(1)
返回仅包含一个元素的元素序列。
【讨论】:
我唯一要补充的是,如果您选择的类型的默认值可能是一个有效值,例如您的结果可能是 int 值 0,那么处理异常似乎成为处理这个问题的最佳方式。 从头开始,我找到了一个更好的方法来完成它,使用: DefaultIfEmpty(-1).First() Take 不会只返回一个元素,它最多返回一个元素(当然,如果您指定 1)。如果序列最初为空,它也可能返回 0 个元素。 我认为最好先解释一下Take
的工作原理,然后再解释一下First()
与Take(1)
的相同之处【参考方案2】:
.First
在没有结果时会抛出异常。 .FirstOrDefault
不会,它只会返回 null(引用类型)或值类型的默认值。 (例如,0
表示 int。)这里的问题不是您何时需要默认类型,而是更多:您愿意处理异常还是处理默认值?由于异常应该是异常的,因此当您不确定是否要从查询中获得结果时,首选FirstOrDefault
。当逻辑上数据应该在那里时,可以考虑异常处理。
Skip()
和Take()
通常用于在结果中设置分页。 (比如显示前 10 个结果,下一页显示后 10 个结果等)
希望这会有所帮助。
【讨论】:
【参考方案3】:如果没有要返回的行,.First()
将抛出异常,而 .FirstOrDefault()
将返回默认值(NULL
用于所有引用类型)。
因此,如果您准备好并愿意处理可能的异常,.First()
很好。如果您更喜欢检查!= null
的返回值,那么.FirstOrDefault()
是您更好的选择。
但我想这也有点个人喜好。使用对您更有意义且更适合您的编码风格的任何一种。
【讨论】:
【参考方案4】:First()
-
返回序列的第一个元素。
当结果中没有元素或源为空时会抛出错误。
您应该使用它,如果需要多个元素并且您只需要第一个元素。
FirstOrDefault()
-
返回序列的第一个元素,如果没有找到元素,则返回默认值。
仅当源为空时才会引发错误。
您应该使用它,如果需要多个元素并且您只需要第一个元素。
如果结果为空也很好。
我们有一个 UserInfos 表,其中有一些记录,如下所示。根据下表,我创建了示例...
如何使用 First()
var result = dc.UserInfos.First(x => x.ID == 1);
只有一条 ID== 1 的记录。应该返回这条记录 ID:1 名字:Manish 姓氏:Dubey 电子邮件:xyz@xyz.com
var result = dc.UserInfos.First(x => x.FName == "Rahul");
有多个记录,其中 FName == "Rahul"。第一条记录应该返回。 ID:7 名字:Rahul 姓氏:Sharma 电子邮件:xyz1@xyz.com
var result = dc.UserInfos.First(x => x.ID ==13);
没有 ID== 13 的记录。应该出现错误。 InvalidOperationException:序列不包含任何元素
如何使用 FirstOrDefault()
var result = dc.UserInfos.FirstOrDefault(x => x.ID == 1);
只有一条 ID== 1 的记录。应该返回这条记录 ID:1 名字:Manish 姓氏:Dubey 电子邮件:xyz@xyz.com
var result = dc.UserInfos.FirstOrDefault(x => x.FName == "Rahul");
有多个记录,其中 FName == "Rahul"。第一条记录应该返回。 ID:7 名字:Rahul 姓氏:Sharma 电子邮件:xyz1@xyz.com
var result = dc.UserInfos.FirstOrDefault(x => x.ID ==13);
没有ID== 13的记录,返回值为null
希望它能帮助您了解何时使用First()
或FirstOrDefault()
。
【讨论】:
在我看来,“应该发生错误”这句话。在第三个 FirstOrDefault() 示例下具有误导性。【参考方案5】:首先,Take
是一种完全不同的方法。它返回一个IEnumerable<T>
而不是一个T
,所以就这样了。
在First
和FirstOrDefault
之间,如果您确定某个元素存在,则应使用First
,如果不存在,则说明有错误。
顺便说一句,如果你的序列中包含default(T)
元素(例如null
),并且需要区分为空和第一个元素为null
,则不能使用FirstOrDefault
。
【讨论】:
【参考方案6】:第一:
返回序列的第一个元素 抛出异常:结果中没有元素 在以下情况下使用:预期有多个元素并且您只需要第一个元素时第一个或默认值:
返回序列的第一个元素,如果没有找到元素,则返回默认值 抛出异常:仅当源为空时 在以下情况下使用:当需要多个元素并且您只需要第一个元素时。结果也可以为空发件人:http://www.technicaloverload.com/linq-single-vs-singleordefault-vs-first-vs-firstordefault/
【讨论】:
【参考方案7】:另一个需要注意的区别是,如果您在生产环境中调试应用程序,您可能无法访问行号,因此确定方法中的哪个特定 .First()
语句引发了异常可能很困难。
异常消息也不会包含您可能使用过的任何 Lambda 表达式,这会使任何问题更难调试。
这就是为什么我总是使用FirstOrDefault()
,即使我知道空条目会构成异常情况。
var customer = context.Customers.FirstOrDefault(i => i.Id == customerId);
if (customer == null)
throw new Exception(string.Format("Can't find customer 0.", customerId));
【讨论】:
“不要故意从你自己的源代码中抛出 System.Exception [...]” (source)。考虑抛出不同的异常类型,例如ArgumentException
或定义自定义异常类型,例如 RecordNotFoundException
。【参考方案8】:
First()
当您知道结果包含超过 1 个预期的元素并且您应该只使用序列的第一个元素时。
FirstOrDefault()
FirstOrDefault() 与 First() 类似,不同之处在于,如果没有元素匹配指定条件,则返回泛型集合底层类型的默认值。如果没有找到元素,它不会抛出 InvalidOperationException。但是元素或序列的集合为空而不是引发异常。
【讨论】:
【参考方案9】:这类函数属于元素运算符。下面定义了一些有用的元素运算符。
-
First/FirstOrDefault
Last/LastOrDefault
单/单或默认
当我们需要根据特定条件从序列中选择单个元素时,我们会使用元素运算符。这是一个例子。
List<int> items = new List<int>() 8, 5, 2, 4, 2, 6, 9, 2, 10 ;
First() 运算符在满足条件后返回序列的第一个元素。如果没有找到元素,则会抛出异常。
int result = items.Where(item => item == 2).First();
FirstOrDefault() 运算符在满足条件后返回序列的第一个元素。如果没有找到元素,那么它将返回该类型的默认值。
int result1 = items.Where(item => item == 2).FirstOrDefault();
【讨论】:
【参考方案10】:我发现一个网站可以解释 FirstOrDefault 的需求http://thepursuitofalife.com/the-linq-firstordefault-method-and-null-resultsets/ 如果查询没有结果,并且您想调用 First() 或 Single() 来获取单行...您将收到“Sequence contains no elements”异常。
免责声明:我从未使用过 LINQ,所以如果这离题了,我深表歉意。
【讨论】:
【参考方案11】:其他人已经很好地描述了First()
和FirstOrDefault()
之间的区别。我想进一步解释这些方法的语义。在我看来FirstOrDefault
被过度使用了。在大多数情况下,当您过滤数据时,您要么期望返回与逻辑条件匹配的元素集合,要么通过其唯一标识符获取单个唯一元素——例如用户、书籍、帖子等......为什么我们甚至可以说FirstOrDefault()
是一种代码气味,不是因为它有问题,而是因为它被使用得太频繁了。 This blog post 详细探讨了该主题。 IMO 大多数时候SingleOrDefault()
是一个更好的选择,因此请注意这个错误,并确保您使用最合适的方法,清楚地代表您的合同和期望。
【讨论】:
根据您的上下文,.SingleOrDefault()
很容易被滥用,并可能影响某些类型的查询的性能。 .SingleOrDefault()
的底层实现实际上使用.Take(2)
然后应用验证逻辑。代码气味更多地在于我们为什么使用 OrDefault(),而不一定是 First 与 Single 如果我们的代码已经预先假设或预先验证条件只会返回 1 行或不返回任何行,我们是否需要在方法链的后面继续使用和验证 .Single()
?【参考方案12】:
someList.First(); // exception if collection is empty.
someList.FirstOrDefault(); // first item or default(Type)
使用哪一个? 它应该由业务逻辑决定,而不是担心异常/程序失败。
例如, 如果业务逻辑说我们在任何工作日都不能有零交易(假设)。那么你不应该尝试用一些聪明的编程来处理这种情况。 我将始终在此类集合上使用 First(),如果其他事情搞砸了业务逻辑,则让程序失败。
代码:
var transactionsOnWorkingDay = GetTransactionOnLatestWorkingDay();
var justNeedOneToProcess = transactionsOnWorkingDay.First(): //Not FirstOrDefault()
我希望看到其他 cmets 解决这个问题。
【讨论】:
引用和可空类型的默认值为空。 快速失败是好的 - 但是对于您描述的场景,我宁愿看到首先,让它失败,捕获异常,然后返回一个有意义的错误。比如 catch(InvalidOperationException e)throw new InvalidOperationException("一天内不能有零笔交易!", e);但是,是的,使用默认值来避免处理真正的业务逻辑问题是非常糟糕的。【参考方案13】:好吧,让我给我两分钱。 First / Firstordefault 用于使用第二个构造函数时。我不会解释它是什么,但它是你可能总是使用一个,因为你不想引起异常。
person = tmp.FirstOrDefault(new Func<Person, bool>((p) =>
return string.IsNullOrEmpty(p.Relationship);
));
【讨论】:
不完全是。当您只需要检索一个项目或在将结果分配给一个不是数组的值时必须避免编译错误并且您确定查询只返回一个结果时,第一个构造函数被广泛使用。虽然使用第二个构造函数而不是使用额外的 .Where() 可能看起来更快(因为您 think LINQ 在找到第一个之后停止评估列表中的项目)它总是停在第一个元素 【参考方案14】:linq 对集合实现单个简单查询的方法有很多,只需我们在 sql 中编写连接,可以根据需要和需要先应用或最后应用过滤器。
这是一个示例,我们可以在集合中找到具有 id 的元素。
要对此添加更多信息,方法 First,FirstOrDefault
,理想情况下会在集合具有至少一条记录时返回相同的值。但是,如果一个集合可以是空的。那么First
将返回一个异常,但FirstOrDefault
将返回null
或默认值。例如,int
将返回 0。因此虽然说是个人喜好,但最好使用 FirstOrDefault
以避免异常处理。
【讨论】:
以上是关于何时使用 .First 以及何时将 .FirstOrDefault 与 LINQ 一起使用?的主要内容,如果未能解决你的问题,请参考以下文章
何时使用SESSION以及何时在Web应用程序中使用GET进行参数传递?