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


Повышение эффективности отладки с помощью атрибутов просмотра отладчика

Атрибуты просмотра отладчика позволяют разработчику типа, который задает и хорошо понимает поведение среды выполнения данного типа, также определять, как будет выглядеть тип при отображении в отладчике. Помимо этого, атрибуты просмотра отладчика, которые предоставляют свойство Target, могут применяться на уровне сборки пользователями, незнакомыми с исходным кодом. Атрибут DebuggerDisplayAttribute управляет тем, как тип или член отображаются в окнах переменных отладчика. Атрибут DebuggerBrowsableAttribute определяет, будет ли поле или свойство отображаться в окнах переменных отладчика, и каким образом. Атрибут DebuggerTypeProxyAttribute указывает заменяющий тип или прокси для типа и изменяет способ его отображения в окнах отладчика. При просмотре переменной, у которой есть прокси или заменяющий тип, прокси заменяет исходный тип в окне отладчика**.** Окно переменных отладчика отображает толькооткрытые члены прокси-типа. Закрытые члены не отображаются.

Использование атрибута DebuggerDisplay

У конструктора DebuggerDisplayAttribute есть один аргумент: строка, отображаемая в столбце значений для экземпляров типа. Эта строка может содержать фигурные скобки ({ и }). Текст внутри пары фигурных скобок интерпретируется как выражение. Например, выполнение следующего кода на языке C# приводит к отображению строки "Count = 4" при выборе знака плюс (+) с целью развертывания отображения отладчика для экземпляра MyHashtable:

[DebuggerDisplay("Count = {count}")]
class MyHashtable
{
    public int count = 4;
}

Атрибуты, применяемые для свойств, на которые ссылается выражение, не обрабатываются. Для компилятора С# разрешены общие выражения, которые обращаются к этой ссылке на текущий экземпляр целевого типа только неявным образом. Такое выражение ограничено, у него нет доступа к псевдонимам, локальным переменным или указателям. В коде С# можно использовать общие выражения, заключенные в фигурные скобки, которые обращаются к указателю this на текущий экземпляр целевого типа только неявным образом.

Например, если в C# объект был переопределен ToString(), то отладчик вызовет переопределение и отобразит его результат вместо стандартного {<typeName>}. Таким образом, если переопределить ToString(), нет необходимости использовать DebuggerDisplayAttribute. Если используется и то и другое, то атрибут DebuggerDisplayAttribute будет иметь более высокий приоритет по отношению к переопределению ToString().

Использование атрибута DebuggerBrowsable

Атрибут DebuggerBrowsableAttribute применяется к полю или свойству, чтобы определить, каким образом поле или свойство должно отображаться в отладчике. Конструктор для этого атрибута принимает одно из значений перечисления DebuggerBrowsableState, которое задает одно из приведенных ниже состояний:

  • Never. Свидетельствует о том, что член в окне данных не отображается. Например, использование этого значения для атрибута DebuggerBrowsableAttribute в каком-либо поле приводит к удалению этого поля из иерархии; при развертывании включающего типа щелчком по знаку плюс (+) рядом с экземпляром типа это поле не отображается.

  • Collapsed. Свидетельствует о том, что член отображается, но по умолчанию не развернут. Это поведение установлено по умолчанию.

  • RootHidden указывает на то, что член сам по себе не отображается, однако отображаются составляющие его объекты, если он находится в массиве или коллекции.

ПримечаниеПримечание

Visual Basic в .NET Framework версии 2.0 не поддерживает DebuggerBrowsableAttribute.

Следующий пример кода демонстрирует использование DebuggerBrowsableAttribute, чтобы предотвратить отображение свойства, следующего за ним, в окне отладчика для класса:

[DebuggerBrowsable(DebuggerBrowsableState.Never)]
public static string y = "Test String";

Использование атрибута DebuggerTypeProxy

