我知道流是字节序列的表示。每个流都提供了读取和写入字节到其给定后备存储的方法。但是流的意义何在?为什么我们与之交互的不是后备存储本身?
无论出于何种原因,这个概念对我来说并不适用。我读过很多文章,但我想我需要一个类比什么的。
之所以选择“流”这个词,是因为它代表(在现实生活中)与我们在使用它时想要传达的意思非常相似。
让我们暂时忘记后备存储,并开始考虑与水流的类比。您会收到持续不断的数据流,就像河流中不断流动的水一样。您不一定知道数据来自哪里,而且通常您也不需要;无论是来自文件、套接字还是任何其他来源,它都(不应该)真的很重要。这与接收水流非常相似,您无需知道它来自哪里;无论是来自湖泊、喷泉还是任何其他来源,都(不应该)真正重要。
也就是说,一旦你开始认为你只关心获取你需要的数据,不管它来自哪里,其他人谈论的抽象就会变得更加清晰。你开始认为你可以包装流,你的方法仍然可以完美地工作。例如,您可以这样做:
int ReadInt(StreamReader reader) { return Int32.Parse(reader.ReadLine()); } // in another method: Stream fileStream = new FileStream("My Data.dat"); Stream zipStream = new ZipDecompressorStream(fileStream); Stream decryptedStream = new DecryptionStream(zipStream); StreamReader reader = new StreamReader(decryptedStream); int x = ReadInt(reader);
如您所见,在不更改处理逻辑的情况下更改输入源变得非常容易。例如,要从网络套接字而不是文件读取数据:
Stream stream = new NetworkStream(mySocket); StreamReader reader = new StreamReader(stream); int x = ReadInt(reader);
尽可能简单。并且美丽还在继续,因为您可以使用任何类型的输入源,只要您可以为它构建一个流“包装器”。你甚至可以这样做:
public class RandomNumbersStreamReader : StreamReader { private Random random = new Random(); public String ReadLine() { return random.Next().ToString(); } } // and to call it: int x = ReadInt(new RandomNumbersStreamReader());
看?只要您的方法不关心输入源是什么,您就可以通过各种方式自定义您的源。抽象允许您以一种非常优雅的方式将输入与处理逻辑分离。
请注意,我们自己创建的流没有后备存储,但它仍然完美地服务于我们的目的。
因此,总而言之,流只是一个输入源,隐藏(抽象)另一个源。只要您不破坏抽象,您的代码就会非常灵活。