Amélioration du débogage avec les attributs d'affichage de débogueur

Remarque

Cet article est spécifique au .NET Framework. Elle n’est pas applicable aux implémentations plus récentes de .NET, notamment .NET 6 et les versions ultérieures.

Les attributs d’affichage de débogueur permettent au développeur du type, qui spécifie et appréhende le mieux le comportement d’exécution de ce type, de spécifier également ce à quoi le type ressemblera une fois affiché dans un débogueur. De plus, les attributs d’affichage de débogueur qui fournissent une propriété Target peuvent être appliqués au niveau de l’assembly par des utilisateurs sans qu’il leur soit nécessaire de connaître le code source. L’attribut DebuggerDisplayAttribute contrôle la façon dont un type ou un membre s’affiche dans les fenêtres de variables du débogueur. L’attribut DebuggerBrowsableAttribute contrôle si une classe ou un champ s’affiche dans les fenêtres de variables du débogueur et comment ils s’affichent. L’attribut DebuggerTypeProxyAttribute spécifie un type de substitution (ou un proxy) pour un type, et modifie le mode d’affichage de ce type dans les fenêtres de débogage. Quand vous visualisez une variable ayant un proxy, ou un type de substitution, le proxy remplace le type d’origine dans la fenêtre d’affichage du débogage. La fenêtre de variables du débogueur n'affiche que les membres publics du type du proxy. Les membres privés ne sont pas affichés.

Utilisation de DebuggerDisplayAttribute

Le constructeur DebuggerDisplayAttribute n’a qu’un seul argument : une chaîne à afficher dans la colonne valeur pour les instances du type. Cette chaîne peut contenir des accolades ({ et }). Le texte entre accolades est évalué comme une expression. Par exemple, le code C# suivant entraîne l’affichage de « Count = 4 » quand le signe plus (+) est sélectionné pour développer l’affichage de débogueur pour une instance de MyHashtable.

[DebuggerDisplay("Count = {count}")]
class MyHashtable
{
    public int count = 4;
}

Les attributs appliqués aux propriétés référencées dans l’expression ne sont pas traités. Pour le compilateur C#, une expression générale est autorisée si elle n’a qu’un accès implicite à cette référence pour l’instance actuelle du type cible. L’expression est limitée ; il n’existe aucun accès aux alias, aux variables locales ou aux pointeurs. En code C#, vous pouvez utiliser une expression générale entre accolades qui dispose d’un accès implicite au pointeur this uniquement pour l’instance actuelle du type cible.

Par exemple, si un objet C# a un ToString() substitué, le débogueur appelle la substitution et affiche son résultat au lieu du {<typeName>}. standard. Ainsi, si vous avez substitué ToString(), vous n’avez pas besoin d’utiliser DebuggerDisplayAttribute. Si vous utilisez les deux, l'attribut DebuggerDisplayAttribute prend la priorité sur la substitution de ToString().

Utilisation de DebuggerBrowsableAttribute

Appliquez l’attribut DebuggerBrowsableAttribute à un champ ou une propriété pour spécifier comment il ou elle doit être affiché(e) dans la fenêtre du débogueur. Le constructeur pour cet attribut prend l’une des valeurs d’énumération DebuggerBrowsableState, qui spécifie l’un des états suivants :

  • Never indique que le membre n’est pas affiché dans la fenêtre de données. Par exemple, l’utilisation de cette valeur pour DebuggerBrowsableAttribute sur un champ supprime le champ de la hiérarchie ; le champ n’est pas affiché quand vous développez le type englobant en cliquant sur le signe plus (+) pour l’instance de type.

  • Collapsed indique que le membre est affiché mais n’est pas développé par défaut. Il s'agit du comportement par défaut.

  • RootHidden indique que le membre proprement dit n’est pas affiché, mais ses objets constituants sont affichés s’il s’agit d’un tableau ou d’une collection.

Notes

DebuggerBrowsableAttribute n’est pas pris en charge par Visual Basic dans la version 2.0 de .NET Framework.

L’exemple de code suivant illustre l’utilisation du DebuggerBrowsableAttribute pour empêcher la propriété qui le suit d’apparaître dans la fenêtre de débogage pour la classe.

[DebuggerBrowsable(DebuggerBrowsableState.Never)]
public static string y = "Test String";

Utilisation de DebuggerTypeProxy

Utilisez l’attribut DebuggerTypeProxyAttribute quand vous avez besoin de modifier radicalement l’affichage de débogage d’un type, sans toutefois changer le type proprement dit. L’attribut DebuggerTypeProxyAttribute sert à spécifier un proxy d’affichage pour un type, ce qui permet à un développeur de personnaliser l’affichage en fonction du type. Cet attribut, à l’instar de DebuggerDisplayAttribute, peut être utilisé au niveau de l’assembly, auquel cas la propriété Target spécifie le type pour lequel le proxy sera utilisé. Il est généralement recommandé que cet attribut spécifie un type imbriqué privé qui se produit dans le type auquel l’attribut est appliqué. Un évaluateur d’expression qui prend en charge les visionneuses de type vérifie la présence de cet attribut lors de l’affichage d’un type. Si l’attribut est trouvé, l’évaluateur d’expression substitue le type du proxy d’affichage pour le type auquel l’attribut est appliqué.

Quand DebuggerTypeProxyAttribute est présent, la fenêtre des variables du débogueur affiche uniquement les membres publics du type de proxy. Les membres privés ne sont pas affichés. Le comportement de la fenêtre de données n’est pas modifié par les affichages améliorés par les attributs.

Pour éviter une dégradation inutile des performances, les attributs du proxy d’affichage ne sont pas traités tant que l’objet n’est pas développé, soit par un clic de l’utilisateur sur le signe plus (+) en regard du type dans une fenêtre de données, soit par le biais de l’application de l’attribut DebuggerBrowsableAttribute. Ainsi, nous vous recommandons de n’appliquer aucun attribut au type d’affichage. Les attributs peuvent et doivent être appliqués dans le corps du type d’affichage.

L’exemple de code suivant montre comment utiliser DebuggerTypeProxyAttribute pour spécifier un type à utiliser en tant que proxy de l’affichage du débogueur.

[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;
        }
    }
}

Exemple

Description

Vous pouvez afficher l’exemple de code suivant dans Visual Studio pour observer les résultats de l’application des attributs DebuggerDisplayAttribute, DebuggerBrowsableAttribute et DebuggerTypeProxyAttribute.

Code


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

Voir aussi