技术速递|介绍 .NET API 文档的源代码链接

news2025/2/26 9:12:47

作者:Min Huang,Matt Trilby-Bassett

排版:Alan Wang

开发人员在阅读 API 参考文档时,有时会需要或希望查看相应的源代码。直到不久之前,.NET API 参考文档还没有提供指向源代码的链接,这引起社区添加这一功能的呼声。针对这一反馈,我们很高兴地宣布,现在大多数流行的 .NET API 上都提供了连接文档和源代码的链接。

在这篇博文中,我们将分享将链接添加到文档以及利用现有 API 来实现这一改进的详细信息。

链接的实例

在介绍实施细节之前,我们想展示一下文档的改动。对于符合我们标准(启用了源代码链接、具有可访问的 PDB 并托管在公共存储库中)的 .NET API,其链接包含在 Definition 元数据中。以下来自 String 类的截图演示了这个新链接的位置:
在这里插入图片描述
如果存在重载,链接将包含在重载标题的下面。下面的 String.IndexOf 方法截图演示了这种情况:
在这里插入图片描述

我们如何建立链接?

.NET 参考文档管道对一组 DLL 文件和 NuGet 包进行操作。这些文件由各种工具处理,以将其内容转换为显示在 Microsoft Learn 上的 HTML 页面。正确构建源代码的链接需要了解源代码、二进制文件和 GitHub 之间的关系,以及它们如何与一些现有的 .NET API 配合在一起。在与 .NET 和 Roslyn 团队的开发人员讨论我们公开源代码链接的目标时,很明显我们的要求与 Visual Studio 的 Go to definition 功能紧密相关。

凭借这种理解以及 @davidwengier 在 Roslyn 中针对外部源的 Go to definition 改进中提供的有关 Go to definition 的大量细节,我们能够采用类似的方法来构建指向文档源代码的链接。

源代码链接

源代码链接是一种技术,它使 .NET 开发人员能够调试其应用程序引用的程序集的源代码。尽管源代码链接最初旨在用于源代码调试,但它完全适用于我们的场景。每个启用源代码链接的 .NET 项目都会在 PDB(程序数据库)中生成从相对文件夹路径到 绝对存储库 URL 的映射。这与 @davidwengier 在 Roslyn 中针对外部源的 Go to definition 改进中所述一致。

若要查看源链接条目,可以使用 dotPeek 或 ILSpy 打开 DLL。以下屏幕截图显示了使用 dotPeek 访问 System.Private.CoreLib 的源链接条目的示例,方法是导航到 Portable PDB Metadata,然后导航到 CustomDebugInformation 表:
在这里插入图片描述

[!NOTE] 若要了解有关源代码链接的元数据定义,请转到:PortablePdb-Metadata。

建立链接

现在我们知道在源代码链接条目中存储了一个总体映射,下一个问题是如何为这个 DLL 中的每个类型/成员构建唯一的链接?

例如,我们为 String.Clone 方法构建的链接是:

https://github.com/dotnet/runtime/blob/5535e31a712343a63f5d7d796cd874e563e5ac14/src/libraries/System.Private.CoreLib/src/System/String.cs#L388C13-L388C25

此链接可分为 3 个部分:

  1. 第一部分 https://github.com/dotnet/runtime/blob/5535e31a712343a63f5d7d796cd874e563e5ac14 是从源代码链接映射 json 解析出来的,并且与特定的存储库提交绑定。

  2. 第二部分 src/libraries/System.Private.CoreLib/src/System/String.cs 可以在 PDB 的文档表中找到。

  3. 最后一部分 #L388C13-L388C25 是基于 MethodDebugInformation 表的 SequencePoints 列构建的。SequencePoints blob 会将此方法块中的一系列 IL 指令映射回其原始源代码行号,如下面的屏幕截图所示。有关更多详细信息,请转到 SequencePoints 元数据定义。
    在这里插入图片描述
    我们使用 System.Reflection.Metadata 库来遍历此 DLL 中的所有类型/成员,然后匹配 MethodDebugInformation 表中的记录以构建最终的链接。

var mdReader = peReader.GetMetadataReader();
foreach(var typeDefHandle in mdReader.TypeDefinitions)
{
    var typeDef = mdReader.GetTypeDefinition(typeDefHandle);
    string typeName = mdReader.GetString(typeDef.Name);
    string ns = mdReader.GetString(typeDef.Namespace);
    string fullName = String.IsNullOrEmpty(ns) ? typeName : $"{ns}.{typeName}";
    Console.WriteLine(fullName);
    foreach (var document in debugReader.FindSourceDocuments(typeDefHandle))
    {
        Console.WriteLine($"  {document.SourceLinkUrl}");
    }
}

