Condividi tramite


Indicare al debugger cosa mostrare usando l'attributo DebuggerDisplay (C#, Visual Basic, F#, C++/CLI)

Il DebuggerDisplayAttribute controlla la modalità di visualizzazione di un oggetto, una proprietà o un campo nelle finestre delle variabili del debugger. Questo attributo può essere applicato ai tipi (classi, struct, enumerazioni, delegati), ma in genere viene applicato solo a classi e struct. Se applicato a un tipo di base, l'attributo si applica anche a una sottoclasse.

L'attributo DebuggerDisplay ha un singolo argomento, ovvero una stringa da visualizzare nella colonna valore per le istanze del tipo. Questa stringa può contenere parentesi graffe ({ e }). Il testo all'interno di una coppia di parentesi graffe viene valutato come campo, proprietà o metodo.

Se una classe ha un metodo di ToString() sottoposto a override, il debugger usa il metodo sottoposto a override anziché il {<typeName>}predefinito . In questo caso non è necessario usare DebuggerDisplay. Se si usano entrambi, l'attributo DebuggerDisplay ha la precedenza sul metodo di ToString() sottoposto a override. L'attributo DebuggerDisplay ha anche la precedenza sul metodo ToString() sottoposto a override in una sottoclasse.

Se il debugger valuta questa chiamata implicita ToString() dipende da un'opzione di debug in Strumenti (o Opzioni debug). >

Important

Se si seleziona l'opzione Mostra struttura non elaborata di oggetti nelle finestre variabili , l'attributo DebuggerDisplay viene ignorato. Questa impostazione si trova nel riquadro Opzioni strumenti> della sezione Tutte le impostazioni>>.

Important

Se si seleziona l'opzione Mostra struttura non elaborata di oggetti nelle finestre variabili , l'attributo DebuggerDisplay viene ignorato. Questa impostazione si trova nella finestra di dialogo Opzioni strumenti> della sezione Debug>.

Note

Per il codice nativo, questo attributo è supportato solo nel codice C++/CLI.

Nella tabella seguente vengono illustrati alcuni possibili usi dell'attributo DebuggerDisplay e degli output di esempio.

Attribute Output visualizzato nella colonna dei valori
[DebuggerDisplay("x = {x} y = {y}")]

Utilizzato in un tipo con campi x e y.
x = 5 y = 18
[DebuggerDisplay("String value is {getString()}")]sintassi dei parametri può variare tra i linguaggi. Pertanto, usalo con cautela. String value is [5, 6, 6]

DebuggerDisplay può anche accettare parametri denominati.

Parameters Purpose
Name, Type Questi parametri influiscono sulle colonne Nome e Tipo delle finestre delle variabili. Possono essere impostati su stringhe usando la stessa sintassi del costruttore. L'utilizzo eccessivo di questi parametri, o l'uso non corretto, può causare un output confuso.
Target, TargetTypeName Specifica il tipo di destinazione quando l'attributo viene usato a livello di assembly.

Il file autoexp.cs usa l'attributo DebuggerDisplay a livello di assembly. Il file autoexp.cs determina le espansioni predefinite usate da Visual Studio per gli oggetti .NET. È possibile esaminare il file autoexp.cs per esempi di come usare l'attributo DebuggerDisplay oppure modificare e compilare il file autoexp.cs per modificare le espansioni predefinite. Assicurarsi di eseguire il backup del file di autoexp.cs prima di modificarlo.

Per compilare autoexp.cs, aprire un prompt dei comandi per gli sviluppatori per VS2015 ed eseguire i comandi seguenti

cd <directory containing autoexp.cs>
csc /t:library autoexp.cs

Le modifiche apportate a autoexp.dll verranno prelevate nella sessione di debug successiva.

Uso di espressioni in DebuggerDisplay

Sebbene sia possibile usare un'espressione generale tra parentesi graffe in un attributo DebuggerDisplay, questa procedura non è consigliata.

