web-dev-qa-db-fra.com

Dois-je utiliser Single () ou SingleOrDefault () s'il y a une chance que l'élément ne soit pas trouvé?

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?

36
the_drow
  • Utilisez SingleOrDefault() si 0 ou 1 éléments sont attendus
  • Utilisez Single() si 1, pas 0 ou 2 et plus, l'élément est attendu

Gardez également à l'esprit qu'il existe un certain nombre de scénarios possibles:

  • Vous avez obtenu 0 alors que 0 ou 1 était attendu (ok)
  • Vous avez obtenu 1 lorsque 0 ou 1 était attendu (ok)
  • Vous en avez obtenu 2 ou plus alors que 0 ou 1 était attendu (erreur)

Et:

  • Vous avez obtenu 0 alors que 1 était attendu (erreur)
  • Vous en avez 1 quand 1 était attendu (ok)
  • Vous en avez obtenu 2 ou plus alors que 1 était attendu (erreur)

Et n'oubliez pas First(), FirstOrDefault() et Any()

79
abatishchev

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?

5
Myster

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.

4
Aliostad

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.

3
Roy Dictus

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)

1
Kieren Johnstone

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");
}
1
Juanjo

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.

0
ChristiaanV

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();

    }
 }   
}
0
Nimesh khatri