Udostępnij za pomocą


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

DebuggerDisplayAttribute określa sposób wyświetlania obiektu, właściwości lub pola w oknach zmiennych debugera. Ten atrybut można stosować do typów (klas, struktur, wyliczeń, delegatów), ale zazwyczaj stosuje się go tylko do klas i struktur. 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 ({ i }). Tekst w ramach pary nawiasów klamrowych jest oceniany jako pole, właściwość lub metoda.

Jeśli klasa ma zastąpioną metodę ToString(), debuger używa metody zastąpionej zamiast domyślnej {<typeName>}. W takim przypadku nie musisz używać DebuggerDisplay. Jeśli używasz obu tych metod, atrybut DebuggerDisplay ma pierwszeństwo przed zastąpioną metodą ToString(). Atrybut DebuggerDisplay ma również pierwszeństwo przed zastąpioną metodą ToString() w podklasie.

To, czy debuger ocenia to niejawne ToString() wywołanie, zależy od opcji Debugowanie w obszarze Narzędzia (lub Debug) >Opcje.

Important

Jeśli wybierzesz opcję Pokaż nieprzetworzone struktury obiektów w oknach zmiennych , DebuggerDisplay atrybut zostanie zignorowany. To ustawienie znajduje się w okienku Narzędzia>Opcje w sekcji Wszystkie ustawienia>Debugowanie>ogólne .

Important

Jeśli wybierzesz opcję Pokaż nieprzetworzone struktury obiektów w oknach zmiennych , DebuggerDisplay atrybut zostanie zignorowany. To ustawienie znajduje się w oknie dialogowym Opcje narzędzi> w sekcji Ogólne debugowania>.

Note

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.

Attribute 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.

Parameters Purpose
Name, Type Te parametry mają wpływ na kolumny Name i Type 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ć rozszerzenia domyślne. Pamiętaj, aby utworzyć kopię zapasową pliku autoexp.cs przed jego zmodyfikowaniem.

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

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

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

Używanie wyrażeń w debugerzeDisplay

Mimo że 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 DebuggerDisplay ma niejawny dostęp do wskaźnika this wyłącznie dla bieżącego wystąpienia typu docelowego. Wyrażenie nie ma dostępu do aliasów, zmiennych 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 języka C# [DebuggerDisplay("Object {count - 2}")] wyświetli Object 6, jeśli pole count było 8.

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

  • Obliczanie wyrażeń jest najdroższą operacją w debugerze, a wyrażenie jest obliczane za każdym razem, gdy jest wyświetlane. 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 oceniacz wyrażeń języka bieżącej ramki stosu, a nie przez oceniacz języka, w którym zostało napisane wyrażenie. Może to spowodować nieprzewidywalne wyniki, gdy języki są różne.

  • Ewaluacja wyrażenia może zmienić stan aplikacji. Na przykład wyrażenie, które ustawia wartość właściwości, zmienia wartość tej właściwości podczas wykonywania kodu.

    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). Aby uzyskać więcej informacji na temat formatatorów, zobacz Specyfikatory formatu w języku C#.

Example

Poniższy przykład kodu pokazuje, jak używać DebuggerDisplay, wraz z DebuggerBrowsable i DebuggerTypeProxy. Po wyświetleniu w oknie zmiennych debugera, na przykład w oknie Watch, generowane jest rozwinięcie, które wygląda następująco:

Name Value Type
Key "three" obiekt {string}
Value 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;
            }
        }
    }
}