news 2026/7/17 7:01:38

单层 ?? 的含义是:左边为 null 则取右边。

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
单层 ?? 的含义是:左边为 null 则取右边。
<span style="background-color:#e3eaf2"><span style="color:#111b27"><code class="language-none">string name = userInput ?? "未命名"; // 等价于 string name = userInput is not null ? userInput : "未命名"; </code></span></span>
多级回退:链式??

当需要逐级尝试多个候选值时,直接串联:

<span style="background-color:#e3eaf2"><span style="color:#111b27"><code class="language-none">var result = first ?? second ?? third ?? fallback; </code></span></span>

编译器将其展开为右结合的嵌套三元表达式:

<span style="background-color:#e3eaf2"><span style="color:#111b27"><code class="language-none">var result = first ?? (second ?? (third ?? fallback)); </code></span></span>

执行流程:从左到右逐一求值,遇到第一个非 null 值立即返回,后续不再求值(短路语义)。

真实案例:三级回退链

以下是 OpenClaw.NET 项目中 AdminEndpoints.cs 的实际代码:

<span style="background-color:#e3eaf2"><span style="color:#111b27"><code class="language-none">var modelProfiles = app.Services.GetService<IModelProfileRegistry>() ?? runtime.Operations.ModelProfiles as IModelProfileRegistry ?? ConfiguredModelProfileRegistry.CreateInitialized(startup.Config); var modelEvaluationRunner = app.Services.GetService<ModelEvaluationRunner>() ?? new ModelEvaluationRunner( runtime.Operations.ModelProfiles as ConfiguredModelProfileRegistry ?? modelProfiles as ConfiguredModelProfileRegistry ?? ConfiguredModelProfileRegistry.CreateInitialized(startup.Config), startup.Config, NullLogger<ModelEvaluationRunner>.Instance); </code></span></span>

这里第二条链值得展开分析。它由三级回退组成:

第一级:从运行时获取
<span style="background-color:#e3eaf2"><span style="color:#111b27"><code class="language-none">runtime.Operations.ModelProfiles as ConfiguredModelProfileRegistry </code></span></span>

运行时对象在启动阶段已经构建好了一份ModelProfiles。使用as运算符尝试安全类型转换——成功则直接用,失败则返回 null,进入下一级。

这是最快路径,不需要任何新建或查找。

第二级:从已解析变量复用
<span style="background-color:#e3eaf2"><span style="color:#111b27"><code class="language-none">?? modelProfiles as ConfiguredModelProfileRegistry </code></span></span>

modelProfiles是上一行刚解析出来的变量,声明类型是IModelProfileRegistry接口,但运行时实例很可能就是ConfiguredModelProfileRegistry

这一级是整个设计的关键优化点——当 DI 容器和运行时对象都缺失注册表时,第一行代码为我们创建了唯一的回退实例。通过as尝试复用同一实例,避免了在modelEvaluationRunner内部再调用CreateInitialized创建第二个注册表。

为什么要避免重复创建?因为CreateInitialized内部会调用BuildRegistrations,为每个模型配置创建IChatClient实例并标记ownsClient = true。如果创建两份注册表,就会产生两套独立的客户端,造成:

  • 内存浪费(重复的客户端实例)
  • 资源泄漏风险(只有一份会被Dispose,另一份丢失引用)
第三级:兜底创建
<span style="background-color:#e3eaf2"><span style="color:#111b27"><code class="language-none">?? ConfiguredModelProfileRegistry.CreateInitialized(startup.Config) </code></span></span>

最后的保险丝。如果前两级都无法提供(例如 DI 注入了一个非ConfiguredModelProfileRegistry类型的自定义实现),使用工厂方法初始化一份全新的注册表,确保 admin 端点在任何情况下都能正常工作

为什么用as而不是强转?

as运算符转换失败返回null,正好喂给??进入下一级:

<span style="background-color:#e3eaf2"><span style="color:#111b27"><code class="language-none">// ✅ 推荐:类型不匹配时返回 null,无缝衔接 ?? runtime.Operations.ModelProfiles as ConfiguredModelProfileRegistry // ❌ 不推荐:类型不匹配时抛出 InvalidCastException (ConfiguredModelProfileRegistry)runtime.Operations.ModelProfiles </code></span></span>

as+??是 C# 中处理不确定类型的经典组合。

执行顺序图解
<span style="background-color:#e3eaf2"><span style="color:#111b27"><code class="language-none">请求 ConfiguredModelProfileRegistry │ ▼ runtime.Operations.ModelProfiles 能转成 ConfiguredModelProfileRegistry 吗? │ ┌────┴────┐ 否 是 → ✅ 返回(最快路径) │ ▼ modelProfiles (上一行解析的) 能转成 ConfiguredModelProfileRegistry 吗? │ ┌────┴────┐ 否 是 → ✅ 返回(复用,避免重复创建) │ ▼ CreateInitialized(...) 新建一个 → ✅ 返回(兜底保底) </code></span></span>
回退链的设计原则

