web-dev-qa-db-ja.com

現在のメソッドを呼び出したメソッドはどうやって見つけることができますか?

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

436
flipdoubt

これを試して:

using System.Diagnostics;
// Get call stack
StackTrace stackTrace = new StackTrace();

// Get calling method name
Console.WriteLine(stackTrace.GetFrame(1).GetMethod().Name);

Reflection [C#]を使った呼び出しメソッドの取得からです。

444
Firas Assaad

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

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

[CallerFilePath][CallerLineNumber]も入手できます。

305
Coincoin

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

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倍高速にクロックしました。

98
dove

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

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

多分それがその単一のフレームを作成するためにまだフルスタックを使用しなければならないけれども、これはもう少し良く実行するかもしれません。また、Alex Lymanが指摘したのと同じ警告がまだあります(オプティマイザ/ネイティブコードは結果を壊すかもしれません)。最後に、new StackFrame(1)または.GetFrame(1)nullを返さないことを確認する必要があるかもしれません。

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

60
Joel Coehoorn

一般に、 System.Diagnostics.StackTrace クラスを使用して System.Diagnostics.StackFrame を取得し、次に GetMethod() メソッドを使用して System.Reflection.MethodBase オブジェクトを取得できます。しかし、このアプローチには いくつかの注意点 があります。

  1. それはruntime stackを表します - 最適化はメソッドをインライン化することができ、あなたはNOTスタックトレースでそのメソッドを見るでしょう。
  2. それはnotネイティブフレームを表示するので、もしあなたのメソッドがネイティブメソッドによって呼ばれている可能性さえあっても、これはnotうまくいくでしょう、そして実際にそれを行うための現在利用可能な方法はありません。

注:私は、Firas Assadが提供する 答え に展開しています。)

60
Alex Lyman

速度比較が重要な部分である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倍高速です。

54
Tikall

.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. Microsoft - CallerFilePathAttributeクラス
  3. Microsoft - CallerLineNumberAttributeクラス
  4. Microsoft - CallerMemberNameAttributeクラス
25
Ivan Pinto

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

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

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

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

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
6
jesal
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/

4
Tebo

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

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

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

十分でしょう、と思います。

1
caner

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

    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
Arian

.NETのロギングメソッド名を見てください。本番コードでは使用しないでください。 StackFrameは信頼できないかもしれません...

1
Yuval Peled

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

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

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("&gt;", 1, StringComparison.Ordinal) - 1)
              );
    }

。 MethodAを呼び出すとき、Action/Funcパラメータは呼び出し元のメソッドによって生成されなければなりません。例:

MethodA(() => {});

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

例:

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

Firas Assaadの回答に対する追加情報。

私は依存性注入と.NETコア2.1でnew StackFrame(1).GetMethod().Name;を使用していて、私は 'Start'としてmethodを呼び出しています。

私は[System.Runtime.CompilerServices.CallerMemberName] string callerName = ""を試してみました、そしてそれは私に正しい呼び出しメソッドを与えます

0
cdev