该实现也可以在 Roslyn DocumentDebugInfoReader.cs 和 SymbolSourceDocumentFinder.cs 中找到。

查找 PDB 文件

因为我们知道链接的信息可以在 PDB 中找到,所以下一步就是找到这些 PDB 以供我们使用。

目前,指定某一个 DLL,我们会在三个地方查找相应的 PDB:

  1. 嵌入式 PDB。如果您的 csproj 中指定了 embedded,则 PDB 文件将嵌入到此 DLL 中。

  2. 磁盘上的 PDB。您可以将 PDB 放在 DLL 旁边。

  3. Microsoft Symbol Server。有一个公共符号服务器,我们可以从中下载 DLL 的 PDB。

请参阅 Roslyn PdbFileLocatorService.cs 中的实现。

查找正确的 PDB 版本

我们想进一步讨论如何从 Microsoft Symbol Server 下载指定 DLL 的正确版本的 PDB。

下面是一个PDB 下载 URL 的示例 ,其格式在 portable-pdb-signature 中定义。

http://msdl.microsoft.com/download/symbols/System.Private.CoreLib.pdb/8402667829752b9d0b00ebbc1d5a66d9FFFFFFFF/System.Private.CoreLib.pdb

从 URL 模式中我们可以观察到,我们需要提供 PDB 文件名 System.Private.CoreLib.pdb 和 GUID 8402667829752b9d0b00ebbc1d5a66d9FFFFFFFF。那么问题是我们可以在哪里找到这些信息?

之前我们使用 dotPeek 打开 DLL 来查找源代码链接条目。现在我们可以再次打开它并检查元数据部分。
在这里插入图片描述
在上面的截图中,我们可以在 Debug Directory 中找到这个 GUID,并且该条目必须是一个可移植代码视图条目。该条目的 Path 属性代表 PDB 文件的路径,我们可以从中获取文件名。

foreach (var entry in peReader.ReadDebugDirectory())
{
    if (entry.Type == DebugDirectoryEntryType.CodeView && entry.IsPortableCodeView)
    {
        var codeViewEntry = peReader.ReadCodeViewDebugDirectoryData(entry);
        var pdbName = Path.GetFileName(codeViewEntry.Path);
        var codeViewEntryGuid = $"{codeViewEntry.Guid.ToString("N").ToUpper()}FFFFFFFF";
        return $"{MsftSymbolServerUrl}/{pdbName}/{codeViewEntryGuid}/{pdbName}";
    }
}

查找 DLL 文件

如前所述,我们的 .NET 参考文档管道对 DLL 文件或 NuGet 包的集合进行操作。但对于某些程序集,我们需要发挥创造力来生成指向源代码的链接。以下是我们需要开发解决方案的两种情况:

  1. 参考程序集。例如, Microsoft.NETCore.App.Ref 包中的 DLL。参考程序集没有将 PDB 上传到符号服务器,这阻止我们生成源代码链接。我们当前的解决方案是下载 Runtime 包并使用其中的程序集下载匹配的 PDB。

  2. 源代码嵌入在 PDB 中。例如,System.Threading.AccessControl 包在构建时会将源代码生成到 obj 文件夹中。

在这里插入图片描述

使用文档管道中的链接

一旦我们找到正确的 DLL/PDB 文件并成功建立源代码的链接,我们就会将此信息以 JSON 文件形式保存在目标文档 GitHub 存储库中。

为了了解我们将如何使用这些信息,我们需要重新审视 .NET 参考文档管道。管道为每种唯一类型创建一个 XML 文件,我们的构建系统稍后会将其转换为显示在 Microsoft Learn 上的 HTML 页面。为了将 XML 中的 API 映射到 JSON 文件中找到的相应源代码链接,我们使用唯一标识符 DocId。此值存在于 XML(DocId)和 JSON(DocsId)中。

例如,System.String 的 DocId 为 T:System.String。此 DocId 值将用于定位 System.Private.CoreLib.json 文件(其对应版本)中的源代码链接。

"DocsId": "T:System.String",
"SourceLink": "https://github.com/dotnet/runtime/blob/5535e31a712343a63f5d7d796cd874e563e5ac14/src/libraries/System.Private.CoreLib/src/System/String.cs"

若要了解如何生成 DocId,请参阅 DocCommentId.cs 或 DocumentationCommentId.cs。

已知限制

