みなさんこんにちは!ヒロポンです!
ログや例外メッセージに、プロパティ名を "UserName" って文字列で直書きしてませんか??
それ、プロパティをリネームした瞬間にログだけ古い名前のまま残るやつです。
コンパイルは通るから気づかない。障害調査でログを見て「あれ、このプロパティ今こんな名前だっけ」ってなる。
俺も一度やりました。直し忘れたログのせいで、原因追いをまるまる遠回りしたことがある。
これを防ぐのが nameof。ついでに、よく一緒に混乱する typeof と GetType() も整理しときます。この3つ、取れるものが全部違うので。
結論: 名前・静的な型・実行時の型で使い分ける
先に答えです。
nameof(x)… 識別子を名前の文字列にする("x")。コンパイル時に決まる。ログ・例外・プロパティ名にtypeof(T)… 型のType 情報を取る。コンパイル時に書いた静的な型GetType()… インスタンスの実行時の実際の型を取る。継承があるとtypeofと違う値になる
「名前がほしいなら nameof、書いた型の情報がほしいなら typeof、実物の型が知りたいなら GetType()」。
これだけ押さえれば迷いません。
なぜ3つあるのか — 取れるものが違うから
似た場面で出てくるから混ざりがち。え、どれ使うんやったっけ??ってなるやつですが、返すものが根本から違います。
nameof が返すのはただの文字列。
typeof と GetType() が返すのは System.Type というオブジェクト、つまり型情報です。
そして typeof と GetType() の差は、いつの型か。typeof はコードに書いた静的な型(コンパイル時)、GetType() は目の前のインスタンスの実際の型(実行時)。
継承や多態が絡むと、この2つは違う答えを返します。
「文字列か型か」「コンパイル時か実行時か」。この2軸で見ると、スッと分かれます。
最短対処: コピペで動く使い分け
nameof — 名前を文字列で取る
using System;
class User { public string Name { get; set; } }
class Program
{
static void Main()
{
var user = new User { Name = "田中" };
Console.WriteLine(nameof(user)); // user
Console.WriteLine(nameof(user.Name)); // Name (末尾のメンバ名だけ)
Console.WriteLine(nameof(User)); // User
}
}
実行結果:

例外メッセージで使うと、こんな感じで効いてきます。
void SetAge(int age)
{
if (age < 0)
throw new ArgumentOutOfRangeException(nameof(age), "年齢は 0 以上で指定してください");
}
age をリネームすれば nameof(age) も勝手に追従する。文字列直書きと違って、直し忘れが起きません!!
typeof と GetType() — 型を取る(継承で差が出る)
using System;
class Animal { }
class Dog : Animal { }
class Program
{
static void Main()
{
Animal a = new Dog();
Console.WriteLine(typeof(Animal)); // 書いた型
Console.WriteLine(a.GetType()); // 実際の型
Console.WriteLine(a.GetType() == typeof(Dog)); // 実型で判定
Console.WriteLine(a is Animal); // 派生も含めて判定
}
}
実行結果:

