Compartilhar via


Informe ao depurador o que mostrar usando o atributo DebuggerDisplay (C#, Visual Basic, F#, C++/CLI)

O DebuggerDisplayAttribute controla como um objeto, propriedade ou campo é exibido nas janelas variáveis do depurador. Esse atributo pode ser aplicado a tipos (classes, structs, enums, delegates), mas normalmente é aplicado apenas a classes e structs. Se aplicado a um tipo base, o atributo também se aplica a uma subclasse.

O atributo DebuggerDisplay tem um único argumento, que é uma cadeia de caracteres a ser exibida na coluna de valor para instâncias do tipo. Essa cadeia de caracteres pode conter chaves ({ e }). O texto dentro de um par de chaves é avaliado como um campo, propriedade ou método.

Se uma classe tiver um método de ToString() substituído, o depurador usará o método substituído em vez do {<typeName>}padrão. Portanto, se você substituiu o método ToString(), o depurador usará o método substituído em vez do {<typeName>}padrão e não precisará usar DebuggerDisplay. Se você usar ambos, o atributo DebuggerDisplay terá precedência sobre o método de ToString() substituído. O atributo DebuggerDisplay também tem precedência sobre o método de ToString() substituído em uma subclasse.

A avaliação dessa chamada ToString() implícita pelo depurador depende de uma configuração do usuário na caixa de diálogo Ferramentas/Opções/Depuração.

Importante

Se a caixa de seleção Exibir estrutura bruta de objetos nas janelas de variáveis estiver selecionada na caixa de diálogo Ferramentas > Opções > Depuração, o atributo DebuggerDisplay será ignorado.

Nota

Para código nativo, esse atributo só tem suporte no código C++/CLI.

A tabela a seguir mostra alguns usos possíveis do atributo DebuggerDisplay e exemplos de saídas.

Atributo Saída que aparece na coluna Valor
[DebuggerDisplay("x = {x} y = {y}")]

Usado em um tipo com campos x e y.
x = 5 y = 18
[DebuggerDisplay("String value is {getString()}")]Sintaxe de parâmetro pode variar entre idiomas. Portanto, use-o com cuidado. String value is [5, 6, 6]

DebuggerDisplay também pode aceitar parâmetros nomeados.

Parâmetros Propósito
Name, Type Esses parâmetros afetam as colunas Nome e Tipo das janelas de variáveis. (Eles podem ser definidos como cadeias de caracteres usando a mesma sintaxe que o construtor.) Usar esses parâmetros em excesso ou usá-los incorretamente pode causar saída confusa.
Target, TargetTypeName Especifica o tipo de destino quando o atributo é usado no nível da assemblagem.

O arquivo autoexp.cs usa o atributo DebuggerDisplay no nível do assembly. O arquivo autoexp.cs determina as expansões padrão que o Visual Studio usa para objetos .NET. Você pode examinar o arquivo autoexp.cs para obter exemplos de como usar o atributo DebuggerDisplay ou pode modificar e compilar o arquivo autoexp.cs para alterar as expansões padrão. Faça backup do arquivo de autoexp.cs antes de modificá-lo.

Para criar autoexp.cs, abra um Prompt de Comando do Desenvolvedor para VS2015 e execute os comandos a seguir

cd <directory containing autoexp.cs>
csc /t:library autoexp.cs

As alterações no autoexp.dll serão selecionadas na próxima sessão de depuração.

Usando expressões no DebuggerDisplay

Embora você possa usar uma expressão geral entre as chaves em um atributo DebuggerDisplay, esta prática não é recomendada.

Uma expressão geral em DebuggerDisplay tem acesso implícito ao ponteiro this somente para a instância atual do tipo de destino. A expressão não tem acesso a alias, locais ou ponteiros. Se a expressão referenciar propriedades, os atributos nessas propriedades não serão processados. Por exemplo, o código C# [DebuggerDisplay("Object {count - 2}")] apareceria como Object 6 se o campo count fosse 8.

O uso de expressões no DebuggerDisplay pode levar aos seguintes problemas:

  • Avaliar expressões é a operação mais cara no depurador e a expressão é avaliada sempre que é exibida. Isso pode causar problemas de desempenho ao navegar pelo código. Por exemplo, uma expressão complexa usada para exibir os valores em uma coleção ou lista pode ser muito lenta quando o número de elementos é grande.

  • As expressões são avaliadas pelo avaliador de expressão da linguagem do quadro de pilhas atual e não pelo avaliador da linguagem na qual a expressão foi gravada. Isso pode causar resultados imprevisíveis quando os idiomas são diferentes.

  • Avaliar uma expressão pode alterar o estado do aplicativo. Por exemplo, uma expressão que define o valor de uma propriedade altera o valor da propriedade no código de execução.

    Uma maneira de reduzir os possíveis problemas de avaliação de expressão é criando uma propriedade privada que executa a operação e retorna uma cadeia de caracteres. O atributo DebuggerDisplay pode exibir o valor dessa propriedade privada. O exemplo a seguir implementa esse padrão:

[DebuggerDisplay("{DebuggerDisplay,nq}")]
public sealed class MyClass
{
    public int count { get; set; }
    public bool flag { get; set; }
    private string DebuggerDisplay
    {
        get
        {
            return string.Format("Object {0}", count - 2);
        }
    }
}

O sufixo "nq" informa ao avaliador de expressão para remover as aspas ao exibir o valor final (nq = sem aspas).

Exemplo

O exemplo de código a seguir mostra como usar DebuggerDisplay, juntamente com DebuggerBrowsable e DebuggerTypeProxy. Quando exibido em uma janela variáveis de depurador, como, por exemplo, a janela Inspeção, ele produz uma expansão semelhante ao seguinte:

Nome Valor Tipo
Chave "three" objeto {string}
Valor 3 objeto {int}
[DebuggerDisplay("{value}", Name = "{key}")]
internal class KeyValuePairs
{
    private IDictionary dictionary;
    private object key;
    private object value;
    public KeyValuePairs(IDictionary dictionary, object key, object value)
    {
        this.value = value;
        this.key = key;
        this.dictionary = dictionary;
    }

    public object Key
    {
        get { return key; }
        set
        {
            object tempValue = dictionary[key];
            dictionary.Remove(key);
            key = value;
            dictionary.Add(key, tempValue);
        }
    }

    public object Value
    {
        get { return this.value; }
        set
        {
            this.value = value;
            dictionary[key] = this.value;
        }
    }
}

[DebuggerDisplay("{DebuggerDisplay,nq}")]
[DebuggerTypeProxy(typeof(HashtableDebugView))]
class MyHashtable
{
    public Hashtable hashtable;

    public MyHashtable()
    {
        hashtable = new Hashtable();
    }

    private string DebuggerDisplay { get { return "Count = " + hashtable.Count; } }

    private class HashtableDebugView
    {
        private MyHashtable myhashtable;
        public HashtableDebugView(MyHashtable myhashtable)
        {
            this.myhashtable = myhashtable;
        }

        [DebuggerBrowsable(DebuggerBrowsableState.RootHidden)]
        public KeyValuePairs[] Keys
        {
            get
            {
                KeyValuePairs[] keys = new KeyValuePairs[myhashtable.hashtable.Count];

                int i = 0;
                foreach (object key in myhashtable.hashtable.Keys)
                {
                    keys[i] = new KeyValuePairs(myhashtable.hashtable, key, myhashtable.hashtable[key]);
                    i++;
                }
                return keys;
            }
        }
    }
}