在当前的实施中,我们意识到一些限制:

  1. 对于 PDB 中没有记录文档信息的类型(例如枚举或接口),在 CustomDebugInformation 表中引入了新的 GUID TypeDefinitionDocuments 来解决此问题。但是,对于某些 DLL,这些信息有时会被修剪,导致我们无法生成链接。
    请参阅此处的错误详细信息 https://github.com/dotnet/runtime/issues/100051。

  2. 对于没有定义主体的类成员(例如 extern 或 abstract),PDB 中不包含行信息(SequencePoints)。因此,我们无法指向某个跨度范围,而是指向整个文件。我们计划在未来做出改进以解决此问题。

另一个改进想法

您可能已经注意到,我们与 Go to definition共享了许多核心逻辑。事实上,我们在实现中重用了它们的几个类。我们提出了一个准备用来的改进此过程的功能,即使用现有代码修改 Roslyn,以生成供我们使用的类型/成员级源映射。

如果社区有同样的需求,请评论为我们投票。谢谢!

向我们提供您的反馈

我们很乐意听取您对使用这些链接的反馈,因此请告诉我们您的想法!如果您发现任何与链接相关的问题,请随时使用反馈控件分享或在相关文档存储库上提交 GitHub 问题。
在这里插入图片描述

最后,致谢

我要感谢我的同事@shiminxu 为这个项目做出的贡献。还要感谢 .NET 团队的 @ericstj 和 Roslyn 团队的 @tmat 提供的技术指导。最后,感谢无数为实现这一改变做出的贡献的人。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1823707.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

【Python】Numpy的使用

文章目录 数组创建数组属性ndarray数组索引ndarray数组的基本索引和切片ndarray数组的布尔索引ndarray数组的花式索引 ndarray数组的转置和轴对换ndarray通用函数一元ufunc二元ufunc NumPy的where函数使用常用统计函数排序 ndarray数组的去重以及集合运算numpy中的线性代数nump…

优思学院|如何选择六西格玛黑带的项目?

不管六西格玛的实施着重于变革式的还是渐进式的目标,项目都是六西格玛最核心的部分。选择和使用组织中最好的人才本身并不一定能保证达到最好的结果,项目的选取是领导层无可推卸的责任。选择一个项目意味着什么?领导团队必须将无数的问题、困…

DAC测试实验——FPGA学习比

一、DAC简介 DAC全称Digital to Analog Converter,即数模转换器。它用于将主控芯片产生的数字值(0和1)转换为模拟值(电压值)。 1、DAC参数指标 2、DAC类型 常用的DAC可大致分为权电阻网络DAC、T型电阻网络DAC、倒T型电阻网络DAC以及权电流型DAC。 3、AD9708/3PD9…

html中a标签的多用性

在HTML中&#xff0c;<a> 标签&#xff08;通常称为锚标签或链接标签&#xff09;具有多种用途和强大的功能。以下是<a>标签的一些主要多用性&#xff1a; 网页间的导航&#xff1a; 这是<a>标签最常见的用途。通过href属性&#xff0c;可以指定一个URL&am…

【问题解决】国际化messages_zh_CN.properties中乱码问题

打开 messages_zh_CN.properties 文件 之前用中文写的现在都是各种各样的符号 解决方法&#xff1a; 打开idea 找到File>Settings>Editor>File Encodings 确定这三个地方是否都是utf-8&#xff0c;改好之后点确定&#xff0c;就能正常显示了

吴恩达深度学习笔记:机器学习(ML)策略(1)(ML strategy(1))1.9-1.10

这里写自定义目录标题 第三门课 结构化机器学习项目&#xff08;Structuring Machine Learning Projects&#xff09;第一周 机器学习&#xff08;ML&#xff09;策略&#xff08;1&#xff09;&#xff08;ML strategy&#xff08;1&#xff09;&#xff09;1.9 可避免偏差&am…

红队内网攻防渗透:内网渗透之Linux内网权限提升技术:udf提权Capability权限LD_PRELOAD环境变量

红队内网攻防渗透 1. 内网权限提升技术1.1 Linux系统提权-Web&用户-数据库udf提权1.1.1 信息收集1.1.2 Web权限获取1.1.3 MYSQL-UDF提权1.1.4 下载到目标上1.1.5 连接确认是否有条件进行导出调用1.1.6 开始进行写入导出调用1.2 Linux系统提权-Web&用户-Capability能力1…

高分论文密码---大尺度空间模拟预测与数字制图

大尺度空间模拟预测和数字制图技术和不确定性分析广泛应用于高分SCI论文之中&#xff0c;号称高分论文密码。大尺度模拟技术可以从不同时空尺度阐明农业生态环境领域的内在机理和时空变化规律&#xff0c;又可以为复杂的机理过程模型大尺度模拟提供技术基础。我们将结合一些经典…

JDK8时间类,时区,时间和格式化

