C# 试题 23:集合接口
0441 以下代码输出什么?
难度: 实战
int count = 0;
IEnumerable<int> e = Enumerable.Range(0, 3)
.Select(x => { count++; return x; });
e.Count();
e.Count();
Console.WriteLine(count);
- A.
3 - B.
4 - C.
6,每次Count()都从头重新执行查询 - D.
0
查看答案与解析
正确答案C
解析: LINQ 查询是惰性执行:两次 Count() 各自完整枚举一次,选择器分别执行 3 次,副作用计数累计为 6。
关键点: IEnumerable 不缓存结果,多次枚举等于多次执行,副作用会叠加。
0442 以下代码输出什么?
难度: 进阶
IEnumerable<int> e = new List<int> { 1, 2, 3 };
((List<int>)e).Add(4);
Console.WriteLine(e.Count());
- A.
4,接口只约束通过接口的访问,下转后可修改原对象 - B.
3 - C. 运行抛
InvalidCastException - D. 编译失败
查看答案与解析
正确答案A
解析: 接口类型只约束“通过这个接口能做什么”,底层对象仍是 List<int>;下转成功后 Add 直接修改原集合,枚举能看到新元素。
关键点: “IEnumerable 就是只读”是误解,它只代表“可枚举”。
0443 以下代码输出什么?
难度: 实战
IEnumerable<int> Get()
{
yield return 1;
yield break;
yield return 2;
}
Console.WriteLine(Get().Count());
- A.
0 - B.
1 - C.
2 - D. 编译失败,
yield break后不能有语句
查看答案与解析
正确答案B
解析: yield break 立即结束迭代器,其后的 yield return 2 永远不会执行(编译期会有不可达代码警告),序列只有 1 个元素。
关键点: yield break 是迭代器的“提前 return”,后面的代码是死代码。
0444 以下代码运行结果是什么?
难度: 实战
ICollection<int> c = new int[3];
Console.WriteLine(c.IsReadOnly);
c.Add(1);
- A.
True,随后正常添加 - B.
False,随后正常添加 - C.
False,随后抛异常 - D.
True,随后Add抛NotSupportedException
查看答案与解析
正确答案D
解析: 数组实现了 ICollection<T>,但 IsReadOnly 恒为 true,Add/Remove/Clear 都抛 NotSupportedException;数组大小固定,不能改变元素个数。
关键点: IsReadOnly 为 true 意味着“结构不可变”,可变集合该属性为 false。
0445 以下代码输出什么?
难度: 实战
var set = new HashSet<int> { 1, 2, 3 };
Console.WriteLine(set.Add(2));
Console.WriteLine(set.Add(4));
Console.WriteLine(set.Count);
- A.
False、True、4 - B.
True、True、5 - C.
False、False、3 - D.
True、False、4
查看答案与解析
正确答案A
解析: ISet<T>.Add 返回“是否新增”:2 已存在返回 False,4 是新的返回 True;Count 从 3 变为 4。
关键点: HashSet 的 Add 不是 void,返回值携带“是否真的加入”的信息。
0446 以下代码输出什么?
难度: 实战
var a = new HashSet<int> { 1, 2, 3 };
var b = new HashSet<int> { 2, 3, 4 };
a.ExceptWith(b);
Console.WriteLine(string.Join(",", a));
- A.
1,2,3 - B.
1,2,3,4 - C.
1 - D.
2,3
查看答案与解析
正确答案C
解析: ExceptWith 是原地修改:从 a 中移除所有也出现在 b 里的元素(2、3),剩下 1;它不返回新集合。
关键点: ExceptWith 原地改,Except(LINQ)返回新序列,别混用。
0447 以下代码输出什么?
难度: 进阶
var list = new List<int> { 1, 2, 3 };
IEnumerable<int> e = list.Where(x => x > 1);
list.Add(4);
Console.WriteLine(string.Join(",", e));
- A.
2,3 - B.
2,3,4,Where延迟执行,枚举时读取最新集合 - C.
1,2,3,4 - D.
4
查看答案与解析
正确答案B
解析: Where 的筛选在枚举时执行:虽然查询在 Add(4) 之前创建,但枚举发生在之后,能看到新元素,结果为 2,3,4。
关键点: 延迟执行 = 枚举时才取数据,集合随后变化会影响查询结果。
0448 以下代码输出什么?
难度: 进阶
IReadOnlyList<int> r = new List<int> { 1, 2, 3 };
((List<int>)r).Add(4);
Console.WriteLine(r.Count);
- A.
3 - B. 运行抛
InvalidCastException - C. 编译失败
- D.
4,IReadOnlyList<T>只保证通过接口只读,不保证对象不可变
查看答案与解析
正确答案D
解析: IReadOnlyList<T> 只是“只读视图”契约,底层对象仍可能是可变的 List<T>;调用方下转后就能修改,Count 变为 4。
关键点: 只读接口不等于不可变对象,防君子不防小人。
0449 以下代码输出什么?
难度: 进阶
IEnumerable<int> Get()
{
try
{
yield return 1;
}
finally
{
Console.Write("F");
}
}
using (var e = Get().GetEnumerator())
{
e.MoveNext();
}
- A. 输出
F,using结束时调用Dispose,触发finally - B. 无输出
- C. 编译失败
- D. 运行抛异常
查看答案与解析
正确答案A
解析: 迭代器对象实现 IDisposable;using 结束时即使只 MoveNext 了一次,也会调用 Dispose,编译器生成的迭代器随即执行 finally,输出 F。
关键点: 提前结束枚举时,finally 由 Dispose 触发,这正是 foreach 能清理资源的原因。
0450 以下代码输出什么?
难度: 进阶
IEnumerable<string> strings = new List<string> { "a" };
IEnumerable<object> objects = strings;
Console.WriteLine(objects.Count());
- A. 编译失败,
IEnumerable<T>不支持协变 - B. 运行抛
InvalidCastException - C.
1,IEnumerable<out T>协变,string可隐式转换为object - D. 输出
0
查看答案与解析
正确答案C
解析: IEnumerable<out T> 的类型参数标了 out,支持协变:IEnumerable<string> 可隐式转换为 IEnumerable<object>,枚举得到 1 个元素。注意 IList<T> 等可写接口不协变。
关键点: 只读接口协变、可写接口不变,是接口型变设计的核心。
0451 公开 API 想返回“只读集合”,应如何声明?
难度: 进阶
- A. 直接返回
List<T>,调用方最方便 - B. 按调用方需求声明
IReadOnlyList<T>/IReadOnlyCollection<T>等最小接口;同时注意调用方仍可下转为具体类型修改 - C. 声明
IEnumerable<T>永远优于IReadOnlyList<T> - D. 返回数组,数组内容一定不可变
查看答案与解析
正确答案B
解析: 面向接口编程:按实际需要的最小能力声明(要下标用 IReadOnlyList,只要 Count 用 IReadOnlyCollection,只枚举用 IEnumerable)。但下转仍可能修改原对象,所以“只读”不等于“防篡改”。
关键点: 最小接口约束意图,数组与 List 都非真正不可变。
0452 以下代码输出什么?
难度: 实战
var a = new HashSet<int> { 1, 2, 3 };
var b = new HashSet<int> { 3, 4, 5 };
a.SymmetricExceptWith(b);
Console.WriteLine(string.Join(",", a));
- A.
1,2,3,4,5 - B.
3 - C.
1,2 - D.
1,2,4,5,对称差 = 只出现在其中一个集合里的元素
查看答案与解析
正确答案D
解析: SymmetricExceptWith 原地计算对称差:元素 3 同时存在于两边被移除,剩下 1,2,4,5。
关键点: 对称差是“并集减交集”,与 ExceptWith(差集)语义不同。
0453 以下代码输出什么?
难度: 进阶
IReadOnlySet<int> set = new HashSet<int> { 1, 2 };
Console.WriteLine(set.IsSubsetOf(new[] { 1, 2, 3 }));
- A.
True,IReadOnlySet<T>自 .NET 5 起存在,HashSet<T>实现了它 - B. 编译失败,不存在
IReadOnlySet<T> - C.
False - D. 运行抛异常
查看答案与解析
正确答案A
解析: IReadOnlySet<T> 从 .NET 5 引入,HashSet<T> 与 SortedSet<T> 都实现了它,IsSubsetOf 判断集合是否被另一集合包含。
关键点: 只读集合接口比很多人以为的丰富,IReadOnlySet<T> 不是“只有 Count 和 Contains”。
0454 以下代码输出什么?
难度: 进阶
IList<int> list = new int[3];
list[0] = 5;
Console.WriteLine(list[0] + " " + list.IsReadOnly);
- A.
5 False - B.
0 True - C.
5 True,数组的IList<T>索引器可写,但IsReadOnly为 true - D. 运行抛
NotSupportedException
查看答案与解析
正确答案C
解析: 数组实现 IList<T> 时,索引器的 setter 正常工作(可以改元素值),但 IsReadOnly 恒为 true,因为数组长度固定、不能增删元素。
关键点: “只读”指结构不可变(不能增删),不代表元素不可改。
0455 以下代码运行结果是什么?
难度: 实战
var xs = new List<int>();
Console.WriteLine(xs.First());
- A. 输出
0 - B. 运行抛
InvalidOperationException - C. 运行抛
IndexOutOfRangeException - D. 输出
null
查看答案与解析
正确答案B
解析: First() 要求序列至少一个元素,空序列抛 InvalidOperationException;它不会返回默认值,那是 FirstOrDefault 的职责。
关键点: First/单值系列与 OrDefault 系列失败行为不同。
0456 以下代码输出什么?
难度: 进阶
HashSet<int> set = new HashSet<int>();
Console.WriteLine(set is ISet<int>);
Console.WriteLine(set is IReadOnlySet<int>);
- A.
True和False - B.
False和True - C.
False和False - D.
True和True,HashSet<T>同时实现ISet<T>与IReadOnlySet<T>
查看答案与解析
正确答案D
解析: HashSet<T> 实现了 ISet<T>、ICollection<T>、IReadOnlySet<T>、IReadOnlyCollection<T> 等;同一个对象可以同时以读写和只读接口访问。
关键点: 一个类型实现多个接口很常见,“是只读接口的实现”不代表对象不可变。
0457 以下代码输出什么?
难度: 实战
var set = new SortedSet<int> { 3, 1, 2 };
Console.WriteLine(string.Join(",", set));
- A.
1,2,3,SortedSet按比较器升序枚举 - B.
3,1,2 - C.
3,2,1 - D. 枚举顺序未定义
查看答案与解析
正确答案A
解析: SortedSet<T> 按默认比较器维护有序,枚举输出 1,2,3;而 HashSet<T> 的契约不保证任何枚举顺序。
关键点: 需要有序枚举用 SortedSet,需要顺序保证时不要依赖 HashSet。
0458 以下代码输出什么?
难度: 实战
ICollection<int> c = new List<int> { 1, 2, 3 };
Console.WriteLine(c.IsReadOnly);
c.Remove(2);
Console.WriteLine(c.Count);
- A.
True 2 - B.
False 3 - C.
False 2 - D.
True 3
查看答案与解析
正确答案C
解析: List<T> 的 IsReadOnly 为 false,Remove(2) 按值移除第一个匹配项,Count 从 3 变为 2。
关键点: 可变集合的 IsReadOnly 是 false;Remove 按值删、RemoveAt 按下标删。
0459 关于 ICollection<T> 与 IReadOnlyCollection<T> 的区别,哪项正确?
难度: 进阶
- A. 两者都保证集合不可变
- B.
ICollection<T>暴露Add/Remove/Contains/IsReadOnly等成员;IReadOnlyCollection<T>只有Count与枚举能力,用于表达“只读视图”意图 - C.
IReadOnlyCollection<T>比IEnumerable<T>少一个成员 - D.
List<T>只实现了IReadOnlyCollection<T>
查看答案与解析
正确答案B
解析: ICollection<T> 是“可读写集合”的通用契约(含 IsReadOnly 标记);IReadOnlyCollection<T> 只有 Count,比 IEnumerable<T> 多出“已知元素数”的承诺。
关键点: 接口的“readonly”只描述访问方式,不描述底层对象的可改性。
0460 关于“返回 IEnumerable<T> 就是不可变集合”的说法,哪项正确?
难度: 进阶
- A. 返回
IEnumerable<T>后调用方无法修改原集合 - B.
IEnumerable<T>一定是惰性序列 - C.
IEnumerable<T>保证最多枚举一次 - D. 接口类型只约束通过该接口的访问:调用方可下转修改,也可多次枚举,惰性查询还会重复执行
查看答案与解析
正确答案D
解析: IEnumerable<T> 既不防下转修改,也不防多次枚举;列表、数组等内存集合可反复枚举,LINQ 惰性查询每次枚举都会重新执行。它只是“可枚举”的最小契约。
关键点: 返回类型表达意图,不提供“不可变/单次”的运行时保证。