|
|
連載:C# 3.0入門
第3回 varによる変数宣言とコレクション初期化子
株式会社ピーデー 川俣 晶
2008/06/13 |
|
|
なぜvarを使うのか?
しかし、いくら型が厳格にチェックされるとしても、明示的に型名を書かないことには批判もあるだろう。なぜなら、明示的に型を書くことを通じて検出されるバグや、そのようにして型が明示されることでソース・コードの分かりやすさが向上するケースもあるからだ。
例えば、「string」と6文字打つ手間をケチって「var」の3文字に手抜きするぐらいなら、6文字打つべきだと思う人もいるだろう。さらにいえば、「int」の場合は「var」に変えてもタイプする文字数に変わりはない。この点については筆者もおおむね同意する。
しかし、型名は常に短いわけではない。例えば、以下のようなコードの場合はどうだろうか?
using System;
using System.Collections.Generic;
using System.Text;
namespace SampleProgram
{
class Program
{
static void Main(string[] args)
{
Dictionary<string, List<StringBuilder>> stringToBuilderMap
= new Dictionary<string,List<StringBuilder>>();
// ……
}
}
}
|
|
リスト3 極めて長い型で変数を宣言する例 |
このコードに含まれる変数の宣言には以下の2つの問題がある。
- 極端に長すぎてソース・コードの読みやすさを損なっている
- 同じ型名(Dictionary<string, List<StringBuilder>>型)が2回書かれていて冗長
このようなコードに対して、varを用いることは特にデメリットはなく、これら2つの問題を解決する。
using System;
using System.Collections.Generic;
using System.Text;
namespace SampleProgram
{
class Program
{
static void Main(string[] args)
{
var stringToBuilderMap
= new Dictionary<string,List<StringBuilder>>();
// ……
}
}
}
|
|
リスト4 varを使用してリスト3を書き直した例 |
つまり、「暗黙的に型指定されるローカル変数」はC#を含むいくつかの言語に見られる以下のような「新しいインスタンスで初期化する変数宣言で、型名を2回書かねばならない冗長性」を軽減してくれる。
このようなケースでvarを用いることは、ソース・コードの読みやすさを損なうことはなく、むしろ冗長性を排除することで分かりやすくなる効能も期待できる。積極的に活用すべきだろう。
一方で、newを使わない初期化(「int a = 1;」や「string s= "abc";」のようなタイプ)に使うべきかは微妙なところである。少なくとも、「int a = 1;」をあえて「var a = 1;」と書くメリットは思い付かないが、逆に反射的に変数宣言を「var」で書き始める習慣を付けてしまうと、「var a = 1;」と書いてもよいような気もする。
ちなみに、newキーワードや定数を使わずとも、暗黙的な型指定は可能である。以下のリスト5で、変数objは暗黙的な型指定によりstring型となる。
using System;
class SampleObjectProvider
{
public static string GetSampleObject()
{
return "Hello!";
}
}
class Program
{
static void Main(string[] args)
{
var obj = SampleObjectProvider.GetSampleObject();
Console.WriteLine(obj);
}
}
|
|
リスト5 初期化を伴わない暗黙的な型指定 |
このような事例では、変数宣言の式から素早くその変数の型を読み取ることは難しく、ソース・コードの分かりやすさを損なう可能性がある。
しかし、込み入った型名、覚えにくい型名などの場合、手抜きの手段として型名を書かずにvarで済ませる使い方はあり得るだろう。
varが使用できない場面
以下、varが使用できない場面をいくつか紹介しよう。
■型を知るためのヒントが何もない
varは「暗黙的に型指定されるローカル変数」を示すのであって、「代入されるまで型が確定しないローカル変数」ではない。つまり、宣言時に型が決まらないと宣言できないのである。
それ故に、以下のような変数宣言は認められない。型を確定するためのヒントが何もないからである。
var a;
// エラー 1 暗黙的に型指定されたローカル変数を初期化しなければなりません
|
|
一方、以下のように書けばヒントは得られるので許される。
「型が明確に決まる式」とは、例えば「123」のような整数でもよい。123はint型であることがあいまいさなく決まるからである。
■ローカル変数以外
まず、「暗黙的に型指定される“ローカル”変数」という名前から分かるとおり、対象となるのはローカル変数だけである。つまり、フィールドには使用できない。以下のような使い方はNGである。明示的に型名を書かねばならない。
class Sample
{
private int a = 1;
private var b = 1;
//エラー 1 コンテキスト キーワード 'var' は、ローカル変数宣言内でのみ有効です。
}
|
|
リスト6 フィールドにvarは使用できない |
■ラムダ式、匿名メソッド
ラムダ式や匿名メソッドには使用できない。
var calc = () => 1 + 2;
// エラー 1 ラムダ式 を暗黙的に型指定されたローカル変数に割り当てることはできません
|
|
var calc = delegate() { return 1 + 2; };
// エラー 1 匿名メソッド を暗黙的に型指定されたローカル変数に割り当てることはできません
|
|
ただし、型が厳密に明示されたデリゲート型であれば問題なくvarで使用できる。そのため、上記の2例も、デリゲート型へのキャストを挟めばそれだけでコンパイルが通るようになる。
var calcByラムダ式 = (Func<int>)(() => 1 + 2);
|
|
var calcBy匿名メソッド = (Func<int>)delegate() { return 1 + 2; };
|
|
しかし、この知識はあまり役立たないだろう。なぜかといえば、次のようにvarを使わない宣言を行う方が、よりカッコの数が少なく短く書けるからだ。
Func<int> calcByラムダ式 = () => 1 + 2;
|
|
Func<int> calcBy匿名メソッド = delegate() { return 1 + 2; };
|
|
■null値の初期化
null値で初期化する場合、varは使用できない。
var a = null;
// エラー 1 <null> を暗黙的に型指定されたローカル変数に割り当てることはできません
|
|
しかし、null値であろうと厳密な型を持っていればvarを使用できる。以下のようにnullをキャストすると問題なくvarで変数を宣言できる。