引言:每位 C# 开发者都熟悉的恐惧
\n设想一个场景:您正在对数据库或内存中的集合编写查询。您需要获取满足条件的第一个元素。您确信该元素一定存在。或者您并不确定?于是,纠结就开始了:是用 .First() 还是 .FirstOrDefault()?
这个问题在 Stack Overflow 上可是个热门话题,浏览量成千上万。这并非没有道理。在这两个方法之间做出选择,可能意味着代码是干净、可预测的,还是在生产环境中突然抛出 InvalidOperationException。让我们一劳永逸地弄清楚何时使用哪个方法,从此不再犹豫。
核心要点:区别是什么?
\n这两个方法做的事情几乎相同:返回序列中满足条件的第一个元素(如果未指定条件,则返回第一个元素)。但有一个关键区别:
\n- \n
- .First() — 如果未找到元素,则抛出
System.InvalidOperationException异常。 \n - .FirstOrDefault() — 如果未找到元素,则返回该类型的默认值(引用类型返回
null,int返回0,bool返回false等)。 \n
看起来很简单。但魔鬼,一如既往,藏在细节中。
\n\n何时使用 .First():当“必须存在,没得商量”
\n当您的业务逻辑严格保证元素存在时,请使用 .First()。如果不存在,那就是异常情况,是数据或应用逻辑中的错误。
示例 1:按唯一标识符查找
\n如果您按数据库中的主键(例如 Id)查找用户,并且确信该用户存在,请使用 .First():
public class UserService\n{\n private readonly AppDbContext _context;\n\n public UserService(AppDbContext context)\n {\n _context = context;\n }\n\n public User GetUserById(int userId)\n {\n // 我们期望具有此 ID 的用户存在。\n // 如果不存在——这是一个严重问题,最好让它抛出异常。\n return _context.Users.First(u => u.Id == userId);\n }\n}\n在这种情况下,异常就是一个警报。它说:“嘿,数据已损坏,或者有人传入了错误的 ID”。您会在调用栈的更高层捕获这个异常,并将其作为严重错误处理。
\n\n示例 2:排序后的第一个元素(保证存在)
\n如果您对列表进行排序并取第一个元素,并且列表中肯定有元素:
\nvar highestScore = players\n .OrderByDescending(p => p.Score)\n .First(); // 玩家数量不可能为 0,我们之前已经检查过了\n如果您事先检查过集合不为空(例如 if (players.Any())),那么 .First() 就是您的选择。
何时使用 .FirstOrDefault():当元素可能不存在时
\n黄金法则:如果缺少结果是正常情况,而不是错误,请使用 .FirstOrDefault()。
示例 1:可选查找
\n假设我们按订单号查找订单,但订单可能尚未创建:
\npublic Order FindOrderByNumber(string orderNumber)\n{\n // 订单可能不存在。这是正常的。\n return _context.Orders\n .FirstOrDefault(o => o.OrderNumber == orderNumber);\n // 如果未找到订单,则返回 null。\n}\n调用此方法后,我们必须检查结果是否为 null:
var order = FindOrderByNumber("ORD-12345");\nif (order != null)\n{\n // 处理订单\n Console.WriteLine($"订单已找到:{order.Id}");\n}\nelse\n{\n Console.WriteLine("未找到订单。正在创建新订单。");