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

C# 试题 21:Span 与 Memory

0401 以下代码输出什么?

难度: 实战

int[] a = { 1, 2, 3 };
Span<int> s = a;
s[0] = 42;

Console.WriteLine(a[0]);
  • A. 1
  • B. 42Span 是数组元素的共享视图
  • C. 编译失败,Span 不能指向托管数组
  • D. 运行抛 IndexOutOfRangeException
查看答案与解析

正确答案B

解析: Span<T> 由数组构造时直接引用底层存储,写入 s[0] 等价于写入 a[0],与数组切片是拷贝语义完全不同。

关键点: Span 是视图不是副本,读写都会穿透到被引用的存储。

0402 以下代码输出什么?

难度: 实战

int[] a = { 1, 2, 3, 4, 5 };
Span<int> s = a.AsSpan(1, 3);
s[0] = 99;

Console.WriteLine(string.Join(",", a));
  • A. 1,2,3,4,5
  • B. 1,2,99,4,5
  • C. 99,2,3,4,5
  • D. 1,99,3,4,5
查看答案与解析

正确答案D

解析: AsSpan(1, 3) 创建指向 a[1..4] 的视图,s[0]a[1],所以改为 99 后数组变成 1,99,3,4,5

关键点: Span 切片零拷贝、共享存储,修改视图会反映到原数组。

0403 以下代码输出什么?

难度: 实战

ReadOnlySpan<char> s = "hello";

Console.WriteLine(s.Length);
Console.WriteLine(s[0]);
  • A. 5h
  • B. 编译失败,string 不能隐式转换为 ReadOnlySpan<char>
  • C. 50
  • D. 运行抛 NullReferenceException
查看答案与解析

正确答案A

解析: C# 11 起 string 可隐式转换为 ReadOnlySpan<char>,视图直接引用字符串内部存储,s[0]h

关键点: 字符串只能得到只读视图,不能得到可写的 Span<char>

0404 以下代码能否编译?

难度: 进阶

ReadOnlySpan<int> s = new int[] { 1, 2, 3 };

s[0] = 9;
  • A. 编译通过,写入成功
  • B. 运行抛 NotSupportedException
  • C. 编译失败,ReadOnlySpan<T> 的索引器只有 getter
  • D. 运行抛 InvalidOperationException
查看答案与解析

正确答案C

解析: ReadOnlySpan<T> 的索引器是只读的,s[0] = 9 在编译期就报错;它不是运行期拒绝,而是类型系统层面的约束。

关键点: 只读性是编译期强制,不是运行期异常。

0405 以下代码输出什么?

难度: 实战

Span<int> s = stackalloc int[4];
s[0] = 7;

Console.WriteLine(s[0] + " " + s.Length);
  • A. 0 4
  • B. 7 4
  • C. 编译失败,stackalloc 必须写在 unsafe
  • D. 运行抛 StackOverflowException
查看答案与解析

正确答案B

解析:stackalloc 结果赋给 Span<T> 是受支持的安全写法,不需要 unsafe;内存分配在线程栈上,写入 s[0] 得到 7,长度为 4。

关键点: Span<T> = stackalloc ... 是安全代码,不等于指针。

0406 以下代码能否编译?

难度: 进阶

async Task M()
{
    Span<int> s = stackalloc int[4];
    await Task.Delay(1);
    Console.WriteLine(s[0]);
}
  • A. 编译通过并正常运行
  • B. 运行抛 InvalidOperationException
  • C. 编译失败,stackalloc 需要 unsafe
  • D. 编译失败,Span 是 ref struct,不能跨 await 保留
查看答案与解析

正确答案D

解析: async 方法的状态机要保存局部变量,而 ref struct 不能被存入状态机;即使代码里只有一次 await,编译器也会直接报错(CS4007)。

关键点: “没用 await 跨过它”也不行,async 方法里根本不能声明 ref struct 局部变量。

0407 以下代码输出什么?

难度: 实战

async Task M()
{
    var arr = new int[10];
    Memory<int> m = arr;
    await Task.Delay(1);
    m.Span[0] = 42;
    Console.WriteLine(arr[0]);
}

await M();
  • A. 输出 42Memory<T> 可以跨 await 传递
  • B. 编译失败
  • C. 输出 0
  • D. 运行抛 NullReferenceException
查看答案与解析

正确答案A

解析: Memory<T> 是普通结构,可以存进 async 状态机、跨 await 传递;await 之后再取 .Span 使用是合法的,写入会反映到原数组。

关键点: Span 不能跨 await,Memory 可以;需要“取 Span”的时机是同步使用点。

