Example:
myEnumerable.Select(a => ThisMethodMayThrowExceptions(a));
How to make it work even if it throws exceptions? Like a try catch block with a default value case an exceptions is thrown...
Example:
myEnumerable.Select(a => ThisMethodMayThrowExceptions(a));
How to make it work even if it throws exceptions? Like a try catch block with a default value case an exceptions is thrown...
/// <summary>
/// Catch the exception and then omit the value if exception thrown.
/// </summary>
public static IEnumerable<T> Catch<T>(this IEnumerable<T> source, Action<Exception> action = null)
{
return Catch<T, Exception>(source, action);
}
/// <summary>
/// Catch the exception and then omit the value if exception thrown.
/// </summary>
public static IEnumerable<T> Catch<T, TException>(this IEnumerable<T> source, Action<TException> action = null) where TException : Exception
{
using var enumerator = source.GetEnumerator();
while(true)
{
T item;
try
{
if (!enumerator.MoveNext())
break;
item = enumerator.Current;
}
catch (TException e)
{
action?.Invoke(e);
continue;
}
yield return item;
}
}
/// <summary>
/// Catch the exception and then return the default value.
/// </summary>
public static IEnumerable<T> Catch<T>(this IEnumerable<T> source, Func<Exception, T> defaultValue)
{
return Catch<T, Exception>(source, defaultValue);
}
/// <summary>
/// Catch the exception and then return the default value.
/// </summary>
public static IEnumerable<T> Catch<T, TException>(this IEnumerable<T> source, Func<TException, T> defaultValue) where TException : Exception
{
using var enumerator = source.GetEnumerator();
while(true)
{
T item;
try
{
if (!enumerator.MoveNext())
break;
item = enumerator.Current;
}
catch (TException e)
{
item = defaultValue(e);
}
yield return item;
}
}
Usage:
myEnumerable.Select(a => ThisMethodMayThrowExceptions(a)).Catch(e => Console.WriteLine(e.Message));
myEnumerable.Select(a => ThisMethodMayThrowExceptions(a)).Catch(e => default);
myEnumerable.Select(a => ThisMethodMayThrowExceptions(a)).Catch();
myEnumerable.Select(a => ThisMethodMayThrowExceptions(a)).Catch(((InvalidOperationException) e) => Console.WriteLine(e.Message));
myEnumerable.Select(a => ThisMethodMayThrowExceptions(a)).Catch(((InvalidOperationException) e) => default);
I've created small library for this purposes. It's supported exception handling for Select, SelectMany and Where operators. Usage example:
var target = source.AsCatchable() // move source to catchable context
.Select(v => int.Parse(v)) // can throw an exception
.Catch((Exception e) => { /* some action */ }, () => -1)
.Select(v => v * 2)
.ToArray();
which equivalet to
var target = source
.Select(v =>
{
try
{
return int.Parse(v);
}
catch (Exception)
{
return -1; // some default behaviour
}
})
.Select(v => v * 2)
.ToArray();
It's also possible to handle several types of exceptions
var collection = Enumerable.Range(0, 5)
.AsCatchable()
.Select(v =>
{
if (v == 2) throw new ArgumentException("2");
if (v == 3) throw new InvalidOperationException("3");
return v.ToString();
})
.Catch((ArgumentException e) => { /* */ }, v => "ArgumentException")
.Catch((InvalidOperationException e) => { /* */ }, v => "InvalidOperationException")
.Catch((Exception e) => { /* */ })
.ToList();
wrap ThisMethodMayThrowExceptions with a new function,
myEnumerable.Select(a => ThisMethodMayThrowExceptions(a)); //old
myEnumerable.Select(a => tryCall(ThisMethodMayThrowExceptions,a)); //new
it's a generic function, with try catch inside.
T2 tryCall<T1, T2>(Func<T1, T2> fn, T1 input, T2 exceptionValue = default)
{
try
{
return fn(input);
}
catch
{
return exceptionValue;
}
}
var numbers = new [] {"1", "a"};
numbers.Select(n => tryCall(double.Parse, n)); //1, 0
numbers.Select(n => tryCall(double.Parse, n, double.NaN)); //1, NaN
I believe this is the correct answer since it allows you to handle the troublesome item and it is filtered from the end result.
public static class IEnumerableExtensions {
public static IEnumerable<TResult> SelectWithExceptionHandler<T, TResult>(this IEnumerable<T> enumerable, Func<T, TResult> func, Action<T, Exception> handler)
=> enumerable
.Select(x => {
try {
return (true, func(x));
} catch (Exception error) {
handler(x, error);
}
return default;
})
.Where(x => x.Item1)
.Select(x => x.Item2);
}