singleordefault secuencia firstordefault elementos contiene c# .net linq coding-style

c# - secuencia - ¿Debería usar Single() o SingleOrDefault() si existe la posibilidad de que no se encuentre el elemento?



singleordefault c# (9)

Creo que está bien escribir

var item = list.SingleOrDefault(x => x.HasFoo); if (item == null) ...

pero también puedes escribir

if (list.Any(x => x.HasFoo)) ...

si realmente no necesita acceso al valor.

¿Qué preferirías ver?

try { var item = list.Single(x => x.HasFoo); } catch(InvalidOperationException e) { throw new InvalidOperationException("Exactly one item with foo expected, none found", e); }

O:

var item = list.SingleOrDefault(x => x.HasFoo); if (item == null) throw new InvalidOperationException("Exactly one item with foo expected, none found");

¿Cuál es la mejor práctica aquí? ¿Cuál hace la excepción más comprensible?


Estoy de acuerdo con Kieren Johnstone, no espere la excepción, esto es bastante costoso, seguro cuando llama a este método muchas veces.

El primer fragmento de código es aún más caro porque esperas la excepción original y te lanzas uno nuevo.


Prefiero ver un cheque para la cantidad de elementos en la lista antes de obtener el elemento, en lugar de esperar una excepción, y luego lanzar uno nuevo.

var listFiltered = list.Where(x => x.HasFoo).ToList(); int listSize = listFiltered.Count(); if (listSize == 0) { throw new InvalidOperationException("Exactly one item with foo expected, none found"); } else if (listSize > 1) { throw new InvalidOperationException("Exactly one item with foo expected, more than one found"); }

Es bueno que las sugerencias sean compactas, pero es mejor ser OMI más explícito.

(También en sus sugerencias, las excepciones no son estrictamente válidas: dicen ''no encontrado'' cuando podría haber más de uno)

Editar: Jeebus, agregó una línea para filtrar la lista primero para gente pedante. (Pensé que habría sido obvio para cualquiera)


Si SIEMPRE ESPERABA un elemento en la lista, simplemente use

var item = list.Single(x => x.HasFoo);

y capture excepción en el método de nivel superior, donde registrará los detalles de la excepción y mostrará un mensaje amigable al usuario.

Si a veces espera 0 o más de 1 elemento, el método más seguro será

var item = list.FirstOrDefault(x => x.HasFoo); if (item == null) {  // empty list processing, not necessary throwing exception }

Supuse que no es importante verificar, hay más de 1 registro existente o no.

Se discutió una pregunta similar en el artículo de Code Project LINQ: Single vs. SingleOrDefault


Suponiendo que estaba preguntando sobre el escenario 0..1 , prefiero SingleOrDefault porque le permite especificar su propia forma de manejar el escenario de "nada encontrado".

Entonces, una buena manera de hacerlo usando un poco de azúcar sintáctico sería:

// assuming list is List<Bar>(); var item = list.SingleOrDefault(x => x.HasFoo) ?? notFound<Bar>();

donde notFound () es:

T notFound<T>() { throw new InvalidOperationException("Exactly one item with foo expected, none found"); }


Yo escribiría:

var item = list.Single(x => x.HasFoo);

Si el caso en que esto no devuelve un solo elemento es tan común que necesita un mensaje de error más amigable, entonces, ¿es realmente una excepción?


Prácticamente , son lo mismo. Pero prefiero el segundo puesto ya que se arroja una excepción en los dos primeros. Las excepciones son costosas.


Soltero

Devuelve un único elemento específico de una colección de elementos si se encuentra elemento coincidente. Se lanza una excepción, si no se encuentra ninguna o más coincidencias para ese elemento en la colección.

SingleOrDefault

Devuelve un único elemento específico de una colección de elementos si se encuentra elemento coincidente. Se lanza una excepción, si se encuentra más de una coincidencia para ese elemento en la colección. Se devuelve un valor predeterminado, si no se encuentra coincidencia para ese elemento en la colección.

aquí hay un ejemplo de muestra:

using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; namespace LinqSingleorSingleOrDefault { class Employee { public int Id { get; set; } public string Name { get; set; } public string City { get; set; } } public class Program { static void Main(string[] args) { IList<Employee> employeeList = new List<Employee>(){ new Employee() { Id = 10, Name = "Chris", City = "London" }, new Employee() { Id=11, Name="Robert", City="London"}, new Employee() { Id=12, Name="Mahesh", City="India"}, new Employee() { Id=13, Name="Peter", City="US"}, new Employee() { Id=14, Name="Chris", City="US"} }; //Single Example var result1 = employeeList.Single(); // this will throw an InvalidOperationException exception because more than 1 element in employeeList. var result2 = employeeList.Single(e => e.Id == 11); //exactly one element exists for Id=11 var result3 = employeeList.Single(e => e.Name == "Chris"); // throws an InvalidOperationException exception because of more than 1 element contain for Name=Chris IList<int> intList = new List<int> { 2 }; var result4 = intList.Single(); // return 2 as output because exactly 1 element exists //SingleOrDefault Example var result5 = employeeList.SingleOrDefault(e => e.Name == "Mohan"); //return default null because not element found for specific condition. var result6 = employeeList.SingleOrDefault(e => e.Name == "Chris"); // throws an exception that Sequence contains more than one matching element var result7 = employeeList.SingleOrDefault(e => e.Id == 12); //return only 1 element Console.ReadLine(); } } }


  • Use SingleOrDefault() si se esperan 0 o 1 elementos
  • Use Single() si se espera 1, no 0 o 2 y más.

También tenga en cuenta que hay varios escenarios posibles:

  • Tienes 0 cuando se esperaba 0 o 1 (ok)
  • Tienes 1 cuando se esperaba 0 o 1 (ok)
  • Tienes 2 o más cuando se esperaba 0 o 1 (error)

Y:

  • Tienes 0 cuando se esperaba 1 (error)
  • Tienes 1 cuando se esperaba 1 (ok)
  • Obtuvo 2 o más cuando se esperaba 1 (error)

Y no te olvides de First() , FirstOrDefault() y Any()