ログ出力メソッドの呼び出し元クラス名が欲しいなって時に
調べてみたらStackFrameというのを使えばとれるらしいのでサンプル作ってました
これを使えば呼び出し元のメソッド名もとれるらしい!
「呼び出し元のメソッド名を取得したい」で実施してみたように
[CallerMemberName]が使えない.Net Frameworkバージョンの時とか
助かりますね
動かすサンプル
Formにbuttonを3つ設置します
クリックでそれぞれbutton1_Click、button2_Click、button3_Clickが呼び出されます
Form1.cs
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.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
※ただし非同期メソッドからの呼び出しからでは想定通りに取得できない
非同期メソッドでもスマートに取得する方法知ってたら教えてほしいです…