Roslyn もくもく勉強会の記録。
Roslyn は活発に開発中のため、言語仕様含めて随時更新される。
内容は8/6(土)現在のものがベース。
あまり込み入った記述をするとエディターが落ちるので注意。
必須環境
- Visual Studio 2015(Update 2以降)
- VS拡張性ツール インストールしていない場合はコントロールパネルの「プログラムと機能」から修正する。
- コードアナライザなども試したければ .NET Compiler Platform SDK もいるはず(未確認)。
準備
Gitでリポジトリを取得。
git clone https://github.com/dotnet/roslyn (your folder)
Visual Studio 開発者コマンドプロンプトを起動し、取得先のフォルダに移動。
cd (your folder)
下のコマンドを実行。Nugetの更新やパッケージの取得が走る(10分くらいかかる)。
restore.cmd
下のコマンドを実行してビルド(これも10分以上かかるはず)。
msbuild /v:m /m Roslyn.sln
Visual Studio でRoslyn.sln を開く。
ソリューションをビルドする。
Binaries フォルダ以下に VisualStudioSetUp.visx が生成されれば一安心。
VisualStudioSetUp をスタートアッププロジェクトに指定してF5で実行する。
独立した Visual Studio のインスタンスが起動するので、そこでコンソールアプリケーションプロジェクトを作成する。
参考
ほぼ下のリンクの通り。
Let's code!
現時点で使える機能は以下の通り。
- Binary Literals
- Digit Separators
- Local Functions
- Type Switch
- Ref Returns
- Tuple Syntax
- Out Var
- ValueTask
詳細と最新のステータスは Language Feature Status を参照。
……ただし。
関数の戻り値で Tuple Types を使うとコンパイルエラーが出るので、一点だけ追加の準備が必要。
-
System.ValueTuple
を nuget でプロジェクトに追加するか、 - Roslyn のフォルダの src/Compilers/test/resource/ のなかの System.ValueTuple.dll の参照を追加する
新機能について諸々
きちんとした内容は主催者でもある 岩永氏のブログを参照。
Binary Literals
2進数リテラル。 0b
または 0B
から定数値の記述をはじめると2進数表記が使える。
Digit Separators
整数値リテラルを _
で区切ることができる。 0b_1100_1111
のようにして使う。
Local Functions
ローカル関数。つまり関数内関数。
デリゲートやラムダ式と同じで周囲の変数をキャプチャできる。
Type Switch
文字どおり、switch 文を型+変数で分岐させるもの。
型のみを対象としたパターンマッチングの一部。
switch だけでなく、 is 演算子も予定されている。
これができると下のコードが、
var s = x as string;
if (a != null) {
return int.Parse(s);
}
下のようにかける。intのような値型でも統一的に使える点で as 演算子よりも優れている。
var s = x as string;
if (x is (string s)) {
return int.Parse(s);
}
覚書:
ILレベルでは予想通り if + as にコンパイルされるので、
現時点では単なるシンタックスシュガー。
Ref Returns
戻り値とローカル変数で ref
を使えるようにするという話。
もともとの ref
の意味からは外れるが、
構造体の受け渡しのオーバーヘッドをなくしたり、
アンマネージドコードのポインタを返す関数を受け取ったりする要望に応えたもの。
Tuple Syntax
タプルまわりのアップグレード。
LL的な書き心地ももちろんあるが、async/await
構文のために関数の 多重戻り値 のサポートが必要になったのも大きいだろう。
具体的には以下の3つの小機能からなる。
- タプル型サポート。関数の戻り値などで名前付きタプルを定義できる。
- タプルリテラル。タプル生成(やパターンマッチング)のための専用記法が導入される。
- タプル分解。タプルを要素に分解して変数で受け取れる。言語によっては多重代入と呼ばれる。
全部使うと以下のようになる。
public static void Main(string[] args)
{
var t2 = Divide(2, 3);
Debug.Assert(t2.quotient == q); //名前によるアクセス。
Debug.Assert(t2.remainder == r);
(var q, var r) = Divide(2, 3); //タプル分解
Debug.Assert(q == 0);
Debug.Assert(r == 2);
}
public static (int quotient, int remainder) Divide(int lhs, int rhs) //タプル型戻り値。
{
return (lhs / rhs, lhs % rhs); //タプルリテラル。
}
補足
名前によるアクセスはコンパイラにしか見えない擬似的なフィールド。
ILのレベルではプレーンな System.Tuple
と変わらず、
単に TupleElementNamesAttribute
という属性で名前を与えている。
なので、リフレクションや式木で扱うときは注意がいる。
分解の対象はタプルに限らない。
仕様は揺れているが、拡張メソッドを含めて特定の名前と形のメソッドを提供してさえいればどんなクラスでも適用できるようになる予定。
イメージとしては演算子オーバーロードに近い。
Out Var
out
で変数を受け取るときに var
で変数を定義するという話。
要するにこれが
var dict = new Dictionary<string, string>();
string value; //NOT var
if (dict.TryGetValue("none", out value))
{
...
}
こうなる。
変数のスコープが「はみ出ない」ことにも注目されたし。
修正されました。単純に外側の {}
ではなく、説明すると少し複雑になる。
var dict = new Dictionary<string, string>();
if (dict.TryGetValue("none", out var value)) //YES! var
{
...
}
ValueTask
- Arbitary async returns ... 現状の
async
メソッドではvoid
/Task
/Task<T>
しか使えないところを、Task-likeなクラスであればなんでも利用できるようにする。 - ValueTask ... 実際に
Task
を使わない場合は効率的に動作するValueTask
型を提供する。
という話で、言語機能の変更としては 1 が大。
Task-like は構文だけあっていればインターフェイスなどは不要(いわゆる structural typing)。
なので、今後、拡張メソッドの static メソッド対応(static extension methods)が実装されると
既存の独自の「非同期」型でも async/await
が使えるようになると期待されている。
その他
ある程度、新機能を追っているひとへ。
- ここ一箇月くらいで Record 型が先送りになりました(2016/6頃)。あわせて With 構文も。
- パターンマッチングはほぼ先送りで、Type Switch のみが先行導入された形。
- 各種の文(statement)の式(expression)化も先送り。合わせて throw 式も先送り。
なお Visual Studio 15 Preview での実装状況については以下を参照のこと(8/24時点)。
https://blogs.msdn.microsoft.com/dotnet/2016/08/24/whats-new-in-csharp-7-0/