Funkcje lokalne

Wybierz funkcję lokalną, jeśli chcesz nadać logice pomocnika opisową nazwę i tylko jeden element członkowski potrzebuje tego pomocnika. W porównaniu z metodą w typie zawierającym funkcja lokalna informuje czytelników, że pomocnik należy do tej operacji. Jego nazwa umożliwia czytelnikom śledzenie głównego przepływu przed sprawdzeniem szczegółów.

Wskazówka

W tym artykule przedstawiono praktyczne wykorzystanie funkcji lokalnych dla deweloperów, którzy wiedzą, jak pisać metody. Aby uzyskać szczegółowe informacje o deklaracji i reguły diagnostyczne, zobacz Lokalne deklaracje funkcji.

Funkcja lokalna jest funkcją nazwaną zadeklarowaną wewnątrz innego elementu członkowskiego, taką jak metoda lub konstruktor. Jego nazwa jest widoczna tylko w obrębie tego elementu.

Następujący kształt deklaruje i wywołuje funkcję lokalną:

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

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

AddGreeting znajduje się wewnątrz elementu CreateMessage. Inni członkowie typu nie mogą go wywołać.

Zachowaj logikę pomocniczą w pobliżu miejsca wywołania

Funkcje lokalne pozwalają głównemu przepływowi określić, co się dzieje, podczas gdy funkcje pomocnicze wyjaśniają poszczególne kroki. Poniższy przykład tworzy plan czytania. Używa funkcji lokalnej, aby dodać jednotygodniowy przydział naraz.

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);
    }
}

Element ReadingWeek rejestruje numer tygodnia i zakres rozdziałów przypisanych do tego tygodnia. AddWeek należy tylko do CreateReadingPlan elementu, więc jego zakres lokalny wyraża tę relację.

Funkcję lokalną można zadeklarować przed lub po instrukcjach, które ją wywołują. Umieść go tam, gdzie czytelnicy mogą śledzić główny tok bez utraty kontekstu.

Używanie parametrów lub przechwytywanie stanu lokalnego

Niestatyczna funkcja lokalna może używać parametrów i zmiennych lokalnych z zawierającej ją składowej. To użycie jest nazywane przechwytywaniem. W przykładzie planu czytania AddWeek przedstawia:

  • bookTitle, chapterCounti chaptersPerWeek z parametrów CreateReadingPlan .
  • plan odczytaj ze zmiennej lokalnej.

Przechwycenie jest przydatne, gdy pomocnik naturalnie należy do otaczającej operacji. Ogranicz liczbę przechwytywanych wartości do kilku oczywistych. Jeśli pomocnik może odbierać wszystkie swoje dane za pomocą parametrów, rozważ funkcję lokalną static :

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

Statyczna funkcja lokalna nie może przechwytywać zmiennych lokalnych, parametrów ani stanu wystąpienia w obrębie składowej zawierającej. Jego parametry sprawiają, że podane dane są jawne, a funkcja przechwytywania umożliwia pomocnikowi użycie kontekstu operacji, w której został zdefiniowany.

Użyj rekurencji, gdy problem nawraca

Rekursja występuje, gdy funkcja wywołuje samą siebie z mniejszą częścią problemu. AddWeek w przykładzie dodaje jeden tydzień, a następnie wywołuje sam siebie z pierwszym rozdziałem na następny tydzień.

Każda funkcja rekursywna wymaga warunku, który zatrzymuje wywołania. AddWeekzatrzymuje się, gdy firstChapter przekracza chapterCount:

if (firstChapter > chapterCount)
{
    return;
}

Funkcje lokalne działają dobrze w przypadku rekursji, ponieważ ich nazwy są widoczne w całym zakresie składowej zawierającej. Preferuj pętlę, gdy wyraża powtarzaną pracę bardziej bezpośrednio. Wybierz rekursję, gdy struktura rekursywna ułatwia zrozumienie algorytmu.

Wybierz funkcję lokalną lub wyrażenie lambda

Funkcje lokalne i wyrażenia lambda mogą używać zmiennych otaczających, ale komunikują różne intencje:

Wybierz Kiedy
Funkcja lokalna Pomocnicza funkcja korzysta na posiadaniu nazwy, ma kilka instrukcji, wymaga rekursji lub jest wywoływana z więcej niż jednego miejsca w elemencie członkowskim zawierającym tę funkcję.
Wyrażenie lambda Przekazujesz krótkie zachowanie bezpośrednio do innej metody lub przechowujesz je w zmiennej delegata.

Poniższy kod używa obu form:

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 nadaje regule przekazanej do Where opisową nazwę. Przekazana do Select lambda wykonuje jedną krótką transformację w miejscu użycia. Oba formularze przekazują działanie do innej metody w tym miejscu. Funkcja lokalna może być również pomocnikiem, który wywołujesz bezpośrednio, podobnie jak AddWeek w przykładzie dotyczącym planu czytania.

Użyj metody regularnej zamiast funkcji lokalnej, gdy kilka elementów klasy potrzebuje pomocnika. Zamiast tego należy użyć wyrażenia lambda, gdy zachowanie jest krótkie, a jego przeznaczenie jasno wynika z metody przyjmującej.

Funkcje lokalne mogą być również funkcjami iteratora, które zawierają yield return. W tym celu zobacz Iteratory.

Informacje dodatkowe