private static FixedSingleResultOpt <TSource> WhereSingleImmediate <TSource>(this IEnumerable <TSource> source, Func <TSource, bool> predicate) { const bool usingPredicate = true; if (source is IOpt <TSource> sourceOpt) { return(SingleResultOpt.GetFixedFromOpt(sourceOpt.ToFixed().WhereRaw(predicate), usingPredicate)); } else { bool hasValue = false; TSource value = default; foreach (var element in source) { if (predicate(element)) { if (hasValue) { return(SingleResultOpt.GetFixedMoreThanOne <TSource>(usingPredicate)); } else { value = element; hasValue = true; } } } return(SingleResultOpt.GetFixedFromValue(hasValue, value, usingPredicate)); } }
private static FixedSingleResultOpt <TSource> WhereSingleImmediate <TSource>(this IEnumerable <TSource> source) { const bool usingPredicate = false; if (source is IOpt <TSource> sourceOpt) { return(SingleResultOpt.GetFixedFromOpt(sourceOpt.ToFixed(), usingPredicate)); } else if (source is IList <TSource> sourceList) { if (sourceList.Count > 1) { return(SingleResultOpt.GetFixedMoreThanOne <TSource>(usingPredicate)); } else if (sourceList.Count == 0) { return(SingleResultOpt.GetFixedZero <TSource>(usingPredicate)); } else { return(SingleResultOpt.GetFixedOne(usingPredicate, sourceList[0])); } } else { using (var sourceEnumerator = source.GetEnumerator()) { return(EnumeratorSingleToFixedOpt(sourceEnumerator, usingPredicate)); } } }
/// <summary> /// Returns an option that contains the only element of a sequence. /// </summary> /// <remarks> /// <para>This method is an option-returning counterpart to <see cref="Enumerable.SingleOrDefault{TSource}(IEnumerable{TSource})"/>.</para> /// <para> /// This method is implemented using deferred execution; the query represented by this method /// is not performed until the contents of the returned option are resolved, such as by enumeration. /// </para> /// <para> /// This method returns a single result option ( <see cref="ISingleResultOpt{T}"/>). If /// <paramref name="source"/> contains zero elements or one element, this option can be used /// as an ordinary option ( <see cref="IOpt{T}"/>). If <paramref name="source"/> contains /// more than one element, enumerating or resolving this option as an ordinary option will /// result in an <see cref="InvalidOperationException"/> being thrown. Methods such as <see /// cref="SingleResultOpt.Match{TSource, TResult}(ISingleResultOpt{TSource}, Func{TResult}, /// Func{TSource, TResult}, Func{TResult})"/> and <see /// cref="SingleResultOpt.ReplaceIfMoreThanOne{TSource}(ISingleResultOpt{TSource}, /// IOpt{TSource})"/> can be used to handle the more-than-one case without exceptions. /// </para> /// </remarks> /// <typeparam name="TSource">The type of the elements of <paramref name="source"/>.</typeparam> /// <param name="source">A source sequence.</param> /// <returns> /// An option containing the only element of <paramref name="source"/>, or an empty option if /// <paramref name="source"/> is empty. /// </returns> /// <exception cref="ArgumentNullException"><paramref name="source"/> is <see langword="null"/>.</exception> /// <exception cref="InvalidOperationException"> /// Returned single result was resolved as an ordinary option, but <paramref name="source"/> /// contained more than one element. /// </exception> public static ISingleResultOpt <TSource> WhereSingle <TSource>(this IEnumerable <TSource> source) { if (source == null) { throw new ArgumentNullException(nameof(source)); } return(SingleResultOpt.DeferRaw(() => source.WhereSingleImmediate())); }
private static FixedSingleResultOpt <TSource> EnumeratorSingleToFixedOpt <TSource>(IEnumerator <TSource> source, bool usingPredicate) { if (source.MoveNext()) { TSource value = source.Current; if (source.MoveNext()) { // More than one element return(SingleResultOpt.GetFixedMoreThanOne <TSource>(usingPredicate)); } else { // Exactly one element return(SingleResultOpt.GetFixedOne(usingPredicate, value)); } } else { // No elements return(SingleResultOpt.GetFixedZero <TSource>(usingPredicate)); } }