nameof演算子とは?

nameof演算子は、変数・メソッド・型などの「識別子の名前」をコンパイル時に文字列として取得するためのC#の言語機能です。

文字列リテラルで名前を直接書く代わりに使うことで、リネーム時の追従漏れやタイプミスを防げます。

あくまでコンパイル時に解決される仕組みのため、実行時のコストは発生しません。

基本的な使い方

nameofの括弧の中に対象の識別子を書くと、その識別子名が文字列として返ります。

using UnityEngine;

public class NameofSample : MonoBehaviour
{
    private int _hp = 100;

    private void Start()
    {
        // フィールド名を文字列化
        Debug.Log(nameof(_hp));          // "_hp"

        // 型名を文字列化
        Debug.Log(nameof(NameofSample)); // "NameofSample"

        // メソッド名を文字列化
        Debug.Log(nameof(Start));        // "Start"
    }
}

名前空間を含めてフルパスを取りたい場合でも、最後の識別子だけが返る点には注意が必要です。

Unityでの典型的な活用例

Invokeのメソッド指定

MonoBehaviourInvokeは文字列でメソッドを指定する仕様のため、タイプミスがあっても気付きにくいという欠点があります。

nameofを使うとメソッド参照をそのまま渡せるため、リネームしてもエラーですぐに気付けます。

using UnityEngine;

public class InvokeSample : MonoBehaviour
{
    private void Start()
    {
        // 文字列直書きだとタイプミスに気付きにくい
        // Invoke("FireBullet", 1f);

        // nameofを使えば存在しないメソッドはコンパイルエラーになる
        Invoke(nameof(FireBullet), 1f);
    }

    private void FireBullet()
    {
        Debug.Log("発射");
    }
}

InvokeRepeatingCancelInvokeも同様に文字列指定なので、同じ方法で安全に書き換えられます。

SendMessageのメソッド指定

SendMessage系のメソッドも文字列でメソッド名を指定する仕様です。

こちらもnameofを組み合わせることで、ターゲット側のメソッド名を直接参照できます。

using UnityEngine;

public class EnemyController : MonoBehaviour
{
    // SendMessageから呼び出される側のメソッド
    public void TakeDamage(int amount)
    {
        Debug.Log($"被弾: {amount}");
    }
}

public class AttackSender : MonoBehaviour
{
    [SerializeField] private GameObject _target;

    private void Start()
    {
        // 受け取り側のメソッド名を文字列リテラルではなくnameofで指定
        _target.SendMessage(nameof(EnemyController.TakeDamage), 10);
    }
}

Debug.Logでの変数・プロパティ名表示

デバッグログに値を出力する際、変数名やプロパティ名もセットで出すと原因究明がしやすくなります。

nameofを使えばリネームしても文字列側を直す必要がありません。

using UnityEngine;

public class LogSample : MonoBehaviour
{
    [SerializeField] private int _score;

    private void Start()
    {
        // "_score = 0" のように出力される
        Debug.Log($"{nameof(_score)} = {_score}");
    }
}

例外メッセージでの引数名指定

ArgumentNullExceptionArgumentOutOfRangeExceptionなど、引数名を渡す例外でもnameofは定番です。

リファクタリングで引数名が変わったとき、例外側の文字列が古いまま残るのを防げます。

using System;

public class DamageCalculator
{
    public int Calculate(string targetName, int damage)
    {
        if (targetName == null)
        {
            // 引数名をnameofで渡す
            throw new ArgumentNullException(nameof(targetName));
        }

        if (damage < 0)
        {
            throw new ArgumentOutOfRangeException(nameof(damage), "ダメージ値は0以上で指定してください");
        }

        return damage;
    }
}

nameofを使うメリット

リファクタリング耐性

nameofで参照されている識別子は、RiderやVSCodeなどのIDEのリネーム機能で一緒に書き換わります。

文字列リテラルのままだとリネームから取り残されがちで、後から「あれ、なぜ動かない?」と原因究明に時間を取られることもあります。 nameofを挟んでおけば、こうした追従漏れはコンパイラ任せにできます。

タイプミスの防止

存在しない識別子をnameofに渡すとコンパイルエラーになります。

実行してから「メソッド名のスペルが違ってInvokeが動かない」といったトラブルを未然に防げるのは、地味ですが大きな効果です。 特にチーム開発でメソッド名がよく変わるプロジェクトほど、その恩恵を実感しやすいです。

注意点

フルパスは取得できない

nameof(UnityEngine.Debug)と書いても結果は"Debug"になります。

名前空間付きの完全修飾名が欲しい場合は、typeof(T).FullNameなど別の方法を併用しましょう。

名前を持たない要素は対象外

匿名型のメンバーのように、型や変数として定義されていない要素にはnameofを使えません。

また、メソッドのオーバーロードを区別することはできず、あくまで「識別子の見た目の名前」を返すだけです。 同名で引数違いのメソッドが複数あっても、nameofの結果はどれも同じ文字列になります。

文字列ベースAPIすべてが安全になるわけではない

Animator.SetTrigger("Jump")のような、文字列の中身が「実際にAnimator Controller側で定義されているか」まではnameofでは検証できません。

C#側に対応する識別子がある場合に限り、リネーム耐性のメリットを得られます。

まとめ

nameof演算子は、文字列で識別子名を書いていた箇所を「コンパイラに守られた書き方」に変えてくれる便利な機能です。

メソッド名やフィールド名を文字列リテラルで指定していたところをnameofに置き換えるだけで、リファクタリング時の追従漏れやタイプミスを大きく減らせます。

特にInvokeSendMessageDebug.Logでの変数名表示、例外の引数名指定などは効果が分かりやすい場面ですね。

小さな書き換えで安全性が一段上がるので、文字列で名前を指定している箇所を見かけたら、ぜひ置き換えてみてください。