Funkcje lokalne (Przewodnik programowania w języku C#)Local functions (C# Programming Guide)

Począwszy od języka C# 7,0, C# obsługuje funkcje lokalne.Starting with C# 7.0, C# supports local functions. Funkcje lokalne są prywatnymi metodami typu, które są zagnieżdżone w innym elemencie członkowskim.Local functions are private methods of a type that are nested in another member. Mogą być wywoływane tylko z ich składowych.They can only be called from their containing member. Funkcje lokalne można zadeklarować w i wywołać z:Local functions can be declared in and called from:

  • Metody, zwłaszcza metody iteratorów i metody asynchroniczneMethods, especially iterator methods and async methods
  • KonstruktoryConstructors
  • Metody dostępu do właściwościProperty accessors
  • Metody dostępu zdarzeńEvent accessors
  • Metody anonimoweAnonymous methods
  • Wyrażenia lambdaLambda expressions
  • FinalizatoryFinalizers
  • Inne funkcje lokalneOther local functions

Jednak funkcji lokalnych nie można deklarować wewnątrz elementu członkowskiego będącego w posiadaniu wyrażenia.However, local functions can't be declared inside an expression-bodied member.

Uwaga

W niektórych przypadkach można użyć wyrażenia lambda, aby zaimplementować funkcje również obsługiwane przez funkcję lokalną.In some cases, you can use a lambda expression to implement functionality also supported by a local function. Aby zapoznać się z porównaniem, zobacz funkcje lokalne a wyrażenia lambda.For a comparison, see Local functions vs. lambda expressions.

Funkcje lokalne sprawiają, że zamiar kodu jest przejrzysty.Local functions make the intent of your code clear. Każda osoba odczytująca kod może zobaczyć, że metoda nie jest wywoływana z wyjątkiem metody zawierającej.Anyone reading your code can see that the method is not callable except by the containing method. W przypadku projektów zespołowych uniemożliwiają one również innym deweloperom błędne wywoływanie metody bezpośrednio z innych miejsc w klasie lub strukturze.For team projects, they also make it impossible for another developer to mistakenly call the method directly from elsewhere in the class or struct.

Składnia funkcji lokalnychLocal function syntax

Funkcja lokalna jest definiowana jako metoda zagnieżdżona wewnątrz składowej zawierającej.A local function is defined as a nested method inside a containing member. Jego definicja ma następującą składnię:Its definition has the following syntax:

<modifiers> <return-type> <method-name> <parameter-list>

Można użyć następujących modyfikatorów z funkcją lokalną:You can use the following modifiers with a local function:

  • async
  • unsafe
  • static (w języku C# 8,0 i nowszych).static (in C# 8.0 and later). Statyczna funkcja lokalna nie może przechwycić lokalnych zmiennych lub stanu wystąpienia.A static local function can't capture local variables or instance state.
  • extern (w języku C# 9,0 i nowszych).extern (in C# 9.0 and later). Zewnętrzna funkcja lokalna musi być static .An external local function must be static.

Wszystkie zmienne lokalne, które są zdefiniowane w składowej zawierającej, łącznie z parametrami metody, są dostępne w niestatycznej funkcji lokalnej.All local variables that are defined in the containing member, including its method parameters, are accessible in a non-static local function.

W przeciwieństwie do definicji metody lokalnej definicja funkcji nie może zawierać modyfikatora dostępu do składowej.Unlike a method definition, a local function definition cannot include the member access modifier. Ponieważ wszystkie funkcje lokalne są prywatne, łącznie z modyfikatorem dostępu, takim jak private słowo kluczowe, generuje błąd kompilatora CS0106 "modyfikator" Private "jest nieprawidłowy dla tego elementu".Because all local functions are private, including an access modifier, such as the private keyword, generates compiler error CS0106, "The modifier 'private' is not valid for this item."

W poniższym przykładzie zdefiniowano funkcję lokalną o nazwie AppendPathSeparator , która jest prywatna dla metody o nazwie GetText :The following example defines a local function named AppendPathSeparator that is private to a method named GetText:

private static string GetText(string path, string filename)
{
     var reader = File.OpenText($"{AppendPathSeparator(path)}{filename}");
     var text = reader.ReadToEnd();
     return text;

     string AppendPathSeparator(string filepath)
     {
        return filepath.EndsWith(@"\") ? filepath : filepath + @"\";
     }
}

Począwszy od języka C# 9,0, można zastosować atrybuty do funkcji lokalnej, jej parametrów i parametrów typu, jak pokazano na poniższym przykładzie:Beginning with C# 9.0, you can apply attributes to a local function, its parameters and type parameters, as the following example shows:

#nullable enable
private static void Process(string?[] lines, string mark)
{
    foreach (var line in lines)
    {
        if (IsValid(line))
        {
            // Processing logic...
        }
    }

    bool IsValid([NotNullWhen(true)] string? line)
    {
        return !string.IsNullOrEmpty(line) && line.Length >= mark.Length;
    }
}

Poprzedni przykład używa specjalnego atrybutu , aby pomóc kompilatorowi w analizie statycznej w kontekście dopuszczającym wartość null.The preceding example uses a special attribute to assist the compiler in static analysis in a nullable context.

Lokalne funkcje i wyjątkiLocal functions and exceptions

Jedną z użytecznych funkcji lokalnych funkcji jest możliwość natychmiastowego zezwolenia na korzystanie z wyjątków.One of the useful features of local functions is that they can allow exceptions to surface immediately. W przypadku iteratorów metod wyjątki są nakierowane tylko wtedy, gdy zwracana sekwencja jest wyliczana, a nie podczas pobierania iteratora.For method iterators, exceptions are surfaced only when the returned sequence is enumerated, and not when the iterator is retrieved. W przypadku metod asynchronicznych wszystkie wyjątki zgłoszone w metodzie asynchronicznej są zaobserwowane, gdy zwracane zadanie jest oczekiwane.For async methods, any exceptions thrown in an async method are observed when the returned task is awaited.

W poniższym przykładzie zdefiniowano OddSequence metodę, która wylicza liczby nieparzyste w określonym zakresie.The following example defines an OddSequence method that enumerates odd numbers in a specified range. Ponieważ przekazuje liczbę większą niż 100 do OddSequence metody Enumerator, metoda zgłasza ArgumentOutOfRangeException .Because it passes a number greater than 100 to the OddSequence enumerator method, the method throws an ArgumentOutOfRangeException. Ponieważ dane wyjściowe z przykładu pokazują, powierzchnie wyjątków tylko w przypadku iteracji liczby, a nie podczas pobierania modułu wyliczającego.As the output from the example shows, the exception surfaces only when you iterate the numbers, and not when you retrieve the enumerator.

using System;
using System.Collections.Generic;

public class IteratorWithoutLocalExample
{
   public static void Main()
   {
      IEnumerable<int> xs = OddSequence(50, 110);
      Console.WriteLine("Retrieved enumerator...");

      foreach (var x in xs)  // line 11
      {
         Console.Write($"{x} ");
      }
   }

   public static IEnumerable<int> OddSequence(int start, int end)
   {
      if (start < 0 || start > 99)
         throw new ArgumentOutOfRangeException(nameof(start), "start must be between 0 and 99.");
      if (end > 100)
         throw new ArgumentOutOfRangeException(nameof(end), "end must be less than or equal to 100.");
      if (start >= end)
         throw new ArgumentException("start must be less than end.");

      for (int i = start; i <= end; i++)
      {
         if (i % 2 == 1)
            yield return i;
      }
   }
}
// The example displays the output like this:
//
//    Retrieved enumerator...
//    Unhandled exception. System.ArgumentOutOfRangeException: end must be less than or equal to 100. (Parameter 'end')
//    at IteratorWithoutLocalExample.OddSequence(Int32 start, Int32 end)+MoveNext() in IteratorWithoutLocal.cs:line 22
//    at IteratorWithoutLocalExample.Main() in IteratorWithoutLocal.cs:line 11

W przypadku umieszczenia logiki iteratora w funkcji lokalnej, wyjątki walidacji argumentów są generowane podczas pobierania modułu wyliczającego, jak pokazano w poniższym przykładzie:If you put iterator logic into a local function, argument validation exceptions are thrown when you retrieve the enumerator, as the following example shows:

using System;
using System.Collections.Generic;

public class IteratorWithLocalExample
{
   public static void Main()
   {
      IEnumerable<int> xs = OddSequence(50, 110);  // line 8
      Console.WriteLine("Retrieved enumerator...");

      foreach (var x in xs)
      {
         Console.Write($"{x} ");
      }
   }

   public static IEnumerable<int> OddSequence(int start, int end)
   {
      if (start < 0 || start > 99)
         throw new ArgumentOutOfRangeException(nameof(start), "start must be between 0 and 99.");
      if (end > 100)
         throw new ArgumentOutOfRangeException(nameof(end), "end must be less than or equal to 100.");
      if (start >= end)
         throw new ArgumentException("start must be less than end.");

      return GetOddSequenceEnumerator();

      IEnumerable<int> GetOddSequenceEnumerator()
      {
         for (int i = start; i <= end; i++)
         {
            if (i % 2 == 1)
               yield return i;
         }
      }
   }
}
// The example displays the output like this:
//
//    Unhandled exception. System.ArgumentOutOfRangeException: end must be less than or equal to 100. (Parameter 'end')
//    at IteratorWithLocalExample.OddSequence(Int32 start, Int32 end) in IteratorWithLocal.cs:line 22
//    at IteratorWithLocalExample.Main() in IteratorWithLocal.cs:line 8

Funkcji lokalnych można używać w podobny sposób z operacjami asynchronicznymi.You can use local functions in a similar way with asynchronous operations. Wyjątki zgłoszone na powierzchni metody asynchronicznej, gdy jest oczekiwane odpowiednie zadanie.Exceptions thrown in an async method surface when the corresponding task is awaited. Funkcje lokalne umożliwiają szybkie i niepowodzenie wykonywania kodu oraz umożliwiają synchroniczną i zaobserwowany wyjątek.Local functions allow your code to fail fast and allow your exception to be both thrown and observed synchronously.

W poniższym przykładzie zastosowano metodę asynchroniczną o nazwie GetMultipleAsync do pauzy przez określoną liczbę sekund i zwracają wartość, która jest losowo wielokrotnością tej liczby sekund.The following example uses an asynchronous method named GetMultipleAsync to pause for a specified number of seconds and return a value that is a random multiple of that number of seconds. Maksymalne opóźnienie wynosi 5 sekund; ArgumentOutOfRangeException wyniki, jeśli wartość jest większa niż 5.The maximum delay is 5 seconds; an ArgumentOutOfRangeException results if the value is greater than 5. Jak pokazano na poniższym przykładzie, wyjątek, który jest generowany, gdy wartość 6 jest przekazana do GetMultipleAsync metody jest zaobserwowana tylko wtedy, gdy zadanie jest oczekiwane.As the following example shows, the exception that is thrown when a value of 6 is passed to the GetMultipleAsync method is observed only when the task is awaited.

using System;
using System.Threading.Tasks;

public class AsyncWithoutLocalExample
{
   public static async Task Main()
   {
      var t = GetMultipleAsync(6);
      Console.WriteLine("Got the task");
      
      var result = await t;  // line 11
      Console.WriteLine($"The returned value is {result:N0}");
   }

   static async Task<int> GetMultipleAsync(int delayInSeconds)
   {
      if (delayInSeconds < 0 || delayInSeconds > 5)
         throw new ArgumentOutOfRangeException(nameof(delayInSeconds), "Delay cannot exceed 5 seconds.");

      await Task.Delay(delayInSeconds * 1000);
      return delayInSeconds * new Random().Next(2,10);
   }
}
// The example displays the output like this:
//
// Got the task
// Unhandled exception. System.ArgumentOutOfRangeException: Delay cannot exceed 5 seconds. (Parameter 'delayInSeconds')
//   at AsyncWithoutLocalExample.GetMultipleAsync(Int32 delayInSeconds) in AsyncWithoutLocal.cs:line 18
//   at AsyncWithoutLocalExample.Main() in AsyncWithoutLocal.cs:line 11

Podobnie jak w przypadku iteratora metody można ponownie określić poprzedni przykład i umieścić kod operacji asynchronicznej w funkcji lokalnej.Like with the method iterator, you can refactor the preceding example and put the code of asynchronous operation in a local function. Jak pokazano na poniższym przykładzie, zostanie zgłoszony, gdy ArgumentOutOfRangeException tylko GetMultiple Metoda zostanie wywołana.As the output from the following example shows, the ArgumentOutOfRangeException is thrown as soon as the GetMultiple method is called.

using System;
using System.Threading.Tasks;

public class AsyncWithLocalExample
{
   public static async Task Main()
   {
      var t = GetMultiple(6);  // line 8
      Console.WriteLine("Got the task");
      
      var result = await t;
      Console.WriteLine($"The returned value is {result:N0}");
   }

   static Task<int> GetMultiple(int delayInSeconds)
   {
      if (delayInSeconds < 0 || delayInSeconds > 5)
         throw new ArgumentOutOfRangeException(nameof(delayInSeconds), "Delay cannot exceed 5 seconds.");

      return GetValueAsync();

      async Task<int> GetValueAsync()
      {
         await Task.Delay(delayInSeconds * 1000);
         return delayInSeconds * new Random().Next(2,10);
      }
   }
}
// The example displays the output like this:
//
// Unhandled exception. System.ArgumentOutOfRangeException: Delay cannot exceed 5 seconds. (Parameter 'delayInSeconds')
//   at AsyncWithLocalExample.GetMultiple(Int32 delayInSeconds) in AsyncWithLocal.cs:line 18
//   at AsyncWithLocalExample.Main() in AsyncWithLocal.cs:line 8

Funkcje lokalne a wyrażenia lambdaLocal functions vs. lambda expressions

Na pierwszy rzut oka funkcje lokalne i wyrażenia lambda są bardzo podobne.At first glance, local functions and lambda expressions are very similar. W wielu przypadkach wybór między wyrażeniami lambda i funkcjami lokalnymi jest kwestią stylu i preferencji osobistych.In many cases, the choice between using lambda expressions and local functions is a matter of style and personal preference. Istnieją jednak rzeczywiste różnice w tym, gdzie można korzystać z jednej z nich lub drugiej.However, there are real differences in where you can use one or the other that you should be aware of.

Sprawdźmy różnice między funkcją lokalną a implementacją wyrażenia lambda algorytmu silnia.Let's examine the differences between the local function and lambda expression implementations of the factorial algorithm. Oto wersja z użyciem funkcji lokalnej:Here's the version using a local function:

public static int LocalFunctionFactorial(int n)
{
    return nthFactorial(n);

    int nthFactorial(int number) => number < 2 
        ? 1 
        : number * nthFactorial(number - 1);
}

Ta wersja używa wyrażeń lambda:This version uses lambda expressions:

public static int LambdaFactorial(int n)
{
    Func<int, int> nthFactorial = default(Func<int, int>);

    nthFactorial = number => number < 2
        ? 1
        : number * nthFactorial(number - 1);

    return nthFactorial(n);
}

NazewnictwoNaming

Funkcje lokalne są jawnie nazywane metodami podobnymi.Local functions are explicitly named like methods. Wyrażenia lambda to metody anonimowe i muszą być przypisane do zmiennych delegate typu, zazwyczaj obu Action lub Func typów.Lambda expressions are anonymous methods and need to be assigned to variables of a delegate type, typically either Action or Func types. Kiedy deklarujesz funkcję lokalną, proces przypomina pisanie normalnej metody; deklaruje zwracany typ i sygnaturę funkcji.When you declare a local function, the process is like writing a normal method; you declare a return type and a function signature.

Sygnatury funkcji i typy wyrażeń lambdaFunction signatures and lambda expression types

Wyrażenia lambda bazują na typie Action / Func zmiennej, do której są przypisane, aby określić argument i typy zwracane.Lambda expressions rely on the type of the Action/Func variable that they're assigned to determine the argument and return types. W funkcjach lokalnych, ponieważ składnia jest podobnie jak pisanie normalnej metody, typy argumentów i typ zwracany są już częścią deklaracji funkcji.In local functions, since the syntax is much like writing a normal method, argument types and return type are already part of the function declaration.

Przypisanie określoneDefinite assignment

Wyrażenia lambda są obiektami, które są zadeklarowane i przypisywane w czasie wykonywania.Lambda expressions are objects that are declared and assigned at runtime. Aby można było użyć wyrażenia lambda, musi ono być ostatecznie przypisane: Action / Func zmienna, do której zostanie przypisany, musi być zadeklarowana i wyrażenie lambda przypisane do niego.In order for a lambda expression to be used, it needs to be definitely assigned: the Action/Func variable that it will be assigned to must be declared and the lambda expression assigned to it. Należy zauważyć, że LambdaFactorial przed zdefiniowaniem należy zadeklarować i zainicjować wyrażenie lambda nthFactorial .Notice that LambdaFactorial must declare and initialize the lambda expression nthFactorial before defining it. Nie powoduje to błędu czasu kompilacji dla odwołania nthFactorial przed przypisaniem.Not doing so results in a compile time error for referencing nthFactorial before assigning it.

Funkcje lokalne są definiowane w czasie kompilacji.Local functions are defined at compile time. Ponieważ nie są one przypisane do zmiennych, można odwoływać się do nich z dowolnego miejsca w kodzie, w którym znajduje się w zakresie; w pierwszym przykładzie LocalFunctionFactorial możemy zadeklarować funkcję lokalną powyżej lub poniżej return instrukcji i nie wyzwolić błędów kompilatora.As they're not assigned to variables, they can be referenced from any code location where it is in scope; in our first example LocalFunctionFactorial, we could declare our local function either above or below the return statement and not trigger any compiler errors.

Różnice te oznaczają, że algorytmy cykliczne są łatwiejsze do tworzenia przy użyciu funkcji lokalnych.These differences mean that recursive algorithms are easier to create using local functions. Można zadeklarować i zdefiniować funkcję lokalną, która wywołuje samą siebie.You can declare and define a local function that calls itself. Wyrażenia lambda muszą być zadeklarowane i przypisane do wartości domyślnej przed ponownym przypisaniem do treści, która odwołuje się do tego samego wyrażenia lambda.Lambda expressions must be declared, and assigned a default value before they can be re-assigned to a body that references the same lambda expression.

Implementacja jako delegatImplementation as a delegate

Wyrażenia lambda są konwertowane na delegatów, gdy są one deklarowane.Lambda expressions are converted to delegates when they're declared. Funkcje lokalne są bardziej elastyczne, ponieważ mogą być zapisywane jak tradycyjną metodę lub delegat.Local functions are more flexible in that they can be written like a traditional method or as a delegate. Funkcje lokalne są konwertowane na delegatów tylko wtedy, gdy są używane jako delegat.Local functions are only converted to delegates when used as a delegate.

Jeśli zadeklarujesz funkcję lokalną i odwołujesz się do niej tylko przez wywołanie jej jako metody, nie zostanie ona przekonwertowana na delegata.If you declare a local function and only reference it by calling it like a method, it will not be converted to a delegate.

Przechwytywanie zmiennychVariable capture

Reguły o nieograniczonym przypisaniu wpływają również na wszystkie zmienne, które są przechwytywane przez funkcję lokalną lub wyrażenie lambda.The rules of definite assignment also affect any variables that are captured by the local function or lambda expression. Kompilator może wykonać analizę statyczną, która umożliwia lokalne funkcje, aby ostatecznie przypisywać przechwycone zmienne w zakresie otaczającym.The compiler can perform static analysis that enables local functions to definitely assign captured variables in the enclosing scope. Rozważ taki przykład:Consider this example:

int M()
{
    int y;
    LocalFunction();
    return y;

    void LocalFunction() => y = 0;
}

Kompilator może określić, że jest on LocalFunction przypisywany, y gdy jest wywoływany.The compiler can determine that LocalFunction definitely assigns y when called. Ponieważ LocalFunction jest wywoływana przed return instrukcją, y jest ostatecznie przypisana do return instrukcji.Because LocalFunction is called before the return statement, y is definitely assigned at the return statement.

Należy pamiętać, że gdy funkcja lokalna przechwytuje zmienne w otaczającym zakresie, funkcja lokalna jest implementowana jako typ delegata.Note that when a local function captures variables in the enclosing scope, the local function is implemented as a delegate type.

Alokacje stertyHeap allocations

W zależności od ich użycia funkcje lokalne mogą uniknąć przydziałów sterty, które są zawsze niezbędne dla wyrażeń lambda.Depending on their use, local functions can avoid heap allocations that are always necessary for lambda expressions. Jeśli funkcja lokalna nigdy nie jest konwertowana na delegata, a żadna ze zmiennych przechwyconych przez funkcję lokalną nie zostanie przechwycona przez inne wyrażenia lambda lub funkcje lokalne, które są konwertowane na delegatów, kompilator może uniknąć przydziałów sterty.If a local function is never converted to a delegate, and none of the variables captured by the local function are captured by other lambdas or local functions that are converted to delegates, the compiler can avoid heap allocations.

Rozważmy ten przykład asynchroniczny:Consider this async example:

public Task<string> PerformLongRunningWorkLambda(string address, int index, string name)
{
    if (string.IsNullOrWhiteSpace(address))
        throw new ArgumentException(message: "An address is required", paramName: nameof(address));
    if (index < 0)
        throw new ArgumentOutOfRangeException(paramName: nameof(index), message: "The index must be non-negative");
    if (string.IsNullOrWhiteSpace(name))
        throw new ArgumentException(message: "You must supply a name", paramName: nameof(name));

    Func<Task<string>> longRunningWorkImplementation = async () =>
    {
        var interimResult = await FirstWork(address);
        var secondResult = await SecondStep(index, name);
        return $"The results are {interimResult} and {secondResult}. Enjoy.";
    };

    return longRunningWorkImplementation();
}

Zamknięcie tego wyrażenia lambda zawiera address index zmienne, i name .The closure for this lambda expression contains the address, index and name variables. W przypadku funkcji lokalnych obiekt implementujący zamknięcie może być struct typem.In the case of local functions, the object that implements the closure may be a struct type. Ten typ struktury zostałby przesłany przez odwołanie do funkcji lokalnej.That struct type would be passed by reference to the local function. Różnica w implementacji spowodowałaby zapisanie alokacji.This difference in implementation would save on an allocation.

Wystąpienie niezbędne dla wyrażeń lambda oznacza dodatkowe alokacje pamięci, które mogą być czynnikiem wydajności w ścieżkach kodu o kluczowym znaczeniu.The instantiation necessary for lambda expressions means extra memory allocations, which may be a performance factor in time-critical code paths. Funkcja lokalna nie wiąże się z tym obciążeniem.Local functions do not incur this overhead. W powyższym przykładzie wersja funkcji lokalnych ma dwa mniejsze alokacje niż wersja wyrażenia lambda.In the example above, the local functions version has two fewer allocations than the lambda expression version.

Jeśli wiesz, że funkcja lokalna nie zostanie przekonwertowana na delegata i żadna ze zmiennych przechwyconych przez nią nie zostanie przechwycona przez inne wyrażenia lambda lub funkcje lokalne, które są konwertowane na Delegaty, możesz zagwarantować, że funkcja lokalna nie zostanie przydzielone na stercie przez zadeklarowanie jej jako static funkcji lokalnej.If you know that your local function won't be converted to a delegate and none of the variables captured by it are captured by other lambdas or local functions that are converted to delegates, you can guarantee that your local function avoids being allocated on the heap by declaring it as a static local function. Należy pamiętać, że ta funkcja jest dostępna w języku C# 8,0 i nowszych.Note that this feature is available in C# 8.0 and newer.

Uwaga

Funkcja lokalna równoważna tej metody używa również klasy do zamykania.The local function equivalent of this method also uses a class for the closure. Czy zamknięcie funkcji lokalnej jest zaimplementowane jako class struct szczegóły implementacji.Whether the closure for a local function is implemented as a class or a struct is an implementation detail. Funkcja lokalna może używać struct wyrażenia lambda, które zawsze będzie używać class .A local function may use a struct whereas a lambda will always use a class.

public Task<string> PerformLongRunningWork(string address, int index, string name)
{
    if (string.IsNullOrWhiteSpace(address))
        throw new ArgumentException(message: "An address is required", paramName: nameof(address));
    if (index < 0)
        throw new ArgumentOutOfRangeException(paramName: nameof(index), message: "The index must be non-negative");
    if (string.IsNullOrWhiteSpace(name))
        throw new ArgumentException(message: "You must supply a name", paramName: nameof(name));

    return longRunningWorkImplementation();

    async Task<string> longRunningWorkImplementation()
    {
        var interimResult = await FirstWork(address);
        var secondResult = await SecondStep(index, name);
        return $"The results are {interimResult} and {secondResult}. Enjoy.";
    }
}

Użycie yield słowa kluczowegoUsage of the yield keyword

Jedną z końcowych zalet nie pokazanych w tym przykładzie jest to, że funkcje lokalne można zaimplementować jako Iteratory, używając yield return składni w celu utworzenia sekwencji wartości.One final advantage not demonstrated in this sample is that local functions can be implemented as iterators, using the yield return syntax to produce a sequence of values.

public IEnumerable<string> SequenceToLowercase(IEnumerable<string> input)
{
    if (!input.Any())
    {
        throw new ArgumentException("There are no items to convert to lowercase.");
    }
    
    return LowercaseIterator();
    
    IEnumerable<string> LowercaseIterator()
    {
        foreach (var output in input.Select(item => item.ToLower()))
        {
            yield return output;
        }
    }
}

yield returnInstrukcja jest niedozwolona w wyrażeniach lambda, zobacz błąd kompilatora CS1621.The yield return statement is not allowed in lambda expressions, see compiler error CS1621.

Podczas gdy funkcje lokalne mogą wydawać się nadmiarowe w wyrażeniach lambda, są one w rzeczywistości wykorzystywane do różnych celów i mają różne zastosowania.While local functions may seem redundant to lambda expressions, they actually serve different purposes and have different uses. Funkcje lokalne są wydajniejsze w przypadku, gdy chcesz napisać funkcję, która jest wywoływana tylko z kontekstu innej metody.Local functions are more efficient for the case when you want to write a function that is called only from the context of another method.

Zobacz teżSee also