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

C# 试题 11:结构体与 ref struct

0201 以下代码输出什么?

难度: 实战

struct Point { public int X; }

var a = new Point { X = 1 };
var b = a;
b.X = 2;

Console.WriteLine($"{a.X},{b.X}");
  • A. 1,1
  • B. 1,2,结构体赋值逐字段复制,b 是独立副本
  • C. 2,2
  • D. 编译失败,结构体没有复制语义
查看答案与解析

正确答案B

解析: 结构体赋值复制全部字段;修改 b.X 不影响 a。这与引用类型赋值产生“别名”的行为正好相反。

关键点: 结构体赋值 = 复制,不是建立引用。

0202 以下代码输出什么?

难度: 实战

struct Point { public int X; }

static void Move(Point p) => p.X = 99;

var p = new Point { X = 1 };
Move(p);

Console.WriteLine(p.X);
  • A. 输出 1,按值传参会复制,方法内修改不影响实参
  • B. 输出 99
  • C. 编译失败,结构体不能作为参数
  • D. 输出 0
查看答案与解析

正确答案A

解析: 默认按值传递,Move 修改的是副本。想让修改回流,必须用 ref(或可变语义的 in 之外的其他方案)。

关键点: 值类型参数默认复制,修改不回流。

0203 以下代码输出什么?

难度: 进阶

struct Counter { public int Value; public void Increment() => Value++; }

static void Take(in Counter c) => c.Increment();

var c = new Counter { Value = 0 };
Take(c);

Console.WriteLine(c.Value);
  • A. 输出 1
  • B. 编译失败,不能对 in 参数调用实例方法
  • C. 输出 0in 参数是只读引用,调用可变方法前编译器先做防御性复制
  • D. 运行抛 InvalidOperationException
查看答案与解析

正确答案C

解析: Increment 不是 readonly 成员,编译器在只读的 in 引用上先复制一份再调用,修改落在副本上,原值不变。这也意味着“用 in 省复制”的前提是成员都只读。

关键点: in 传参遇到可变成员会触发防御性复制。

0204 以下代码输出什么?

难度: 实战

struct Point { public int X; }

static void Move(ref Point p) => p.X = 99;

var p = new Point { X = 1 };
Move(ref p);

Console.WriteLine(p.X);
  • A. 输出 1
  • B. 编译失败,结构体不能用 ref 传递
  • C. 输出 0
  • D. 输出 99ref 传递的是实参的存储位置
查看答案与解析

正确答案D

解析: ref 参数共享实参的存储位置,方法内对字段的修改直接作用于原变量,没有复制。

关键点: ref 让值类型也具备“别名”语义。

0205 以下代码能否编译?

难度: 进阶

readonly struct S
{
    public int X;
}
  • A. 编译通过,X 自动只读
  • B. 编译失败,readonly 结构体的实例字段必须声明为 readonly
  • C. 编译通过,但运行抛异常
  • D. 编译失败,readonly 结构体不能有字段
查看答案与解析

正确答案B

解析: readonly struct 要求所有实例字段都带 readonly(CS8340),这是结构性约束,确保实例天然不可变。

关键点: readonly 是结构层面的强制,不只是文档约定。

0206 以下代码能否编译?

难度: 进阶

readonly struct S
{
    public readonly int X;
    public void Bad() => X = 1;
}
  • A. 编译失败,readonly 结构体的实例方法不能给字段赋值
  • B. 编译通过,赋值只在副本上发生
  • C. 运行抛 FieldAccessException
  • D. 编译通过,X 变为 1
查看答案与解析

正确答案A

解析: readonly 结构体的实例成员运行在 readonly 上下文中,写入字段是编译错误。想要“可写”只能靠构造或静态成员。

关键点: readonly 结构体禁止实例级写操作。

0207 以下代码能否编译?

难度: 实战

ref struct R { public int X; }

var r = new R { X = 1 };
object o = r;
  • A. 编译通过,发生装箱
  • B. 编译通过,不发生装箱
  • C. 编译失败,ref struct 不能被装箱(不能转换为 object 或接口)
  • D. 运行抛 InvalidCastException
查看答案与解析

正确答案C

解析: 装箱会把值移到托管堆上,ref struct 被禁止进入堆,因此到 object/接口的任何转换都是编译错误。