Атрибут DebuggerTypeProxyAttribute следует использовать в тех случаях, когда необходимо внести существенные и принципиальные изменения в отладочное представление типа, сохранив сам тип неизменным. С помощью атрибута DebuggerTypeProxyAttribute можно задать прокси отображения для типа, который позволит разработчикам настраивать представление типа. Этот атрибут, как и DebuggerDisplayAttribute, можно использовать на уровне сборки; в этом случае свойство Target устанавливает тип, для которого будет использоваться прокси. Рекомендуется использовать данный атрибут для указания закрытого вложенного типа, который образуется внутри типа, к которому применен данный атрибут. При отображении типа вычислитель выражений, поддерживающий средства просмотра типов, выполняет проверку на наличие этого атрибута. Если атрибут найден, вычислитель выражений подставляет прокси-тип отображения в тип, к которому был применен данный атрибут.

При наличии атрибута DebuggerTypeProxyAttribute в окне переменной отладчика отображаются только открытые элементы прокси-типа. Закрытые элементы не отображаются. Поведение окна данных не меняется при использовании представлений с расширенными атрибутами.

Чтобы уменьшить потери производительности, атрибуты для прокси отображения не обрабатываются до тех пор, пока объект не будет развернут. Пользователь может его развернуть, щелкнув знак плюс (+) рядом с типом в окне данных, или с помощью приложения, заданного атрибутом DebuggerBrowsableAttribute. Поэтому рекомендуется не применять атрибуты к типам отображения. Атрибуты можно и нужно применять в основной части типа отображения.

В следующем примере кода показано, как с помощью атрибута DebuggerTypeProxyAttribute задать тип, который будет использоваться для отладчика в качестве прокси отображения:

[DebuggerTypeProxy(typeof(HashtableDebugView))]
class MyHashtable : Hashtable
{
    private const string TestString = 
        "This should not appear in the debug window.";

    internal class HashtableDebugView
    {
        private Hashtable hashtable;
        public const string TestStringProxy = 
            "This should appear in the debug window.";

        // The constructor for the type proxy class must have a 
        // constructor that takes the target type as a parameter.
        public HashtableDebugView(Hashtable hashtable)
        {
            this.hashtable = hashtable;
        }
    }
}

Пример

Описание

Следующий пример кода можно просмотреть в Visual Studio 2005 для ознакомления с результатами применения атрибутов DebuggerDisplayAttribute, DebuggerBrowsableAttribute и DebuggerTypeProxyAttribute.

Код

Imports System
Imports System.Collections
Imports System.Diagnostics
Imports System.Reflection



Class DebugViewTest
    ' The following constant will appear in the debug window for DebugViewTest.
    Const TabString As String = "    "
    ' The following DebuggerBrowsableAttribute prevents the property following it
    ' from appearing in the debug window for the class.
    <DebuggerBrowsable(DebuggerBrowsableState.Never)> _
    Public Shared y As String = "Test String"

    Shared Sub Main() 
        Dim myHashTable As New MyHashtable()
        myHashTable.Add("one", 1)
        myHashTable.Add("two", 2)
        Console.WriteLine(myHashTable.ToString())
        Console.WriteLine("In Main.")

    End Sub 'Main 
End Class 'DebugViewTest
<DebuggerDisplay("{value}", Name := "{key}")>  _
Friend Class KeyValuePairs
    Private dictionary As IDictionary
    Private key As Object
    Private value As Object


    Public Sub New(ByVal dictionary As IDictionary, ByVal key As Object, ByVal value As Object) 
        Me.value = value
        Me.key = key
        Me.dictionary = dictionary

    End Sub 'New
End Class 'KeyValuePairs
<DebuggerDisplay("Count = {Count}"), DebuggerTypeProxy(GetType(MyHashtable.HashtableDebugView))> _
Class MyHashtable
    Inherits Hashtable
    Private Const TestString As String = "This should not appear in the debug window."

    Friend Class HashtableDebugView
        Private hashtable As Hashtable
        Public Shared TestString As String = "This should appear in the debug window."

        Public Sub New(ByVal hashtable As Hashtable)
            Me.hashtable = hashtable
        End Sub 'New

        <DebuggerBrowsable(DebuggerBrowsableState.RootHidden)> _
        ReadOnly Property Keys as KeyValuePairs()
            Get
                Dim nkeys(hashtable.Count) as KeyValuePairs

                Dim i as Integer = 0
                For Each key As Object In hashtable.Keys
                    nkeys(i) = New KeyValuePairs(hashtable, key, hashtable(key))
                    i = i + 1
                Next
                Return nkeys
            End Get
        End Property

    End Class 'HashtableDebugView
