Determinación de la información del autor de la llamada mediante atributos interpretados por el compilador de C#

Mediante los atributos de información, se obtiene información sobre el autor de la llamada a un método. Se obtiene la ruta de acceso al código fuente, el número de línea del código fuente y el nombre de miembro del autor de la llamada. Para obtener la información del llamador del miembro, use los atributos que se aplican a los parámetros opcionales. Cada parámetro opcional especifica un valor predeterminado. En la tabla siguiente se enumeran los atributos de información del llamador que se definen en el espacio de nombres System.Runtime.CompilerServices:

Atributo Descripción Tipo
CallerFilePathAttribute Ruta de acceso completa del archivo de código fuente que contiene el llamador. La ruta de acceso completa es la ruta de acceso en tiempo de compilación. String
CallerLineNumberAttribute Número de línea del archivo de código fuente desde el que se llama al método. Integer
CallerMemberNameAttribute Nombre de método o de propiedad del llamador. String
CallerArgumentExpressionAttribute Representación de cadena de la expresión de argumento. String

Esta información ayuda con el seguimiento y la depuración, y permite crear herramientas de diagnóstico. En el ejemplo siguiente se muestra cómo usar atributos de información del autor de la llamada. En cada llamada al método TraceMessage, la información del autor de la llamada se inserta como argumentos para los parámetros opcionales.

public void DoProcessing()
{
    TraceMessage("Something happened.");
}

public void TraceMessage(string message,
        [CallerMemberName] string memberName = "",
        [CallerFilePath] string sourceFilePath = "",
        [CallerLineNumber] int sourceLineNumber = 0)
{
    Trace.WriteLine("message: " + message);
    Trace.WriteLine("member name: " + memberName);
    Trace.WriteLine("source file path: " + sourceFilePath);
    Trace.WriteLine("source line number: " + sourceLineNumber);
}

// Sample Output:
//  message: Something happened.
//  member name: DoProcessing
//  source file path: c:\Visual Studio Projects\CallerInfoCS\CallerInfoCS\Form1.cs
//  source line number: 31

Debe especificar un valor predeterminado explícito para cada parámetro opcional. No puede aplicar atributos de información del autor de la llamada a los parámetros que no se especifican como opcionales. Los atributos de información del autor de la llamada no crean un parámetro opcional, sino que influyen en el valor predeterminado que se pasa cuando se omite el argumento. Los valores de información del autor de la llamada se emiten como literales en el lenguaje intermedio (IL) en tiempo de compilación. A diferencia de los resultados de la propiedad StackTrace para las excepciones, los resultados no se ven afectados por confusión. Puede proporcionar explícitamente los argumentos opcionales para controlar la información del llamador u ocultarla.

Nombres de miembro

Se puede utilizar el atributo CallerMemberName para evitar especificar el nombre de miembro como un argumento String para el método llamado. Mediante esta técnica, se evita el problema de que la refactorización de cambio de nombre no cambie los valores String. Esta ventaja es especialmente útil para las siguientes tareas:

  • Usar el seguimiento y las rutinas de diagnóstico.
  • Implementar la interfaz INotifyPropertyChanged al enlazar datos. Esta interfaz permite que la propiedad de un objeto notifique a un control enlazado que esta propiedad ha cambiado. El control puede mostrar la información actualizada. Sin el atributo CallerMemberName, se debe especificar el nombre de propiedad como un literal.

En el gráfico siguiente se muestran los nombres de miembro que se devuelven cuando se utiliza el atributo CallerMemberName.

Las llamadas se producen en Resultado del nombre de miembro
Método, propiedad o evento Nombre del método, propiedad o evento en el que se originó la llamada.
Constructor Cadena ".ctor"
Constructor estático Cadena ".cctor"
Finalizador Cadena "Finalize"
Conversiones u operadores definidos por el usuario Nombre generado para el miembro, por ejemplo, "op_Addition".
Constructor de atributo Nombre del método o la propiedad a la que se aplica el atributo. Si el atributo es cualquier elemento dentro de un miembro (como un parámetro, un valor devuelto o un parámetro de tipo genérico), el resultado es el nombre del miembro asociado a este elemento.
Ningún miembro contenedor (por ejemplo, nivel de ensamblado o atributos que se aplican a tipos) El valor predeterminado del parámetro opcional.

Expresiones de argumento

Se debe usar System.Runtime.CompilerServices.CallerArgumentExpressionAttribute cuando quiera que la expresión se pase como argumento. Es posible que las bibliotecas de diagnóstico quieran proporcionar más detalles sobre las expresiones que se pasan a los argumentos. Al proporcionar la expresión que desencadenó el diagnóstico, además del nombre del parámetro, los desarrolladores tienen más detalles sobre la condición que desencadenó el diagnóstico. Esa información adicional facilita la corrección.

En el ejemplo siguiente se muestra cómo puede proporcionar información detallada sobre el argumento cuando no es válido:

public static void ValidateArgument(string parameterName, bool condition, [CallerArgumentExpression("condition")] string? message=null)
{
    if (!condition)
    {
        throw new ArgumentException($"Argument failed validation: <{message}>", parameterName);
    }
}

Se invocaría tal como se muestra en el ejemplo siguiente:

public void Operation(Action func)
{
    Utilities.ValidateArgument(nameof(func), func is not null);
    func();
}

El compilador inserta la expresión usada para condition en el argumento message. Cuando un desarrollador llama a Operation con un argumento null, el mensaje siguiente se almacena en ArgumentException:

Argument failed validation: <func is not null>

Este atributo permite escribir utilidades de diagnóstico que proporcionan más detalles. Los desarrolladores pueden comprender más rápidamente qué cambios son necesarios. También puede usar CallerArgumentExpressionAttribute a fin de determinar qué expresión se usó como receptor para los métodos de extensión. El método siguiente muestrea una secuencia a intervalos regulares. Si la secuencia tiene menos elementos que la frecuencia, notifica un error:

public static IEnumerable<T> Sample<T>(this IEnumerable<T> sequence, int frequency, 
    [CallerArgumentExpression(nameof(sequence))] string? message = null)
{
    if (sequence.Count() < frequency)
        throw new ArgumentException($"Expression doesn't have enough elements: {message}", nameof(sequence));
    int i = 0;
    foreach (T item in sequence)
    {
        if (i++ % frequency == 0)
            yield return item;
    }
}

En el ejemplo anterior se usa el operador nameof para el parámetro sequence. Esa característica está disponible en C# 11. Antes de C# 11, deberá escribir el nombre del parámetro como una cadena. Se puede llamar a este método de la siguiente manera:

sample = Enumerable.Range(0, 10).Sample(100);

En el ejemplo anterior se produciría un elemento ArgumentException cuyo mensaje es el texto siguiente:

Expression doesn't have enough elements: Enumerable.Range(0, 10) (Parameter 'sequence')

Vea también