0408 以下代码能否编译?

难度: 进阶

class Buffer
{
    private Span<int> _data;
}
  • A. 编译通过,Span 可以作为普通类字段
  • B. 编译通过,但运行抛异常
  • C. 编译失败,ref struct 不能作为普通类的字段
  • D. 编译失败,字段名不能以下划线开头
查看答案与解析

正确答案C

解析: ref struct 的实例可能指向栈内存,因此不能作为普通类/结构的字段;只有 ref struct 的实例成员才能持有 Span<T> 字段(CS8345)。

关键点: “Span 不能存字段”只针对普通类型,ref struct 内部可以。

0409 以下代码能否编译?

难度: 进阶

Span<int> s = new int[3];
object o = s;
  • A. 编译通过并完成装箱
  • B. 编译失败,ref struct 不能被装箱
  • C. 运行抛 InvalidOperationException
  • D. 编译通过,o 得到数组副本
查看答案与解析

正确答案B

解析: 装箱要求把值复制到堆上的对象里,ref struct 生命周期受限、不能上堆,所以任何“转 object/接口”的装箱尝试都在编译期失败。

关键点: ref struct 不能装箱是硬性规则,Span 尤其如此。

0410 以下代码输出什么?

难度: 进阶

int[] a = { 1, 2, 3, 4, 5 };
a.AsSpan(0, 4).CopyTo(a.AsSpan(1, 4));

Console.WriteLine(string.Join(",", a));
  • A. 1,1,1,1,1
  • B. 1,2,3,4,5
  • C. 2,3,4,5,5
  • D. 1,1,2,3,4
查看答案与解析

正确答案D

解析: MemoryExtensions.CopyTo 对重叠区间按“先拷贝到临时位置”的语义处理(类似 memmove):源 1,2,3,4 完整拷到目标后,数组为 1,1,2,3,4

关键点: CopyTo 重叠安全;手写前向循环复制才会被污染。

0411 以下代码输出什么?

难度: 实战

int[] a = { 1, 2, 3, 4, 5 };
for (int i = 0; i < 4; i++)
{
    a[i + 1] = a[i];
}

Console.WriteLine(string.Join(",", a));
  • A. 1,1,1,1,1,前向复制被已覆盖的值污染
  • B. 1,1,2,3,4
  • C. 2,3,4,5,5
  • D. 1,2,3,4,5
查看答案与解析

正确答案A

解析: 前向循环里 a[1] 先被改成 1,随后 a[2] = a[1] 读到的已是新值 1,依次传播,最终全部变成 1;这就是重叠复制要用 CopyTo/Array.Copy 的原因。

关键点: 重叠复制必须用库函数或反向循环,手写前向循环是经典 bug。

0412 以下代码能否编译?

难度: 进阶

var list = new List<Span<int>>();
  • A. 编译通过
  • B. 运行抛 TypeInitializationException
  • C. 编译失败,ref struct 不能作为泛型类型参数
  • D. 编译通过,但只能存 8 个元素
查看答案与解析

正确答案C

解析: ref struct 不能作为泛型类型实参(CS9244),因此 List<Span<int>>Dictionary<..., Span<T>> 等都无法编译;跨泛型传递需用 Memory<T>

关键点: 需要“集合存视图”时改用 Memory<T>,它是普通结构。

0413 以下代码能否编译?

难度: 实战

string text = "abc";
Span<char> s = text.AsSpan();
  • A. 编译通过,获得可写视图
  • B. 运行抛 InvalidOperationException
  • C. 运行抛 NotSupportedException
  • D. 编译失败,string 只能得到 ReadOnlySpan<char>,不能隐式转 Span<char>
查看答案与解析

正确答案D

解析: string.AsSpan() 返回 ReadOnlySpan<char>,只读视图不能赋给可写的 Span<char>(CS0029)。想写字符缓冲得自己分配数组或 StringBuilder

关键点: 字符串内部存储不可写,类型系统保证这一点。

0414 以下代码能否编译?

难度: 进阶

Span<string> s = stackalloc string[3];
  • A. 编译失败,stackalloc 只能用于非托管类型,string 是托管引用类型
  • B. 编译通过,string 也可以 stackalloc
  • C. 运行抛 OutOfMemoryException
  • D. 编译失败,stackalloc 必须写在 unsafe
查看答案与解析

正确答案A

解析: stackalloc 只允许非托管类型(CS0208);栈上不能存放引用类型,因此 stackalloc string[...]Span<object> 这类写法无法编译。

关键点: “stackalloc 返回 Span”成立的前提是元素类型非托管。