关键点: ref struct 生命周期约束的起点就是“不能装箱”。

0208 以下代码能否编译?

难度: 实战

ref struct R { }

class C
{
    R _r;
}
  • A. 编译通过,字段默认在栈上
  • B. 编译通过,但运行抛异常
  • C. 编译通过,自动转为引用类型
  • D. 编译失败,ref struct 不能作为非 ref struct 的实例字段
查看答案与解析

正确答案D

解析: 类实例分配在堆上,ref struct 字段会被搬进堆,违反栈约束;只有 ref struct 内部才能包含 ref struct 实例字段。

关键点: ref struct 只能活在栈上或 ref struct 内部。

0209 以下代码能否编译?

难度: 进阶

ref struct R { public int X; }

static Func<int> Make()
{
    R r = default;
    return () => r.X;
}
  • A. 编译通过
  • B. 编译失败,闭包会把 r 提升到堆上,ref struct 不能被捕获
  • C. 编译通过,但运行抛异常
  • D. 编译通过,返回 0
查看答案与解析

正确答案B

解析: lambda/局部函数捕获局部变量时,编译器会把它提升到堆分配的环境对象中;ref struct 不能进入堆,因此捕获是编译错误。

关键点: 任何“逃逸到堆”的路径对 ref struct 都是禁区。

0210 关于 ref struct 的生命周期约束,哪项正确?

难度: 进阶

  • A. ref struct 可以存储在 async 方法的局部变量中并跨越 await
  • B. ref struct 可以被装箱为 object
  • C. ref struct 不能装箱、不能作类字段、不能被闭包捕获、不能跨越 await,只能存活于栈帧或 ref struct 内部
  • D. ref struct 可以继承普通类
查看答案与解析

正确答案C

解析: ref struct 的所有限制都源于“不能进入托管堆”:装箱、类字段、闭包提升、async/iterator 的挂起存储都会把它搬到堆上,故全部禁止;结构体本就不能继承类。

关键点: 理解“堆 = 禁区”,就能推出全部限制。

0211 以下代码输出什么?

难度: 实战

record struct Point(int X, int Y);

var p1 = new Point(1, 2);
var p2 = new Point(1, 2);

Console.WriteLine(p1 == p2);
  • A. 输出 True,record struct 按成员值比较并生成 ==
  • B. 输出 False,结构体默认引用比较
  • C. 编译失败,结构体不能使用 ==
  • D. 输出 False,record struct 不做相等比较
查看答案与解析

正确答案A

解析: record struct 会合成基于成员的 EqualsGetHashCode==/!=;普通 struct 没有这个待遇。

关键点: record 家族自动获得值相等,普通 struct 不会。

0212 以下代码输出什么?

难度: 进阶

record struct Point(int X, int Y);

var p = new Point(1, 2);
p.X = 100;

Console.WriteLine(p.X);
  • A. 编译失败,位置参数生成的属性只读
  • B. 编译失败,record struct 不可变
  • C. 输出 1
  • D. 输出 100,位置 record struct 的属性是 get; set;,默认可变
查看答案与解析

正确答案D

解析: 位置 record struct 合成的是可写属性;只有 readonly record struct 才生成 init 只读属性。这与 record class 的不可变语义不同。

关键点: record struct 默认可变,别拿 record class 的印象套它。

0213 以下代码能否编译?

难度: 进阶

readonly record struct Point(int X, int Y);

var p = new Point(1, 2);
p.X = 100;
  • A. 编译通过,输出 100
  • B. 编译失败,readonly record struct 的位置属性是 init,只能在初始化时赋值
  • C. 编译通过,赋值被忽略
  • D. 运行抛 InvalidOperationException
查看答案与解析

正确答案B

解析: readonly record struct 合成只读 init 属性,构造完成后赋值是编译错误。加不加 readonly 决定了 record struct 的可变性。

关键点: readonly 前缀改变 record struct 的默认可变性。

0214 以下代码输出什么?

难度: 实战

record struct Point(int X, int Y);

var p = new Point(1, 2);
var q = p with { X = 9 };

Console.WriteLine($"{p.X},{q.X}");
  • A. 输出 1,9with 生成副本,原值不变
  • B. 输出 9,9
  • C. 编译失败,record struct 不支持 with
  • D. 输出 1,1
