16-02-C#常用数据结构源码-附录B-源码索引速查

附录 B:源码索引速查

系列 :C# 与常用数据结构源码剖析 · 附录

基线 :主体集合以 dotnet/runtime tag v8.0.0(commit 5535e31a712343a63f5d7d796cd874e563e5ac14)为研究基线;泛型 OrderedDictionary<TKey,TValue> 以 v9.0.0(commit 9d5a6a9aa463d6d10b0b0ba6d5982cc82f363dc3)为基线。目录和私有 helper 可随版本变化,使用时先 checkout tag,再用 rg 搜索类型/方法。

一、先锁定版本

复制代码
git clone https://github.com/dotnet/runtime.git
cd runtime
git fetch --tags
git checkout v8.0.0
git rev-parse HEAD
# 5535e31a712343a63f5d7d796cd874e563e5ac14

# 研究 .NET 9 泛型 OrderedDictionary 时使用独立 worktree,避免混淆文件
git worktree add ../runtime-v9 v9.0.0
# runtime-v9 HEAD: 9d5a6a9aa463d6d10b0b0ba6d5982cc82f363dc3

一篇可复核的源码分析至少记录:仓库 URL、tag、git rev-parse HEAD、文件路径、目标 TFM、SDK/runtime 完整版本和架构。若 tag 与实际部署 patch 不同,应换成对应 servicing tag/commit,而不用 main 猜测。

二、System.Private.CoreLib 核心集合(v8.0.0)

主题 可检索入口(相对 dotnet/runtime) 建议搜索
Array 公共 API src/libraries/System.Private.CoreLib/src/System/Array.cs Copy、Clear、BinarySearch、Sort
数组泛型接口辅助 src/coreclr/System.Private.CoreLib/src/System/Array.CoreCLR.cs SZArrayHelper、IList<T>、GetEnumerator
List<T> src/libraries/System.Private.CoreLib/src/System/Collections/Generic/List.cs _items、AddWithResize、RemoveAll、Enumerator
LinkedList<T> src/libraries/System.Collections/src/System/Collections/Generic/LinkedList.cs head、InternalInsertNodeBefore、InternalRemoveNode、Invalidate
Dictionary<TKey,TValue> src/libraries/System.Private.CoreLib/src/System/Collections/Generic/Dictionary.cs Entry、TryInsert、FindValue、Resize、StartOfFreeList
HashSet<T> src/libraries/System.Private.CoreLib/src/System/Collections/Generic/HashSet.cs FindItemIndex、AddIfNotPresent、IntersectWith、CheckUniqueAndUnfoundElements
Queue<T> src/libraries/System.Private.CoreLib/src/System/Collections/Generic/Queue.cs _head、_tail、MoveNext、SetCapacity
Stack<T> src/libraries/System.Collections/src/System/Collections/Generic/Stack.cs PushWithResize、Pop、TryPop、Enumerator
PriorityQueue<TElement,TPriority> src/libraries/System.Collections/src/System/Collections/Generic/PriorityQueue.cs Arity、MoveUp、MoveDown、EnqueueDequeue
SortedSet<T> src/libraries/System.Collections/src/System/Collections/Generic/SortedSet.cs AddIfNotPresent、Remove、InsertionBalance、TreeSubSet
SortedDictionary<TKey,TValue> src/libraries/System.Collections/src/System/Collections/Generic/SortedDictionary.cs TreeSet、KeyValuePairComparer、KeyCollection
SortedList<TKey,TValue> src/libraries/System.Collections/src/System/Collections/Generic/SortedList.cs keys、values、Array.BinarySearch、Insert
Span<T> src/libraries/System.Private.CoreLib/src/System/Span.cs Slice、GetPinnableReference、Enumerator
Memory<T> src/libraries/System.Private.CoreLib/src/System/Memory.cs Span、Slice、Pin
ArrayPool<T> 抽象 src/libraries/System.Private.CoreLib/src/System/Buffers/ArrayPool.cs Shared、Rent、Return、Create
共享数组池实现 src/libraries/System.Private.CoreLib/src/System/Buffers/SharedArrayPool.cs Rent、Return、线程本地槽、修剪

Array.Copy、数组分配、JIT intrinsic 或 GC 写屏障的完整路径可跨越托管层与 CoreCLR VM/JIT/GC。从 Array.cs 开始,根据目标 tag 中的 [Intrinsic]、[InternalCall]、QCall/FCall 标记或 JIT 识别点继续搜索,不用一段旧 C++ 伪码代表全链路。

