返回题库高级 .NET 刷题C# 选择题 · 第 28 / 50 篇

C# 试题 28:LINQ 分组与连接

0541 关于 Enumerable.GroupBy 的执行模型,哪项正确?

难度: 进阶

  • A. GroupBy 调用时立即返回分组结果
  • B. GroupBy 是延迟的:调用只构建查询,枚举时才读取整个源并构建分组;因此不能用于无限流
  • C. GroupBy 是流式的,边读边输出组
  • D. GroupBy 每枚举一个元素就输出一组
查看答案与解析

正确答案B

解析: GroupBy 属于延迟执行,但枚举时它会把源完整读入内部查找结构后才开始产出分组——既不是立即执行,也不是流式。对无限序列调用会一直读下去。

关键点: “延迟”与“流式”是两个维度,GroupBy 延迟但全量物化。

0542 以下代码输出什么?

难度: 实战

var query = new[] { 3, 1, 2, 3, 1 }.GroupBy(x => x);

Console.WriteLine(string.Join("", query.Select(g => g.Key)));
  • A. 312,组按键首次出现的顺序输出
  • B. 123,组按键排序
  • C. 3311
  • D. 321,按最后出现顺序
查看答案与解析

正确答案A

解析: 默认实现下分组按键首次出现的顺序产出:3 最先出现,随后 1、2,因此组键序列为 3、1、2。不要假设 GroupBy 按键排序——那需要显式 OrderBy。

关键点: GroupBy 的组序是首次出现序,不是排序序。

0543 以下代码输出什么?

难度: 进阶

var list = new List<int> { 1, 2, 3 };
var query = list.GroupBy(x => x % 2);
list.Add(4);

Console.WriteLine(string.Join(" ", query.Select(g => $"{g.Key}:{string.Join("", g)}")));
  • A. 1:13 0:2
  • B. 0:24 1:13
  • C. 1:13 0:24,枚举时读取最新源,键 0 的组含 2 与新增的 4
  • D. 1:1 0:2 1:3 0:4
查看答案与解析

正确答案C

解析: GroupBy 延迟执行,string.Join 枚举时才读取源,此时源为 4:键 1 组 3,键 0 组 4。组序按首次出现(1 先于 0),输出 1:13 0:24

关键点: 分组结果同样受“枚举时源状态”支配。

0544 以下代码输出什么?

难度: 进阶

var query = new[] { 1, 2, 3, 4 }.GroupBy(x => x % 2);
var evens = query.First(g => g.Key == 0);

Console.WriteLine(evens.Sum());
Console.WriteLine(evens.Sum());
  • A. 抛 InvalidOperationException,组只能枚举一次
  • B. 6 / 抛异常
  • C. 6 / 0
  • D. 6 / 6,组元素已物化存储,可反复枚举
查看答案与解析

正确答案D

解析: 枚举 GroupBy 查询时,每个组内部的元素已物化到内部存储(不是一次性迭代器),因此同一个组可以多次安全枚举,两次 Sum 都是 2+4=6。这与单次使用的原始迭代器不同。

关键点: GroupBy 的组可重复枚举,别与一次性枚举器混淆。

0545 以下代码输出什么?

难度: 实战

var words = new[] { "a", "A", "b", "B" };

foreach (var g in words.GroupBy(w => w, StringComparer.OrdinalIgnoreCase))
    Console.Write(g.Count());
  • A. 1111
  • B. 22,忽略大小写后分为两组
  • C. 4
  • D. 抛异常,GroupBy 不支持自定义比较器
查看答案与解析

正确答案B

解析: 传入 StringComparer.OrdinalIgnoreCase 后,“a”与“A”、“b”与“B”分别并入同组,得到两个各含 2 个元素的组,输出 22。默认比较器区分大小写时会分成 4 组。

关键点: 键比较器决定哪些键“相等并归组”。

0546 以下代码输出什么?

难度: 进阶

var query = new[] { "a", null, "b" }.GroupBy(s => s);

