Que préférez-vous voir?
try
{
var item = list.Single(x => x.HasFoo);
}
catch(InvalidOperationException e)
{
throw new InvalidOperationException("Exactly one item with foo expected, none found", e);
}
Ou:
var item = list.SingleOrDefault(x => x.HasFoo);
if (item == null)
throw new InvalidOperationException("Exactly one item with foo expected, none found");
Quelle est la meilleure pratique ici? Lequel rend l'exception plus compréhensible?
SingleOrDefault()
si 0 ou 1 éléments sont attendusSingle()
si 1, pas 0 ou 2 et plus, l'élément est attenduGardez également à l'esprit qu'il existe un certain nombre de scénarios possibles:
Et:
Et n'oubliez pas First()
, FirstOrDefault()
et Any()
Je souhaiterai écrire:
var item = list.Single(x => x.HasFoo);
Si le cas où cela ne renvoie pas un seul élément est si courant que vous avez besoin d'un message d'erreur plus convivial, est-ce vraiment une exception?
Pratiquement , ce sont les mêmes. Mais je préfère le second un car une exception est levée dans les deux premiers. Les exceptions sont chères.
Je pense que c'est OK d'écrire
var item = list.SingleOrDefault(x => x.HasFoo);
if (item == null) ...
mais vous pouvez aussi écrire
if (list.Any(x => x.HasFoo)) ...
si vous n'avez pas réellement besoin d'accéder à la valeur.
Si vous ATTENDEZ TOUJOURS un élément de la liste, utilisez simplement
var item = list.Single(x => x.HasFoo);
et intercepter l'exception à la méthode de niveau supérieur, où vous enregistrerez les détails de l'exception et afficherez un message convivial à l'utilisateur.
Si vous vous attendez parfois à 0 ou plus de 1 éléments, la méthode la plus sûre sera
var item = list.FirstOrDefault(x => x.HasFoo);
if (item == null)
{
// empty list processing, not necessary throwing exception
}
J'ai supposé qu'il n'est pas important de vérifier s'il existe plus d'un enregistrement.
Une question similaire a été discutée dans l'article Code Project LINQ: Single vs. SingleOrDefault
Je préfère voir une vérification du nombre d'éléments dans la liste avant d'obtenir l'élément, plutôt que d'attendre une exception, puis d'en lancer une nouvelle.
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");
}
C'est bien que les suggestions soient compactes, mais mieux vaut être plus explicite à l'OMI.
(Dans vos suggestions également, les exceptions ne sont pas strictement valables: elles disent "aucune trouvée" alors qu'il pourrait y en avoir plusieurs)
Edit: Jeebus, a ajouté une ligne pour filtrer la liste en premier pour les pédants. (Je pensais que cela aurait été évident pour n'importe qui)
En supposant que vous posiez des questions sur le scénario ..1, je préfère SingleOrDefault car il vous permet de spécifier votre propre façon de gérer le scénario "rien trouvé".
Donc, une bonne façon de faire en utilisant un peu de sucre syntaxique serait:
// assuming list is List<Bar>();
var item = list.SingleOrDefault(x => x.HasFoo) ?? notFound<Bar>();
où notFound () est:
T notFound<T>()
{
throw new InvalidOperationException("Exactly one item with foo expected, none found");
}
Je suis d'accord avec Kieren Johnstone, n'attendez pas l'exception, c'est assez coûteux, bien sûr lorsque vous appelez cette méthode beaucoup de fois.
Votre premier extrait de code est encore plus cher, car vous attendez l'exception d'origine et vous en lancez une nouvelle.
nique
Il renvoie un seul élément spécifique d'une collection d'éléments si une correspondance d'élément est trouvée. Une exception est levée si aucune ou plusieurs correspondances trouvées pour cet élément dans la collection.
SingleOrDefault
Il renvoie un seul élément spécifique d'une collection d'éléments si une correspondance d'élément est trouvée. Une exception est levée, si plusieurs correspondances ont été trouvées pour cet élément dans la collection. Une valeur par défaut est renvoyée, si aucune correspondance n'est trouvée pour cet élément dans la collection.
voici un exemple d'exemple: -
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();
}
}
}