【C#入門】varキーワードの基本と注意点をサンプルコードで学ぶ


【C#入門】varキーワードの基本と注意点をサンプルコードで学ぶ

はじめに

C#の学習を始めると、多くのサンプルコードで var というキーワードに出会うことでしょう。

csharp
var message = "Hello, World!";
var count = 100;

一見すると、この var はどんな種類のデータでも入れられる「魔法の箱」のように見えるかもしれません。PythonやJavaScriptといった動的型付け言語の経験がある方なら、それらと同じようなものだと感じるかもしれません。

しかし、それは大きな誤解です。C#における var は、静的型付け言語の原則を維持したまま、開発者のコーディングを助けてくれる非常に便利な「型推論」機能なのです。

この記事では、C#入門者から中級者へとステップアップを目指すあなたのために、var キーワードの正体から、そのメリット、具体的な使い方、そして陥りがちな注意点まで、豊富なサンプルコードと共に徹底的に解説します。

この記事を読み終える頃には、あなたは var を「なんとなく」使うのではなく、「意図を持って」正しく、効果的に使いこなせるようになっているはずです。モダンで読みやすいC#コードを書くための第一歩として、var の世界を一緒に探検していきましょう。


第1章: varキーワードとは? – 型推論の基本

まず最初に、var の最も重要な核心部分を理解しましょう。

1. varの正体: 型推論 (Type Inference)

var は、それ自体がデータ型ではありません。var は「ここに入る変数の型を、コンパイラ(プログラムを機械が読める言葉に翻訳する翻訳機)が右辺の値から推論して、自動的に決定してください」と指示するためのキーワードです。この仕組みを型推論 (Type Inference) と呼びます。

次のコードを見てみましょう。

csharp
// var を使った宣言
var number = 10;
var name = "Taro Yamada";
var pi = 3.14;

このコードがコンパイルされるとき、C#コンパイラは次のように解釈します。

  • var number = 10;

    • 右辺の 10 は整数リテラルなので、number 変数の型は int だな。
    • 結果: int number = 10; と書いたのと同じ意味になる。
  • var name = "Taro Yamada";

    • 右辺の "Taro Yamada" は文字列リテラルなので、name 変数の型は string だな。
    • 結果: string name = "Taro Yamada"; と書いたのと同じ意味になる。
  • var pi = 3.14;

    • 右辺の 3.14 は浮動小数点リテラルなので、pi 変数の型は double だな。
    • 結果: double pi = 3.14; と書いたのと同じ意味になる。

つまり、var を使って宣言された変数は、コンパイルの時点で型が完全に確定します。これを静的型付け (Statically Typed) と言います。一度 int 型と推論された number 変数に、後から文字列を代入しようとすると、コンパイルエラーが発生します。

csharp
var number = 10; // ここで number は int 型に確定
number = "hello"; // エラー! CS0029: 型 'string' を 'int' に暗黙的に変換できません。

これは、C#の安全性を支える非常に重要な特徴です。var はこの静的型付けのルールを一切壊しません。実行時のパフォーマンスも、型を明示的に書いた場合と全く同じです。var は、あくまで開発者が書くコードをシンプルにするための「シンタックスシュガー(糖衣構文)」なのです。

2. dynamicとの決定的な違い

C#には dynamic というキーワードもあります。これは var とは全くの別物です。

  • var (静的型付け): コンパイル時に型が決定する。型が持つメンバー(メソッドやプロパティ)しか呼び出せず、間違った呼び出しはコンパイルエラーになる。
  • dynamic (動的型付け): 実行時まで型の決定が遅延される。コンパイル時には型チェックが行われず、実行時にメンバーが存在しないと実行時エラー(RuntimeBinderException)が発生する。

“`csharp
// var の例 (静的型付け)
var staticValue = “hello”;
// Console.WriteLine(staticValue.Length); // OK. string型にはLengthプロパティがある
// Console.WriteLine(staticValue.Foo()); // コンパイルエラー! string型にFooメソッドはない

// dynamic の例 (動的型付け)
dynamic dynamicValue = “hello”;
Console.WriteLine(dynamicValue.Length); // 実行時OK.
Console.WriteLine(dynamicValue.Foo()); // コンパイルは通るが、実行時にエラー!
“`