三、并发与不可变集合(v8.0.0)

主题 可检索入口 建议搜索
ConcurrentDictionary<TKey,TValue> src/libraries/System.Collections.Concurrent/src/System/Collections/Concurrent/ConcurrentDictionary.cs Tables、TryAddInternal、GrowTable、AcquireAllLocks
ConcurrentQueue<T> 外壳 src/libraries/System.Private.CoreLib/src/System/Collections/Concurrent/ConcurrentQueue.cs _head、_tail、Enqueue、TryDequeue、快照协调
ConcurrentQueueSegment<T> src/libraries/System.Private.CoreLib/src/System/Collections/Concurrent/ConcurrentQueueSegment.cs Slot、SequenceNumber、TryEnqueue、TryDequeue、EnsureFrozenForEnqueues
ConcurrentStack<T> src/libraries/System.Collections.Concurrent/src/System/Collections/Concurrent/ConcurrentStack.cs PushCore、TryPopCore、Node
ConcurrentBag<T> src/libraries/System.Collections.Concurrent/src/System/Collections/Concurrent/ConcurrentBag.cs WorkStealingQueue、LocalPush、TrySteal
BlockingCollection<T> src/libraries/System.Collections.Concurrent/src/System/Collections/Concurrent/BlockingCollection.cs IProducerConsumerCollection、CompleteAdding、TakeFromAny
ImmutableArray<T> src/libraries/System.Collections.Immutable/src/System/Collections/Immutable/ImmutableArray_1.cs Builder、ToImmutable、Interlocked
ImmutableList<T> src/libraries/System.Collections.Immutable/src/System/Collections/Immutable/ImmutableList_1.cs Node、Balance、Builder
ImmutableDictionary<TKey,TValue> src/libraries/System.Collections.Immutable/src/System/Collections/Immutable/ImmutableDictionary_2.cs HashBucket、MutationInput、Builder
ImmutableHashSet<T> src/libraries/System.Collections.Immutable/src/System/Collections/Immutable/ImmutableHashSet_1.cs HashBucket、MutationInput、Builder、Union

某些文件在不同 tag 中可在 System.Private.CoreLib 与独立 library 之间调整。路径不存在时,使用 rg --files src/libraries | rg '/ConcurrentQueue\.cs$' 或搜索类声明,不为了保持附录文字而强行应用错路径。

四、.NET 9 泛型 OrderedDictionary

主题 v9.0.0 检索方法 边界
System.Collections.Generic.OrderedDictionary<TKey,TValue> src/libraries/System.Collections/src/System/Collections/Generic/OrderedDictionary.cs 使用 .NET 9 reference assembly 确认 GA API,再用 v9.0.0 解释顺序存储与 key 索引的私有实现。
旧非泛型 OrderedDictionary src/libraries/System.Collections.Specialized/src/System/Collections/Specialized/OrderedDictionary.cs namespace、API、装箱和实现均不同,不能与 .NET 9 泛型类型混用证据。

IAlternateEqualityComparer<TAlternate,T> 与 Dictionary alternate lookup 属于后续框架/API 演进主题。本索引不将它标为 .NET 6 或 .NET 8 的普遍公共契约;研究时必须用目标 reference assembly 编译并锁定对应 runtime tag。

五、CoreCLR、Mono 与设计文档

