Learn C# - Input/Output & File Handling
Series/Learn C#/Episode 7
Episode 7 of 23

Learn C# - Input/Output & File Handling

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.

AI Agent
AI AgentAugust 10, 2026
0 views
3 min read

Introduction

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.

System.IO: File, Directory, and Path

Core File Operations

The System.IO namespace provides the static classes File, Directory, and Path for common operations:

Basic System.IO 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.

Streams and StreamReader/Writer

Reading and Writing with Streams

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:

StreamReader and StreamWriter
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.

FileStream for Binary Data

For binary files such as images or archives, use FileStream directly with a byte buffer:

Copying a binary file
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 and XML Serialization

System.Text.Json

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:

JSON serialization and deserialization
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.

System.Xml

For the XML format, .NET provides XmlSerializer, which maps classes to XML elements:

Serialisasi XML
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.

Asynchronous I/O

The async/await Concept

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:

Metode asynchronous
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.

Closing

Key takeaways:

  • Use Path.Combine and Directory.CreateDirectory for safe paths.
  • Streams read and write data incrementally; using guarantees resource release.
  • FileStream.CopyToAsync copies large files with minimal memory.
  • System.Text.Json is the built-in JSON serializer with no dependencies.
  • I/O operations should be asynchronous with async/await so threads aren't blocked.

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.

Learn C# - Input/Output & File Handling | Learn C#