Funkcje lokalne w porównaniu do wyrażenia lambdaLocal functions compared to lambda expressions

Na pierwszy rzut oka funkcje lokalne i wyrażeń lambda są bardzo podobne.At first glance, local functions and lambda expressions are very similar. W wielu przypadkach wybór między korzystaniem z wyrażenia lambda i funkcji lokalnych jest kwestią styl i osobistych preferencji.In many cases, the choice between using lambda expressions and local functions is a matter of style and personal preference. Istnieją jednak rzeczywistych różnic, w których można użyć jednej lub drugiej, należy pamiętać o.However, there are real differences in where you can use one or the other that you should be aware of.

Przeanalizujmy różnice między lokalnym funkcji i implementacji wyrażenia lambda silni algorytmu.Let's examine the differences between the local function and lambda expression implementations of the factorial algorithm. Pierwszą wersję przy użyciu funkcji lokalnego:First 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);
}

Porównaj tę implementację, wersji, który używa wyrażenia lambda:Contrast that implementation with a version that 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);
}

Funkcje lokalne mają nazwy.The local functions have names. Wyrażenia lambda są metod anonimowych, które są przypisane do zmiennych, które są Func lub Action typów.The lambda expressions are anonymous methods that are assigned to variables that are Func or Action types. Kiedy Deklarujesz funkcję lokalnego, typy argumentów i zwracanego typu są częścią deklaracji funkcji.When you declare a local function, the argument types and return type are part of the function declaration. Zamiast część treści lambda wyrażenia, typy argumentów i zwracanego typu należą deklaracji zmiennej typu wyrażenia lambda.Instead of being part of the body of the lambda expression, the argument types and return type are part of the lambda expression's variable type declaration. Różnice te dwa może spowodować kod bardziej zrozumiały.Those two differences may result in clearer code.

Funkcje lokalne mają różną asercja określonego przydziału niż wyrażenia lambda.Local functions have different rules for definite assignment than lambda expressions. Deklaracja funkcji lokalnych mogą być przywoływane z dowolnego miejsca kodu, gdzie są dostępne w zakresie.A local function declaration can be referenced from any code location where it is in scope. Wyrażenie lambda musi być przypisany do zmiennej delegata, zanim można uzyskać dostępu do (lub wywoływanym za pośrednictwem delegata, odwołuje się do wyrażenia lambda.) Zwróć uwagę, że wersji za pomocą wyrażenia lambda musi zadeklarować i zainicjować wyrażenia lambda nthFactorial przed zdefiniowaniem go.A lambda expression must be assigned to a delegate variable before it can be accessed (or called through the delegate referencing the lambda expression.) Notice that the version using the lambda expression must declare and initialize the lambda expression, nthFactorial before defining it. To nie powoduje to błąd czasu kompilacji do odwoływania się do nthFactorial przed przypisaniem go.Not doing so results in a compile time error for referencing nthFactorial before assigning it. Te różnice oznacza, że cyklicznego algorytmy są łatwiej utworzyć przy użyciu funkcji lokalnych.These differences mean that recursive algorithms are easier to create using local functions. Można zadeklarować i zdefiniuj funkcji lokalnej, który wywołuje sam siebie.You can declare and define a local function that calls itself. Wyrażenia lambda należy zadeklarować i przypisać wartość domyślną, zanim zostaną ponownie przypisane do jednostki, który 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.

Asercja określonego przydziału reguły również wpływać na wszystkie zmienne, które są przechwytywane przez lokalny funkcja lub wyrażenie lambda.Definite assignment rules also affect any variables that are captured by the local function or lambda expression. Funkcje lokalne i reguł wyrażeń lambda popytu, że wszystkie przechwycone zmienne zdecydowanie są przypisywane w punkcie, po przekonwertowaniu lokalnej funkcja lub wyrażenie lambda do delegata.Both local functions and lambda expression rules demand that any captured variables are definitely assigned at the point when the local function or lambda expression is converted to a delegate. Różnica polega na tym, że wyrażenia lambda są konwertowane na delegatów, gdy są one zgłoszone.The difference is that lambda expressions are converted to delegates when they are declared. Funkcje lokalne są konwertowane do delegatów tylko wtedy, gdy jest używany jako pełnomocnik.Local functions are converted to delegates only when used as a delegate. Jeśli zadeklarujesz funkcją lokalną i odwoływać wywołując jak metody, nie będzie można przekonwertować 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. Tej reguły można zadeklarować funkcji lokalnej w dowolnym miejscu wygodny w jej zasięgu.That rule enables you to declare a local function at any convenient location in its enclosing scope. Są często do deklarowania funkcji lokalnych na końcu metody nadrzędnego po dowolnej instrukcji return.It's common to declare local functions at the end of the parent method, after any return statements.

