我用数字枚举定义状态,本以为类型很安全,结果一个根本不在枚举里的数字 99 被当成合法状态溜了进来,遍历枚举时还冒出一堆奇怪的数字键,我对着 TypeScript 数字枚举这两个坑排查了大半天的复盘

一个让我对 TypeScript 的 enum 从信任到警惕的坑,隐蔽在 enum 看起来是个限定取值范围保证类型安全的好东西,我以为用了它一个变量就只能是枚举里那几个值,可它既没拦住非法的值溜进来又在运行时生成了一堆我没料到的东西。用数字枚举 enum Status { Active, Inactive, Pending }(0,1,2)。坑1:数字枚举类型居然能接受任意数字,setStatus(99)、const x: Status = 99 都不报错,99 根本不是任何 Status 却通过了类型检查,外部来的非法状态值就这么合法地流进了业务。坑2:for in 遍历枚举本以为得到三个名字,结果多冒出 0,1,2 三个数字键。深究才明白:出于历史和位运算兼容原因 TS 对数字枚举处理较宽松、允许把任意 number 赋给它不严格校验,枚举限定范围的保护失效;数字枚举编译成 JS 会生成双向映射对象(既有名字到数字又有数字到名字),所以遍历多出数字键、且 enum 不是纯类型会生成运行时代码有体积成本;字符串枚举没有这些坑(无反向映射、类型更严、值可读)。这篇从故障现场、数字枚举两坑真相、正解(字符串枚举、字面量联合类型+as const 对象最安全最干净零运行时、外部数据仍运行时校验、必须用数字枚举要过滤数字键校验范围)、TS 类型与运行时其他坑(类型擦除、enum 非纯类型、readonly 浅层、声明合并)、几种枚举方案对照表、看似安全的特性反而最危险、决策图与铁律,到附上一段打印运行时产物+边界值试探看清数字枚举两坑和字面量联合对比的实验。核心领悟:一个特性的名字承载我们的先验期待但和它在具体语言里的实际行为未必一致,仅凭名字推断信任是危险的,要主动确认它在当前语境的真实定义和行为;一个看起来提供某种保证实际却没完全提供的东西比明确说不提供的更危险,会用虚假安全感麻痹警惕;了解一个需求的多种方案及其演进、采用社区当前更优实践(字面量联合替代数字枚举);用打印运行时产物+边界值试探类型检查看清特性真实行为。

我用数字枚举定义状态,本以为类型很安全,结果一个根本不在枚举里的数字 99 被当成合法状态溜了进来,遍历枚举时还冒出一堆奇怪的数字键,我对着 TypeScript 数字枚举这两个坑排查了大半天的复盘

这是一个让我对 TypeScript 的 enum "从信任到警惕"的坑。它隐蔽在:enum 看起来是个"限定取值范围、保证类型安全"的好东西,我以为用了它,一个变量就只能是枚举里那几个值了;可它既没拦住非法的值溜进来,又在运行时生成了一堆我没料到的东西

需求很常见:我用一个枚举来表示状态。我顺手用了数字枚举(最常见的写法):

enum Status {
    Active,      // 0
    Inactive,    // 1
    Pending,     // 2
}
// 默认数字枚举: Active=0, Inactive=1, Pending=2

function setStatus(s: Status) { /* 处理状态 */ }

// ★ 坑1: 数字枚举类型, 居然能接受任意数字!
setStatus(Status.Active);   // ✓ 正常
setStatus(99);              // 😱 居然不报错! 99根本不是任何一个Status, 却通过了类型检查!
const fromApi: Status = 99; // 😱 也不报错! 非法值就这么"合法地"流进来了

// ★ 坑2: 遍历枚举, 冒出一堆奇怪的键
for (const key in Status) {
    console.log(key);
}
// 期望: Active, Inactive, Pending
// 实际: 0, 1, 2, Active, Inactive, Pending  ← 多了一堆数字键?!
//   (因为数字枚举编译后生成了"双向映射"的对象)

我盯着这两个现象,大跌眼镜。坑 1:我以为 Status 类型只能是 Active/Inactive/Pending,可 setStatus(99)const x: Status = 99 这种传一个根本不在枚举里的数字,TypeScript 居然不报错!于是一个从外部来的非法状态值(99),就这么"合法地"通过了类型检查、流进了业务逻辑,埋下隐患。坑 2:我遍历这个枚举,本以为会得到 Active、Inactive、Pending 三个名字,结果多冒出了 0、1、2 这三个数字键!这个"数字枚举既不类型安全、又在运行时生成奇怪的东西"的现象,让我对 enum 这个看似简单的特性彻底改观。

