Funções locais

Escolhe uma função local quando quiseres dar um nome descritivo à lógica auxiliar e só um membro precisa desse auxiliar. Comparado com um método no tipo que contém, uma função local indica aos leitores que a função auxiliar pertence a esta operação. O seu nome permite aos leitores seguir o fluxo principal antes de inspecionarem os detalhes.

Tip

Este artigo ensina o uso prático de funções locais para programadores que sabem escrever métodos. Para detalhes das declarações e regras de diagnóstico, veja Declarações de função local.

Uma função local é uma função nomeada declarada dentro de outro membro, como um método ou construtor. O seu nome só é visível dentro desse membro.

A seguinte figura declara e chama uma função local:

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

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

AddGreeting fica dentro de CreateMessage. Outros membros do tipo não conseguem chamá-la.

Mantenha a lógica auxiliar perto do seu chamador

As funções locais permitem que o fluxo principal indique o que acontece, enquanto as funções auxiliares explicam os passos individuais. O exemplo seguinte cria um plano de leitura. Utiliza uma função local para adicionar uma semana de atribuição de cada vez.

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

A ReadingWeek regista o número da semana e o intervalo de capítulos atribuídos para essa semana. AddWeek pertence apenas a CreateReadingPlan, pelo que o seu âmbito local comunica essa relação.

Pode declarar uma função local antes ou depois das instruções que a chamam. Coloque-o de forma que os leitores possam acompanhar o fluxo principal sem perder o contexto.

Usar parâmetros ou capturar o estado local

Uma função local não estática pode usar parâmetros e variáveis locais do membro que a contém. Esta utilização chama-se captura. No exemplo do plano de leitura, AddWeek captura:

  • bookTitle, chapterCount e chaptersPerWeek dos parâmetros CreateReadingPlan.
  • plan a partir de uma variável local.

A captura é útil quando a função auxiliar pertence naturalmente à operação envolvente. Mantenha poucos e óbvios os valores capturados. Se o ajudante conseguir receber todos os seus dados através de parâmetros, considere uma static função local:

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

Uma função local estática não pode capturar variáveis locais, parâmetros ou estado de instância do membro que o contém. Os seus parâmetros tornam explícitos os dados fornecidos, enquanto a captura permite a um ajudante usar o contexto da operação que a contém.

Usa recursão quando o problema se repete

A recursão ocorre quando uma função se chama a si própria com uma parte menor do problema. AddWeek no exemplo acrescenta uma semana, depois chama-se a si próprio com o primeiro capítulo para a semana seguinte.

Toda função recursiva precisa de uma condição que pare as chamadas. AddWeek pára quando firstChapter excede chapterCount:

if (firstChapter > chapterCount)
{
    return;
}

As funções locais funcionam bem para recursão porque os seus nomes estão no âmbito ao longo de todo o membro que o contém. Prefira um loop quando expressa o trabalho repetido de forma mais direta. Escolha recursão quando a estrutura recursiva tornar o algoritmo mais fácil de entender.

Escolha uma função local ou expressão lambda

As funções locais e as expressões lambda podem ambas usar variáveis circundantes, mas comunicam intenções diferentes:

Selecione Quando
Função local A função auxiliar beneficia de ter um nome, tem várias instruções, necessita de recursão, ou é chamada a partir de mais do que um local no membro contentor.
Expressão lambda Passa o comportamento breve diretamente para outro método ou armazena-o numa variável de delegado.

O seguinte código utiliza ambas as formas:

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 dá a regra passada a Where um nome descritivo. A função lambda passada para Select realiza uma curta transformação no seu ponto de utilização. Ambas as formas fornecem comportamento a outro método aqui. Uma função local pode também ser uma função auxiliar que pode chamar diretamente, como AddWeek no exemplo do plano de leitura.

Use um método normal em vez de uma função local quando vários membros precisarem da função auxiliar. Use uma expressão lambda em vez disso quando o comportamento é curto e o seu propósito é claro pelo método receptor.

As funções locais também podem ser funções iteradoras que contêm yield return. Para esse uso, veja Iteradores.

Ver também