主题 目录/检索入口
RyuJIT src/coreclr/jit/;从 compiler.h、importer、morph、VN、lowering、codegen 相关文件与 jit-diff 工具入手
CoreCLR GC src/coreclr/gc/;搜索 gc.cpp、gcpriv.h、heap/segment/generation/pinning 等符号
CoreCLR VM/类型系统 src/coreclr/vm/;搜索 MethodTable、EEType、array、generic dictionary、dispatch 等主题
Mono src/mono/;按 runtime、metadata、mini/JIT、interpreter 与 GC 具体主题搜索
ReadyToRun / crossgen2 src/coreclr/tools/aot/crossgen2/;结合目标 tag 搜索 compilation、dependency graph、R2R emission 与命令行入口
NativeAOT src/coreclr/nativeaot/ 与 src/coreclr/tools/aot/;按 compiler、runtime、ILCompiler、object writer 等目标主题搜索
原生宿主 src/native/corehost/;搜索 hostfxr、hostpolicy、runtimeconfig、deps 与框架解析
CoreCLR BOTR docs/design/coreclr/botr/;用于建立概念图,再与目标 tag 源码相互校验
库设计文档 docs/design/libraries/ 与 src/libraries/*/README.md;目录随 tag 变化,用 rg --files docs 查找

SOH/LOH/POH 的 segment、压缩、修剪和内存压力策略必须从指定 GC 版本、配置、架构和实验解释。本附录不列".NET 8 LOH 分段优化"这类没有已锁定 PR、测试和运行时证据的笼统变更。

六、Roslyn 与 Unity 源码边界

Roslyn 不在 dotnet/runtime 仓库。使用 dotnet/roslyn,根据 SDK 中 Microsoft.CodeAnalysis*.dll 的版本/commit 信息 checkout 对应源码,然后从以下入口搜索:

主题 可检索入口(相对 dotnet/roslyn)
C# Syntax src/Compilers/CSharp/Portable/Syntax/ 及 Syntax/InternalSyntax/
Binder / Symbols src/Compilers/CSharp/Portable/Binder/、Symbols/
Bound Tree / Lowering src/Compilers/CSharp/Portable/BoundTree/、Lowering/;具体文件按 commit 搜索
Emit / PDB src/Compilers/Core/Portable/Emit/ 与 C# Emit 相关目录
Analyzer / Generator API src/Compilers/Core/Portable/DiagnosticAnalyzer/、SourceGeneration/ 及 SDK 公开 API 测试

Unity 的 Mono/IL2CPP/Burst/Collections 版本与引擎发行绑定。桌面 dotnet/runtime v8.0.0 可用作算法参考,不可作为 Unity 私有字段、GC、JIT/AOT 或 API 可用性的直接证据。必须记录 Unity Editor 完整版本、包版本、API Compatibility Level、脚本后端和目标 Player。

七、从"我想知道什么"到"证据在哪里"

源码索引只解决入口问题。真正的源码研究应从一个可观察问题出发,而不是从"打开一个很长的文件"出发。例如,"Dictionary.Remove 之后为什么不立即缩容"是可以用公开行为、字段不变式和分配实验共同回答的问题;"Dictionary 内部怎么写"则太宽,容易变成字段抄录。

7.1 先判定证据层级

同一句结论可能需要四种完全不同的证据:

问题 首要证据 不能单独作为证据的材料
某 TFM 能否调用成员 reference assembly、API 文档、实际编译 main 分支已有该方法
公开行为是什么 文档、规范、公开测试与黑盒实验 某个私有 helper 的名字
固定版本如何实现 tag/commit 下的源码、测试和构建产物 另一版本的博客截图
性能为何如此 固定环境的 trace、反汇编、分配与基准 源码看起来"只有几行"

这个分层可避免两类常见错位:用私有实现猜 API 契约,或用 API 契约猜私有布局。前者会让应用依赖随时可变的细节,后者则会把不同运行时强行画成同一张图。

7.2 一条可复用的追踪链

研究一个集合操作时,可按以下顺序前进:

  1. 写出公开语义:先记录返回值、异常、顺序、线程安全和枚举失效规则,暂时不看字段。
  2. 找公开入口:在类声明中定位成员,注意同名重载、显式接口实现和部分类文件。
  3. 画调用小图:只记录与问题有关的 helper、快路、慢路和异常边,不复制整个调用树。
  4. 提取不变式 :对比操作前后的 Count、容量、链接、版本号、owner 与可达性,说明每个写入为何必要。
  5. 读同 tag 测试:搜索类名、成员名、异常类型和边界值。测试常比注释更清楚地表明作者要守住什么。
  6. 用最小程序封闭回路:黑盒断言公开行为;如果研究性能,再增加分配、事件、JIT 证据或 heap snapshot。

只有第 2‑4 步属于"读实现"。第 1、5、6 步让结论可以被外部观察和反例检验,否则很容易把一段可读的伪代码当成事实。

7.3 搜索时不要只搜类名

类名适合找入口,控制流和不变式往往要搜其他线索。查异常路径时搜异常资源或 throw helper;查枚举失效时搜 _version 的所有写入;查清引用时搜 IsReferenceOrContainsReferences、Clear 和 default;查并发发布时搜 Volatile、Interlocked、lock 以及不变表的整体替换。还应搜对应测试中的极端输入,如空集合、自身作为参数、比较器冲突、枚举期修改和容量上限。

八、四个典型追踪案例

8.1 Dictionary 删除:一次操作同时改两条链

从 Remove 入口开始,先找它如何用哈希和 comparer 定位桶,再记录当前节点和前驱节点。命中后不只是"把 key/value 清空":桶内冲突链需要越过被删槽位,而被删槽位又要编码进 free list。此时 _count 仍可以是已启用区间上界,公开 Count 则要结合 _freeCount 理解。

验证不应反射断言某个负数常量永久不变。更稳定的做法是使用恒定哈希 comparer 构造长冲突链,删除链头、中间和链尾,分别断言其他键仍可查找;再插入新键,用分配或容量观察证明槽位能复用。这样即使下一版改变 free list 的具体编码,测试仍能守住算法目的。

8.2 ConcurrentQueue:外层段链与段内协议要分开

先读 ConcurrentQueue.cs 中头尾段、新段链接和快照协调,再进入 ConcurrentQueueSegment.cs 读槽位序号、head/tail 票号与环形映射。如果只读外层,会看到"取当前尾段并尝试入队",却无法证明两个生产者为什么不会同时发布同一槽位。如果只读段内,又会漏掉段冻结、增长和旧段保活。

这个案例还说明"成功出队会清引用"为什么不能写成无条件句子。快照式枚举或 TryPeek 为了保持可观察内容,可使某些段暂时保留 item。正确的实验是分开验证 FIFO/不丢不重的功能压力测试与对象保活的 heap 实验,不用一张堆快照反推无锁正确性。

8.3 ImmutableList:追踪"哪些节点没有变"

持久化集合的阅读重点不是数了多少个 new Node,而是标出修改路径和未访问子树。对 SetItem、Insert 或 RemoveAt 选一个操作,沿索引导航到目标,记录返回新根时哪些祖先被重建、哪些左右子树引用被原样复用。如果平衡因子越界,再只展开局部旋转,不要把旋转误写成整树拷贝。

Builder 需要另走一条证据链。它能否原地改写取决于节点是否由当前构建阶段独占,而不是"Builder 返回可变 List"。实验时保留几个旧快照,对 Builder 执行批量修改并发布新版,最后再断言所有旧版内容未变。这比反射查 _frozen 值更接近应用真正依赖的契约。

8.4 Array.Copy:托管入口不是执行链终点

先在 Array.cs 找公开入口和参数检查,再看固定 tag 中的 intrinsic、internal call 或运行时识别点。不同元素形状可走不同路径:纯字节搬运、引用复制需要 GC 协议,数组协变还可能需要赋值兼容检查。只看 C# 入口的方法长度,无法推导最终复制指令、屏障成本或重叠区域语义。

验证时至少分开纯值类型、引用类型、含引用字段的值类型和可能触发协变检查的数组。功能测试覆盖同数组重叠拷贝与边界;性能测试再固定长度、架构、GC 和运行时版本。这样才能解释"语义相同的 Copy"为何对不同 T 表现出不同实现成本。

九、源码阅读中的十个常见误读

  1. 把文件路径当 API 契约:类可以跨 partial 文件、平台特定文件和生成代码;文件搬家不等于公开行为改变。
  2. 把方法名当实际调用次数:JIT 可内联,helper 又可分出快慢路;一个源码调用点不对应固定机器间接次数。
  3. 把私有字段当序列化格式 :存档、网络和签名协议必须由显式 DTO 与规范定义,不能跟随 _entries 或枚举顺序。
  4. 用 main 证明旧发行版 :main 可含尚未发布的 API、已回退的实验或新平台文件。历史结论必须回到 tag/commit。
  5. 用 tag 名代替部署版本 :v8.0.0 适合统一教学基线,却不自动等于生产机的 servicing patch。调查故障时要对齐实际 runtime commit。
  6. 只看正常路径:比较器抛异常、容量溢出、自身集合运算、取消和竞争失败才常是不变式被真正考验的地方。
  7. 把结构化节选当可替换源码:教学片段常删除 null 检查、溢出、架构分支与异常恢复,只能用于说明主干。
  8. 看到 lock 就否定"无锁快路":进度保证要精确到操作。单元素快路可为 lock-free,快照、扩容或全局观察仍可使用锁。
  9. 用源码行数排性能:分支可被折叠,小方法可内联,一行 intrinsic 可进入大量运行时代码。成本必须由最终执行路径和数据分布解释。
  10. 把桌面 CoreCLR 路径移植到 Unity:即使类名相同,Unity 的类库代际、Mono fork、IL2CPP 转换和 GC 也可不同;共用业务库要只依赖共同公开契约。

十、版本升级时如何复核

从 .NET 8 升到新 runtime 时,不应用"文件还在、所以结论不变"作为审查。一次高效复核可分五层:

10.1 API 与参考程序集

先编译项目实际使用的成员,检查新增、过时、可空注解、泛型约束和重载决议。这一层回答"代码能不能以相同语义编译",与私有算法 diff 分开。

10.2 符号级 diff,而不是逐行噪声

在旧新 tag 中先按类和方法列出关注符号,再对这些符号的字段读写、调用 helper 和测试集做差异。大量格式化、命名和文件搬迁可以先排除。重点记录不变式是否改变:例如容量选择、冲突处理、版本号更新、空闲槽位表示、快照协议和并发发布顺序。

10.3 实现测试与业务回归

运行新 tag 对应的上游测试,但不要把上游测试当作业务协议的替代品。项目仍要覆盖自己的 comparer、序列化、并发拓扑、容量峰值、Unity Player 和 AOT/裁剪路径。上游测试证明维护者想守住的实现行为,业务回归证明你的依赖没有被破坏。

10.4 性能重建基线

将旧新 runtime 放在同一硬件和发布配置下,固定数据集、预热、PGO/R2R、GC 和采样方式。同时保存正确性校验和原始报告,不只保存 Ratio。如果结果改变,先用分配、代码版本、分支与缓存证据定位,再将它归因于某个源码 diff。

10.5 更新结论的表述范围

最终记录三类结果:公开契约是否改变,私有实现是否改变,项目实测是否改变。只有第一类适合直接写成跨实现依赖;第二类必须继续携带 tag/commit;第三类必须携带环境和输入。"升级后快了"不是完整结论,"在某负载下,某代码版本的分配或分支改变"才是可继续复核的记录。

十一、练习:把索引变成可验证结论

  1. List 容量练习 :追踪 Add、AddRange、EnsureCapacity 与 TrimExcess,写出 Count/Capacity 不变式;用空集合、已知 ICollection<T>、未知长度枚举和自身追加做差分测试。
  2. Dictionary 冲突练习 :不反射私有数组,用可计数 comparer 统计成功/失败查找的 Equals 次数,解释冲突链增长与比较成本的关系。
  3. Queue 环绕练习 :构造 head 不为零且有效区间跨数组末尾的状态,验证 ToArray、枚举和扩容仍保持 FIFO 逻辑顺序。
  4. ConcurrentQueue 观察练习:在生产/消费并发压力中长时间持有一个枚举器,比较持有前后的对象保活;不将保活现象误判为 FIFO 错误。
  5. ImmutableList 旧版练习 :保存随机旧版,连续执行插入、删除和替换,每一步与普通 List<T> 参考模型对比,并在最后重新验证所有旧版。
  6. Array.Copy 路径练习:对字节、引用和含引用结构分别测试重叠拷贝与大数组拷贝;区分语义测试、分配事件和最终机器码证据。
  7. 跨版本练习:选一个容器在两个发行 tag 中做符号级 diff,产出一页报告:契约变化、实现变化、测试变化、项目是否受影响。
  8. Unity 边界练习:选一个桌面 .NET 源码结论,列出将它用于 Unity Player 前必须补齐的 Editor、BCL、Mono/IL2CPP、平台、构建和 Profiler 证据,不直接复制桌面性能数字。

完成练习的标准不是"找到了某个私有字段",而是能用一句有范围的结论连接契约、固定版本实现和可重复实验,并能说明哪个反例会推翻它。

上一篇 :附录 A:术语表

下一篇 :附录 C:参考资源

相关推荐
海天鹰2 小时前
C#入门:在cmd显示字符
c#
速易达网络3 小时前
无人机远程操控仿真
游戏引擎·cocos2d
郝学胜-神的一滴3 小时前
游戏引擎原理与实践 02:揭开3A游戏背后的技术面纱
开发语言·计算机网络·程序人生·游戏·游戏引擎
警醒与鞭策12 小时前
【无标题】
android·unity·性能优化·游戏引擎·perforce
zhchyun200813 小时前
【Unity UI 进阶】仿 Element UI 打造企业级 Unity UI 组件库(10)
ui·unity·游戏引擎
波力海苔夹心脆67513 小时前
C# 序列化与反序列化详解:System.Text.Json、Newtonsoft.Json、XmlSerializer 用法、特性选项与安全实践
经验分享·后端·c#·json·.net
地狱为王18 小时前
Unity使用NVIDIA的Audio2Face驱动口型
unity·游戏引擎
小羊没烦恼!19 小时前
在Scrum中实施敏捷建模
java·开发语言·windows·算法·c#
Lost of 程序猿20 小时前
建造者模式实战:告别“十参数构造函数“的数据导出任务
后端·设计模式·c#·asp.net