Generalized async return types (Task-like) の最小実装

## はじめに
C# 7.0 で導入された [_Generalized async return types_](https://docs.microsoft.com/ja-jp/dotnet/csharp/whats-new/csharp-7#generalized-async-return-types)（_Task-like_、_arbitrary async returns_、_Async Task Type_ とも）。

[ValueTask](https://docs.microsoft.com/ja-jp/dotnet/csharp/programming-guide/concepts/async/async-return-types#generalized-async-return-types-and-valuetaskt) を使用する事でその恩恵を受ける事ができますが、その仕組みは汎化されていますので、独自の _Task-like_ を定義する事も可能です。
（かといって、実務で不必要に _Task-like_ を作ると複雑度が上がるだけなのでお勧めしませんが）

この記事では、理解を深める事を主目的として、独自の _Task-like_ の最小実装を示します。

## GetAwaiter があれば良い？

[公式リファレンス](https://docs.microsoft.com/ja-jp/dotnet/csharp/language-reference/keywords/async#return-types)を参照すると以下のように記されています。簡単そうですね。

> ## 戻り値の型
非同期メソッドの戻り値の型を次に示します。

> - Task
- Task\<TResult>
- void: イベント ハンドラーに対してのみ使用します。
- C# 7 以降、アクセス可能な `GetAwaiter` を持つ任意の型です。 `System.Threading.Tasks.ValueTask<TResult>` 型はこの実装例で、 NuGet パッケージ `System.Threading.Tasks.Extensions` を追加することで使用できます。

> （中略）

> C# 7 以降、`GetAwaiter` メソッドを持つ別の型 (通常は値の型) を返して、コードのパフォーマンスが重要なセクションでメモリ割り当てを最小限に抑えます。

「これだけ？」と思わずにはいられない情報量ですが、百聞は一見に如かず、実際にコードを記述してみましょう。
ここで `GetValueAsync` は「非同期に 1 秒待って `123` を返す」だけのメソッドです。

```csharp
public static async MyTask<int> GetValueAsync() {
    await Task.Delay(1000);
    return 123;
}

public struct MyTask<T> {
    public MyTask(T result) => Result = result;
    
    public T Result { get; }

    public TaskAwaiter<T> GetAwaiter() => Task.FromResult(Result).GetAwaiter();
}
```

`MyTask` を _Task-like_ とする為、シンプルな `TaskAwaiter` を返す `GetAwaiter` を実装してビルドします。
すると、

```
CS1983 The return type of an async method must be void, Task or Task<T>
```
となりました。

アレ？

## GetAwaiter とは
> アクセス可能な `GetAwaiter`

を持たせただけでは _Task-like_ として認められませんでした。

一方で、以下のコードは実行できます。
（async の戻り値の型を `Task` にしてしまっているので、`MyTask` を挟む意味は全くないのですが）

```csharp
public static async /*My*/Task<int> GetValueAsync() {
    await Task.Delay(1000);
    //return 123;
    return await new MyTask<int>(123);
}

public struct MyTask<T> {
    public MyTask(T result) => Result = result;
    
    public T Result { get; }

    public TaskAwaiter<T> GetAwaiter() => Task.FromResult(Result).GetAwaiter();
}
```

つまり、**GetAwaiter は任意の型を await 可能にする為に必要** であって、「async の戻り値の型」には必須ではないのですね。

「C# 言語仕様」を紐解くと、「7.7.7.1 待機可能な式」に `GetAwaiter` の事が記載されています（Web 版は[こちら](https://docs.microsoft.com/ja-jp/dotnet/csharp/language-reference/language-specification/expressions#awaitable-expressions)）。

> await 式のタスクは、_**待機可能**_である必要があります。式 `t` は、次のいずれかに該当する場合、待機可能です。
>
- `t` is of compile time type `dynamic`
- `t` has an accessible instance or extension method called `GetAwaiter` with no parameters and no type parameters, and a return type `A` for which all of the following hold:

## 本当に必要なのは AsyncMethodBuilder 属性
では「async の戻り値の型」に必要なモノって一体なんなのでしょうか？

その答えは [Async Task Types in C# - dotnet/roslyn](https://github.com/dotnet/roslyn/blob/master/docs/features/task-types.md) にありました。

> A _task type_ is a `class` or `struct` with an associated _builder type_ identified with `System.Runtime.CompilerServices.AsyncMethodBuilderAttribute`.

上記から `AsyncMethodBuilder` 属性と _builder type_ なるものが必要だと判ります。

_builder type_ が何かというと、

> The _builder type_ is a `class` or `struct` that corresponds to the specific _task type_. The _builder type_ has the following public methods. For non-generic _builder types_, `SetResult()` has no parameters.

とあり、_builder type_ は下記の形式のメンバーを持っていると書かれています。

```csharp
class MyTaskMethodBuilder<T>
{
    public static MyTaskMethodBuilder<T> Create();

    public void Start<TStateMachine>(ref TStateMachine stateMachine)
        where TStateMachine : IAsyncStateMachine;

    public void SetStateMachine(IAsyncStateMachine stateMachine);
    public void SetException(Exception exception);
    public void SetResult(T result);

    public void AwaitOnCompleted<TAwaiter, TStateMachine>(
        ref TAwaiter awaiter, ref TStateMachine stateMachine)
        where TAwaiter : INotifyCompletion
        where TStateMachine : IAsyncStateMachine;
    public void AwaitUnsafeOnCompleted<TAwaiter, TStateMachine>(
        ref TAwaiter awaiter, ref TStateMachine stateMachine)
        where TAwaiter : ICriticalNotifyCompletion
        where TStateMachine : IAsyncStateMachine;

    public MyTask<T> Task { get; }
}
```

## 最小実装
上記を踏まえ、改めて `MyTask` を実装してみます。

```csharp:MyTask.cs
[AsyncMethodBuilder(typeof(MyTaskMethodBuilder<>))]
public struct MyTask<T> {
    private Task<T> _task;

    public MyTask(Task<T> task) => _task = task;

    public T Result => _task.Result;
}
```
```csharp:MyTaskMethodBuilder.cs
public struct MyTaskMethodBuilder<T> {
    private AsyncTaskMethodBuilder<T> _methodBuilder;

    public static MyTaskMethodBuilder<T> Create() =>
        new MyTaskMethodBuilder<T> { _methodBuilder = AsyncTaskMethodBuilder<T>.Create() };

    public void Start<TStateMachine>(ref TStateMachine stateMachine)
        where TStateMachine : IAsyncStateMachine {
        _methodBuilder.Start(ref stateMachine);
    }

    public void SetStateMachine(IAsyncStateMachine stateMachine) {
        _methodBuilder.SetStateMachine(stateMachine);
    }
    public void SetException(Exception exception) {
        _methodBuilder.SetException(exception);
    }
    public void SetResult(T result) {
        _methodBuilder.SetResult(result);
    }

    public void AwaitOnCompleted<TAwaiter, TStateMachine>(
        ref TAwaiter awaiter, ref TStateMachine stateMachine)
        where TAwaiter : INotifyCompletion
        where TStateMachine : IAsyncStateMachine {
        _methodBuilder.AwaitOnCompleted(ref awaiter, ref stateMachine);
    }
    public void AwaitUnsafeOnCompleted<TAwaiter, TStateMachine>(
        ref TAwaiter awaiter, ref TStateMachine stateMachine)
        where TAwaiter : ICriticalNotifyCompletion
        where TStateMachine : IAsyncStateMachine {
        _methodBuilder.AwaitUnsafeOnCompleted(ref awaiter, ref stateMachine);
    }

    public MyTask<T> Task => new MyTask<T>(_methodBuilder.Task);
}
```

`ValueTask` の実装をベースに記述していますので、興味のある方は[ソースも見てみて下さい](https://github.com/dotnet/corefx/blob/master/src/System.Threading.Tasks.Extensions/src/System/Threading/Tasks/ValueTask.cs)。
（この実装だと結局 `Task` が生成されてしまうので、パフォーマンスには寄与しませんし。ただのラッパー……）

もし `System.Runtime.CompilerServices.AsyncMethodBuilderAttribute` が無い環境であれば、以下を定義して下さい。

```csharp:AsyncMethodBuilderAttribute.cs
namespace System.Runtime.CompilerServices {
    public sealed class AsyncMethodBuilderAttribute : Attribute {
        public AsyncMethodBuilderAttribute(Type builderType) {
            BuilderType = builderType;
        }
        
        public Type BuilderType { get; }
    }
}
```

これで準備は整いました。
実際に使ってみましょう。

```csharp:Program.cs
static void Main() {
    var result = GetValueAsync().Result;
    Console.WriteLine(result);
}

public static async MyTask<int> GetValueAsync() {
    await Task.Delay(1000);
    return 123;
}
```

今度はビルドも成功し、標準出力に `123` と出力されました！

## サンプルコード
上記コードを LinqPad ファイルとしてまとめましたので、手っ取り早く確認したい方はこちらをご参照ください。
[C# 7.0 Generalized async return types - Minimum Implementation Code for LinqPad 5](https://gist.github.com/in-async/1c1ff9600705e3f0f5f10151e44d28c4)


## GetAwaiter？ 知らない子ですね
別に `GetAwaiter` が無くても _Generalized async return types_ は実現できましたね！

\--- 完 ---

でも良いのですが、_Task-like_ という場合はきちんと await 可能にしないとタスクっぽくないですよね。
という事で `MyTask` に `GetAwaiter` を足します。

```csharp:MyTask.cs
[AsyncMethodBuilder(typeof(MyTaskMethodBuilder<>))]
public struct MyTask<T> {
    ...
    public TaskAwaiter<T> GetAwaiter() => _task.GetAwaiter();
}
```

こうする事[^1]で、async の戻り値を await する事ができるようになりました。

[^1]: `ValueTask` のようにパフォーマンスを出す為には必然的に `Awaiter` も実装する事になるのですが、本稿の趣旨から逸れてしまうので割愛。

```csharp:Program.cs
static async Task Main() {
    var result = await GetValueAsync();
    Console.WriteLine(result);
}

public static async MyTask<int> GetValueAsync() {
    await Task.Delay(1000);
    return 123;
}
```

チャンチャン

## See also
[async (C# リファレンス) | Microsoft Docs](https://docs.microsoft.com/ja-jp/dotnet/csharp/language-reference/keywords/async#return-types)
[非同期の戻り値の型 (C#) | Microsoft Docs](https://docs.microsoft.com/ja-jp/dotnet/csharp/programming-guide/concepts/async/async-return-types)
[Async Task Types in C# - dotnet/roslyn](https://github.com/dotnet/roslyn/blob/master/docs/features/task-types.md)
[corefx/ValueTask.cs at master · dotnet/corefx](https://github.com/dotnet/corefx/blob/master/src/System.Threading.Tasks.Extensions/src/System/Threading/Tasks/ValueTask.cs)
[任意の型を戻り値に持つ非同期メソッド - xin9le.net](http://blog.xin9le.net/entry/2016/07/28/212318)