我给一个联合类型加了个新的状态值,以为编译器会提醒我去所有用到它的地方补上处理,结果它一声不吭、那个新状态在好几个 switch 里被悄悄漏掉了:一次没用 never 做穷尽检查的深度复盘

我有个订单状态联合类型 type Status = 'pending' | 'paid',好几处用 switch 分别处理这两种。后来加了已退款状态、把类型改成 'pending' | 'paid' | 'refunded',我想当然以为 TS 会在所有没处理 refunded 的 switch 处报错提醒我。结果 TS 一声不吭、一个错都没报,那几个 switch 里 refunded 要么走到 default 要么没人处理,新状态被悄悄漏掉,直到线上退款订单行为异常才发现。复盘才明白:TS 默认并不检查 switch/if 是否穷尽处理了联合类型的所有成员,不完整的 switch 不报错(有 default 兜底时更不报)。要让它检查,得用 never 做穷尽检查——在 default 里写 const _:never=status;全部成员处理了 status 收窄为 never 合法编译通过,漏了某成员走到 default 的 status 就是那个字面量类型、赋给 never 编译报错,强制你去补上。我没用这个技巧,等于把加了新成员要去所有地方补处理的责任压在了自己记性上、而我漏了。这篇复盘从故障现场讲到 TS 默认不强制穷尽、never 穷尽检查的原理、几种写法,再到 switch 配 never、assertNever、Record 映射、ESLint 规则的完整正解,以及其他该让工具强制却靠人记得的坑,和一致性约束靠人记性必然遗漏、要让工具在漏改时强制报错、把人肉保证升级为机器保证的认知。

我给一个联合类型加了个新的状态值,以为编译器会提醒我去所有用到它的地方补上处理,结果它一声不吭、那个新状态在好几个 switch 里被悄悄漏掉了:一次没用 never 做穷尽检查、把"记得改全"的责任压在自己身上的深度复盘

那批"新加的退款状态在好几个地方都没生效"的 bug,源于我对 TypeScript 类型检查的一个过高期待。我有个订单状态的联合类型 type Status = 'pending' | 'paid',代码里好几处用 switch (status) 分别处理这两种状态。后来业务要加一个"已退款"状态,我把类型改成了 'pending' | 'paid' | 'refunded'。我想当然地以为:改了联合类型,TypeScript 这么强的类型系统,一定会在所有"没处理 refunded"的 switch 处报错提醒我,我跟着报错一个个补上就行。结果:TS 一声不吭、一个错都没报;那几个 switch 里,'refunded' 要么走到 default、要么干脆没人处理——新状态在好几个地方被悄悄漏掉了,直到线上退款的订单行为异常才被发现。复盘这件事,我才彻底明白:问题出在我以为"加了联合类型成员,编译器会强制我在所有地方处理它",但默认情况下 TS 并不会——一个不完整的 switch(没覆盖所有联合成员)默认不报错TypeScript 的类型检查,确实能帮你发现很多错误;但它不会自动检查"你的 switch/if 是否穷尽处理了联合类型的所有成员"——除非你显式地用一种技巧去"要求"它检查;这个技巧就是 never 类型做穷尽检查(exhaustiveness check):在 switch 的 default 分支里写 const _exhaustive: never = status;当所有成员都被 case 处理后,走到 default 的 status 类型会被收窄成 never(赋给 never 合法);可一旦你加了新成员(refunded)却没加对应 case,走到 default 的 status 就是 'refunded',把 'refunded' 赋给 never 类型 → 编译报错!——它就这样在编译期强制提醒你"这里还有个新成员没处理";没用这个技巧,等于把"加了新成员要记得去所有地方补上处理"这个容易遗漏的责任,完全压在了自己"记性"上——而我漏了。根本原因是:TS 默认不检查 switch/if 是否穷尽处理了联合类型的所有成员;加了新成员忘了在某处补 case 时编译不报错、悄悄漏处理;我没用 never 穷尽检查让编译器强制提醒,把"记得改全"的责任压在了自己记性上。问题的根,是没用 never 做穷尽检查——TS 默认不强制处理联合类型所有成员,加新成员漏处理不报错;应让编译器在漏处理时强制报错,而非靠人记得。这篇就把这次"穷尽检查缺失"的坑,从头到尾复盘一遍。

故障现场:加了新状态,switch 却不报错

问题在于没做穷尽检查,漏处理新成员不报错:

// 原来的联合类型 + switch
type Status = 'pending' | 'paid';

function getLabel(status: Status): string {
  switch (status) {
    case 'pending': return '待支付';
    case 'paid':    return '已支付';
    default:        return '未知';   // 我以为default兜底就万无一失
  }
}

// 后来加了新成员:
type Status = 'pending' | 'paid' | 'refunded';   // ← 加了 refunded

// 我以为: TS会在getLabel(及其他所有switch)处报错"没处理refunded"
// 实际: TS【一个错都不报】! getLabel('refunded') 走default返回'未知' —— 新状态被悄悄漏掉!
// 而项目里有好几个这样的switch, 全都漏了refunded, 直到线上退款订单行为异常才发现。

/*
为什么TS默认不报错:
  - 一个switch没覆盖联合类型的所有成员, TS默认【不认为是错误】(只是没处理那些情况而已);
  - 有default分支时更"安全"地兜底了, 反而更不会报——但default可能根本不是你想要的处理;
  - 所以"加了新成员"和"所有该处理它的地方"之间, TS默认【不帮你建立强制联系】;
  - 这个"记得去所有地方补上处理"的责任, 默认压在【开发者的记性】上——而人会忘。

never 穷尽检查(exhaustiveness check)是怎么强制的:
  - never 是"不可能有值"的类型; 当switch处理完所有联合成员后, default里的变量类型被收窄为never;
  - 在default里写 const _: never = status; —— 把它赋给never;
  - 全部成员都处理了 → status在default处是never → 赋值合法, 编译通过;
  - 漏了某个成员(如refunded) → status在default处是'refunded' → 'refunded'不能赋给never
    → 【编译报错】"Type 'refunded' is not assignable to type 'never'" —— 强制你去补上处理!

★ 核心: TS默认不检查switch/if是否穷尽处理联合类型所有成员; 用never做穷尽检查可让"漏处理新成员"
  在编译期强制报错; 别把"加了新成员要改全所有地方"的责任压在记性上, 让编译器替你兜住。

看着线上退款订单那些"状态显示未知、流程不对"的现象,再回想我加 refunded 时那句"编译器会提醒我的"的自信,我又懊恼又恍然:"我太高估 TS 了——我以为加了联合类型成员,它会逼着我去每个 switch 补上处理。可它默认根本不管这个,尤其我还都写了 default 兜底,它更不会报。这个'记得改全'的活儿,其实一直是我自己的,而我忘了。"这个坑最隐蔽的地方在于:编译完全通过、不报任何错,给你"类型系统已经帮我检查过了、没问题"的虚假安全感;尤其是有 default 分支时,新成员被 default "悄悄兜住"(返回了一个错误的默认值),连运行时都不一定立刻崩,只是行为不对——更难发现下面就来拆解,联合类型的穷尽处理该怎么保证。

第一件事:搞懂穷尽检查与 never

我顺着这次事故,把联合类型的穷尽检查彻底理清了。

TS默认不检查"是否处理了联合类型所有成员"? 怎么强制?

【核心: TS默认不强制switch/if穷尽处理联合类型所有成员、加新成员漏处理不报错; 用never做穷尽检查可让漏
   处理在编译期报错; 把"加新成员要改全"的责任从人的记性转移给编译器强制——让遗漏在编译期暴露】

1. 默认行为: TS不强制穷尽:
   - switch/if没覆盖联合类型的所有成员, 默认不报错;
   - 有default兜底时更不会报(但default可能不是你想要的正确处理);
   - 所以"联合类型加了成员"和"所有处理它的地方"之间, 默认没有强制联系。

2. never 穷尽检查的原理:
   - never 是"不可能有值"的底类型;
   - switch处理完所有成员后, 控制流分析会把default(或else)里的变量类型收窄为never;
   - 在default里: const _exhaustive: never = status;
     * 全处理了 → status是never → 合法, 编译通过;
     * 漏了成员 → status是那个漏掉的字面量类型 → 不能赋给never → 编译报错!

3. 几种穷尽检查的写法:
   ① switch default里: default: { const _: never = status; throw new Error(`未处理: ${status}`); }
   ② 抽个函数: function assertNever(x: never): never { throw new Error('未处理: '+x); }
      然后 default: return assertNever(status);
   ③ 用 satisfies / 详尽的if-else链, 最后else里赋never。

4. 配套手段:
   - 别滥用default兜底"吞掉"未知成员: default要么真的合理, 要么用穷尽检查暴露遗漏;
   - 开 tsconfig 的相关严格选项; 用ESLint规则(如@typescript-eslint/switch-exhaustiveness-check)
     也能在加成员漏case时报错(IDE/CI层面);
   - 联合类型/枚举集中定义, 处理它的地方有限且可控。

5. 本质: 把"容易遗漏的一致性约束"交给编译器强制, 而非靠人自觉
   - "加了新成员, 要在所有相关处理它的地方都补上"——这是个容易遗漏的一致性约束;
   - 靠人记得改全, 不可靠(人会忘、地方多); 让编译器在漏的时候报错, 才可靠;
   - never穷尽检查就是"把这个约束变成编译期强制"的手段。

一句话: TS默认不检查switch/if是否穷尽处理联合类型所有成员、加新成员漏处理不报错; 用never做穷尽检查
   (default里赋never/assertNever)让漏处理在编译期报错; 把"加新成员要改全"的责任交给编译器强制而非记性。

这套认知,是整个坑的根。默认行为:TS 不强制 switch/if 穷尽处理联合类型所有成员,漏处理不报错,有 default 时更不报never 穷尽检查原理:never 是不可能有值的底类型;处理完所有成员后 default 里变量收窄为 never;赋给 never——全处理了合法、漏了成员就是字面量类型不能赋给 never、编译报错几种写法:default 里 const _: never = status、抽 assertNever(x: never) 函数、详尽 if-else 最后赋 never配套:别滥用 default 吞未知成员、开 ESLint switch-exhaustiveness-check、联合类型集中定义本质:把"容易遗漏的一致性约束(加新成员要改全所有地方)"交给编译器强制,而非靠人自觉(人会忘)一句话:TS 默认不检查 switch/if 是否穷尽处理联合类型所有成员、加新成员漏处理不报错;用 never 做穷尽检查(default 里赋 never/assertNever)让漏处理在编译期报错;把"加新成员要改全"的责任交给编译器强制而非记性。

第二件事:正解——用 never 做穷尽检查

知道了 TS 默认不强制穷尽,正解就清楚了:用 never 让"漏处理"在编译期报错。

type Status = 'pending' | 'paid' | 'refunded';

// 正解1: switch default 里用 never 穷尽检查(本次该加的)
function getLabel(status: Status): string {
  switch (status) {
    case 'pending': return '待支付';
    case 'paid':    return '已支付';
    case 'refunded':return '已退款';
    default: {
      const _exhaustive: never = status;   // ✓ 漏掉某成员时, 这里编译报错!
      throw new Error(`未处理的状态: ${_exhaustive}`);
    }
  }
}
// 现在若再加 'cancelled' 而忘了补 case → default 里 status 是 'cancelled' →
//   'cancelled' 不能赋给 never → 编译报错 → 强制你去补上处理! (它替我兜住了"记得改全")

// 正解2: 抽一个 assertNever 工具(更优雅, 可复用)
function assertNever(x: never): never {
  throw new Error(`未处理的情况: ${JSON.stringify(x)}`);
}
function getColor(status: Status): string {
  switch (status) {
    case 'pending': return 'gray';
    case 'paid':    return 'green';
    case 'refunded':return 'orange';
    default: return assertNever(status);   // ✓ 漏处理 → 传给assertNever(never) 报错
  }
}

// 正解3: if-else 链也能穷尽检查
function handle(status: Status) {
  if (status === 'pending') { /*...*/ }
  else if (status === 'paid') { /*...*/ }
  else if (status === 'refunded') { /*...*/ }
  else { const _: never = status; }   // 漏了就报错
}

