Frage

Wie kann ich beim Anmelden in C#den Namen der Methode lernen, die die aktuelle Methode bezeichnete? Ich weiß alles über System.Reflection.MethodBase.GetCurrentMethod(), aber ich möchte einen Schritt darunter in der Stapelspur gehen. Ich habe darüber nachgedacht, die Stapelspur zu analysieren, aber ich hoffe, einen saubereren Weg zu finden, so etwas wie Assembly.GetCallingAssembly() Aber für Methoden.

War es hilfreich?

Lösung

Versuche dies:

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

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

Es ist aus Rufmethode mit Reflexion [C#] abrufen.

Andere Tipps

In C# 5 können Sie diese Informationen mit Caller -Info erhalten:

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

Sie können auch das bekommen [CallerFilePath] und [CallerLineNumber].

Sie können Anruferinformationen und optionale Parameter verwenden:

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

Dieser Test zeigt 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 die Stacktrace in den meisten Fällen ziemlich schnell über oben funktioniert und in den meisten Fällen kein Leistungsproblem wäre, sind die Anruferinformationen noch viel schneller. In einer Probe von 1000 Iterationen habe ich es 40 -mal schneller getaktet.

Wir können den Code von Herrn Assad (die aktuelle akzeptierte Antwort) verbessern, nur ein wenig, indem wir nur den Rahmen instanziieren, den wir tatsächlich benötigen, anstatt den gesamten Stapel:

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

Dies könnte ein wenig besser funktionieren, obwohl es aller Wahrscheinlichkeit nach immer noch den vollständigen Stapel verwenden muss, um diesen Einzelrahmen zu erstellen. Außerdem hat es immer noch die gleichen Vorbehalte wie Alex Lyman (Optimierer/Native Code könnte die Ergebnisse beschädigen). Schließlich möchten Sie vielleicht überprüfen, ob dies sichergestellt ist new StackFrame(1) oder .GetFrame(1) Kehre nicht zurück null, so unwahrscheinlich wie diese Möglichkeit scheint.

Siehe diese verwandte Frage:Können Sie Reflection verwenden, um den Namen der aktuell ausführenden Methode zu finden?

Im Allgemeinen können Sie die verwenden System.Diagnostics.StackTrace Klasse, um eine zu bekommen System.Diagnostics.StackFrame, und dann verwenden Sie die GetMethod() Methode, um a zu erhalten System.Reflection.MethodBase Objekt. Es gibt jedoch Einige Einschränkungen zu diesem Ansatz:

  1. Es repräsentiert die Laufzeit Stack - Optimierungen könnten eine Methode einführen, und Sie werden es tun nicht Sehen Sie sich diese Methode in der Stapelspur an.
  2. Es wird nicht Zeigen Sie native Frames. Wenn Sie also sogar eine Chance haben, dass Ihre Methode von einer nativen Methode aufgerufen wird, wird dies jedoch nicht Arbeit, und es gibt derzeit keine verfügbare Möglichkeit, dies zu tun.

(HINWEIS: Ich expnese nur weiter die Antwort bereitgestellt von Firas Assad.)

Eine schnelle Zusammenfassung der 2 Ansätze mit Geschwindigkeitsvergleich ist der wichtige Teil.

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

Ermittlung des Anrufers bei der Kompilierungszeit

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

Bestimmung des Anrufers mit dem Stapel

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 2 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, dass die Verwendung der Attribute viel, viel schneller ist! Fast 25x schneller.

Ab .NET 4.5 können Sie verwenden Anruferinformationen Attribute:

  • CallerFilePath - die Quelldatei, die die Funktion nannte;
  • CallerLineNumber - Codezeile, die die Funktion nannte;
  • CallerMemberName - Mitglied, das die Funktion nannte.

    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

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

In Betracht ziehen Aspekt-orientiertes Programmieren (AOP) wie Postsharp, was anstatt aus Ihrem Code aufgerufen zu werden, ändert Ihren Code und weiß daher, wo er zu jeder Zeit ist.

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

Natürlich ist dies eine verspätete 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);
}

Dadurch wird das aktuelle Datum und die aktuelle Uhrzeit gedruckt, gefolgt von "Namespace.ClassName.Methodname" und endet mit ": text".
Beispielausgabe:

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

Probengebrauch:

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

Vielleicht suchen Sie 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
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 da: http://www.csharp411.com/c-get-calling-method/

Ein anderer Ansatz, den ich verwendet habe, ist, der fraglichen Methode einen Parameter hinzuzufügen. Zum Beispiel statt von void Foo(), verwenden void Foo(string context). Geben Sie dann eine eindeutige Zeichenfolge über, die den aufrufenden Kontext anzeigt.

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

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

wird genug sein, denke ich.

Sich ansehen Protokollierungsmethodenname in .NET. Achten Sie darauf, es im Produktionscode zu verwenden. Stackframe ist möglicherweise nicht zuverlässig ...

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

Angenommen, Sie haben eine von Ihnen definierte Methode:

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

Und Sie möchten den Anrufer finden.

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

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

2. Lambda -Namen werden nicht zufällig generiert. Die Regel scheint:>u003CCallerMethodName> __X, wo Callmethodname durch die vorherige Funktion ersetzt wird und X ein Index ist.

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

3. Wenn wir Methoda aufrufen, muss der Parameter Aktion/Func von der Anrufermethode generiert werden. Beispiel:

MethodA(() => {});

4. Innerhalb von Methoda können wir jetzt die oben definierte Helferfunktion aufrufen und die Methode der Anrufermethode finden.

Beispiel:

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

Um den Methodennamen und der Unterrichtsname 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 );
    }

Zusätzliche Informationen an Firas Assaad Antwort.

Ich habe benutzt new StackFrame(1).GetMethod().Name; In .NET Core 2.1 mit Abhängigkeitsinjektion und ich erhalte eine Anrufmethode als "Start".

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

var callingMethod = new StackFrame(1, true).GetMethod();
string source = callingMethod.ReflectedType.FullName + ": " + callingMethod.Name;
Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top