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

C# 试题 31:文件与流

0601 以下代码输出什么?

难度: 实战

var ms = new MemoryStream();
var sw = new StreamWriter(ms);
sw.Write("hello");

Console.WriteLine(ms.Length);
  • A. 0,字符还留在 StreamWriter 的内部缓冲区
  • B. 5
  • C. 抛 ObjectDisposedException
  • D. 编译失败
查看答案与解析

正确答案A

解析: StreamWriter 默认带 1024 字节缓冲区,Write("hello") 只写入缓冲区,尚未落到 MemoryStream;只有 FlushDispose 或缓冲区写满时才会真正写入底层流,因此此时 ms.Length 为 0。

关键点: 包装流的缓冲区不 Flush,底层流看不到数据。

0602 以下代码输出什么?

难度: 实战

var ms = new MemoryStream();
var sw = new StreamWriter(ms);
sw.Write("hello");
sw.Flush();

Console.WriteLine(ms.Length);
  • A. 0
  • B. 5,Flush 把缓冲区内容写入底层流
  • C. 抛 IOException
  • D. 输出结果不确定
查看答案与解析

正确答案B

解析: Flush() 把 StreamWriter 缓冲区中的 5 字节 UTF-8 数据写入底层 MemoryStream,此时 ms.Length 为 5。

关键点: 需要立即读取底层流时先 Flush,或直接依赖 using 块的 Dispose 隐式刷新。

0603 data.bin 内容只有 3 字节,以下代码输出什么?

难度: 实战

byte[] buf = new byte[1024];
using var fs = File.OpenRead("data.bin");

int n = fs.Read(buf, 0, buf.Length);
Console.WriteLine(n);
  • A. 1024
  • B. 0
  • C. 3,Read 返回实际读到的字节数,到文件末尾则不足请求量
  • D. 抛 EndOfStreamException
查看答案与解析

正确答案C

解析: Stream.Read 的返回值是“实际读到的字节数”,可能小于请求值;读到末尾返回 0 而不是抛 EndOfStreamExceptionEndOfStreamException 只来自 ReadExactlyStreamReader 按字节读取等特定 API。

关键点: 循环读流的终止条件是返回值 0,而不是“读到请求数量”。

0604 假设 stream 中只有 5 个字节,以下代码运行结果是什么?

难度: 进阶

byte[] buf = new byte[10];

Console.WriteLine(stream.ReadAtLeast(buf, 8));
  • A. 5
  • B. 8
  • C. 0
  • D. 抛 EndOfStreamException,ReadAtLeast 默认在读到 EOF 且不足 minimumBytes 时抛异常
查看答案与解析

正确答案D

解析: Stream.ReadAtLeast(.NET 7+)默认 throwOnEndOfStream: true:流里只剩 5 字节而请求至少 8 字节,读到 EOF 即抛 EndOfStreamException。只有显式传 throwOnEndOfStream: false 才会返回实际读到的 5;ReadExactly 则无条件要求读满并抛 EndOfStreamException

关键点: ReadAtLeast 的默认行为是“读不够就抛”,与很多人的直觉相反。

0605 以下代码输出什么?

难度: 实战

byte[] data = { 1, 2, 3, 4 };
using var ms = new MemoryStream(data);
ms.WriteByte(9);

Console.WriteLine(data[0]);
  • A. 1,MemoryStream 会复制数组
  • B. 9,该构造函数直接暴露原数组,写入会修改它
  • C. 抛 NotSupportedException
  • D. 抛 ObjectDisposedException
查看答案与解析

正确答案B

解析: new MemoryStream(byte[]) 直接包住传入的数组而不是复制:流内写入会同步修改原数组。如果希望流拥有独立缓冲区,应改用 ToArray()new MemoryStream() 再写。

关键点: 公开缓冲区构造函数 = 写穿到原数组,容易被误当快照。

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

难度: 进阶

