我知道流是字节序列的表示。每个流都提供用于将字节读取和写入其给定的后备存储的方法。但是流的意义是什么?为什么后备商店本身不与我们互动?
无论出于何种原因,这个概念都无法满足我的需求。我读了很多文章,但是我想我需要一个类比。
最佳答案
选择“流”一词是因为(在现实生活中)它与我们在使用时想要传达的含义非常相似。
让我们稍微忘记一下后备店,然后开始考虑与水流的类比。您会收到连续的数据流,就像水在河中不断流动一样。您不必知道数据来自何处,而且通常不需要。无论是来自文件,套接字还是其他任何来源,都没有(应该)没有关系。这与接收水流非常相似,您无需知道水的来源。无论是来自湖泊,喷泉还是其他任何来源,都没有(应该)没有关系。
就是说,一旦您开始考虑只关心获取所需数据(无论它来自何处),其他人谈论的抽象就会变得更加清晰。您开始认为可以包装流,并且您的方法仍然可以完美地工作。例如,您可以这样做:
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());
看到?只要您的方法不关心输入源是什么,就可以通过各种方式自定义源。抽象允许您以非常优雅的方式将输入与处理逻辑解耦。
请注意,我们自己创建的流没有后备存储,但是仍然可以完美地满足我们的目的。
因此,总而言之,流只是输入的源,而隐藏(抽象)了另一个源。只要不破坏抽象,您的代码就会非常灵活。