Miglioramento del debug tramite gli attributi di visualizzazione del debugger

Gli attributi di visualizzazione del debugger consentono allo sviluppatore del tipo, che specifica e conosce al meglio il comportamento di runtime di tale tipo, di specificare anche quale sarà l'aspetto del tipo quando verrà visualizzato in un debugger. Gli attributi di visualizzazione del debugger che forniscono una proprietà Target possono essere applicati a livello di assembly dagli utenti anche senza conoscere il codice sorgente. L'attributo DebuggerDisplayAttribute controlla la modalità di visualizzazione di un tipo o di un membro nelle finestre delle variabili del debugger. L'attributo DebuggerBrowsableAttribute determina se e come un campo o una proprietà viene visualizzata nelle finestre delle variabili del debugger. L'attributo DebuggerTypeProxyAttribute specifica un tipo sostituito, o proxy, per un tipo e modifica il modo in cui il tipo viene visualizzato nelle finestre del debugger. Quando si visualizza una variabile che ha un proxy, o tipo sostituito, il proxy prende il posto del tipo originale nella finestra di visualizzazione del debugger. Nella finestra delle variabili del debugger vengono visualizzati soltanto i membri pubblici del tipo proxy. I membri privati non vengono visualizzati.

Uso di DebuggerDisplayAttribute

Il costruttore DebuggerDisplayAttribute presenta un solo argomento: una stringa da visualizzare nella colonna del valore per le istanze del tipo. Questa stringa può contenere parentesi graffe ({ e }). Il testo racchiuso tra due parentesi graffe viene valutato come espressione. Il codice C# seguente, ad esempio, visualizza "Count = 4" quando viene selezionato il segno più (+) per espandere la visualizzazione del debugger per un'istanza di MyHashtable.

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

Gli attributi applicati alle proprietà a cui si fa riferimento nell'espressione non vengono elaborati. Per il compilatore C#, è consentita un'espressione generale che ha solo l'accesso implicito a questo riferimento per l'istanza corrente del tipo di destinazione. L'espressione è limitata e non ha accesso ad alias, variabili locali o puntatori. Nel codice C# è possibile usare un'espressione generale tra parentesi graffe che ha accesso implicito al puntatore this solo per l'istanza corrente del tipo di destinazione.

Se ad esempio un oggetto C# ha un metodo ToString() sottoposto a override, il debugger chiamerà l'override e ne visualizzerà il risultato, invece di chiamare il codice {<typeName>}. standard. Se quindi si è eseguito l'override di ToString(), non è necessario usare DebuggerDisplayAttribute. Se si utilizzano entrambi, l'attributo DebuggerDisplayAttribute avrà la precedenza sull'override di ToString().

Uso di DebuggerBrowsableAttribute

Applicare DebuggerBrowsableAttribute a un campo o una proprietà per specificare come il campo o la proprietà deve essere visualizzata nella finestra del debugger. Il costruttore di questo attributo accetta uno dei valori di enumerazione DebuggerBrowsableState, che specifica uno degli stati seguenti:

  • Never indica che il membro non viene visualizzato nella finestra dei dati. Ad esempio, usando questo valore per DebuggerBrowsableAttribute in un campo, il campo viene rimosso dalla gerarchia. Il campo non viene visualizzato quando si espande il tipo di inclusione facendo clic sul segno più (+) per l'istanza del tipo.

  • Collapsed indica che il membro viene visualizzato, ma non espanso per impostazione predefinita. Si tratta del comportamento predefinito.

  • RootHidden indica che non viene visualizzato il membro in sé, ma gli oggetti costituenti se è presente una matrice o una raccolta.

Nota

DebuggerBrowsableAttribute non è supportato da Visual Basic in .NET Framework versione 2.0.

L'esempio di codice seguente illustra l'uso di DebuggerBrowsableAttribute per impedire la visualizzazione della proprietà che lo segue nella finestra di debug della classe.

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

Uso di DebuggerTypeProxy

Usare l'attributo DebuggerTypeProxyAttribute quando è necessario modificare in modo significativo e sostanziale la visualizzazione di debug di un tipo, ma non il tipo in sé. L'attributo DebuggerTypeProxyAttribute viene usato per specificare un proxy di visualizzazione per un tipo, consentendo a uno sviluppatore di personalizzare la vista per il tipo. Questo attributo, come DebuggerDisplayAttribute, può essere usato a livello di assembly, nel qual caso la proprietà Target specifica il tipo per cui il proxy verrà usato. L'utilizzo consigliato prevede che questo attributo specifichi un tipo annidato privato che viene generato nel tipo a cui l'attributo viene applicato. Un analizzatore di espressioni che supporta i visualizzatori di tipo cerca questo attributo quando viene visualizzato un tipo. Se l'attributo viene trovato, l'analizzatore di espressioni sostituisce il tipo proxy di visualizzazione per il tipo a cui l'attributo viene applicato.

Quando DebuggerTypeProxyAttribute è presente, nella finestra delle variabili del debugger vengono visualizzati soltanto i membri pubblici del tipo proxy. I membri privati non vengono visualizzati. Il comportamento della finestra dei dati non viene modificato dalle visualizzazioni avanzate dell'attributo.

Per evitare inutili conseguenze per le prestazioni, gli attributi del proxy di visualizzazione vengono elaborati solo dopo che l'oggetto viene espanso, tramite il clic da parte dell'utente sul segno più (+) accanto al tipo in una finestra di dati o tramite l'applicazione dell'attributo DebuggerBrowsableAttribute. È quindi consigliabile non applicare attributi al tipo visualizzato. È possibile e consigliabile applicare gli attributi nel corpo del tipo visualizzato.

L'esempio di codice seguente illustra l'uso di DebuggerTypeProxyAttribute per specificare un tipo da usare come proxy di visualizzazione del debugger.

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

Esempio

Descrizione

L'esempio di codice seguente può essere visualizzato in Visual Studio per verificare i risultati dell'applicazione degli attributi DebuggerDisplayAttribute, DebuggerBrowsableAttribute e DebuggerTypeProxyAttribute.

Codice


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

Vedi anche