byte[] data = { 1, 2, 3 };
using var ms = new MemoryStream(data);
for (int i = 0; i < 4; i++)
{
    ms.WriteByte((byte)i);
}
  • A. 正常结束
  • B. 抛 IOException
  • C. 第 4 次写入时抛 NotSupportedException,固定缓冲区不可扩展
  • D. 抛 OverflowException
查看答案与解析

正确答案C

解析: new MemoryStream(byte[]) 创建的流不可扩展:前三字节写入成功,第 4 次写入超过缓冲区末尾时抛 NotSupportedException(MemoryStream 不可扩展)。用无参构造或 SetLength 之前要确认流是否可扩容。

关键点: 公开数组构造的 MemoryStream 只能覆盖,不能追加扩展。

0607 以下代码输出什么?

难度: 实战

using var src = new MemoryStream(new byte[] { 1, 2, 3, 4, 5 });
src.ReadByte();
using var dst = new MemoryStream();
src.CopyTo(dst);

Console.WriteLine(dst.Length);
  • A. 4,CopyTo 从当前 Position 复制到流末尾
  • B. 5
  • C. 0
  • D. 抛 NotSupportedException
查看答案与解析

正确答案A

解析: ReadByte() 已把 Position 推进到 1,CopyTo 从当前位置复制到末尾,因此只复制后 4 字节。先读了一部分再 CopyTo 会丢前面的数据,这是流处理里常见的漏数据原因。

关键点: CopyTo 不是“从 0 复制”,而是“从当前 Position 复制”。

0608 networkStream 是不可寻址的流(CanSeek 为 false),以下代码运行结果是什么?

难度: 进阶

Console.WriteLine(networkStream.Position);
  • A. 0
  • B. -1
  • C. 抛 IOException
  • D. 抛 NotSupportedException,不可寻址流不支持读取 Position
查看答案与解析

正确答案D

解析: NetworkStream 等不可寻址流(CanSeek 为 false)的 PositionSeekLength 都抛 NotSupportedException。写通用流处理代码前先检查 CanSeek,否则会在真实网络流上踩坑。

关键点: 流不是都有 Position;先看 CanSeek 再寻址。

0609 关于 FileStream 的异步 I/O,哪项正确?

难度: 进阶

  • A. 只要调用 ReadAsync/WriteAsync 就是操作系统级异步
  • B. 需要 FileOptions.Asynchronous(或 useAsync: true)才会在操作系统层执行真正的异步 I/O;未指定时实现可能回退到线程池执行
  • C. 该选项只对 Linux 生效
  • D. 该选项已被移除,.NET 10 不再支持
查看答案与解析

正确答案B

解析: 官方文档明确:要获得操作系统级异步 I/O,需在 FileStream 构造时指定 useAsync: trueoptions: FileOptions.Asynchronous;不指定时,异步调用仍不会阻塞调用线程,但底层实现可能借道线程池,性能特征不同。

关键点: “异步不阻塞”与“OS 级异步 I/O”是两回事。

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

难度: 实战

using (var fs = File.Create("a.txt"))
{
    using var fs2 = File.OpenRead("a.txt");
}
  • A. 正常运行
  • B. fs2 抛 UnauthorizedAccessException
  • C. fs2 抛 IOException,File.Create 默认使用 FileShare.None 独占文件
  • D. 编译失败
查看答案与解析

正确答案C

解析: File.Create(path) 等价于 FileMode.Create + FileAccess.ReadWrite + FileShare.None,即独占打开;在它未释放时再以 File.OpenRead(FileShare.Read)打开同一文件会因共享冲突抛 IOException

关键点: File.Create 与 File.OpenRead 的默认 FileShare 不同,混用要显式指定共享模式。

0611 以下代码输出什么?

难度: 进阶

