Lokale Funktionen im Vergleich zu Lambdaausdrücken

Sie können in manchen Anwendungsfällen einen Lambdaausdruck erstellen und ihn verwenden, ohne eine lokale Funktion erstellen zu müssen. Hier ist ein Beispiel für eine asynchrone Methode, die genau das macht:

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

Es gibt jedoch eine Anzahl von Gründen, lokale Funktionen lieber zu verwenden, anstatt Lambdaausdrücke zu definieren und aufzurufen.

Für Lambdaausdrücke muss der Compiler zunächst eine anonyme Klasse und eine Instanz dieser Klasse erstellen, um alle Variablen zu speichern, die vom Abschluss erfasst wurden. Der Abschluss dieses Lambdaausdrucks enthält die Variablen address, index und name.

Zweitens werden Lambdaausdrücke durch das Instanziieren und Abrufen eines Delegats implementiert. Lokale Funktionen werden als Methodenaufrufe implementiert. Die für Lambdaausdrücke erforderliche Instanziierung bedeutet zusätzliche Speicherbelegung, was ein Leistungsfaktor in zeitkritischen Codepfaden sein kann. Lokale Funktionen erfordern diesen Mehraufwand nicht.

Drittens können lokale Funktionen aufgerufen werden, bevor sie definiert werden. Lambdaausdrücke müssen deklariert werden, bevor sie definiert werden. Dies bedeutet, dass lokale Funktionen einfacher in rekursiven Algorithmen verwendet werden:

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:

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

Beachten Sie, dass die Version mit Lambdaausdrücken den Lambdaausdruck nthFactorial deklarieren und initialisieren muss, bevor er definiert wird. Wird das nicht gemacht, führt dies zu einem Kompilierzeitfehler, weil auf nthFactorial verwiesen wurde, bevor es zugewiesen wurde. Rekursiver Algorithmen sind einfacher mit lokalen Funktion zu erstellen.

Während lokale Funktionen für Lambdaausdrücke als überflüssig erscheinen, dienen sie tatsächlich anderen Zwecken und haben unterschiedliche Verwendungen. Lokale Funktionen sind effizienter, im Fall dass Sie eine Funktion schreiben möchten, die nur aus dem Kontext einer anderen Methode abgerufen wird.