// 正解4: 配合工具在更早/更广层面拦截
//   - ESLint: @typescript-eslint/switch-exhaustiveness-check 规则, 不穷尽就报(无需手写never);
//   - 联合类型/枚举集中定义, 让"处理它的地方"可控、可被检查覆盖。

// 反例(别这样):
// switch(status){ case 'pending':...; case 'paid':...; default: return '未知'; }
//   ✗ 没穷尽检查, 加了refunded也不报, default悄悄兜住、行为错。

// 核心: 用 never 做穷尽检查(default里赋never / assertNever / if-else最后赋never), 让"漏处理联合类型
//   新成员"在编译期就报错; 配ESLint规则; 别用default默默吞掉未处理的成员。

这套正解的关键,是never 把"是否处理了所有成员"这个检查,从"靠人记得"变成"编译器强制"switch default 里赋 never:漏掉某成员时 default 里的变量就是那个字面量类型、不能赋给 never、编译报错——这正是本次该加的。抽 assertNever 工具:更优雅可复用,default 里 return assertNever(status)if-else 链:最后 else 里赋 never 同样能穷尽检查。配 ESLint 规则:switch-exhaustiveness-check 不穷尽就报,无需手写 never。核心是:让"漏处理新成员"在编译期就报错,别用 default 默默吞掉。

