昨日、Unity 上での async/await の話と、そのついでにTaskクラスでかすぎるよとか、async/awaitはTask依存強すぎかなぁとか書いたわけですが。

ちょうどその関連の話題が2件ほど出てたので追記。

ほんと、これがもう2年早く出ていればMinimumAsyncBridgeの実装もっと楽だったのに。

Unity カスタム コルーチン

Unity 5.3 でCustomYieldInstructionってクラスが追加されて、これを実装すればコルーチン内でyield returnで返して、「待ってもらえる」型を自作できるようになるとのこと。

今まであったYieldInstructionクラスとか、メンバー何もなくて、お前何のために居るんだよ。C# 的にはそういうマーカー用の基底クラス作る文化ねぇよ。そういうのには属性使えよ。とかいう状態だったわけですが。

YieldInstructionなんて、まだ「待っててほしいかどうか」をbool 1個返すだけでカスタム処理掛けるし、awaitやらTaskクラスやらと比べたらだいぶ簡素なんだから最初からCustomYieldInstruction提供してくれればいいのに… とずっと思っていたものがやっと実装されるそうです。

任意の「Task 風の型」

一方で、C# のawaitはTaskクラスに依存しすぎって話も書いてたわけですが。ちょうどRoslynリポジトリにそれ関連のissueページが立ちました。

任意のTask風の型を非同期メソッドの戻り値にできるようにする提案。

要求はずっとある

先日も言った通り、C# 5.0の仕様が公開された瞬間からさんざん「Task以外も使いたい」って要望は出てます。

Microsoft内部都合的にいっても、UWP(WinRT)だと非同期処理にはIAsyncActionインターフェイスを使わないと行けない(Taskに対するラッパーを1段かまさないと行けなくて面倒)とかあったり。

ValueTaskなんかの話もある様子。非同期処理の結果をキャッシュとして持つにあたって、クラス(= 参照型、ヒープ圧迫)であるTaskは避けたくて、構造体(= 値型)版がほしいって言う需要があり。C#は最近、パフォーマンス向上に向けて結構攻めてたりします。

あと、これがあれば例えば、「同期コンテキスト拾わない版Task」みたいなのを作れて、ConfigureAwait(false)付けまくらないと死ぬ問題を多少マシにできるかも。

問題と対策、提案内容

そもそもC# 5.0導入当時の背景として、

  • C# 5.0を出すときに、一度は検討したし、実際プロトタイプ実装はした
  • ジェネリックな型に対して型推論がきれいにできなかった
  • それを問題視してこの実装は正式版には取り入れなかった

ってのがあります。

とりあえず、複雑な型推論はやっぱりあきらめる必要がありそう。まあ、戻り値側を見ての推論が必要な型とかはやっぱり無理。そこさえあきらめれば、現状のTaskクラス前提のasync/awaitと同程度の型推論はちゃんとできるっぽい。もしかしたら、C# 5.0当時よりも今の方が型推論が賢いので、今だからこそってのもあるのかもしれないですね。

非同期メソッドの戻り値として使いたい型(Task風の型)を作りたい場合は、以下のような型を書けとのこと。

[TaskLike(typeof(FooBuilder))] struct Foo { … }
struct FooBuilder { … similar to AsyncVoidMethodBuilder … }

[TaskLike(typeof(FooBuilder<T>))] struct Foo<T> { … }
struct FooBuilder<T> { … similar to AsyncTaskMethodBuilder<T> … }