a は Animal 型の変数だけど、中身は Dog。
typeof(Animal) は書いた型のまま Animal。a.GetType() は実物の Dog を返します。こんな感じで、書いた型と実物の型がずれる。
ここが一番の使いどころの違いです。
ハマりポイント: 知らないと詰まるやつ
nameof(x.Prop) は「末尾のメンバ名だけ」
nameof(user.Name) が返すのは "Name" だけ。
ん?user.Name 全部ちゃうの?ってなるけど、"user.Name" みたいなパス全体にはなりません。
「ログに user.Name ってフルで出したいのに、Name しか出ない」ってなるやつ。フルで出したいなら $"{nameof(user)}.{nameof(user.Name)}" みたいに自分で組み立てます。
typeof(List<>) は「中身を書かない」開いた総称型
ジェネリックの型情報を取る時、要素の型を書くかどうかで意味が変わります。
using System;
using System.Collections.Generic;
class Program
{
static void Main()
{
Console.WriteLine(typeof(List<int>)); // 閉じた型 (要素は int)
Console.WriteLine(typeof(List<>)); // 開いた総称型 (要素型は未指定)
}
}
typeof(List<>) は「要素の型をまだ決めてない List」を表す、開いた総称型(open generic type)です。
リフレクションで「List かどうか」だけ判定したい時に使います。typeof(List<T>) と間違えやすいので注意。
GetType() を null に呼ぶと落ちる
GetType() は Object のインスタンスメソッド。だから変数が null だと NullReferenceException で落ちます。
string s = null;
var t = s.GetType(); // NullReferenceException
null の可能性がある値の型を見たいなら、先に null チェックするか、s is string みたいに is 演算子を使う。is は null なら素直に false になってくれます。
現場メモ: ログを壊さないために
ここからは現場メモ。俺が業務で固定してるのは「ログと例外に出す名前は、文字列直書きせず nameof で書く」ってルールです。
数年前、流通系の基幹でプロパティを一括リネームしたことがありました。「これで統一完了、よし帰ろ」ってなってたんですよね。
で、翌日に障害調査。ログに出てる名前と実際のプロパティ名が食い違ってて、grep しても当たらない。原因のコードにたどり着くまで、30分ほど無駄に溶かしました。地味にきつい。
原因はシンプルで、ログ文字列の直書きだけが古い名前で残ってた。それだけ。
それ以来、ArgumentNullException も LogError の中のメンバ名も、全部 nameof で書くようにしてます。こんな感じにしておくと、リネームすればログも例外も勝手に追従する。リファクタで壊れないんですよね。
今だったら「最初から nameof で書いとけよ」って自分に言いたいですよ?でも当時は、ログの文字列なんて雰囲気で直書きしてたんですよね。
型判定の方は「書いた型で見たいか、実物で見たいか」で typeof と GetType() を選ぶ。多態で実型ごとに分岐したい時だけ GetType()、それ以外はだいたい is か typeof で足ります。
まとめ
3つの違いを一枚で。

文字列直書きから nameof に変えるだけで、リファクタでログが壊れる事故が消えます。

「名前は nameof、書いた型は typeof、実物の型は GetType()」。
この3つを押さえておくと、リネームで壊れにくいログが書けます。障害調査で名前の食い違いに時間を溶かすこともなくなる。
そこが、この3つを使い分ける一番の効きどころです。
よくある質問
nameof はパフォーマンスに影響しますか
しません。nameof はコンパイル時に文字列リテラルへ置き換わるので、実行時には「ただの文字列」がそこにあるだけです。名前を計算するコストはゼロ。リフレクションみたいな重さもありません。
プロパティ名を文字列で持ちたい既存コードも nameof に変えるべきですか
ログ・例外・INotifyPropertyChanged のプロパティ名みたいに、リネームで壊れると困る箇所は変える価値があります。逆に、外部仕様で決まってる固定の文字列(API のキー名など)は、コード上の識別子と連動しないので無理に nameof にしなくて大丈夫です。
GetType() と is 演算子はどう違いますか
obj.GetType() == typeof(Foo) は実型が完全に Foo かを判定します(派生クラスは false)。obj is Foo はFoo に代入可能かを判定するので、Foo の派生クラスでも true になります。「ちょうど Foo だけ」なら GetType、「Foo かその仲間」なら is、って感じで使い分けます。
次に読むべき記事





以上!
同じ「文字列直書きでログ壊した」経験がある人いたら、どんどんシェア待ってるぜ!!
執筆者
バイブス父さん — 業務 SE 7 年 (SIer 正社員 2 / フリーランス 5)。 現職は SEO 直轄部の AI アドバイザー兼 PL、 副業で中小 SIer の CTO。 SIer の正社員からフリーランスに転じ、 複数のエージェント経由で案件を回してきた経験ベースで「業務 SE 視点」 の技術 + キャリア記事を書いています。
🐦 X: @hiro_progra0524 (日々の現場メモ更新中)
📝 About Me で経歴詳細を見る



