Fungsi Lokal

Fungsi pembantu di dalam metode.

Daftar isi

Topik ini membahas fungsi lokal (local function), yaitu metode privat yang dideklarasikan di dalam metode lain dan diperkenalkan pada C# 7. Fungsi semacam ini melayani kebutuhan pembantu yang hanya berlaku bagi satu metode, sehingga kelas tidak dipenuhi anggota yang dipakai sekali. Setelah topik ini diselesaikan, fungsi pembantu dapat dideklarasikan di dalam metode, variabel lokal dapat ditangkap dengan sadar, dan bentuk static serta pola validasi iterator dapat dipilih sesuai kebutuhan.

Fungsi pembantu di dalam metode#

Fungsi lokal dideklarasikan di antara pernyataan metode induknya, lengkap dengan tipe kembalian, nama, dan parameter seperti metode biasa. Jangkauannya terbatas pada metode yang menaunginya, sehingga nama pendek yang spesifik tidak menimbulkan tumpang tindih dengan anggota kelas lain. Deklarasinya boleh diletakkan di mana saja di dalam metode, dan konvensi yang lazim adalah meletakkannya setelah pernyataan utama atau di bagian akhir agar alur utama tetap terbaca lebih dahulu.

csharp Program.cs
// Program: fungsi lokal sebagai pembantu metode, penangkapan variabel, dan static.
string Rapikan(string teks)
{
    string NormalisasiSpasi(string s)
    {
        while (s.Contains("  "))
        {
            s = s.Replace("  ", " ");
        }
        return s;
    }

    return NormalisasiSpasi(teks.Trim());
}

Console.WriteLine(Rapikan("  statistik   nilai   ujian  "));

int total = 0;

void Catat(int nilai)
{
    total += nilai;
}

Catat(10);
Catat(5);
Console.WriteLine(total);

static double RataRata(int jumlah, double totalNilai) => totalNilai / jumlah;
Console.WriteLine(RataRata(4, 344));

IEnumerable<int> GenapHingga(int batas)
{
    if (batas < 0)
    {
        throw new ArgumentException("batas tidak boleh negatif");
    }

    return DaftarGenap();

    IEnumerable<int> DaftarGenap()
    {
        for (int i = 0; i <= batas; i += 2)
        {
            yield return i;
        }
    }
}

try
{
    IEnumerable<int> daftar = GenapHingga(-1);
    Console.WriteLine("pemanggilan berhasil");
}
catch (ArgumentException e)
{
    Console.WriteLine($"Error: {e.Message}");
}

Console.WriteLine(string.Join(", ", GenapHingga(6)));
text Keluaran
statistik nilai ujian
15
86
Error: batas tidak boleh negatif
0, 2, 4, 6

Metode Rapikan mendeklarasikan fungsi lokal NormalisasiSpasi yang mengganti spasi ganda sampai habis, kemudian memanggilnya setelah Trim() membersihkan ujung teks. Pemanggilan berulang di dalam perulangan while menunjukkan keunggulan fungsi bernama, yaitu logika pembantu cukup ditulis sekali lalu dipanggil berulang dengan jelas. Keluaran pertama berupa statistik nilai ujian karena seluruh spasi berlebih telah dirapatkan menjadi satu spasi.

Fungsi lokal juga boleh memanggil dirinya sendiri, sehingga perhitungan bertingkat dapat ditulis berulang tanpa mengotori kelas dengan metode pembantu baru. Kemampuan ini membedakannya dari ekspresi lambda, yang tidak dapat memuat yield return dan yang harus dirujuk melalui variabel perantara untuk memanggil dirinya sendiri, sehingga iterator hanya dapat ditulis sebagai fungsi bernama. Kedua keterbatasan lambda tersebut hilang begitu pembantunya dideklarasikan sebagai fungsi lokal.

Penangkapan variabel lokal#

Fungsi lokal yang tidak bertanda static boleh membaca dan mengubah variabel lokal metode induknya. Mekanisme ini disebut closure, yaitu penangkapan variabel oleh fungsi sehingga masa hidup variabel itu melampaui metode yang mendeklarasikannya. Pada program di atas, fungsi lokal Catat menambahkan nilai ke variabel total milik induknya, dan dua pemanggilan berturut-turut menghasilkan 15 karena keduanya menyentuh variabel yang sama.

