Description
Motivation
In LINQ we have the Single
and SingleOrDefault
methods, but they don't tell you if it succeeded or not.
The problem with these methods is that if there is more than one element in source
(or more than one matching element in case of predicate
) they all throw a System.InvalidOperationException
. That is fine if more than one element is an error or very rare. If more than one element is common or expected then this will cause major slow downs.
Additionaly, in cases of (example: int collections), default(int) is 0, which wouldn't tell the caller whether the operation succeeded (returning the item 0) or not (returning the default 0).
Proposal
namespace System.Linq
{
public static class Enumerable
{
public static TSource Single<TSource>(this IEnumerable<TSource> source);
public static TSource Single<TSource>(this IEnumerable<TSource> source, Func<TSource, bool> predicate);
public static TSource SingleOrDefault<TSource>(this IEnumerable<TSource> source);
public static TSource SingleOrDefault<TSource>(this IEnumerable<TSource> source, Func<TSource, bool> predicate);
+ public static bool TrySingle<TSource>(this IEnumerable<TSource> source, out TSource element);
+ public static bool TrySingle<TSource>(this IEnumerable<TSource> source, Func<TSource, bool> predicate, out TSource element);
public static TSource First<TSource>(this IEnumerable<TSource> source);
public static TSource First<TSource>(this IEnumerable<TSource> source, Func<TSource, bool> predicate);
public static TSource FirstOrDefault<TSource>(this IEnumerable<TSource> source);
public static TSource FirstOrDefault<TSource>(this IEnumerable<TSource> source, Func<TSource, bool> predicate);
+ public static bool TryFirst<TSource>(this IEnumerable<TSource> source, out TSource element);
+ public static bool TryFirst<TSource>(this IEnumerable<TSource> source, Func<TSource, bool> predicate, out TSource element);
public static TSource Last<TSource>(this IEnumerable<TSource> source);
public static TSource Last<TSource>(this IEnumerable<TSource> source, Func<TSource, bool> predicate);
public static TSource LastOrDefault<TSource>(this IEnumerable<TSource> source);
public static TSource LastOrDefault<TSource>(this IEnumerable<TSource> source, Func<TSource, bool> predicate);
+ public static bool TryLast<TSource>(this IEnumerable<TSource> source, out TSource element);
+ public static bool TryLast<TSource>(this IEnumerable<TSource> source, Func<TSource, bool> predicate, out TSource element);
public static TSource ElementAt<TSource>(this IEnumerable<TSource> source, int index);
public static TSource ElementAtOrDefault<TSource>(this IEnumerable<TSource> source, int index);
+ public static bool TryElementAt<TSource>(this IEnumerable<TSource> source, int index, out TSource element);
}
public static class Queryable
{
public static TSource Single<TSource>(this IQueryable<TSource> source);
public static TSource Single<TSource>(this IQueryable<TSource> source, Expression<Func<TSource, bool>> predicate);
public static TSource SingleOrDefault<TSource>(this IQueryable<TSource> source);
public static TSource SingleOrDefault<TSource>(this IQueryable<TSource> source, Expression<Func<TSource, bool>> predicate);
+ public static (bool success, T value) TrySingle<T>(this IQueryable<T> source);
+ public static (bool success, T value) TrySingle<T>(this IQueryable<T> source, Expression<Func<TSource, bool>> predicate);
+ public static bool TrySingle<TSource>(this IQueryable<TSource> source, out TSource element);
+ public static bool TrySingle<TSource>(this IQueryable<TSource> source, Expression<Func<TSource, bool>> predicate, out TSource element);
public static TSource First<TSource>(this IQueryable<TSource> source);
public static TSource First<TSource>(this IQueryable<TSource> source, Expression<Func<TSource, bool>> predicate);
public static TSource FirstOrDefault<TSource>(this IQueryable<TSource> source);
public static TSource FirstOrDefault<TSource>(this IQueryable<TSource> source, Expression<Func<TSource, bool>> predicate);
+ public static (bool success, T value) TryFirst<T>(this IQueryable<T> source);
+ public static (bool success, T value) TryFirst<T>(this IQueryable<T> source, Expression<Func<TSource, bool>> predicate);
+ public static bool TryFirst<TSource>(this IQueryable<TSource> source, out TSource element);
+ public static bool TryFirst<TSource>(this IQueryable<TSource> source, Expression<Func<TSource, bool>> predicate, out TSource element);
public static TSource Last<TSource>(this IQueryable<TSource> source);
public static TSource Last<TSource>(this IQueryable<TSource> source, Expression<Func<TSource, bool>> predicate);
public static TSource LastOrDefault<TSource>(this IQueryable<TSource> source);
public static TSource LastOrDefault<TSource>(this IQueryable<TSource> source, Expression<Func<TSource, bool>> predicate);
+ public static (bool success, T value) TryLast<T>(this IQueryable<T> source);
+ public static (bool success, T value) TryLast<T>(this IQueryable<T> source, Expression<Func<TSource, bool>> predicate);
+ public static bool TryLast<TSource>(this IQueryable<TSource> source, out TSource element);
+ public static bool TryLast<TSource>(this IQueryable<TSource> source, Expression<Func<TSource, bool>> predicate, out TSource element);
public static TSource ElementAt<TSource>(this IQueryable<TSource> source, int index);
public static TSource ElementAtOrDefault<TSource>(this IQueryable<TSource> source, int index);
+ public static (bool success, T value) TryElementAt<T>(this IQueryable<T> source, int index);
+ public static bool TryElementAt<TSource>(this IQueryable<TSource> source, int index, out TSource element);
}
}
Original
I'd like to propose a new LINQ method: TrySingle
. The idea is pretty much the same as the existing Single
method except it does not throw an exception when there is not only a single item in the sequence.
To do this I made to modifications to the Single
contract:
- First the method returns
bool
.true
if the sequence contains exactly one item,false
otherwise. - I added an out parameter of type
TSource
. This will "hold" the single element on success. On failure it is filled withdefault(TSource)
.
I have found this method very useful in my own code and if accepted can quickly put together a pull request.