Lokale Funktionen, die im Vergleich zu Lambda-AusdrückeLocal functions compared to lambda expressions

Auf den ersten Blick sind lokale Funktionen und Lambdaausdrücke sehr ähnlich.At first glance, local functions and lambda expressions are very similar. In vielen Fällen ist die Wahl zwischen der Verwendung von Lambda-Ausdrücke und lokale Funktionen nur wenige der Stil und einem persönlichen Vorlieben.In many cases, the choice between using lambda expressions and local functions is a matter of style and personal preference. Es gibt jedoch real Unterschiede in den dort können Sie verwenden eine oder andere, die Sie kennen sollten.However, there are real differences in where you can use one or the other that you should be aware of.

Sehen wir uns die Unterschiede zwischen der Implementierungen des Fakultätsalgorithmus als lokale Funktion und als Lambdaausdruck an.Let's examine the differences between the local function and lambda expression implementations of the factorial algorithm. Erste die Version mit einer lokalen Funktion: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);
}

Vergleichen Sie diese Implementierung mit einer Version, die Lambdaausdrücke verwendet: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);
}

Der lokalen Funktionen haben Namen.The local functions have names. Der Lambda-Ausdrücke sind anonyme Methoden, die auf Variablen zugewiesen sind, sind Func oder Action Typen.The lambda expressions are anonymous methods that are assigned to variables that are Func or Action types. Wenn Sie eine lokale Funktion deklarieren, gehören die Argumenttypen und der Rückgabetyp der Funktionsdeklaration.When you declare a local function, the argument types and return type are part of the function declaration. Statt Bestandteil der Text des Lambda-Ausdrucks gehören Ausdruck, der Argumenttypen und der Rückgabetyp des Lambda-Ausdrucks Variablentyp Deklaration.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. Diese zwei Unterschiede klarer Code zur Folge.Those two differences may result in clearer code.

Lokale Funktionen besitzen verschiedene Regeln für die definitive Zuweisung als Lambda-Ausdrücke.Local functions have different rules for definite assignment than lambda expressions. Eine lokale Funktionsdeklaration kann von jedem Ort Code verwiesen werden, in denen sich im Bereich befindet.A local function declaration can be referenced from any code location where it is in scope. Ein Lambda-Ausdruck muss in einer zuweisungsanordnung zugewiesen werden, bevor Zugriff auf (oder möglich durch die Delgate verweisen auf den Lambda-Ausdruck aufgerufen.) Beachten Sie, dass die Version mit Lambdaausdrücken den Lambdaausdruck nthFactorial deklarieren und initialisieren muss, bevor er definiert wird.A lambda expression must be assigned to a delegate variable before it can be accessed (or called through the delgate referencing the lambda expression.) Notice that the version using the lambda expression must declare and initialize the lambda expression, nthFactorial before defining it. Wird das nicht gemacht, führt dies zu einem Kompilierzeitfehler, weil auf nthFactorial verwiesen wurde, bevor es zugewiesen wurde.Not doing so results in a compile time error for referencing nthFactorial before assigning it. Das bedeutet, rekursive Algorithmen einfacher zu erstellen, mit der lokalen Funktionen sind.These differences mean that recursive algorithms are easier to create using local functions. Sie können deklarieren und definieren eine lokale Funktion, die sich selbst aufruft.You can declare and define a local function that calls itself. Lambda-Ausdrücke müssen deklariert werden und ein Standardwert zugewiesen werden, bevor sie einen Text neu zugewiesen werden können, die der gleichen Lambdaausdruck verweist auf.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.

Definitiven Zuweisungsregeln wirken sich auch auf alle Variablen, die von der lokalen Epression der Funktion oder Lambda-Ausdruck erfasst werden.Definite assignment rules also affect any variables that are captured by the local function or lamdba epression. Lokale Funktionen und Lambda-Ausdruck Regeln fordern, dass alle erfassten Variablen an dem Punkt definitiv zugewiesen werden, wenn der lokale-Funktion oder einen Lambda-Ausdruck in einen Delegaten konvertiert wird.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. Der Unterschied ist, dass Lambda-Ausdrücke in Delegaten konvertiert werden, wenn sie deklariert werden.The difference is that lambda expressions are converted to delegates when they are declared. Lokale Funktionen werden in Delegaten nur bei Verwendung als einen Delegaten konvertiert.Local functions are converted to delegates only when used as a delegate. Wenn Sie eine lokale zu deklarieren und darauf nur verweisen, indem Sie diese wie eine Methode aufruft, wird er nicht in einen Delegaten konvertiert werden.If you declare a local function and only reference it by calling it like a method, it will not be converted to a delegate. Diese Regel können Sie eine lokale Funktion an einen beliebigen Ort in seinem einschließenden Bereich zu deklarieren.That rule enables you to declare a local function at any convenient location in its enclosing scope. Es ist üblich, deklarieren Sie lokale Funktionen am Ende der übergeordneten Methode nach return-Anweisungen.It's common to declare local functions at the end of the parent method, after any return statements.

