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

C# 试题 12:record、init 与 required

0221 以下代码输出什么?

难度: 实战

record Point(int X, int Y);

class CPoint
{
    public int X { get; }
    public int Y { get; }
    public CPoint(int x, int y) { X = x; Y = y; }
}

var r1 = new Point(1, 2);
var r2 = new Point(1, 2);
var c1 = new CPoint(1, 2);
var c2 = new CPoint(1, 2);

Console.WriteLine(r1 == r2);
Console.WriteLine(c1 == c2);
  • A. TrueFalse,record 默认值相等,普通 class 默认引用相等
  • B. TrueTrue
  • C. FalseFalse
  • D. FalseTrue
查看答案与解析

正确答案A

解析: record 合成基于成员的 Equals==;普通 class 未重载时 == 比较引用。这是 record 最核心的卖点,也是最容易记反的点。

关键点: record 带来“值相等”,class 默认“引用相等”。

0222 以下代码输出什么?

难度: 进阶

record Bag(int[] Items);

var a = new Bag(new[] { 1, 2 });
var b = new Bag(new[] { 1, 2 });

Console.WriteLine(a == b);
  • A. 输出 True
  • B. 输出 True,数组被自动展开比较
  • C. 输出 False,record 相等性是浅比较,数组成员按引用比较
  • D. 编译失败,record 不能包含数组
查看答案与解析

正确答案C

解析: record 的 Equals 逐成员调用成员的 Equals;数组没有值相等语义,比较的是引用,内容相同也返回 false。record 不是深比较。

关键点: record 相等性是“成员级”浅比较,不是递归深比较。

0223 以下代码能否编译?

难度: 实战

record Point(int X, int Y);

var p = new Point(1, 2);
p.X = 9;
  • A. 编译通过,输出 9
  • B. 编译失败,位置 record class 的属性是 init,构造完成后不可赋值
  • C. 编译通过,赋值被忽略
  • D. 运行抛 InvalidOperationException
查看答案与解析

正确答案B

解析: 位置 record class 合成 get; init; 属性;想“改”只能通过 with 生成新实例。

关键点: record class 不可变,record struct 可变,别混淆。

0224 以下代码输出什么?

难度: 进阶

record Base(int X);
record Derived(int X, int Y) : Base(X);

Base b = new Derived(1, 2);
Base c = b with { X = 5 };

Console.WriteLine(c.GetType().Name);
  • A. 输出 Base
  • B. 编译失败,基类引用不能用 with
  • C. 输出 Object
  • D. 输出 Derivedwith 保持运行时类型,通过复制构造函数重建
查看答案与解析

正确答案D

解析: with 调用复制构造函数,复制构造函数保留派生类型;即使静态类型是 Base,结果运行时类型仍是 Derived

关键点: with 是“同类型复制 + 修改”,不会退化成基类。

0225 以下代码输出什么?

难度: 实战

record R(int[] Items);

var a = new R(new[] { 1, 2 });
var b = a with { };

Console.WriteLine(ReferenceEquals(a.Items, b.Items));
  • A. 输出 Truewith 是浅拷贝,引用成员仍然共享
  • B. 输出 Falsewith 深拷贝所有成员
  • C. 编译失败
  • D. 输出 False,数组被复制
查看答案与解析

正确答案A

解析: with 只复制成员引用本身,不复制引用指向的对象;Items 数组在 ab 之间共享。修改数组内容会同时影响两个记录。

关键点: 不可变 record + 可变引用成员 = 可被观察到的“共享突变”。

0226 以下代码能否编译?

难度: 实战

class C
{
    public int X { get; init; }
}

var c = new C { X = 1 };
c.X = 2;
  • A. 编译通过,输出 2
  • B. 编译通过,输出 1
  • C. 编译失败,init 属性只能在对象初始化阶段赋值
  • D. 运行抛 InvalidOperationException
查看答案与解析

正确答案C

解析: init-only 的赋值窗口是对象初始化器、with 表达式与(含派生类的)构造函数;构造完成后任何赋值都是编译错误。

关键点: init 是“编译期窗口”,不是运行时校验。

0227 以下代码输出什么?

难度: 进阶

class C
{
    public int X { get; init; }
    public C(int x) { X = x; }
}

Console.WriteLine(new C(5).X);
  • A. 编译失败,构造函数不能给 init 属性赋值
  • B. 输出 5,声明类型的构造函数可以给 init 属性赋值
  • C. 输出 0
  • D. 编译失败,init 属性必须有 setter
查看答案与解析

正确答案B

解析: init 访问器可在本类型构造函数(以及派生类构造函数)中赋值,这是官方允许的初始化路径之一。

