.First vs .FirstOrDefault en C#: ¿Cuándo elegir cuál?

Entrenador de Python en Línea para Principiantes

Aprende Python fácilmente sin sobrecargarte de teoría. Resuelve tareas prácticas con verificación automática, recibe pistas y escribe código directamente en el navegador, sin instalar nada.

Start Course

Introducción: El miedo familiar de todo desarrollador C#

\n

Imagina la situación: estás escribiendo una consulta a la base de datos o a una colección en memoria. Necesitas obtener el primer elemento que cumple una condición. ¿Estás seguro de que ese elemento definitivamente existe? ¿O no estás seguro? Ahí es donde comienza el baile: .First() o .FirstOrDefault()?

\n

Esta pregunta es un verdadero éxito en Stack Overflow con miles de visitas. Y con razón. La elección entre estos dos métodos puede significar la diferencia entre un código limpio y predecible y un InvalidOperationException repentino en Producción. Aclaremos de una vez por todas cuándo usar cada método y olvidémonos para siempre de las dudas.

\n\n

Resumen: ¿Cuál es la diferencia?

\n

Ambos métodos hacen casi lo mismo: devuelven el primer elemento de una secuencia que cumple una condición (o simplemente el primer elemento si no se especifica una condición). Pero hay una diferencia crítica:

\n
    \n
  • .First() — si no se encuentra el elemento, lanza la excepción System.InvalidOperationException.
  • \n
  • .FirstOrDefault() — si no se encuentra el elemento, devuelve el valor predeterminado para el tipo (null para tipos de referencia, 0 para int, false para bool, etc.).
  • \n
\n

Parece simple. Pero el diablo, como siempre, está en los detalles.

\n\n

¿Cuándo usar .First(): Cuando "debe existir, y punto"

\n

Usa .First() cuando tu lógica de negocio garantice estrictamente que el elemento existe. Si no existe, es una situación excepcional, un error en los datos o en la lógica de la aplicación.

\n\n

Ejemplo 1: Búsqueda por identificador único

\n

Si buscas un usuario por su Clave Primaria en la base de datos (por ejemplo, por Id), y estás seguro de que ese usuario existe, usa .First():

\n
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 el usuario con ese ID exista.\n        // Si no, es un problema grave, mejor que lance una excepción.\n        return _context.Users.First(u => u.Id == userId);\n    }\n}
\n

La excepción en este caso es una señal de alarma. Dice: "Oye, los datos están corruptos, o alguien pasó un ID incorrecto". Capturarás esta excepción más arriba en la pila y la manejarás como un error crítico.

\n\n

Ejemplo 2: Primer elemento después de ordenar (garantizado)

\n

Si ordenas una lista y tomas el primer elemento, y la lista definitivamente tiene elementos:

\n
var highestScore = players\n    .OrderByDescending(p => p.Score)\n    .First(); // No puede haber 0 jugadores, lo verificamos antes
\n

Si verificaste previamente que la colección no está vacía (por ejemplo, if (players.Any())), entonces .First() es tu elección.

\n\n

¿Cuándo usar .FirstOrDefault(): Cuando el elemento puede no existir

\n

Regla de oro: Si la ausencia de un resultado es una situación normal, no un error, usa .FirstOrDefault().

\n\n

Ejemplo 1: Búsqueda opcional

\n

Imagina que buscamos un pedido por número, pero el pedido puede que aún no se haya creado:

\n
public Order FindOrderByNumber(string orderNumber)\n{\n    // El pedido puede no existir. Es normal.\n    return _context.Orders\n        .FirstOrDefault(o => o.OrderNumber == orderNumber);\n    // Devuelve null si no se encuentra el pedido.\n}
\n

Después de llamar a este método, debemos verificar si el resultado es null:

\n
var order = FindOrderByNumber("ORD-12345");\nif (order != null)\n{\n    // Trabajamos con el pedido\n    Console.WriteLine($"Pedido encontrado: {order.Id}");\n}\nelse\n{\n    Console.WriteLine("Pedido no encontrado. Creando uno nuevo.");                

Blogs

Book Recommendations