告知调试器如何使用 DebuggerDisplay 属性显示的内容(C#、Visual Basic、F#、C++/CLI)

控制 DebuggerDisplayAttribute 对象、属性或字段在调试器变量窗口中的显示方式。 此属性可以应用于类型(类、结构、枚举、委托),但通常仅适用于类和结构。 如果应用于基类型,该属性也适用于子类。

DebuggerDisplay 特性具有单个参数,它是要在类型实例的值列中显示的字符串。 此字符串可以包含大括号({})。 一对大括号之间的文本将作为字段、属性或方法进行计算。

如果类具有重写 ToString() 的方法,调试器将使用重写的方法而不是默认值 {<typeName>}。 因此,如果你已重写 ToString() 方法,调试器将使用重写的方法而非默认 {<typeName>},你无需使用 DebuggerDisplay。 如果同时使用,DebuggerDisplay 属性优先于替代的 ToString() 方法。 DebuggerDisplay 属性优先于子类中替代的 ToString() 方法。

调试器是否评估此隐式 ToString() 调用取决于 “工具”/“选项”/“调试 ”对话框中的用户设置。

重要

如果在“工具/选项/调试”对话框中选中了“在变量窗口中显示对象的原始结构”复选框,则忽略该DebuggerDisplay属性。

注释

对于本机代码,此属性仅在 C++/CLI 代码中受支持。

下表显示了属性和示例输出的 DebuggerDisplay 一些可能用法。

特征 显示在“值”列中的输出
[DebuggerDisplay("x = {x} y = {y}")]

在具有 xy字段的类型上使用。
x = 5 y = 18
[DebuggerDisplay("String value is {getString()}")]参数语法可能因语言而异。 因此,请谨慎使用。 String value is [5, 6, 6]

DebuggerDisplay 还可以接受命名参数。

参数 目的
NameType 这些参数会影响变量窗口的“名称和类型”列。 (可以使用与构造函数相同的语法将其设置为字符串。过度使用这些参数或错误地使用这些参数可能会导致输出混乱。
TargetTargetTypeName 指定在程序集级别使用特性时的目标类型。

autoexp.cs文件在程序集级别使用 DebuggerDisplay 属性。 autoexp.cs文件确定 Visual Studio 用于 .NET 对象的默认扩展。 可以检查 autoexp.cs 文件,了解如何使用 DebuggerDisplay 属性的示例,也可以修改和编译autoexp.cs文件以更改默认扩展。 在修改 autoexp.cs 文件之前,请务必备份该文件。

若要生成 autoexp.cs,请打开 VS2015 开发人员命令提示符并运行以下命令

cd <directory containing autoexp.cs>
csc /t:library autoexp.cs

将在下一调试会话中选取对 autoexp.dll 的更改

在 DebuggerDisplay 中使用表达式

尽管可以在 DebuggerDisplay 属性中的大括号之间使用常规表达式,但不建议这样做。

DebuggerDisplay 中的常规表达式只能隐式访问目标类型的当前实例的 this 指针。 表达式无权访问别名、局部变量或指针。 如果表达式引用属性,则不会处理这些属性的属性。 例如,如果字段count为 8,C# 代码[DebuggerDisplay("Object {count - 2}")]将显示Object 6

在 DebuggerDisplay 中使用表达式可能会导致以下问题:

  • 计算表达式是调试器中最消耗资源的操作,并且表达式在每次显示时都会被计算。 在单步执行代码时,这可能会导致性能问题。 例如,当元素数量较大时,用于显示集合或列表中的值的复杂表达式可能非常慢。

  • 表达式由当前堆栈帧语言的表达式计算器计算,而不是由编写表达式的语言的计算器计算。 当语言不同时,这可能会导致不可预知的结果。

  • 计算表达式可更改应用程序的状态。 例如,一个表达式,用于设置属性的值会改变执行代码中的属性值。

    减少表达式评估可能出现的问题的一种方法是创建一个私有属性,该属性执行操作并返回字符串。 然后,DebuggerDisplay 属性可以显示该私有属性的值。 以下示例实现此模式:

[DebuggerDisplay("{DebuggerDisplay,nq}")]
public sealed class MyClass
{
    public int count { get; set; }
    public bool flag { get; set; }
    private string DebuggerDisplay
    {
        get
        {
            return string.Format("Object {0}", count - 2);
        }
    }
}

“,nq”后缀指示表达式计算器在显示最终值时删除引号(nq = 无引号)。 有关格式化程序的详细信息,请参阅 C# 中的格式说明符

示例:

下面的代码示例演示如何结合使用DebuggerDisplayDebuggerBrowsableDebuggerTypeProxy。 在调试器变量窗口中(如 监视 窗口)中查看时,它会生成如下所示的扩展:

名称 价值 类型
密钥 "three" object {string}
价值 3 对象 {int}
[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;
    }

    public object Key
    {
        get { return key; }
        set
        {
            object tempValue = dictionary[key];
            dictionary.Remove(key);
            key = value;
            dictionary.Add(key, tempValue);
        }
    }

    public object Value
    {
        get { return this.value; }
        set
        {
            this.value = value;
            dictionary[key] = this.value;
        }
    }
}

[DebuggerDisplay("{DebuggerDisplay,nq}")]
[DebuggerTypeProxy(typeof(HashtableDebugView))]
class MyHashtable
{
    public Hashtable hashtable;

    public MyHashtable()
    {
        hashtable = new Hashtable();
    }

    private string DebuggerDisplay { get { return "Count = " + hashtable.Count; } }

    private class HashtableDebugView
    {
        private MyHashtable myhashtable;
        public HashtableDebugView(MyHashtable myhashtable)
        {
            this.myhashtable = myhashtable;
        }

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

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