Penangkapan ini bekerja dua arah, yaitu perubahan dari fungsi lokal terlihat oleh induknya dan perubahan dari induk terbaca oleh fungsi lokal pada pemanggilan berikutnya. Karena itu total tidak perlu dikirim sebagai parameter dan hasilnya tidak perlu dikembalikan. Kebiasaan yang baik adalah membatasi penangkapan pada satu atau dua variabel dengan peran yang jelas, karena penangkapan yang menyebar membuat aliran data sulit dilacak.

Fungsi lokal static#

Kata kunci static pada fungsi lokal menutup penangkapan variabel, sehingga seluruh data yang dibutuhkan wajib masuk melalui parameter. Fungsi RataRata pada program di atas dideklarasikan static dan menghitung 344 / 4 menjadi 86 murni dari parameternya. Bentuk ini diperkenalkan pada C# 8 dan bermanfaat sebagai pernyataan niat, yaitu fungsi ini tidak menyentuh keadaan di luar parameternya, sehingga pembaca dapat memercayainya tanpa memeriksa tubuhnya satu per satu.

Apabila fungsi lokal static mencoba membaca variabel luar, kompilator (compiler), program yang menerjemahkan kode sumber menjadi program yang dapat dieksekusi, menolaknya dengan error CS8421.

csharp Contoh: fungsi lokal static yang menangkap variabel ditolak
// tidak-dijalankan
static int AmbilTotal()
{
    return total; // menghasilkan error CS8421
}

Kompilator melaporkan error CS8421: A static local function cannot contain a reference to 'total'. Perbaikannya adalah mengirim total sebagai parameter atau menghapus static apabila penangkapan memang dimaksudkan. Aturan ini membuat pilihan antara kedua bentuk terlihat jelas di dalam kode, bukan tersirat dari isi fungsi.

Validasi pada fungsi iterator#

Fungsi iterator yang memakai yield return menunda seluruh isinya sampai perulangan benar-benar dilakukan (deferred execution), sehingga pemeriksaan argumen yang ditulis langsung di dalamnya baru terjadi terlambat. Pola yang lazim adalah memeriksa argumen di metode luar, kemudian mengembalikan hasil pemanggilan fungsi lokal iterator yang berisi yield return. Pada program di atas, fungsi lokal DaftarGenap itulah yang berperan sebagai iterator, sedangkan GenapHingga memeriksa batas terlebih dahulu.

Keluaran Error: batas tidak boleh negatif membuktikan sifat itu. Pemanggilan GenapHingga(-1) melempar ArgumentException seketika di dalam blok try, jauh sebelum hasilnya diulangi oleh pernyataan apa pun, sedangkan pemanggilan dengan batas 6 menghasilkan deretan 0, 2, 4, 6 ketika diulangi oleh string.Join. Tanpa fungsi lokal, pemeriksaan yang sama harus ditulis di dalam iterator dan errornya baru muncul saat perulangan dimulai, sehingga penyebabnya lebih sulit dilacak.

Kesalahan umum#

  • Menganggap fungsi lokal dapat dilihat dari luar metode. Jangkauannya terbatas pada metode induknya, sehingga pemanggilan dari metode lain menghasilkan error pencarian nama. Angkat deklarasinya ke tingkat kelas apabila pembantunya memang dibutuhkan lebih dari satu metode.
  • Menyisakan penangkapan pada fungsi yang seharusnya murni. Fungsi pembantu yang tanpa dinyatakan membaca variabel induknya sulit dipindahkan dan sulit diuji. Tandai dengan static agar kompilator menuntut seluruh masukan lewat parameter.
  • Memeriksa argumen di dalam tubuh iterator. Pemeriksaan di dalam fungsi iterator baru berjalan saat perulangan dimulai, sehingga argumen tidak sah lolos tanpa terdeteksi. Terapkan pola metode pemeriksa yang mengembalikan fungsi lokal iterator.

Ringkasan#

  • Fungsi lokal dari C# 7 dideklarasikan di dalam metode lain, berjangkauan privat, dan cocok untuk pembantu yang hanya dipakai satu metode.
  • Fungsi lokal non-static menangkap variabel lokal induknya melalui closure, sehingga keduanya berbagi keadaan yang sama.
  • Kata kunci static sejak C# 8 menutup penangkapan, dan pelanggarannya ditolak dengan error CS8421.
  • Fungsi lokal iterator dipasangkan dengan pemeriksaan argumen di metode luarnya agar error terjadi saat pemanggilan, bukan saat perulangan.

Referensi#