End Class 'MyHashtable
using System;
using System.Collections;
using System.Diagnostics;
using System.Reflection;

class DebugViewTest
{
    // The following constant will appear in the debug window for DebugViewTest.
    const string TabString = "    ";
    // The following DebuggerBrowsableAttribute prevents the property following it
    // from appearing in the debug window for the class.
    [DebuggerBrowsable(DebuggerBrowsableState.Never)]
    public static string y = "Test String";

    static void Main()
    {
        MyHashtable myHashTable = new MyHashtable();
        myHashTable.Add("one", 1);
        myHashTable.Add("two", 2);
        Console.WriteLine(myHashTable.ToString());
        Console.WriteLine("In Main.");
    }
}
[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;
    }
}
[DebuggerDisplay("Count = {Count}")]
[DebuggerTypeProxy(typeof(HashtableDebugView))]
class MyHashtable : Hashtable
{
    private const string TestString = "This should not appear in the debug window.";

    internal class HashtableDebugView
    {
        private Hashtable hashtable;
        public const string TestString = "This should appear in the debug window.";
        public HashtableDebugView(Hashtable hashtable)
        {
            this.hashtable = hashtable;
        }

        [DebuggerBrowsable(DebuggerBrowsableState.RootHidden)]
        public KeyValuePairs[] Keys
        {
            get
            {
                KeyValuePairs[] keys = new KeyValuePairs[hashtable.Count];

                int i = 0;
                foreach(object key in hashtable.Keys)
                {
                    keys[i] = new KeyValuePairs(hashtable, key, hashtable[key]);
                    i++;
                }
                return keys;
            }
        }
    }
}

using namespace System;
using namespace System::Collections;
using namespace System::Diagnostics;
using namespace System::Reflection;

ref class HashtableDebugView;

[DebuggerDisplay("{value}", Name = "{key}")]
ref class KeyValuePairs
{
private:
    IDictionary^ dictionary;
    Object^ key;
    Object^ value;

public:
    KeyValuePairs(IDictionary^ dictionary, Object^ key, Object^ value)
    {
        this->value = value;
        this->key = key;
        this->dictionary = dictionary;
    }
};

[DebuggerDisplay("Count = {Count}")]
[DebuggerTypeProxy(HashtableDebugView::typeid)]
ref class MyHashtable : Hashtable
{
private:
    static const String^ TestString = "This should not appear in the debug window.";

internal:
    ref class HashtableDebugView
    {
    private:
        Hashtable^ hashtable;
    public:
        static const String^ TestString = "This should appear in the debug window.";
        HashtableDebugView(Hashtable^ hashtable)
        {
            this->hashtable = hashtable;
        }

        [DebuggerBrowsable(DebuggerBrowsableState::RootHidden)]
        property array<KeyValuePairs^>^ Keys
        {
            array<KeyValuePairs^>^ get()
            {
                array<KeyValuePairs^>^ keys = gcnew array<KeyValuePairs^>(hashtable->Count);

                IEnumerator^ ie = hashtable->Keys->GetEnumerator();
                int i = 0;
                Object^ key;
                while (ie->MoveNext())
                {
                    key = ie->Current;
                    keys[i] = gcnew KeyValuePairs(hashtable, key, hashtable[key]);
                    i++;
                }
                return keys;
            }
        }
    };
};

public ref class DebugViewTest
{
private:
    // The following constant will appear in the debug window for DebugViewTest.
    static const String^ TabString = "    ";
public:
    // The following DebuggerBrowsableAttribute prevents the property following it
    // from appearing in the debug window for the class.
    [DebuggerBrowsable(DebuggerBrowsableState::Never)]
    static String^ y = "Test String";

    static void Main()
    {
        MyHashtable^ myHashTable = gcnew MyHashtable();
        myHashTable->Add("one", 1);
        myHashTable->Add("two", 2);
        Console::WriteLine(myHashTable->ToString());
        Console::WriteLine("In Main.");
    }
};

int main()
{
    DebugViewTest::Main();
}

См. также

Ссылки

DebuggerDisplayAttribute

DebuggerBrowsableAttribute

DebuggerTypeProxyAttribute