Introdução: O medo familiar de todo desenvolvedor C#
\nImagine a situação: você está escrevendo uma consulta ao banco de dados ou a uma coleção em memória. Precisa obter o primeiro elemento que satisfaz uma condição. Você tem certeza de que esse elemento existe? Ou não tem tanta certeza? É aí que começa a dança: .First() ou .FirstOrDefault()?
Essa pergunta é um verdadeiro sucesso no Stack Overflow, com milhares de visualizações. E não é à toa. A escolha entre esses dois métodos pode significar a diferença entre um código limpo e previsível e um InvalidOperationException inesperado em Produção. Vamos entender de uma vez por todas quando usar cada método e acabar com as dúvidas para sempre.
Resumo: Qual é a diferença?
\nAmbos os métodos fazem quase a mesma coisa: retornam o primeiro elemento de uma sequência que satisfaz uma condição (ou apenas o primeiro elemento, se nenhuma condição for especificada). Mas há uma diferença crucial:
\n- \n
- .First() — se o elemento não for encontrado, lança a exceção
System.InvalidOperationException. \n - .FirstOrDefault() — se o elemento não for encontrado, retorna o valor padrão para o tipo (
nullpara tipos de referência,0paraint,falseparabool, etc.). \n
Parece simples, não é? Mas o diabo, como sempre, está nos detalhes.
\n\nQuando usar .First(): Quando "tem que existir, e ponto final"
\nUse .First() quando sua lógica de negócios garantir estritamente que o elemento existe. Se ele não existir, é uma situação excepcional, um erro nos dados ou na lógica da aplicação.
Exemplo 1: Busca por identificador único
\nSe você está buscando um usuário pela Chave Primária no banco de dados (por exemplo, pelo Id) e tem certeza de que o usuário existe, use .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 // Esperamos que o usuário com este ID exista.\n // Se não existir, é um problema sério; é melhor lançar uma exceção.\n return _context.Users.First(u => u.Id == userId);\n }\n}\nA exceção neste caso é um sinal de alerta. Ela diz: "Ei, os dados estão corrompidos, ou alguém passou um ID inválido". Você capturará essa exceção em um nível superior e a tratará como um erro crítico.
\n\nExemplo 2: Primeiro elemento após ordenação (garantido)
\nSe você está ordenando uma lista e pegando o primeiro elemento, e a lista com certeza tem elementos:
\nvar highestScore = players\n .OrderByDescending(p => p.Score)\n .First(); // Não pode haver 0 jogadores, verificamos isso antes\nSe você verificou previamente que a coleção não está vazia (por exemplo, if (players.Any())), então .First() é a sua escolha.
Quando usar .FirstOrDefault(): Quando o elemento pode não existir
\nRegra de ouro: Se a ausência de um resultado é uma situação normal, e não um erro, use .FirstOrDefault().
Exemplo 1: Busca opcional
\nImagine que estamos procurando um pedido pelo número, mas o pedido pode ainda não ter sido criado:
\npublic Order FindOrderByNumber(string orderNumber)\n{\n // O pedido pode não existir. Isso é normal.\n return _context.Orders\n .FirstOrDefault(o => o.OrderNumber == orderNumber);\n // Retornará null se o pedido não for encontrado.\n}\nApós chamar este método, somos obrigados a verificar se o resultado é null:
var order = FindOrderByNumber("ORD-12345");\nif (order != null)\n{\n // Trabalhamos com o pedido\n Console.WriteLine($"Pedido encontrado: {order.Id}");\n}\nelse\n{\n Console.WriteLine("Pedido não encontrado. Criando um novo.");