foreach (var g in query)
    Console.Write($"{g.Key ?? "null"}:{g.Count()} ");
  • A. a:1 null:1 b:1,null 是合法分组键
  • B. a:1 b:1,null 键被丢弃
  • C. 抛 ArgumentNullException
  • D. a:1 :1 b:1,null 键变成空字符串
查看答案与解析

正确答案A

解析: 引用类型的 null 可以作为分组键,GroupBy 不会抛异常,null 键的组正常存在,Count 为 1。容易误以为“键不能为 null”而漏掉该分支。

关键点: null 键合法,处理分组结果时记得判空。

0547 以下代码输出什么?

难度: 实战

var n = 0;
IEnumerable<int> Source()
{
    for (int i = 0; i < 3; i++) { n++; yield return i; }
}

var grouped = Source().GroupBy(x => x % 2);
Console.WriteLine(n);

var lookup = Source().ToLookup(x => x % 2);
Console.WriteLine(n);
  • A. 0 / 0
  • B. 3 / 3
  • C. 3 / 0
  • D. 0 / 3,GroupBy 延迟;ToLookup 立即物化
查看答案与解析

正确答案D

解析: GroupBy 不枚举源,第一次打印 n=0;ToLookup 立即枚举并构建查找表,第二次打印 n=3。两者结果形态相似,但执行时机一个延迟一个立即,是高频混淆点。

关键点: ToLookup 立即执行,GroupBy 延迟执行。

0548 以下代码输出什么?

难度: 实战

var lookup = new[] { 1, 2, 3 }.ToLookup(x => x % 2);

Console.WriteLine(lookup[5].Count());
  • A. 抛 KeyNotFoundException
  • B. 1
  • C. 0,ILookup 索引器对缺失键返回空序列
  • D. null
查看答案与解析

正确答案C

解析: ILookup 的索引器对不存在的键返回空序列而不是抛异常,因此 lookup[5] 是零元素序列,Count 为 0。这与字典索引器的契约截然不同。

关键点: Lookup 缺失键返回空序列,不抛异常。

0549 以下代码运行结果是什么?

难度: 实战

var dict = new[] { "a", "b", "a" }.ToDictionary(x => x);
  • A. 正常运行,得到两个键
  • B. 运行抛 ArgumentException(重复键)
  • C. 运行抛 InvalidOperationException
  • D. 编译失败
查看答案与解析

正确答案B

解析: ToDictionary 遇到重复键时抛 ArgumentException(“已存在相同键”)。先把数据 GroupBy 再 ToDictionary,或改用显式合并逻辑,才能避免该异常。

关键点: ToDictionary 假定键唯一,重复键是异常不是覆盖。

0550 以下代码输出什么?

难度: 实战

var outer = new[] { 1, 2, 3 };
var inner = new[] { 1, 1, 3 };

var query = outer.Join(inner, o => o, i => i, (o, i) => $"{o}-{i}");

Console.WriteLine(string.Join(" ", query));
  • A. 1-1 1-1 3-3,每个 outer 元素与每个匹配的 inner 元素各产生一行
  • B. 1-1 3-3
  • C. 1-1 1-1 2-2 3-3
  • D. 1-1 3-3 3-3
查看答案与解析

正确答案A

解析: Join 是笛卡尔积式的内连接:outer 的 1 命中 inner 的两个 1,产生两行;outer 的 3 命中一个 3,产生一行;outer 的 2 无匹配被丢弃。匹配对数决定结果行数。

关键点: Join 不做去重,一对多会产生多行。

0551 以下代码输出什么?

难度: 实战

var outer = new[] { "A", "B" };
var inner = new[] { "a", "b" };

var query = outer.Join(inner, o => o, i => i, (o, i) => o + i);

Console.WriteLine(query.Count());
  • A. 2
  • B. 4
  • C. 1
  • D. 0,Join 默认键比较是区分大小写的序号比较
查看答案与解析

正确答案D