using var ms = new MemoryStream(Encoding.UTF8.GetBytes("hi"));
using (var sr = new StreamReader(ms, Encoding.UTF8,
    detectEncodingFromByteOrderMarks: true, bufferSize: 1024, leaveOpen: true))
{
    Console.Write(sr.ReadToEnd());
}
Console.WriteLine(ms.CanRead);
  • A. hi / False
  • B. hi / True,leaveOpen: true 让 StreamReader 释放时不关闭底层流
  • C. 空 / True
  • D. 抛 ObjectDisposedException
查看答案与解析

正确答案B

解析: leaveOpen: true 表示释放 StreamReader 时不释放底层流,因此 ms.CanRead 仍为 True;默认 leaveOpen: false 时释放包装器会一并关闭底层流。需要复用底层流时必须显式传 leaveOpen: true

关键点: 包装流默认“接管”底层流的生命周期。

0612 以下代码输出什么?

难度: 实战

var ms = new MemoryStream();
using (var writer = new StreamWriter(ms))
{
    writer.Write("data");
}

Console.WriteLine(ms.CanWrite);
  • A. True
  • B. 抛 ObjectDisposedException
  • C. 编译失败
  • D. False,StreamWriter 默认接管底层流,Dispose 时一并关闭
查看答案与解析

正确答案D

解析: new StreamWriter(ms) 默认 leaveOpen: false:using 块结束时 StreamWriter 先 Flush 再关闭底层 MemoryStream,因此 ms.CanWrite 为 False。想在释放包装器后继续使用底层流,必须用带 leaveOpen: true 的重载。

关键点: 别在 using 块外用已被包装流关闭的底层流。

0613 以下代码输出什么?

难度: 实战

using var ms = new MemoryStream(new byte[] { 42 });
ms.ReadByte();

Console.WriteLine(ms.ReadByte());
  • A. -1,ReadByte 在 EOF 时返回 -1,不抛异常
  • B. 0
  • C. 抛 EndOfStreamException
  • D. 42
查看答案与解析

正确答案A

解析: Stream.ReadByte() 读完最后一个字节后再读返回 -1,与 Read 返回 0 表示 EOF 的约定不同。用 BinaryReader.ReadByte 则会抛 EndOfStreamException,两者行为不一致。

关键点: ReadByte 的 EOF 哨兵是 -1,不是 0 也不是异常。

0614 以下代码输出什么?

难度: 进阶

using var fs = new FileStream("t.bin", FileMode.Create);
fs.Write(new byte[100]);

Console.WriteLine(fs.Position);
Console.WriteLine(new FileInfo("t.bin").Length);
  • A. 100 / 100
  • B. 100 / 0,写操作先进入 FileStream 的内部缓冲区,尚未落盘
  • C. 0 / 100
  • D. 100 / 抛 IOException
查看答案与解析

正确答案B

解析: FileStream 默认有 4096 字节缓冲区:写入 100 字节后 Position 前进到 100,但数据还在缓冲区,磁盘上的文件长度仍是 0,直到 Flush/Dispose。以文件长度判断写入是否生效要小心这一点。

关键点: Position 反映逻辑位置,磁盘 Length 反映已落盘数据。

0615 以下代码输出什么?

难度: 实战

using var ms = new MemoryStream(new byte[] { 1, 2, 3 });
byte[] buf = new byte[4];

int n = await ms.ReadAsync(buf);
Console.WriteLine(n);
  • A. 4
  • B. 0
  • C. 3,ReadAsync 返回实际读取的字节数
  • D. 抛 EndOfStreamException
查看答案与解析

正确答案C

解析: ReadAsync(Memory<byte>) 返回 ValueTask<int>,值是实际读取的字节数(这里 3),不会因为缓冲区大就填满,也不因未读满抛异常。异步读取同样要循环直到返回 0。

关键点: ReadAsync 的返回值语义与同步 Read 一致:实际字节数。

0616 以下代码输出什么?

难度: 进阶

var ms = new MemoryStream();
ms.Dispose();
ms.Dispose();

Console.WriteLine("ok");
  • A. 输出 ok,Dispose 幂等,重复释放不抛异常
  • B. 第二次 Dispose 抛 ObjectDisposedException
  • C. 第二次 Dispose 抛 InvalidOperationException
  • D. 编译失败
