Mejorar la depuración con los atributos de visualización del depurador
Nota:
Este artículo es específico de .NET Framework. No se aplica a implementaciones más recientes de .NET, incluido .NET 6 y versiones posteriores.
Los atributos de visualización del depurador permiten al desarrollador del tipo, que especifica y mejor comprende el comportamiento del runtime de ese tipo, que especifique también el aspecto que tendrá ese tipo cuando se muestre en un depurador. Además, los atributos de visualización del depurador que proporcionan una propiedad Target
pueden aplicarse en el nivel de ensamblado por usuarios sin conocimiento del código fuente. El atributo DebuggerDisplayAttribute controla la forma en que se muestra un tipo o un miembro en las ventanas de variables del depurador. El atributo DebuggerBrowsableAttribute determina si un campo o propiedad se muestra en las ventanas de variables del depurador y cómo se muestra. El atributo DebuggerTypeProxyAttribute especifica un tipo sustitutivo, o un proxy, para un tipo y cambia la forma en que se muestra el tipo en las ventanas del depurador. Cuando se ve una variable que tiene un proxy, o un tipo sustitutivo, el proxy reemplaza al tipo original en la ventana de presentación del depurador. En la ventana de las variables del depurador se muestran sólo los miembros públicos del tipo de servidor proxy. No se muestran los miembros privados.
Usar el atributo DebuggerDisplayAttribute
El constructor DebuggerDisplayAttribute tiene un argumento único: una cadena que se va a mostrar en la columna de valor de las instancias del tipo. Esta cadena puede contener llaves ({ y }). El texto encerrado entre llaves se evalúa como una expresión. Por ejemplo, el código de C# siguiente hace que "Recuento = 4" se muestre cuando el signo más (+) está seleccionado para expandir la presentación del depurador para una instancia de MyHashtable
.
[DebuggerDisplay("Count = {count}")]
class MyHashtable
{
public int count = 4;
}
Los atributos que se aplican en las propiedades a las que se hace referencia en la expresión no se procesan. Para el compilador de C#, se permite una expresión general que solo tenga acceso implícito a esta referencia para la instancia actual del tipo de destino. La expresión está limitada; no tiene acceso a los alias, variables locales ni punteros. En el código de C#, puede usar una expresión general entre las llaves que tenga acceso implícito al puntero this
solo para la instancia actual del tipo de destino.
Por ejemplo, si un objeto de C# tiene un ToString()
invalidado, el depurador llamará a la invalidación y mostrará su resultado en lugar del {<typeName>}.
estándar. Por lo tanto, si ha invalidado ToString()
, no necesita usar DebuggerDisplayAttribute. Si utiliza ambos, el atributo DebuggerDisplayAttribute tiene prioridad sobre el reemplazo de ToString()
.
Usar el atributo DebuggerBrowsableAttribute
Aplique DebuggerBrowsableAttribute en un campo o propiedad para especificar cómo va a mostrarse el campo o la propiedad en la ventana del depurador. El constructor de este atributo toma uno de los valores de enumeración DebuggerBrowsableState, que especifica uno de los estados siguientes:
Never indica que el miembro no se muestra en la ventana de datos. Por ejemplo, con este valor para DebuggerBrowsableAttribute en un campo se quita el campo de la jerarquía; el campo no se muestra cuando expande el tipo envolvente haciendo clic en el signo más (+) para la instancia del tipo.
Collapsed indica que el miembro se muestra pero no se expande de manera predeterminada. Este es el comportamiento predeterminado.
RootHidden indica que el propio miembro no se muestra, pero sus objetos constituyentes se muestran si es una matriz o una colección.
Nota
DebuggerBrowsableAttribute no es compatible con Visual Basic en la versión 2.0 de .NET Framework.
En el siguiente ejemplo de código se muestra el uso de DebuggerBrowsableAttribute para evitar que la propiedad que lo sigue aparezca en la ventana del depurador para la clase.
[DebuggerBrowsable(DebuggerBrowsableState.Never)]
public static string y = "Test String";
Usar DebuggerTypeProxy
Use el atributo DebuggerTypeProxyAttribute cuando necesite realizar cambios significativos y fundamentales en la vista de depuración de un tipo, sin cambiar el propio tipo. El atributo DebuggerTypeProxyAttribute se usa para especificar un servidor proxy de presentación de un tipo y permitir a un desarrollador que ajuste la vista para el tipo. Este atributo, como DebuggerDisplayAttribute, también se puede usar en el nivel de ensamblado, en cuyo caso la propiedad Target especifica el tipo para el que se usará el servidor proxy. El uso recomendado es que este atributo especifique un tipo anidado privado que aparece dentro del tipo al que se aplica el atributo. Un evaluador de expresiones que admite los visores de tipo comprueba la existencia de este atributo cuando se muestra un tipo. Si se encuentra el atributo, el evaluador de expresiones sustituye el tipo de servidor proxy de presentación por el tipo al que se aplica el atributo.
Cuando está presente DebuggerTypeProxyAttribute, en la ventana de las variables del depurador se muestran solo los miembros públicos del tipo de servidor proxy. No se muestran los miembros privados. El comportamiento de la ventana de datos no lo modifican las vistas mejoradas por atributos.
Para evitar disminuciones innecesarias del rendimiento, los atributos del servidor proxy de presentación no se procesan hasta que el objeto esté expandido, ya sea a través del usuario que hace clic en el signo más (+) situado junto al tipo en una ventana de datos o a través de la aplicación del atributo DebuggerBrowsableAttribute. Por consiguiente, se recomienda no aplicar ningún atributo al tipo de presentación. Los atributos pueden y deben aplicarse en el cuerpo del tipo de presentación.
En el ejemplo de código siguiente se muestra el uso del atributo DebuggerTypeProxyAttribute para especificar un tipo que se va a usar como servidor proxy de presentación del 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;
}
}
}
Ejemplo
Descripción
El ejemplo de código siguiente puede verse en Visual Studio para ver los resultados de aplicar los atributos DebuggerDisplayAttribute, DebuggerBrowsableAttribute y 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