Упрощаем просмотр значений переменной в отладке

Знаете ли вы об атрибутах, способных облегчить отладку .NET приложений?

[DebuggerDisplay]

Этот атрибут позволяет отобразить значения ключевых свойств экземпляра класса вместо типа в окнах Watch. Для этого необходимо добавить его в описание класса, указав имена этих свойств в фигурных скобках в качестве параметра. Например:

[DebuggerDisplay("{Date} => {Value}")]
internal class ChartPoint
{
    public DateTime Date { get; set; }
    public float Value { get; set; }
    public string Hint { get; set; }
}

И тогда вместо стандартного

DebuggerDisplay-off

отладчик покажет следующее:

DebuggerDisplay-on

Кроме того, такое решение удобно для перечислений. Например:

DebuggerDisplay-array

[DebuggerBrowsable]

Еще один атрибут, определяющий как будет выглядеть значение в окне отладчика. Позволяет определить три состояния, определенных в перечислении DebuggerBrowsableState:

  • Never – не показывать элемент (полезен при создании библиотечных классов и компонент);
  • Collapsed – показывать свернутым (поведение по умолчанию);
  • RootHidden – не показывать сам элемент, но показывать содержащуюся в нем коллекцию.

Посмотрим пример:

internal class ChartData
{
    [DebuggerBrowsable(DebuggerBrowsableState.Never)]
    private int _chartId;

    public string Title { get; set; }

    [DebuggerBrowsable(DebuggerBrowsableState.RootHidden)]
    public ChartPoint[] Points { get; set; }
}

В данном случае, поле _chartId не будет отображено в окне отладчика, а точки Points будут показаны сразу на уровне экземпляра. Обратите внимание, что [DebuggerDisplay] по прежнему сразу отображает текущие значения:

DebuggerBrowsable

[DebuggerStepThrough]

DebuggerStepThroughAttribute позволяет указать метод, в который отладчик не должен заходить в пошаговом режиме. Однако он не запрещает установку внутри точек останова, которые сработают. При этом опция отладчика “Just My Code” должна быть включена.

[DebuggerHidden]

Этот атрибут полностью скрывает элемент от отладчика. Использование его со свойством аналогично [DebuggerBrowsable(DebuggerBrowsableState.Never)]. При указании его для метода, отладчик в пошаговом режиме не только проигнорирует сам метод, но и установленные внутри точки останова.

[DebuggerTypeProxy]

DebuggerTypeProxyAttribute позволяет задать объект, который будет использоваться для отображения данных экземпляра в отладке. Это позволяет кардинально изменить вид объекта в отладчике.

Прокси это обычный класс. Единственное условие – параметром его его конструктора должен быть экземпляр исходного типа. Например, с помощью данной техники выведем для ChartData максимальную дату и среднее значение:

internal class ChartDataDebugView
{
    private readonly ChartData _chartData;

    public ChartDataDebugView(ChartData chartData)
    {
        this._chartData = chartData;
    }

    public DateTime MaxDate
    {
        get { return this._chartData.Points.Max(p => p.Date); }
    }

    public float AveraveValue
    {
        get { return this._chartData.Points.Average(p => p.Value); }
    }
}

[DebuggerTypeProxy(typeof(ChartDataDebugView))]
[DebuggerDisplay("{Title}")]
internal class ChartData
{
    private int _chartId;
    public string Title { get; set; }
    public ChartPoint[] Points { get; set; }
}

Результатом будет следующий вид при отладке:1

Ссылка Raw View позволяет посмотреть оригинальный экземпляр. Также обратите внимание, что [DebuggerDisplay] в данном случае работает с свойствами исходного типа.

[DebuggerVisualizer]

И в завершении, стоит упомянуть еще один атрибут – DebuggerVisualizerAttribute, который позволяет указать собственный класс для отображения текущих значений. В данном случае весь вывод информации, включая окна для отображения, полностью ложиться на разработчика.

Для упрощения этой задачи можно использовать абстрактный базовый класс DialogDebuggerVisualizer, реализующий окно вывода. В этом случае, необходимо переопределить его метод Show() для отображения информации. Простейшую реализацию такого подхода можно посмотреть в MSDN.