Poinformuj debugera, co pokazać przy użyciu atrybutu DebuggerDisplay (C#, Visual Basic, F#, C++/CLI)

Kontrolka DebuggerDisplayAttribute sposobu wyświetlania obiektu, właściwości lub pola w oknach zmiennych debugera. Ten atrybut można stosować do typów (klas, struktur, wyliczenia, delegatów). W przypadku zastosowania do typu podstawowego atrybut ma również zastosowanie do podklasy.

Atrybut DebuggerDisplay ma jeden argument, który jest ciągiem, który ma być wyświetlany w kolumnie wartości dla wystąpień typu. Ten ciąg może zawierać nawiasy klamrowe ({ i }). Tekst w ramach pary nawiasów klamrowych jest oceniany jako pole, właściwość lub metoda.

Jeśli klasa ma przesłoniętą ToString() metodę, debuger używa metody zastąpionej zamiast domyślnej {<typeName>}metody . W związku z tym, jeśli zastąpisz metodę ToString() , debuger używa metody zastąpionej zamiast domyślnej {<typeName>}, i nie trzeba używać metody DebuggerDisplay. Jeśli używasz obu tych metod, DebuggerDisplay atrybut ma pierwszeństwo przed przesłoniętą ToString() metodą. Atrybut DebuggerDisplay ma również pierwszeństwo przed przesłoniętą ToString() metodą w podklasie.

To, czy debuger ocenia to niejawne ToString() wywołanie, zależy od ustawienia użytkownika w oknie dialogowym Narzędzia / Opcje / Debugowanie .

Ważne

Jeśli pole wyboru Pokaż nieprzetworzone strukturę obiektów w oknach zmiennych jest zaznaczone w oknie dialogowym Narzędzia / Opcje / Debugowanie , DebuggerDisplay atrybut jest ignorowany.

Uwaga

W przypadku kodu natywnego ten atrybut jest obsługiwany tylko w kodzie C++/CLI.

W poniższej tabeli przedstawiono niektóre możliwe zastosowania atrybutu DebuggerDisplay i przykładowych danych wyjściowych.

Atrybut Dane wyjściowe wyświetlane w kolumnie Wartość
[DebuggerDisplay("x = {x} y = {y}")]

Używany w typie z polami x i y.
x = 5 y = 18
[DebuggerDisplay("String value is {getString()}")]Składnia parametrów może się różnić w zależności od języków. W związku z tym należy używać go z ostrożnością. String value is [5, 6, 6]

DebuggerDisplay może również akceptować nazwane parametry.

Parametry Purpose
Name, Type Te parametry mają wpływ na kolumny Name (Nazwa ) i Type (Typ ) okien zmiennych. (Można je ustawić na ciągi przy użyciu tej samej składni co konstruktor). Nadmierne użycie tych parametrów lub nieprawidłowe użycie ich może spowodować mylące dane wyjściowe.
Target, TargetTypeName Określa typ docelowy, gdy atrybut jest używany na poziomie zestawu.

Plik autoexp.cs używa atrybutu DebuggerDisplay na poziomie zestawu. Plik autoexp.cs określa domyślne rozszerzenia używane przez program Visual Studio dla obiektów platformy .NET. Możesz sprawdzić plik autoexp.cs, aby zapoznać się z przykładami używania atrybutu DebuggerDisplay lub zmodyfikować i skompilować plik autoexp.cs, aby zmienić domyślne rozszerzenia. Pamiętaj, aby utworzyć kopię zapasową pliku autoexp.cs przed jego zmodyfikowaniem.

Aby skompilować plik autoexp.cs, otwórz wiersz polecenia dla deweloperów dla programu VS2015 i uruchom następujące polecenia

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

Zmiany w pliku autoexp.dll zostaną pobrane w następnej sesji debugowania.

Używanie wyrażeń w debugerzeDisplay

Chociaż można użyć wyrażenia ogólnego między nawiasami klamrowymi w atrybucie DebuggerDisplay, ta praktyka nie jest zalecana.

Wyrażenie ogólne w debugerzeDisplay ma niejawny dostęp do this wskaźnika tylko dla bieżącego wystąpienia typu docelowego. Wyrażenie nie ma dostępu do aliasów, ustawień lokalnych ani wskaźników. Jeśli wyrażenie odwołuje się do właściwości, atrybuty tych właściwości nie są przetwarzane. Na przykład kod [DebuggerDisplay("Object {count - 2}")] języka C# będzie wyświetlany Object 6 , jeśli pole count miało wartość 8.

Używanie wyrażeń w debugerzeDisplay może prowadzić do następujących problemów:

  • Ocenianie wyrażeń jest najdroższą operacją w debugerze, a wyrażenie jest obliczane za każdym razem, gdy jest wyświetlana. Może to spowodować problemy z wydajnością podczas przechodzenia przez kod. Na przykład wyrażenie złożone używane do wyświetlania wartości w kolekcji lub na liście może być bardzo powolne, gdy liczba elementów jest duża.

  • Wyrażenia są oceniane przez ewaluatora wyrażeń języka bieżącej ramki stosu, a nie przez ewaluatora języka, w którym zostało napisane wyrażenie. Może to spowodować nieprzewidywalne wyniki, gdy języki są różne.

  • Obliczanie wyrażenia może zmienić stan aplikacji. Na przykład wyrażenie, które ustawia wartość właściwości wycisza wartość właściwości w kodzie wykonywania.

    Jednym ze sposobów zmniejszenia możliwych problemów z oceną wyrażenia jest utworzenie właściwości prywatnej, która wykonuje operację i zwraca ciąg. Atrybut DebuggerDisplay może następnie wyświetlić wartość tej właściwości prywatnej. Poniższy przykład implementuje ten wzorzec:

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

Sufiks ",nq" informuje ewaluatora wyrażeń o usunięciu cudzysłowów podczas wyświetlania wartości końcowej (nq = brak cudzysłowów).

Przykład

W poniższym przykładzie kodu pokazano, jak używać elementów DebuggerDisplay, wraz z elementami DebuggerBrowsable i DebuggerTypeProxy. Po wyświetleniu w oknie zmiennych debugera, takim jak okno Obserwowanie , tworzy rozszerzenie, które wygląda następująco:

Nazwa/nazwisko Wartość Type
Key "trzy" obiekt {string}
Wartość 3 obiekt {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;
            }
        }
    }
}