クラス名を取得したい | プログラミングがわからなすぎる

プログラミングがわからなすぎる

気が向いた時の備忘録。プログラミングは好きなのに物凄く単純なものしか理解できないからメモしていくうちに覚えられたらいいな
ターゲットフレームワークは大体4.7.2

ログ出力メソッドの呼び出し元クラス名が欲しいなって時に

調べてみたらStackFrameというのを使えばとれるらしいのでサンプル作ってました

 

これを使えば呼び出し元のメソッド名もとれるらしい!

呼び出し元のメソッド名を取得したい」で実施してみたように

[CallerMemberName]が使えない.Net Frameworkバージョンの時とか

助かりますね

 

動かすサンプル 

Formにbuttonを3つ設置します

クリックでそれぞれbutton1_Click、button2_Click、button3_Clickが呼び出されます

 

 Form1.cs

using System;
using System.Diagnostics;
using System.Threading.Tasks;
using System.Windows.Forms;

namespace TestProject
{
    public partial class Form1 : Form
    {
        public Form1()
        {
            InitializeComponent();
        }

        private void button1_Click(object sender, EventArgs e)
        {
            OutputClass.GetClass();
        }

        private void button2_Click(object sender, EventArgs e)
        {
            RelayClass.RelayMethod();
        }

        private async void button3_Click(object sender, EventArgs e)
        {
            await Task.Delay(1000);
            OutputClass.GetClass();
        }
    }

    public static class RelayClass
    { 
        public static void RelayMethod()
        {
            OutputClass.GetClass();
        }
    }

    public static class OutputClass
    { 
        public static void GetClass()
        {
            StackFrame frame = new StackFrame(1);
            string methodName = frame.GetMethod().Name;
            string className = frame.GetMethod().ReflectedType.Name;
            Console.WriteLine("呼び出し元のクラス名は" + className + "です。メソッド名は" + methodName + "です。");
        }
    }
}

StackFrameのインスタンスを生成しているときのパラメータの"1"は

1つ前の呼び出し元のデータを取得するときに設定するようです

取得したいクラス名・メソッド名の相手が2つ前の呼び出し元の場合はここが"2"になるのでしょう

 

呼び出し元のクラス名が欲しいので、以下のように出力されれば満足です

 button1クリック:

  「呼び出し元のクラス名はForm1です。メソッド名はbutton1_Clickです。」

 button2クリック:

  「呼び出し元のクラス名はRelayClassです。メソッド名はRelayMethodです。」

 button3クリック:

  「呼び出し元のクラス名はForm1です。メソッド名はbutton3_Clickです。」

 

実行結果 

button1をクリック→button2をクリック→button3をクリック

をしてみました

button1とbutton2の出力結果は嬉しいのですが

button3が困る感じになってる!

欲しいクラス名も違うし、欲しいメソッド名はMoveNextじゃないです

 

クラス名が入ってくる何かないか 

frame.GetMethod().DeclaringType.FullNameをしてみると

基本的にクラス名(全体)がとれるみたいことがわかりました

上のFormクラスから呼ばれた場合は「TestProject.Form1」てかんじ

 

非同期メソッドでこれを実行してみると

「TestProject.Form1+<button3_Click>d__3」

という出力結果になりました

クラス名(全体)+<メソッド名>d__3

という形になっているので、とりあえずここから抜き出すことにします

d__3は何かは分かりません

 

ソースの修正 

 Form1.cs

using System;
using System.Diagnostics;
using System.Threading.Tasks;
using System.Windows.Forms;

namespace TestProject
{
    public partial class Form1 : Form
    {
        public Form1()
        {
            InitializeComponent();
        }

        private void button1_Click(object sender, EventArgs e)
        {
            OutputClass.GetClass();
        }

        private void button2_Click(object sender, EventArgs e)
        {
            RelayClass.RelayMethod();
        }

        private async void button3_Click(object sender, EventArgs e)
        {
            await Task.Delay(1000);
            OutputClass.GetClass2();
        }
    }

    public static class RelayClass
    { 
        public static void RelayMethod()
        {
            OutputClass.GetClass();
        }
    }

    public static class OutputClass
    { 
        public static void GetClass()
        {
            StackFrame frame = new StackFrame(1);
            string methodName = frame.GetMethod().Name;
            string className = frame.GetMethod().ReflectedType.Name;
            Console.WriteLine(frame.GetMethod().DeclaringType.FullName);
            Console.WriteLine("呼び出し元のクラス名は" + className + "です。メソッド名は" + methodName + "です。");
        }

        public static void GetClass2()
        {
            StackFrame frame = new StackFrame(1);
            string fullClassName = frame.GetMethod().DeclaringType.FullName;
            Console.WriteLine(fullClassName);

            string methodName = fullClassName.Split('<')[1].Split('>')[0];

            string[] names = fullClassName.Split('+')[0].Split('.');
            string className = names[names.Length - 1];

            Console.WriteLine("呼び出し元のクラス名は" + className + "です。メソッド名は" + methodName + "です。");
        }
    }
}

非同期メソッドからの呼び出しのみGetClass2()を使ってもらいます

 

不格好ですが、非同期メソッドからの呼び出しであるGetClass2()では

frame.GetMethod().DeclaringType.FullNameを取得すると

「TestProject.Form1+<button3_Click>d__3」

のような取れ方になるので、青字にした部分を切り取りたいので

そこから"<>"の間にある文字列をメソッド名、

"+"の前の文字がクラス名(全体)となっているみたいなので

"."でsplitした最後の文字列をクラス名、として表示してみることにします

 

一応クラス名・メソッド名の出力の前に、クラス名(全体)の出力も表示させて

もう一度実行してみます

 

実行結果2 

もう一度、button1をクリック→button2をクリック→button3をクリック

今度はbutton3クリックで表示したクラス名、メソッド名も

欲しい通りに出力してます

 

メモ 

 

呼び出し元のクラス名・メソッド名を取得したいときはStackFrameを使用する

(以下は1つ前の呼び出し元)

StackFrame frame = new StackFrame(1);

 クラス名:frame.GetMethod().ReflectedType.Name

 メソッド名:frame.GetMethod().Name

※ただし非同期メソッドからの呼び出しからでは想定通りに取得できない

 

非同期メソッドでもスマートに取得する方法知ってたら教えてほしいです…