Должен ли я использовать Single () или SingleOrDefault (), если есть вероятность, что элемент не будет найден?

c# .net linq coding-style

30653 просмотра

9 ответа

Что бы вы предпочли увидеть?

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

Или же:

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

Какова лучшая практика здесь? Какой из них делает исключение более понятным?

Автор: the_drow Источник Размещён: 12.11.2019 09:20

Ответы (9)


80 плюса

Решение
  • Используйте, SingleOrDefault()если ожидается 0 или 1
  • Используйте Single()1, а не 0 или 2 и более, ожидается, что элемент

Также имейте в виду, что существует ряд возможных сценариев:

  • Вы получили 0, когда ожидали 0 или 1 (хорошо)
  • Вы получили 1, когда ожидали 0 или 1 (хорошо)
  • Вы получили 2 или более, когда ожидали 0 или 1 (ошибка)

А также:

  • Вы получили 0, когда ожидалось 1 (ошибка)
  • Вы получили 1, когда 1 ожидалось (хорошо)
  • Вы получили 2 или более, когда ожидалось 1 (ошибка)

И не забывайте о том First(), FirstOrDefault()иAny()

Автор: abatishchev Размещён: 11.04.2011 09:47

5 плюса

Я бы написал:

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

Если случай, когда это не возвращает ни одного элемента, настолько распространен, вам нужно более дружелюбное сообщение об ошибке, тогда действительно ли это вообще исключение?

Автор: Myster Размещён: 28.08.2011 10:51

4 плюса

Практически они одинаковы. Но я предпочитаю второе, поскольку в первых двух выбрасывается одно исключение . Исключения дорогие.

Автор: Aliostad Размещён: 11.04.2011 09:36

3 плюса

Я думаю нормально писать

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

но вы также можете написать

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

если вам на самом деле не нужен доступ к значению.

Автор: Roy Dictus Размещён: 11.04.2011 09:38

2 плюса

Если вы ВСЕГДА ОЖИДАЕТЕ один элемент в списке, просто используйте 

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

и поймать исключение в методе верхнего уровня, где вы будете регистрировать детали исключения и показывать дружественное сообщение пользователю.

Если вы иногда ожидаете 0 или более 1 элементов, самый безопасный метод будет

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

Я предположил, что не важно проверять, существует более 1 записи или нет.

Подобный вопрос обсуждался в статье Code Project LINQ: Single против SingleOrDefault.

Автор: Michael Freidgeim Размещён: 06.06.2013 11:44

1 плюс

Я предпочел бы увидеть проверку количества элементов в списке перед тем, как получить элемент, вместо того, чтобы ждать исключения, а затем выдавать новый.

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

Приятно, что предложения компактны, но лучше быть более откровенным ИМО.

(Также в ваших предложениях исключения не являются строго действительными: они говорят «ничего не найдено», когда их может быть больше одного)

Изменить: Jeebus, добавил одну строку, чтобы сначала отфильтровать список для педантичных людей. (Я думал, что это было бы очевидно для всех)

Автор: Kieren Johnstone Размещён: 11.04.2011 09:29

1 плюс

Предполагая, что вы спрашивали о сценарии 0..1 , я предпочитаю SingleOrDefault, потому что он позволяет вам указать свой собственный способ обработки сценария «ничего не найдено».

Итак, хороший способ использовать немного синтаксического сахара:

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

где notFound () это:

T notFound<T>()
{
  throw new InvalidOperationException("Exactly one item with foo expected, none found");
}
Автор: Juanjo Размещён: 11.10.2013 06:06

0 плюса

Я согласен с Киреном Джонстоном, не ждите исключения, это довольно дорого, конечно, когда вы вызываете этот метод много раз.

Ваш первый фрагмент кода еще дороже, потому что вы ждете исходное исключение, а потом бросаете себе новое.

Автор: ChristiaanV Размещён: 11.04.2011 09:37

0 плюса

Один

Он возвращает один конкретный элемент из коллекции элементов, если найдено соответствие элемента. Исключение выдается, если для этого элемента в коллекции не найдено ни одного, ни нескольких совпадений.

SingleOrDefault

Он возвращает один конкретный элемент из коллекции элементов, если найдено соответствие элемента. Исключение выдается, если для этого элемента в коллекции найдено более одного совпадения. Возвращается значение по умолчанию, если для этого элемента в коллекции не найдено совпадений.

Вот пример примера:

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

    }
 }   
}
Автор: Nimesh khatri Размещён: 09.03.2017 10:40
32x32