Ver. 4.0
2008年10月、3年ぶりに C# バージョンアップに関する情報が公開されました。
C# 4.0 で追加される機能は以下の3つ。
( 注 2008/11/14: 2008年10月に発表された内容としてはこの3つですが、今後の機能追加に関して。 C# 4.0 CTP の顧客フィードバックサイトの掲示板議論によれば、 「C# 4.0 の仕様は現状で確定ではない。ただし、これから大幅な変更は入れたくはない。」ということらしい。 C# 3.0 の時には、後から追加された機能は自動プロパティとpartialメソッドだけで、 「小さな変更」だったので、今回も同程度の「小さな変更」くらいしか追加されないはず。 )
dynamic キーワードを使うことで、動的型付け変数を定義できます。
dynamic 型を使うことで、 コンパイル時に確定しないプロパティアクセス・メソッド呼び出しが可能です。 (スクリプト言語との連携や、DLL の遅延バインディングのために使います。)
使い方としては var (C# 3.0 で追加された型推論)と似ています。 しかしながら、あくまで型推論である var と違って、dynamic で宣言した変数の型は「動的型」になります。
var sx = 1; // sx の型は int 型 dynamic dx = 1; // dx の型は dynamic 型
通常、C# (3.0 以前)のような静的型付け言語では、 オブジェクトがどういう名前のプロパティやメソッドを持っているかをコンパイル時に知っておく必要があります。
例えば、以下のようなコードを書くと、 「'object' に 'X' の定義が含まれていません」というようなエラーが生じます。
static object GetX(object obj) { return obj.X; }
object 型が X という名前のプロパティを持っていないので、静的言語の世界ではエラーが出て当たり前。
一方、C# 4.0 では、dynamic 型を使うことで、以下のようなコードが書けるようになりました。
static dynamic GetX(dynamic obj) { return obj.X; }
obj が本当に X という名前のプロパティを持っているかどうかは、 コンパイル時ではなく、実行時に調べられます。
C# 4.0 でオプション引数と名前付き引数が追加されました。
オプション引数は C++ にもある機能ですね。 これは、メソッドのオーバーロードで似たようなことが可能なので、 今まで C# では敬遠し続けてきたようです。
まず、C++ 同様、 以下のようにデフォルト値を持ったメソッドを定義します。
static int Sum(int x = 0, int y = 0, int z = 0) { return x + y + z; }
すると、以下のように、引数の一部もしくは全てを省略可能になります。 省略可能ということで、オプション引数(optional parameter)と呼びます。
int s1 = Sum(); // Sum(0, 0, 0); と同じ意味。 int s2 = Sum(1); // Sum(1, 0, 0); と同じ意味。 int s3 = Sum(1, 2); // Sum(1, 2, 0); と同じ意味。
この記法で省略可能になるのは、後ろの引数のみです。 この例でいうところの、x や y だけを省略することはできません。
で、もう1つ、 こちらも VB には昔からある機能なんですが、 名前付き引数(named parameter)が使えるようになりました。
先ほど定義したデフォルト引数付きのメソッドを、以下のような構文で呼び出せます。
int s1 = Sum(x: 1, y: 2, z: 3); // Sum(1, 2, 3); と同じ意味。 int s2 = Sum(y: 1, z: 2, x: 3); // Sum(3, 1, 2); と同じ意味。 int s3 = Sum(y: 1); // Sum(0, 1, 0); と同じ意味。
名前付き引数の場合、引数の順序は自由に書けます。 また、任意の箇所を省略可能になります。
C# 4.0 で、ジェネリクスの型引数に共変性・反変性を持たせることが可能になりました。 共変性・反変性という言葉の意味は 「covariance と contravariance」 参照。
これがどういうものかというのを説明する前に、まず背景を。 ジェネリックコレクションに関して、昔から以下のようなことをしたいという要望がありました。
List<string> strings = {"aa", "bb", "cc"}; List<object> objs = strings;
これを認めてしまうと何がまずいかというと、 以下のような不正な値の書き換えが起こり得る。
// strings と objs は同じオブジェクト objs[0] = 5; // int に書き換えられたらまずい string str = strings[0];
この問題が起きる原因どこにあるかというと、 List が set 可能なインデクサーを持っていることです。 読み取り専用であったなら、ここで挙げたような不正な書き換えは起こらないわけです。 なので、値の set ができないインターフェースの場合なら反変性を認めてもいいはず。 (同様に、書き込み専用な場合には共変性を認められます。)
ということで、C# 4.0 から、ジェネリクスの型引数に対して、 in/out を指定できるようになります。 読み取り(メソッドの戻り値、プロパティの get)にしか使わない型には in という修飾子を指定します。 例えば、以下のとおり。
interface IEnumerator<out T> { T Current { get; } // get しかない = 出力のみ bool MoveNext(); void Reset(); }
こうすることで、反変性が認められます。
IEnumerator<string> strEnum = new Enumerator<string>(); IEnumerator<object> objEnum = strEnum;
また、書き込み(メソッドの引数、プロパティの set)にしか使わない型には out を指定します。
interface IComparer<in T> { int Compare(T a, T b); // T は引数としてしか使われない }
こうすることで、今度は共変性が認められます。
IComparer<object> strComp = new Comparer<object>(); IComparer<string> objComp = strComp;