/
Single.cs
144 lines (128 loc) · 6.08 KB
/
Single.cs
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
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis;
namespace System.Linq
{
public static partial class Enumerable
{
public static TSource Single<TSource>(this IEnumerable<TSource> source)
{
TSource? single = source.TryGetSingle(out bool found);
if (!found)
{
ThrowHelper.ThrowNoElementsException();
}
return single!;
}
public static TSource Single<TSource>(this IEnumerable<TSource> source, Func<TSource, bool> predicate)
{
TSource? single = source.TryGetSingle(predicate, out bool found);
if (!found)
{
ThrowHelper.ThrowNoMatchException();
}
return single!;
}
public static TSource? SingleOrDefault<TSource>(this IEnumerable<TSource> source)
=> source.TryGetSingle(out _);
/// <summary>Returns the only element of a sequence, or a default value if the sequence is empty; this method throws an exception if there is more than one element in the sequence.</summary>
/// <typeparam name="TSource">The type of the elements of <paramref name="source" />.</typeparam>
/// <param name="source">An <see cref="IEnumerable{T}" /> to return the single element of.</param>
/// <param name="defaultValue">The default value to return if the sequence is empty.</param>
/// <returns>The single element of the input sequence, or <paramref name="defaultValue" /> if the sequence contains no elements.</returns>
/// <exception cref="ArgumentNullException"><paramref name="source" /> is <see langword="null" />.</exception>
/// <exception cref="InvalidOperationException">The input sequence contains more than one element.</exception>
public static TSource SingleOrDefault<TSource>(this IEnumerable<TSource> source, TSource defaultValue)
{
var single = source.TryGetSingle(out bool found);
return found ? single! : defaultValue;
}
public static TSource? SingleOrDefault<TSource>(this IEnumerable<TSource> source, Func<TSource, bool> predicate)
=> source.TryGetSingle(predicate, out _);
/// <summary>Returns the only element of a sequence that satisfies a specified condition or a default value if no such element exists; this method throws an exception if more than one element satisfies the condition.</summary>
/// <typeparam name="TSource">The type of the elements of <paramref name="source" />.</typeparam>
/// <param name="source">An <see cref="IEnumerable{T}" /> to return a single element from.</param>
/// <param name="predicate">A function to test an element for a condition.</param>
/// <param name="defaultValue">The default value to return if the sequence is empty.</param>
/// <returns>The single element of the input sequence that satisfies the condition, or <paramref name="defaultValue" /> if no such element is found.</returns>
/// <exception cref="ArgumentNullException"><paramref name="source" /> or <paramref name="predicate" /> is <see langword="null" />.</exception>
/// <exception cref="InvalidOperationException">More than one element satisfies the condition in <paramref name="predicate" />.</exception>
public static TSource SingleOrDefault<TSource>(this IEnumerable<TSource> source, Func<TSource, bool> predicate, TSource defaultValue)
{
var single = source.TryGetSingle(predicate, out bool found);
return found ? single! : defaultValue;
}
private static TSource? TryGetSingle<TSource>(this IEnumerable<TSource> source, out bool found)
{
if (source is null)
{
ThrowHelper.ThrowArgumentNullException(ExceptionArgument.source);
}
if (source is IList<TSource> list)
{
switch (list.Count)
{
case 0:
found = false;
return default;
case 1:
found = true;
return list[0];
}
}
else
{
using (IEnumerator<TSource> e = source.GetEnumerator())
{
if (!e.MoveNext())
{
found = false;
return default;
}
TSource result = e.Current;
if (!e.MoveNext())
{
found = true;
return result;
}
}
}
found = false;
ThrowHelper.ThrowMoreThanOneElementException();
return default;
}
private static TSource? TryGetSingle<TSource>(this IEnumerable<TSource> source, Func<TSource, bool> predicate, out bool found)
{
if (source == null)
{
ThrowHelper.ThrowArgumentNullException(ExceptionArgument.source);
}
if (predicate == null)
{
ThrowHelper.ThrowArgumentNullException(ExceptionArgument.predicate);
}
using (IEnumerator<TSource> e = source.GetEnumerator())
{
while (e.MoveNext())
{
TSource result = e.Current;
if (predicate(result))
{
while (e.MoveNext())
{
if (predicate(e.Current))
{
ThrowHelper.ThrowMoreThanOneMatchException();
}
}
found = true;
return result;
}
}
}
found = false;
return default;
}
}
}