動くコード図鑑技術記事現場の渡り方キャリア論すべての記事About
技術記事

C# の nameof / typeof / GetType() の使い分け(リネームに強いログを書く)

バイブス父さん
現役の業務SE
2026年9月14日11 min read広告 (PR) を含む場合があります
C# の nameof / typeof / GetType() の使い分け(リネームに強いログを書く)

みなさんこんにちは!ヒロポンです!

ログや例外メッセージに、プロパティ名を "UserName" って文字列で直書きしてませんか??

それ、プロパティをリネームした瞬間にログだけ古い名前のまま残るやつです。

コンパイルは通るから気づかない。障害調査でログを見て「あれ、このプロパティ今こんな名前だっけ」ってなる。

俺も一度やりました。直し忘れたログのせいで、原因追いをまるまる遠回りしたことがある。

これを防ぐのが nameof。ついでに、よく一緒に混乱する typeofGetType() も整理しときます。この3つ、取れるものが全部違うので。

結論: 名前・静的な型・実行時の型で使い分ける

先に答えです。

  • nameof(x) … 識別子を名前の文字列にする("x")。コンパイル時に決まる。ログ・例外・プロパティ名に
  • typeof(T) … 型のType 情報を取る。コンパイル時に書いた静的な型
  • GetType() … インスタンスの実行時の実際の型を取る。継承があると typeof と違う値になる

「名前がほしいなら nameof、書いた型の情報がほしいなら typeof、実物の型が知りたいなら GetType()」。

これだけ押さえれば迷いません。

なぜ3つあるのか — 取れるものが違うから

似た場面で出てくるから混ざりがち。え、どれ使うんやったっけ??ってなるやつですが、返すものが根本から違います。

nameof が返すのはただの文字列

typeofGetType() が返すのは System.Type というオブジェクト、つまり型情報です。

そして typeofGetType() の差は、いつの型か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
    }
}

実行結果:

nameof の実行結果 — nameof(user) は user、nameof(user.Name) は Name、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);                    // 派生も含めて判定
    }
}

実行結果:

typeof と GetType の実行結果 — typeof(Animal) は Animal、a.GetType() は Dog、実型判定とis演算子の結果

aAnimal 型の変数だけど、中身は Dog

typeof(Animal) は書いた型のまま Animala.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分ほど無駄に溶かしました。地味にきつい。

原因はシンプルで、ログ文字列の直書きだけが古い名前で残ってた。それだけ。

それ以来、ArgumentNullExceptionLogError の中のメンバ名も、全部 nameof で書くようにしてます。こんな感じにしておくと、リネームすればログも例外も勝手に追従する。リファクタで壊れないんですよね。

今だったら「最初から nameof で書いとけよ」って自分に言いたいですよ?でも当時は、ログの文字列なんて雰囲気で直書きしてたんですよね。

型判定の方は「書いた型で見たいか、実物で見たいか」で typeofGetType() を選ぶ。多態で実型ごとに分岐したい時だけ GetType()、それ以外はだいたい istypeof で足ります。

まとめ

3つの違いを一枚で。

nameof / typeof / GetType() の取れるもの・評価タイミング・継承での挙動・用途の比較表

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

ログと例外のメンバ名を文字列直書きから nameof に置き換える差分

「名前は nameof、書いた型は typeof、実物の型は GetType()」。

この3つを押さえておくと、リネームで壊れにくいログが書けます。障害調査で名前の食い違いに時間を溶かすこともなくなる。

そこが、この3つを使い分ける一番の効きどころです。

よくある質問

nameof はパフォーマンスに影響しますか

しません。nameof はコンパイル時に文字列リテラルへ置き換わるので、実行時には「ただの文字列」がそこにあるだけです。名前を計算するコストはゼロ。リフレクションみたいな重さもありません。

プロパティ名を文字列で持ちたい既存コードも nameof に変えるべきですか

ログ・例外・INotifyPropertyChanged のプロパティ名みたいに、リネームで壊れると困る箇所は変える価値があります。逆に、外部仕様で決まってる固定の文字列(API のキー名など)は、コード上の識別子と連動しないので無理に nameof にしなくて大丈夫です。

GetType() と is 演算子はどう違いますか

obj.GetType() == typeof(Foo)実型が完全に Foo かを判定します(派生クラスは false)。obj is FooFoo に代入可能かを判定するので、Foo の派生クラスでも true になります。「ちょうど Foo だけ」なら GetType、「Foo かその仲間」なら is、って感じで使い分けます。

次に読むべき記事

以上!

同じ「文字列直書きでログ壊した」経験がある人いたら、どんどんシェア待ってるぜ!!


執筆者

バイブス父さん — 業務 SE 7 年 (SIer 正社員 2 / フリーランス 5)。 現職は SEO 直轄部の AI アドバイザー兼 PL、 副業で中小 SIer の CTO。 SIer の正社員からフリーランスに転じ、 複数のエージェント経由で案件を回してきた経験ベースで「業務 SE 視点」 の技術 + キャリア記事を書いています。

🐦 X: @hiro_progra0524 (日々の現場メモ更新中)
📝 About Me で経歴詳細を見る


この記事のコードと手順は ぜんぶ動作検証済み。 安心して現場で試してくれ。
バイブス父さん

現役の業務SE。C# / SQL Server 保守の現場から、コードも人もキャリアも全部書く。 実体験ベース。

運営者について