C#の非同期でthen()とかcatch()とか書けたらどうなるの……という、あくまで実験です。
TaskScheduler
のことは何も考慮していません。
だからたぶん本番コードに投入しないほうがいいですよ。特にGUIなコードでは。
<追記>やはりこのコードではだめみたい。TaskScheduler
の問題ではなく、ContinueWith
でOnlyOnRanToCompletion
を指定して得られたTask
に対して、さらにContinueWith
でNotOnRanToCompletion
を指定したとしても、もともとのTaskの例外は拾えないようです。</追記>
最初に、使用例を示します。
Program.cs
using System;
using System.Threading.Tasks;
class Program
{
static void Main(string[] args)
{
Foo()
.Then(() =>
{
Console.WriteLine("Success!");
return "succeeded";
},
ex =>
{
Console.WriteLine("Failure!");
Console.WriteLine(ex == null ? "Canceled" : ex.ToString());
return "failed";
})
.Then(str => Console.WriteLine(str))
.Wait();
}
static async Task Foo()
{
await Task.Delay(1000);
throw new TaskCanceledException("cancel test");
}
}
次に、実装を示します。退屈なコードがただ並ぶだけですよ…
TaskEx.cs
using System;
using System.Threading.Tasks;
static class TaskEx
{
#region T -> TResult
public static Task<TResult> Then<T, TResult>(this Task<T> self, Func<T, TResult> onFulfilled)
{
return self
.ContinueWith(t => onFulfilled(t.Result), TaskContinuationOptions.OnlyOnRanToCompletion);
}
public static Task<TResult> Then<T, TResult>(this Task<T> self, Func<T, Task<TResult>> onFulfilled)
{
return self
.ContinueWith(t => onFulfilled(t.Result), TaskContinuationOptions.OnlyOnRanToCompletion)
.Unwrap();
}
public static Task<TResult> Then<T, TResult>(this Task<T> self, Func<T, TResult> onFulfilled, Func<AggregateException, TResult> onRejected)
{
return self
.ContinueWith(t => onFulfilled(t.Result), TaskContinuationOptions.OnlyOnRanToCompletion)
.ContinueWith(t => onRejected(t.Exception), TaskContinuationOptions.NotOnRanToCompletion);
}
public static Task<TResult> Then<T, TResult>(this Task<T> self, Func<T, Task<TResult>> onFulfilled, Func<AggregateException, Task<TResult>> onRejected)
{
return self
.ContinueWith(t => onFulfilled(t.Result), TaskContinuationOptions.OnlyOnRanToCompletion)
.Unwrap()
.ContinueWith(t => onRejected(t.Exception), TaskContinuationOptions.NotOnRanToCompletion)
.Unwrap();
}
public static Task<TResult> Catch<T, TResult>(this Task<T> self, Func<AggregateException, TResult> onRejected)
{
return self
.ContinueWith(t => onRejected(t.Exception), TaskContinuationOptions.NotOnRanToCompletion);
}
public static Task<TResult> Catch<T, TResult>(this Task<T> self, Func<AggregateException, Task<TResult>> onRejected)
{
return self
.ContinueWith(t => onRejected(t.Exception), TaskContinuationOptions.NotOnRanToCompletion)
.Unwrap();
}
#endregion
#region T -> void
public static Task Then<T>(this Task<T> self, Action<T> onFulfilled)
{
return self
.ContinueWith(t => onFulfilled(t.Result), TaskContinuationOptions.OnlyOnRanToCompletion);
}
public static Task Then<T>(this Task<T> self, Func<T, Task> onFulfilled)
{
return self
.ContinueWith(t => onFulfilled(t.Result), TaskContinuationOptions.OnlyOnRanToCompletion)
.Unwrap();
}
public static Task Then<T>(this Task<T> self, Action<T> onFulfilled, Action<AggregateException> onRejected)
{
return self
.ContinueWith(t => onFulfilled(t.Result), TaskContinuationOptions.OnlyOnRanToCompletion)
.ContinueWith(t => onRejected(t.Exception), TaskContinuationOptions.NotOnRanToCompletion);
}
public static Task Then<T>(this Task<T> self, Func<T, Task> onFulfilled, Func<AggregateException, Task> onRejected)
{
return self
.ContinueWith(t => onFulfilled(t.Result), TaskContinuationOptions.OnlyOnRanToCompletion)
.Unwrap()
.ContinueWith(t => onRejected(t.Exception), TaskContinuationOptions.NotOnRanToCompletion)
.Unwrap();
}
public static Task Catch<T>(this Task<T> self, Action<AggregateException> onRejected)
{
return self.ContinueWith(t => onRejected(t.Exception), TaskContinuationOptions.NotOnRanToCompletion);
}
public static Task Catch<T>(this Task<T> self, Func<AggregateException, Task> onRejected)
{
return self
.ContinueWith(t => onRejected(t.Exception), TaskContinuationOptions.NotOnRanToCompletion)
.Unwrap();
}
#endregion
#region void -> T
public static Task<TResult> Then<TResult>(this Task self, Func<TResult> onFulfilled)
{
return self
.ContinueWith(t => onFulfilled(), TaskContinuationOptions.OnlyOnRanToCompletion);
}
public static Task<TResult> Then<TResult>(this Task self, Func<Task<TResult>> onFulfilled)
{
return self
.ContinueWith(t => onFulfilled(), TaskContinuationOptions.OnlyOnRanToCompletion)
.Unwrap();
}
public static Task<TResult> Then<TResult>(this Task self, Func<TResult> onFulfilled, Func<AggregateException, TResult> onRejected)
{
return self
.ContinueWith(t => onFulfilled(), TaskContinuationOptions.OnlyOnRanToCompletion)
.ContinueWith(t => onRejected(t.Exception), TaskContinuationOptions.NotOnRanToCompletion);
}
public static Task<TResult> Then<TResult>(this Task self, Func<Task<TResult>> onFulfilled, Func<AggregateException, Task<TResult>> onRejected)
{
return self
.ContinueWith(t => onFulfilled(), TaskContinuationOptions.OnlyOnRanToCompletion)
.Unwrap()
.ContinueWith(t => onRejected(t.Exception), TaskContinuationOptions.NotOnRanToCompletion)
.Unwrap();
}
public static Task<TResult> Catch<TResult>(this Task self, Func<AggregateException, TResult> onRejected)
{
return self
.ContinueWith(t => onRejected(t.Exception), TaskContinuationOptions.NotOnRanToCompletion);
}
public static Task<TResult> Catch<TResult>(this Task self, Func<AggregateException, Task<TResult>> onRejected)
{
return self
.ContinueWith(t => onRejected(t.Exception), TaskContinuationOptions.NotOnRanToCompletion)
.Unwrap();
}
#endregion
#region void -> void
public static Task Then(this Task self, Action onFulfilled)
{
return self
.ContinueWith(t => onFulfilled(), TaskContinuationOptions.OnlyOnRanToCompletion);
}
public static Task Then(this Task self, Func<Task> onFulfilled)
{
return self
.ContinueWith(t => onFulfilled(), TaskContinuationOptions.OnlyOnRanToCompletion)
.Unwrap();
}
public static Task Then(this Task self, Action onFulfilled, Action<AggregateException> onRejected)
{
return self
.ContinueWith(t => onFulfilled(), TaskContinuationOptions.OnlyOnRanToCompletion)
.ContinueWith(t => onRejected(t.Exception), TaskContinuationOptions.NotOnRanToCompletion);
}
public static Task Then(this Task self, Func<Task> onFulfilled, Func<AggregateException, Task> onRejected)
{
return self
.ContinueWith(t => onFulfilled(), TaskContinuationOptions.OnlyOnRanToCompletion)
.Unwrap()
.ContinueWith(t => onRejected(t.Exception), TaskContinuationOptions.NotOnRanToCompletion)
.Unwrap();
}
public static Task Catch(this Task self, Action<AggregateException> onRejected)
{
return self
.ContinueWith(t => onRejected(t.Exception), TaskContinuationOptions.NotOnRanToCompletion);
}
public static Task Catch(this Task self, Func<AggregateException, Task> onRejected)
{
return self
.ContinueWith(t => onRejected(t.Exception), TaskContinuationOptions.NotOnRanToCompletion)
.Unwrap();
}
#endregion
}