Streams in C#
Work with data as a flow of bytes using System.IO streams — reading and writing files, memory, network data, compression, and stream chaining.
What Is a Stream?
A stream is a sequence of bytes that flows from a source to a destination. Instead of loading an entire file or network response into memory at once, you read or write it piece by piece. This makes streams memory-efficient for large data. In C#, all streams inherit from the abstract base class `System.IO.Stream`, which defines a common interface: `Read()`, `Write()`, `Seek()`, `Position`, and `Length`.
1. Stream Concepts
Streams can be readable, writable, or both. They can support seeking (jumping to any position) or be forward-only. The base `Stream` class exposes all these capabilities.
FileStream — Reading & Writing Files
FileStream is the lowest-level file I/O class in C#. It gives you direct byte-level access to a file. For most text file work you will wrap it in a `StreamReader` or `StreamWriter`, but understanding FileStream is essential when you need binary I/O — images, audio, compressed data, or custom binary formats.
1. Writing and Reading with FileStream
Open a file with `FileStream`, write bytes to it, then read them back. Use `FileMode` to control whether the file is created, opened, or appended.
2. Reading Large Files in Chunks
Never load a huge file into a single byte array — read it in fixed-size chunks. This keeps memory usage constant regardless of file size.
StreamReader & StreamWriter
FileStream deals in raw bytes. `StreamReader` and `StreamWriter` wrap any stream and handle text encoding (UTF-8, ASCII, Unicode) for you. They are the go-to classes for reading and writing text files line by line.
1. Writing Text with StreamWriter
Wrap a FileStream in a StreamWriter to write strings instead of raw bytes. StreamWriter handles encoding and buffering automatically.
2. Reading Text with StreamReader
StreamReader reads text from any stream. Use `ReadLine()` for line-by-line processing and `ReadToEnd()` for small files.
MemoryStream & BufferedStream
MemoryStream acts like a file stream but lives entirely in RAM — perfect for building up byte sequences, intermediate processing, or unit-testing stream code. BufferedStream wraps any stream and adds an internal buffer so that many small reads/writes are batched into larger, more efficient I/O operations.
1. MemoryStream — In-Memory Buffer
Build byte content in memory, then use it as a stream — no temporary files needed. Commonly used for image processing, serialisation, and HTTP response bodies.
2. BufferedStream — Faster I/O
Wrap a slow stream (like a FileStream or NetworkStream) in a BufferedStream to batch many small reads/writes into fewer, larger OS calls.
Compression Streams
C# provides compression streams in `System.IO.Compression`. You chain them over another stream: data passes through the compressor/decompressor as it flows. `GZipStream` produces `.gz` files; `DeflateStream` produces raw DEFLATE data (used inside ZIP and HTTP `Content-Encoding: deflate`). Both are wrappers — they do not store data themselves.
1. Compress & Decompress with GZipStream
Pipe data through GZipStream to compress it to disk. Reverse the pipe to decompress. Works with any text or binary data.
Async Streams
Blocking a thread while waiting for disk or network I/O wastes resources. C# stream classes expose async counterparts — `ReadAsync()`, `WriteAsync()`, `CopyToAsync()` — that release the thread while waiting. For producing sequences of data asynchronously, C# 8 introduced `IAsyncEnumerable<T>` with `await foreach`.
1. Async File Read & Write
Use `await ReadAsync` and `await WriteAsync` to perform file I/O without blocking the calling thread. Always use `FileOptions.Asynchronous` when creating a FileStream for async use.
2. IAsyncEnumerable — Async Streams
Produce items one at a time asynchronously with `yield return` inside an `async` method, and consume them with `await foreach`.