C#
这些工具中的大多数仅在中断模式下有效。
在变量窗口和数据提示中创建数据的自定义视图
您可以自定义C++类型、托管对象以及您自己的类型在调试器变量窗口和数据提示中的显示方式。
创建自定义可视化工具
自定义C++视图
使用 Natvis 框架在C++调试器中创建对象的自定义视图
Natvis 的可视化功能可以让你创建的类型在调试期间更加直观清晰。
Natvis 替换了 Visual Studio 早期版本中的 autoexp.dat 文件,提供了 XML 语法、更好的诊断功能、版本控制功能以及多文件支持功能。
Natvis 可视化效果
你可以使用 Natvis 框架为自己创建的类型创建可视化规则,让开发人员在调试过程中更轻松地查看这些类型。
例如,下图显示的类型 Windows::UI::Xaml::Controls::TextBox 的变量在调试器窗口中未应用任何自定义可视化。
调试器不知道如何解释自定义字符串类型,所以你看不到文本框中的字符串。
类的重要成员会显示在一起,并且调试器会显示自定义字符串类型的基础字符串值。
在 C++ 项目中使用 .natvis 文件
Natvis 架构在 %VSINSTALLDIR%\Xml\Schemas\natvis.xsd中定义。
每个 Type 元素的完全限定名称都在其 Name 属性中指定。
<?xml version="1.0" encoding="utf-8"?>
<AutoVisualizer xmlns="http://schemas.microsoft.com/vstudio/debugger/natvis/2010">
<Type Name="MyNamespace::CFoo">
.
.
</Type>
<Type Name="...">
.
.
</Type>
</AutoVisualizer>
这些文件包含许多通用类型的可视化规则,并且可用作编写新类型的可视化效果的示例。
将 natvis 文件添加到C++项目
可以将natvis文件添加到任何C++项目。
若要添加新的natvis文件:
-
-
-
新文件将添加到解决方案资源管理器,并在 Visual Studio 文档窗格中打开。
如果不想在 .pdb 中包含 .natvis 文件,可以从生成的 .pdb 文件中将其排除。
从 .pdb 中排除 .natvis 文件:
如果另一个模块也定义了一个名为 Test 的类,则 Module1.pdb 的 Natvis 条目不适用于它。
通过 VSIX 包安装并注册natvis文件:
无论他们安装在何处,都将在调试过程中自动提取所有注册的natvis文件。
-
例如,对于以下项目文件:
XML
-
<?xml version="1.0" encoding="utf-8"?> <Project DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003" ToolsVersion="14.0"> <ItemGroup> <VSIXSourceItem Include="Visualizer.natvis" /> </ItemGroup> </Project> -
在source.extension.vsixmanifest文件中注册natvis文件:
XML
-
<?xml version="1.0" encoding="utf-8"?> <PackageManifest Version="2.0.0" xmlns="http://schemas.microsoft.com/developer/vsx-schema/2011" xmlns:d="http://schemas.microsoft.com/developer/vsx-schema-design/2011"> <Assets> <Asset Type="NativeVisualizer" Path="Visualizer.natvis" /> </Assets> </PackageManifest>
Natvis 文件位置
如果你希望将 .natvis 文件应用于多个项目,可以将它们添加到用户目录或系统目录中。
将按照以下顺序来评估 .Natvis 文件:
-
所调试的 .pdb 文件中内嵌的所有的 .natvis 文件,除非加载的项目中存在同名文件。
-
这包括所有已加载的 C++ 项目(包括类库),但不包括其他语言的项目。
-
通过 VSIX 包安装并注册的任何natvis文件。
- 特定于用户的 Natvis 目录 (例如, %USERPROFILE%\Documents\Visual Studio 2019\Visualizers)。
- 如果你具有管理员权限,可以将文件添加到此目录中。
调试时修改 natvis 文件
保存后,监视和局部变量窗口就会随之更新,显示所做的更改。你还可以在调试的解决方案中添加或删除 .natvis 文件,Visual Studio 会随之添加或删除相关的可视化效果。在调试时,无法更新 .pdb 文件中内嵌的 .natvis 文件。
更改随后就会生效,无需重新启动调试会话。
例如, .natvis 文件可能被纳入了源代码管理中,并且你需要获取其他人最近所做的更改。
表达式和格式化
除了上下文运算符 (C++) 中所述的、调试器中的 C++ 表达式的增强功能和限制外,还要注意以下事项:
-
尽管可以访问全局变量,但不能访问 Natvis 表达式中的局部变量。
-
由于调试器内部函数没有副作用,因此可以从任何 Natvis 表达式随意调用,即使系统不允许进行其他函数调用也是如此。
-
如果条目由 Natvis 在内部使用,格式说明符将被忽略,例如
SizeArrayItems 扩展中的 表达式。
Natvis 视图
默认视图和 DisplayString 视图中都显示了 ArrayItems 和 simple 元素,但 [size] 视图中却未显示 [capacity] 和 simple 项。
<Type Name="std::vector<*>">
<DisplayString>{{ size={_Mylast - _Myfirst} }}</DisplayString>
<Expand>
<Item Name="[size]" ExcludeView="simple">_Mylast - _Myfirst</Item>
<Item Name="[capacity]" ExcludeView="simple">_Myend - _Myfirst</Item>
<ArrayItems>
<Size>_Mylast - _Myfirst</Size>
<ValuePointer>_Myfirst</ValuePointer>
</ArrayItems>
</Expand>
</Type>
simple 视图将显示为 vec,view(simple) :
Natvis 错误
你可以使用 Natvis 的诊断功能来了解调试器忽略可视化条目的原因,还可以查看基础语法和分析错误。
若要启用 Natvis 诊断:
Visual C/C++自定义可视化工具兼容性
作为此更新的一部分,必须更新 C/C++ expression 计算器的某些扩展以使它们与新调试器兼容。
因此,如果您希望调试大型项目,则建议您与扩展的所有者合作,使其与此调试选项兼容。
托管代码自定义视图
创建托管对象的自定义视图
DebuggerBrowsableAttribute 为自定义数据添加扩展。
此限制已在最新版本的 .NET 中删除。
使用 DebuggerDisplay 特性(C#、Visual Basic、 F#、 C++/cli)告诉调试器要显示的内容
一对大括号之间的文本将作为字段、属性或方法进行计算。
@No__t_0 特性还优先于子类中重写的 ToString() 方法。
Visual Basic 不实现此隐式 ToString() 计算。
对于本机代码,此属性仅在/Cli 代码C++中受支持。
下表显示 DebuggerDisplay 特性的一些可能用法和示例输出。
| 特性 | 显示在“值”列中的输出 |
|---|---|
[DebuggerDisplay("x = {x} y = {y}")]在具有 x 和 y字段的类型上使用。
|
x = 5 y = 18 |
| 因此,使用时要小心。 | String value is [5, 6, 6] |
DebuggerDisplay 还可以接受命名参数。
| 参数 | 目标 |
|---|---|
Name,Type |
(可将它们设置为使用与构造函数相同的语法的字符串。)如果过度使用这些参数或使用这些参数不当,则会导致混乱的输出。 |
Target,TargetTypeName |
指定在程序集级别使用该特性时的目标类型。 |
在修改 autoexp.cs 文件之前,一定要对该文件进行备份。
若要生成 autoexp.cs,请打开 VS2015 开发人员命令提示,并运行以下命令
cd <directory containing autoexp.cs>
csc /t:library autoexp.cs
将在下一调试会话中选取对 autoexp.dll 的更改。
在 DebuggerDisplay 中使用表达式
虽然您可以在 DebuggerDisplay 特性中的大括号之间使用常规表达式,但建议不要这样做。
是 8,则 C# 代码 Object 6 将显示 count。
在 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);
}
}
}
示例
在调试器变量窗口(如 “监视” 窗口)中查看时,它生成类似以下内容的扩展:
| 名称 | “值” | Type |
|---|---|---|
| 项 | object {string} | |
| “值” | 3 | object {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;
}
}
}
}
使用 DebuggerTypeProxy 特性(C#Visual Basic, C++/cli)告诉调试器要显示的类型
不会显示私有成员。
此特性可应用于:
- 结构
- 类
- 程序集
备注
对于本机代码,此属性仅在/Cli 代码C++中受支持。
因此,不应在构造函数中执行非必需的工作。
特性可以且应该用于显示类型的正文中。
这样,它便能轻松访问内部成员。
DebuggerTypeProxyAttribute,因此,如果在基类上指定了类型代理,则它将应用于任何派生类,除非这些派生类指定其自己的类型代理。
DebuggerTypeProxyAttribute,则 Target 参数将指定代理要替换的类型。
DebuggerTypeProxyAttribute 一起使用的示例,请参阅使用 DebuggerDisplay 属性。
将泛型与 DebuggerTypeProxy 一起使用
不支持封闭类型(也称作“构造类型”)。
开放类型的语法类似于:
Namespace.TypeName<,>
DebuggerTypeProxy 机制将为你推理类型参数。
有关中C#的 C# 打开和关闭类型的详细信息,请参阅语言规范部分20.5.2 打开和关闭类型。
而必须使用开放类型名称的字符串表示形式。
"Namespace.TypeName'2"