一.时间类 二.获取所有的时区 1.获取所有的时区Set<String> zoneIds ZoneId.getAvailableZoneIds();System.out.println(zoneIds.size()); 根据打印的结果可以看到java类中一共有603个时区。 三.获取当前系统默认的时区 ZoneId zoneId ZoneId.systemDefault();Syste…

测试基础13:测试用例设计方法-错误推断、因果图判定表

课程大纲 1、错误推测法 靠主观经验和直觉来推测可能容易出现问题的功能或场景&#xff0c;设计相关测试用例进行验证。 2、因果图&判定表 2.1定义 因果图和判定表是分析和表达多逻辑条件下&#xff0c;执行不同操作的情况的工具。 &#xff08;因果图和判定表配合使用&a…

20.2 JSON-JSON解码、映射数据类型、处理JSON响应

1. JSON解码 JSON解码&#xff0c;即将JSON格式在字符串转换为Go语言数据类型的变量。 函数Unmarshal接受一个JSON字节切片和一个指定目标格式的接口。而这个借口即与JSON字符串中的结果相匹配的结构体类型的变量。 定义结构体类型 type Person struct { ... }创建结构体变量…

Internet Download Manager(IDM6.41)安装教程+软件安装包下载

IDM是一款多线程下载工具&#xff0c;全称InternetDownloadManager。IDM的多线程加速功能&#xff0c;能够充分利用宽带&#xff0c;所以下载速度会比较快&#xff0c;而且它支持断点续传。它的网站音视频捕获、站点抓取、静默下载等功能&#xff0c;也特别实用。 安 装 包 获 …

堆的实现及其应用

堆的概念 堆是完全二叉树&#xff0c;分为大堆和小堆。大堆&#xff1a;任何一个父亲都大于等于孩子&#xff0c;小堆&#xff1a;任何一个父亲都小于等于孩子。 堆的实现 目录 typedef int HPDataType;typedef struct Heap { HPDataType* a;int size;int capacity; }HP;//交…

java写一个验证码

生成验证码 内容&#xff1a;可以是小写字母&#xff0c;也可以是大写字母&#xff0c;还可以是数字 规则 长度为5 内容中是四位字母&#xff0c;1位数字。 其中数字只有1位&#xff0c;但是可以出现在任意的位置。 package User;import java.util.ArrayList; import jav…

MFC动态创建按钮

void CMFCApplication1Dlg::OnBnClickedOk() {for (int i 0; i < 100; i){for (int j 0; j < 100; j){CButton* pButton3 new CButton;pButton[i][j] pButton3;}}CRect rect;GetClientRect(&rect); // 获取对话框客户区的大小rect.top 10; // 设置按钮的位置rec…

【培训】企业档案管理专题(私货)

导读&#xff1a;通过该专题培训&#xff0c;可以系统了解企业档案管理是什么、为什么、怎么做。尤其是对档案的价值认知&#xff0c;如何构建与新质生产力发展相适应的企业档案工作体系将有力支撑企业新质生产力的发展&#xff0c;为企业高质量发展贡献档案力量&#xff0c;提…

02-adb的工作原理和常见命令

一、什么是adb&#xff1f; ADB 是 Android Debug Bridge 的简称&#xff0c;是 Android 平台的调试工具。通过 adb 命令可以去获取安卓设备上的一些信息&#xff0c;也可以直接操作管理 Android 模拟器或者真实的 Android 设备。 ADB 采用客户端-服务端程序架构&#xff0c;简…

12306 火车票价格解析 (PHP 解析)

1. 从接口拿数据 日期 出发站 终点站 都填上 xxx/otn/leftTicketPrice/queryAllPublicPrice?leftTicketDTO.train_date2024-06-15&leftTicketDTO.from_stationBJP&leftTicketDTO.to_stationSJP&purpose_codesADULT 返回的数据是这样的 {"validateMess…

格式工厂转换mp3失败,原因和解决方法来了

在使用格式工厂进行音频转换时&#xff0c;有时候可能会遇到转换为MP3失败的问题。这可能会让人感到困扰&#xff0c;但幸运的是&#xff0c;这样的问题通常有一些常见的原因&#xff0c;而且大多数情况下都能够轻松解决。在本文中&#xff0c;我们将探讨一些可能导致格式工厂转…

【Linux】高级IO——五种IO方式,select,poll,epoll

文章目录 一、简单了解什么是IO及五种IO模式五种IO同步IO和异步IO区别1.阻塞IO&#xff08;张三钓鱼方式&#xff09;2.非阻塞IO非阻塞轮询&#xff08;李四钓鱼方式&#xff09;使用fcntl函数实现SetNonBlock非阻塞 二、IO多路转接——select&#xff08;赵六钓鱼方式&#xf…