第一件事:看清真相——数字枚举接受任意 number,且编译成双向映射的运行时对象

我去深入研究了 TypeScript 数字枚举的类型行为和它编译后的样子,才彻底明白这两个坑——第一,出于历史和兼容原因,TypeScript 的数字枚举类型,实际上可以接受任意 number(它把数字枚举看得比较"宽松",不严格限定在定义的那几个值);第二,数字枚举编译成 JS 后,会生成一个"双向映射"的对象(既有"名字→数字",也有"数字→名字"),所以遍历它会多出数字键,运行时也多了这些代码

数字枚举两个坑的真相

# 坑1: 数字枚举类型可以接受【任意number】, 不类型安全!
#   enum Status { Active, Inactive, Pending }   // 0,1,2
#   const x: Status = 99;   // ✓ 不报错! 99不是任何Status, 却被接受了!
#   - 原因: TypeScript对数字枚举的处理比较"宽松"(历史/位运算兼容等原因),
#     它允许把任意number赋给数字枚举类型, 不严格校验"必须是定义的那几个值"。
#   - 后果: 外部传入的、计算出的非法数字(如99), 能"合法地"通过类型检查,
#     枚举"限定取值范围"的保护作用, 在这里【失效了】。

# 坑2: 数字枚举编译成JS后, 生成【双向映射(reverse mapping)】对象:
#   enum Status { Active, Inactive }
#   ↓ 编译成 JS 后大致是:
#   var Status = {};
#   Status[Status["Active"] = 0] = "Active";    // 既有 Active->0
#   Status[Status["Inactive"] = 1] = "Inactive";//      又有 0->"Active"
#   → 所以 Status 对象里既有 {Active:0, Inactive:1} 也有 {0:"Active", 1:"Inactive"}
#   - 后果a: for...in 遍历会同时遍历到 数字键 和 名字键(多了一堆);
#   - 后果b: enum 不是"纯类型", 它会生成运行时代码(对象), 有体积/运行时成本;
#     (这也是为什么有 const enum——它会被内联、不生成对象, 但有别的限制。)

# 3. 对比: 【字符串枚举】没有这些坑:
#   enum Status { Active = "active", Inactive = "inactive" }
#   - 字符串枚举【不生成反向映射】(没有 "active"->Active 这种);
#   - 类型上也更安全(不能把任意字符串赋给它);
#   - 遍历干净, 值也更可读(日志里是"active"而非0)。

# 核心: TS数字枚举有两坑——类型上能接受任意number(不安全, 非法值能溜进来)、编译生成双向映射
#   对象(遍历多出数字键、有运行时代码); 字符串枚举或字面量联合类型更安全、更干净。

真相大白,我恍然大悟。第一个坑:出于历史和兼容(位运算等)原因,TypeScript 对数字枚举的处理比较"宽松"——它允许把任意 number 赋给数字枚举类型,不严格校验"必须是定义的那几个值";所以 const x: Status = 99 不报错,外部传入的非法数字(99)能"合法地"通过类型检查,枚举"限定取值范围"的保护在这里失效了第二个坑:数字枚举编译成 JS 后,会生成一个"双向映射(reverse mapping)"的对象——既有 {Active:0, Inactive:1}(名字→数字),又有 {0:"Active", 1:"Inactive"}(数字→名字);所以 for...in 遍历会同时遍历到数字键和名字键(多了一堆),而且 enum 不是纯类型、会生成运行时代码(对象)、有体积成本(这也是 const enum 的由来)。对比之下,字符串枚举没有这些坑:enum Status { Active = "active" } 不生成反向映射、类型上更安全(不能把任意字符串赋给它)、遍历干净、值也更可读(日志里是 "active" 而非 0)

第二件事:正解——用字符串枚举,或字面量联合类型 + as const 对象

搞懂了原理,正解就清晰了:优先用字符串枚举(更安全、无反向映射);或用"字面量联合类型 + as const 对象"这种更轻量、更类型安全、零运行时枚举对象的方案;外部数据仍要运行时校验

// ====== 正解一: 用字符串枚举(比数字枚举安全干净) ======
enum Status {
    Active = "active",
    Inactive = "inactive",
    Pending = "pending",
}
const x: Status = "active" as Status;
// const y: Status = "xxx";   // ✗ 字符串枚举不能随便赋任意字符串(比数字枚举严格)
// 优点: 无反向映射、遍历干净、值可读(日志是"active"); 类型也更安全。