0415 以下代码输出什么?

难度: 实战

Span<int> s = stackalloc int[] { 1, 2, 3 };

Console.WriteLine(s[2]);
  • A. 0
  • B. 3
  • C. 编译失败
  • D. 运行抛 IndexOutOfRangeException
查看答案与解析

正确答案B

解析: stackalloc 支持集合初始化器写法,s 指向栈上的 {1,2,3}s[2] 是 3。

关键点: 初始化列表与长度两种 stackalloc 写法都返回 Span<T>

0416 以下代码的问题在哪?

难度: 实战

byte[] arr = ArrayPool<byte>.Shared.Rent(100);
Memory<byte> m = arr;
ArrayPool<byte>.Shared.Return(arr);

m.Span[0] = 1;   // 归还后继续写
  • A. 没有问题,Memory<T> 是安全拷贝
  • B. 编译失败,Memory<byte> 不能由数组构造
  • C. 归还后 m 仍指向原数组,池可能已把它复用于其他请求,继续写会破坏池中数据
  • D. 运行抛 ObjectDisposedException
查看答案与解析

正确答案C

解析: Memory<T> 只是数组的引用,不会阻止归还;Return 之后通过 m 继续写入就是“归还后继续使用”,属于未定义行为,可能污染其他租借者的数据。这里不会自动抛异常,正因如此更危险。

关键点: 生命周期责任在调用方:归还前完成所有读写,之后不要再持有引用。

0417 同步方法里只需只读遍历一段字符串,不跨 await、不存字段,应优先选择哪种类型?

难度: 进阶

  • A. Memory<char>,可跨 await
  • B. string,直接下标最省事
  • C. ReadOnlyMemory<char>,一定不分配
  • D. ReadOnlySpan<char>,同步场景零开销、无额外分配
查看答案与解析

正确答案D

解析: 同步、只读、不逃逸的场景正是 ReadOnlySpan<char> 的目标用途:不分配、不拷贝、直接引用原存储;Memory 是为跨 async/存字段设计的。

关键点: 选型口诀:同步用 Span,异步/字段用 Memory,只读加 ReadOnly 前缀。

0418 以下代码输出什么?

难度: 进阶

ReadOnlySpan<int> s = stackalloc int[] { 5, 6, 7 };

Console.WriteLine(s[0]);
  • A. 5
  • B. 0
  • C. 编译失败,ReadOnlySpan 不能由 stackalloc 构造
  • D. 运行抛异常
查看答案与解析

正确答案A

解析: Span<T> 可以隐式转换为 ReadOnlySpan<T>,所以 stackalloc 结果赋给只读视图同样合法;s[0] 是 5,且视图只读。

关键点: Span → ReadOnlySpan 是隐式转换,反向不行。

0419 关于 Span<T>Memory<T> 的本质区别,哪项正确?

难度: 进阶

  • A. Span<T> 分配在堆上,Memory<T> 分配在栈上
  • B. Span<T> 是 ref struct,只能存在于栈上、不能装箱或存字段;Memory<T> 是普通结构,可存字段、可跨 async
  • C. 两者都不能指向托管数组
  • D. Memory<T> 的性能总是优于 Span<T>
查看答案与解析

正确答案B

解析: Span<T> 是 ref struct,受限最多但开销最低;Memory<T> 是普通结构,能被装箱、存字段、跨 async,代价是访问元素前要先取 .Span

关键点: 约束不同源于“是否可能逃逸到堆上”,而不是功能强弱。

0420 以下代码输出什么?

难度: 实战

ref struct Buffer
{
    public Span<int> Data;
}

var b = new Buffer { Data = new int[3] };
b.Data[0] = 1;

Console.WriteLine(b.Data[0]);
  • A. 编译失败,ref struct 不能包含 Span 字段
  • B. 运行抛 NullReferenceException
  • C. 输出 1,ref struct 可以包含 Span<T> 字段
  • D. 输出 0
查看答案与解析

正确答案C

解析: ref struct 的实例成员可以持有 Span<T> 字段,只要整个类型不逃逸到堆上;本例编译运行正常,输出 1。

关键点: “Span 不能做字段”仅限普通 class/struct,ref struct 内部是允许的。

官方资料

当前分类

C# 选择题

查看全部分类 →
  1. 19C# 试题 19:异常处理20 题
  2. 20C# 试题 20:数组、Index 与 Range20 题
  3. 21C# 试题 21:Span 与 Memory20 题
  4. 22C# 试题 22:字符串与文本处理20 题
  5. 23C# 试题 23:集合接口20 题
ESC

输入关键词开始搜索