Determinar informações do chamador usando atributos interpretados pelo compilador C#

Usando atributos info, você obtém informações sobre o chamador para um método. Você obtém o caminho do arquivo do código-fonte, o número da linha no código-fonte e o nome do membro do chamador. Para obter informações sobre o chamador de membro, use atributos que são aplicados a parâmetros opcionais. Cada parâmetro opcional especifica um valor padrão. A tabela a seguir lista os atributos Caller Info definidos no System.Runtime.CompilerServices namespace:

Atributo Description Type
CallerFilePathAttribute Caminho completo do arquivo de origem que contém o chamador. O caminho completo é o caminho em tempo de compilação. String
CallerLineNumberAttribute Número da linha no arquivo de origem do qual o método é chamado. Integer
CallerMemberNameAttribute Nome do método ou nome da propriedade do chamador. String
CallerArgumentExpressionAttribute Representação de cadeia de caracteres da expressão de argumento. String

Essas informações ajudam você a rastrear e depurar e a criar ferramentas de diagnóstico. O exemplo a seguir mostra como usar atributos de informações do chamador. Em cada chamada para o TraceMessage método, as informações do chamador são inseridas para os argumentos para os parâmetros opcionais.

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

Você especifica um valor padrão explícito para cada parâmetro opcional. Não é possível aplicar atributos de informações do chamador a parâmetros que não são especificados como opcionais. Os atributos de informações do chamador não tornam um parâmetro opcional. Em vez disso, eles afetam o valor padrão que é passado quando o argumento é omitido. Os valores de informações do chamador são emitidos como literais para a linguagem intermediária (IL) em tempo de compilação. Ao contrário dos resultados da StackTrace propriedade para exceções, os resultados não são afetados pela ofuscação. Você pode fornecer explicitamente os argumentos opcionais para controlar as informações do chamador ou ocultar as informações do chamador.

Nomes dos membros

Você pode usar o CallerMemberName atributo para evitar especificar o nome do membro como um String argumento para o método chamado. Usando essa técnica, você evita o problema de que Renomear refatoração não altera os String valores. Este benefício é especialmente útil para as seguintes tarefas:

  • Utilização de rotinas de rastreio e diagnóstico.
  • Implementação da INotifyPropertyChanged interface ao vincular dados. Essa interface permite que a propriedade de um objeto notifique um controle acoplado de que a propriedade foi alterada. O controle pode exibir as informações atualizadas. Sem o CallerMemberName atributo, você deve especificar o nome da propriedade como um literal.

O gráfico a seguir mostra os nomes de membros que são retornados quando você usa o CallerMemberName atributo.

As chamadas ocorrem dentro de Resultado do nome do membro
Método, propriedade ou evento O nome do método, propriedade ou evento do qual a chamada se originou.
Construtor A cadeia de caracteres ".ctor"
Construtor estático A cadeia de caracteres ".cctor"
Finalizador A string "Finalizar"
Operadores ou conversões definidos pelo usuário O nome gerado para o membro, por exemplo, "op_Addition".
Construtor Attribute O nome do método ou propriedade à qual o atributo é aplicado. Se o atributo for qualquer elemento dentro de um membro (como um parâmetro, um valor de retorno ou um parâmetro de tipo genérico), esse resultado será o nome do membro associado a esse elemento.
Nenhum membro contendo (por exemplo, nível de assembly ou atributos que são aplicados a tipos) O valor padrão do parâmetro opcional.

Expressões de argumento

Use o System.Runtime.CompilerServices.CallerArgumentExpressionAttribute quando quiser que a expressão seja passada como um argumento. As bibliotecas de diagnóstico podem querer fornecer mais detalhes sobre as expressões passadas para argumentos. Ao fornecer a expressão que disparou o diagnóstico, além do nome do parâmetro, os desenvolvedores têm mais detalhes sobre a condição que disparou o diagnóstico. Essa informação extra facilita a correção.

O exemplo a seguir mostra como você pode fornecer informações detalhadas sobre o argumento quando ele é inválido:

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

Você o invocaria como mostrado no exemplo a seguir:

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

A expressão usada para condition é injetada pelo compilador no message argumento. Quando um desenvolvedor chama Operation com um null argumento, a seguinte mensagem é armazenada no ArgumentException:

Argument failed validation: <func is not null>

Esse atributo permite que você escreva utilitários de diagnóstico que fornecem mais detalhes. Os desenvolvedores podem entender mais rapidamente quais alterações são necessárias. Você também pode usar o CallerArgumentExpressionAttribute para determinar qual expressão foi usada como o recetor para métodos de extensão. O método seguinte recolhe amostras de uma sequência a intervalos regulares. Se a sequência tiver menos elementos do que a frequência, ela relata um erro:

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

O exemplo anterior usa o nameof operador para o parâmetro sequence. Esse recurso está disponível em C# 11. Antes do C# 11, você precisará digitar o nome do parâmetro como uma cadeia de caracteres. Você pode chamar esse método da seguinte maneira:

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

O exemplo anterior lançaria uma ArgumentException mensagem cuja mensagem é o seguinte texto:

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

Consulte também