Partilhar via


Diga 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 de 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. Esta 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 ToString() substituído, o depurador usará o método substituído em vez do padrão {<typeName>}. Neste caso, você não precisa usar DebuggerDisplay. Se você usar ambos, o atributo DebuggerDisplay terá precedência sobre o método ToString() substituído. O atributo DebuggerDisplay também tem precedência sobre o método ToString() substituído em uma subclasse.

Se o depurador avalia essa chamada implícita ToString() depende de uma opção de Depuração em Ferramentas (ou Depuração)>Opções.

Important

Se você selecionar a opção Mostrar estrutura bruta de objetos em janelas de variáveis , o DebuggerDisplay atributo será ignorado. Essa configuração está localizada no painel Ferramentas>Opções na seção Todas as Configurações>Depuração>Geral.

Important

Se você selecionar a opção Mostrar estrutura bruta de objetos em janelas de variáveis , o DebuggerDisplay atributo será ignorado. Essa configuração está localizada na caixa de diálogo Ferramentas>Opções na seção Depuração>Geral.

Note

Para código nativo, esse atributo é suportado apenas no código C++/CLI.

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

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

Utilizado em um tipo com campos x e y.
x = 5 y = 18
[DebuggerDisplay("String value is {getString()}")]A sintaxe do 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.

Parameters Purpose
Name, Type Esses parâmetros afetam as colunas Name e Type das janelas de variáveis. (Eles podem ser definidos como strings usando a mesma sintaxe do construtor.) O uso excessivo desses parâmetros, ou o uso incorreto, pode causar uma saída confusa.
Target, TargetTypeName Especifica o tipo de destino quando o atributo é usado ao nível da assemblagem.

O arquivo autoexp.cs usa o atributo DebuggerDisplay no nível de 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. Certifique-se de fazer backup do arquivo autoexp.cs antes de modificá-lo.

Para criar autoexp.cs, abra um prompt de comando do desenvolvedor para VS2015 e execute os seguintes comandos

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

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

Usando expressões no DebuggerDisplay

Embora se possa usar uma expressão geral entre as chaves num atributo DebuggerDisplay, essa prática não é recomendada.

Uma expressão geral em DebuggerDisplay tem acesso implícito ao ponteiro this apenas para a instância atual do tipo-alvo. A expressão não tem acesso a aliases, locais ou ponteiros. Se a expressão fizer referência a propriedades, os atributos nessas propriedades não serão processados. Por exemplo, o código C# [DebuggerDisplay("Object {count - 2}")] exibiria 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 cada vez que é exibida. Isso pode causar problemas de desempenho ao passar 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ões da linguagem do quadro de pilha atual e não pelo avaliador da linguagem na qual a expressão foi escrita. Isso pode causar resultados imprevisíveis quando os idiomas são diferentes.

  • A avaliação de uma expressão pode alterar o estado do aplicativo. Por exemplo, uma expressão que define o valor de uma propriedade muta 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" diz ao avaliador de expressão para remover as aspas ao exibir o valor final (nq = sem aspas). Para obter mais informações sobre formatters, consulte Especificadores de formato em C#.

Example

O exemplo de código a seguir mostra como usar DebuggerDisplay, juntamente com DebuggerBrowsable e DebuggerTypeProxy. Quando visualizado numa janela de variáveis do depurador, como a janela Watch, ele produz uma expansão semelhante a esta:

Name Value Type
Key "three" objeto {string}
Value 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;
            }
        }
    }
}