Compartilhar via


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

Usando atributos de informações, 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 do membro que está chamando, use atributos aplicados a parâmetros opcionais. Cada parâmetro opcional especifica um valor padrão. A tabela a seguir lista os atributos de Informações do Chamador definidos no System.Runtime.CompilerServices namespace:

Atributo Descrição Tipo
CallerFilePathAttribute O 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 de 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ê com o rastreamento e a depuração e ajudam você 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

Especifique um valor padrão explícito para cada parâmetro opcional. Você não pode aplicar atributos de informações de 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 passado quando o argumento é omitido. Os valores de informações do chamador são emitidos como literais na IL (Linguagem Intermediária) no momento da compilação. Ao contrário dos resultados da StackTrace propriedade para exceções, a ofuscação não afeta os resultados. Você pode fornecer explicitamente os argumentos opcionais para controlar as informações do chamador ou ocultar 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. Ao usar essa técnica, você evita o problema de que a Refatoração de Renomeação não altera os valores de String. Esse benefício é especialmente útil para as seguintes tarefas:

  • Usar rotinas de rastreamento e diagnóstico.
  • Implementando a INotifyPropertyChanged interface ao associar dados. Essa interface permite que a propriedade de um objeto notifique um controle associado de que a propriedade foi alterada. O controle pode exibir as informações atualizadas. Sem o atributo CallerMemberName, você deve especificar o nome da propriedade como um valor literal.

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

As chamadas ocorrem dentro Resultado de nome de 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 cadeia de caracteres "Finalize"
Operadores ou conversões definidos pelo usuário O nome gerado para o membro, por exemplo, "op_Addition".
Construtor de atributos O nome do método ou propriedade ao qual o atributo é aplicado. Se o atributo for qualquer elemento dentro de um membro (como um parâmetro, um valor retornado ou um parâmetro de tipo genérico), esse resultado será o nome do membro associado a esse elemento.
Nenhum membro contentor (por exemplo, nível de assembly ou atributos que são aplicadas aos tipos) O valor padrão do parâmetro opcional.

Expressões de argumento

Use quando System.Runtime.CompilerServices.CallerArgumentExpressionAttribute quiser que a expressão seja passada como um argumento. As bibliotecas de diagnóstico podem 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. Essas informações extras facilitam 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 conforme mostrado no exemplo a seguir:

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

O compilador injeta a expressão usada no conditionmessage 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 a CallerArgumentExpressionAttribute para determinar qual expressão foi usada como receptor para membros de extensão. O método a seguir amostra uma sequência em intervalos regulares. Se a sequência tiver menos elementos do que a frequência, ela relatará um erro:

extension<T>(IEnumerable<T> sequence)
{
    public IEnumerable<T> Sample(int frequency, 
        [CallerArgumentExpression(nameof(sequence))] string? message = null)
    {
        if (sequence.Count() < frequency)
            throw new InvalidOperationException($"Expression doesn't have enough elements: {message}");
        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. Você pode chamar esse método da seguinte maneira:

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

O exemplo anterior geraria uma ArgumentException mensagem cujo texto é o seguinte:

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

Consulte também