くうと徒然なるままに

モバイルアプリを作りながらバックエンドも作っています。

C# で クラス名を String で取得する方法

やること

  • Class の名前を動的に文字列で取得する

環境

やり方

方法1. nameof を利用してクラス名を取得

nameof は クラス名を取得するための C# の言語機能です。 この方法を利用すればクラス名を取得することができます。

nameof を利用してクラス名を取得するコード

class hoge
{
    public string getClassName() => nameof(hoge);
}

問題点

このメソッドを定義したクラスを継承したクラスからこの関数を呼び出すと、恐らく望んだ結果にはならないです。 ので、テストヘルパー等を作成しているときに現在のクラス名(継承している場合は継承先の) を取得したい時には少し力不足です。

問題点を再現したコード

Program.cs

class Program
{
    static void Main(string[] args)
    {
        Console.WriteLine("*** nameof() を利用してクラス名を取得 ***");
        var parent = new parent();
        Console.WriteLine(parent.getClassName());
        var child = new child();
        Console.WriteLine(child.getClassName());
    }
}
public class parent {
    public string getClassName() => nameof(parent);
}
class child:parent{
}

出力

*** nameof() を利用してクラス名を取得 ***
parent
parent

方法2. 動的にクラス名を取得する

動的にクラス名を取得することで問題を回避しました。 C# で動的にあれこれするためには、リフレクションという言語機能を利用します。

しかし、今回の場合は、クラス名を取得するだけなのでそこまで難しくは無いかと思います。

要点解説

今回は、動的にクラス名を取得します。 クラス名を取得するためには、クラス名を取得元の型宣言(個人的には型情報って呼んでる)を取得しその型宣言を元に クラス名を取得します。

正確には、メンバーの名前を取得します。また、完全修飾名では無いので場合によっては別のクラスをで実行しているのに同じクラス名の場合は、同じ文字列が取得してしまう。ってことがおきます。 (実際に業務とかで使うコードはそのままコピペして使う人はいないと思うので問題ないですが)

補足

完全修飾名 を取得する方法

完全修飾名 を取得する場合には、 以下のコードで取得できるようです。

情報をいただいた @sha256 さん、ありがとうございます。

Type.Fullname

https://msdn.microsoft.com/ja-jp/library/system.type.fullname(v=vs.110).aspx

アセンブリ修飾も加えて取得する方法

アセンブリ名も付随して取得したい場合は、以下のようすることで取得できるようです。 また、 Static class に対しては GetType()が使えないため、 typeof() を使わないといけないそうです。

情報をいただいた 大澤 正 さん、ありがとうございます。

Type.AssemblyQualifiedName

https://msdn.microsoft.com/ja-jp/library/system.type.assemblyqualifiedname(v=vs.110).aspx

https://msdn.microsoft.com/ja-jp/library/system.reflection.memberinfo.name(v=vs.110).aspx

動的にクラス名を取得するコード

Program.cs

class Program
{
    static void Main(string[] args)
    {
        Console.WriteLine("*** リフレクションを利用して動的取得 ***");
        var parent = new parent();
        Console.WriteLine(parent.getClassName());
        var child = new child();
        Console.WriteLine(child.getClassName());
    }
}
public class parent {
    public string getClassName() => this.GetType().Name;
}
class child:parent{
}

実行結果

*** リフレクションを利用して動的取得 ***
parent
child

違うネームスペースでも同じクラス名になってしまう件について検証コード

Program.cs

using System;

namespace dotnetclassnametest
{
    class Program
    {
        static void Main(string[] args)
        {
            var parent = new parent();
            var child = new child();
            var anotherChild = new anotherdotnetclassnametest.child();
            Console.WriteLine($"namespace is {child.GetType().Namespace}");
            Console.WriteLine(child.getClassName());
            Console.WriteLine($"namespace is {anotherChild.GetType().Namespace}");
            Console.WriteLine(anotherChild.getClassName());
        }
    }

    public class parent {
        public string getClassName() => this.GetType().Name;
    }

    class child:parent{
    }
}

namespace anotherdotnetclassnametest{
    using dotnetclassnametest;

    public class child:parent
    {

    }
}

実行結果

namespace is dotnetclassnametest
child
namespace is anotherdotnetclassnametest
child

動的にクラス名を取得するときの懸念点

リフレクションを利用する時の懸念点として、実行速度が遅いというものがあります。 静的にコードを実行している場合は、コンパイルプロセスで最適化かけれるので当たり前ではあるが。

とはいえ、検証までしていない&そこまで速度が求められる部分では無いので今回は、リフレクションを利用しクラス名を取得することにしました。

「補足」 親クラスに関数を持たせるのではなく、拡張メソッドにする手もあり

クラスの

まとめ

nameof({クラス名}) で雑にかけば解決するかと思いましたが、ユースケースによってはそれで全て解決はしませんでした。

リフレクションを使えるようになると、細々したところで便利に解決することができるので、今後もリフレクションを使うと圧倒的に便利になるところでは使っていきたいと思いました。

Visual Studio Code で この記事を書いているのですが、C# を実行させつつ実行結果を 記事に書き込むみたいなことができるので便利!