我理解流是字节序列的表示。每个流都提供了将字节读写到其给定的后备存储的方法。但溪流的意义何在?为什么后台存储本身不是我们交互的对象?
不知什么原因,我就是不喜欢这个概念。我读了很多文章,但我觉得我需要一个类比。
我理解流是字节序列的表示。每个流都提供了将字节读写到其给定的后备存储的方法。但溪流的意义何在?为什么后台存储本身不是我们交互的对象?
不知什么原因,我就是不喜欢这个概念。我读了很多文章,但我觉得我需要一个类比。
当前回答
流是字节序列的抽象。其思想是,您不需要知道字节来自何处,只需以标准化的方式读取它们。
例如,如果你通过流处理数据,那么数据来自文件、网络连接、字符串、数据库中的blob等等,对你的代码来说都无关紧要。
与备份存储本身交互本身并没有什么问题,除了它将您绑定到备份存储实现。
其他回答
之所以选择“流”这个词,是因为它(在现实生活中)与我们使用它时想要传达的意思非常相似。
Let's forget about the backing store for a little, and start thinking about the analogy to a water stream. You receive a continuous flow of data, just like water continuously flows in a river. You don't necessarily know where the data is coming from, and most often you don't need to; be it from a file, a socket, or any other source, it doesn't (shouldn't) really matter. This is very similar to receiving a stream of water, whereby you don't need to know where it is coming from; be it from a lake, a fountain, or any other source, it doesn't (shouldn't) really matter.
也就是说,一旦您开始认为您只关心获得所需的数据,而不管数据来自何处,其他人谈论的抽象概念就会变得更加清晰。您开始认为可以包装流,并且您的方法仍然可以完美地工作。例如,你可以这样做:
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());
看到了吗?只要您的方法不关心输入源是什么,您就可以以各种方式自定义源。抽象允许您以一种非常优雅的方式将输入与处理逻辑解耦。
请注意,我们自己创建的流没有备份存储,但它仍然完美地满足了我们的目的。
所以,总的来说,流只是一个输入源,隐藏(抽象)了另一个源。只要你不打破抽象,你的代码就会非常灵活。
为了增加回声室,流是一个抽象,所以您不关心底层存储。当您考虑有和没有流的场景时,这是最有意义的。
文件在很大程度上是无趣的,因为除了我熟悉的非基于流的方法之外,流并没有做太多事情。让我们从网络文件开始。
如果我想从互联网上下载一个文件,我必须打开一个TCP套接字,建立一个连接,并接收字节,直到没有更多的字节。我必须管理一个缓冲区,知道预期文件的大小,并编写代码来检测连接何时断开并适当地处理这个问题。
假设我有某种TcpDataStream对象。我用适当的连接信息创建它,然后从流中读取字节,直到它说没有任何字节。流处理缓冲区管理、数据结束条件和连接管理。
通过这种方式,流使I/O更容易。当然,您可以编写一个TcpFileDownloader类来完成流所做的工作,但是这样您就有了一个特定于TCP的类。大多数流接口只提供Read()和Write()方法,任何更复杂的概念都由内部实现处理。因此,您可以使用相同的基本代码来读写内存、磁盘文件、套接字和许多其他数据存储。
我使用的可视化是传送带,不是在真实的工厂里,因为我对此一无所知,而是在卡通工厂里,物品沿着线移动,被盖章、装箱、计数和检查,由一系列愚蠢的设备完成。
你有做一件事的简单组件,例如一个把樱桃放在蛋糕上的设备。这个设备有一个无樱桃蛋糕的输入流,和一个有樱桃蛋糕的输出流。用这种方式组织处理有三个优点值得一提。
首先,它简化了组件本身:如果你想把巧克力糖衣放在蛋糕上,你不需要一个复杂的设备,知道蛋糕的一切,你可以创造一个愚蠢的设备,把巧克力糖衣粘在任何东西上(在漫画中,这甚至不知道下一个东西不是蛋糕,而是怀尔E.大狼)。
其次,你可以通过将这些设备按不同的顺序排列来创造不同的产品:也许你想让你的蛋糕在樱桃上放糖衣,而不是樱桃在糖衣上,你可以简单地通过在生产线上交换设备来做到这一点。
Thirdly, the devices don't need to manage inventory, boxing, or unboxing. The most efficient way of aggregating and packaging things is changeable: maybe today you're putting your cakes into boxes of 48 and sending them out by the truckload, but tomorrow you want to send out boxes of six in response to custom orders. This kind of change can be accommodated by replacing or reconfiguring the machines at the start and end of the production line; the cherry machine in the middle of the line doesn't have to be changed to process a different number of items at a time, it always works with one item at a time and it doesn't have to know how its input or output is being grouped.
关键是你不应该知道后台存储是什么——它只是一个抽象。实际上,甚至可能没有备份存储——您可能正在从网络中读取数据,而数据根本就没有“存储”。
如果你写的代码能够在文件系统、内存、网络或任何支持流思想的东西上工作,那么你的代码就会更加灵活。
此外,流通常是链接在一起的——你可以有一个流来压缩放入其中的任何内容,将压缩的表单写入另一个流,或者加密数据,等等。在另一端是反向链,解密,解压缩等等。
流是字节序列的抽象。其思想是,您不需要知道字节来自何处,只需以标准化的方式读取它们。
例如,如果你通过流处理数据,那么数据来自文件、网络连接、字符串、数据库中的blob等等,对你的代码来说都无关紧要。
与备份存储本身交互本身并没有什么问题,除了它将您绑定到备份存储实现。