// ====== 正解二(很多人更推荐): 字面量联合类型 + as const 对象 ======
// 用 as const 定义常量对象, 再从中推导出联合类型:
const Status = {
    Active: "active",
    Inactive: "inactive",
    Pending: "pending",
} as const;
type Status = typeof Status[keyof typeof Status];   // "active" | "inactive" | "pending"

function setStatus(s: Status) { /* ... */ }
setStatus("active");          // ✓
// setStatus("xxx");          // ✗ 编译错误! 只能是那三个字面量之一, 真正类型安全!
// setStatus(99 as any);      // 只有强行as any才能绕过(那是你自找的)
// 优点: 类型上是精确的字面量联合(最安全)、Status对象就是普通对象(无反向映射)、
//   遍历干净、可tree-shaking、值可读; 是现代TS里很流行的"枚举替代方案"。

// ====== 正解三: 外部数据仍要运行时校验(类型只是编译期) ======
// 不管用哪种, 从API/外部来的状态值, 运行时仍要校验它是不是合法状态:
function isStatus(v: unknown): v is Status {
    return v === "active" || v === "inactive" || v === "pending";
}
// (因为类型是编译期的, 运行时的非法值要靠运行时校验拦, 见"数据泄漏/as断言"等篇)

// ====== 如果一定要用数字枚举 ======
// - 知道它能接受任意number、有反向映射的坑;
// - 遍历时过滤掉数字键: Object.keys(E).filter(k => isNaN(Number(k)))
// - 对外部数字仍要校验是否在枚举值范围内。

// 核心: 优先用字符串枚举或"字面量联合类型+as const对象"(类型安全、无反向映射、干净); 数字枚举
//   有"接受任意number+双向映射"的坑要少用; 外部数据无论如何都要运行时校验是否合法值。

修复的核心,是"用字符串枚举或字面量联合类型替代数字枚举,外部数据仍运行时校验"正解一:用字符串枚举——enum Status { Active = "active" },无反向映射、遍历干净、值可读、类型也更安全(不能随便赋任意字符串)正解二(很多人更推荐):字面量联合类型 + as const 对象——as const 定义常量对象,再 type Status = typeof Status[keyof typeof Status] 推导出精确的字面量联合类型("active"|"inactive"|"pending");setStatus("xxx") 会编译报错、真正类型安全,且 Status 就是普通对象(无反向映射)、可 tree-shaking,是现代 TS 流行的枚举替代方案正解三:外部数据仍要运行时校验——类型只是编译期的,从 API 来的状态值运行时仍要用类型守卫校验是不是合法状态如果一定要用数字枚举:知道它的坑,遍历时过滤数字键,对外部数字校验范围归根结底:优先用字符串枚举或"字面量联合类型+as const 对象"(类型安全、无反向映射、干净);数字枚举有"接受任意 number+双向映射"的坑要少用;外部数据无论如何都要运行时校验。

第三件事:TypeScript 类型与运行时相关的其他坑

排查后我把 TypeScript "类型期望和运行时现实不符"的其他常见坑也系统梳理了一遍。

TS 类型与运行时的其他坑

# 1. 数字枚举接受任意number+反向映射(本文): 不安全+运行时对象。→ 字符串枚举/字面量联合。

# 2. 类型是编译期的, 运行时被擦除: 类型对≠运行时数据对(见as断言/数据校验篇)。

# 3. enum不是纯类型: 会生成运行时代码(对象), 有体积; 纯类型场景用union/as const。

# 4. readonly是编译期+浅层: 运行时仍可改、且只读外层(里层对象仍可变)。
#    → 真不可变用Object.freeze(浅)/deepFreeze/不可变库。

# 5. interface/type的声明合并: interface会自动合并同名声明(可能意外); type不会。

# 6. 类型推断过宽/过窄: 比如let推断为宽类型、字面量没加as const推断为string。

# 7. 可选属性?和undefined: a?: T 意味着可能是undefined, 访问要判空。

# 8. 把类型当运行时校验: 以为标了类型运行时就安全(外部数据并不会自动符合类型)。

# 共同根源: TS的类型系统是【编译期】的、会被擦除; 而enum等少数特性又会生成运行时代码;
#   分不清"哪些是纯编译期的类型、哪些有运行时行为、类型保证到运行时还成不成立", 就会踩坑。