从这个案例中可以提炼出几条通用原则:

原则说明
频率降序越常用的回退源排越前面,最大化短路收益
代价升序创建新对象的操作放最后,避免不必要的开销
共享优先于新建中间层插入"复用已有"逻辑,防止重复创建
永远有兜底最后一级确保无论如何都有可用值
对比其他写法

同样的逻辑,不用??链会写成:

<span style="background-color:#e3eaf2"><span style="color:#111b27"><code class="language-none">// 传统 if-else 写法(啰嗦、易出错) ConfiguredModelProfileRegistry registry; if (runtime.Operations.ModelProfiles is ConfiguredModelProfileRegistry r1) registry = r1; else if (modelProfiles is ConfiguredModelProfileRegistry r2) registry = r2; else registry = ConfiguredModelProfileRegistry.CreateInitialized(config); </code></span></span>
<span style="background-color:#e3eaf2"><span style="color:#111b27"><code class="language-none">// ?? 链式写法(简洁、声明式) var registry = runtime.Operations.ModelProfiles as ConfiguredModelProfileRegistry ?? modelProfiles as ConfiguredModelProfileRegistry ?? ConfiguredModelProfileRegistry.CreateInitialized(config); </code></span></span>

??链将"是什么"(声明意图)和"怎么做"(执行细节)完美分离。

注意事项
  1. as仅用于引用类型。值类型用可空转换:value as int?

  2. ??的右结合性a ?? b ?? c等价于a ?? (b ?? c),不是(a ?? b) ?? c。但在短路语义下,两者在绝大多数场景中行为一致。

  3. 避免过长的链。超过 4-5 层建议考虑重构——不是语法限制,而是认知负担。

  4. 警惕副作用??只对左侧进行短路求值,但如果右侧表达式中包含CreateInitialized这样的工厂方法,确保调用频率符合预期。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/7/17 7:01:22

GHelper:为华硕笔记本量身打造的轻量级控制工具

GHelper&#xff1a;为华硕笔记本量身打造的轻量级控制工具 【免费下载链接】g-helper Lightweight Armoury Crate alternative for Asus laptops with nearly the same functionality. Works with ROG Zephyrus, Flow, TUF, Strix, Scar, ProArt, Vivobook, Zenbook, Expertbo…

作者头像 李华
网站建设 2026/7/12 10:05:20

图片太大怎么缩小

## 图片太大怎么缩小&#xff1a;核心结论直接说答案&#xff1a;图片太大怎么缩小这个问题&#xff0c;其实有几种方法&#xff0c;**最推荐的是用在线工具&#xff0c;5秒就能搞定**&#xff0c;不用下载任何软件。下面详细展开每种方法的操作步骤和适用场景&#xff0c;帮你…

作者头像 李华
网站建设 2026/7/13 5:18:06

FastCut 大更新:第一个能让 Codex / ZCode 直接操刀的浏览器剪辑台

引言&#xff1a;剪辑这件事&#xff0c;能不能更「偷懒」一点&#xff1f; 做视频的人都懂那种累—— 素材导进来&#xff0c;要拉时间线、对字幕、配旁白、加背景音乐、调节奏……每一步都得手动点、手动拖。哪怕用上了 AI 字幕、AI 配音&#xff0c;你依然是那个一直坐在剪…

作者头像 李华
网站建设 2026/7/13 5:18:05

Kindle漫画转换终极指南:让你的电子阅读器变身漫画图书馆

Kindle漫画转换终极指南&#xff1a;让你的电子阅读器变身漫画图书馆 【免费下载链接】kcc KCC (a.k.a. Kindle Comic Converter) is a comic and manga converter for ebook readers. 项目地址: https://gitcode.com/gh_mirrors/kc/kcc 还在为在Kindle、Kobo等电子阅读…

作者头像 李华
网站建设 2026/7/13 5:20:28

【毕业设计】基于 SpringBoot 的餐厅订单统计与菜品管理系统 中小型餐厅订单业务管理平台设计与实现(源码+文档+远程调试,全bao定制等)

博主介绍&#xff1a;✌️码农一枚 &#xff0c;专注于大学生项目实战开发、讲解和毕业&#x1f6a2;文撰写修改等。全栈领域优质创作者&#xff0c;博客之星、掘金/华为云/阿里云/InfoQ等平台优质作者、专注于Java、小程序技术领域和毕业项目实战 ✌️技术范围&#xff1a;&am…

作者头像 李华
网站建设 2026/7/13 5:18:22

从零搭建:基于UWB与MiniFly的室内无人机协同定位系统

1. UWB室内定位技术基础 超宽带&#xff08;UWB&#xff09;技术近年来在室内定位领域崭露头角&#xff0c;其厘米级的高精度特性让它成为无人机室内定位的理想选择。与传统蓝牙、WiFi定位技术相比&#xff0c;UWB就像是用上了激光测距仪&#xff0c;而其他技术还停留在卷尺时代…

作者头像 李华