Indiquez au débogueur ce qu’il doit afficher à l’aide de l’attribut DebuggerDisplay (C#, Visual Basic, F#, C++/CLI)
Le DebuggerDisplayAttribute contrôle l’affichage d’un objet, d’une propriété ou d’un champ dans les fenêtres de variables du débogueur. Cet attribut peut être appliqué aux types (classes, structs, énumérations, délégués), mais est généralement appliqué uniquement aux classes et aux structs. S’il est appliqué à un type de base, l’attribut s’applique également à une sous-classe.
L’attribut DebuggerDisplay
a un seul argument, qui est une chaîne à afficher dans la colonne valeur pour les instances du type. Cette chaîne peut contenir des accolades ({
et }
). Le texte dans une paire d’accolades est évalué comme un champ, une propriété ou une méthode.
Si une classe a une méthode ToString()
substituée, le débogueur utilise la méthode substituée au lieu du {<typeName>}
par défaut. Par conséquent, si vous avez remplacé la méthode ToString()
, le débogueur utilise la méthode substituée au lieu du {<typeName>}
par défaut, et vous n’avez pas besoin d’utiliser DebuggerDisplay
. Si vous utilisez les deux, l’attribut DebuggerDisplay
est prioritaire sur la méthode ToString()
substituée. L’attribut DebuggerDisplay
est également prioritaire sur la méthode ToString()
substituée dans une sous-classe.
L’évaluation par le débogueur de cet appel ToString()
implicite dépend d’un paramètre utilisateur dans la boîte de dialogue Outils / Options / Débogage.
Important
Si la case à cocher Afficher la structure brute des objets dans les fenêtres variables est cochée dans la boîte de dialogue Outils / Options / Débogage, l’attribut DebuggerDisplay
est ignoré.
Note
Pour le code natif, cet attribut est pris en charge uniquement dans le code C++/CLI.
Le tableau suivant montre certaines utilisations possibles de l’attribut DebuggerDisplay
et des exemples de sorties.
Attribut | Sortie apparaissant dans la colonne Valeur |
---|---|
[DebuggerDisplay("x = {x} y = {y}")] Utilisé sur un type avec des champs x et y . |
x = 5 y = 18 |
[DebuggerDisplay("String value is {getString()}")] syntaxe de paramètre peut varier d’un langage à l’autre. Par conséquent, utilisez-le avec soin. |
String value is [5, 6, 6] |
DebuggerDisplay
peut également accepter des paramètres nommés.
Paramètres | Objectif |
---|---|
Name , Type |
Ces paramètres affectent les colonnes Name et Type des fenêtres variables. (Ils peuvent être définis sur des chaînes à l’aide de la même syntaxe que le constructeur.) L’utilisation excessive de ces paramètres ou leur utilisation incorrecte peut entraîner une sortie déroutante. |
Target , TargetTypeName |
Spécifie le type cible lorsque l’attribut est utilisé au niveau de l’assembly. |
Le fichier autoexp.cs utilise l’attribut DebuggerDisplay au niveau de l’assembly. Le fichier autoexp.cs détermine les expansions par défaut que Visual Studio utilise pour les objets .NET. Vous pouvez examiner le fichier autoexp.cs pour obtenir des exemples d’utilisation de l’attribut DebuggerDisplay, ou vous pouvez modifier et compiler le fichier autoexp.cs pour modifier les extensions par défaut. Veillez à sauvegarder le fichier autoexp.cs avant de le modifier.
Pour générer autoexp.cs, ouvrez une Invite de commandes développeur pour VS2015, puis exécutez les commandes suivantes
cd <directory containing autoexp.cs>
csc /t:library autoexp.cs
Les modifications apportées à autoexp.dll seront prises en compte lors de la prochaine session de débogage.
Utilisation d’expressions dans DebuggerDisplay
Bien que vous puissiez utiliser une expression générale à l'intérieur de l'accolade dans un attribut DebuggerDisplay, cette méthode n'est pas recommandée.
Une expression générale dans DebuggerDisplay a un accès implicite au pointeur this
pour l’instance actuelle du type cible uniquement. L'expression n'a accès ni aux alias, ni aux variables locales, ni aux pointeurs. Si l’expression fait référence à des propriétés, les attributs de ces propriétés ne sont pas traités. Par exemple, le code C# [DebuggerDisplay("Object {count - 2}")]
affiche Object 6
si le champ count
était 8.
L’utilisation d’expressions dans DebuggerDisplay peut entraîner les problèmes suivants :
L’évaluation des expressions est l’opération la plus coûteuse dans le débogueur et l’expression est évaluée chaque fois qu’elle est affichée. Cela peut entraîner des problèmes de performances lors de l’exécution pas à pas du code. Par exemple, une expression complexe utilisée pour afficher les valeurs d’une collection ou d’une liste peut être très lente lorsque le nombre d’éléments est volumineux.
Les expressions sont évaluées par l'évaluateur d'expression du langage du cadre de pile actuel et pas par l'évaluateur du langage dans lequel l'expression a été écrite. Cela peut entraîner des résultats imprévisibles lorsque les langues sont différentes.
L’évaluation d’une expression peut modifier l’état de l’application. Par exemple, une expression qui définit la valeur d’une propriété mute la valeur de propriété dans le code en cours d’exécution.
Une façon de réduire les problèmes possibles d’évaluation d’expression consiste à créer une propriété privée qui effectue l’opération et retourne une chaîne. L’attribut DebuggerDisplay peut ensuite afficher la valeur de cette propriété privée. L’exemple suivant implémente ce modèle :
[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);
}
}
}
Le suffixe « ,nq » indique à l’évaluateur d’expression de supprimer les guillemets lors de l’affichage de la valeur finale (nq = pas de guillemets).
Exemple
L’exemple de code suivant montre comment utiliser DebuggerDisplay
, avec DebuggerBrowsable
et DebuggerTypeProxy
. Lorsqu'il s'affiche dans une fenêtre de variables du débogueur, comme la fenêtre Espion, il produit une expansion de ce genre :
Nom | Valeur | Type |
---|---|---|
Touche | « trois » | objet {string} |
Valeur | 3 | objet {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;
}
}
}
}