Po trzecie kompilator będzie mógł wykonać analizy statycznej, który umożliwia funkcji lokalnych zdecydowanie przypisać przechwyconych zmiennych w obejmującym zakresie.Third, the compiler can perform static analysis that enables local functions to definitely assign captured variables in the enclosing scope. Rozważmy następujący przykład:Consider this example:

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

    void LocalFunction() => y = 0;
}

Kompilator można określić, że LocalFunction zdecydowanie przypisuje y po wywołaniu.The compiler can determine that LocalFunction definitely assigns y when called. Ponieważ LocalFunction jest wywoływana przed return instrukcji y zdecydowanie przydzielono return instrukcji.Because LocalFunction is called before the return statement, y is definitely assigned at the return statement.

Analizy, które włącza analizę przykład umożliwia czwarty różnica.The analysis that enables the example analysis enables the fourth difference. W zależności od ich użycie funkcji lokalnych można uniknąć alokacji stosu, które zawsze są niezbędne do wyrażenia 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 konwertowany na obiekt delegowany, a żaden z przechwycone przez funkcję Lokalne zmienne są przechwytywane przez inne wyrażenia lambda lub funkcji lokalnych, które są konwertowane na obiekty delegowane, kompilator można uniknąć alokacji sterty.If a local function is never converted to a delegate, and none of the variables captured by the local function is captured by other lambdas or local functions that are converted to delegates, the compiler can avoid heap allocations.

Rozważmy następujący przykład async: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();
}

Zawiera zamknięcia dla tego wyrażenia lambda address, index i name zmiennych.The closure for this lambda expression contains the address, index and name variables. W przypadku funkcji lokalnych, może być obiekt, który implementuje zamknięcia struct typu.In the case of local functions, the object that implements the closure may be a struct type. Tego typu struktury zostaną przekazane przez odwołanie do funkcji lokalnej.That struct type would be passed by reference to the local function. Różnica w implementacji będzie zapisywać w alokacji.This difference in implementation would save on an allocation.

Wystąpienia niezbędne dla wyrażeń lambda oznacza alokacje dodatkową pamięć, która może być czynnikiem wydajności ścieżek kodu wrażliwego na czas.The instantiation necessary for lambda expressions means extra memory allocations, which may be a performance factor in time-critical code paths. Funkcje lokalne nie są naliczane to obciążenie.Local functions do not incur this overhead. W powyższym przykładzie wersja funkcji lokalnych ma 2 alokacje mniej niż wersja wyrażenia lambda.In the example above, the local functions version has 2 fewer allocations than the lambda expression version.

Uwaga

Funkcja lokalna wielokrotność ta metoda używa również klasy do zamknięcia.The local function equivalent of this method also uses a class for the closure. Czy zamknięcia dla funkcji lokalnej jest implementowany jako class lub struct jest szczegółowo opisuje implementacja.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 natomiast lambda będzie zawsze 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.";
    }
}

Jedną z zalet końcowego nie zostało to pokazane w tym przykładzie jest, można zaimplementować jako Iteratory, za pomocą funkcji lokalnych yield return składni, aby wygenerować je sekvence hodnot.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. yield return Instrukcji jest niedozwolone w wyrażeniach lambda.The yield return statement is not allowed in lambda expressions.

Funkcje lokalne, może wydawać się nadmiarowe wyrażenia lambda, mogą faktycznie służyć do różnych celów i mają różne sposoby zastosowania.While local functions may seem redundant to lambda expressions, they actually serve different purposes and have different uses. Funkcje lokalne są bardziej efektywne w przypadku, gdy chcesz napisać funkcję, która jest wywoływana tylko w kontekście 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.