Skip to content

Add TrySingle<TSource>(IEnumerable<TSource>, out TSource) #16362

Open
@shmuelie

Description

@shmuelie

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:

  1. First the method returns bool. true if the sequence contains exactly one item, false otherwise.
  2. I added an out parameter of type TSource. This will "hold" the single element on success. On failure it is filled with default(TSource).

I have found this method very useful in my own code and if accepted can quickly put together a pull request.

Metadata

Metadata

Assignees

No one assigned

    Labels

    api-needs-workAPI needs work before it is approved, it is NOT ready for implementationarea-System.LinqblockedIssue/PR is blocked on something - see comments

    Type

    No type

    Projects

    No projects

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions