26日前のDesign Notesを1件紹介し忘れてた。 だって、Madsってば、最初にAgendaだけのページ作って後から埋めるんだもん… 全部埋まってから見ようと思ったら忘れてた…
提案されてる新機能のうちのいくつかに関する現状の着地点についてと、async streamに感するブレインストーミングを始めたという2つの議題。
Bestest betterness
オーパーロードの解決ルール(一番一致度の高いやつを選ぶって意味で、betternessルール。それのよりよいの、今度こそ最良のみたいな意味でbestest)について。
仕様を過度に複雑にしないように控えてたルールを追加するかっていう内容。例えば、静的メソッドしか受け付けない場面ではインスタンスメソッドを無視してオーバーロード解決試みるとかそういうの。
bestest betternessって言葉はずっと出てるけども、具体的にどんなルール足すかの話は初めて表に出てきたかも。
private protected
現状の protected internal
が「protected または internal」なのに対して、「protected かつ internal」なアクセス制御をしたいんだけども…っていうやつ。
問題はそのためのキーワードをどうするかで、前々から決め手に欠けつつも、妥協的に private protected
を提案していたものです。妥協的なんでやっぱりさんざん批判され、他にこうしたらいいんじゃないかって案もちらほら出たものの、結局どの案も決め手に欠けて、元の提案通り private protected
にしとこうかなという雰囲気。
属性がらみ
いくつか要望が出てるの、くみ取っていくみたい。
- ジェネリックな属性: .NETランタイム的にはできそうで、言語的に今まで入れてなかった
-
コンパイル時のみの属性: Code Analyzerとか使ってメタプログラミング的に使う属性、別にコンパイル結果に残す必要ないよねということで。
- 僕なんかも割かし最近、
[Conditional("NEVER_USED_AT_RUNTIME")]
とかいうConditional属性作ったりしたこともあるんで
- 僕なんかも割かし最近、
- もっといろんな場所に属性つけたい: ラムダ式にとか
- 属性にいろんな型を渡せるように: 属性の引数に渡せるもの限られてるのをもうちょっと緩めてもいいかも
ローカル extern 関数
ローカル関数(メソッド内でメソッド定義したり)が最近実装されつつあるけども、それで extern (DllImpoert
とかで外部の関数呼び出すやつ)も使えるようにした方が、とのこと。
任意の型に対する wither
レコード型に対して、with 式(指定したプロパティだけ書き換えて、残りのプロパティは元のをコピー)みたいなのを提案中だけども、それを任意の型についてできる方法もあった方がという話。
継承とか絡むとめんどくさいそう。
あと、似たような構文で、immutable な型の作成時にオブジェクト初期化子使えるようにもしたそう。
params IEnumerable
これは一瞬 C# 6.0 に入れようかみたいな話も出てたけどもいったん流れてたもの。
単純な実装なら 6.0 にも入れれたんだけど、せっかくだからもっと効率の良い実装を模索できないかと考えてるみたい。
引数の数が少ないときに配列作ってるとメモリ的にもったいないとかそういう話。
非同期ストリーム
C# 5.0 で await
が入って、1つの値を返すだけの非同期処理はずいぶんと楽になったけども、ストリームデータを非同期に返していくのがまだなくて課題になってたやつ。
メモリのアロケーション
例えばの話、「1000件のデータをページングして、1ページ当たり100件を、10ページ分読む」みたいなの考えるとわかりやすいと思うんですけど、非同期処理が必要なのはページを読み込む10回だけで、残りの990回は同期でいいわけです。そういう状況下で、990回分のawait
のオーバーヘッドが大きかったら結局性能でなくなるのをずっと懸念していました。
これに関しては、public Task<bool> MoveNextAsync();
の戻り値の true/false をキャッシュするような最適化をかければ問題ない性能が出るんじゃないかとのこと。
LINQ
IAsyncEnumerable
を導入した場合、引数/戻り値の同期/非同期の組み合わせで、
- 同期 → 同期
- 同期 → 非同期
- 非同期 → 同期
- 非同期 → 非同期
の4パターン出てくることになります。すべてのLINQプロバイダー作者にこの4パターン分の実装を強いるのとかちょっと嫌。なので、IEnumerable
とIAsyncEnumerable
、Func<S, T>
とFunc<S, Task<T>>
の間の相互変換みたいなのを提供できないか考えたいとのこと。
言語サポート
非同期ストリームを利用する側では foreach
を使いたいわけですが、今の await
だとIAsyncEnumerable
のforeach列挙には対応できないので、何かしら foreach
ステートメントの拡張が必要。既存のforeach
と同じで行くか、foreach
の周りに async
とかawait
とか追加のキーワードつけるべきかとかで悩み中。
Go言語のチャネル
Go言語が提供してるチャネルも非同期ストリーム的なものなのでそれとの比較でいくらかの考察を。
- チャネルとselectステートメントは理解はしやすいけど、ちょっと命令的すぎないか
- select はC# 7.0向け提案でいうとパターン マッチングで似たことができそう
- チャネルの場合は典型的な利用場面では利用側(consumer側)が1人だけとか、
IAsyncEnumerable
との用途差もある
とか。