查看答案与解析

正确答案A

解析: with 表达式复制当前实例并应用修改:p 保持不变,q 是新副本。值语义下“复制不破坏源值”尤其直观。

关键点: with 不修改源值,始终产出新实例。

0215 大结构体传参的性能选型,哪项正确?

难度: 进阶

  • A. 值传递永远比 in
  • B. 只要声明了 in,就一定零复制
  • C. 值参数会复制整个结构体;in/ref readonly 传引用可避免复制,但访问非 readonly 成员会触发防御性复制,收益需基准验证
  • D. in 参数允许方法内修改实参
查看答案与解析

正确答案C

解析: in 是只读引用,不复制存储;但任何可能修改的访问(调用可变方法、访问带 setter 的属性)都会引入防御性复制。收益依赖调用模式与 JIT,必须实测。

关键点: 大结构体该不该用 in,先基准再下结论。

0216 以下代码能否编译?

难度: 实战

struct S { public int X; }

static void Bad(in S s) => s.X = 5;
  • A. 编译通过,修改被忽略
  • B. 编译通过,修改作用于实参
  • C. 编译通过,在副本上修改
  • D. 编译失败,in 参数是只读引用,不能直接赋值
查看答案与解析

正确答案D

解析: in 参数按只读引用传递,直接写入字段/属性是编译错误。这与“调用可变方法产生防御性复制”不同:显式赋值连复制都不会做,直接编译期拒绝。

关键点: in = 只读引用,写操作在编译期被禁止。

0217 以下代码能否编译?

难度: 进阶

struct Point { public int X; }

var a = new Point { X = 1 };
var b = new Point { X = 1 };

Console.WriteLine(a == b);
  • A. 输出 True
  • B. 编译失败,普通结构体不会自动生成 ==,需自行重载运算符
  • C. 输出 False
  • D. 运行抛 NotSupportedException
查看答案与解析

正确答案B

解析: 自定义 struct 不会像 record struct 那样合成 ==;未重载运算符时 == 不适用,编译失败。很多资深开发者会在这里踩坑。

关键点: 普通 struct 的“值相等”需要显式实现,不是默认行为。

0218 以下代码输出什么?

难度: 进阶

struct S
{
    public int X;
    public S() { X = 42; }
}

S a = new S();
S b = default;

Console.WriteLine($"{a.X},{b.X}");
  • A. 输出 42,0new S() 调用显式无参构造函数,default 跳过构造函数直接零初始化
  • B. 输出 42,42
  • C. 输出 0,0
  • D. 编译失败,结构体不能声明无参构造函数
查看答案与解析

正确答案A

解析: C# 10 起结构体可声明显式无参构造函数,new S() 会执行它;但 default(S) 不经过任何构造函数,字段全为零。二者语义必须区分。

关键点: default 永远绕开构造函数,这是结构体与类的关键差异。

0219 以下代码能否编译?

难度: 进阶

ref struct R { }

var list = new List<R>();
  • A. 编译通过,ref struct 是合法的泛型实参
  • B. 编译通过,但运行抛异常
  • C. 编译失败,List<T> 未声明 allows ref struct,ref struct 不能作为其类型实参
  • D. 编译通过,发生装箱
查看答案与解析

正确答案C

解析: C# 13 起 ref struct 只能用于带 allows ref struct 反约束的类型参数;List<T> 没有该标注,故编译失败。泛型支持 ref struct 是有条件的。

关键点: 泛型默认拒绝 ref struct,除非显式放行。

0220 大结构体使用 in 参数的正确姿势,哪项最准确?

难度: 进阶

  • A. 所有结构体都应默认用 in,收益必然为正
  • B. in 参数在方法内可以任意修改
  • C. in 与值参数在语义上完全相同
  • D. 对“大且只读”的结构体评估使用 in/ref readonly,留意非 readonly 成员的防御性复制,并用基准验证收益
查看答案与解析

正确答案D

解析: in 适合“大而只读”的结构体;小结构体或访问可变成员时,防御性复制可能抵消收益。选型必须结合调用点与基准数据。

关键点: in 是优化手段而非规则,一切以测量为准。

官方资料

当前分类

C# 选择题

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

输入关键词开始搜索