控制 DebuggerDisplayAttribute 对象、属性或字段在调试器变量窗口中的显示方式。 此属性可以应用于类型(类、结构、枚举、委托),但通常仅适用于类和结构。 如果应用于基类型,该属性也适用于子类。
该 DebuggerDisplay
特性具有单个参数,它是要在类型实例的值列中显示的字符串。 此字符串可以包含大括号({
和 }
)。 一对大括号之间的文本将作为字段、属性或方法进行计算。
如果类具有重写 ToString()
的方法,调试器将使用重写的方法而不是默认值 {<typeName>}
。 因此,如果你已重写 ToString()
方法,调试器将使用重写的方法而非默认 {<typeName>}
,你无需使用 DebuggerDisplay
。 如果同时使用,DebuggerDisplay
属性优先于替代的 ToString()
方法。 DebuggerDisplay
属性优先于子类中替代的 ToString()
方法。
调试器是否评估此隐式 ToString()
调用取决于 “工具”/“选项”/“调试 ”对话框中的用户设置。
重要
如果在“工具/选项/调试”对话框中选中了“在变量窗口中显示对象的原始结构”复选框,则忽略该DebuggerDisplay
属性。
注释
对于本机代码,此属性仅在 C++/CLI 代码中受支持。
下表显示了属性和示例输出的 DebuggerDisplay
一些可能用法。
特征 | 显示在“值”列中的输出 |
---|---|
[DebuggerDisplay("x = {x} y = {y}")] 在具有 x 和 y 字段的类型上使用。 |
x = 5 y = 18 |
[DebuggerDisplay("String value is {getString()}")] 参数语法可能因语言而异。 因此,请谨慎使用。 |
String value is [5, 6, 6] |
DebuggerDisplay
还可以接受命名参数。
参数 | 目的 |
---|---|
Name 、Type |
这些参数会影响变量窗口的“名称和类型”列。 (可以使用与构造函数相同的语法将其设置为字符串。过度使用这些参数或错误地使用这些参数可能会导致输出混乱。 |
Target 、TargetTypeName |
指定在程序集级别使用特性时的目标类型。 |
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# 中的格式说明符。
示例:
下面的代码示例演示如何结合使用DebuggerDisplay
、DebuggerBrowsable
和DebuggerTypeProxy
。 在调试器变量窗口中(如 监视 窗口)中查看时,它会生成如下所示的扩展:
名称 | 价值 | 类型 |
---|---|---|
密钥 | "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;
}
}
}
}