defaultifempty 只在查询结果为空时插入默认项,对 null 集合直接抛 argumentnullexception;典型用于左连接占位或空集合兜底,不处理源为 null 或下游未判空问题。

DefaultIfEmpty 不是防空异常的工具,它只在查询结果为空时补一个元素;对 null 集合直接调用会立刻抛 ArgumentNullException。
DefaultIfEmpty 什么时候真正起作用?
它只在 LINQ 查询执行后、结果序列为空时插入一个默认项,典型场景是左连接中为右表缺失项“占位”,或对已知可能为空的集合做兜底展示。
- 空
List<int></int>调用DefaultIfEmpty()→ 返回含单个0的IEnumerable<int></int> - 空
List<string></string>调用DefaultIfEmpty("N/A")→ 返回含单个"N/A"的IEnumerable<string></string> - 非空集合(哪怕只有一个元素)调用 → 原样返回,
DefaultIfEmpty完全不生效 - 源集合为
null→ 直接抛ArgumentNullException,它不处理 null
为什么 DefaultIfEmpty(null) 会崩溃?
因为 DefaultIfEmpty 是扩展方法,第一个参数 this IEnumerable<tsource> source</tsource> 被标记为不可为空。传入 null 就像对 null 调用任何实例方法一样,运行时立刻失败。
- 错误写法:
var list = (List<string>)null; list.DefaultIfEmpty("x");</string>→ 崩溃 - 安全写法(推荐分步):
list?.Any() == true ? list : Enumerable.Repeat("x", 1) - 或用空合并:
list?.DefaultIfEmpty("x") ?? Enumerable.Repeat("x", 1),但注意右侧必须是IEnumerable<string></string>类型
左连接中 DefaultIfEmpty 的标准用法
这是它最不可替代的场景:配合 GroupJoin 实现 SQL 风格左外连接,右表无匹配时补一行默认数据。
var leftJoin = customers.GroupJoin(
orders,
c => c.Id,
o => o.CustomerId,
(c, orderGroup) => new { Customer = c, Orders = orderGroup }
)
.SelectMany(
x => x.Orders.DefaultIfEmpty(), // ← 关键:此处补 null 表示“无订单”
(x, o) => new { x.Customer, Order = o }
);
注意:展开后的 o 可能为 null,后续使用必须判空,比如 Order?.Status ?? "No Order";否则链式访问如 o.Status.Length 仍会抛 NullReferenceException。
DefaultIfEmpty(T) 和 DefaultIfEmpty() 的区别
两者行为一致,但类型推导和语义不同:
-
source.DefaultIfEmpty()→ 使用T的default(T):值类型是0、false,引用类型是null,可空类型(如int?)也是null -
source.DefaultIfEmpty(value)→ 显式指定值,避免意外为null,也更易读 - 对
int?调用无参版,返回的是null而非0,这点容易被忽略
复杂点在于:它不改变原集合类型(返回总是 IEnumerable<t></t>),也不影响查询延迟执行特性;而最容易被忽略的,是它完全不解决“源为 null”或“下游使用未判空”这两个真实痛点。










