使用调试器显示特性增强调试

注意

本文特定于 .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 表示不显示成员本身,但如果成员是一个数组或集合,则会显示其组成对象。

备注

在 .NET Framework 2.0 中,Visual Basic 不支持 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 中查看以下代码示例,以查看应用 DebuggerDisplayAttributeDebuggerBrowsableAttributeDebuggerTypeProxyAttribute 属性的结果。

代码


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

另请参阅