現在のメソッドを呼び出したメソッドを見つけるにはどうすればよいですか?


503

C#にログインするときに、現在のメソッドを呼び出したメソッドの名前を知るにはどうすればよいですか?についてSystem.Reflection.MethodBase.GetCurrentMethod()はすべて知っていますが、スタックトレースでこれより1つ下のステップに進みたいと思います。スタックトレースの解析を検討しましたAssembly.GetCallingAssembly()が、メソッドのような、より明確で明確な方法を見つけたいと思っています。


22
.net 4.5ベータ版+を使用している場合は、CallerInformation APIを使用できます
Rohit Sharma

5
発信者情報もはるかに高速です

4
私は迅速に作成BenchmarkDotNetの三つの主要な方法のベンチマークを(StackTraceStackFrameCallerMemberName)と、ここで見ることが他の人のための要点として結果を掲載:gist.github.com/wilson0x4d/7b30c3913e74adf4ad99b09163a57a1f
ショーン・ウィルソン

回答:


513

これを試して:

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

これは、リフレクション[C#]を使用したGet Calling Methodからのものです。


12
また、むしろスタック全体よりも必要なだけのフレームを作成することができます
ジョエルCoehoorn

187
新しいStackFrame(1).GetMethod()。Name;
Joel Coehoorn、2008年

12
ただし、これは完全に信頼できるわけではありません。これがコメントで機能するかどうか見てみましょう!コンソールアプリケーションで以下を試してみてください。コンパイラの最適化がそれを壊していることがわかります。static void Main(string [] args){CallIt(); }プライベート静的void CallIt(){Final(); } static void Final(){StackTrace trace = new StackTrace(); StackFrameフレーム= trace.GetFrame(1); Console.WriteLine( "{0}。{1}()"、frame.GetMethod()。DeclaringType.FullName、frame.GetMethod()。Name); }
BlackWasp

10
コンパイラーがインライン化または末尾呼び出しがメソッドを最適化している場合、これは機能しません。その場合、スタックが折りたたまれ、予想外の値が見つかります。これをデバッグビルドでのみ使用すると、うまく機能します。
アベル

46
過去に行ったのは、スタックトレースを検索するメソッドの前に、コンパイラー属性[MethodImplAttribute(MethodImplOptions.NoInlining)]を追加することです。コンパイラは、インライン方式、およびスタックトレースが真の呼び出し元のメソッドが含まれていないことを保証していること(私はほとんどの場合、末尾再帰を心配していないよ。)
ヨルダンリーガー

363

C#5では、発信者情報を使用してその情報を取得できます。

//using System.Runtime.CompilerServices;
public void SendError(string Message, [CallerMemberName] string callerName = "") 
{ 
    Console.WriteLine(callerName + "called me."); 
} 

[CallerFilePath]およびも取得でき[CallerLineNumber]ます。


13
こんにちは、C#5ではありません。4.5で利用可能です。
AFract 2015

35
@AFract言語(C#)のバージョンは.NETのバージョンと同じではありません。
kwesolowski、2015

6
@stuartdは[CallerTypeName]、現在の.Netフレームワーク(4.6.2)およびCore CLRから削除されたようです
Ph0en1x

4
@ Ph0en1xこれはフレームワークに含まれていなかったので、たとえばCallerMemberのタイプ名を取得する方法
stuartd

3
@DiegoDeberdt-コンパイル時にすべての作業を行うため、これを使用しても反省点がないことを読みました。私はそれがメソッドと呼ばれるものに関して正確であると信じています。
チェンバレン

109

発信者情報とオプションのパラメーターを使用できます。

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倍の速度で測定しました。


ただし、.Net 4.5からのみ利用可能
DerApe 2015

1
:これは仕事、呼び出し側がagrumentに合格した場合しないことに注意してくださいCachingHelpers.WhoseThere("wrong name!");==> "wrong name!"のでCallerMemberName、デフォルトの値を代入しています。
Olivier Jacot-Descombes 2017

@ OlivierJacot-Descombesは、パラメーターを渡した場合に拡張メソッドが機能しないのと同じように機能しません。使用できる別の文字列パラメータを使用することもできます。また、resharperは、引数を渡そうとした場合に警告を表示することに注意してください。

1
@doveでは、明示的なthisパラメーターを拡張メソッドに渡すことができます。また、Olivierは正しく、値を渡すことができ、[CallerMemberName]適用されません。代わりに、デフォルト値が通常使用されるオーバーライドとして機能します。実際のところ、ILを見ると、結果のメソッドは通常、[opt]arg に対して発行されたものと同じであることがわかりCallerMemberNameます。したがって、の注入はCLR動作です。最後に、ドキュメント:「呼び出し元情報属性[...] は、引数が省略されたときに渡されるデフォルト値に影響します
Shaun Wilson

2
これは完璧であり、あなたを助けないasyncフレンドリーStackFrameです。また、ラムダからの呼び出しにも影響しません。
アーロン

65

速度比較が重要な部分である2つのアプローチの簡単な要約。

http://geekswithblogs.net/BlackRabbitCoder/archive/2013/07/25/c.net-little-wonders-getting-caller-information.aspx

コンパイル時に呼び出し元を決定する

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倍高速です。


この方法は優れたアプローチのようです。Xamarinでも機能し、名前空間を使用できないという問題はありません。
lyndon hughey 2017

63

スタック全体ではなく実際に必要なフレームのみをインスタンス化することで、アサド氏のコード(現在受け入れられている回答)を少し改善できます。

new StackFrame(1).GetMethod().Name;

これは少しパフォーマンスが良いかもしれませんが、おそらく単一のフレームを作成するためにフルスタックを使用する必要があります。また、Alex Lymanが指摘したのと同じ警告があります(オプティマイザ/ネイティブコードは結果を破損する可能性があります)。最後に、あなたは確信していることであることを確認したいかもしれませんnew StackFrame(1).GetFrame(1)返さないnullという可能性が見えるかもしれないとして可能性は低いとして、。

この関連質問を参照してください: 現在実行中のメソッドの名前を見つけるためにリフレクションを使用できますか?


1
new ClassName(…)nullに等しいことさえ可能ですか?
表示名

1
これが.NET Standard 2.0でも機能するのは素晴らしいことです。
srsedate 2017年

60

通常、System.Diagnostics.StackTraceクラスを使用してを取得しSystem.Diagnostics.StackFrameGetMethod()メソッドを使用してSystem.Reflection.MethodBaseオブジェクトを取得できます。ただし、このアプローチにはいくつかの注意事項があります。

  1. ランタイムスタックを表します。最適化によりメソッドがインライン化される可能性があり、スタックトレースにそのメソッドが表示されません
  2. ネイティブフレーム表示されないため、メソッドがネイティブメソッドによって呼び出されている可能性がある場合でも、これは機能せず、実際にそれを実行する方法はありません。

注:私は、Firas Assadによって提供された答えを拡張しています。)


2
最適化をオフにしたデバッグモードで、スタックトレース内のメソッドが何であるかを確認できますか?
AttackingHobo 2011年

1
@AttackingHobo:はい-メソッドがインライン化されている(最適化がオンになっている)か、ネイティブフレームでない限り、表示されます。
Alex Lyman

38

.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」にも存在します。

参考文献

  1. Microsoft-発信者情報(C#)
  2. マイクロソフト- CallerFilePathAttributeクラス
  3. マイクロソフト- CallerLineNumberAttributeクラス
  4. マイクロソフト- CallerMemberNameAttributeクラス

15

最適化のため、リリースコードではそうすることは信頼できないことに注意してください。さらに、アプリケーションをサンドボックスモード(ネットワーク共有)で実行すると、スタックフレームを取得できなくなります。

PostSharpのようなアスペクト指向プログラミング(AOP)を検討してください。これは、コードから呼び出されるのではなく、コードを変更し、常にどこにあるかを認識します。


これはリリースでは機能しないことは間違いありません。コードインジェクションのアイデアが気に入っているかどうかはわかりませんが、ある意味でデバッグステートメントにはコードの変更が必要ですが、それでもそうです。Cマクロに戻ってみませんか?それは少なくともあなたが見ることができるものです。
ebyrob 2017年

9

明らかにこれは遅い答えですが、.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");

8
/// <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;
}

おっと、 "MethodAfter"パラメータについてもう少し詳しく説明する必要がありました。したがって、このメソッドを「ログ」タイプの関数で呼び出す場合は、「ログ」関数の直後にメソッドを取得する必要があります。したがって、GetCallingMethod( "log")を呼び出します。
フランダース

6

多分あなたはこのようなものを探しています:

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

4
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/


StackFrameは信頼できません。「2フレーム」上がると、メソッド呼び出しも簡単に戻る可能性があります。
user2864740 2016年

2

私が使用した別のアプローチは、問題のメソッドにパラメーターを追加することです。たとえば、の代わりにvoid Foo()を使用しますvoid Foo(string context)。次に、呼び出しコンテキストを示す一意の文字列を渡します。

開発に呼び出し元/コンテキストのみが必要な場合は、param出荷前にを削除できます。


2

メソッド名とクラス名を取得するには、これを試してください:

    public static void Call()
    {
        StackTrace stackTrace = new StackTrace();

        var methodName = stackTrace.GetFrame(1).GetMethod();
        var className = methodName.DeclaringType.Name.ToString();

        Console.WriteLine(methodName.Name + "*****" + className );
    }

1
StackFrame caller = (new System.Diagnostics.StackTrace()).GetFrame(1);
string methodName = caller.GetMethod().Name;

十分だと思います。



1

呼び出し元を見つけるためにラムダを使用することもできます。

自分で定義したメソッドがあるとします。

public void MethodA()
    {
        /*
         * Method code here
         */
    }

そして、あなたはそれが発信者であることを見つけたいです。

。メソッドシグネチャを変更して、アクションタイプのパラメーターを設定します(Funcも機能します)。

public void MethodA(Action helperAction)
        {
            /*
             * Method code here
             */
        }

。ラムダ名はランダムに生成されません。ルールは次のようです:> <CallerMethodName> __ Xここで、CallerMethodNameは前の関数に置き換えられ、Xはインデックスです。

private MethodInfo GetCallingMethodInfo(string funcName)
    {
        return GetType().GetMethod(
              funcName.Substring(1,
                                funcName.IndexOf("&gt;", 1, StringComparison.Ordinal) - 1)
              );
    }

。MethodAを呼び出す場合、Action / Funcパラメータは呼び出し元のメソッドによって生成される必要があります。例:

MethodA(() => {});

。MethodAの内部で、上で定義したヘルパー関数を呼び出して、呼び出し元メソッドのMethodInfoを見つけることができます。

例:

MethodInfo callingMethodInfo = GetCallingMethodInfo(serverCall.Method.Name);

0

Firas Assaad回答への追加情報。

new StackFrame(1).GetMethod().Name;依存関係注入を伴う.netコア2.1で使用しており、メソッドを「開始」として呼び出しています。

私が試したところ、[System.Runtime.CompilerServices.CallerMemberName] string callerName = "" 正しい呼び出し方法が得られました


-1
var callingMethod = new StackFrame(1, true).GetMethod();
string source = callingMethod.ReflectedType.FullName + ": " + callingMethod.Name;

1
私は反対票を投じなかったが、非常に類似した情報(数年後)を投稿した理由を説明するテキストを追加すると、質問の価値が高まり、それ以上の反対票を避けることができることに注意したい。
Shaun Wilson
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.