Un'espressione generale in DebuggerDisplay ha accesso implicito al puntatore this solo per l'istanza attuale del tipo di destinazione. L'espressione non ha accesso ad alias, variabili locali o puntatori. Se l'espressione fa riferimento alle proprietà, gli attributi di tali proprietà non vengono elaborati. Ad esempio, il codice C# [DebuggerDisplay("Object {count - 2}")] verrebbe visualizzato come Object 6 se il campo count fosse 8.

L'uso delle espressioni in DebuggerDisplay può causare i problemi seguenti:

  • La valutazione delle espressioni è l'operazione più costosa nel debugger e l'espressione viene valutata ogni volta che viene visualizzata. Ciò può causare problemi di prestazioni durante l'esecuzione del codice. Ad esempio, un'espressione complessa usata per visualizzare i valori in una raccolta o un elenco può essere molto lenta quando il numero di elementi è elevato.

  • Le espressioni vengono valutate dall'analizzatore di espressioni del linguaggio del frame dello stack corrente e non dall'analizzatore del linguaggio in cui è stata scritta l'espressione. Ciò può causare risultati imprevedibili quando le lingue sono diverse.

  • La valutazione di un'espressione può modificare lo stato dell'applicazione. Ad esempio, un'espressione che imposta il valore di una proprietà modifica il valore della proprietà nel codice in esecuzione.

    Un modo per ridurre i possibili problemi di valutazione delle espressioni consiste nel creare una proprietà privata che esegue l'operazione e restituisce una stringa. L'attributo DebuggerDisplay può quindi visualizzare il valore di tale proprietà privata. L'esempio seguente implementa questo modello:

[DebuggerDisplay("{DebuggerDisplay,nq}")]
public sealed class MyClass
{
    public int count { get; set; }
    public bool flag { get; set; }
    private string DebuggerDisplay
    {
        get
        {
            return string.Format("Object {0}", count - 2);
        }
    }
}

Il suffisso "nq" indica all'analizzatore di espressioni di rimuovere le virgolette quando viene visualizzato il valore finale (nq = senza virgolette). Per altre informazioni sui formattatori, vedere Identificatori di formato in C#.

Example

Nell'esempio di codice seguente viene illustrato come usare DebuggerDisplay, insieme a DebuggerBrowsable e DebuggerTypeProxy. Quando viene visualizzato in una finestra delle variabili del debugger, ad esempio la finestra di controllo del Watch, genera un'espansione simile alla seguente:

Name Value Type
Key "three" oggetto {string}
Value 3 oggetto {int}
[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;
    }

    public object Key
    {
        get { return key; }
        set
        {
            object tempValue = dictionary[key];
            dictionary.Remove(key);
            key = value;
            dictionary.Add(key, tempValue);
        }
    }

    public object Value
    {
        get { return this.value; }
        set
        {
            this.value = value;
            dictionary[key] = this.value;
        }
    }
}

[DebuggerDisplay("{DebuggerDisplay,nq}")]
[DebuggerTypeProxy(typeof(HashtableDebugView))]
class MyHashtable
{
    public Hashtable hashtable;

    public MyHashtable()
    {
        hashtable = new Hashtable();
    }

    private string DebuggerDisplay { get { return "Count = " + hashtable.Count; } }

    private class HashtableDebugView
    {
        private MyHashtable myhashtable;
        public HashtableDebugView(MyHashtable myhashtable)
        {
            this.myhashtable = myhashtable;
        }

        [DebuggerBrowsable(DebuggerBrowsableState.RootHidden)]
        public KeyValuePairs[] Keys
        {
            get
            {
                KeyValuePairs[] keys = new KeyValuePairs[myhashtable.hashtable.Count];

                int i = 0;
                foreach (object key in myhashtable.hashtable.Keys)
                {
                    keys[i] = new KeyValuePairs(myhashtable.hashtable, key, myhashtable.hashtable[key]);
                    i++;
                }
                return keys;
            }
        }
    }
}