Interpolasi string $"Total: {total}" terlihat seperti sintaks sederhana, tetapi compiler sebenarnya menerjemahkannya menjadi serangkaian pemanggilan method pada sebuah objek penampung yang disebut interpolated string handler. Pola ini bukan rahasia internal: kita bisa membuat handler sendiri, misalnya untuk log yang hanya menyusun teks saat diaktifkan. Bab ini memperkenalkan polanya secara ringkas.

Anatomi sebuah Handler #

Tipe handler ditandai atribut [InterpolatedStringHandler] dan wajib menyediakan konstruktor dengan dua parameter tetap: literalLength (panjang seluruh teks tetap) dan formattedCount (jumlah ekspresi interpolasi). Dua method berikut menampung isinya: AppendLiteral menerima potongan teks tetap, dan AppendFormatted menerima hasil tiap ekspresi interpolasi:

csharp
[InterpolatedStringHandler]
public readonly struct CatatanHandler
{
    private readonly StringBuilder? _penampung;

    public CatatanHandler(int literalLength, int formattedCount, bool aktif, out bool aktifkan)
    {
        _penampung = aktif ? new StringBuilder(literalLength + 8) : null;
        aktifkan = aktif;
    }

    public void AppendLiteral(string teks) => _penampung?.Append(teks);

    public void AppendFormatted<T>(T nilai) => _penampung?.Append(nilai?.ToString());

    public string Hasil => _penampung?.ToString() ?? string.Empty;
}

Parameter aktif berasal dari method pemanggil, dan out bool aktifkan memberi tahu compiler apakah bagian interpolasi perlu dieksekusi. Bila aktif bernilai false, _penampung tidak dialokasikan, seluruh Append* menjadi no-op, dan string final tidak pernah dibangun.

Dua parameter tetap pada konstruktor punya tujuan praktis: literalLength memungkinkan handler mengalokasikan buffer dengan ukuran awal yang cukup sejak awal, sedangkan formattedCount memberi tahu berapa banyak ekspresi interpolasi yang akan datang. Keduanya dihitung compiler saat kompilasi, sehingga tidak ada penghitungan saat program berjalan.

Memakai Handler pada Method #

Parameter bertipe handler membuat compiler menerima bentuk $"..." langsung di titik pemanggilan. Atribut [InterpolatedStringHandlerArgument] meneruskan parameter lain ke konstruktor handler:

csharp
private static void Catat(
    bool aktif,
    [InterpolatedStringHandlerArgument("aktif")] CatatanHandler handler)
{
    string hasil = handler.Hasil;
    Console.WriteLine(hasil.Length > 0 ? hasil : "(string tidak dibangun)");
}

Catat(true, $"Total belanja: {45000 + 30000}");
Catat(false, $"Total belanja: {45000 + 30000}");

Output yang Dihasilkan:

text
Total belanja: 75000
(string tidak dibangun)

Mari kita telaah apa yang terjadi:

  1. Pemanggilan pertama: Compiler membangun CatatanHandler dengan aktif = true, lalu memanggil AppendLiteral("Total belanja: ") dan AppendFormatted(75000); handler menyusun teks lengkapnya.
  2. Pemanggilan kedua: Konstruktor menerima aktif = false dan melaporkan aktifkan = false, sehingga compiler melewati seluruh panggilan Append* dan buffer tidak dialokasikan.
  3. Method Catat: Properti Hasil mengembalikan string kosong pada pemanggilan kedua, dan program mencetak penanda (string tidak dibangun).

Pola ini layak dibuat bila penyusunan teks mahal atau jarang dibutuhkan: pesan log bertingkat, peringatan debug, atau teks laporan yang hanya aktif pada kondisi tertentu. Untuk interpolasi biasa di dalam program sehari-hari, bentuk $"..." langsung tetap pilihan yang paling sederhana.