Повышение эффективности отладки с помощью атрибутов просмотра отладчика
Примечание.
Эта статья относится к .NET Framework. Он не применяется к более новым реализациям .NET, включая .NET 6 и более поздние версии.
С помощью атрибутов просмотра отладчика разработчик типа может определить параметры отображения типа в отладчике, что позволяет лучше описать его поведение во время выполнения. Кроме того, пользователи, не знакомые с соответствующим исходным кодом, могут применить на уровне сборки атрибуты просмотра отладчика, предоставляющие свойство Target
. Атрибут DebuggerDisplayAttribute определяет, как тип или член отображается в окнах переменных отладчика. Атрибут DebuggerBrowsableAttribute определяет, отображается ли поле или свойство в окнах переменных отладчика, и каким образом это реализуется. Атрибут DebuggerTypeProxyAttribute указывает прокси (заменяющий тип) для типа и меняет способ отображения типа в окнах отладчика. При просмотре переменной, у которой есть прокси (заменяющий тип), прокси заменяет исходный тип в окне просмотра отладчика. Окно переменных отладчика отображает только открытые члены прокси-типа. Закрытые члены не отображаются.
Использование атрибута DebuggerDisplayAttribute
Конструктор DebuggerDisplayAttribute имеет один аргумент, определяющий строку, которая должна отображаться в столбце "Значение" для экземпляров типа. Эта строка может содержать фигурные скобки ({ и }). Текст, заключенный в фигурные скобки, вычисляется как выражение. Например, при нажатии на значок плюса (+) для развертывания окна просмотра отладчика для экземпляра MyHashtable
следующий код C# отображает "Count = 4".
[DebuggerDisplay("Count = {count}")]
class MyHashtable
{
public int count = 4;
}
Атрибуты, применяемые к свойствам, на которые есть ссылки в выражении, не обрабатываются. Компилятор C# поддерживает только общие выражения с неявным доступом к этой ссылке для текущего экземпляра конечного типа. Выражение ограничено и не имеет доступа к псевдонимам, локальным переменным или указателям. В C# можно использовать общее выражение в скобках, которое имеет неявный доступ к указателю this
только для текущего экземпляра конечного типа.
Например, если в объекте C# имеется переопределенный метод ToString()
, отладчик будет вызывать переопределенный метод и отображать возвращаемый им результат, а не имя типа {<typeName>}.
. Таким образом, если метод ToString()
переопределен, нет необходимости использовать DebuggerDisplayAttribute. Если используется и то и другое, то атрибут DebuggerDisplayAttribute будет иметь более высокий приоритет по отношению к переопределению ToString()
.
Использование атрибута DebuggerBrowsableAttribute
Применяя атрибут DebuggerBrowsableAttribute к полю или свойству, можно указать, как они будут отображаться в окне отладчика. Конструктор этого атрибута принимает одно из значений перечисления DebuggerBrowsableState, которое задает одно из следующих состояний:
Never указывает, что член не отображается в окне данных. Например, если применить это значение к полю DebuggerBrowsableAttribute, это поле будет удалено из иерархии и не будет отображаться при развертывании включающего типа путем нажатия кнопки плюса (+) для экземпляра типа.
Collapsed указывает, что член отображается, но по умолчанию не развернут. Это поведение принимается по умолчанию.
RootHidden указывает, что сам член не отображается, однако если это массив или коллекция, то выводятся составляющие его объекты.
Примечание.
Не DebuggerBrowsableAttribute поддерживается Visual Basic в платформа .NET Framework версии 2.0.
В следующем примере кода показано, как использовать атрибут 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;
}
}
}
Пример
Description
Следующий пример кода можно просмотреть в Visual Studio, чтобы просмотреть результаты применения DebuggerDisplayAttributeDebuggerBrowsableAttributeатрибутов и DebuggerTypeProxyAttribute атрибутов.
Код
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();
}
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;
}
}
}
}
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
End Class
<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
End Class
<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
<DebuggerBrowsable(DebuggerBrowsableState.RootHidden)> _
ReadOnly Property Keys as KeyValuePairs()
Get
Dim nkeys(hashtable.Count - 1) 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
End Class