tag remarks generate example c# .net logging stack-trace system.diagnostics

generate - remarks c#



¿Cómo puedo encontrar el método que llamó al método actual? (17)

Al iniciar sesión en C #, ¿cómo puedo aprender el nombre del método que llamó al método actual? Sé todo sobre System.Reflection.MethodBase.GetCurrentMethod() , pero quiero ir un paso debajo de esto en el seguimiento de la pila. He considerado analizar el seguimiento de la pila, pero espero encontrar una forma más explícita más limpia, algo como Assembly.GetCallingAssembly() pero para los métodos.



En C # 5 puede obtener esa información usando la información de la persona que llama:

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

También puede obtener [CallerFilePath] y [CallerLineNumber] .


En general, puede usar la clase System.Diagnostics.StackTrace para obtener un System.Diagnostics.StackFrame , y luego usar el método GetMethod() para obtener un objeto System.Reflection.MethodBase . Sin embargo, hay algunas advertencias a este enfoque:

  1. Representa la pila de tiempo de ejecución : las optimizaciones podrían incluir un método en línea, y no verá ese método en el seguimiento de la pila.
  2. No mostrará ningún fotograma nativo, por lo que si existe la posibilidad de que su método sea llamado por un método nativo, esto no funcionará y, de hecho, no hay una manera disponible para hacerlo.

( NOTA: estoy ampliando la respuesta proporcionada por Firas Assad ).


Obviamente, esta es una respuesta tardía, pero tengo una mejor opción si puedes usar .NET 4.5 o más:

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

Esto imprimirá la Fecha y Hora actuales, seguido de "Namespace.ClassName.MethodName" y terminando con ": text".
Salida de muestra:

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

Uso de la muestra:

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


Otro enfoque que he usado es agregar un parámetro al método en cuestión. Por ejemplo, en lugar de void Foo() , use void Foo(string context) . Luego pase una cadena única que indique el contexto de llamada.

Si solo necesita la persona que llama / contexto para el desarrollo, puede eliminar el param antes del envío.


Podemos mejorar el código del Sr. Assad (la respuesta actual aceptada) solo un poco al crear una instancia del marco que realmente necesitamos en lugar de toda la pila:

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

Esto podría funcionar un poco mejor, aunque con toda probabilidad aún tiene que usar la pila completa para crear ese único fotograma. Además, todavía tiene las mismas advertencias que señaló Alex Lyman (el optimizador / código nativo podría corromper los resultados). Finalmente, es posible que desee verificar para asegurarse de que el new StackFrame(1) o .GetFrame(1) no devuelva null , por muy improbable que pueda parecer esa posibilidad.

Vea esta pregunta relacionada: ¿Puede usar la reflexión para encontrar el nombre del método que se ejecuta actualmente?


Prueba esto:

using System.Diagnostics; // Get call stack StackTrace stackTrace = new StackTrace(); // Get calling method name Console.WriteLine(stackTrace.GetFrame(1).GetMethod().Name);

Es a partir del método Get Calling usando Reflection [C #] .


Puede utilizar la información de la persona que llama y los parámetros opcionales:

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

Esta prueba ilustra esto:

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

Mientras que el StackTrace funciona bastante rápido arriba y no sería un problema de rendimiento en la mayoría de los casos, la información de la persona que llama es mucho más rápida aún. En una muestra de 1000 iteraciones, lo cronometré 40 veces más rápido.


Tal vez usted está buscando algo como esto:

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


También podemos utilizar lambda para encontrar a la persona que llama.

Supongamos que usted tiene un método definido por usted:

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

y quieres encontrar a la persona que llama.

1 . Cambie la firma del método para que tengamos un parámetro de tipo Acción (Func también funcionará):

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

2 . Los nombres Lambda no se generan aleatoriamente. La regla parece ser:> <CallerMethodName> __X donde CallerMethodName se reemplaza por la función anterior y X es un índice.

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

3 . Cuando llamamos al Método A, el método de llamada debe generar el parámetro Acción / Función. Ejemplo:

MethodA(() => {});

4 . Dentro del Método A, ahora podemos llamar a la función de ayuda definida anteriormente y encontrar el Método de Información del método que llama.

Ejemplo:

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


Tenga en cuenta que hacerlo no será confiable en el código de lanzamiento, debido a la optimización. Además, ejecutar la aplicación en modo sandbox (compartir en red) no te permitirá agarrar el marco de la pila en absoluto.

Considere la programación orientada a aspectos (AOP), como PostSharp , que en lugar de ser llamada desde su código, modifica su código y, por lo tanto, sabe dónde está en todo momento.


Un resumen rápido de los 2 enfoques con comparación de velocidad es la parte importante.

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

Determinación de la persona que llama en tiempo de compilación

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

Determinar la persona que llama utilizando la pila

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

Comparación de los 2 enfoques.

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

Así que ya ves, usar los atributos es mucho, mucho más rápido! De hecho, casi 25 veces más rápido.


A partir de .NET 4.5 puede usar los atributos de información del llamante :

  • CallerFilePath : el archivo de origen que llamó a la función;
  • CallerLineNumber - Línea de código que llamó a la función;
  • CallerMemberName : miembro que llamó a la función.

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

Esta instalación también está presente en ".NET Core" y ".NET Standard".

Referencias

  1. Microsoft - Información del llamante (C #)
  2. CallerFilePath
  3. CallerLineNumber
  4. CallerMemberName

/// <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 caller = (new System.Diagnostics.StackTrace()).GetFrame(1); string methodName = caller.GetMethod().Name;

Será suficiente, creo.


private static MethodBase GetCallingMethod() { return new StackFrame(2, false).GetMethod(); } private static Type GetCallingType() { return new StackFrame(2, false).GetMethod().DeclaringType; }

Una clase fantástica está aquí: http://www.csharp411.com/c-get-calling-method/


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