下面是对 DebuggerDisplay、DebuggerBrowsable 及其相关"系列"特性的系统性说明,涵盖概念、语法、选项、示例、性能与调试器行为等。
一、DebuggerDisplay
- 作用:自定义对象在调试器中的显示文本(Locals/Watch/数据提示)。
- 用法:在类型或成员上标注
[DebuggerDisplay("格式字符串")]。 - 格式字符串语法:
- 文本常量:"MyType"
- 引用成员:
{Member},支持属性、字段、无参实例方法,如{Id}、{Count}、{GetState()} - 去引号:
{Name,nq}对字符串去除引号 - 可组合:
"Id={Id}, Count={Items.Count}, Ready={IsReady}"
- 注意事项:
- 求值发生在调试暂停时,若成员访问抛异常,调试器显示错误提示。
- 避免昂贵或有副作用的 getter/方法。
- 示例(通用):
csharp
using System.Diagnostics;
[DebuggerDisplay("{Name,nq} (Id={Id}, Count={Items.Count})")]
public class Sample
{
public int Id { get; set; }
public string Name { get; set; }
public List<int> Items { get; } = new List<int>();
}
二、DebuggerBrowsable
- 作用:控制成员在调试器中的可见性与展开方式,减少噪声或提供更友好的展开视图。
- 用法:标注在字段或属性上(常用于字段)。
- 枚举值:
DebuggerBrowsableState.Never:在调试器中隐藏该成员。DebuggerBrowsableState.RootHidden:将该成员的元素直接作为父对象的子级展开显示(典型用于集合字段/属性)。DebuggerBrowsableState.Collapsed:成员在调试器中默认折叠(具体效果依调试器版本而定,常见实现可能忽略)。
- 示例(隐藏或扁平化集合):
csharp
using System.Diagnostics;
public class Bag<T>
{
[DebuggerBrowsable(DebuggerBrowsableState.RootHidden)]
public T[] Items;
[DebuggerBrowsable(DebuggerBrowsableState.Never)]
private string _debugNoise; // 在调试器中不显示
}
- 注意事项:
- RootHidden 适用于返回数组或实现 IEnumerable 的成员;展开时元素直接显示,父级容器成员名不出现。
- 对属性使用时需留意 getter 是否有副作用。
三、DebuggerTypeProxy(同属"调试视图系列",常与以上配合)
- 作用:为类型提供一个"调试专用代理视图",在调试器中以代理的公开成员来展示对象,适合复杂对象/集合。
- 用法:在类型上标注
[DebuggerTypeProxy(typeof(MyTypeDebugView))] - 示例(为集合自定义可读视图):
csharp
using System.Diagnostics;
using System.Linq;
[DebuggerTypeProxy(typeof(InventoryDebugView))]
public class Inventory
{
public List<Item> Items { get; } = new List<Item>();
public string Name { get; set; }
}
internal sealed class InventoryDebugView
{
private readonly Inventory _inv;
public InventoryDebugView(Inventory inv) => _inv = inv;
// 在调试器中以数组展开
public object[] Items => _inv.Items
.Select(i => new { i.Id, i.Title, i.Stock })
.ToArray();
}
- 注意事项:
- 代理仅在调试器中使用,不参与运行。
- 代理成员应轻量、无副作用,避免 IO/锁/昂贵计算。
四、DebuggerStepThrough / DebuggerNonUserCode / DebuggerHidden(相关调试行为特性)
DebuggerStepThrough:指示调试器在单步执行时跳过标注的代码(仍可设置断点)。DebuggerNonUserCode:将代码标记为非用户代码,调试器默认不步入。DebuggerHidden:隐藏方法,使调试器无法在此处设置断点或步入(慎用)。- 用途:控制单步体验,减少进入框架/样板代码。
五、组合最佳实践
- DebuggerDisplay:展示关键、廉价、稳定的状态(Id、Name、Count、简单 Flags)。
- DebuggerBrowsable:
- 对"内部实现细节"使用 Never 隐藏,降低视觉噪声。
- 对集合用 RootHidden 扁平化,便于直接查看元素。
- DebuggerTypeProxy:为复杂对象/集合提供结构化视图(数组、投影),避免在 DebuggerDisplay 中塞过多信息。
- 性能与安全:
- 避免在调试视图中触发昂贵或有副作用操作(数据库、磁盘、网络、锁)。
- Getter 内避免复杂副作用逻辑,防止调试卡顿。
- 若遇到评估过于频繁导致性能问题,可调整 Visual Studio 设置:Tools > Options > Debugging > General,如关闭"Enable property evaluation and other implicit function calls"。
六、常见问题与规避
- 成员名拼写错误导致显示为空或错误提示。
- 在 DebuggerDisplay 中调用可能抛异常的方法,影响调试体验。
- 使用 RootHidden 但返回类型不是可枚举/数组,导致视图不如预期。
- 在代理视图或属性 getter 中执行昂贵逻辑引发卡顿或阻塞。
七、在 Visual Studio 中查看与验证
- 暂停后在 Debug > Windows > Locals 、Watch、悬停数据提示查看效果。
- 如需精简自动求值,调整 Tools > Options > Debugging > General 的相关选项。
- 若想只在调试器中显示某些成员,可以把它们放在代理视图中,原类型中使用
DebuggerBrowsable(Never)隐藏实现字段。
了解更多
DebuggerDisplayAttribute Class
DebuggerBrowsableAttribute Class
DebuggerTypeProxyAttribute Class
System.Windows.Controls 命名空间 | Microsoft Learn
控件库 - WPF .NET Framework | Microsoft Learn
使用 Visual Studio 创建新应用教程 - WPF .NET | Microsoft Learn
HeBianGu的个人空间-HeBianGu个人主页-哔哩哔哩视频