第三件事:其他几个"靠人记得、该让工具强制"的坑

顺着这次穷尽检查,我把"容易遗漏的一致性约束、该交给工具强制而非靠记性"的几类坑也一并理了:

几类"该让工具强制、却靠人记得"的坑:

坑1: 加了联合成员忘了改所有switch(本篇)——靠记性; 正解: never穷尽检查。

坑2: 改了接口/函数签名忘了改所有调用方——靠记性; 正解: 强类型让编译器报错(别用any/as绕过, 同588)。

坑3: 加了枚举值忘了在映射表/配置里补——靠记性; 正解: 用Record(缺key编译报错)。

坑4: 改了数据库字段忘了改ORM/DTO/校验——靠记性; 正解: 用代码生成/单一来源(schema生成类型)。

坑5: 加了功能忘了写测试/忘了更新文档——靠记性; 正解: CI检查覆盖率、文档与代码绑定。

坑6: 多处重复的常量/逻辑改一处忘了改其他处——靠记性; 正解: 提取单一来源(DRY), 别复制粘贴。

坑7: 加了新的错误类型忘了在错误处理里覆盖——靠记性; 正解: 穷尽检查/类型约束。

共同的根: 软件里有大量"一处变了、其他相关处都要跟着变"的一致性约束; 若靠"开发者记得全部改到",
   一定会有遗漏(人会忘、地方多、改的人换了); 真正可靠的做法, 是【让工具(类型系统/编译器/lint/CI)
   在'漏改'时强制报错】, 把"保持一致"的责任从"人的自觉"转移到"机器的强制"——遗漏在编译期暴露, 而非线上。

这些坑看似不同,根却是同一个:软件里有大量"一处变了、其他相关处都要跟着变"的一致性约束;若靠"开发者记得全部改到",一定会有遗漏(人会忘、地方多、改的人换了);真正可靠的做法,是让工具(类型系统/编译器/lint/CI)在"漏改"时强制报错,把"保持一致"的责任从"人的自觉"转移到"机器的强制"认清这个根("容易遗漏的一致性约束要让工具强制、别靠记性"),才能从根上减少这类"改漏了"的 bug。

第四件事:穷尽检查写法 / 各方式对比——两张对照表

我把穷尽检查的几种写法、以及"靠记性 vs 靠工具"的对比,整理成对照表,贴在了团队的 TS 规范里:

方式 怎么做 漏成员时
普通 switch + default 兜底 default 返回默认值 不报错,悄悄走 default(坑)
switch + never default 里 const _:never=x 编译报错 ✓
assertNever 函数 default: return assertNever(x) 编译报错 ✓
if-else + never 最后 else 里赋 never 编译报错 ✓
ESLint 规则 switch-exhaustiveness-check lint 报错 ✓(IDE/CI)
Record<EnumType, V> 映射 用映射表替代 switch 缺 key 编译报错 ✓
维度 靠人记得改全 靠工具强制(never/lint)
可靠性 会遗漏(人会忘) 漏了必报,可靠
暴露时机 线上才发现 编译期就发现
换人维护 新人不知道要改哪 编译器直接指出
成本 事后排查 bug 成本高 写检查一次,长期省心

这两张表的核心,第一张是除了"普通 switch + default",其他方式都能在漏处理成员时报错;never 穷尽检查和 ESLint 规则是最常用的;第二张是"靠工具强制"在可靠性、暴露时机、可维护性上全面优于"靠人记得"。记住一条:处理联合类型/枚举时,加一道穷尽检查——让"以后加了新成员忘了处理"在编译期就报错。

第五件事:关于联合类型处理的几组容易想当然的认知

这次事故也让我厘清了几组关于联合类型/穷尽检查的、容易想当然的概念:

直觉以为 实际上
加了联合成员 TS 会逼我处理它 默认不会,switch 不穷尽也不报错
有 default 兜底就万无一失 default 悄悄吞掉新成员,行为可能错
编译通过就说明都处理到了 不报错≠穷尽,可能漏了新成员
我记得加成员就会去改所有地方 地方多/隔久/换人,一定会漏
never 没什么用 它是穷尽检查的关键
穷尽检查是多余的仪式 它把遗漏从线上提前到编译期
类型系统会自动保证一致性 很多一致性要你主动用技巧去"要求"它

这张表里,我栽的是第一行和第二行:以为"加了联合成员 TS 会逼我处理"、"有 default 兜底就万无一失",结果 default 悄悄把 refunded 吞了、TS 一声不吭厘清这些,核心是一个意识:类型系统能帮你检查很多,但不是所有一致性约束它都默认替你强制——像"穷尽处理联合类型所有成员"这种,需要你主动用 never 等技巧去"要求"它检查;主动建立这道编译期的强制检查,就能把"加了新成员忘了处理"这种最容易犯的遗漏,从"线上事故"提前到"编译报错"。

第六件事:处理联合类型 / 写一致性约束时,我现在的自检习惯

现在每当我用 switch/if 处理联合类型或枚举,我都会先按这张图问自己:

这张图的精髓,是"处理联合类型加 never 穷尽检查、一致性约束让工具强制别靠记性"先问类型会不会加成员(会就加穷尽检查)、default 别普通兜底要用 never一致性约束让工具强制这套习惯,让我从"加了成员靠自己记得改全"变成了"让编译器在漏处理时报错"——核心始终是:TS 默认不检查 switch/if 是否穷尽处理联合类型所有成员;用 never 做穷尽检查让漏处理在编译期报错;把"加新成员要改全"的责任交给编译器强制而非记性。

我立下的几条规矩

这场"加了新状态、好几处漏处理却不报错"的事故,换来了我写 TypeScript 时,刻进骨子里的几条铁律:

  1. TS 默认不检查 switch/if 是否穷尽处理了联合类型的所有成员,漏处理不报错。
  2. 有 default 兜底时更不报错,新成员被 default 悄悄吞掉、行为可能错。
  3. 处理联合类型/枚举,用 never 做穷尽检查:default 里 const _:never=x,或 assertNever(x)。
  4. 加了新成员而漏处理时,never 穷尽检查会在编译期报错,强制你去补上。
  5. 配 ESLint 的 switch-exhaustiveness-check;枚举到值的映射用 Record<EnumType, V>(缺 key 报错)。
  6. 别用 default 默默吞掉未处理的成员;default 要么真合理,要么用穷尽检查暴露遗漏。
  7. 一切"一处变要改多处"的一致性约束,都尽量让工具(类型/编译器/lint/codegen)强制,别靠记性。

附:用 Record 把"枚举到值的映射"也做成穷尽强制

除了 switch 用 never,另一个我现在常用的"让编译器强制穷尽"的技巧,是用 Record<联合类型, 值> 来定义映射表——缺了任何一个成员的映射,编译就报错。

type Status = 'pending' | 'paid' | 'refunded';

// 用 Record 定义映射: 缺任何一个key都会编译报错!
const STATUS_LABEL: Record = {
  pending: '待支付',
  paid:    '已支付',
  refunded:'已退款',
  // 若漏了某个(比如以后加了 'cancelled' 却没在这补) → 编译报错:
  //   Property 'cancelled' is missing in type ... but required in type 'Record'
};

// 取值: 直接查表, 既穷尽又简洁(还省了switch)
function getLabel(status: Status): string {
  return STATUS_LABEL[status];   // ✓ 类型安全, 且 Record 保证了所有成员都有映射
}

// 同理可定义颜色、权限、下一状态等映射, 都用 Record 强制穷尽:
const STATUS_COLOR: Record = { pending:'gray', paid:'green', refunded:'orange' };
const NEXT_STATUS: Record = { pending:'paid', paid:'refunded', refunded:null };

// 对比 switch+never:
//   - switch+never: 适合"每个分支有复杂逻辑"的情况;
//   - Record映射: 适合"成员→值"的简单映射, 更简洁, 缺key直接编译报错;
//   两者都是"把'处理所有成员'变成编译器强制"的手段, 按场景选。