dynamic は、COM相互運用や、Pythonなどの動的言語との連携といった特殊なシナリオで使われます。通常のアプリケーション開発で var の代わりに dynamic を使う理由はほとんどありません。var は安全で、dynamic はリスクを伴う、と覚えておきましょう。

3. なぜvarを使うのか? – varのメリット

では、なぜわざわざ var を使うのでしょうか?それにはいくつかの明確なメリットがあります。

メリット1: コードの簡潔さ

最も分かりやすいメリットは、コードが短く、シンプルになることです。特に、型名が長くなる場合にその威力は絶大です。

例えば、キーが文字列で、値が「整数のリスト」であるディクショナリを考えてみましょう。

“`csharp
// 型を明示する場合
Dictionary> userScores = new Dictionary>();

// var を使う場合
var userScores = new Dictionary>();
“`

どちらが読みやすいかは一目瞭然です。var を使うことで、繰り返し同じ型名を書く必要がなくなり、コードのノイズが減ります。

メリット2: 可読性の向上

「コードが短くなるのは分かったけど、型が分からないと逆に読みにくくない?」と感じるかもしれません。これはもっともな意見です。しかし、多くの場合、var は可読性を向上させます。

その理由は、変数の「使われ方」や「作られ方」に意識を集中できるからです。

csharp
// 右辺を見れば型は明らか
var customer = new Customer();
var products = GetAllProducts(); // メソッド名から製品リストだと分かる
var name = customer.Name;

上記のコードでは、右辺の new Customer() やメソッド名 GetAllProducts() から、変数がどのようなものであるかが明確に分かります。型名を明記しなくても、コードの意味を理解するのに支障はありません。むしろ、重要な情報である「何をしているか(new やメソッド呼び出し)」に目が向きやすくなります。

メリット3: リファクタリング耐性の向上

リファクタリングとは、プログラムの外部的な振る舞いを変えずに、内部の構造を改善することです。var はこのリファクタリングを容易にします。

例を見てみましょう。GetUser というメソッドがあり、最初は User クラスのインスタンスを返していたとします。

“`csharp
public class User { // }
public User GetUser(int id) { // return new User(); }

// 呼び出し側のコード
User user = GetUser(1);
“`

その後、仕様変更で、より詳細な情報を持つ UserDetails クラスを返すように GetUser メソッドを変更したとします。

csharp
public class UserDetails { /* ... */ }
public UserDetails GetUser(int id) { /* ... */ return new UserDetails(); }

このとき、呼び出し側のコードを User user = ... のままにしておくと、コンパイルエラーになります。UserDetails 型を User 型の変数には代入できないからです。次のように修正が必要です。

csharp
// 呼び出し側のコードを修正する必要がある
UserDetails user = GetUser(1);

しかし、もし最初から var を使って書いていたらどうでしょうか?

csharp
// var を使っていた場合
var user = GetUser(1);

このコードは、GetUser メソッドの戻り値が User から UserDetails に変わっても、一切修正する必要がありませんvar が新しい戻り値の型 UserDetails を正しく推論してくれるからです。このように、var は変更に強い、柔軟なコードを書く手助けをしてくれます。


第2章: varの具体的な使い方 – 実践的なサンプルコード

var の基本とメリットが分かったところで、次は実際のコードでどのように使われるかを見ていきましょう。

1. 基本的なデータ型での使用

これは第1章で見た通り、最も基本的な使い方です。

“`csharp
// 整数
var integerValue = 123; // int

// 浮動小数点数
var doubleValue = 123.45; // double
var floatValue = 123.45f; // float (fサフィックスが必要)
var decimalValue = 123.45m; // decimal (mサフィックスが必要)

// 文字列
var stringValue = “こんにちは、世界!”; // string

// 真偽値
var boolValue = true; // bool
“`

Visual StudioなどのIDEを使っている場合、var で宣言した変数にマウスカーソルを合わせると、推論された型が表示されます。ぜひ試してみてください。

2. オブジェクトのインスタンス化

new キーワードを使ってクラスのインスタンスを生成する際は、var の利用が特に推奨されます。右辺で型名を記述しているため、左辺でそれを繰り返すのは冗長だからです。