# 核心: 分清TS的编译期类型和运行时行为; 数字枚举/readonly等有"编译期保证≠运行时现实"的坑;
#   优先用类型安全又轻量的方案(字面量联合)、外部数据运行时校验、真不可变用freeze。

排查让我把 TS 类型与运行时的其他坑也梳理清了。一、数字枚举接受任意 number+反向映射(本文)。二、类型编译期擦除(类型对≠运行时数据对)。三、enum 不是纯类型(生成运行时对象,纯类型用 union/as const)。四、readonly 是编译期+浅层(运行时仍可改、只读外层,真不可变用 freeze)。五、interface 的声明合并(自动合并同名声明)。六、类型推断过宽/过窄七、可选属性 ? 可能是 undefined八、把类型当运行时校验它们的共同根源是:TS 的类型系统是编译期的、会被擦除;而 enum 等少数特性又会生成运行时代码;分不清"哪些是纯编译期类型、哪些有运行时行为、类型保证到运行时还成不成立",就会踩坑核心是:分清 TS 的编译期类型和运行时行为;优先用类型安全又轻量的方案(字面量联合)、外部数据运行时校验、真不可变用 freeze下面这张图,是这次数字枚举的成因与解法:

第四件事:几种"枚举"方案对照表

这次踩坑后,我把 TS 里几种表示"固定取值集合"的方案整理成一张表。

方案 类型安全 运行时 说明
数字枚举 ✗ 接受任意number 有对象+反向映射 坑多, 少用
字符串枚举 ✓ 较安全 有对象, 无反向映射 比数字枚举好
const enum 同枚举 内联, 不生成对象 省体积, 但有限制/不能遍历
字面量联合类型 ✓ 最安全 纯类型, 零运行时 类型层面最干净
as const 对象 + 联合 ✓ 最安全 普通对象, 可遍历 既安全又能拿到值集合, 推荐

这张表把"枚举方案"的取舍钉清了。核心是:数字枚举类型最不安全(接受任意 number)、运行时还有反向映射;字符串枚举好一些;而"字面量联合类型"和"as const 对象+联合"既类型安全又干净(后者还能在运行时拿到值的集合),是现代 TS 里更受推崇的方案它给我的最大启发是:同一个需求("表示一组固定取值"),TypeScript 提供了好几种方案,而语言/社区推荐的"最佳方案",会随着语言演进而变化——早期大家都用 enum,后来发现它的种种坑,社区逐渐转向了"字面量联合类型 / as const 对象"这种更轻量、更安全的方案这让我意识到一个学习态度:对一个常见需求,不要满足于"我会用某一种老办法"(如 enum),而要了解这个领域里有哪些方案、它们各自的优缺点、以及社区当前更推荐哪种;技术在演进,曾经的"标准做法"可能已经有了更好的替代,跟上这种演进,能让你写出更现代、更健壮的代码了解一个需求的多种方案及其演进、采用社区当前更优的实践(字面量联合替代数字枚举)——是这个枚举坑教给我的进阶意识。

第五件事:为什么"看起来安全"的特性反而最危险

这次让我反思了一个有点反直觉的点:数字枚举的危险,恰恰在于它"看起来很安全"。

方面 说明
名字暗示安全 "枚举"听起来就是"限定在这几个值", 让人放心
常规用法都正常 用 Status.Active 这种规范写法时一切正常
给人虚假的保护感 用了枚举, 就以为取值范围被严格限制了
边界处才暴露 只有传非法数字、遍历时, 不安全才显现
比"明显不安全"更坑 明知不安全会防, 自以为安全反而不设防

这张表道出了数字枚举"看似安全实则不"的危险。核心是:数字枚举的危险,恰恰在于它的名字和常规用法都给人一种"很安全、取值被严格限定"的错觉;正因为我"以为它安全",我就放松了警惕、不再去防范非法值(没做运行时校验);而它实际并不安全——这种"自以为有保护、其实没有"的状态,比"明知没保护"更危险它给我的深刻启发是:一个"看起来提供了某种保证、实际却没完全提供"的东西,比一个"明确告诉你它不提供保证"的东西更危险;因为后者会让你主动去防范,而前者会用"虚假的安全感"麻痹你的警惕、让你疏于防范,最终在你最没防备时出问题这让我形成一个警觉:对那些"名字/外表暗示了某种保证"的特性(枚举=安全取值、readonly=不可变、private=外部访问不到),要主动去确认"它到底提供了多强的保证、有没有我以为的那么强",而不是仅凭名字和直觉就信任它;很多坑,正是源于"对一个并没有那么强保证的东西,给予了过强的信任"警惕"看似安全"的虚假保护感、主动确认特性的真实保证强度——是这个数字枚举坑,带给我的关于"如何不被表象误导"的清醒。

