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 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ć 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 wywołanie ToString()
, zależy od ustawienia użytkownika w oknie dialogowym Tools / Options / Debugging.
Ważny
Jeśli pole wyboru Pokaż nieprzetworzone struktury obiektów w oknach zmiennych jest zaznaczone w oknie dialogowym Narzędzia / Opcje / Debugowanie, atrybut DebuggerDisplay
jest ignorowany.
Notatka
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 | Cel |
---|---|
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ć domyślne rozszerzenia. Przed zmodyfikowaniem pliku autoexp.cs należy wykonać kopię zapasową.
Aby skompilować 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 autoexp.dll zostaną uwzględnione w następnej sesji debugowania.
Używanie wyrażeń w debugerzeDisplay
Chociaż można użyć ogólnego wyrażenia między nawiasami klamrowymi w atrybucie DebuggerDisplay, to nie jest to zalecana praktyka.
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:
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 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).
Przykład
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:
nazwa | wartość | typu |
---|---|---|
Klucz | "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;
}
}
}
}