Mads (C# コンパイラーのPM)が、去年8月辺りからの C# Language Design Notes がまとめて投稿されました。 たぶん、C# 7の作業が一段落したのかな(最近のC#チームは、実装作業が落ち着くまでドキュメントの類が放置されがち)。
ちなみに内容的には、個別のトピック用のissueページが別にあって、そっちで一通り公開済み。 当然、特に目新しい情報はなくて、まとめと履歴的な状態になっています。
かなりの分量一気に来たのであんまりしっかり読む気にもなれないけども、軽く紹介:
2016/8/24
-C# Language Design Meeting, Aug 24, 2016
- Task-likeに対して、どうやってmethod builderを取るか決めた
-
フィールド初期化子やコンストラクター初期化子内でのout varを認めたらこんな問題が出るみたいな検討したみたい
- ※結局、初期化子でのout varの利用自体、今のところは禁止してる
2016/9/6
引数の数が同じDeconstruct
メソッドがあるときに分解できないの不便だなぁとは思ってたけども。
タプル間の変換を想定してのことらしい。
2016/10/18
C# 7に入れる機能の最終確認をしていたのはこの次期みたい。
-
discardsがC# 7に入るかどうかまだ怪しい時期だったものの、分解とかout varとか、今後追加する文法では
_
を予約しておこうとはしていたみたい- ※ 結局discardsの実装、C# 7に間に合ってる
case (int, int) x:
みたいなタプルへの型スイッチは認めていない。将来「再帰パターン」って構文が入る予定で、その構文に近くなっちゃうんでその時まで保留-
ローカル関数では、理想としてはその外でできることは全部ローカル関数内でもできるようにしたかったけど、2点ほど無理なものがあった:
- コンストラクター内のローカル関数内での readonly field への書き込み
-
非同期メソッドとイテレーターになっているローカル変数内で、その外の変数の「確実な初期化」保証ができない
- https://gist.github.com/ufcpp/ad84a38cd8b5e324245bda1472de4679 ←たぶんこういう話
-
_
を使った数字区切り、数字の間でだけ認める0x1a_2b
はOKだけど、0x_1a2b
はダメ。先頭と末尾を認めない- 「区切り」なんだから、数字の間だけであるべき
- throw式は、
??
と?:
でだけ使えるように制限した。&&
とか||
の後ろとか()
の中はダメ。 -
タプルの名前の順序間違い、例えば
var (first, last) = (last, first);
みたいなの、事故りそうな予感があって警告は出したい- ※結局は「工数の問題でC# 7の時点ではできない」「後からWarning Wave追加する」ってなってる(11/15のNotes参照)
-
タプルに対して
new (int x, int y)()
は認めないことにしたけども、配列とかnullableはどうかnew (int x, int y)[10]
とnew (int x, int y)?()
は認めたい- ※結局認めたみたい
2016/10/25
-
C# 6の時に一度は検討して、最終的には入れれなかった「変数宣言式」について
- 宣言式自体は問題あって、提案当時のままというわけにはいかない
- 宣言式から派生した、分解とout varはC# 7に入った
-
分解とout varは、再び統一的に扱った方がよさそう(C# 7では間に合わないけど、将来の予定として)
- 分解代入と分解宣言を混ぜたり:
(x, int y) = e
- out引数のところに分解を書いたり:
M(out (x, y))
- 分解代入と分解宣言を混ぜたり:
-
パターン マッチングでは、いくらか、絶対に成功するパターン(irrefutable(反論の余地がない)パターンって呼んでる)がある
- 今は、絶対に成功していることを前提としたフロー解析(確実な初期化とか、null解析とか)をまだあまりやっていないけど、将来的には役立ちそう
-
分解のために使う
Deconstruct
メソッド、out引数を使ったもの(Deconstruct(out int x, out int y)
)じゃなくてタプルを返す("(int x, int y)Deconstruct()")のはどうかというのを検討- 計測してみたけども、どっちが効率的かみたいな差は出なかった
- あんまりやる価値はなさそう
-
if (int.TryParse(s1, out var i)) { ... i ... }
みたいな書き方で、Tryの成功時も失敗時も必ずi
が初期化されちゃうがために、if
の外でもi
が使えてしまうのは問題じゃないかif
の外に変数が漏れる設計にした以上これを防ぐ手立てはないんだけど- 「Tryパターンなメソッドのout varの場合は
if
の外で使うと警告」みたいなことができるAnalyzerは用意した方がいいかも
-
discards に
_
を使うことにしたけども…- 分解とかout varとか、これから足す構文については常にdiscards扱いしたい
- 既存構文の場合、
_
を2個以上宣言していたらdiscards扱い、1つも読み出ししてないならdiscards扱い、みたいなルールでやる予定
2016/11/15
-
要素の名前違いのタプルに関して警告を出すべき状況がある
- 今は実装していないけど、将来的にはWarning Wavesで警告を追加したい
-
Wildcardsって呼び名をDiscardsに改めたのはこの辺り
- MVP Summitで、MVPからこの案が出てた
-
*
よりも_
がいいだろうという感触もここで得た- 現状、
_
が有効な識別子だと言っても、それを「値の無視」以外の目的で使っている人は少なそう
- 現状、
2016/11/30
-
while
の条件式内のout varで宣言された変数は、while
の外に漏れないようにした for
の更新式の中で定義した変数は、for
の内側にも漏れないようにした- 10/25のNotesで検討していた「分解から宣言式への統一化」、自信をもってできそう
-
式中で宣言した変数をどこでも使わなかった場合は「未使用変数警告」を出すべきか?
- そういう変数はたいてい無視するためのダミー変数で、discards機能が入ったら要らなくなるはず
- でも、警告にまではせずとも、サジェストとして「discardsへの変更」機能を提供することもできる
- 結局、後者(警告にはしない)を選択
-
C#では、embedded ステートメント(
if
とかの後ろ)の中で宣言ステートメントを書くとエラーになるけど、同様の文脈で、今後、分解とかout varとかを使って変数宣言できるようになる。これはエラーにすべきか?- しない。むしろ既存の制限の方を緩める可能性すらある(と言ってもC# 7ではやらない)
-
nullでないことを調べるだけのパターンが欲しいって案が出てた
- やりたい。C# 7までには時間がないけど、アイディアとしてはいい
2016/12/7と12/14
-
クエリ式中で(out varとかで)宣言された変数は、式中ずっと使えるべきかも
where int.TryParse(s, out int i)
とかで宣言されたi
は、その後ろのselect句とかでも使えるようにする- そうなると、単純に
Where(x => int.TryParse(x.s, out int i))
みたいな展開はできない。透過識別子が追加される let
句での分解(let (dx, dy) = (x - x0, y - y0)
みたいなの)でも同様に透過識別子が増える- とりあえずC# 7までに実装できる時間はないんで、C# 7時点ではコンパイル エラーにしてある。将来的にはできる余地だけ残してある
-
10/25で出てた「絶対に成功するパターン(irrefutableパターン)」は、到達可能かどうか(
return
やthrow
、永久ループの後ろに何か書くと警告を出して呉れるやつ)の判定にも使うべきか- やってもそこまで価値はなさそうで、既存のセマンティクスを変えるほどのコストは見合わなさそう
- 11/30で、
while
の条件式と、for
の更新式中で宣言した変数のスコープを縮めるって話は書いたけど、言い忘れてただけで、do-while
も同様
2017/1/17
-
定数パターン(
e is 42
みたいなの)の展開結果をちょっと変更- これまで
object.Equals(e, 42)
- これから
object.Equals(42, e)
object.Equals
の中で、第1引数のインスタンス メソッドのEquals
に処理を丸投げしているらしくて、第1引数が定数な(かつ、nullじゃない)保証がある方がちょっとだけパフォーマンスがいいらしい
- これまで
-
タプルに対する拡張メソッドで、型変換が効くように
- 配列に対して
IEnumerable<T>
の拡張メソッドが呼べるんだから、(T[], U[])
に対して(IEnumerable<T>, IEnumerable<U>)
の拡張メソッドを呼べるべきだろうという提案があった - 通常、明示的な型変換が必要な場合に拡張メソッドは呼ばれない。で、
ValueTuple
間には暗黙的型変換が働かないので、上記拡張メソッドを呼べない - でも、全要素がそれぞれ暗黙的に変換できる場合にコンパイラーがタプル間の変換をするんだから、拡張メソッド呼び出しの時にもこの変換を認めるべき
- これ、「今やる」か「今後もうできないか」の2択なので、もう時間も限られているけど頑張って今やってみる
- 配列に対して