流是 C# 中的一个基本概念,用于处理可能需要一些时间才能完成的大量数据、网络通信和文件 I/O 操作。在许多情况下,这些操作的持续时间是不可预测的,因此拥有一种在等待结果时不会阻止整个过程的机制至关重要。
Stream 是一个抽象,它们携带一个字节序列。这些字节表示一些信息;一个重要的方面是,在通过 Streams 读取数据时,您不需要在内存中加载所有内容。
Streams 有一些操作,可以读取一些仍然需要加载的信息。这些操作是 Read、Write 和 Seek。那么让我们谈谈它 🧠
理解流的一个有用类比是将它们视为允许数据连续流动的水龙头。就像厨房水龙头一样,流量可以是慢的也可以是快的,具体取决于情况。有些流可能会很快完成,而其他流可能需要更长的时间。
在这个类比中,缓冲区的作用类似于一个桶。它会在数据流动时捕获数据,以便您访问和处理数据。如果水龙头 (流) 中断,缓冲区将保留到目前为止已收集的内容。这有助于说明数据流的概念以及缓冲区如何管理信息流。
另一个重要方面是知道当缓冲区已满时从何处恢复读取数据。如果无法记住我们在哪里停止,我们就有可能再次读取相同的数据或跳过某些部分。流的游标扮演此角色;在这个类比中,您可以将光标视为水龙头的阀门。它控制水流,允许您根据需要停止和启动溪流,确保您可以继续装满桶而不会损失一滴水。移动光标的过程称为 seek。
下面是使用 C# 中的 FileStream 类从文件中读取数据的示例。_FileStream_ 类继承自抽象 Stream 类,该类提供用于处理流的方法。
using (FileStream stream = new FileStream(filePath, FileMode.Open, FileAccess.Read))
{
byte[] buffer = new byte[1024];
int bytesRead;
while((bytesRead = await stream.ReadAsync(buffer, 0, buffer.Lenght)) > 0)
{
string content = Encoding.UTF8.GetString(buffer, 0, bytesRead);
Console.WriteLine(content);
}
}
在此示例中,我们使用缓冲区(一个字节数组)异步从文件中读取数据。数据根据缓冲区的容量部分流入缓冲区,_while_ 循环继续重新填充缓冲区,直到读取整个流。_Stream_ 类的 Position 属性跟踪光标在流中的最后读取位置,以便我们可以确保可以读取所有数据。
这里我们使用 Encoding.UTF8.GetString(buffer, 0, bytesRead) 将字节信息转换为字符串,但我们可以处理任何时间的信息,因为我们将其作为字节数组。
最后,我们将在控制台上打印每次执行 while 循环的字符串内容。因此,即使文件尚未读取,我们也会将内容打印到屏幕中。
如果需要重置流的位置,可以检查 CanSeek 是否_为 true_。如果是这样,您可以使用 Seek 倒回开头:
if (stream.CanSeek)
{
stream.Seek(0, SeekOrigin.Begin);
}
使用可写流时,请使用 Flush 确保所有缓冲数据都写入底层存储或传输到目标。这对于避免数据丢失至关重要:
using (FileStream stream = new FileStream(filePath, FileMode.OpenOrCreate, FileAccess.Write))
{
byte[] dataBytes = Encoding.UTF8.GetBytes(data);
stream.Write(dataBytes, 0, dataBytes.Length);
stream.Flush(); // Ensure all data is written to disk or transmitted
}
流的生命周期包括几个关键阶段:创建、使用(读取、写入、查找)和处置。正确了解和管理每个阶段对于高效且无差错的流操作至关重要。
管理流生命周期的最佳实践:
using (var stream = new FileStream(filePath, FileMode.Open, FileAccess.Read))
{
// Perform read operations }
// Stream is automatically disposed here
}
using (FileStream stream = new FileStream(filePath, FileMode.Open, FileAccess.Read))
{
// Check if the stream supports reading
if (stream.CanRead)
{
byte[] buffer = new byte[1024]; // Buffer to hold read bytes
int bytesRead;
// Read the data from the stream asynchronously
while ((bytesRead = stream.Read(buffer, 0, buffer.Length)) > 0)
{
// Convert bytes to string and display
string content = System.Text.Encoding.UTF8.GetString(buffer, 0, bytesRead);
Console.WriteLine(content);
}
}
else
{
Console.WriteLine("The stream does not support reading.");
}
} // The stream is automatically closed and disposed of here
通过仔细管理流生命周期并遵循这些最佳实践,您可以确保您的应用程序高效处理数据、最大限度地减少资源使用并保持系统稳定性。
虽然此示例使用 _FileStream_,但 C# 提供了各种流实现,包括:
每种流类型都有独特的特性,使其适用于 C# 应用程序中的不同场景。选择流时,请考虑数据源、性能要求以及是否需要查找等因素。
流是 C# 中一种基本的通用工具,可为大规模 I/O 操作(如文件处理、网络通信和实时数据处理)实现高效的数据处理。通过掌握流使用的复杂性,包括性能优化、有效的错误处理和适当的生命周期管理,开发人员可以最大限度地利用流的优势来构建响应迅速、资源高效且可扩展的应用程序。