如果有可能找不到元素,我应该使用Single()还是SingleOrDefault()?

时间:2011-04-11 09:27:28

标签: c# .net linq coding-style

您更愿意看到什么?

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

这里的最佳做法是什么?哪一个让异常更容易理解?

9 个答案:

答案 0 :(得分:79)

  • 如果需要0或1项,请使用SingleOrDefault()
  • 如果是1,而不是0或2,则使用Single()

另请注意,有许多可能的情况:

  • 当0或1出现时,你得到0(ok)
  • 当预期为0或1时,你得到1(ok)
  • 当预期为0或1时(错误)
  • ,您有2个或更多

  • 当预期为1时,你得到0(错误)
  • 预计1时你得到1(ok)
  • 当预期为1时,你得到2或更多(错误)

不要忘记First()FirstOrDefault()Any()

答案 1 :(得分:5)

我会写:

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

如果这不会返回单个项目的情况如此常见,您需要更友好的错误消息,那么它真的是一个例外吗?

答案 2 :(得分:4)

实际上,它们是一样的。但是我更喜欢第二个,因为在前两个中引发了一个异常例外情况很昂贵。

答案 3 :(得分:3)

我认为写

是可以的
var item = list.SingleOrDefault(x => x.HasFoo);
if (item == null) ...

但你也可以写

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

如果您实际上不需要访问该值。

答案 4 :(得分: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条记录。

代码项目文章LINQ: Single vs. SingleOrDefault

中讨论了类似的问题

答案 5 :(得分: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");
}

建议很紧凑很好,但最好是更明确的IMO。

(同样在你的建议中,例外不是严格有效的:当可能有多个时,他们说“没有找到”)

编辑:Jeebus,为迂腐的人添加了一行来过滤列表。 (我认为这对任何人来说都是显而易见的)

答案 6 :(得分:1)

假设您询问 0..1 方案,我更喜欢 SingleOrDefault ,因为它允许您指定自己的方式来处理“无法找到”方案。< / p>

因此,使用少量语法糖的好方法是:

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

答案 7 :(得分:0)

我同意基伦·约翰斯通的观点,不要等待这个非常昂贵的例外情况,当你多次调用这种方法时确定。

你的第一个代码片段更贵,因为你等待原来的异常,而不是给自己一个新的。

答案 8 :(得分:0)

<强>单

  

它返回元素集合中的单个特定元素if   找到元素匹配。抛出异常,如果没有或多于一个   在集合中找到该元素的匹配项。

<强>的SingleOrDefault

  

它返回元素集合中的单个特定元素if   找到元素匹配。如果有多个匹配,则抛出异常   在集合中找到该元素。返回默认值,   如果没有找到该集合中该元素的匹配项。

这是示例: -

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

    }
 }   
}