varは便利だ。宣言が短くなり、型名の重複も消える。
ただ、その便利さで「右辺を見ないと何の型か分からない宣言」が増えると、読む速度が落ちる。
問題はvarではなく、型と役割が宣言から消えてしまう書き方。
右辺で型が読めるか、型名が役割の説明になっているか。
この2点だけで使い分けの基準を揃える。
このページのゴール
- varの正体(何が起きているか)を最短で掴む
- 「右辺だけで型が分かる/分からない」を判断軸として明文化する
- 「型名が役割を説明する」場面を判例として残す
- LINQ/戻り値/数値リテラルの迷いどころを、表で明確化する
- 指摘コメントがブレないレビュー基準へ落とす
先に切り分ける(似ているが別物)
- varはコンパイル時に型が決まる
実行時に挙動が変わる話ではなく、読みやすさ(情報の出し方)の話になる
- dynamicは実行時解決になる
静的チェックが効きにくく、議論の論点が別になる
- varはローカル変数専用
フィールド/プロパティ/引数の話へ広げると運用が壊れやすい
再発防止の掟: varの使い分け基準
- 右辺だけで型が分かる場合のみvarを使う
なぜ: 型探索の往復が消え、読み筋が切れにくくなるため
- 右辺だけでは型が分からない場合、型名がその変数の役割を説明しているなら型を書く
なぜ: 役割の説明が消えると誤読が増え、変更影響も追いにくくなるため
- 戻り値varを採用する場合、変数名だけで用途が伝わることが前提になる
なぜ: 型も用途も追えない状態が最も事故りやすいため
- 型推論でも役割がその場で決まる形は許可する(out varなど)
なぜ: 行内で型と用途が見え、読みが止まりにくくなるため
迷いどころを捌く順(最短)
- 右辺だけで型が分かるか
- 型名がその変数の役割を説明しているか
- 変数名だけで用途が伝わるか
- 1行に変換が詰まっていないか(LINQ/チェーン)
「右辺だけで型が分かる」の具体
| 右辺の形 |
例 |
判定 |
コメント |
| newで型名が出る |
new List<int>() |
分かる |
varでノイズが減る |
| キャスト/型指定がある |
(Foo)x, Foo.Parse(s)
|
分かる |
右辺に型名が居る |
| 文字列リテラル |
"abc" |
分かる |
迷いが少ない |
| 数値リテラル(サフィックスあり) |
0m, 1.0d, 1.0f
|
分かる |
意図が右辺に出る |
| 数値リテラル(サフィックスなし) |
0, 1
|
迷いが出る |
int/long/decimalなどの意図が消えることがある |
| メソッド戻り値 |
GetUser() |
原則分からない |
型名が右辺に居ない |
| LINQ/チェーン |
q.Where(...).Select(...) |
分からない寄り |
段数が増えるほど情報が潰れる |
| out var |
TryParse(..., out var n) |
分かる寄り |
その行で型が決まるため読み筋が立つ |
例: 使い分けの最小セット
// 右辺だけで型が分かる -> var
var list = new List<int>();
var sb = new StringBuilder();
var price = 0m; // 右辺でdecimalだと分かる
// 型名が役割を説明する -> 型を書く
UserId userId = GetUserId();
Money totalPrice = GetTotalPrice();
// out varは行内で型が決まる -> 許可
if (int.TryParse(text, out var n))
{
// nはintとして読める
}
// 戻り値varは「変数名で用途が伝わる」時に成立しやすい
var activeCount = GetActiveCount();
判例(OK/NG)
| 観点 |
OK例 |
NG例 |
理由(事故) |
レビューで見る所 |
| 右辺だけで型が分かる(new) |
var list = new List<int>(); |
var list = CreateList(); |
戻り値は型が隠れ、型探索が増える |
右辺に型名が出ているか |
| 型名が役割を説明する |
UserId userId = GetUserId(); |
var userId = GetUserId(); |
UserIdが見えないと「何のIDか」が揺れる |
型名が役割説明になっているか |
| 命名で用途が伝わる |
var count = users.Count; |
var data = GetData(); |
dataは用途が曖昧で、型も追いにくい |
変数名が用途を表しているか |
| 数値の型意図 |
decimal price = 0m; |
var price = 0; |
金額なのにint扱いで桁/丸めの事故が起きる |
サフィックス(m/d/f)や型意図があるか |
| LINQの読み筋 |
var ids = users.Select(u => u.Id); |
var x = a.B().C().D().E(); |
変換が詰まると意図も型も追えない |
段数と分割ポイント |
| out var |
TryParse(s, out var n) |
outのみで後段が曖昧 |
後段で用途が揺れ、誤用が入りやすい |
その場で意味が見えているか |
容赦なき断罪: レビュー観点
| 観点 |
ありがちな見落とし |
事故の形 |
指摘コメント例(直球禁止) |
| 右辺だけで分かるか |
戻り値でもvarに寄せる |
型探索が増え、変更影響も追いにくい |
右辺だけでは型が見えにくいため、型を残す書き方の方が読み筋が立ちそう |
| 型名が役割を説明するか |
UserId/Moneyなどをvarで隠す |
役割が揺れて誤読が入る |
この型名は役割説明になっているため、ここは型を残す方が安全そう |
| 命名で用途が伝わるか |
data/ret/tmpが混じる |
用途も型も追えず、デバッグが詰む |
変数名だけでは用途が伝わりにくいため、型か命名で分かる形に寄せたい |
| 数値の意図が出ているか |
0/1をvarで受ける |
int前提で桁/丸め/単位が崩れる |
数値に型意図がありそうなので、型またはサフィックスで明確にした方が良さそう |
| チェーンの情報密度 |
1行に変換が密集 |
条件追加で崩れ、検証が難化 |
変換が多く意図が密集しているため、段階で名前を付ける構成が読みやすい |
new()の扱い |
target-typed newを雑に混ぜる |
コンパイル不能/誤解を誘発 |
new()は左辺の型が前提になるため、ここは左辺で型を残す方が意図が伝わる |
禁書庫A: 逆引き(症状→原因→対策)
| 症状 |
ありがちな原因 |
切り分け(見る場所) |
最短の対処 |
再発防止(ルール化) |
| varを使うか迷って止まる |
「右辺だけで分かる」の基準が無い |
右辺に型名/サフィックスがあるか |
右辺だけで分かるならvar、それ以外は次の判定へ |
「右辺だけで分かる場合のみvar」を規約化 |
| 何を扱う変数か読めない |
型名が役割説明なのにvarで隠れている |
型名の語彙(UserId/Money等) |
型を明示して役割を分かる形にする |
「型名が役割説明なら型を書く」を運用化 |
var dataが増殖する |
命名が用途を表していない |
変数名の語彙 |
用途が分かる命名へ、または型で補う |
曖昧語(data/ret/tmp)の抑制 |
| LINQが読めない |
変換が詰まり過ぎ |
チェーン段数 |
中間結果に名前を付けて分割 |
「チェーンは段階ごとに命名」 |
| 数値の型がズレる |
サフィックス無しリテラルをvarで受ける |
0/1/1.0の形 |
型またはサフィックスで明示 |
金額/率/時間/単位は型で意図を残す |
禁書庫B: チートシート(決め打ち)
-
var x = new Foo()は通る(右辺だけで分かる)
-
var x = GetFoo()は立ち止まる(右辺だけでは分からない)
-
UserId/Moneyなど型名が役割説明なら、varで隠さず型を書く
-
var price = 0mは通るが、var price = 0は型意図を疑う
- チェーンが長い場合、型の議論より先に分割で読み筋を守る
-
var x = new();は成立しない(左辺の型が必要)
関連トピック