“`csharp
public class Person
{
public string Name { get; set; }
public int Age { get; set; }

public Person(string name, int age)
{
    Name = name;
    Age = age;
}

}

// var を使ったインスタンス化
var person = new Person(“Taro”, 30);

// 型を明示した場合(冗長に感じる)
Person person2 = new Person(“Hanako”, 25);
``
このパターンは「
var` を使うべき」と多くのC#コーディング規約で定められているほど、一般的で優れた使い方です。

3. LINQとの組み合わせ

var が最も輝く場面の一つが、LINQ (Language-Integrated Query) を使うときです。LINQは、データコレクションに対してSQLのような問い合わせ(クエリ)を書くことができる機能です。

LINQのクエリ結果、特に select 句で新しいオブジェクトを作成した場合、その結果は匿名型 (Anonymous Type) となることがあります。匿名型とは、その場で一時的に作られる、名前のない型のことです。

“`csharp
public class Customer
{
public int Id { get; set; }
public string Name { get; set; }
public string City { get; set; }
}

var customers = new List
{
new Customer { Id = 1, Name = “Alice”, City = “Tokyo” },
new Customer { Id = 2, Name = “Bob”, City = “Osaka” },
new Customer { Id = 3, Name = “Charlie”, City = “Tokyo” }
};

// LINQクエリ: 東京在住の顧客の名前とIDを取得する
var tokyoCustomers = from c in customers
where c.City == “Tokyo”
select new { CustomerName = c.Name, CustomerId = c.Id };
“`

このとき、tokyoCustomers の型は何でしょうか? select new { ... } によって作られたのは匿名型であり、CustomerName (string) と CustomerId (int) というプロパティを持つ、この場所でしか使えない特別な型です。私たちはこの型の名前を知りませんし、書くこともできません。

ここで var が必須となります。var を使うことで、コンパイラがこの複雑な匿名型を正しく推論し、tokyoCustomers 変数に割り当ててくれます。

この結果をループで処理する際も var が役立ちます。

csharp
foreach (var customerInfo in tokyoCustomers)
{
// customerInfo は匿名型。プロパティにアクセスできる。
Console.WriteLine($"ID: {customerInfo.CustomerId}, Name: {customerInfo.CustomerName}");
}

もし var がなければ、LINQの強力な機能の多くは実現不可能だったでしょう。

4. foreachループでの使用

LINQに限らず、一般的な foreach ループでも var は非常に便利です。コレクションに含まれる要素の型を明記する必要がなくなります。

“`csharp
var names = new List { “Apple”, “Banana”, “Cherry” };
var scores = new Dictionary { { “Math”, 90 }, { “English”, 85 } };

// List のループ
foreach (var name in names)
{
// name は string 型と推論される
Console.WriteLine(name.ToUpper());
}

// Dictionary のループ
foreach (var score in scores)
{
// score は KeyValuePair 型と推論される
Console.WriteLine($”Subject: {score.Key}, Score: {score.Value}”);
}
``
このように、コレクションの型が何であれ
var` を使うことで、一貫性のあるシンプルなループを記述できます。

5. usingステートメントでの使用

ファイルストリームやデータベース接続など、使い終わったら必ずリソースを解放(Disposeメソッドを呼ぶ)する必要があるオブジェクトがあります。これを安全に行うのが using ステートメントです。この using ステートメントでも var を使えます。

“`csharp
using System.IO;

// using でストリームリーダーを生成
// reader は StreamReader 型と推論される
using var reader = new StreamReader(“myFile.txt”);
string content = reader.ReadToEnd();
Console.WriteLine(content);
// using ブロックを抜けるときに自動的に reader.Dispose() が呼ばれる
``
これも
newと組み合わせるパターンなので、var` の利用が推奨されます。


第3章: varを使う上での注意点とルール

var は非常に便利ですが、万能ではありません。使えない場面や、使うべきでない場面が存在します。これらを理解することが、var を正しく使いこなす鍵となります。

1. varが使えない場面

以下のケースでは、var を使うことができず、コンパイルエラーになります。

ルール1: 宣言と同時に初期化が必須

var は右辺の値から型を推論するため、宣言時に右辺の値(初期化子)がなければ型を決定できません。

