DOBON.NET DOBON.NETプログラミング掲示板過去ログ

staticメソッドと継承先クラスで自身のクラス名を取得する方法。

環境/言語:[C#]
分類:[.NET]



初めまして。

表題の件ですが、以下に短いサンプルスクリプトを記述させて頂きました。
以下のスクリプトでやりたいことは2つです。

 @staticメソッド内で自クラスの名前を取得したい。
 A継承先のクラスで何も書かずに継承先クラスの名前を取得したい。

いろいろ試した結果、C#の仕様上できない線が濃厚になってきましたので、
本当にできないのかどうか、皆様の知恵をお借りさせて頂けないでしょうか。
よろしくお願い致します。



//自身のクラス名を返すクラス
public class Class1
{
//普通に取得できる。
public string GetName()
{
return this.GetType().Name;
}

//インスタンスを生成していないので取得できない。
public static GetNameStatic()
{
return this.GetType().Name;
}
}

//上記クラスを継承して楽をしようとするクラス
public class Class2 : Class1{}



//"Class1"が返る
new Class1().GetName();
//取得できない
Class1.GetNameStatic();

//これも"Class1"が返る
new Class2().GetName();
//もし取得できても"Class1"が返ってきそう
Class2.GetNameStatic();
■No30735に返信(ひらまろさんの記事)
> public static GetNameStatic()
public static string GetNameStatic() では無く?


> staticメソッド内で自クラスの名前を取得したい。
強いて挙げれば
 return System.Reflection.MethodInfo.GetCurrentMethod().DeclaringType.Name;
あるいは
 return System.Reflection.MethodBase.GetCurrentMethod().DeclaringType.Name;
あるいは
 return new System.Diagnostics.StackTrace().GetFrame(0).GetMethod().DeclaringType.FullName;
あるいは
 return new System.Diagnostics.StackFrame().GetMethod().DeclaringType.Name;
などでしょうか。遅いですけれどね。

最速はもちろん「return typeof(Class1).Name;」あるいは
「return "Class1";」ですが、これでは要件を満たせませんね。

あとは、自クラス名を返すメソッドを持ったクラスを定義するための
CodeSnippet を用意するぐらいでしょうか。


> //もし取得できても"Class1"が返ってきそう
Class2 で
 //public static string GetNameStatic()
 public new static string GetNameStatic()
のようにシャドウイングされれば、Class2.GetNameStatic の呼び出しになりますし、
Class2 で一切定義されていなければ、Class1.GetNameStatic が呼び出されるかと。
■No30737に返信(魔界の仮面弁士さんの記事)
> ■No30735に返信(ひらまろさんの記事)

魔界の仮面弁士様
ご回答ありがとうございます。

> public static string GetNameStatic() では無く?
おっしゃる通り型指定を忘れておりました;
static内でthisを使っていてのコンパイルエラーだと思い込んで気づかなかったようです;

Reflection と Diagnostics についての情報ありがとうございます。
色々と応用が利きそうなので後でじっくり調査させて頂きますが、
ぱっと見たところ現在実行中のメソッドやそれが記述されているクラスを取得できるようですね。
これをメソッド内で呼び出すことによってstaticであろうとなかろうと問答無用で情報を読み出すことができるということでしょうか。
以下のような使い方もできそうですね。

public static string GetClassName()
{
return new System.Diagnostics.StackTrace().GetFrame(1).GetMethod().DeclaringType.FullName;
}

ご教授頂いた速度面の問題ですが、該当メソッドは何度も呼び出す予定ですので、
最初の呼び出し時にstaticな変数にキャッシュし、2度目以降はキャッシュがあればそれを返す。
という処理が可能ならばそうしたいと思います。

しかし、ご指摘いただいたようにstaticなメンバの場合、
継承先ではnewを指定しないと継承元のメンバを参照してしまいます。
(仮にClass2で文字列"Class2"をキャッシュした場合、Class1を継承しているすべてのクラスに影響してしまいます。)
これはキャッシュに限らず、クラス名を取得する処理もクラスごとにnewして書かなければならないと思います。

しかし、できれば継承先のクラスではなるべく記述を減らしたいと思っており、
また変更が必要になった場合に1クラスの修正のみで済ませられるようにしたいと思っております。
(でなければ継承の意味がなく、似たようなクラスを大量に作ることになり、修正が生じた場合すべてのクラスに対して修正を行う必要が出てきてしまいます。)

このような、staticメンバを継承し、自動的に継承先のクラス専用のstaticメンバになる(切り分けられる)
というような処理は、C#には存在しないのでしょうか。(他の言語にこのような仕様があるかは残念ながら存じてはいませんが…)

それでも、staticなメソッドで自クラスの名前や実行中のメソッド名を取得する方法をご教授頂けたのは大きな収穫です。
引き続き継承先のクラスでできるだけ記述を減らす方法を探していきたいと思います。
本当にありがとうございました。
2012/07/13(Fri) 21:58:09 編集(投稿者)

■No30738に返信(ひらまろさんの記事)
> しかし、ご指摘いただいたようにstaticなメンバの場合、
> 継承先ではnewを指定しないと継承元のメンバを参照してしまいます。

シャドウイングやオーバーライドしてなかった場合には、そもそも
実際にそのメソッドを持っているのは、継承先(Class2)ではなく継承元(Class1)です。

C# では、Class1.GetNameStatic() でも Class2.GetNameStatic() でも呼べますが、
言語によっては、後者の記述はエラーにさえなります。たとえば JScript.NET など:
『error JS1246: 型 'Class2' にこのような静的メンバーはありません。』


> 以下のような使い方もできそうですね。
> return new System.Diagnostics.StackTrace().GetFrame(1).GetMethod().DeclaringType.FullName;
そもそも Class2 を経由していないので、呼び出し元クラスから辿るのも無理そうです。

// C#
Func<string> a = Class1.GetNameStatic;
Func<string> b = Class2.GetNameStatic;

Console.WriteLine(a.Method.DeclaringType.Name); //「Class1」
Console.WriteLine(b.Method.DeclaringType.Name); //「Class1」←Class2に非ず!


一応追試してみましたが、
 Console.WriteLine(Class2.GetNameStatic());
という C# のコードをコンパイルした結果は、
 call string Class1::GetNameStatic()
 call void [mscorlib]System.Console::WriteLine(string)
という IL に置き換わっていました。

ちなみに MSIL では、上記の呼び出しを
 「call string Class2::GetNameStatic()」
とも記述できるのですが、どちらせによ、Class1 が呼ばれるという結果は同じです。



> それでも、staticなメソッドで自クラスの名前や実行中のメソッド名を取得する方法をご教授頂けたのは大きな収穫です。
http://www.tt.rim.or.jp/~rudyard/torii009.html
http://blogs.wankuma.com/jeanne/archive/2005/11/24/19566.aspx
■No30738に返信(ひらまろさんの記事)

今回の事を実現するのにstaticにこだわる理由はなんでしょう?
インスタンス作る方法であれば簡単に出来ることです。

    public class Class1
    {
        public string GetTypeName()
        {
            return this.GetType().Name;
        }
    }

    public class Class2:Class1
    {
    }


            Class1 obj1 = new Class1();
            Class2 obj2 = new Class2();
            Console.WriteLine("obj1.GetTypeName= " + obj1.GetTypeName());
            Console.WriteLine("obj2.GetTypeName= " + obj2.GetTypeName());

で十分です。
というか型名を取得して何か処理をしなければいけないというのは結構特殊なものだと思います。実際に目的としている処理を行うのにもっと適切な方法があるのではないでしょうか?

DOBON.NET | プログラミング道 | プログラミング掲示板