关键点: 构造函数是 init 的合法赋值点。

0228 以下代码能否编译?

难度: 实战

class User
{
    public required string Name { get; init; }
}

var u = new User();
  • A. 编译通过,Namenull
  • B. 编译通过,Name 为空串
  • C. 运行抛 MissingMemberException
  • D. 编译失败,required 成员必须通过对象初始化器或带 [SetsRequiredMembers] 的构造函数赋值
查看答案与解析

正确答案D

解析: required 是编译期契约:调用方未设置 required 成员即报编译错误。它把“忘记初始化”从运行时提前到编译期。

关键点: required 让“漏初始化”无法编译通过。

0229 以下代码输出什么?

难度: 进阶

using System.Diagnostics.CodeAnalysis;

class User
{
    public required string Name { get; init; }

    [SetsRequiredMembers]
    public User() { }
}

var u = new User();

Console.WriteLine(u.Name is null);
  • A. 输出 True[SetsRequiredMembers] 声明“构造函数已设置全部 required 成员”,编译器不再校验,但构造函数实际没赋值
  • B. 输出 False
  • C. 编译失败
  • D. 运行抛 NullReferenceException
查看答案与解析

正确答案A

解析: [SetsRequiredMembers] 是给编译器的承诺而非检查;构造函数未真正赋值时,调用方仍能编译通过,Name 在运行时是 null。这把编译期保证重新变成了运行时风险。

关键点: required 的可信度取决于构造函数是否诚实实现。

0230 关于 required 的语义,哪项正确?

难度: 进阶

  • A. required 只允许用在 init 属性上
  • B. required 会改变类型的运行时内存布局
  • C. required 是编译期契约:要求调用方通过对象初始化器(或 [SetsRequiredMembers] 构造函数)提供值;反射、序列化等运行时路径不会自动获得该校验
  • D. required 成员在 new 时自动填充默认值
查看答案与解析

正确答案C

解析: required 通过编译器强制调用方初始化;它不改变 IL 布局,也没有运行时拦截。依赖反射/反序列化创建对象时,仍需自行校验。

关键点: required 的“强制”止步于编译期。

0231 以下代码输出什么?

难度: 实战

record Base(int X);
record Derived(int X, int Y) : Base(X);

var d1 = new Derived(1, 2);
var d2 = new Derived(1, 2);

Console.WriteLine(d1 == d2);
  • A. 输出 False,继承使相等性失效
  • B. 输出 True,相同运行时类型的派生记录按成员值比较
  • C. 编译失败,record 之间不能继承
  • D. 输出 False,派生记录退化为引用比较
查看答案与解析

正确答案B

解析: 两个 Derived 实例运行时类型一致,Equals 比较 XY(先比对 EqualityContract),值相同即为相等。

关键点: record 继承下,相等性 = 运行时类型 + 成员值。

0232 以下代码能否编译?

难度: 进阶

record Base(int X);

class Derived : Base
{
    public Derived() : base(1) { }
}
  • A. 编译通过
  • B. 编译通过,但相等性失效
  • C. 编译通过,输出 0
  • D. 编译失败,record 只能被 record 继承,普通 class 不能继承 record
查看答案与解析

正确答案D

解析: record class 携带“密封的相等性契约”,其派生类型必须仍是 record;普通 class 继承 record 是编译错误。

关键点: record 的继承树里只能有 record。

0233 以下代码输出什么?

难度: 实战

record Base(int X);
record Derived(int X, int Y) : Base(X);

Console.WriteLine(new Derived(1, 2));
  • A. 输出 Derived { X = 1, Y = 2 },派生位置 record 的主构造函数显式调用基类主构造函数
  • B. 编译失败,派生 record 必须重新声明 X
  • C. 输出 Base { X = 1 }
  • D. 输出 Derived { Y = 2 }
查看答案与解析

正确答案A

解析: record Derived(int X, int Y) : Base(X)X 转发给基类主构造函数;ToString 输出全部成员(基类 + 派生)。

关键点: 位置 record 继承 = 主构造函数 + 基类参数转发。

0234 以下代码输出什么?

难度: 进阶

record Base(int X);
record Derived(int X, int Y) : Base(X);

Base a = new Base(1);
Base b = new Derived(1, 2);

Console.WriteLine(a == b);
  • A. 输出 True,成员值相同
  • B. 输出 TrueBase 引用只比较 X
  • C. 输出 False,record 相等性包含运行时类型(EqualityContract),基类与派生类实例永不相等
  • D. 编译失败
查看答案与解析

正确答案C

