Skip to main content

Command Palette

Search for a command to run...

C# Streams

Published
โ€ข4 min readโ€ขView as Markdown

1. Core Concept: What is a Stream? ๐ŸŒŠ

At its heart, a stream is an abstract concept representing a sequence of bytes. It's like a conveyor belt for data. You can read bytes from it or write bytes to it.

The power of this abstraction is that your code doesn't need to know where the bytes come from or go to. The source could be a file, a network, or memory. Your code interacts with all of them through the same Read and Write API defined by the base System.IO.Stream class.


2. The Foundation: System.IO.Stream

All stream classes inherit from System.IO.Stream, which provides these key members:

  • Properties:

    • CanRead, CanWrite, CanSeek: Booleans that tell you what operations the stream supports. For example, NetworkStream is not seekable (CanSeek is false).

    • Position: The current location in the stream.

    • Length: The total size of the stream.

  • Methods:

    • Read() / ReadAsync(): Reads bytes from the stream into a buffer.

    • Write() / WriteAsync(): Writes bytes from a buffer to the stream.

    • Seek(): Changes the Position within the stream (if CanSeek is true).

    • Flush() / FlushAsync(): Empties any internal buffers, forcing data to be written to the backing store.

    • Close() / Dispose(): Releases the resource (e.g., the file handle).


3. Categories of Streams

Streams fall into two main categories, which can be chained together.

A. Backing Store Streams (The Sources & Sinks)

These streams connect directly to an I/O source managed by the Operating System (OS). They are the endpoints of your data flow.

  • FileStream ๐Ÿ“‚

    • Backing Store: Hard Drive / SSD.

    • Purpose: Reading from and writing to physical files.

    • Clarification: This is the only stream type that can directly interact with the file system. A MemoryStream cannot. Helper methods like File.WriteAllBytes() create and manage a FileStream for you behind the scenes.

  • MemoryStream ๐Ÿง 

    • Backing Store: A byte[] array in your application's RAM.

    • Purpose: A high-speed, in-memory "virtual file." Perfect for creating or manipulating data before sending it elsewhere.

    • Clarification: While it acts like a file in memory, it has no connection to the disk. To save its contents, you must copy its data to a FileStream (or use a helper like File.WriteAllBytes).

    • Important Note: After writing to a MemoryStream, its Position is at the end. You must reset the position to 0 (ms.Position = 0;) before you can read the data from the beginning.

  • NetworkStream ๐ŸŒ

    • Backing Store: An OS-managed network socket.

    • Purpose: Sending and receiving data over a network.

  • PipeStream ๐Ÿ”—

    • Backing Store: An OS-managed IPC (Inter-Process Communication) pipe.

    • Purpose: Allowing two different programs on the same machine to communicate.

B. Decorator / Wrapper Streams (The Attachments)

These streams wrap another stream to add functionality. You always interact with the outermost stream in the chain.

  • GZipStream: Adds on-the-fly compression or decompression.

  • CryptoStream: Adds on-the-fly encryption or decryption.

Example Pipeline (Writing): Data flows inward. Your Code -> GZipStream (compresses) -> FileStream (writes to disk)

Example Pipeline (Reading): Data flows outward. Disk -> FileStream (reads) -> GZipStream (decompresses) -> Your Code


4. Readers and Writers: The Human-Friendly Layer โœ๏ธ

Streams work with raw bytes. Readers and Writers are helper classes that translate between bytes and common data types. They are not streams themselves, but they use a stream.

  • StreamReader & StreamWriter: For text. They handle character encodings (e.g., UTF-8).

  • BinaryReader & BinaryWriter: For primitive data types (int, double, bool, etc.).


5. The Working Mechanism: Clarifying the Role of RAM

This was a key point of confusion. RAM is used everywhere, but its role is different for each stream type.

Stream Type

Role of RAM

Ownership & Purpose

Analogy

MemoryStream

Primary Backing Store

Application RAM. Your code owns and controls this memory directly as a workspace.

A personal notepad on your desk.

FileStream

Performance Cache

OS RAM (File System Cache). A hidden middleman managed by the OS to avoid slow disk reads.

A library's short-term loan desk.

NetworkStream / PipeStream

Transit Buffer

OS RAM (Kernel Buffers). A hidden, temporary holding area managed by the OS for I/O flow control.

A post office sorting bin.


6. Essential Best Practices

  • Always Use using: Streams control OS resources. The using statement guarantees that the stream's .Dispose() method is called to release the resource, even if errors occur. For async code, use await using.

    C#

      using (FileStream fs = new FileStream("log.txt", FileMode.Open))
      {
          // ... use the stream ...
      } // fs.Dispose() is automatically called here.
    
  • Prefer ...Async Methods: Use asynchronous methods (ReadAsync, WriteAsync) in UI or server applications to prevent blocking threads, which improves responsiveness and scalability.

  • Check Can... Properties: Before calling Seek, Read, or Write, you can check the CanSeek, CanRead, or CanWrite properties to ensure the stream supports the operation.