解析: Join 默认使用 EqualityComparer<string>.Default,按序号且区分大小写,“A”与“a”不匹配,结果为 0 行。需要忽略大小写必须传入 StringComparer.OrdinalIgnoreCase

关键点: 连接键的比较器默认区分大小写。

0552 以下代码输出什么?

难度: 实战

var outer = new[] { "A", "B", "C" };
var inner = new[] { "a", "b" };

var query = outer.Join(inner, o => o, i => i, (o, i) => o + i, StringComparer.OrdinalIgnoreCase);

Console.WriteLine(string.Join(" ", query));
  • A. Aa Bb Cc
  • B. Aa Bb
  • C. Aa Bb,忽略大小写后 A↔a、B↔b 匹配,C 无匹配
  • D. A B
查看答案与解析

正确答案C

解析: 传入忽略大小写的比较器后,“A”命中“a”、“B”命中“b”;“C”在内序列没有对应键,作为内连接元素被丢弃。注意 A 选项错在 C 不会凭空匹配。

关键点: 内连接只保留两边都有匹配的键。

0553 以下代码输出什么?

难度: 进阶

var outer = new List<int> { 1, 2 };
var inner = new List<int> { 1, 2, 3 };
var query = outer.Join(inner, o => o, i => i, (o, i) => o + i);

inner.Add(2);

Console.WriteLine(string.Join(" ", query));
  • A. 2 4
  • B. 2 4 4,枚举时内序列已含新增的 2,outer 2 命中两个 inner 2
  • C. 2 4 3
  • D. 2 4 5
查看答案与解析

正确答案B

解析: Join 延迟执行,枚举开始时才物化内序列并配对。此时 inner 已是 2:outer 1 命中 1(和 2),outer 2 命中两个 2(4、4),输出 2 4 4

关键点: 内序列的物化发生在枚举开始,而非查询定义时。

0554 关于 Join 与 GroupJoin 结果形状的差异,哪项正确?

难度: 进阶

  • A. Join 对每对匹配输出一条结果,无匹配的 outer 元素不出现;GroupJoin 对每个 outer 元素恰好输出一次,组可能为空
  • B. 两者都只输出有匹配的 outer 元素
  • C. GroupJoin 对每对匹配都输出一行
  • D. Join 与 GroupJoin 结果完全相同
查看答案与解析

正确答案A

解析: Join 输出的是“匹配对”(一对多会产生多行);GroupJoin 按 outer 元素聚合匹配的 inner 元素,每个 outer 元素一行,即使没有匹配也会得到空组。GroupJoin 是“左外”形态的分组连接。

关键点: Join 的行数由匹配对数决定,GroupJoin 的行数等于 outer 元素数。

0555 以下代码输出什么?

难度: 实战

var outer = new[] { 1, 2, 3 };
var inner = new[] { 1, 3 };

var query = outer.GroupJoin(inner, o => o, i => i, (o, g) => $"{o}:{g.Count()}");

Console.WriteLine(string.Join(" ", query));
  • A. 1:1 3:1
  • B. 1:1 2:1 3:1
  • C. 1:1 2:0 3:1,无匹配的 outer 元素也输出,组为空
  • D. 1:1 2:0 3:0
查看答案与解析

正确答案C

解析: GroupJoin 保证每个 outer 元素都有一条结果:1 命中 1(组大小 1)、3 命中 3(组大小 1)、2 无匹配(空组,大小 0)。空组不是被丢弃,而是 Count 为 0。

关键点: GroupJoin 不会丢 outer 元素,空组用 Count()==0 识别。

0556 以下代码输出什么?

难度: 进阶

var outer = new List<int> { 1, 2 };
var inner = new List<int> { 1 };
var query = outer.GroupJoin(inner, o => o, i => i, (o, g) => $"{o}:{g.Sum()}");

inner.Add(1);

Console.WriteLine(string.Join(" ", query));
  • A. 1:1 2:0
  • B. 1:2 2:2
  • C. 1:1 2:1
  • D. 1:2 2:0,枚举开始时内序列被物化,两个 1 都计入 outer 1 的组
