Should I use Single() or SingleOrDefault() if there is a chance that the element won't be found?
你更喜欢看什么?
1 2 3 4 5 6 7 8 | try { var item = list.Single(x => x.HasFoo); } catch(InvalidOperationException e) { throw new InvalidOperationException("Exactly one item with foo expected, none found", e); } |
要么:
1 2 3 | var item = list.SingleOrDefault(x => x.HasFoo); if (item == null) throw new InvalidOperationException("Exactly one item with foo expected, none found"); |
这里的最佳做法是什么? 哪一个让异常更容易理解?
-
如果需要0或1项,请使用
SingleOrDefault() -
如果是1,而不是0或2,则使用
Single() 项目
还要记住,有许多可能的情况:
- 当0或1预期时,你得0(ok)
- 预计0或1时你得到1(ok)
- 当预期为0或1时,你得到2或更多(错误)
和:
- 当预期为1时,你得到0(错误)
- 预计1时你有1(好)
- 当预期为1时你有2个或更多(错误)
不要忘记
我会写:
1 | var item = list.Single(x => x.HasFoo); |
如果这不返回单个项目的情况如此常见,您需要一个更友好的错误消息,那么它真的是一个例外吗?
实际上,它们是一样的。但我更喜欢第二个,因为在前两个中抛出一个异常。例外是昂贵的。
我认为写作是可以的
1 2 | var item = list.SingleOrDefault(x => x.HasFoo); if (item == null) ... |
但你也可以写
1 | if (list.Any(x => x.HasFoo)) ... |
如果您实际上不需要访问该值。
如果您总是预测列表中的一个元素,只需使用 
1 | var item = list.Single(x => x.HasFoo); |
并在顶级方法中捕获异常,您将在其中记录异常的详细信息并向用户显示友好消息。
如果您有时期望0或超过1个元素,最安全的方法将是
1 2 3 4 5 | var item = list.FirstOrDefault(x => x.HasFoo); if (item == null) { // empty list processing, not necessary throwing exception } |
我假设,验证不重要,是否存在超过1条记录。
代码项目文章LINQ:Single vs. SingleOrDefault中讨论了类似的问题
假设您询问0..1场景,我更喜欢SingleOrDefault,因为它允许您指定自己的方式来处理"无法找到"场景。
因此,使用少量语法糖的好方法是:
1 2 | // assuming list is List<Bar>(); var item = list.SingleOrDefault(x => x.HasFoo) ?? notFound<Bar>(); |
其中notFound()是:
1 2 3 4 | T notFound< T >() { throw new InvalidOperationException("Exactly one item with foo expected, none found"); } |
在获取元素之前,我宁愿查看列表中元素的数量,而不是等待异常,然后再抛出一个新元素。
1 2 3 4 5 6 7 8 9 10 | 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,为迂腐的人添加了一行来过滤列表。 (我认为这对任何人来说都是显而易见的)
单
It returns a single specific element from a collection of elements if
element match found. An exception is thrown, if none or more than one
match found for that element in the collection.
的SingleOrDefault
It returns a single specific element from a collection of elements if
element match found. An exception is thrown, if more than one match
found for that element in the collection. A default value is returned,
if no match is found for that element in the collection.
这是示例: -
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 | 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(); } } } |
我同意Kieren Johnstone,不要等待这个非常昂贵的例外,当你多次调用这种方法时。
你是第一个代码片段甚至更昂贵,因为你等待原始的异常,而不是给自己一个新的。