第六件事:要表示一组固定取值时,我现在的判断习惯

现在每当我要表示"一组固定的取值"(状态、类型、选项),我都会按这张图先想清楚:

这张图的精髓,是"优先用字面量联合/as const 对象,要 enum 也用字符串枚举,外部数据运行时校验"首选字面量联合类型或 as const 对象+联合(需要遍历值集合用后者、只要类型约束用前者);团队习惯用 enum 的话尽量用字符串枚举别用数字枚举,并知道它仍有运行时对象;外部数据无论如何运行时校验是否合法值这套习惯,让我表示固定取值时,从"随手数字 enum"变成了"优先字面量联合、要 enum 也用字符串"——核心始终是:数字枚举不安全且有运行时坑,优先字面量联合/as const,外部数据运行时校验。

我立下的几条规矩

这场"数字枚举不安全"的事故,换来了我写 TypeScript 时,刻进骨子里的几条铁律:

  1. 数字枚举类型能接受任意 number。非法值能溜进来,不类型安全。
  2. 数字枚举编译成双向映射对象。遍历多出数字键、有运行时代码。
  3. 优先用字面量联合类型 / as const 对象。类型安全、轻量、干净。
  4. 要用 enum 就用字符串枚举。比数字枚举安全、无反向映射、值可读。
  5. 外部数据无论如何要运行时校验。类型是编译期的,拦不住运行时非法值。
  6. 遍历数字枚举要过滤数字键。filter(k => isNaN(Number(k)))。
  7. 警惕"看似安全"的特性。主动确认它真实的保证强度。

附:一段亲眼看清数字枚举两个坑的实验

口说无凭。下面这段代码,把数字枚举的"接受任意数字"和"反向映射"两个坑,以及字符串枚举/字面量联合的对比,一次演示清楚:

enum NumStatus { Active, Inactive, Pending }          // 数字枚举 0,1,2
enum StrStatus { Active = "active", Inactive = "inactive" }  // 字符串枚举

console.log("=== 1. 数字枚举接受任意number(不安全) ===");
const bad: NumStatus = 99;            // ✓ 编译通过! 99根本不是合法状态
console.log("  bad =", bad);          // 99 —— 非法值就这么进来了

console.log("\n=== 2. 数字枚举的反向映射(遍历多出数字键) ===");
console.log("  Object.keys(NumStatus):", Object.keys(NumStatus));
// ["0","1","2","Active","Inactive","Pending"]  ← 多了数字键!
console.log("  NumStatus[0]:", NumStatus[0]);        // "Active" ← 反向映射: 数字->名字
console.log("  只取名字键:",
    Object.keys(NumStatus).filter(k => isNaN(Number(k))));  // ["Active","Inactive","Pending"]

console.log("\n=== 3. 字符串枚举没有反向映射(干净) ===");
console.log("  Object.keys(StrStatus):", Object.keys(StrStatus));
// ["Active","Inactive"]  ← 干净, 没有反向映射的多余键

console.log("\n=== 4. 字面量联合类型(类型最安全, 零运行时) ===");
type LitStatus = "active" | "inactive" | "pending";
const ok: LitStatus = "active";       // ✓
// const no: LitStatus = "xxx";       // ✗ 编译错误! 只能是那三个之一
// const no2: LitStatus = 99 as any;  // 只有强行as any能绕过
// (LitStatus是纯类型, 编译后完全消失, 零运行时成本)

// 核心: 跑一遍, 亲眼看到数字枚举能接受99、Object.keys多出数字键(反向映射)、
//   而字符串枚举遍历干净、字面量联合类型上根本不接受非法值——两个坑和替代方案一目了然。

