昨日、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> … }