Saltar al contenido

Artículos, tutoriales, trucos, curiosidades, reflexiones y links sobre programación web ASP.NET Core, MVC, Blazor, SignalR, Entity Framework, C#, Azure, Javascript... y lo que venga ;)

19 años online

el blog de José M. Aguilar

Inicio El autor Contactar

Artículos, tutoriales, trucos, curiosidades, reflexiones y links sobre programación web
ASP.NET Core, MVC, Blazor, SignalR, Entity Framework, C#, Azure, Javascript...

¡Microsoft MVP!
martes, 6 de mayo de 2025
Mago abriendo el capó de un coche para ver la magia que hay dentro

Hace unas semanas estuvimos hablando de los atributos englobados en la categoría de "caller information" de .NET: [CallerFilePath], [CallerLineNumber], [CallerMemberName] y [CallerArgumentExpression]. Como vimos, estos atributos permiten que un método o función conozca información sobre el método que lo ha llamado, como el nombre del archivo, el número de línea, el nombre del método o la expresión de argumento.

Sin embargo, ninguna de estas opciones nos permite saber cuál es la clase que contiene el código que ha llamado al método que está actualmente en ejecución, algo que podría venir bien en algunos escenarios. Por ejemplo, recientemente me lo he encontrado revisando un proyecto en el que se utilizaba el sistema de logging NLog, donde encontraba un código como el siguiente para obtener y almacenar de forma estática un logger para la clase actual:

public static class Program
{
    private static readonly NLog.Logger Logger = NLog.LogManager.GetCurrentClassLogger();
    public static void DoSomething()
    {
        try
        {
           Logger.Info("Hello world");
           System.Console.ReadKey();
        }
        catch (Exception ex)
        {
           Logger.Error(ex, "Goodbye cruel world");
        }
    }
}  

A la vista de estas líneas, puede parecernos difícil deducir qué hace ese método GetCurrentClassLogger() por detrás para saber cuál es la clase actual y crearle un logger específico para ella.

En este post, vamos a abrir el capó para ver la "magia" que hace esto posible.

El truco: acceder al stack trace

Pues sí, así de simple 😉

Lejos de soluciones resueltas en tiempo de compilación, como las basadas en los atributos "caller information" que vimos en su momento, en este caso simplemente vamos a consultar la pila de llamadas en tiempo de ejecución para saber desde qué clase se está invocando al método actual.

Como sabemos, la pila de llamadas (stack trace) es una estructura de datos que contiene información sobre las llamadas a métodos que se ido realizado en un programa hasta llegar al momento en que la estamos consultando. Es muy habitual hacerlo, sobre todo cuando inspeccionamos excepciones, y mientras depuramos, para saber cómo hemos llegado a un determinado punto del código.

En .NET, podemos acceder a esta información a través de la clase StackTrace, que nos permite obtener un array de objetos StackFrame, cada uno de los cuales representa un punto de la pila de llamadas.

Así pues, podemos utilizar estos recursos para conseguir nuestros objetivos. Echad un vistazo al siguiente código, bastante simple:

public static class ThisClass
{
    public static string GetName()
    {
        var frame = new StackTrace().GetFrame(1);
        return frame?.GetMethod()?.DeclaringType?.Name ?? "Unknown";
    }
}

Esta clase permite llamar desde cualquier otro punto del proyecto al método ThisClass.GetName() para obtener el nombre de la clase actual. Para ello:

  • En primer lugar, creamos una instancia de la clase StackTrace, que nos permite acceder a la pila de llamadas.
  • A continuación, usamos el método GetFrame() para obtener el frame con índice 1, que corresponde al método que acaba de llamar a GetName(). El índice 0 sería el propio método GetName() que estamos implementando.
  • El objeto StackFrame retornado permite acceder a información sobre el punto del código fuente donde se ha hecho la llamada (archivo, fila, columna), su posición en el IL o código nativo, y un objeto MethodBase con metadatos sobre el método que llamó a ThisClass.GetName().
  • A partir del StackFrame obtenido, usamos su propiedad DeclaringType para obtener el tipo en el que se ha definido, y de ahí su propiedad Name para obtener el nombre de la clase, que es lo que retornamos.

Para ver un ejemplo de uso, fijaos en la siguiente aplicación de consola:

Console.WriteLine(ThisClass.GetName());
new Foo().DoSomething();

public class Foo
{
    public void DoSomething()
    {
        Console.WriteLine(ThisClass.GetName());
    }
}

Al ejecutarla, lo que obtendremos será:

Program
Foo

En definitiva, un truco muy sencillo que conviene conocer porque nos puede ser de utilidad en algunos casos. Pero eso sí, tened siempre en cuenta que estamos hablando de una técnica que tiene un coste importante en términos de rendimiento, por lo que no conviene abusar de ella, ni introducirla en zonas críticas de nuestro código.

¡Espero que os haya resultado interesante! 😊

Publicado en Variable not found.

Aún no hay comentarios, ¡sé el primero!