Condividi tramite


Miglioramento del debug tramite gli attributi di visualizzazione del debugger

Gli attributi di visualizzazione del debugger consentono allo sviluppatore del tipo, che meglio conosce il comportamento di tale tipo in fase di esecuzione, di specificare anche l'aspetto che il tipo dovrà avere quando verrà visualizzato in un debugger. Inoltre, gli attributi di visualizzazione che forniscono una proprietà Target possono essere applicati a livello di assembly da utenti che non hanno alcuna conoscenza del 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 una proprietà o un campo viene visualizzato nelle finestre delle variabili del debugger. L'attributo DebuggerTypeProxyAttribute specifica un tipo di sostituzione, ovvero un proxy, per un tipo e cambia la modalità di visualizzazione del tipo nelle finestre del debugger. Quando viene visualizzata una variabile a cui è associato un proxy, quest'ultimo sostituisce il tipo originale nella finestra di visualizzazione del debugger**.** Nella finestra delle variabili del debuggervengono visualizzati soltanto i membri pubblici del tipo proxy. I membri privati non vengono visualizzati.

Utilizzo dell'attributo DebuggerDisplayAttribute

Il costruttore DebuggerDisplayAttribute ha un unico argomento, ovvero una stringa da visualizzare nella colonna Valore per le istanze del tipo. Questa stringa può contenere parentesi graffe ({ e }). Il testo racchiuso tra parentesi graffe viene valutato come un'espressione. Ad esempio, il codice C# riportato di seguito fa sì che venga visualizzata la stringa "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 abbia soltanto accesso implicito a questo riferimento per l'istanza corrente del tipo di destinazione. L'espressione è limitata, ovvero non ha accesso ad alias, variabili locali o puntatori. Nel codice C# è possibile utilizzare un'espressione generale tra le parentesi graffe cha abbia soltanto accesso implicito al puntatore this per l'istanza corrente del tipo di destinazione.

Se ad esempio un oggetto C# possiede una funzione ToString() sottoposta a override, il debugger chiamerà l'override e ne visualizzerà il risultato anziché la stringa standard {<typeName>}. Pertanto, se è stato eseguito l'override di ToString(), non è necessario utilizzare DebuggerDisplayAttribute. Se si utilizzano entrambi, l'attributo DebuggerDisplayAttribute avrà la precedenza sull'override di ToString().

Utilizzo dell'attributo DebuggerBrowsableAttribute

Applicare l'attributo DebuggerBrowsableAttribute a un campo o una proprietà per specificare la modalità di visualizzazione del campo o della proprietà nella finestra del debugger. Il costruttore di questo attributo accetta uno dei valori di enumerazione DebuggerBrowsableState, che specifica uno dei seguenti stati:

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

  • Collapsed indica che i membri sono visualizzati ma non espansi per impostazione predefinita. Questo è il funzionamento predefinito.

  • RootHidden indica che il membro stesso non deve essere visualizzato, ma gli oggetti che lo costituiscono devono essere visualizzati se si tratta di una matrice o di un insieme.

NotaNota

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

Nell'esempio di codice riportato di seguito viene illustrato come utilizzare l'attributo DebuggerBrowsableAttribute per impedire che la proprietà che segue l'attributo venga visualizzata nella finestra di debug relativa alla classe.

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

Utilizzo dell'attributo DebuggerTypeProxy

Utilizzare l'attributo DebuggerTypeProxyAttribute quando è necessario modificare radicalmente la visualizzazione di un tipo nel debugger, senza modificare il tipo stesso. L'attributo DebuggerTypeProxyAttribute viene utilizzato per specificare un proxy di visualizzazione per un tipo, consentendo a uno sviluppatore di personalizzare la visualizzazione del tipo. Analogamente a DebuggerDisplayAttribute, questo attributo può essere utilizzato a livello di assembly, nel qual caso la proprietà Target specifica il tipo per il quale verrà utilizzato il proxy. Si consiglia di utilizzare questo attributo per specificare un tipo privato annidato che si trova all'interno del tipo al quale viene applicato l'attributo. Un analizzatore di espressioni che supporta i visualizzatori di tipi verifica la presenza di questo attributo al momento della visualizzazione di un tipo. Se l'attributo viene rilevato, l'analizzatore di espressioni sostituisce il tipo a cui è applicato l'attributo con il tipo proxy di visualizzazione.

Quando è presente DebuggerTypeProxyAttribute, nella finestra delle variabili del debugger vengono visualizzati solo i membri pubblici del tipo proxy. I membri privati non vengono visualizzati. Il comportamento della finestra dei dati rimane invariato.

Per evitare di ridurre inutilmente le prestazioni, gli attributi del proxy di visualizzazione non vengono elaborati fino a quando l'oggetto non viene espanso, mediante il clic dell'utente sul segno più (+) accanto al tipo in una finestra dei dati o mediante l'applicazione dell'attributo DebuggerBrowsableAttribute. Si consiglia pertanto di non applicare attributi al tipo di visualizzazione. ma di applicarli nel corpo del tipo visualizzato.

Nell'esempio di codice riportato di seguito viene illustrato come utilizzare l'attributo DebuggerTypeProxyAttribute per specificare un tipo da utilizzare come proxy di visualizzazione nel 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 riportato di seguito può essere eseguito in Visual Studio 2005 per vedere i risultati dell'applicazione degli attributi DebuggerDisplayAttribute, DebuggerBrowsableAttribute e DebuggerTypeProxyAttribute.

Codice

Imports System
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 'Main 
End Class 'DebugViewTest
<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 'New
End Class 'KeyValuePairs
<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 'New

        <DebuggerBrowsable(DebuggerBrowsableState.RootHidden)> _
        ReadOnly Property Keys as KeyValuePairs()
            Get
                Dim nkeys(hashtable.Count) 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 'HashtableDebugView
End Class 'MyHashtable
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;
            }
        }
    }
}

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();
}

Vedere anche

Riferimenti

DebuggerDisplayAttribute

DebuggerBrowsableAttribute

DebuggerTypeProxyAttribute