FirstOrDefault vs SingleOrDefault
A la hora de trabajar con colecciones, algunos programadores de C# no conocen con exactitud la diferencia entre los métodos FirstOrDefault y SingleOrDefault o las confunden, y cuando los usan en sus aplicaciones, se encuentran a veces con comportamientos extraños.
En esta entrada voy a tratar de explicar algunos de estos comportamientos con el fin de que podamos entender un poco más sus diferencias y sus comportamientos.
Tanto FirstOrDefault como SingleOrDefault pertenecen al namespace System.Linq, dentro del ensamblado System.Core, y de la librería System.Core.dll.
Encontrarás información sobre FirstOrDefault en este enlace.
Encontrarás información sobre SingleOrDefatul en este enlace.
Si miramos la descripción de FirstOrDefault, ésta dice:
Devuelve el primer elemento de una secuencia o un valor predeterminado si no se encuentra ningún elemento.
Si miramos la descripción de SingleOrDefault, ésta dice:
Devuelve un único elemento específico de una secuencia o un valor predeterminado si no se encuentra dicho elemento.
Parecen por lo tanto y a priori muy similares, pero no es así como veremos a continuación.
Cada una de ellas tiene sus pros y contras. Decidirnos por una u otra depende del contexto de nuestro desarrollo.
Si el uso de LINQ no devuelve resultados:
FirstOrDefault devolverá el valor por defecto del tipo de dato, o Null en el caso de que trabajemos con objetos complejos.
SingleOrDefault por su parte, devolverá también el valor por defecto del tipo de dato, o Null en el caso de que trabajemos con objetos complejos.
Si el uso de LINQ encuentra un valor, lo devolverá tanto para FirstOrDefault como para SingleOrDefault.
Una diferencia real entre ambos es que para FirstOrDefault, en cuanto encuentre el primer registro, devolverá el resultado y no seguirá iterando más.
SingleOrDefault por su parte necesita iterar en todos los elementos de una colección.
Esto implica que por lo general, FirtsOrDefault sea más rápido que SingleOrDefault.
Otra consideración a tener en cuenta es que siempre que usemos FirstOrDefault, nunca sabremos a ciencia cierta si existe algún otro registro que cumpla la condición, ya que al encontrar el primer registro, detiene la iteración y devuelve el registro encontrado. Así que además de no saber si hay más de un registro que cumple la condición, tampoco tenemos clara la integridad de datos a no ser que ordenáramos por algún campo como una fecha, etc.
De hecho, una de los comportamiento más destacables, es que si existiera más de un registro que cumpliese la condición, SingleOrDefault lanzaría una excepción de tipo System.InvalidOperationException, mientras que FirstOrDefault no lanzaría ningún tipo de error o warning. Recordemos que SingleOrDefault itera por todos los registros de la colección para asegurarse precisamente que sólo hay un registro como máximo que cumple la condición expuesta.
Si cuando uses estos métodos, tienes claro que no va a haber ninguna posibilidad de que el registro buscado con una determinada condición, se va a dar más de una vez, FirstOrDefault sería tu candidato.
También sería tu candidato el método FirstOrDefault si no nos importa tanto la integridad de datos repetidos que cumplen la condición, pero sí saber que hay al menos uno que cumple la condición.
Pero si lo que te preocupa e interesa es la posibilidad de que exista registros repetidos de acuerdo a la condición LINQ planteada, deberías pensar en una estrategia de contabilizar las ocurrencias, o bien utilizar SingleOrDefault pero gestionar las excepciones para actuar de acuerdo a la excepción lanzada en este caso.
Por lo tanto, SingleOrDefault asume que no hay repeticiones en la condición LINQ indicada (single como indica su nombre).
Pero aquí, también tenemos que tener en cuenta la importancia del rendimiento.
Si no nos importa demasiado, quizás SingleOrDefault fuera nuestra mejor opción.
Todo como siempre, dependerá del contexto.
¡Happy Conding!