Прочитать на английском

Поделиться через


Сообщите отладчику, что нужно показать с помощью атрибута 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, откройте командную строку разработчика для VS2015 и выполните следующие команды.

Командная строка Windows
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 может отобразить значение этого частного свойства. В следующем примере реализован этот шаблон:

C#
[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 = нет кавычек).

Пример

В следующем примере кода показано, как использовать DebuggerDisplayвместе с DebuggerBrowsable и DebuggerTypeProxy. При просмотре в окне переменных отладчика, например в окне Наблюдение, создается расширение, которое выглядит следующим образом:

Имя значение типа
Ключ "три" объект {string}
Ценность 3 объект {int}
C#
[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;
            }
        }
    }
}