这段实验代码,是我这次踩坑后写下的"枚举坑显形器"。它用四组对比,把数字枚举的两个坑摊在你眼前:第 1 段让你看到 const bad: NumStatus = 99 居然编译通过(非法值溜进来);第 2 段用 Object.keys 让你看到数字枚举遍历出来多了 "0"、"1"、"2" 这些数字键(反向映射的产物),还演示了 NumStatus[0] 能反查出 "Active";第 3 段对比字符串枚举的 Object.keys 干干净净;第 4 段则展示字面量联合类型从类型上就拒绝非法值、且零运行时。这正是我想用这段代码,留给每个 TS 开发者的核心方法:当你对一个特性(尤其涉及"它编译后变成什么、运行时行为如何"的)拿不准时,写几行代码把它的运行时产物直接打印出来(用 Object.keysconsole.log 看它真实的样子)、并用边界值(如非法的 99)去试探它的类型检查到底严不严因为一个特性"编译后到底生成了什么、运行时到底长什么样、它的类型检查到底拦不拦得住非法值",光看文档或凭想象常常不准;而把它的运行时产物打印出来、用边界值去试探,能让它的真实行为无所遁形;"打印产物 + 边界试探",是看清一个特性(尤其是有编译期/运行时双重身份的特性,如 enum)真实面目最直接的手段用"打印运行时产物 + 边界值试探类型检查"看清特性的真实行为——这份习惯,是我避免被特性的名字和文档表象误导、确认其真相最可靠的法门。

写在最后

回头看,这场由"数字枚举"引发的、非法值溜进来的事故,真正教给我的,远不止"用字符串枚举或字面量联合"这一个技巧。它让我对"不能仅凭一个特性的名字和外表,就推断它的行为",有了一次深刻的体会。我栽跟头,是因为我被 "enum(枚举)" 这个名字误导了。"枚举"这个词,在我的认知里(以及在很多其他语言里),天然意味着"一个被严格限定在若干个具名值之内的类型"——所以我想当然地以为 TypeScript 的数字枚举也是如此,会帮我把取值严格限制住。可我没有去确认 TypeScript 的数字枚举实际是怎么定义、怎么行为的,就凭着"枚举"这个名字给我的印象去信任它了——而它实际的行为(接受任意 number、生成双向映射),和我从名字推断的大相径庭这让我领悟到一个深刻的认知:一个特性/概念的"名字",承载着我们对它的先验期待(尤其当这个名字在别处有约定俗成的含义时);但这个"名字带来的期待",和它在这个具体语言/工具里的实际行为,未必一致;仅凭名字去推断、信任一个特性的行为,是危险的——你信任的是"你以为它该是的样子",而非"它实际的样子"这其实是一个反复出现的教训(就像 JS 的 sort 不按数值排、Go 的 ConcurrentModification 在单线程也报):对任何一个特性,尤其是那些"名字听起来很熟悉、似乎不言自明"的,都要带着一份审慎,去确认它在当前语境下的真实定义和行为,而不是用名字唤起的、来自别处的直觉去想当然;"名副其实"是一种美好的期待,但工程上,我们必须验证"名是否真的副其实",而非假设它一定如此不被特性的名字和外表误导、主动确认它在当前语境下的真实行为——这,是我用一次数字枚举的事故,换来的、关于 TypeScript、也关于如何严谨地认识任何技术特性的、最朴素也最深刻的领悟。如果这篇复盘,能让你下次想用数字 enum 时,先想起"它其实能接受任意数字哦"、转而用字面量联合类型,那我对着那个溜进来的非法值 99 排查的这大半天,就值了。

—— 别看了 · 2026
声明:本站所有文章,如无特殊说明或标注,均为本站原创发布。任何个人或组织,在未征得本站同意时,禁止复制、盗用、采集、发布本站内容到任何网站、书籍等各类媒体平台。如若本站内容侵犯了原著者的合法权益,可联系我们进行处理 邮箱1846861578@qq.com。
技术教程

我的服务运行越久内存涨得越凶,最后内存泄漏到崩溃,排查发现是一堆本该被回收的对象因为订阅了事件却没取消订阅、被发布者死死攥着无法回收,我对着 C# 事件订阅导致的内存泄漏这个坑排查大半天的复盘

2026-6-2 13:01:05

技术教程

我的 AI Agent 老是选错工具、参数也填得乱七八糟,我一度怀疑是模型不行,排查才发现是我给工具写的描述太含糊、模型根本看不懂该怎么用,我对着工具描述是模型理解工具的唯一窗口这个坑排查大半天的复盘

2026-6-2 13:14:18

0 条回复 A文章作者 M管理员
    暂无讨论,说说你的看法吧
个人中心
购物车
优惠劵
今日签到
有新私信 私信列表
搜索