Информирование отладчика о том, что отображать, с помощью атрибута DebuggerDisplay (C#, Visual Basic, F#, C++/CLI)
DebuggerDisplayAttribute управляет тем, как объект, свойство или поле отображаются в окнах переменных отладчика. Этот атрибут можно применять к типам (классы, структуры, перечисления, делегаты). При применении к базовому типу атрибут также применяется к подклассу.
Атрибут DebuggerDisplay
имеет один аргумент, определяющий строку, которая должна отображаться в столбце "Значение" для экземпляров типа. Эта строка может содержать фигурные скобки ({
и }
). Текст, заключенный в фигурные скобки, вычисляется как поле, свойство или метод.
Если класс содержит переопределенный метод ToString()
, отладчик использует этот метод вместо значения по умолчанию {<typeName>}
. Таким образом, если имеется переопределенный метод ToString()
, отладчик использует его вместо стандартного варианта {<typeName>}
и вам не нужно использовать DebuggerDisplay
. Если используется и то и другое, то атрибут DebuggerDisplay
будет иметь более высокий приоритет по отношению к переопределенному методу ToString()
. Кроме того, атрибут DebuggerDisplay
будет иметь более высокий приоритет по отношению к переопределенному методу ToString()
в подклассе.
Будет ли отладчик выполнять неявный вызов метода ToString()
, зависит от заданных пользователем параметров в диалоговом окне "Сервис" > "Параметры" > "Отладка".
Важно!
Если в диалоговом окне "Сервис" > "Параметры" > "Отладка" установлен флажок Показывать базовую структуру объектов в окнах переменных, то атрибут DebuggerDisplay
игнорируется.
Примечание.
При использовании нативного кода этот атрибут поддерживается только в коде C++/CLI.
В следующей таблице показано несколько примеров возможного использования атрибута DebuggerDisplay
для вывода строк удобочитаемого вида.
Атрибут | Строка, выводимая в столбце "Значение" |
---|---|
[DebuggerDisplay("x = {x} y = {y}")] Применение для типа с полями x и y . |
x = 5 y = 18 |
[DebuggerDisplay("String value is {getString()}")] Синтаксис параметра может различаться в зависимости от языка. Будьте внимательны при его использовании. |
String value is [5, 6, 6] |
АтрибутDebuggerDisplay
также может принимать именованные параметры.
Параметры | Характер использования |
---|---|
Name , Type |
Эти параметры влияют на столбцы Имя и Тип окон переменных. (Для них также может быть задан вывод строк с использованием того же синтаксиса, что и для конструктора.) Злоупотребление этими параметрами или их неправильное использование может привести к выводу неудобочитаемых или недостоверных данных. |
Target , TargetTypeName |
Указывает конечный тип, когда атрибут используется на уровне сборки. |
Файл autoexp.cs использует атрибут DebuggerDisplay на уровне сборки. Файл autoexp.cs определяет расширения по умолчанию, которые Visual Studio использует для объектов .NET. В файле autoexp.cs можно просмотреть примеры использования атрибута DebuggerDisplay. При необходимости в файл autoexp.cs можно внести изменения и скомпилировать его, чтобы изменить расширения по умолчанию. Прежде чем изменять файл autoexp.cs, следует обязательно сделать его резервную копию.
Чтобы создать файл autoexp.cs, откройте командную строку разработчика для VS 2015 и выполните следующие команды.
cd <directory containing autoexp.cs>
csc /t:library autoexp.cs
Изменения в файле autoexp.dll будут учтены в следующем сеансе отладки.
Использование выражений в атрибуте DebuggerDisplay
Хотя в атрибуте DebuggerDisplay допускается использовать общее выражение внутри фигурных скобок, делать это не рекомендуется.
Общее выражение внутри атрибута DebuggerDisplay имеет неявный доступ к указателю this
только для текущего экземпляра конечного типа. Выражение не имеет доступа к псевдонимам, локальным переменным или указателям. Если выражение ссылается на свойства, то атрибуты для этих свойств не обрабатываются. Например, код C# [DebuggerDisplay("Object {count - 2}")]
отобразил бы Object 6
, если бы поле count
имело значение 8.
Использование выражений в атрибуте DebuggerDisplay может привести к следующим проблемам:
Вычисление выражений является наиболее ресурсоемкой операцией в отладчике, причем выражение вычисляется при каждом его отображении. Это может вызвать проблемы производительности при пошаговом выполнении кода. Например, сложное выражение, используемое для отображения значений в коллекции или в списке, при большом количестве элементов может вычисляться очень медленно.
Выражения вычисляются с помощью вычислителя выражений языка текущего кадра стека, а не языка, на котором было написано выражение. Это может привести к непредсказуемым результатам, если языки различаются.
Вычисление выражения может изменить состояние приложения. Например, выражение, которое задает значение свойства, изменяет значение свойства в выполняемом коде.
Одним из способов сократить возможные проблемы вычисления выражения является создание закрытого свойства, которое выполняет операцию и возвращает строку. Атрибут DebuggerDisplay сможет отобразить значение этого закрытого свойства. Ниже показан пример реализации этого способа.
[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);
}
}
}
Суффикс ",nq" указывает средству оценки выражений, что при отображении окончательного значения нужно удалить кавычки (nq = no quotes, без кавычек).
Пример
Следующий пример демонстрирует способ использования атрибута DebuggerDisplay
совместно с атрибутами DebuggerBrowsable
и DebuggerTypeProxy
. При работе данного кода в окна переменных отладчика, например в окно Контрольные значения , выводится расширенная информация, а именно:
Имя | Value | Тип |
---|---|---|
Ключ. | "три" | object {string} |
Значение | 3 | object {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;
}
}
}
}