Melhorando a depuração com os atributos de exibição do depurador
Observação
Este artigo é específico para aplicativos .NET Framework. Não se aplica a implementações mais recentes do .NET, incluindo o .NET 6 e versões posteriores.
Os atributos de exibição do depurador permitem ao desenvolvedor do tipo, que especifica e entende melhor o comportamento de runtime desse tipo, especificar também a aparência desse tipo quando ele é exibido em um depurador. Além disso, os atributos de exibição do depurador que fornecem uma propriedade Target
podem ser aplicados no nível do assembly pelos usuários sem conhecimento do código-fonte. O atributo DebuggerDisplayAttribute controla como um tipo ou um membro é exibido nas janelas de variáveis do depurador. O atributo DebuggerBrowsableAttribute determina se e como um campo ou uma propriedade é exibida nas janelas de variáveis do depurador. O atributo DebuggerTypeProxyAttribute especifica um tipo substituto ou um proxy, para um tipo e altera o modo como o tipo é exibido nas janelas do depurador. Quando você exibe uma variável que tem um proxy ou um tipo substituto, o proxy substitui o tipo original na janela de exibição do depurador. A janela de variáveis do depurador exibe apenas os membros públicos do tipo de proxy. Os membros particulares não são exibidos.
Usando o DebuggerDisplayAttribute
O construtor DebuggerDisplayAttribute tem um único argumento: uma cadeia de caracteres a ser exibida na coluna de valor das instâncias do tipo. Essa cadeia de caracteres pode conter chaves ({ e }). O texto dentro de um par de chaves é avaliado como uma expressão. Por exemplo, o código C# a seguir faz com que “Contagem = 4” seja exibida quando o sinal de adição (+) é selecionado para expandir a exibição do depurador para uma instância de MyHashtable
.
[DebuggerDisplay("Count = {count}")]
class MyHashtable
{
public int count = 4;
}
Os atributos aplicados às propriedades referenciadas na expressão não são processados. Para o compilador C#, uma expressão geral é permitida que tem somente acesso implícito a essa referência na instância atual do tipo de destino. A expressão é limitada; não há nenhum acesso a aliases, locais ou ponteiros. No código C#, você pode usar uma expressão geral entre as chaves que tem acesso implícito ao ponteiro this
na instância atual somente do tipo de destino.
Por exemplo, se um objeto do C# tiver um ToString()
substituído, o depurador chamará a substituição e mostrará seu resultado, em vez do {<typeName>}.
padrão. Portanto, se você tiver substituído ToString()
, não precisará usar DebuggerDisplayAttribute. Se você usar ambas, o atributo DebuggerDisplayAttribute terá precedência sobre a substituição de ToString()
.
Usando o DebuggerBrowsableAttribute
Aplique o DebuggerBrowsableAttribute a um campo ou uma propriedade para especificar como o campo ou a propriedade deve ser exibida na janela do depurador. O construtor desse atributo usa um dos valores de enumeração DebuggerBrowsableState, que especifica um dos seguintes estados:
Never indica que o membro não é exibido na janela de dados. Por exemplo, o uso desse valor para o DebuggerBrowsableAttribute em um campo remove o campo da hierarquia; o campo não é exibido quando você expande o tipo delimitador clicando no sinal de adição (+) da instância de tipo.
Collapsed indica que o membro é exibido, mas não expandido por padrão. Esse é o comportamento padrão.
RootHidden indica que o próprio membro não é mostrado, mas seus objetos constituintes são exibidos, se ele é uma matriz ou uma coleção.
Observação
Não há suporte para o DebuggerBrowsableAttribute no Visual Basic no .NET Framework 2.0.
O exemplo de código a seguir mostra o uso do DebuggerBrowsableAttribute para impedir que a propriedade após ele seja exibida na janela de depuração da classe.
[DebuggerBrowsable(DebuggerBrowsableState.Never)]
public static string y = "Test String";
Usando o DebuggerTypeProxy
Use o atributo DebuggerTypeProxyAttribute quando você precisar alterar a exibição de depuração de um tipo de forma significativa e básica, mas não o próprio tipo. O atributo DebuggerTypeProxyAttribute é usado para especificar um proxy de exibição para um tipo, permitindo que um desenvolvedor adapte a exibição para o tipo. Este atributo, como o DebuggerDisplayAttribute, pode ser usado no nível do assembly, caso em que a propriedade Target especifica o tipo para o qual o proxy será usado. O uso recomendado é que este atributo especifique um tipo aninhado particular que ocorre dentro do tipo ao qual o atributo é aplicado. Um avaliador de expressão que dá suporte a visualizadores de tipo verifica se esse atributo existe quando um tipo é exibido. Se o atributo for encontrado, o avaliador de expressão substituirá o tipo de proxy de exibição do tipo ao qual o atributo é aplicado.
Quando o DebuggerTypeProxyAttribute está presente, a janela de variáveis do depurador exibe apenas os membros públicos do tipo de proxy. Os membros particulares não são exibidos. O comportamento da janela de dados não é alterado por exibições aprimoradas por atributo.
Para evitar penalidades de desempenho desnecessárias, os atributos do proxy de exibição não são processados até que o objeto seja expandido, por meio do clique pelo usuário no sinal de adição (+) ao lado do tipo em uma janela de dados ou por meio da aplicação do atributo DebuggerBrowsableAttribute. Portanto, é recomendável que nenhum atributo seja aplicado ao tipo de exibição. Os atributos podem e devem ser aplicados no corpo do tipo de exibição.
O exemplo de código a seguir mostra o uso do DebuggerTypeProxyAttribute para especificar um tipo a ser usado como um proxy de exibição do depurador.
[DebuggerTypeProxy(typeof(HashtableDebugView))]
class MyHashtable : Hashtable
{
private const string TestString =
"This should not appear in the debug window.";
internal class HashtableDebugView
{
private Hashtable hashtable;
public const string TestStringProxy =
"This should appear in the debug window.";
// The constructor for the type proxy class must have a
// constructor that takes the target type as a parameter.
public HashtableDebugView(Hashtable hashtable)
{
this.hashtable = hashtable;
}
}
}
Exemplo
Descrição
O exemplo de código a seguir pode ser exibido no Visual Studio para ver os resultados da aplicação dos atributosDebuggerDisplayAttribute, DebuggerBrowsableAttribute e DebuggerTypeProxyAttribute.
Código
using namespace System;
using namespace System::Collections;
using namespace System::Diagnostics;
using namespace System::Reflection;
ref class HashtableDebugView;
[DebuggerDisplay("{value}", Name = "{key}")]
ref class KeyValuePairs
{
private:
IDictionary^ dictionary;
Object^ key;
Object^ value;
public:
KeyValuePairs(IDictionary^ dictionary, Object^ key, Object^ value)
{
this->value = value;
this->key = key;
this->dictionary = dictionary;
}
};
[DebuggerDisplay("Count = {Count}")]
[DebuggerTypeProxy(HashtableDebugView::typeid)]
ref class MyHashtable : Hashtable
{
private:
static const String^ TestString = "This should not appear in the debug window.";
internal:
ref class HashtableDebugView
{
private:
Hashtable^ hashtable;
public:
static const String^ TestString = "This should appear in the debug window.";
HashtableDebugView(Hashtable^ hashtable)
{
this->hashtable = hashtable;
}
[DebuggerBrowsable(DebuggerBrowsableState::RootHidden)]
property array<KeyValuePairs^>^ Keys
{
array<KeyValuePairs^>^ get()
{
array<KeyValuePairs^>^ keys = gcnew array<KeyValuePairs^>(hashtable->Count);
IEnumerator^ ie = hashtable->Keys->GetEnumerator();
int i = 0;
Object^ key;
while (ie->MoveNext())
{
key = ie->Current;
keys[i] = gcnew KeyValuePairs(hashtable, key, hashtable[key]);
i++;
}
return keys;
}
}
};
};
public ref class DebugViewTest
{
private:
// The following constant will appear in the debug window for DebugViewTest.
static const String^ TabString = " ";
public:
// The following DebuggerBrowsableAttribute prevents the property following it
// from appearing in the debug window for the class.
[DebuggerBrowsable(DebuggerBrowsableState::Never)]
static String^ y = "Test String";
static void Main()
{
MyHashtable^ myHashTable = gcnew MyHashtable();
myHashTable->Add("one", 1);
myHashTable->Add("two", 2);
Console::WriteLine(myHashTable->ToString());
Console::WriteLine("In Main.");
}
};
int main()
{
DebugViewTest::Main();
}
using System;
using System.Collections;
using System.Diagnostics;
using System.Reflection;
class DebugViewTest
{
// The following constant will appear in the debug window for DebugViewTest.
const string TabString = " ";
// The following DebuggerBrowsableAttribute prevents the property following it
// from appearing in the debug window for the class.
[DebuggerBrowsable(DebuggerBrowsableState.Never)]
public static string y = "Test String";
static void Main()
{
MyHashtable myHashTable = new MyHashtable();
myHashTable.Add("one", 1);
myHashTable.Add("two", 2);
Console.WriteLine(myHashTable.ToString());
Console.WriteLine("In Main.");
}
}
[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;
}
}
[DebuggerDisplay("Count = {Count}")]
[DebuggerTypeProxy(typeof(HashtableDebugView))]
class MyHashtable : Hashtable
{
private const string TestString = "This should not appear in the debug window.";
internal class HashtableDebugView
{
private Hashtable hashtable;
public const string TestString = "This should appear in the debug window.";
public HashtableDebugView(Hashtable hashtable)
{
this.hashtable = hashtable;
}
[DebuggerBrowsable(DebuggerBrowsableState.RootHidden)]
public KeyValuePairs[] Keys
{
get
{
KeyValuePairs[] keys = new KeyValuePairs[hashtable.Count];
int i = 0;
foreach(object key in hashtable.Keys)
{
keys[i] = new KeyValuePairs(hashtable, key, hashtable[key]);
i++;
}
return keys;
}
}
}
}
Imports System.Collections
Imports System.Diagnostics
Imports System.Reflection
Class DebugViewTest
' The following constant will appear in the debug window for DebugViewTest.
Const TabString As String = " "
' The following DebuggerBrowsableAttribute prevents the property following it
' from appearing in the debug window for the class.
<DebuggerBrowsable(DebuggerBrowsableState.Never)> _
Public Shared y As String = "Test String"
Shared Sub Main()
Dim myHashTable As New MyHashtable()
myHashTable.Add("one", 1)
myHashTable.Add("two", 2)
Console.WriteLine(myHashTable.ToString())
Console.WriteLine("In Main.")
End Sub
End Class
<DebuggerDisplay("{value}", Name:="{key}")> _
Friend Class KeyValuePairs
Private dictionary As IDictionary
Private key As Object
Private value As Object
Public Sub New(ByVal dictionary As IDictionary, ByVal key As Object, ByVal value As Object)
Me.value = value
Me.key = key
Me.dictionary = dictionary
End Sub
End Class
<DebuggerDisplay("Count = {Count}"), DebuggerTypeProxy(GetType(MyHashtable.HashtableDebugView))> _
Class MyHashtable
Inherits Hashtable
Private Const TestString As String = "This should not appear in the debug window."
Friend Class HashtableDebugView
Private hashtable As Hashtable
Public Shared TestString As String = "This should appear in the debug window."
Public Sub New(ByVal hashtable As Hashtable)
Me.hashtable = hashtable
End Sub
<DebuggerBrowsable(DebuggerBrowsableState.RootHidden)> _
ReadOnly Property Keys as KeyValuePairs()
Get
Dim nkeys(hashtable.Count - 1) As KeyValuePairs
Dim i as Integer = 0
For Each key As Object In hashtable.Keys
nkeys(i) = New KeyValuePairs(hashtable, key, hashtable(key))
i = i + 1
Next
Return nkeys
End Get
End Property
End Class
End Class