附录 A:术语表
系列 :C# 与常用数据结构源码剖析 · 附录
使用方式:本表给出本专栏采用的精确口径。具体 API 仍以目标 TFM 契约、参考程序集和对应运行时版本为准。
运行时与工具链
| 术语 | 精确口径 |
|---|---|
| CLI | ECMA-335 定义的 Common Language Infrastructure,包含 CTS、元数据、CIL、虚执行系统等规范。它不等于某一个 .NET 实现。 |
| CLR | Common Language Runtime 的概念和产品称呼,通常指 .NET 执行引擎一类能力,不应用来暗指所有平台都使用 CoreCLR 源码。 |
| CoreCLR | 现代 .NET 中的主要运行时实现,位于 dotnet/runtime 的 src/coreclr,包含 VM、GC、RyuJIT 等。 |
| Mono | .NET 的另一运行时实现家族,现代源码在 dotnet/runtime/src/mono。Unity 的 Mono 需按 Unity 版本核验,不可自动等同于当前 upstream Mono。 |
| IL2CPP | Unity 的脚本后端:将托管 IL 转换为 C++,再使用平台原生工具链 AOT 编译。它不等于 CoreCLR JIT,也不会让托管对象自动变成无 GC 原生对象。 |
| Roslyn | C# / Visual Basic 编译器平台与公开分析 API。".NET 8 SDK 的 Roslyn"仍要用 SDK 完整版本和 Roslyn commit 锁定。 |
| IL / CIL | CLI 定义的中间指令集。反编译工具显示的 C# 是对 IL 和元数据的重建,不是原始源码或 Roslyn 内部树。 |
| JIT | 在运行期将 IL/中间表示编译为机器码。具体优化受运行时版本、分层编译、PGO、架构和调用形状影响。 |
| AOT | 在执行前产生机器码的策略总称。NativeAOT、ReadyToRun 与 IL2CPP 的管线、兼容性和产物不同。 |
| TFM | Target Framework Moniker,例如 net8.0。它指定编译时目标 API 表面,不唯一决定实际运行时实现和硬件。 |
| Reference assembly | 用于编译的 API 契约程序集,通常不包含可用的实现方法体。它回答"目标 TFM 能否调用"。 |
| Runtime / implementation assembly | 实际执行的运行时与类库实现。同一 TFM 可在不同 patch、OS、架构或 Unity 后端上展现不同私有实现和性能。 |
源码阅读与证据
| 术语 | 精确口径 |
|---|---|
| 公共契约 | 文档、语言/平台规范、reference assembly 与公开测试所承诺的可观察行为。应用可依赖契约,不应依赖私有字段名。 |
| 实现事实 | 固定版本中可从源码/二进制验证的算法、字段、阈值和优化;版本升级时可改变。 |
| Tag / commit | 用于锁定仓库状态的发行标签或提交 ID。tag 便于阅读发行版,commit SHA 提供更精确的可重现坐标。 |
main 分支 |
开发中的移动目标,可包含尚未发布改动。不能用当前 main 证明 .NET 8、.NET 9 或某 Unity 版本必然如此。 |
| 结构化节选 | 保留真实方法和关键分支,却删除条件编译、特性、快路径等细节的教学代码。必须标明不可直接替换原文件。 |
| 伪代码 / 概念转换 | 用来解释语义或算法,不承诺字段名、控制流、IL 或可编译性与任何发行版逐字相同。 |
| 可复现实验 | 同时保存源码、依赖、完整环境、输入分布、正确性 oracle、原始报告和随机种子的实验。 |
内存、引用与所有权
| 术语 | 精确口径 |
|---|---|
| GC | 根据可达性管理托管对象生命周期的机制。对象不可达不表示进程内存立即下降,Dispose 也不普遍等于回收托管对象。 |
| SOH | Small Object Heap,CoreCLR GC 中通常容纳未达大对象阈值的对象,包含分代区域。准确布局和策略必须按运行时验证。 |
| LOH | Large Object Heap,CoreCLR 将达到当前大对象阈值的特定对象放入的区域。常见阈值约 85,000 字节,但边界、对齐、数组特例、回收与压缩是版本/配置事实,不是业务契约。 |
| POH | Pinned Object Heap,CoreCLR 中用于某些预先固定对象的专用堆。一个对象被短期 fixed/pin 不意味着它必然搬到 POH。 |
| 写屏障 | 托管引用写入时配合 GC 维护卡表/记忆集等数据的运行时机制。它不是普通多线程 memory barrier 的同义词。 |
| 装箱 / 拆箱 | 值类型值转换为兼容引用表示,以及从装箱对象取回值类型值的操作。泛型、constrained call 和 JIT 可改变实际代价,不能仅根据接口名猜测。 |
ref struct |
受 C# ref-safety 规则约束、不能普通地装箱或作为任意堆对象字段的值类型。"只能存在栈上"是误导性简化:JIT 可使局部留在寄存器,Span<T> 也可指向托管堆、原生内存或 stackalloc 区域;关键是值本身不能逃逸出合法生命期。 |
Span<T> |
对连续 T 区域的借用视图,不拥有内存。创建/切片 Span 通常不需要为元素再分配数组,但这不保证整个方法、解析器或底层 owner 绝对零分配。 |
Memory<T> |
可存入堆对象并跨异步边界保存的连续内存视图。它不是 owner,不会自动延长数组池租期或 IMemoryOwner<T> 生命期。 |
| Owner | 对资源生命期与最终释放/归还负唯一责任的逻辑主体。owner 可是数组本身的持有者、IMemoryOwner<T>、原生容器或业务协议的当前层。 |
| View / borrow | 不负责释放底层资源的视图/借用。其有效期不得超过 owner,也不自动获得不可变或线程安全。 |
| Pinning | 使托管对象在某时段不被移动,以便取得稳定地址。pin 的作用域必须覆盖 native 使用,长期 pin 的成本需按 GC 实现测量。 |
类型系统与泛型
| 术语 | 精确口径 |
|---|---|
| 值类型 | 变量通常直接包含该类型的值表示;复制变量会复制值。值类型可以位于对象字段、数组元素、栈帧或寄存器,不能简化成"总在栈上"。 |
| 引用类型 | 变量保存对对象的托管引用,多个变量可以指向同一对象。复制引用不会深复制对象;对象位置及 GC 行为由运行时决定。 |
| 对象身份 | 两个引用是否指向同一对象的关系。身份相等与成员值相等是两种问题,不能用业务 Equals 代替所有引用身份判断。 |
| 值相等 | 根据类型定义比较内容或业务键。若类型用于哈希集合,Equals 为真必须推出 GetHashCode 相同;反向不要求成立。 |
record |
编译器生成值相等、打印、解构或克隆相关成员的一类声明形式;具体合成成员随 record class/struct 和自定义成员变化。它不自动深度不可变。 |
| 可空值类型 | Nullable<T>,表示值类型的"有值/无值"状态。它与可空引用类型注解不是同一种运行时表示。 |
| 可空引用类型 | C# 编译期流分析与注解系统,主要通过警告表达 null 契约;它不会给引用包一层运行时容器,也不阻止反射、旧程序集或错误抑制传入 null。 |
| 泛型具现化 | CLI 元数据保留类型参数与闭合构造,运行时再决定具体代码特化、共享和调用适配。不能把它概括成"每个 T 都复制一份机器码"。 |
| 泛型代码共享 | 多个泛型实例复用一份机器码,并通过运行时上下文处理类型相关信息。引用类型常可共享,值类型和 AOT 后端的具体策略需按实现验证。 |
| 泛型约束 | 对类型参数可用操作和允许实参的静态限制,如 class、struct、unmanaged、接口或构造约束。约束不是运行时性能保证。 |
unmanaged |
递归不含托管引用的类型约束,允许某些指针和 sizeof 用法。它不等于任意 ABI 下都 blittable,也不保证 packing、bool 宽度或端序匹配。 |
| 方差 | 泛型接口或委托在引用转换中的协变/逆变规则。可变集合通常保持不变性;数组协变是历史规则,会把部分类型错误推迟到运行时。 |
ref / byref |
对存储位置的受限别名,而不是普通对象引用。其可写性、逃逸范围、跨调用和跨挂起点规则由 C# ref-safety 约束。 |
in / ref readonly |
以只读 byref 方式传递或返回位置,主要避免某些大值复制并限制写入。编译器仍可能为非 readonly 成员调用产生防御性复制。 |
dynamic |
将成员绑定推迟到运行时 binder 的静态类型标记。它不是反射的别名,也不保证在裁剪/AOT 下任意调用形状都可用。 |
| Blittable | 托管和非托管表示可按目标互操作规则直接复制的一类类型。定义受封送器/API/平台影响,不应仅凭 unmanaged 约束推断。 |
集合与算法
| 术语 | 精确口径 |
|---|---|
Count |
当前逻辑元素数。它不等于底层数组长度、容量、哈希表 occupancy 或进程内存占用。 |
Capacity |
容器在再次扩容前可容纳的元素规模或内部数组长度。预留容量能减少扩容,但会增加驻留内存,且不保证整条路径零分配。 |
| 扩容 | 分配更大存储并复制/重建索引的过程,通常是单次 O(n)。支持扩容的集合仍可能给出连续插入的均摊 O(1)。 |
| 负载因子 | 哈希表有效条目或占用槽位与容量的关系,用于决定扩容与探测成本。具体阈值属于固定版本实现,不是 Dictionary 公共契约。 |
| 哈希冲突 | 不相等键产生相同桶位置或哈希码的情况。哈希表必须继续用相等比较区分;存在冲突不表示实现错误。 |
| Comparer | 定义排序关系的对象;结果应满足目标算法要求的自反/反对称/传递等性质。排序相等不必等于对象身份相同。 |
| Equality comparer | 成对定义相等与哈希的对象。相等键必须同哈希;错误或可变 comparer 会破坏查找语义,而不只是变慢。 |
| 可变键 | 进入哈希表后,参与哈希或相等的状态仍可改变的键。修改后条目仍位于旧桶,可能导致查找/删除失败,应使用不可变键或移除后重插。 |
| 冲突链 | 分离链接哈希表中连接同桶候选 Entry 的索引链。它不是对象链的必然形式,也不是所有哈希表实现都采用的方案。 |
| 开放寻址 | 条目直接存储在槽数组中,冲突后按探测序列寻找其他槽。删除通常需要墓碑或等价状态以维持既有探测链。 |
| Free list | 容器内部记录可复用槽位的链或索引结构。逻辑删除可以先复用槽位而不缩小底层数组;它与 GC free list 不是同一概念。 |
| 稳定顺序 | 对相等键或保留元素维持原相对顺序的性质。稳定不等于跨运行时确定性,也不等于容器承诺持久枚举顺序。 |
| 结构修改版本 | 集合用来让枚举器检测不允许修改的计数器或令牌。它是 fail-fast 诊断机制,不是线程同步或事务隔离。 |
| 迭代器 / 枚举器 | 分别表示可枚举序列的生成逻辑和一次遍历游标。延迟执行可能重复运行副作用;枚举器是否为 struct、是否装箱取决于静态调用形状。 |
| 物化 | 将延迟序列完整读取到数组、列表、查找表等存储。它建立快照边界并付出时间/内存,不应与"只创建查询描述"混为一谈。 |
| 结构共享 | 不可变集合的新旧版本复用未变化节点。它减少整棵复制,却仍会为修改路径分配节点,也不让节点内引用对象自动不可变。 |
| 环形缓冲 | 用固定或可增长数组配合头尾索引表达逻辑连续队列。发生回绕时逻辑顺序分成两个物理区间,不能把整个有效区当单一切片。 |
| 堆 | 满足父子优先级关系的树形数组结构,常用于优先队列。它不提供完整排序;同优先级元素顺序也通常不稳定。 |
| 红黑树 | 通过颜色、根、红节点与黑高等不变式维持 O(log n) 高度的平衡搜索树。它不是"每次操作后绝对完美平衡"。 |
异步与并发
| 术语 | 精确口径 |
|---|---|
| 线程安全 | 在声明的并发使用范围内保持公共契约。它不自动意味着复合操作原子、公平、无锁、可重入或高吞吐。 |
| 原子操作 | 对其他观察者不可见中间状态的单个契约操作。两个分别原子的步骤组合后通常不再原子,需要锁、CAS 循环或更高层协议。 |
| CAS | Compare-And-Swap/CompareExchange:值仍等于期望值时才更新。失败后通常要重新读取并重算;一次 CAS 不是完整无锁算法。 |
| ABA 问题 | CAS 只看到值从 A 又回到 A,无法发现中间经历过 B。可用版本戳、不可复用节点或适合平台的回收协议处理。 |
| 争用 | 多个执行者竞争同一锁、原子位置、缓存行或资源造成等待与一致性流量。无显式锁仍可能有原子争用。 |
| 伪共享 | 线程修改逻辑上独立、物理上处于同一缓存行的数据,导致缓存行反复转移。字段相邻不保证一定发生,需结合布局和硬件计数验证。 |
| Happens-before | 用来推理写入何时对另一执行者可见的顺序关系。时间上"先执行"不等于语言/运行时内存模型已建立可见性。 |
Task |
表示一次异步操作的最终完成、结果或异常,可被多个观察者等待。Task 不等于线程,也不保证工作已转移到线程池。 |
ValueTask |
可避免特定同步完成路径 Task 分配的值型 awaitable。实例通常有更严格的消费规则;无证据地全面替换 Task 会增加复杂度。 |
| Awaiter | 实现 GetAwaiter 协议并决定完成检查、continuation 注册和结果获取的对象/结构。await 的调度语义来自具体 awaiter,而不是关键字固定保证。 |
| 异步状态机 | 编译器把 async 方法重写成的状态与恢复逻辑。只有跨挂起点仍活跃的值需要保存;真正挂起时状态机可获得堆上稳定身份。 |
| Continuation | 等待完成后恢复的后续工作。在哪个线程、上下文或 PlayerLoop 阶段执行取决于 awaiter、上下文和调度器。 |
SynchronizationContext |
提供环境特定 continuation 投递策略的抽象。捕获上下文不等于对象仍存活,也不保证某个 Unity PlayerLoop 阶段。 |
| 取消 | 通过 token 等信号请求协作终止的协议。收到请求不表示底层 I/O 已停止、资源已释放或任务一定进入 Canceled 状态。 |
| 完成 / 故障 / 取消 | 三种需要分别建模的终止维度。管道 writer 完成不等于所有读者取消;某次等待取消也不等于共享通道关闭。 |
| Async stream | 以 IAsyncEnumerable<T>/IAsyncEnumerator<T> 表达可异步等待下一项的序列。它仍需处理取消、DisposeAsync、重复枚举和生产速度。 |
| Channel | 同时建模读写端、等待、容量、完成和故障的生产消费原语。它不是"加锁 Queue"的同义词,顺序和丢弃策略由选项决定。 |
性能测量
| 术语 | 精确口径 |
|---|---|
| 延迟 | 单次操作从开始到结束的时间,需说明计时边界和分布。平均延迟不能代替 P95/P99 等尾部指标。 |
| 吞吐 | 单位时间完成的工作量。批处理可提高吞吐却增加单项等待;吞吐和延迟不是可互换的一个数字。 |
| 帧时间 | 一帧各 CPU/GPU 阶段耗时及其同步关系。FPS 是倒数式汇总,会掩盖偶发慢帧和主线程尖峰。 |
| CPU time / wall-clock | CPU time 是线程实际消耗处理器的时间;wall-clock 包含等待、调度和 I/O。阻塞优化可能降低其中一个却不改变另一个。 |
| 分配率 | 单位时间或每操作分配的托管字节/对象数,不等于当前存活堆、工作集或 GC 暂停。 |
| Retained memory | 因可达引用链仍被保留的内存。对象池可能降低分配率却提高 retained memory,二者必须分别观察。 |
| 工作集 | 操作系统当前驻留于物理内存的进程页近似量,不等于托管堆大小,也不等于进程所有虚拟地址空间。 |
| GC 暂停 | 应用线程因回收协调而停顿的时间与分布。分配量、堆大小和暂停相关但不能互相直接换算。 |
| 基线 | 修改前在固定环境、输入和正确性条件下保存的原始结果。没有同口径基线,优化后的单个数字无法归因。 |
| 微基准 | 隔离小段代码机制成本的实验。它不能自动代表真实请求、Unity PlayerLoop、缓存争用、I/O 或长时间 GC 稳态。 |
| 暖机 | 让 JIT tier、动态 PGO、缓存、池和数据页进入目标状态。冷启动也是需求时,应另设实验而不是用暖机覆盖。 |
| 观察者效应 | profiler、日志、Deep Profile、分配堆栈或硬件计数器改变被测程序时序与成本。需保存无采集基线并控制采样强度。 |
| 正确性 oracle | 判断优化前后结果等价的参考实现、校验和或业务断言。漏处理工作会虚假变快,性能测试必须先关闭这个漏洞。 |
| 死代码消除 | 编译器/JIT 移除不可观察计算。基准必须消费结果并保持输入等价,不能把"工作被删掉"报告成算法收益。 |
| 统计显著与工程显著 | 前者讨论差异是否超出采样噪声,后者讨论收益是否足以影响预算并承担复杂度。统计可检出不等于值得上线。 |
| SIMD | 一条指令处理多个数据通道的执行方式。收益依赖数据连续性、循环规则、尾部、溢出、误差、硬件和后端,不保证线性加速。 |
Unity 专用边界
| 术语 | 精确口径 |
|---|---|
| Editor / Player | Editor 运行包含编辑器自身逻辑、检查和宿主开销;Player 才接近发布环境。Editor 结果不能替代目标设备构建。 |
| Scripting Backend | Unity 将脚本程序集执行/转换的后端选择,如 Mono 或 IL2CPP。它与 API Compatibility Level、Burst 和平台原生编译器是不同维度。 |
| API Compatibility Level | Unity 提供给脚本编译的参考 API 表面。它不是把 Unity 运行时切换成相同名字的桌面 .NET,也不保证所有包/平台实现相同。 |
| Managed Stripping | 构建时移除静态分析认为不可达的托管代码和元数据。反射、序列化和动态泛型路径需提供静态可达证据或精确保留规则。 |
[Preserve] / link.xml |
向 Unity linker 表达保留意图的机制,主要处理裁剪可达性。它们不会给 IL2CPP 增加任意 JIT,也不能自动生成所有 AOT 泛型调用。 |
| Burst | 面向受支持 C# 子集和数据导向代码的编译器。Job 加 [BurstCompile] 不保证自动 SIMD,也不允许普通托管 API 任意进入 Burst 路径。 |
| Job System | 用 JobHandle 依赖和容器访问声明组织并行工作的系统。调度不是立即完成;主线程提前读写或 Dispose 会违反依赖契约。 |
NativeArray<T> |
带原生存储、Allocator 和安全句柄语义的 Unity 容器。它不是"更快的数组"标签,必须明确所有者、Job 依赖与 Dispose 时机。 |
| Allocator.Temp / TempJob / Persistent | Unity 原生分配器的生命周期策略。名称不是建议性的注释,跨越允许期限或提前释放都属于正确性问题。 |
| Collections Safety Checks | 开发配置中帮助发现越界、并发访问和生命周期错误的检查。发布关闭检查不表示原契约消失,只是错误更难被及时诊断。 |
| Unity fake null | UnityEngine.Object 重载相等运算,用托管壳与原生对象状态表达"已销毁"。obj == null 与 obj is null 可能回答不同问题。 |
| PlayerLoop | Unity 主线程各阶段的更新循环。continuation 位于主线程不等于位于指定 Update/LateUpdate 阶段;需用具体 awaitable/调度 API 表达。 |
GC.Alloc |
Unity Profiler 在其采集边界内记录的托管分配指标。它不覆盖所有原生分配、驻留内存和生命周期问题,也会受 Development/Profiler 配置影响。 |
| ScriptableObject | 具有 Unity 资产身份和序列化规则的对象,适合作为编辑配置源。共享资产不是免费不可变运行时状态,后台线程也不应直接操作 Unity 对象。 |
数据结构、并发与性能
| 术语 | 精确口径 |
|---|---|
| 不变式 | 容器所有公开操作前后必须保持的性质,例如 Count <= Capacity、树的排序/颜色条件、环形队列的头尾关系。 |
| 均摊复杂度 | 将扩容等偶发高成本分摊到一系列操作后的平均上界。"均摊 O(1)"不表示每次操作的延迟都为常数。 |
| 线性化点 | 并发操作在其调用与返回之间可被视为瞬时生效的抽象时刻。可线性化不等于全局公平、无锁或等待自由。 |
| 线性化 | 并发历史可重排为符合实时先后约束的合法顺序历史。必须结合每个 API 的契约判定,不能仅因为使用 Interlocked 就声称线性化。 |
| Lock-free | 系统整体总有某个操作能在有限步内取得进展,不保证每个线程不饥饿。"内部有锁"、"无锁"和"线程安全"是不同维度。 |
| Memory barrier | 对内存操作的顺序和可见性施加约束的机制。C#/.NET 中应从 lock、Volatile、Interlocked等公开契约推理,不直接把 CPU 指令名当语言保证。 |
| 背压 | 当消费跟不上生产时,将容量压力沿协议传回上游的机制。"有界"不自动等于背压:TryWrite 失败或 Drop 模式可表示拒绝/丢弃,只有上游遵守等待/重试协议才会传导压力。 |
| Snapshot | 在某个逻辑时点提供稳定观察的数据。浅快照只复制元素/引用,不保证被引用的可变对象不再改变。 |
| Stable sort | 比较相等的元素在排序后保留原相对顺序。它与确定性输出、线程安全或原地排序是不同属性。 |
| SSA | Static Single Assignment,中间表示中每个定义版本只赋值一次的形式,便于数据流优化。它是 JIT/编译器内部表示,不是 C# 源码契约。 |
| PGO | Profile-Guided Optimization,使用采样或训练数据指导优化。动态 PGO、静态 profile 与 Unity AOT 工具链不可混为一种实现。 |
易混术语对照
| 容易混淆 | 正确区分 |
|---|---|
| C# 版本 vs TFM vs runtime | 语言版本决定语法和 lowering;TFM/reference assembly 决定可调用 API;runtime/backend 决定执行与优化。 |
| 只读 vs 不可变 | 只读视图只限制当前访问路径;不可变对象要求所有可观察状态都不能经其他别名改变。 |
Dispose vs GC |
Dispose 结束外部资源或租约;GC 回收不可达托管对象。二者可能协作,但没有普遍的一一对应。 |
| Span vs Memory vs owner | Span 是短期 ref-like view;Memory 是可保存 view;owner 才负责底层资源有效期和释放。 |
| pin vs lock | pin 防止对象移动;lock 建立互斥和内存可见性。pin 不防并发写,lock 也不自动提供稳定原生地址。 |
| equality vs ordering | Equality comparer 决定等价类和哈希;Comparer 决定前后顺序。排序为零不应随意当成哈希相等。 |
| 平均 O(1) vs 均摊 O(1) | 平均/期望通常依赖输入或哈希分布;均摊把一个操作序列中的偶发高成本分摊,两者证明方法不同。 |
| thread-safe vs atomic | 类型可在线程安全范围内并发使用,不表示"先检查再修改"的复合业务步骤是原子的。 |
| lock-free vs wait-free | lock-free 保证系统整体进展;wait-free 还要求每个操作在有界步数完成,后者更强。 |
| deferred vs streaming | 延迟执行表示消费时才运行;流式表示可在未读完整输入前逐项产出。OrderBy 延迟但通常不流式。 |
| snapshot vs immutable | 快照固定某时点的容器观察;若只复制引用,元素对象仍可能变化。不可变要求更强的状态约束。 |
| AOT vs NativeAOT vs IL2CPP | AOT 是类别;NativeAOT 和 IL2CPP 是不同工具链。相同"无 JIT"属性不能推出相同泛型、GC、反射或 ABI。 |
| Profiler sample vs benchmark | profiler sample 定位真实场景热点;基准隔离并量化机制。二者互补,不能相互替代。 |
| allocation vs leak | 分配是创建存储;泄漏是无业务价值却仍被保留。高分配可迅速回收,低分配也可持续泄漏。 |
使用规则与反例
- 先写证据层级。 "规范要求""公开 API 契约""v8.0.0 源码观察""本机 Release 实验"不能写成同一种事实。例如,字段名
_items是实现事实,索引越界失败才是公共契约。 - 复杂度必须带操作和条件。 不写"Dictionary 是 O(1)",应写"在 comparer 正确且哈希分布合理时,查找期望 O(1),退化输入最坏可达 O(n)"。
- 性能结论必须带环境。 不写"foreach 比 for 慢"或"Burst 快十倍",应记录静态类型、运行时/后端、构建模式、设备、输入规模和原始报告。
- 所有视图都追问 owner。 看到 Span、Memory、原生指针、池数组或 NativeArray 时,必须回答谁拥有、何时失效、能否跨 await/Job、谁负责 Return/Dispose。
- 并发 API 要写复合语义。
ConcurrentDictionary的单次方法线程安全,不代表ContainsKey后索引器更新原子;应选用满足整体操作的 API 或外部同步。 - 取消不写成强制终止。 token 发出协作请求后,底层工作可能继续。调用方仍需观察任务、处理迟到结果并释放资源。
- AOT 问题先分裁剪和代码生成。
link.xml能保留元数据/成员,却不保证任意运行时构造的闭合泛型已有可执行代码。 - Unity 结论至少绑定四项。 Editor 完整版本、API Compatibility Level、Mono/IL2CPP、目标平台缺一不可;涉及 Burst/Collections 时再记录包版本与 Safety Checks。
- 反例也要保持可编译边界。 故意错误的代码应明确标为不可编译、运行时失败或逻辑错误,不能让读者猜它属于哪一类。
- 术语不能替代验证。 "零分配""无锁""不可变""线程安全""原生性能"都只是待证明的具体属性,不是看到某个类型名即可附上的标签。
上一篇 :附录概要
下一篇 :附录 B:源码索引速查