namespace System.Threading.Tasks { using System; using System.Collections.Generic; public static class TaskExtensions { public static Task Map(this Task task, Func selector) { Task genericTask = MakeGenericTask(task); Task mappedTask = genericTask.Map(value => selector == null ? default : selector.Invoke()); return mappedTask; } public static Task Map(this Task task, Func selector) { return MapInternal(task, value => selector == null ? default : selector.Invoke(value)); } public static Task Bind(this Task task, Func> selector) { Task genericTask = MakeGenericTask(task); Task boundTask = genericTask.Bind(value => selector == null ? Task.FromResult(default(TResult)) : selector.Invoke()); return boundTask; } public static Task Bind(this Task task, Func> selector) { Task boundTask = task.Map(value => selector == null ? default : selector.Invoke(value).GetAwaiter().GetResult()); return boundTask; } private static Task MapInternal(Task task, Func selector) { if (task == null) { return Task.FromException(new InvalidOperationException("Task cannot be null")); } if (selector == null) { return Task.FromException(new ArgumentNullException(nameof(selector))); } try { return task.ContinueWith(finishedTask => { if (finishedTask.IsFaulted) { throw (Exception)finishedTask?.Exception ?? new InvalidOperationException("Task faulted without exception"); } // Only if finishedTask is Task it has a property Result ... so check for generic task before accessing property Result TValue value = finishedTask.GetType().IsGenericType ? finishedTask.Result : default; TResult result = selector.Invoke(value); return result; }); } catch (Exception exc) { if (exc is AggregateException aggregateExc) { exc = aggregateExc.Flatten().InnerExceptions[0]; } return Task.FromException(exc); } } private static Task MakeGenericTask(Task task) { try { Task genericTask = task .ContinueWith(finishedTask => { if (finishedTask.IsFaulted) { throw (Exception)finishedTask?.Exception ?? new InvalidOperationException("Task faulted without exception"); } return default(TResult); } ); return genericTask; } catch (Exception exc) { if (exc is AggregateException aggregateExc) { exc = aggregateExc.Flatten().InnerExceptions[0]; } return Task.FromException(exc); } } } }