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

作者: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.mzph.cn/web/27089.shtml

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

相关文章

借助ChatGPT撰写学术论文,如何设定有效的角色提示词指

大家好,感谢关注。这个给大家提供关于论文写作方面专业的讲解,以及借助ChatGPT等AI工具如何有效辅助的攻略技巧。有兴趣的朋友可以添加我(yida985)交流学术写作或ChatGPT等AI领域相关问题,多多交流,相互成就…

12. Django 第三方功能应用

12. 第三方功能应用 因为Django具有很强的可扩展性, 所以延伸了第三方功能应用. 通过本章的学习, 读者能够在网站开发过程中快速实现API接口开发, 验证码生成与使用, 站内搜索引擎, 第三方网站实现用户注册, 异步任务和定时任务, 即时通信等功能.12.1 Django Rest Framework框…

【区块链】记账的千年演化:从泥板到区块链

🌈个人主页: 鑫宝Code 🔥热门专栏: 闲话杂谈| 炫酷HTML | JavaScript基础 ​💫个人格言: "如无必要,勿增实体" 文章目录 记账的千年演化:从泥板到区块链引言一、古代记账:泥板与…

2024 年勒索软件将比以往更加残酷

如今,世界各地的人们去学校、去医院或去药店时,都会被告知:“抱歉,我们的计算机系统瘫痪了。” 罪魁祸首往往是在世界另一端活动的网络犯罪团伙,他们会要求人们支付系统访问费用或安全归还被盗数据。 尽管警方加大打…

[vue2]深入理解vuex

本节内容 概述初始化仓库定义数据访问数据修改数据处理异步派生数据模块拆分案例-购物车 概述 vuex是一个vue的状态管理工具, 状态就是数据 场景 某个状态在很多个组件使用 (个人信息)多个组件 共同维护 一份数据 (购物车) 优势 数据集中式管理数据响应式变化 初始化仓库 …

Vue36-组件化编程的概念

一、组件化编程VS传统编程 1-1、传统方式的编写应用 存在的问题: 1-2、组件方式的编写应用 注意:是引入,不是复制! 体现了封装的概念! 二、模块化、组件化

SonarQube安全扫描常见问题

目录 一、SonarQube质量报告 二、SonarQube扫描常见问题和修复方法 三、SonarQube质量配置 最近小编在使用SonarQube工具进行代码扫描,检查代码异味,系统漏洞等,实际过程中也遇到了不少问题,这篇文章主要列举我遇到的常见问题和…

Android Jetpack Compose 实现一个电视剧选集界面

文章目录 需求概述效果展示实现思路代码实现总结 需求概述 我们经常能看到爱奇艺或者腾讯视频这类的视频APP在看电视剧的时候都会有一个选集的功能。如下图所示 这个功能其实很简单,就是绘制一些方块,在上面绘制上数字,还有标签啥的。当用户…

流程与IT双驱动:锐捷网络如何构建持续领先的服务竞争力?

AI大模型及相关应用进入“竞赛时代”,算力作为关键要素备受关注,由于算力行业对网络设备和性能有较大需求,其发展也在推动ICT解决方案提供商加速升级,提升服务响应速度和服务质量。 锐捷网络是行业领先的ICT基础设施及行业解决方…

Spark groupByKey和reduceByKey对比

在 Apache Spark 中,groupByKey 和 reduceByKey 都是用于对键值对 (key-value) 数据集进行分组和聚合的操作。然而,它们在性能和使用场景上有显著的差异。 groupByKey 函数 groupByKey 将数据集中的所有键相同的值进行分组,然后返回一个键值…

Error:Kotlin: Module was compiled with an incompatible version of Kotlin.

一、问题:运行spring boot项目时,idea报出错误:时提示报错如下图: 错误代码: Error:Kotlin: Module was compiled with an incompatible version of Kotlin. The binary version of its metadata is 1.6.0, expected …

未来AI大模型的发展趋势

大家好,我是小悟 未来AI大模型的发展趋势无疑将是多元化、高效化、普及化以及人性化。随着技术的飞速进步,AI大模型将在各个领域中展现出更加广泛和深入的应用,成为推动社会进步的重要力量。 多元化是AI大模型发展的重要方向。随着数据量的…

FastAPI系列 4 -路由管理APIRouter

FastAPI系列 -路由管理APIRouter 文章目录 FastAPI系列 -路由管理APIRouter一、前言二、APIRouter使用示例1、功能拆分2、users、books模块开发3、FastAPI主体 三、运行结果 一、前言 未来的py开发者请上座,在使用python做为后端开发一个应用程序或 Web API&#x…

java:使用JSqlParser给sql语句增加tenant_id和deleted条件

# 示例代码 【pom.xml】 <dependency><groupId>com.baomidou</groupId><artifactId>mybatis-plus-core</artifactId><version>3.4.3.1</version> </dependency>【MyJSqlParserTest.java】 package com.chz.myJSqlParser;pu…

请求headers处理

自学python如何成为大佬(目录):https://blog.csdn.net/weixin_67859959/article/details/139049996?spm1001.2014.3001.5501 有时在请求一个网页内容时&#xff0c;发现无论通过GET或者是POST以及其他请求方式&#xff0c;都会出现403错误。产生这种错误是由于该网页为了防止…

phpStudy里面的MySQL启动不了

C:\Users\Administrator>netstat -an | find "3306" TCP 0.0.0.0:3306 0.0.0.0:0 LISTENING TCP 0.0.0.0:33060 0.0.0.0:0 LISTENING TCP [::]:3306 [::]:0 LISTENING TCP [::]:33060 [::]:0 LISTENING 从你提供的输出结果可以看到&#xff0c;端口3306和33060已经…

python中的turtle

turtle个别指令 初始箭头默认指向为东&#xff08;右&#xff09; 往前&#xff08;右&#xff09;三个格&#xff1a;turtle.forward(3) 往后&#xff08;左&#xff09;三个格&#xff1a;turtle.backward(3) 往左转90度&#xff1a;turtle.left(90) 往右转90度&#xf…

r语言数据分析案例25-基于向量自回归模型的标准普尔 500 指数长期预测与机制分析

一、背景介绍 2007 年的全球经济危机深刻改变了世界经济格局&#xff0c;引发了一系列连锁反应&#xff0c;波及各大洲。经济增长停滞不前&#xff0c;甚至在某些情况下出现负增长&#xff0c;给出口导向型发展中国家带来了不确定性。实体经济受到的冲击尤为严重&#xff0c;生…

ATFX汇市:日本央行维持0.1%利率不变,植田和男发言偏鹰

ATFX汇市&#xff1a;北京时间11:25&#xff0c;日本央行公布6月利率决议结果&#xff0c;宣布维持0~0.1%的基准利率区间不变&#xff0c;此前市场预期其将再次加息。消息公布后&#xff0c;USDJPY的5分钟内从157.09上涨至157.70&#xff0c;涨幅61基点。25分钟之后&#xff0c…