Повышение эффективности отладки с помощью атрибутов просмотра отладчика
Обновлен: Ноябрь 2007
Атрибуты просмотра отладчика позволяют разработчику типа, который задает и хорошо понимает поведение среды выполнения данного типа, также определять, как будет выглядеть тип при отображении в отладчике. Помимо этого, атрибуты просмотра отладчика, которые предоставляют свойство 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
Shared Sub Main(ByVal args() As String)
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 Const TestString As String = "This should appear in the debug window."
Public Sub New(ByVal hashtable As Hashtable)
Me.hashtable = hashtable
End Sub 'New
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(string[] args)
{
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;
}
}
}
}