Funciones locales

Elija una función local cuando quiera asignar un nombre descriptivo a la lógica auxiliar y solo un elemento necesita esa función auxiliar. En comparación con un método en el tipo contenedor, una función local indica a los lectores que la función auxiliar pertenece a esta operación. Su nombre permite a los lectores seguir el flujo principal antes de inspeccionar los detalles.

Tip

En este artículo se enseña el uso práctico de funciones locales para desarrolladores que saben cómo escribir métodos. Para obtener detalles de declaración y reglas de diagnóstico, consulte Declaraciones de función local.

Una función local es una función con nombre declarada dentro de otro miembro, como un método o constructor. Su nombre solo tiene alcance dentro de ese miembro.

La siguiente figura declara y llama a una función local:

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

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

AddGreeting permanece dentro de CreateMessage. Otros miembros del tipo no pueden llamarlo.

Mantener la lógica auxiliar cerca de la función que la invoca

Las funciones locales permiten que el flujo principal indique lo que sucede mientras las funciones auxiliares explican los pasos individuales. En el ejemplo siguiente se crea un plan de lectura. Usa una función local para agregar una asignación de una semana a la 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);
    }
}

Un ReadingWeek registra un número de semana y el intervalo de capítulos asignados para esa semana. AddWeek solo pertenece a CreateReadingPlan, por lo que su ámbito local comunica esa relación.

Puede declarar una función local antes o después de las instrucciones que la llaman. Colóquelo donde los lectores puedan seguir el flujo principal sin perder el contexto.

Usar parámetros o capturar el estado local

Una función local no estática puede usar parámetros y variables locales de su miembro contenedor. Este uso se denomina captura. En el ejemplo de plan de lectura, AddWeek captura:

  • bookTitle, chapterCount y chaptersPerWeek de los parámetros CreateReadingPlan.
  • Obtenga plan de una variable local.

Capture es útil cuando el auxiliar pertenece naturalmente a la operación circundante. Mantenga los valores capturados al mínimo y que sean evidentes. Si el asistente puede recibir todos sus datos a través de parámetros, considere la posibilidad de usar una static función local:

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

Una función local estática no puede capturar variables locales, parámetros ni estado de instancia del miembro contenedor. Sus parámetros hacen que los datos proporcionados sean explícitos, mientras que la captura permite a un asistente usar el contexto de su operación contenedora.

Utiliza la recursividad cuando el problema se repite a sí mismo

La recursividad se produce cuando una función se llama a sí misma con una parte más pequeña del problema. AddWeek en el ejemplo añade una semana y, a continuación, se llama a sí mismo con el primer capítulo de la semana siguiente.

Cada función recursiva necesita una condición que detenga las llamadas. AddWeek se detiene cuando firstChapter supera chapterCount:

if (firstChapter > chapterCount)
{
    return;
}

Las funciones locales funcionan bien para la recursividad porque sus nombres son visibles en todo el miembro contenedor. Prefiere un bucle cuando expresa el trabajo repetido más directamente. Elija la recursividad cuando la estructura recursiva haga que el algoritmo sea más fácil de entender.

Elija una función local o una expresión lambda

Tanto las funciones locales como las expresiones lambda pueden usar variables circundantes, pero comunican una intención diferente:

Elegir Cuando
Función local La función auxiliar se beneficia de un nombre, tiene varias instrucciones, necesita recursividad o se llama desde más de un lugar en el miembro contenedor.
Expresión lambda Se pasa un comportamiento breve directamente a otro método o se almacena en una variable de delegado.

El código siguiente usa ambas 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 da un nombre descriptivo a la regla pasada a Where. La expresión lambda pasada a Select realiza una transformación corta en su punto de uso. Ambas formas proporcionan comportamiento a otro método aquí. Una función local también puede ser una función auxiliar a la que se llama directamente, como ocurre con AddWeek en el ejemplo de plan de lectura.

Use un método normal en lugar de una función local cuando varios miembros necesiten el método auxiliar. Utilice una expresión lambda en su lugar cuando el comportamiento sea corto y su propósito se deduzca del método receptor.

Las funciones locales también pueden ser funciones de iterador que contienen yield return. Para ese uso, consulte Iteradores.

Consulte también