查看答案与解析

正确答案D

解析: GroupJoin 在枚举开始时把内序列物化成查找结构,此时 inner 已含两个 1:outer 1 的组为 1(和 2),outer 2 无匹配(和 0)。

关键点: 内序列在首次枚举时被完整缓冲,之后的变化不影响本次结果。

0557 关于 GroupJoin 为每个 outer 元素生成的 inner 组,哪项正确?

难度: 进阶

  • A. 组只能枚举一次,第二次抛异常
  • B. 组基于已物化的查找结构,可以多次安全枚举
  • C. 组每次枚举都会重新执行整个查询
  • D. 组是流式枚举,可能读到后续 outer 元素
查看答案与解析

正确答案B

解析: GroupJoin 先把内序列整体物化,再为每个 outer 元素生成指向该存储的组视图,因此组可以多次安全枚举(如先 Count 再遍历元素)。这不同于需要小心的一次性迭代器。

关键点: GroupJoin 的组是“已物化数据的视图”。

0558 以下代码输出什么?

难度: 实战

var n = 0;
var query = new[] { 1, 2, 3 }.GroupBy(x => { n++; return x % 2; });

Console.WriteLine(n);
Console.WriteLine(query.Count());
Console.WriteLine(n);
  • A. 0 / 2 / 3,键选择器在枚举时执行
  • B. 3 / 2 / 3
  • C. 0 / 2 / 0
  • D. 0 / 3 / 3
查看答案与解析

正确答案A

解析: 定义查询时键选择器未执行(n=0);Count() 驱动枚举,3 个元素各执行一次键选择器(n=3),得到 2 个组。键选择器与谓词一样,都是延迟求值。

关键点: 分组键的选择器在枚举时才运行。

0559 以下代码输出什么?

难度: 实战

var outer = new[] { "a", "b" };
var inner = new[] { "b", "a" };

var query = outer.Join(inner, o => o, i => i, (o, i) => o);

Console.WriteLine(string.Join(",", query));
  • A. b,a,按内序列顺序
  • B. a,b,结果自动排序
  • C. b,a,按匹配先后
  • D. a,b,外层主导:按 outer 元素顺序输出
查看答案与解析

正确答案D

解析: Join 的结果顺序由外层驱动:按 outer 元素的出现顺序,逐元素输出其全部匹配。outer 是 “a” 在前,因此结果为 a,b,与 inner 的顺序无关,也没有排序。

关键点: Join 输出顺序跟随 outer,想排序要显式 OrderBy。

0560 以下代码输出什么?

难度: 进阶

class BadComparer : IEqualityComparer<int>
{
    public bool Equals(int x, int y) => x % 2 == y % 2;
    public int GetHashCode(int obj) => obj;
}

var query = new[] { 1, 2, 3, 4, 5, 6 }.GroupBy(x => x, new BadComparer());

Console.WriteLine(query.Count());
  • A. 2,Equals 按奇偶分组
  • B. 3
  • C. 6,哈希不一致导致“相等”元素落入不同桶,未归并;比较器必须保证 Equals 相等时哈希相同
  • D. 抛 ArgumentException
查看答案与解析

正确答案C

解析: 该比较器 Equals 认为奇偶相同的键相等,但 GetHashCode 返回键本身,1、3、5 哈希不同、落入不同桶,永远不会被 Equals 比较,因此分组完全按哈希进行,得到 6 组。违反“Equals 相等 ⇒ GetHashCode 相等”的契约,行为不可预期。

关键点: 键比较器必须同时遵守相等与哈希一致性。

官方资料

当前分类

C# 选择题

查看全部分类 →
  1. 26C# 试题 26:有序、并发与不可变集合20 题
  2. 27C# 试题 27:LINQ 延迟执行20 题
  3. 28C# 试题 28:LINQ 分组与连接20 题
  4. 29C# 试题 29:迭代器与异步流20 题
  5. 30C# 试题 30:日期时间、Guid 与 Random20 题
ESC

输入关键词开始搜索