// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. See License.txt in the project root for license information. using System; using System.Collections.Generic; using System.Linq; namespace System.Linq { public static partial class EnumerableEx { /// /// Creates a sequence that corresponds to the source sequence, concatenating it with the sequence resulting from calling an exception handler function in case of an error. /// /// Source sequence element type. /// Exception type to catch. /// Source sequence. /// Handler to invoke when an exception of the specified type occurs. /// Source sequence, concatenated with an exception handler result sequence in case of an error. public static IEnumerable Catch(this IEnumerable source, Func> handler) where TException : Exception { if (source == null) throw new ArgumentNullException("source"); if (handler == null) throw new ArgumentNullException("handler"); return source.Catch_(handler); } private static IEnumerable Catch_(this IEnumerable source, Func> handler) where TException : Exception { var err = default(IEnumerable); using (var e = source.GetEnumerator()) { while (true) { var b = default(bool); var c = default(TSource); try { b = e.MoveNext(); c = e.Current; } catch (TException ex) { err = handler(ex); break; } if (!b) break; yield return c; } } if (err != null) { foreach (var item in err) yield return item; } } /// /// Creates a sequence by concatenating source sequences until a source sequence completes successfully. /// /// Source sequence element type. /// Source sequences. /// Sequence that continues to concatenate source sequences while errors occur. public static IEnumerable Catch(this IEnumerable> sources) { if (sources == null) throw new ArgumentNullException("sources"); return sources.Catch_(); } /// /// Creates a sequence by concatenating source sequences until a source sequence completes successfully. /// /// Source sequence element type. /// Source sequences. /// Sequence that continues to concatenate source sequences while errors occur. public static IEnumerable Catch(params IEnumerable[] sources) { if (sources == null) throw new ArgumentNullException("sources"); return sources.Catch_(); } /// /// Creates a sequence that returns the elements of the first sequence, switching to the second in case of an error. /// /// Source sequence element type. /// First sequence. /// Second sequence, concatenated to the result in case the first sequence completes exceptionally. /// The first sequence, followed by the second sequence in case an error is produced. public static IEnumerable Catch(this IEnumerable first, IEnumerable second) { if (first == null) throw new ArgumentNullException("first"); if (second == null) throw new ArgumentNullException("second"); return new[] { first, second }.Catch_(); } private static IEnumerable Catch_(this IEnumerable> sources) { var error = default(Exception); foreach (var source in sources) { using (var e = source.GetEnumerator()) { error = null; while (true) { var b = default(bool); var c = default(TSource); try { b = e.MoveNext(); c = e.Current; } catch (Exception ex) { error = ex; break; } if (!b) break; yield return c; } if (error == null) break; } } if (error != null) throw error; } /// /// Creates a sequence whose termination or disposal of an enumerator causes a finally action to be executed. /// /// Source sequence element type. /// Source sequence. /// Action to run upon termination of the sequence, or when an enumerator is disposed. /// Source sequence with guarantees on the invocation of the finally action. public static IEnumerable Finally(this IEnumerable source, Action finallyAction) { if (source == null) throw new ArgumentNullException("source"); if (finallyAction == null) throw new ArgumentNullException("finallyAction"); return source.Finally_(finallyAction); } private static IEnumerable Finally_(this IEnumerable source, Action finallyAction) { try { foreach (var item in source) yield return item; } finally { finallyAction(); } } /// /// Creates a sequence that concatenates both given sequences, regardless of whether an error occurs. /// /// Source sequence element type. /// First sequence. /// Second sequence. /// Sequence concatenating the elements of both sequences, ignoring errors. public static IEnumerable OnErrorResumeNext(this IEnumerable first, IEnumerable second) { if (first == null) throw new ArgumentNullException("first"); if (second == null) throw new ArgumentNullException("second"); return OnErrorResumeNext_(new[] { first, second }); } /// /// Creates a sequence that concatenates the given sequences, regardless of whether an error occurs in any of the sequences. /// /// Source sequence element type. /// Source sequences. /// Sequence concatenating the elements of the given sequences, ignoring errors. public static IEnumerable OnErrorResumeNext(params IEnumerable[] sources) { if (sources == null) throw new ArgumentNullException("sources"); return OnErrorResumeNext_(sources); } /// /// Creates a sequence that concatenates the given sequences, regardless of whether an error occurs in any of the sequences. /// /// Source sequence element type. /// Source sequences. /// Sequence concatenating the elements of the given sequences, ignoring errors. public static IEnumerable OnErrorResumeNext(this IEnumerable> sources) { if (sources == null) throw new ArgumentNullException("sources"); return OnErrorResumeNext_(sources); } private static IEnumerable OnErrorResumeNext_(IEnumerable> sources) { foreach (var source in sources) { using (var innerEnumerator = source.GetEnumerator()) { while (true) { var value = default(TSource); try { if (!innerEnumerator.MoveNext()) break; value = innerEnumerator.Current; } catch { break; } yield return value; } } } } /// /// Creates a sequence that retries enumerating the source sequence as long as an error occurs. /// /// Source sequence element type. /// Source sequence. /// Sequence concatenating the results of the source sequence as long as an error occurs. public static IEnumerable Retry(this IEnumerable source) { if (source == null) throw new ArgumentNullException("source"); return new[] { source }.Repeat().Catch(); } /// /// Creates a sequence that retries enumerating the source sequence as long as an error occurs, with the specified maximum number of retries. /// /// Source sequence element type. /// Source sequence. /// Maximum number of retries. /// Sequence concatenating the results of the source sequence as long as an error occurs. public static IEnumerable Retry(this IEnumerable source, int retryCount) { if (source == null) throw new ArgumentNullException("source"); if (retryCount < 0) throw new ArgumentOutOfRangeException("retryCount"); return new[] { source }.Repeat(retryCount).Catch(); } } }