Ver. 5.0
2010年10月、PDC 10 にて C# 5.0 がらみの情報が少し。
.NET 4 以降、ライブラリもツールも、拡張という形で小出しにされるようになりましたが、 言語機能も多分に漏れず、小出しに出て来ることになりました。 PDC 10 では、「C# 5.0 / VB 11」という形での発表ではなく、 「Asynchronous Programming for C# and Visual Basic」という名前で、非同期処理に関する部分だけが公開されました。
2011年9月の BUILD でも、少し C# 5.0 の追加機能、Caller Info 属性についての情報が公開されました。
async/await キーワードを使うことで、非同期処理を簡単に行えるようになります。
非同期処理には、処理の内容をシーケンス図やフローチャートに起こすと、同期の時と全く同じになるものがあります。 このようなタイプのものに対しては、async/await を使うことによって、同期処理とほぼ同じコードで非同期処理を行えます。
C++ でいうところの __FILE__ や __LINE__ マクロに相当するデバッグ用診断を実現するための機能です。
以下のように、メソッドの引数に属性をつけておくと、その引数に対して、コンパイラーが診断情報を渡してくれます。
public static class Trace { public static void WriteLine(string message, [CallerFilePath] string file = "", [CallerLineNumber] int line = 0, [CallerMemberName] string member = "") { var s = string.Format("{0}:{1} - {2}: {3}", file, line, member, message); Console.WriteLine(s); } }
ちなみに、コンパイル結果的には、定数やオプション引数と同様、コンパイル時のリテラル埋め込みになります。(実行時のコストは 0。)
イテレーター ブロックや非同期メソッド内から呼び出しても、ちゃんとメソッド名を拾えます。 また、匿名関数(ラムダ式や匿名メソッド式)中で呼び出した場合も、匿名関数を書いているメソッドの名前が使われます。 (これらの機能は内部的に別のクラスやメソッドを生成していますが、そのコンパイラー生成名ではなく、元のメソッド名を拾います。)
C# 5.0 で、foreach の仕様が少しだけ変わるそうです。 (一応、破壊的変更になります(既存のコードが動かなくなる可能性が0ではない)。)
C# 4.0 までは、foreach ステートメントは以下のように展開されていました。
static void ForeachSample<T>(IEnumerable<T> data) { // 展開前 foreach (var x in data) { Console.WriteLine(x); } // 展開後 using (var e = data.GetEnumerator()) { T x; while(e.MoveNext()) { x = e.Current; Console.WriteLine(x); } } }
これで何が嫌かというと、ラムダ式で、foreach のループ変数(上記の例でいう x)をキャプチャしたときの挙動がいまいちなことです。 例えば、以下のコードを実行したとします。
var data = new[] { 1, 2, 3, 4, 5 }; Action a = null; foreach (var x in data) { a += () => Console.WriteLine(x); } a();
C# 4.0 まででは、以下のような結果が得られる仕様になっていました。
5 5 5 5 5
C# 5.0 では foreach の展開結果が以下のように変わる予定です。ループ変数の位置が、while ループの中に移動しました。
static void ForeachSample<T>(IEnumerable<T> data) { // 展開前 foreach (var x in data) { Console.WriteLine(x); } // 展開後 using (var e = data.GetEnumerator()) { while(e.MoveNext()) { T x; x = e.Current; Console.WriteLine(x); } } }
これで、先ほどのラムダ式の例の実行結果は、以下のように変わります。
1 2 3 4 5
C# 4.0 までの仕様が前述のようになっていたのは、以下のような理由によるものです。
C# 5.0 で、破壊的変更になってまでこの仕様を変えたのは、ラムダ式の利用が広まって、上記のようなループ変数のキャプチャでの問題が大きくなったからのようです。 破壊的変更にならないように、構文自体を少し変えるという案もあったようですが、影響範囲は狭いだろうということで、仕様変更に決まったようです。