解析: record 的 Equals 先比较 EqualityContract(运行时类型),类型不同直接返回 false,即使成员值相同。这避免了“不同派生记录因成员相同而相等”的破坏性语义。

关键点: record 继承的相等性带“类型指纹”。

0235 record 含有可变引用成员(如 List<int>)时,关于相等性哪项正确?

难度: 进阶

  • A. record 会自动深比较集合内容
  • B. 相等性与哈希只“浅层”覆盖该成员:引用不同内容相同仍不相等;可变成员参与哈希会在内容变化后破坏 HashSet/字典不变量
  • C. 可变引用成员使 record 无法编译
  • D. 只要属性是 init,比较就一定安全
查看答案与解析

正确答案B

解析: record 的比较停留在“成员引用”层面;集合内容变化不会更新哈希,放入 HashSet/字典后可能找不到原对象。设计值对象时,参与相等的成员应保持稳定。

关键点: 值语义不等于“对可变内容的深比较”。

0236 以下代码输出什么?

难度: 进阶

class Base
{
    public int X { get; init; }
}

class Derived : Base
{
    public Derived() { X = 1; }
}

Console.WriteLine(new Derived().X);
  • A. 编译失败,派生类不能给基类的 init 属性赋值
  • B. 输出 0
  • C. 输出 1,但运行抛异常
  • D. 输出 1,派生类构造函数在对象仍处于构造阶段时,可以给基类 init 属性赋值
查看答案与解析

正确答案D

解析: init 的赋值窗口覆盖“构造阶段”,包括派生类构造函数(官方示例行为)。构造完成后才禁止。

关键点: init 的边界是“构造阶段”,不是“本类型内部”。

0237 以下代码输出什么?

难度: 实战

class User
{
    public required string Name { get; set; }
}

var u = new User { Name = "a" };
u.Name = "b";

Console.WriteLine(u.Name);
  • A. 输出 b,required 不要求 init,配 set 的属性同样合法且仍可变
  • B. 输出 a
  • C. 编译失败,required 只能搭配 init
  • D. 编译失败,required 属性不可变
查看答案与解析

正确答案A

解析: required 只要求“调用方必须初始化”,访问器可以是 set 甚至字段;初始化之后仍可自由修改。

关键点: required 管“必须有初值”,不管“是否可变”。

0238 以下代码输出什么?

难度: 实战

record R(int X);

var a = new R(1);
var b = new R(1);

Console.WriteLine(Equals(a, b));
Console.WriteLine(ReferenceEquals(a, b));
Console.WriteLine(a == b);
  • A. True True True
  • B. False False False
  • C. True False True,record 重写了 Equals==,但对象身份仍独立
  • D. True True False
查看答案与解析

正确答案C

解析: record 合成 Equals(值相等)和 ==,但不改变对象身份;ReferenceEquals 永远是引用比较。三者语义不同,容易混为一谈。

关键点: 值相等 ≠ 同一实例;ReferenceEquals 永远看引用。

0239 关于 with 表达式,哪项正确?

难度: 进阶

  • A. 任何 class 都可以使用 with
  • B. with 要求类型提供复制构造函数(record 自动生成),返回与接收者相同运行时类型的新对象,原对象不变
  • C. with 会原地修改原对象
  • D. with 只能用于 record struct
查看答案与解析

正确答案B

解析: record 自动生成受保护复制构造函数供 with 使用;普通 class 没有它,不能直接用 withwith 生成新对象,原对象不变,且保持运行时类型。

关键点: with 是“复制 + 改”,不是“改 + 回写”。

0240 以下代码输出什么?

难度: 实战

record Base(int X);
record Derived(int X, int Y) : Base(X);

var d = new Derived(1, 2);
var d2 = d with { Y = 9 };

Console.WriteLine(d2);
  • A. 编译失败
  • B. 输出 Derived { X = 1, Y = 2 }
  • C. 输出 Base { X = 1 }
  • D. 输出 Derived { X = 1, Y = 9 }with 保持派生类型并只更新指定成员
查看答案与解析

正确答案D

解析: with 复制整个派生对象(含基类成员),应用 Y = 9 修改,其余保持不变;输出展示全部成员。

关键点: 继承场景下 with 依然保持派生类型,不丢基类成员。

官方资料

当前分类

C# 选择题

查看全部分类 →
  1. 10C# 试题 10:接口与抽象类20 题
  2. 11C# 试题 11:结构体与 ref struct20 题
  3. 12C# 试题 12:record、init 与 required20 题
  4. 13C# 试题 13:枚举与元组20 题
  5. 14C# 试题 14:模式匹配20 题
ESC

输入关键词开始搜索