This episode covers file management in C#: System.IO with File, Directory, and Path, using streams such as StreamReader, StreamWriter, and FileStream, JSON and XML serialization with System.Text.Json, and the asynchronous I/O pattern with async/await.

Most real applications read or write data outside memory: configuration files, logs, export data, or cache. Managing I/O operations correctly is the skill that separates long-lived applications from fragile ones.
The most common problems are unreleased resources, hardcoded paths, and synchronous operations that block a thread while waiting for disk. All three can be avoided with the right patterns.
Episode 7 covers the System.IO API, streams for reading and writing large data, JSON and XML serialization, and the asynchronous I/O concept you'll use in almost every web application. All the code in this episode can be run directly with dotnet run.
The System.IO namespace provides the static classes File, Directory, and Path for common operations:
var jalur = Path.Combine("data", "catatan.txt");
Directory.CreateDirectory("data");
File.WriteAllText(jalur, "Halo dari C#\n");
var isi = File.ReadAllText(jalur);
Console.WriteLine(isi);
foreach (var file in Directory.EnumerateFiles("data"))
{
Console.WriteLine(Path.GetFileName(file));
}Notice the use of Path.Combine("data", "catatan.txt") — don't build paths manually with a slash, because the path separator differs between Linux and Windows. Path.Combine handles both automatically.
For large files, reading the entire contents into memory at once is inefficient. A stream processes data incrementally. StreamReader and StreamWriter provide line-by-line reading:
using var writer = new StreamWriter("log.txt");
for (var i = 1; i <= 100; i++)
{
await writer.WriteLineAsync($"Baris ke-{i}");
}
using var reader = new StreamReader("log.txt");
while (await reader.ReadLineAsync() is { } baris)
{
Console.WriteLine(baris);
}The WriteLineAsync and ReadLineAsync methods run asynchronously, so the thread isn't blocked while waiting for disk. The using block guarantees the stream is closed even if an error occurs.
For binary files such as images or archives, use FileStream directly with a byte buffer:
using var sumber = File.OpenRead("foto.jpg");
using var tujuan = File.Create("salinan.jpg");
await sumber.CopyToAsync(tujuan);CopyToAsync moves data in chunks, minimizing memory usage even for large files.
JSON is the most common data exchange format in the modern ecosystem. System.Text.Json is a fast built-in serializer with no extra dependencies:
record Produk(string Nama, decimal Harga, bool Tersedia);
var produk = new Produk("Kopi Gayo", 85_000m, true);
var json = JsonSerializer.Serialize(produk);
Console.WriteLine(json);
var kembali = JsonSerializer.Deserialize<Produk>(json);
Console.WriteLine(kembali!.Nama);The output is {"Nama":"Kopi Gayo","Harga":85000,"Tersedia":true}. JsonSerializer.Deserialize<Produk>(json) turns JSON back into an object — the pattern used by almost all modern APIs.
For the XML format, .NET provides XmlSerializer, which maps classes to XML elements:
var serializer = new XmlSerializer(typeof(Produk));
using var stream = new StringWriter();
serializer.Serialize(stream, produk);
Console.WriteLine(stream.ToString());XML is still dominant in the enterprise world for legacy system integration. With XmlSerializer, the same C# object can be exported to both formats.
I/O operations take far longer than CPU computation. Without async, the application thread blocks while waiting — fatal for a web server serving many requests. The async pattern returns control to the caller while waiting:
async Task<string> BacaFileAsync(string jalur)
{
return await File.ReadAllTextAsync(jalur);
}
var isi = await BacaFileAsync("data/konfigurasi.txt");
Console.WriteLine(isi);The async Task method marks an asynchronous operation, and await waits for the result without blocking the thread. Important rules: use the Async suffix on method names, and never block with .Result — use await throughout the call chain. We'll dive into concurrency details in episode 14.
Key takeaways:
Path.Combine and Directory.CreateDirectory for safe paths.using guarantees resource release.FileStream.CopyToAsync copies large files with minimal memory.System.Text.Json is the built-in JSON serializer with no dependencies.In the next episode 8 we manage application behavior without changing code: configuration and environment management — appsettings.json and environment variables, the options pattern, secrets management, and the .NET Generic Host with IHostEnvironment.