C# にログインするとき、現在のメソッドを呼び出したメソッドの名前を知るにはどうすればよいですか? についてはすべて知ってSystem.Reflection.MethodBase.GetCurrentMethod()
いますが、スタック トレースでこの 1 つ下のステップに進みたいと思います。スタック トレースの解析を検討しましたが、メソッド以外のような、より明確で明確な方法を見つけたいと考えていAssembly.GetCallingAssembly()
ます。
19 に答える
これを試して:
using System.Diagnostics;
// Get call stack
StackTrace stackTrace = new StackTrace();
// Get calling method name
Console.WriteLine(stackTrace.GetFrame(1).GetMethod().Name);
一発ギャグ:
(new System.Diagnostics.StackTrace()).GetFrame(1).GetMethod().Name
これは、Reflection [C#] を使用した Get Calling Methodからのものです。
C# 5 では、 caller infoを使用してその情報を取得できます。
//using System.Runtime.CompilerServices;
public void SendError(string Message, [CallerMemberName] string callerName = "")
{
Console.WriteLine(callerName + "called me.");
}
[CallerFilePath]
とも取得できます[CallerLineNumber]
。
発信者情報とオプションのパラメータを使用できます。
public static string WhoseThere([CallerMemberName] string memberName = "")
{
return memberName;
}
このテストはこれを示しています:
[Test]
public void Should_get_name_of_calling_method()
{
var methodName = CachingHelpers.WhoseThere();
Assert.That(methodName, Is.EqualTo("Should_get_name_of_calling_method"));
}
StackTraceは上記で非常に高速に動作し、ほとんどの場合、パフォーマンスの問題にはなりませんが、発信者情報はさらに高速です。1000回の反復のサンプルでは、40倍速くクロックしました。
速度の比較が重要な部分である 2 つのアプローチの簡単な要約。
コンパイル時の呼び出し元の決定
static void Log(object message,
[CallerMemberName] string memberName = "",
[CallerFilePath] string fileName = "",
[CallerLineNumber] int lineNumber = 0)
{
// we'll just use a simple Console write for now
Console.WriteLine("{0}({1}):{2} - {3}", fileName, lineNumber, memberName, message);
}
スタックを使用して呼び出し元を判別する
static void Log(object message)
{
// frame 1, true for source info
StackFrame frame = new StackFrame(1, true);
var method = frame.GetMethod();
var fileName = frame.GetFileName();
var lineNumber = frame.GetFileLineNumber();
// we'll just use a simple Console write for now
Console.WriteLine("{0}({1}):{2} - {3}", fileName, lineNumber, method.Name, message);
}
2 つのアプローチの比較
Time for 1,000,000 iterations with Attributes: 196 ms
Time for 1,000,000 iterations with StackTrace: 5096 ms
ご覧のとおり、アトリビュートを使用すると、はるかに高速になります。実際、ほぼ 25 倍高速です。
スタック全体ではなく、実際に必要なフレームのみをインスタンス化することで、アサド氏のコード (現在受け入れられている回答) を少しだけ改善できます。
new StackFrame(1).GetMethod().Name;
これにより、パフォーマンスが少し向上する可能性がありますが、おそらく、その単一のフレームを作成するためにスタック全体を使用する必要があります。また、Alex Lyman が指摘したのと同じ警告がまだあります (オプティマイザー/ネイティブ コードが結果を破損する可能性があります)。new StackFrame(1)
最後に、 or .GetFrame(1)
don't returnであることを確認する必要があるnull
かもしれません。
この関連する質問を参照してください: リフレクションを使用して、現在実行中のメソッドの名前を見つけることができますか?
一般に、System.Diagnostics.StackTrace
クラスを使用して を取得し、メソッドをSystem.Diagnostics.StackFrame
使用してオブジェクトを取得できます。ただし、このアプローチにはいくつかの注意事項があります。GetMethod()
System.Reflection.MethodBase
- これはランタイムスタックを表します。最適化によってメソッドがインライン化される可能性があり、そのメソッドはスタック トレースに表示されません。
- ネイティブ フレームは表示されないため、メソッドがネイティブ メソッドによって呼び出される可能性さえある場合、これは機能せず、実際に現在利用可能な方法はありません。
(注:Firas Assadが提供した回答を拡張しているだけです。)
.NET 4.5 以降では、発信者情報属性を使用できます。
CallerFilePath
- 関数を呼び出したソース ファイル。CallerLineNumber
- 関数を呼び出したコード行。CallerMemberName
- 関数を呼び出したメンバー。public void WriteLine( [CallerFilePath] string callerFilePath = "", [CallerLineNumber] long callerLineNumber = 0, [CallerMemberName] string callerMember= "") { Debug.WriteLine( "Caller File Path: {0}, Caller Line Number: {1}, Caller Member: {2}", callerFilePath, callerLineNumber, callerMember); }
この機能は、「.NET Core」と「.NET Standard」にも存在します。
参考文献
明らかにこれは遅い回答ですが、.NET 4.5 以降を使用できる場合は、より良いオプションがあります。
internal static void WriteInformation<T>(string text, [CallerMemberName]string method = "")
{
Console.WriteLine(DateTime.Now.ToString() + " => " + typeof(T).FullName + "." + method + ": " + text);
}
これにより、現在の日付と時刻が出力され、その後に「Namespace.ClassName.MethodName」が続き、「: text」で終わります。
出力例:
6/17/2016 12:41:49 PM => WpfApplication.MainWindow..ctor: MainWindow initialized
サンプル使用:
Logger.WriteInformation<MainWindow>("MainWindow initialized");
これを行うと、最適化のため、リリース コードでは信頼できないことに注意してください。さらに、アプリケーションをサンドボックス モード (ネットワーク共有) で実行すると、スタック フレームをまったく取得できなくなります。
コードから呼び出される代わりにPostSharpのようなアスペクト指向プログラミング(AOP) を検討してください。
/// <summary>
/// Returns the call that occurred just before the "GetCallingMethod".
/// </summary>
public static string GetCallingMethod()
{
return GetCallingMethod("GetCallingMethod");
}
/// <summary>
/// Returns the call that occurred just before the the method specified.
/// </summary>
/// <param name="MethodAfter">The named method to see what happened just before it was called. (case sensitive)</param>
/// <returns>The method name.</returns>
public static string GetCallingMethod(string MethodAfter)
{
string str = "";
try
{
StackTrace st = new StackTrace();
StackFrame[] frames = st.GetFrames();
for (int i = 0; i < st.FrameCount - 1; i++)
{
if (frames[i].GetMethod().Name.Equals(MethodAfter))
{
if (!frames[i + 1].GetMethod().Name.Equals(MethodAfter)) // ignores overloaded methods.
{
str = frames[i + 1].GetMethod().ReflectedType.FullName + "." + frames[i + 1].GetMethod().Name;
break;
}
}
}
}
catch (Exception) { ; }
return str;
}
多分あなたはこのようなものを探しています:
StackFrame frame = new StackFrame(1);
frame.GetMethod().Name; //Gets the current method name
MethodBase method = frame.GetMethod();
method.DeclaringType.Name //Gets the current class name
private static MethodBase GetCallingMethod()
{
return new StackFrame(2, false).GetMethod();
}
private static Type GetCallingType()
{
return new StackFrame(2, false).GetMethod().DeclaringType;
}
素晴らしいクラスがここにあります: http://www.csharp411.com/c-get-calling-method/
私が使用した別のアプローチは、問題のメソッドにパラメーターを追加することです。たとえば、 の代わりにvoid Foo()
を使用しますvoid Foo(string context)
。次に、呼び出しコンテキストを示す一意の文字列を渡します。
開発のために呼び出し元/コンテキストのみが必要な場合は、param
出荷前に削除できます。
Logging method name in .NETを見てください。本番コードで使用する場合は注意してください。StackFrame は信頼できない可能性があります...
Firas Assaadの回答への追加情報。
私はnew StackFrame(1).GetMethod().Name;
.netコア2.1で依存性注入を使用しており、呼び出しメソッドを「開始」として取得しています。
試してみたところ、[System.Runtime.CompilerServices.CallerMemberName] string callerName = ""
正しい呼び出し方法が得られました
StackFrame caller = (new System.Diagnostics.StackTrace()).GetFrame(1);
string methodName = caller.GetMethod().Name;
で十分だと思います。
呼び出し元を見つけるためにラムダを使用することもできます。
自分で定義したメソッドがあるとします。
public void MethodA()
{
/*
* Method code here
*/
}
呼び出し元を見つけたいとします。
1 . メソッド シグネチャを変更して、Action 型のパラメーターを指定します (Func も機能します)。
public void MethodA(Action helperAction)
{
/*
* Method code here
*/
}
2 . ラムダ名はランダムに生成されません。ルールは次のようです: > <CallerMethodName>__X ここで、CallerMethodName は前の関数に置き換えられ、X はインデックスです。
private MethodInfo GetCallingMethodInfo(string funcName)
{
return GetType().GetMethod(
funcName.Substring(1,
funcName.IndexOf(">", 1, StringComparison.Ordinal) - 1)
);
}
3 . MethodA を呼び出すとき、呼び出し元のメソッドによって Action/Func パラメータを生成する必要があります。例:
MethodA(() => {});
4 . MethodA 内で、上記で定義したヘルパー関数を呼び出して、呼び出し元メソッドの MethodInfo を見つけることができます。
例:
MethodInfo callingMethodInfo = GetCallingMethodInfo(serverCall.Method.Name);
var callingMethod = new StackFrame(1, true).GetMethod();
string source = callingMethod.ReflectedType.FullName + ": " + callingMethod.Name;