匿名型
匿名型を使用すると、クラス宣言を行わずにクラスをインスタンス化できます。言うまでもありませんが、このクラスには名前がないので、varキーワードの使用が必要になります。次に例を示します。
var person = new { Name = "Peter", Age=4};
この構文から推測できるように、匿名型にメソッドを定義する方法はありません。そのため実際のところ、実環境のシナリオでの匿名型の有用性は限られています。型を実際に宣言することはないので、この型には属性を追加できません。したがって、シリアライズ可能にすることもできません。
また、匿名型は不変(immutable)です。これはつまり、匿名型をインスタンス化するときにはフィールドではなく読み取り専用のプロパティを宣言するということです。このようなプロパティはgetのみを持ち、setはありません。
要するに、匿名型は実際にはきわめて一時的なデータのみに有用です。
使用時のヒント
- 長期的なデータでは匿名型を避ける。
暗黙的に型付けされたローカル変数
賛否が分かれそうなもう1つの追加は、暗黙的に型付けされたローカル変数です。これをvarと呼ぶことにします。この呼び方についてはさておき(varとしておけば、他の型と混同する可能性は非常に低くなるでしょう)、varが導入された主な理由は、LINQクエリで返される型には匿名型が含まれることがあり、必ずしも人間が読み取りやすい名前になるとは限らないからです。
LINQステートメントから返される型は、きわめて複雑になることもあります。匿名型を扱わない場合は、結果の型を推定して、宣言において手動で型付けできる可能性もあります。しかし、たいていの場合、データベースクエリは非常に可変性が高いので、変更のたびに型を再推定する必要があります。
LINQステートメントでの使用を別にしても、varを使用するとソースコードの読みやすさが低下し、検索が難しくなる傾向があります。たとえばMyClassというクラスを宣言し、このクラスをインスタンス化するときに毎回varを使用していると、var変数への代入式の右辺でクラス名を使用していない限り、コード内で「MyClass」を検索して使用箇所を特定することができなくなります。
C#では、大半の式で予測どおりの型が生成されますが、そうでないこともあります。二項演算と整数型がその好例です。整数の二項演算では、一方の項が符号あり(signed)でもう一方の項が符号なし(unsigned)の演算はサポートされません。次のコードではエラーが発生します。
uint unsignedNumber = 42; int signedNumber = 10; int result = unsignedNumber * signedNumber;
上記のコードを実行すると、「Cannot implicitly convert type 'long' to 'int'」(long型からint型への暗黙的変換はできません)という少々わかりにくいエラーメッセージが表示されます。このエラーメッセージでlong型と言及されているのは、コンパイラによってuint型からlong型への変換が暗黙的に行われるためです。つまり、コンパイラは以下の処理を実行しています。
int result = (long)unsignedNumber * signedNumber
これは、符号あり整数と符号なし整数で二項演算を行う1つの方法です。しかし、結果をint型にする必要があるのに、コンパイラはlong型をint型に暗黙的に変換できません。そのため警告が生成されます。
結果の型を指定するのにint型の代わりにvarを使用すると、エラーは解決します。
var result = unsignedNumber * signedNumber;
これで正常に実行されますが、結果はuint型でもint型でもなく、long型です。また、これはulong型でもありません。
もう1つ、メソッドの戻り値の型に関するちょっとした例を紹介しましょう。あるメソッドの結果にvarを使用すると、その変数を使用するコードは、メソッドの実装に関連付けられることになります。後でそのメソッドの戻り値の型を変更すると、対応する変数を使うコードの働きも変わってきます。その例を次に示します。
private int GetWeight ( ) { return 17; } private decimal GetQuantity ( ) { return 11; } String Method() { var quantity = GetQuantity (); decimal result = GetWeight() * quantity / 4; Trace.WriteLine("result:" + result); return "result: " + result; }
上記のコードを実行すると、結果は以下のようになります。
result:46.75
ここでGetQuantityメソッドに手を加え、値をint型で返すようにすると、次のようになります。
static int GetQuantity ( ) { return 11; }
結果はこうなります。
total:46
変更した部分はごくわずかです。GetQuantityメソッドの本体には手を加えていませんが、出力は戻り値の型に応じて変わります。
厳密な型付けを利用できるというメリットはありますが、暗黙的に型付けされたローカル変数を使用するとコンパイラの選択が先送りされ、コンパイル時にバグを検出できる確率が低下します。
使用時のヒント
- 組み込み型でvarを使用することは避ける。
- 結果が組み込み型ではないLINQステートメントでのみ使用する。
- 明示的な型のローカル変数を優先する。
