var(型推論)(C#)
『C#』の var キーワードを使うと、右辺の式からコンパイラが型を自動推論するため、型名を省略して変数を宣言できます。あくまでコンパイル時に型が確定する静的型付けであり、実行時に型が変わる動的型付けとは異なります。型名が長くなりがちな場面では可読性が上がりますが、使えない場所(フィールド・引数・戻り値など)もあります。
構文
// var を使ったローカル変数の宣言です。右辺の値から型が推論されます。
var variableName = value;
// 匿名型と組み合わせる場合は必ず var を使います(匿名型には型名がありません)。
var obj = new { PropertyName1 = value1, PropertyName2 = value2 };
// var は宣言と同時に初期化しなければなりません。
// var x; // コンパイルエラー: 初期化子が必要です。
// var x = null; // コンパイルエラー: null だけでは型を推論できません。
var が使える場所と使えない場所
| 場所 | var の使用 | 理由 |
|---|---|---|
| ローカル変数(メソッド内) | 使用可 | 右辺の式から型を推論できます。 |
| foreach の反復変数 | 使用可 | コレクションの要素型から推論されます。 |
| using 宣言・using ステートメント | 使用可 | 右辺から型を推論できます。 |
| クラスのフィールド | 使用不可 | フィールドは初期化子なしに宣言できる上、コンパイラが推論できない場合があります。 |
| メソッドの引数 | 使用不可 | 呼び出し側から型情報が来るため、宣言側では推論できません。 |
| メソッドの戻り値型 | 使用不可 | 戻り値の型は明示的に宣言する必要があります。 |
| プロパティの型 | 使用不可 | フィールドと同様の理由です。 |
サンプルコード
VarBasic.cs
using System;
using System.Collections.Generic;
class VarBasic {
static void Main() {
// --- var による基本的な型推論 ---
// var を使っても型は静的に確定します。右辺の型がそのまま変数の型になります。
var name = "item_x"; // string として推論されます。
var power = 250; // int として推論されます。
var ratio = 50.0; // double として推論されます。
var isActive = true; // bool として推論されます。
Console.WriteLine(name + " のスコア: " + power);
Console.WriteLine("倍率: " + ratio);
Console.WriteLine("アクティブ: " + isActive);
// var で推論された変数は型固定です。別の型の値は代入できません。
// power = "文字列"; // コンパイルエラー: int 型に string は代入できません。
// --- 型名が長いときに var が特に便利です ---
// Dictionary<string, List<int>> と書く代わりに var で短くできます。
var scores = new Dictionary<string, List<int>>();
scores["item_x"] = new List<int> { 100, 250, 500 };
scores["item_y"] = new List<int> { 80, 200, 450 };
foreach (var entry in scores) {
// entry は KeyValuePair<string, List<int>> として推論されます。
Console.WriteLine(entry.Key + " の記録数: " + entry.Value.Count);
}
// --- foreach でも var を使えます ---
var items = new List<string> { "item_x", "item_y", "item_z", "item_w" };
foreach (var item in items) {
Console.WriteLine(" - " + item);
}
}
}
コンパイルして実行すると次のようになります。
dotnet script VarBasic.cs item_x のスコア: 250 倍率: 50 アクティブ: True item_x の記録数: 3 item_y の記録数: 3 - item_x - item_y - item_z - item_w
VarAnonymousType.cs
using System;
class VarAnonymousType {
static void Main() {
// --- 匿名型には必ず var を使います ---
// 匿名型は型名を持たないため、var 以外で受け取る方法がありません。
var entry = new {
Name = "item_x",
Score = 250,
Category = "type_a",
IsActive = true
};
Console.WriteLine(entry.Name + "(" + entry.Category + ")スコア: " + entry.Score);
Console.WriteLine("アクティブ: " + entry.IsActive);
// 匿名型のプロパティは読み取り専用です(値の変更はできません)。
// entry.Score = 450; // コンパイルエラー: 読み取り専用プロパティです。
// --- 複数の匿名型オブジェクトを配列にまとめます ---
// 各要素が同じ構造(プロパティ名・型)であれば配列にできます。
var entries = new[] {
new { Name = "item_x", Score = 250, Category = "type_a" },
new { Name = "item_y", Score = 200, Category = "type_a" },
new { Name = "item_z", Score = 150, Category = "type_b" },
new { Name = "item_w", Score = 100, Category = "type_c" }
};
Console.WriteLine();
Console.WriteLine("=== スコアランキング ===");
foreach (var f in entries) {
// f は匿名型として推論されます。Name / Score / Category にアクセスできます。
Console.WriteLine(f.Name + "(" + f.Category + "): " + f.Score);
}
// --- LINQ と組み合わせた匿名型の活用 ---
// 必要なプロパティだけを抜き出して新しい匿名型に射影します。
// ※ LINQ の詳細は Enumerable.Select のページを参照してください。
Console.WriteLine();
Console.WriteLine("=== type_a のみ ===");
foreach (var f in entries) {
if (f.Category == "type_a") {
// 匿名型のまま扱えます。
Console.WriteLine(f.Name + ": score " + f.Score);
}
}
}
}
コンパイルして実行すると次のようになります。
dotnet script VarAnonymousType.cs item_x(type_a)スコア: 250 アクティブ: True === スコアランキング === item_x(type_a): 250 item_y(type_a): 200 item_z(type_b): 150 item_w(type_c): 100 === type_a のみ === item_x: score 250 item_y: score 200
VarReadability.cs
using System;
using System.Collections.Generic;
class VarReadability {
static void Main() {
// --- var を使うべき場面・使わない方がよい場面 ---
// ◎ 右辺の new から型が自明なので var が読みやすいです。
var scores = new Dictionary<string, int>();
scores["item_x"] = 250;
scores["item_y"] = 200;
scores["item_z"] = 500;
// ◎ foreach の反復変数は右辺のコレクション型から明らかです。
foreach (var kvp in scores) {
Console.WriteLine(kvp.Key + ": " + kvp.Value);
}
Console.WriteLine();
// △ 右辺がメソッド呼び出しで戻り値の型が不明瞭な場合は型名を書いた方が親切です。
// var result = GetItemName(); // GetItemName() が何を返すかコードを読まないとわかりません。
// string result = GetItemName(); // string と明示する方が読みやすいです。
// ◎ リテラルが明確な場合は var でも問題ありません。
var label = "item_x"; // string だと一目でわかります。
var level = 250; // int だと一目でわかります。
// △ 数値リテラルは型が曖昧になることがあります。
var ratio = 1.5;
double ratioExplicit = 1.5;
Console.WriteLine(label + "(レベル " + level + ")倍率: " + ratio);
// --- var は null だけでは使えません ---
// null だけでは型を推論できないため、コンパイルエラーになります。
// var empty = null; // コンパイルエラーです。
// null を代入したい場合は型を明示します。
string emptyName = null;
Console.WriteLine("emptyName is null: " + (emptyName == null));
// --- フィールド・引数・戻り値には var を使えません ---
// 以下はすべてクラスのフィールドや引数には書けない例です(コメントで示します)。
//
// class Example {
// var field = 9000; // コンパイルエラー: フィールドには使えません。
// void Method(var x) { } // コンパイルエラー: 引数には使えません。
// var GetValue() { ... } // コンパイルエラー: 戻り値型には使えません。
// }
Console.WriteLine("var の使いどころを確認しました。");
}
}
コンパイルして実行すると次のようになります。
dotnet script VarReadability.cs item_x: 250 item_y: 200 item_z: 500 item_x(レベル 250)倍率: 1.5 emptyName is null: True var の使いどころを確認しました。
よくあるミス
var を動的型付けだと誤解する
var で宣言した変数の型はコンパイル時に確定します。一度型が決まると、別の型の値を代入することはできません。JavaScript の var は実行時に型が変わる動的型付けですが、C# の var は静的型推論です。名前が同じため混同されることがありますが、動作は根本的に異なります。
var count = 10; // int として推論されます。 // count = "text"; // コンパイルエラー: int 型に string は代入できません。
初期化子なしで var を使う
var は右辺の式から型を推論するため、初期化子が必須です。宣言だけの var x; や、var x = null; はコンパイルエラーになります。null を代入したい場合は型を明示して宣言します。
// var x = null; // コンパイルエラー: null だけでは型を推論できません。 string x = null; // 型を明示すれば null を代入できます。
概要
『C#』の var はコンパイル時に型が確定する静的型推論です。実行時に型が変わる動的型付け(JavaScript の var など)とは根本的に異なり、一度確定した型の変数に別の型の値は代入できません。右辺から型が明らかな場面(new によるインスタンス生成・リテラルの代入など)では積極的に使うことで冗長な型名の繰り返しを避けられます。
var が使えるのはローカル変数(メソッド内の変数・foreach の反復変数・using 宣言)のみです。クラスのフィールド・メソッドの引数・メソッドの戻り値型には使えません。また、var x; のように初期化子なしの宣言や、var x = null; のように null だけでは型を推論できないためコンパイルエラーになります。
匿名型(new { Name = "item_x", Score = 250 })は型名を持たないため、受け取る変数には必ず var を使います。匿名型のプロパティは読み取り専用で、後から値を変更することはできません。LINQ のクエリ式で必要なプロパティだけを射影する場合に匿名型と var の組み合わせはよく登場します。
可読性の観点では、右辺のメソッド呼び出し結果を var で受け取ると戻り値の型が不明瞭になることがあります。型が自明でない場合は明示的な型名を書いた方が、コードを読む人にとって親切です。チームのコーディング規約に従い、var の使いどころを統一するのが一般的です。
記事の間違いや著作権の侵害等ございましたらお手数ですがこちらまでご連絡頂ければ幸いです。