// 原则: 凡"每个联合成员/枚举值都要对应一个东西(标签/颜色/处理器)"的场景, 用 Record
//   定义——它从类型层面强制"必须给每个成员都提供", 加了新成员忘了补 = 编译报错, 比switch更省心。

这个 Record 技巧和 switch 的 never 穷尽检查异曲同工,都是把"必须处理/覆盖所有成员"这个约束,变成编译器能强制的形式:Record<Status, V> 要求你必须给每个成员都提供一个值,缺一个就编译报错。对于"成员 → 值"的映射场景,它比 switch 更简洁、更省心。我现在的习惯是:能用 Record 表达的映射就用 Record(自带穷尽强制),需要复杂分支逻辑的 switch 就配 never——总之,让"覆盖所有成员"这件事,由编译器替我盯着。

写在最后

回头看,这场由"没用 never 做穷尽检查"引发的、新状态在多处被悄悄漏掉的事故,真正教给我的,远不止"用 never 穷尽检查"这一个技巧。它让我对"软件里有大量'这里改了, 那里也得跟着改'的一致性约束; 如果把'记得把所有相关的地方都改到'的责任压在人的'记性', 那遗漏几乎是必然的——人会忘、地方会多、维护的人会换; 而真正可靠的, 是让工具在'你漏改了'的那一刻就强制拦住你",有了一次刻骨的体会。我栽跟头,是因为我把一个本该交给机器的活儿(检查"所有该处理新成员的地方都处理了吗"), 交给了我自己的记性——我加 refunded 时, 心里想的是"我会记得去改所有 switch 的";可这种"散落在好几处、需要逐一改到、改漏了也不立刻报错"的活儿, 恰恰是人最容易遗漏、而机器最擅长检查的;我没有去用那个"让编译器替我检查"的技巧(never), 等于放着一个不知疲倦、绝不遗漏的检查员不用, 偏要靠自己那会犯困、会健忘的脑子——结果当然是漏了这让我领悟到一个关于"人的记性与工具的强制"的深刻认知:凡是"容易遗漏、且遗漏后果严重"的一致性约束(改了 A 要同步改 B/C/D、加了成员要处处处理、改了接口要更新所有调用方),都不应该依赖"人记得做"来保证——因为人的记性是这个系统里最不可靠的一环; 正确的做法, 是想办法把这个约束"编码"成一种机器能检查的形式(类型约束、穷尽检查、单一来源、自动化测试/lint), 让"违反约束"在尽可能早的时刻(编译期 > 测试期 > 线上)被工具自动、强制地揪出来;"让正确成为编译器/工具强制的结果, 而非人自觉的结果"——这是把脆弱的"人肉保证"升级为可靠的"机器保证"的核心思路, 也是优秀工程实践(强类型、测试、CI、DRY)背后共同的精神这给了我一种设计时的自觉:每当我发现自己在依赖"我/我们会记得做某件事"来保证正确性时,要警觉地问"这件容易忘的事, 能不能让工具在我忘的时候自动拦住我?"——主动把"靠记性的约束"改造成"靠工具强制的约束"(用类型、穷尽检查、单一来源、自动化), 让遗漏在编译期暴露, 而不是把希望寄托在永远不会出错的记性上;"识别依赖人记性的脆弱约束、把它转化为工具强制的可靠约束",是构建不易出错的系统的关键工程智慧认清一致性约束靠人记性必然遗漏、要让工具在漏改时强制报错、把人肉保证升级为机器保证——这,是我用一次缺失穷尽检查的事故,换来的、关于 TypeScript、也关于如何用工具兜住人之遗漏的、最朴素也最深刻的领悟。如果这篇复盘,能让你下次写处理联合类型的 switch 时,顺手在 default 里加上那行 const _: never = status,让以后的自己加新成员忘了处理时被编译器当场拦住,那我对着那几处被悄悄漏掉的 refunded 排查的这段时间,就值了。

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

我的一个高频接口性能差、GC 压力大,代码里却看不出哪行慢,profiler 一看全是堆分配,原来是我把一堆 int、bool 当 object 存进了集合、每次存取都在悄悄装箱拆箱的深度复盘

2026-6-3 2:34:57

技术教程

我搭了个多 Agent 系统让几个智能体协作干活,以为人多力量大会更强,结果它们要么抢着做同一件事、要么都以为对方会做而漏了、甚至 A 等 B 的结果 B 又在等 A 直接卡死的深度复盘

2026-6-3 2:47:14

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