This episode teaches correct error handling in C#: try, catch, finally, throw, and custom exceptions. You will also learn using declarations, dispose patterns, debugging in VS Code, and basic logging with Microsoft.Extensions.Logging.

Applications do not always run smoothly — files go missing, connections drop, input is invalid. Episode 6 teaches you how to deal with these situations correctly in C#: exception handling, dispose patterns for resources, debugging techniques in VS Code, and structured logging.
The end goal is not to avoid errors, but to handle them predictably: the application does not crash wildly, resources are released, and adequate traces are available when incidents occur. This is an important prerequisite before you build your web API in phase 4.
A try block wraps code that might error, catch handles the exception, and finally always runs — whether success or error:
try
{
var angka = int.Parse("abc");
}
catch (FormatException ex)
{
Console.WriteLine($"Input tidak valid: {ex.Message}");
}
finally
{
Console.WriteLine("Selesai");
}Catch specific exceptions like FormatException first, then general exceptions afterwards. The ex.Message property contains the error description; never swallow an exception without logging it.
For business errors, create a custom exception that describes the domain context:
public class SaldoTidakCukupException : Exception
{
public SaldoTidakCukupException(string pesan) : base(pesan) { }
}
throw new SaldoTidakCukupException("Saldo tidak mencukupi");Custom exceptions derive from Exception and are usually added as a separate class in the Exceptions folder. Use throw to report conditions that cannot be recovered at that location.
Resources such as files and connections must be released. Using declarations guarantee that the Dispose method is called when exiting scope:
using var stream = File.Create("catatan.txt");
stream.WriteByte(65);using var stream automatically calls Dispose when the block ends. Since C# 8, this declaration is more concise than a block using, and it applies to all types that implement IDisposable.
If your class holds unmanaged resources, implement this pattern:
public class KoneksiDb : IDisposable
{
public void Dispose()
{
Console.WriteLine("Koneksi ditutup");
}
}IDisposable signals that an instance needs to be released. Most resources in the .NET ecosystem — database connections, streams, HttpClient — already implement this pattern, so you just use them together with using.
With the C# Dev Kit extension, VS Code can run the .NET debugger. The .vscode/launch.json file is generated automatically from a template; its core purpose is targeting the active project:
{
"version": "0.2.0",
"configurations": [
{
"name": "Debug Console",
"type": "coreclr",
"request": "launch",
"preLaunchTask": "build",
"program": "${workspaceFolder}/bin/Debug/net8.0/HelloDotnet.dll"
}
]
}The configuration above tells the debugger which assembly to run. Press the Run and Debug button to start, then click the left margin to place a breakpoint.
Once the breakpoint is active, run the application and watch the Watch, Variables, and Call Stack panels. Use the Step Over, Step Into, and Step Out commands to trace execution. Debugging is the fastest way to find where the state went wrong, before you fix the cause.
Instead of Console.WriteLine, modern applications use logging providers that provide levels and structure. Microsoft.Extensions.Logging is the built-in .NET standard:
using Microsoft.Extensions.Logging;
var logger = LoggerFactory.Create(builder =>
builder.AddConsole()).CreateLogger("App");
logger.LogInformation("Aplikasi dimulai");
logger.LogWarning("Memori menipis: {Memori}%", 85);builder.AddConsole() adds the console provider, and logger.LogInformation writes a log at the Information level. Placeholders like {Memori} fill values without concatenation — keeping logs structured and easy to parse. Episode 20 will expand on this with Serilog and OpenTelemetry.
Warning
Don't write exceptions directly to the console in production code. Always pass them to ILogger with the appropriate level so the operations team can find and analyze incidents.
A summary of practices to get used to:
Key takeaways:
try, catch, finally handle errors; throw reports new conditions.In the next episode 7 we will discuss I/O and file handling — System.IO, StreamReader and StreamWriter, FileStream, JSON serialization with System.Text.Json, and configuration files such as appsettings.json and environment variables.