Fonctions locales

Choisissez une fonction locale lorsque vous souhaitez donner un nom descriptif à la logique auxiliaire et qu’une seule méthode a besoin de cette aide. Par rapport à une méthode sur le type conteneur, une fonction locale indique aux lecteurs que la fonction auxiliaire appartient à cette opération. Son nom permet aux lecteurs de suivre le flux principal avant d’inspecter les détails.

Tip

Cet article explique l’utilisation pratique des fonctions locales pour les développeurs qui savent écrire des méthodes. Pour les détails de déclaration et les règles de diagnostic, consultez les déclarations de fonction locale.

Une fonction locale est une fonction nommée déclarée à l’intérieur d’un autre membre, telle qu’une méthode ou un constructeur. Son nom n’est visible qu’au sein de ce membre.

La forme suivante déclare et appelle une fonction locale :

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

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

AddGreeting reste à l’intérieur de CreateMessage. Les autres membres du type ne peuvent pas l’appeler.

Conserver la logique utilitaire près du code qui l’appelle

Les fonctions locales permettent au flux principal d’indiquer ce qui se passe, tandis que les fonctions d’assistance expliquent les étapes individuelles. L’exemple suivant crée un plan de lecture. Il utilise une fonction locale pour ajouter une semaine d’affectation à la fois.

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

Un ReadingWeek enregistre un numéro de semaine et la plage de chapitres attribués pour cette semaine. AddWeek appartient uniquement à CreateReadingPlan, de sorte que son étendue locale communique cette relation.

Vous pouvez déclarer une fonction locale avant ou après les instructions qui l’appellent. Placez-le là où les lecteurs peuvent suivre le flux principal sans perdre de contexte.

Utiliser des paramètres ou capturer l’état local

Une fonction locale non statique peut utiliser des paramètres et des variables locales de son membre englobant. Cette utilisation est appelée capture. Dans l’exemple de plan de lecture, AddWeek capture :

  • bookTitle, chapterCount et chaptersPerWeek des paramètres CreateReadingPlan.
  • plan à partir d’une variable locale.

La capture est utile lorsque l’assistant appartient naturellement à l’opération environnante. Conservez les valeurs capturées en nombre limité et évidentes. Si la fonction auxiliaire peut recevoir toutes ses données par le biais de paramètres, envisagez une static fonction locale :

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

Une fonction locale statique ne peut pas capturer les variables locales, les paramètres ou l’état de l’instance à partir du membre conteneur. Ses paramètres rendent les données fournies explicites, tandis que la capture permet à un assistant d’utiliser le contexte de son opération contenante.

Utiliser la récursivité lorsque le problème se répète lui-même

La récursivité se produit lorsqu’une fonction s’appelle elle-même avec une partie plus petite du problème. AddWeek dans l’exemple ajoute une semaine, puis s’appelle avec le premier chapitre pour la semaine suivante.

Chaque fonction récursive a besoin d’une condition qui arrête les appels. AddWeek s’arrête lorsque firstChapter dépasse chapterCount :

if (firstChapter > chapterCount)
{
    return;
}

Les fonctions locales fonctionnent bien pour la récursivité, car leurs noms sont visibles dans tout le membre conteneur. Préférez une boucle lorsqu’elle exprime le travail répété plus directement. Choisissez la récursivité lorsque la structure récursive facilite la compréhension de l’algorithme.

Choisir une fonction locale ou une expression lambda

Les fonctions locales et les expressions lambda peuvent utiliser les variables environnantes, mais elles communiquent une intention différente :

Choisir Lorsque
Fonction locale La fonction auxiliaire bénéficie d’un nom, a plusieurs instructions de code, a besoin de récursivité ou est appelée à partir de plusieurs emplacements dans le membre conteneur.
Expression lambda Vous passez un petit bloc de code directement à une autre méthode ou le stockez dans une variable déléguée.

Le code suivant utilise les deux formes :

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 donne à la règle passée à Where un nom descriptif. Le lambda passé pour Select effectue une transformation courte à son point d’utilisation. Les deux formes fournissent un comportement à une autre méthode ici. Une fonction locale peut également être une fonction utilitaire que vous appelez directement, comme AddWeek dans l’exemple de plan de lecture.

Utilisez une méthode régulière au lieu d’une fonction locale lorsque plusieurs membres ont besoin de la fonction d’aide. Utilisez plutôt une expression lambda lorsque le comportement est court et que son objectif est clair à la lecture de la méthode appelante.

Les fonctions locales peuvent également être des fonctions itérateurs qui contiennent yield return. Pour cette utilisation, consultez Itérateurs.

Voir également :