Fungsi lokal

Pilih fungsi lokal saat Anda ingin memberikan nama deskriptif pada logika pembantu dan hanya satu anggota yang membutuhkan pembantu tersebut. Dibandingkan dengan metode pada tipe yang memuat, fungsi lokal memberi tahu pembaca bahwa fungsi bantu ini merupakan bagian dari operasi ini. Namanya memungkinkan pembaca mengikuti alur utama sebelum mereka memeriksa detailnya.

Tip

Artikel ini mengajarkan penggunaan praktis fungsi lokal untuk pengembang yang tahu cara menulis metode. Untuk detail deklarasi dan aturan diagnostik, lihat Deklarasi fungsi lokal.

Fungsi lokal adalah fungsi bernama yang dideklarasikan di dalam anggota lain, seperti metode atau konstruktor. Namanya hanya berada dalam cakupan di dalam member tersebut.

Bentuk berikut mendeklarasikan dan memanggil fungsi lokal:

string CreateMessage(string name)
{
    return AddGreeting(name);

    string AddGreeting(string value) => $"Hello, {value}!";
}

AddGreeting tetap berada di dalam CreateMessage. Anggota lain dari tipe tidak dapat memanggilnya.

Tempatkan logika pembantu di dekat pemanggilnya

Fungsi lokal memungkinkan alur utama menyatakan apa yang terjadi sementara fungsi pembantu menjelaskan langkah-langkah individual. Contoh berikut menunjukkan cara membuat rencana bacaan. Ini menggunakan fungsi lokal untuk menambahkan tugas selama satu minggu pada satu waktu.

IReadOnlyList<ReadingWeek> plan =
    CreateReadingPlan("The Long Way Home", chapterCount: 10, chaptersPerWeek: 3);

foreach (ReadingWeek week in plan)
{
    Console.WriteLine(
        $"Week {week.Number}: {week.BookTitle}, chapters {week.FirstChapter}-{week.LastChapter}");
}

static IReadOnlyList<ReadingWeek> CreateReadingPlan(
    string bookTitle,
    int chapterCount,
    int chaptersPerWeek)
{
    var plan = new List<ReadingWeek>();
    AddWeek(firstChapter: 1);
    return plan;

    void AddWeek(int firstChapter)
    {
        if (firstChapter > chapterCount)
        {
            return;
        }

        int lastChapter = Math.Min(
            firstChapter + chaptersPerWeek - 1,
            chapterCount);

        plan.Add(new ReadingWeek(
            plan.Count + 1,
            bookTitle,
            firstChapter,
            lastChapter));

        AddWeek(lastChapter + 1);
    }
}

ReadingWeek mencatat nomor minggu dan rentang bab yang ditetapkan untuk minggu itu. AddWeek hanya milik CreateReadingPlan, sehingga cakupan lokalnya mengomunikasikan hubungan itu.

Anda dapat mendeklarasikan fungsi lokal sebelum atau sesudah pernyataan yang memanggilnya. Tempatkan di mana pembaca dapat mengikuti alur utama tanpa kehilangan konteks.

Menggunakan parameter atau menangkap state lokal

Fungsi lokal non-statis dapat menggunakan parameter dan variabel lokal dari anggota induknya. Penggunaan ini disebut capture. Dalam contoh rencana bacaan, AddWeek mencakup:

  • bookTitle, chapterCount, dan chaptersPerWeekCreateReadingPlan parameter.
  • plan dari variabel lokal.

Capture berguna ketika fungsi pembantu secara alami menjadi bagian dari operasi yang melingkupinya. Batasi nilai yang ditangkap agar sedikit dan mudah dikenali. Jika helper dapat menerima semua datanya melalui parameter, pertimbangkan static fungsi lokal:

static int LastChapter(int firstChapter, int chaptersPerWeek, int chapterCount) =>
    Math.Min(firstChapter + chaptersPerWeek - 1, chapterCount);

Fungsi lokal statis tidak dapat menangkap variabel lokal, parameter, atau status instans dari anggota yang berisi. Parameternya membuat data yang disediakan eksplisit, sementara capture memungkinkan fungsi pembantu menggunakan konteks operasi yang berisi.

Gunakan rekursi saat masalah berulang

Rekursi terjadi ketika fungsi memanggil dirinya sendiri dengan bagian yang lebih kecil dari masalah. AddWeek dalam contoh menambahkan satu minggu, lalu memanggil dirinya sendiri dengan bab pertama untuk minggu depan.

Setiap fungsi rekursif membutuhkan kondisi yang menghentikan rekursi. AddWeek berhenti ketika firstChapter melebihi chapterCount:

if (firstChapter > chapterCount)
{
    return;
}

Fungsi lokal bekerja dengan baik untuk rekursi karena namanya berada dalam cakupan di seluruh anggota yang memuatnya. Gunakan perulangan ketika mengekspresikan pekerjaan berulang secara lebih langsung. Pilih rekursi saat struktur rekursif membuat algoritma lebih mudah dipahami.

Pilih fungsi lokal atau ekspresi lambda

Fungsi lokal dan ekspresi lambda keduanya dapat menggunakan variabel di sekitarnya, tetapi keduanya menyampaikan maksud yang berbeda:

Pilih When
Fungsi lokal Helper ini mendapat manfaat jika diberi nama, memiliki beberapa pernyataan, membutuhkan rekursi, atau dipanggil dari lebih dari satu tempat di anggota induk.
Ekspresi Lambda Anda meneruskan blok perilaku singkat langsung ke metode lain atau menyimpannya dalam variabel delegasi.

Kode berikut menggunakan kedua bentuk ini:

static IEnumerable<string> DescribeShortWeeks(
    IEnumerable<ReadingWeek> plan,
    int maximumChapters)
{
    return plan
        .Where(IsShortWeek)
        .Select(week => $"Week {week.Number}: {week.FirstChapter}-{week.LastChapter}");

    bool IsShortWeek(ReadingWeek week) =>
        week.LastChapter - week.FirstChapter + 1 <= maximumChapters;
}

Console.WriteLine("Short weeks:");
foreach (string description in DescribeShortWeeks(plan, maximumChapters: 2))
{
    Console.WriteLine(description);
}

IsShortWeek memberi nama deskriptif pada aturan yang diteruskan ke Where. Lambda yang diteruskan ke Select melakukan satu transformasi pendek pada titik penggunaannya. Kedua bentuk memberikan perilaku ke metode lain di sini. Fungsi lokal juga dapat menjadi pembantu yang Anda panggil secara langsung, seperti AddWeek dalam contoh rencana bacaan.

Gunakan metode reguler alih-alih fungsi lokal saat beberapa anggota membutuhkan metode pembantu. Gunakan lambda sebagai gantinya ketika perilakunya pendek dan tujuannya jelas dari metode pemanggil.

Fungsi lokal juga dapat menjadi fungsi iterator yang berisi yield return. Untuk itu, lihat Iterators.

Baca juga