“`csharp
// NG: 初期化されていないため、型を推論できない
var myVariable; // エラー CS0818: 暗黙的に型指定された変数は初期化されなければなりません

// OK
var myVariable = 10;
“`

ルール2: nullでの初期化は不可(C# 9以前)

null は「何もない」ことを示すリテラルで、それ自体は特定の型を持ちません。参照型(stringやクラスなど)にも、Null許容値型(int?など)にも代入できます。そのため、コンパイラは null から特定の型を一つに絞り込むことができません。

csharp
// NG (C# 9以前): null から型を推論できない
var myObject = null; // エラー CS0815: 暗黙的に型指定されたローカルを null に初期化することはできません

補足: C# 10以降では、プロジェクトの設定でNull許容参照型が有効になっている場合、var x = null;object? x = null; と解釈されることがあります。しかし、意図が不明確になりがちなので、null で初期化したい場合は string? myString = null; のように型を明示するのが一般的です。

ルール3: メソッドの戻り値の型や引数の型には使えない

メソッドのシグネチャ(名前、引数、戻り値の型)は、そのメソッドの「契約」を定義するものです。外部から見て型が明確でなければならないため、var で曖昧にすることは許されません。

“`csharp
// NG: メソッドの戻り値の型には使えない
public var MyMethod() // エラー
{
return “hello”;
}

// NG: メソッドの引数の型には使えない
public void AnotherMethod(var parameter) // エラー
{
// …
}
“`

ルール4: クラスのフィールドには使えない

var はローカル変数(メソッド内で宣言される変数)でのみ使用できます。クラスのメンバーであるフィールド(インスタンス変数や静的変数)には使用できません。

“`csharp
class MyClass
{
// NG: フィールドの宣言には var は使えない
// private var myField = “This is not allowed.”; // エラー CS0825

public void MyMethod()
{
    // OK: ローカル変数の宣言には var は使える
    var myLocalVariable = "This is allowed.";
}

}
“`
これは、C#のコンパイルの仕組みに関係しています。クラスのメンバーは、メソッド本体がコンパイルされるより前に型が確定している必要があるため、型推論に頼ることができないのです。

2. varを使うべきでない場面 – 可読性を損なうケース

技術的にはvarを使えるけれど、使わない方が良い(コードが分かりやすくなる)場面もあります。これは絶対的なルールではなく、チームや個人のコーディングスタイルによりますが、一般的に考慮すべきガイドラインです。

ケース1: 右辺から型が自明でない場合

var の大原則は「右辺を見れば型が分かる」ことです。逆に言えば、右辺を見ても型が分かりにくい場合は、var を使うべきではありません。

“`csharp
// 悪い例: GetValue() が何を返すか、この1行だけでは分かりにくい
// int? long? double? それともカスタム型?
var result = GetValue();

// 良い例: 型を明示することで、メソッドの戻り値の型が明確になる
int count = GetCount();
string name = GetName();
“`

メソッド名が GetCustomerById のように戻り値を強く示唆している場合は var でも問題ありませんが、ProcessDataCalculate のような汎用的な名前の場合は、型を明示する方が親切です。

ケース2: 数値リテラルの型を意図的に制御したい場合

C#の数値リテラルには、デフォルトの型があります。

  • 10 -> int
  • 10.5 -> double

var を使うと、このデフォルトの型が推論されます。

csharp
var price = 100; // int 型
var weight = 75.5; // double 型

しかし、金融計算などで高精度な計算が必要な decimal 型や、メモリを節約したい場合の float 型を使いたい場合、var は意図しない型を招く可能性があります。

“`csharp
// 意図: 金額なので decimal 型を使いたい
// 悪い例: var を使うと double 型になってしまう!
var money = 120.50; // money は double 型になる

// 良い例: mサフィックスで decimal であることを明示する
var moneyDecimal = 120.50m; // moneyDecimal は decimal 型になる

// さらに良い例: 型を明示することで、意図がより明確になる
decimal moneyExplicit = 120.50m;
“`

特に doubledecimal は内部的な表現が全く異なり、計算誤差の挙動も変わるため、非常に重要な違いです。このような場合は、var に頼らず、型を明記する方が安全で、コードの意図も明確になります。

結論として、var を使うかどうかの判断基準は「コードの可読性」です。 自分やチームのメンバーが、数ヶ月後にそのコードを読んだときに、一瞬で意味を理解できるのはどちらの書き方か? を常に考えるようにしましょう。


第4章: varにまつわる発展的なトピック

最後に、var に関連する少し発展的なトピックをいくつか紹介します。

1. ターゲット型のnew式 (C# 9.0以降)

C# 9.0から、var とは逆のアプローチが導入されました。それがターゲット型の newです。

これは、左辺の変数の型が分かっている場合に、右辺の new 式で型名を省略できる機能です。

“`csharp
// 従来の var
var person1 = new Person(“Taro”, 30);

// ターゲット型の new 式
Person person2 = new(“Hanako”, 25); // new Person(…) の Person を省略
``
これにより、
varを使うか、ターゲット型のnew式を使うか、開発者が好みのスタイルを選べるようになりました。フィールドの初期化のようにvar` が使えない場面では、後者が特に役立ちます。

csharp
class MyClass
{
// フィールドの初期化では var は使えないが、ターゲット型のnew式は使える
private List<string> _names = new();
}

2. varとラムダ式、メソッドグループ

ラムダ式やメソッドグループは、それ単体では特定のデリゲート型(ActionFunc<T>など)に確定しません。複数の互換性のあるデリゲート型に変換できる可能性があります。var は型を一つに絞り込めないため、これらを直接 var 変数に代入することはできません。

“`csharp
// NG: ラムダ式は Action か Func か、あるいは他のデリゲートか判断できない
var myAction = () => Console.WriteLine(“Hello”); // エラー

// NG: メソッドグループも同様
var myPrinter = Console.WriteLine; // エラー

// OK: キャストして型を明示すれば var を使える
var myActionOk = (Action)(() => Console.WriteLine(“Hello”));
var myPrinterOk = (Action)Console.WriteLine;

// OK: もちろん、最初から型を明示するのが最もシンプル
Action myActionSimple = () => Console.WriteLine(“Hello”);
Action myPrinterSimple = Console.WriteLine;
``
このルールは、「
var` はコンパイラが推論に迷うような曖昧な状況では使えない」という原則を示しています。


まとめ

長くなりましたが、C#の var キーワードについて、その本質から応用までを詳しく見てきました。最後に、重要なポイントをまとめておさらいしましょう。

  • varは型推論: var は、コンパイラが右辺から変数の型を推論し、自動で設定してくれる機能です。動的型付けの dynamic とは全くの別物で、C#の静的型付けの安全性を損ないません。

  • varのメリット:

    • 簡潔さ: 長い型名を繰り返し書く手間を省き、コードをスッキリさせます。
    • 可読性: 右辺の処理に集中でき、何が行われているかを理解しやすくなります。
    • 柔軟性: リファクタリング時に、メソッドの戻り値の型変更などに強くなります。
  • varを使うべき時:

    • new を使ったインスタンス化 (var p = new Person();)
    • LINQのクエリ結果、特に匿名型を扱う時
    • 右辺のメソッド呼び出しなどから型が自明な時 (var name = customer.GetName();)
    • foreach, using ステートメント内
  • varを避けるべき時(または注意すべき時):

    • 右辺から型が分かりにくい場合 (var result = ProcessData();)
    • 数値リテラルの型(int vs double vs decimal)を厳密に制御したい場合
    • コードを読む人にとって、型を明示した方が親切だと判断される場合
  • varが使えない場面:

    • 宣言のみで初期化しない場合
    • nullでの初期化(基本的に避けるべき)
    • メソッドの戻り値や引数の型
    • クラスのフィールド

var は、現代のC#プログラミングにおいて必須のツールです。しかし、それは決して思考停止して使うべきものではありません。「なぜここで var を使うのか?」「型を明示した方が分かりやすくないか?」と常に自問自答することが、質の高いコードを書く上で非常に重要です。

この記事が、あなたの var に対する理解を深め、より良いC#コードを書くための一助となれば幸いです。Happy Coding

コメントする

メールアドレスが公開されることはありません。 が付いている欄は必須項目です

上部へスクロール