it-swarm.com.de

Wie finde ich die Methode, die die aktuelle Methode aufgerufen hat?

Wie erfahre ich beim Anmelden in C # den Namen der Methode, die die aktuelle Methode aufgerufen hat? Ich weiß alles über System.Reflection.MethodBase.GetCurrentMethod(), aber ich möchte in der Stack-Ablaufverfolgung noch einen Schritt weitergehen. Ich habe mir überlegt, die Stack-Spur zu parsen, aber ich hoffe, einen klareren Weg zu finden, etwa Assembly.GetCallingAssembly(), aber für Methoden.

421
flipdoubt

Versuche dies:

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

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

Es stammt von Aufrufmethode mit Reflection [C #].

427
Firas Assaad

In C # 5 können Sie diese Informationen mithilfe der Anruferinformationen abrufen:

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

Sie können auch [CallerFilePath] und [CallerLineNumber] erhalten.

290
Coincoin

Sie können Anruferinformationen und optionale Parameter verwenden:

public static string WhoseThere([CallerMemberName] string memberName = "")
{
       return memberName;
}

Dieser Test veranschaulicht dies:

[Test]
public void Should_get_name_of_calling_method()
{
    var methodName = CachingHelpers.WhoseThere();
    Assert.That(methodName, Is.EqualTo("Should_get_name_of_calling_method"));
}

Während der StackTrace oben recht schnell arbeitet und in den meisten Fällen kein Leistungsproblem darstellt, sind die Anruferinformationen noch viel schneller. In einem Sample von 1000 Iterationen habe ich es 40-mal schneller getaktet.

95
dove

Im Allgemeinen können Sie die Klasse System.Diagnostics.StackTrace verwenden, um ein System.Diagnostics.StackFrame abzurufen, und dann die GetMethod() -Methode verwenden, um ein System.Reflection.MethodBase - Objekt abzurufen. Es gibt jedoch einige Vorbehalte zu diesem Ansatz:

  1. Es stellt den Stack von runtime dar - Optimierungen könnten eine Methode inline einbinden, und Sie werden not diese Methode im Stack-Trace sehen.
  2. Nicht zeigt alle nativen Frames. Wenn also sogar die Möglichkeit besteht, dass Ihre Methode von einer nativen Methode aufgerufen wird, funktionieren nicht und derzeit gibt es keine Möglichkeit, dies zu tun.

(HINWEIS: Ich erweitere gerade die Antwort wird von Firas Assad bereitgestellt.)

59
Alex Lyman

Wir können den Code von Herrn Assad (die derzeit akzeptierte Antwort) nur ein wenig verbessern, indem wir nur den Frame instanziieren, den wir tatsächlich benötigen, und nicht den gesamten Stack:

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

Dies kann etwas besser ausfallen, obwohl aller Wahrscheinlichkeit nach noch der volle Stapel verwendet werden muss, um diesen einzelnen Frame zu erstellen. Es hat auch immer noch die gleichen Vorbehalte, auf die Alex Lyman hingewiesen hat (Optimierer/nativer Code kann die Ergebnisse beschädigen). Schließlich möchten Sie vielleicht überprüfen, ob new StackFrame(1) oder .GetFrame(1)null nicht zurückgegeben werden, so unwahrscheinlich diese Möglichkeit auch ist.

Siehe diese verwandte Frage: Können Sie mit Hilfe von Reflection den Namen der aktuell ausgeführten Methode ermitteln?

57
Joel Coehoorn

Eine kurze Zusammenfassung der beiden Ansätze, wobei der Geschwindigkeitsvergleich der wichtigste Teil ist.

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

Ermittlung des Anrufers zur Kompilierzeit

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);
}

Ermittlung des Anrufers anhand des Stapels

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);
}

Vergleich der beiden Ansätze

Time for 1,000,000 iterations with Attributes: 196 ms
Time for 1,000,000 iterations with StackTrace: 5096 ms

Sie sehen also, die Verwendung der Attribute ist viel, viel schneller! Fast 25x tatsächlich schneller.

52
Tikall

