【C#】varの使い方、メリットとデメリット

使い方

C#では変数宣言時、型を書く必要がある。

string text = "あいうえお";

この時、varキーワードを使用することで右辺から型を推論させて変数宣言を行える。

var text = "あいうえお";

// textはstring型となる

Visual Studioで確認すると、string型となっていることがわかる。

varは右辺から型を判別している。そのため、下記のコードはコンパイルエラーとなる。

var text = null;

// nullの型を判別できないためコンパイルエラーとなる

もしstring型のnullvarを使って宣言する場合、下記のように書かなければならない。

var text = (string)null;

この記法だと一応宣言はできるが、あまり型推論のメリットがない。(後述するようにメリットがない訳ではない)

メリット

varを使うと次のメリットがある。

  • 複雑な型名を書かなくても良い
  • 型が変更されたときに書き直さなくても良い
  • コードが読みやすくなる

順番に解説する。

複雑な型名を書かなくても良い

たとえばDictionary<Dictionary<Dictionary<int, string>, string>, Dictionary<Dictionary<int, string>, Dictionary<int, string>>>を返すNegativeMethodという関数があるとしよう。

これもvarを使うと型名を書かなくてよい。

public static Dictionary<Dictionary<Dictionary<int, string>, string>, Dictionary<Dictionary<int, string>, Dictionary<int, string>>> NegativeMethod()
{
    Dictionary<Dictionary<Dictionary<int, string>, string>, Dictionary<Dictionary<int, string>, Dictionary<int, string>>> something = new Dictionary<Dictionary<Dictionary<int, string>, string>, Dictionary<Dictionary<int, string>, Dictionary<int, string>>>();

    // ~複雑な処理

    return something;
}

Dictionary<Dictionary<Dictionary<int, string>, string>, Dictionary<Dictionary<int, string>, Dictionary<int, string>>> result = NegativeMethod();

上記のようなコードが、下記のように書ける。

public static Dictionary<Dictionary<Dictionary<int, string>, string>, Dictionary<Dictionary<int, string>, Dictionary<int, string>>> NegativeMethod()
{
    var something = new Dictionary<Dictionary<Dictionary<int, string>, string>, Dictionary<Dictionary<int, string>, Dictionary<int, string>>>();

    // ~複雑な処理

    return something;
}

var result = NegativeMethod();

型が明確になっている場合は、複雑な型を書かなくて良くなる。TupleDictionaryは複雑になる場合もあるが、それをいちいち書かなくても良いメリットとなる。なお、後述するがこういう使い方にはデメリットもあるので注意。

型が変更されたときに書き直さなくても良い

たとえばuserIdint型だったが、アルファベットを含めるという仕様変更にあたりstring型に変更することになったとする。

ここで、下記のように修正する必要がある。

// GenerateUserIdはintを返す
int userId = GenerateUserId();

// ▲これを下記のように変更する▼

// GenerateUserIdはstringを返すようになった
string userId = GenerateUserId();

数か所であれば問題ないがもし何十箇所・何百箇所も記載してあればそれらを漏れなく修正する必要がある。

一括で置換するという方法もない訳ではないが、対象が漏れなく置換されたか、本来修正範囲外のコードが変更されていないかなどの差分確認の作業が結局発生する。

初めからvarで記載しておけば型が変更されてもコードの修正が必要なく、修正後にテストを走らせればそれだけで済む。

// GenerateUserIdがintからstringになっても、ここは修正する必要がない
var userId = GenerateUserId();

コードが読みやすくなる

ここは感覚的な問題だが例を示す。

下記のようなコードがあるとする。

int userId = GenerateUserId();
string firstName = "";
string lastName = "";
DateTime birthday = new DateTime(1990, 1, 1);

これはvarを使用すると次のように書ける。

var userId = GenerateUserId();
var firstName = "";
var lastName = "";
var birthday = new DateTime(1990, 1, 1);

こうすると型の部分が全てvarになり、変数名のインデントが揃って読みやすくなる…気がする。少なくとも私は、バラバラに型を書いているよりvarで揃っていた方が読みやすい。

デメリット

上記のようにとてもメリットの大きなvarだが、デメリットとなるケースも存在する。

それは、人間が容易に型を推論できない場合だ。

var firstName = GetFirstName(userId);

上記の場合、おそらくfirstNamestring型になるだろうという想像が出来る。varを使用するのが相応しいケースの一つだ。

しかし下記の場合はどうだろう?

var idAndNames = GetIdAndNames();

GetIdAndNamesはおそらくidnameを(おそらくペアで)取得するメソッドだと予想はできるが、どういう型になっているかは実装部分を見ないと判定できない。Dictionary<int, string>かもしれないしList<Tuple<int, string>>かもしれない。独自クラスのコレクションになっている可能性もある。

varを使用したらGetIdAndNamesがどのような型を返すとしても寛容に受け入れてしまう反面、型が分かりにくくなるというデメリットもある。(上記の例は関数名などのネーミングの問題もあるが)

Visual StudioなどのIDE上で読むのであれば、変数名にマウスオーバーしたら型が分かるが、バージョン管理ソフトの履歴などの画面では当然そのような機能がないため読みづらい。型が分かりにくい場合は型を明記するが、コメントを添えておくと良いだろう。

コメントは後で型が変わった時などにメンテしなければならないので、明記する方が個人的には良いと思う。型を明記していればコンパイルエラーなどで検知できるので修正漏れを防ぐ助けにもなる。

// varを使わず型を明記する場合
Dictionary<int, string> = GetIdAndNames();

// コメントを添える場合
// GetIdAndNamesはDictionary<int, string>
var idAndNames = GetIdAndNames();

varは使うべきか

これはもちろんチームのコーディング規約や方針に従わなければならないが、少なくとも私は個人開発では積極的に利用する。私がアサインされたチームでも今のところ「varは禁止」というところはなかったので非常に助かっている。

MicrosoftのC# のコーディング規則 (C# プログラミング ガイド)にも、

変数の型が割り当ての右側から明らかである場合、または厳密な型が重要でない場合は、ローカル変数の暗黙の型指定を使用します。

と明記されている。