分析问题
流是一种对于字节流的直接操作。例如在处理一个文件时,本质上需要通过操作系统提供的API来进行文件打开,读取文件中的字节流,再关闭文件等操作,而读取文件的过程就可以看作字节流的一个过程。常见的流类型有文件流、终端操作流、网络Socket等。
在.NET中,System.IO.Stream类型被设计为所有流类型的虚基类,所有常见的流类型都继承自System.IO.Stream类型,当程序员需要自定义一种流类型时,也应该直接或者间接继承自Stream类型。.NET提供了丰富的内建的流类型,其中包括了操作文件的FileStream、操作内存的MemoryStream、操作网络的NetworkStream等。下图展示了.NET中常见的流类型以及它们的类型结构图。
如上图所示,Stream类型继承自MarshalByRefObject类型,这保证了流类型可以跨越应用程序域进行交互。所有的常见的流类型都继承自System.IO.Stream类型,这保证了流类型的同一性。配合StreamReader和StreamWriter类型,.NET提供的流类型使用起来都非常简便,完全屏蔽了底部的一些复杂操作。
以下代码以最常用的FileStream为例,介绍了流的使用方法。首先是从一个流读出所有字节和向下一个流传入所有字节的方法,这两个方法对于所有流对象是通用的。
using System; using System.Collections.Generic; using System.Text; using System.IO; namespace Test { class UseStream { private const int bufferLength = 50; static void Main() { //创建一个文件,并写入内容 string fileName = @"D:\TestStream.txt"; string fileContent = GetTestString(); try { //创建文件并写入内容 using (FileStream fs = new FileStream(fileName, FileMode.Create)) { byte[] bytes = Encoding.Default.GetBytes(fileContent); WriteAllBytes(fs, bytes, bufferLength); fs.Close(); } //读取文件并且打印出来 using (FileStream fs = new FileStream (fileName,FileMode.Open)) { byte[] result = ReadAllBytes(fs, bufferLength); Console.WriteLine(Encoding.Default.GetString(result)); Console.Read(); fs.Close(); } } finally { try { if (File.Exists(fileName)) { File.Delete(fileName); } } finally { } } } //取得测试数据 static string GetTestString() { StringBuilder builder = new StringBuilder(); for (int i = 0; i < 10; i++) { builder.Append("我是测试数据\r\n"); } return builder.ToString(); } //从一个流中读取所有字节 static byte[] ReadAllBytes(Stream stream, int bufferLength) { //读取的缓存 byte[] buffer = new byte[bufferLength]; List<byte> result = new List<byte>(); int read; //直至读取结束 while ((read=stream.Read(buffer,0,bufferLength))>0) { if (read<bufferLength) { byte[] temp = new byte[read]; Array.Copy(buffer, temp, read); result.AddRange(temp); } else { result.AddRange(buffer); } } return result.ToArray(); } //把字节写入一个流中 static void WriteAllBytes(Stream stream, byte[] data, int bufferLength) { //写入的缓存 byte[] buffer = new byte[bufferLength]; for (long i = 0; i < data.LongLength; i+=bufferLength) { int length = bufferLength; if (i+bufferLength>data.LongLength) { length = (int)(data.LongLength - i); } Array.Copy(data, i, buffer, 0, length); stream.Write(buffer, 0, length); } } } }
由于优秀的封装,.NET中所有的流类型的使用方法都很类似,程序员基本可以无须关心底层实现的具体内容。当需要实现自定义的流类型时,程序员应该毫无疑问地使其直接或者间接地继承自Stream类型,以保证所有流类型的一致性。
最后相当重要的一点是,Stream类型实现了IDisposable接口,所有对于流类型的使用都应该使用using语句确保Dispose方法被调用。
答案
流是对于字节集合对象的一种操作。.NET中常见的流类型有FileStream、MemoryStream、NetworkStream、UnmanagedMemoryStream等。
什么是流,.NET中有哪些常见的流