[C#]TaskクラスをJavaScriptのPromiseっぽく使ってみる実験

C#の非同期でthen()とかcatch()とか書けたらどうなるの……という、あくまで実験です。
`TaskScheduler`のことは何も考慮していません。
だからたぶん本番コードに投入しないほうがいいですよ。特にGUIなコードでは。

＜追記＞やはりこのコードではだめみたい。`TaskScheduler`の問題ではなく、`ContinueWith`で`OnlyOnRanToCompletion`を指定して得られた`Task`に対して、さらに`ContinueWith`で`NotOnRanToCompletion`を指定したとしても、もともとのTaskの例外は拾えないようです。＜／追記＞

最初に、使用例を示します。

```csharp: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");
    }
}
```

次に、実装を示します。退屈なコードがただ並ぶだけですよ…

```csharp: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
}
```