查看答案与解析

正确答案A

解析: IDisposable.Dispose 的契约是幂等且不抛异常,Stream 及其所有实现都遵循这一点;释放后再释放是无害空操作。这与释放后访问 Read/Write 等成员抛 ObjectDisposedException 不同。

关键点: Dispose 本身必须幂等;“释放后再使用”才抛异常。

0617 关于 Stream.Read 的返回值与 EOF,哪项描述正确?

难度: 进阶

  • A. 只有缓冲区填满才算读取成功
  • B. 返回 -1 表示已到末尾
  • C. Read 保证返回请求的字节数
  • D. 返回值可能小于请求字节数,0 表示 EOF;应循环读取直到返回 0
查看答案与解析

正确答案D

解析: Read 是“尽力而为”的:网络流等场景下一次只返回部分数据,且不一定表示 EOF;只有返回 0 才确定到达末尾。正确的读法是用循环累加直到返回 0(或用 ReadExactly/ReadAtLeast)。

关键点: 一次 Read 读不满是常态,不是错误。

0618 以下代码输出什么?

难度: 实战

var ms = new MemoryStream();
using (var sw = new StreamWriter(ms, leaveOpen: true))
{
    sw.Write("ok");
}

ms.Position = 0;
Console.WriteLine(new StreamReader(ms).ReadToEnd());
  • A. 空字符串
  • B. ok,leaveOpen: true 保留流,Dispose 时缓冲区已刷新
  • C. 抛 ObjectDisposedException
  • D. 抛 IOException
查看答案与解析

正确答案B

解析: leaveOpen: true 使 StreamWriter 释放时不关闭底层流;释放过程仍会先 Flush,所以数据已写入 ms,重置 Position 后能读回 ok

关键点: leaveOpen 只影响“是否关闭底层流”,不影响“是否刷新缓冲区”。

0619 以下代码输出什么?

难度: 进阶

byte[] bytes = { 0xEF, 0xBB, 0xBF, (byte)'A' };
using var ms = new MemoryStream(bytes);
using var sr = new StreamReader(ms);

Console.WriteLine(sr.ReadToEnd());
  • A. \uFEFFA,BOM 原样保留
  • B. 空字符串
  • C. A,StreamReader 默认检测并跳过 UTF-8 BOM
  • D. 抛 DecoderFallbackException
查看答案与解析

正确答案C

解析: StreamReader 默认 detectEncodingFromByteOrderMarks: true,会识别开头的 EF BB BF 并把 BOM 从结果中去掉;只有显式传 false 才会把 BOM 字节解成 \uFEFF。用默认 StreamReader 读 UTF-8 文件得到的字符串不含 BOM。

关键点: 字符串首字符出现 \uFEFF,多半是绕过了 BOM 检测。

0620 使用 StreamReader/StreamWriter 包装底层流时,关于释放所有权哪项正确?

难度: 实战

  • A. 默认 leaveOpen=false,释放包装器会同时释放底层流
  • B. 包装器永远不会关闭底层流
  • C. 必须显式分别释放两者,否则泄漏
  • D. 释放顺序无关紧要
查看答案与解析

正确答案A

解析: 包装流的默认契约是“接管”底层流:Dispose 包装器时(先 Flush)一并 Dispose 底层流。显式传 leaveOpen: true 时才由调用方自行管理底层流生命周期。

关键点: 不要对已被包装流关闭的流二次释放或继续使用。

官方资料

当前分类

C# 选择题

查看全部分类 →
  1. 29C# 试题 29:迭代器与异步流20 题
  2. 30C# 试题 30:日期时间、Guid 与 Random20 题
  3. 31C# 试题 31:文件与流20 题
  4. 32C# 试题 32:释放模式与终结器20 题
  5. 33C# 试题 33:Task 与 async/await20 题
ESC

输入关键词开始搜索