Der Compiler kann im dritten statische Analysen durchführen, die lokale Funktionen erfasste Variablen im einschließenden Bereich definitiv zugewiesen ermöglicht.Third, the compiler can perform static analysis that enables local functions to definitely assign captured variables in the enclosing scope. Betrachten Sie das folgende Beispiel:Consider this example:

bool M()
{
    int y;
    Local();
    return y;

    void Local() => y = 0;
}

Der Compiler können Sie ermitteln, Local definitiv zugewiesen y beim Aufruf.The compiler can determine that Local definitely assigns y when called. Da Local wird aufgerufen, bevor die return -Anweisung y Definitiely wird zugewiesen werden, auf die return Anweisung.Because Local is called before the return statement, y is definitiely assigned at the return statement.

Die Analyse, die es ermöglicht, dass die Analyse den vierten Unterschied ermöglicht.The analysis that enables that analysis enables the fourth difference. Je nach ihrer Verwendung können lokale Funktionen Heapzuordnungen vermeiden, die immer für Lambda-Ausdrücke erforderlich sind.Depending on their use, local functions can avoid heap allocations that are always necessary for lambda expressions. Wenn eine lokale Funktion niemals in einen Delegaten konvertiert wird und keines der Variablen, die von der lokalen Funktion erfasst, die von anderen Lambdas oder lokale Funktionen, die in Delegaten konvertiert werden aufgezeichnet wird, kann der Compiler Heapzuordnungen vermeiden.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.

Betrachten Sie das folgende asynchrone Beispiel: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();
}

Der Abschluss dieses Lambdaausdrucks enthält die Variablen address, index und name.The closure for this lambda expression contains the address, index and name variables. Im Fall von lokalen Funktionen ist das Objekt, das den Abschluss implementiert, möglicherweise vom Typ struct.In the case of local functions, the object that implements the closure may be a struct type. Dieses Strukturtyps würde durch Verweis auf die lokale Funktion übergeben werden.That struct type would be passed by reference to the local function. Dieser Unterschied in der Implementierung würde auf eine Zuweisung zu speichern.This difference in implementation would save on an allocation.

Die Instanziierung für Lambda-Ausdrücke erforderlich bedeutet, dass zusätzliche speicherbelegungen, u. u. einen Faktor in zeitkritischem Codepfaden.The instantiation necessary for lambda expressions means extra memory allocations, which may be a performance factor in time-critical code paths. Lokale Funktionen erfordern diesen Mehraufwand nicht.Local functions do not incur this overhead. Im obigen Beispiel hat die Version mit der lokalen Funktion zwei Zuordnungen weniger als die Version mit dem Lambdaausdruck.In the example above, the local functions version has 2 fewer allocations than the lambda expression version.

Hinweis

Die Entsprechung dieser Methode mit der lokalen Funktion verwendet auch eine Klasse für den Abschluss.The local function equivalent of this method also uses a class for the closure. Ob der Abschluss für eine lokale Funktion als class oder struct implementiert wird, ist ein Implementierungsdetail.Whether the closure for a local function is implemented as a class or a struct is an implementation detail. Eine lokale Funktion verwendet möglicherweise struct, während ein Lambdaausdruck immer class nutzt.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.";
    }
}

Eine letzter Vorteil, der in diesem Beispiel zu kurz gekommen ist, besteht darin, dass lokale Funktionen mithilfe der yield return-Syntax als Iteratoren implementiert werden können, um eine Sequenz von Werten zu erzeugen.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. Die yield return Anweisung ist in Lambda-Ausdrücken nicht zulässig.The yield return statement is not allowed in lambda expressions.

Während lokale Funktionen für Lambdaausdrücke als überflüssig erscheinen, dienen sie tatsächlich anderen Zwecken und haben unterschiedliche Verwendungen.While local functions may seem redundant to lambda expressions, they actually serve different purposes and have different uses. Lokale Funktionen sind effizienter, im Fall dass Sie eine Funktion schreiben möchten, die nur aus dem Kontext einer anderen Methode abgerufen wird.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.