Ab .NET 4.5 können Sie Caller Information Attributes:verwenden.

  • CallerFilePath - Die Quelldatei, die die Funktion aufgerufen hat.
  • CallerLineNumber - Codezeile, die die Funktion aufgerufen hat; 
  • CallerMemberName - Mitglied, das die Funktion aufgerufen hat.

    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);
    }
    

Diese Einrichtung ist auch in ".NET Core" und ".NET Standard" vorhanden.

Verweise

  1. Microsoft - Anruferinformationen (C #)
  2. Microsoft - CallerFilePathAttribute Klasse
  3. Microsoft - CallerLineNumberAttribute Klasse
  4. Microsoft - CallerMemberNameAttribute Klasse
20
Ivan Pinto

Beachten Sie, dass dies aufgrund der Optimierung im Release-Code unzuverlässig ist. Wenn Sie die Anwendung im Sandbox-Modus (Netzwerkfreigabe) ausführen, können Sie den Stack-Frame auch gar nicht abrufen.

Betrachten Sie Aspekt-orientierte Programmierung (AOP), wie PostSharp , das, anstatt vom Code aufgerufen zu werden, Ihren Code ändert und somit jederzeit weiß, wo er sich befindet.

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

Offensichtlich ist dies eine späte Antwort, aber ich habe eine bessere Option, wenn Sie .NET 4.5 oder mehr verwenden können:

internal static void WriteInformation<T>(string text, [CallerMemberName]string method = "")
{
    Console.WriteLine(DateTime.Now.ToString() + " => " + typeof(T).FullName + "." + method + ": " + text);
}

Das aktuelle Datum und die Uhrzeit werden gedruckt, gefolgt von "Namespace.ClassName.MethodName" und endet mit ": text".
Beispielausgabe:

6/17/2016 12:41:49 PM => WpfApplication.MainWindow..ctor: MainWindow initialized

Verwendungsbeispiel:

Logger.WriteInformation<MainWindow>("MainWindow initialized");
8

Vielleicht suchst du so etwas:

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

Eine fantastische Klasse ist hier: http://www.csharp411.com/c-get-calling-method/

4
Tebo

Ein anderer Ansatz, den ich verwendet habe, ist, der fraglichen Methode einen Parameter hinzuzufügen. Verwenden Sie beispielsweise anstelle von void Foo()void Foo(string context). Übergeben Sie dann eine eindeutige Zeichenfolge, die den aufrufenden Kontext angibt.

Wenn Sie nur den Aufrufer/Kontext für die Entwicklung benötigen, können Sie die Variable param vor dem Versand entfernen.

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

wird genug sein, denke ich.

1
caner

Schauen Sie sich Name der Protokollierungsmethode in .NET an. Hüten Sie sich vor der Verwendung im Produktionscode. StackFrame ist möglicherweise nicht zuverlässig ...

1
Yuval Peled

Wir können auch Lambda's verwenden, um den Anrufer zu finden.

Angenommen, Sie haben eine von Ihnen definierte Methode:

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

und du willst es finden, Anrufer.

1 . Ändern Sie die Methodensignatur, sodass wir einen Parameter vom Typ Action haben (Func funktioniert auch):

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

2 . Lambda-Namen werden nicht zufällig generiert. Die Regel scheint zu lauten:> <CallerMethodName> __ X Wobei CallerMethodName durch die vorherige Funktion ersetzt wird und X einen Index darstellt.

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

3 . Beim Aufruf von MethodA muss der Parameter Action/Func von der Aufrufermethode ..__ generiert werden.

MethodA(() => {});

4 . Innerhalb von MethodA können wir jetzt die oben definierte Hilfsfunktion aufrufen und die MethodInfo der Aufrufermethode finden.

Beispiel:

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

Um Methodennamen und Klassennamen zu erhalten, versuchen Sie folgendes:

    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 );
    }
0
Arian

Zusätzliche Informationen zu Firas Assaad antworten.

Ich habe new StackFrame(1).GetMethod().Name; in .net Core 2.1 mit Abhängigkeitseinspritzung verwendet und bekomme die Aufrufmethode als 'Start'. 

Ich habe es mit [System.Runtime.CompilerServices.CallerMemberName] string callerName = "" Versucht, und es gibt mir eine korrekte Aufrufmethode

0
cdev