C# Language
Lambda-Ausdrücke
Suche…
Bemerkungen
Verschlüsse
Lambda-Ausdrücke erfassen implizit die verwendeten Variablen und erzeugen eine Schließung . Ein Abschluss ist eine Funktion zusammen mit einem Zustandskontext. Der Compiler generiert einen Abschluss, wenn ein Lambda-Ausdruck einen Wert aus dem umgebenden Kontext "umschließt".
ZB wenn das Folgende ausgeführt wird
Func<object, bool> safeApplyFiltererPredicate = o => (o != null) && filterer.Predicate(i);
safeApplyFilterPredicate
bezieht sich auf ein neu erstelltes Objekt, das einen privaten Verweis auf den aktuellen Wert des filterer
hat und dessen Invoke
Methode sich so verhält
o => (o != null) && filterer.Predicate(i);
Dies kann wichtig sein, denn solange der Verweis auf den Wert in safeApplyFilterPredicate
beibehalten wird, gibt es einen Verweis auf das Objekt, auf das sich der filterer
aktuell bezieht. Dies hat Auswirkungen auf die Garbage Collection und kann zu einem unerwarteten Verhalten führen, wenn das Objekt, auf das der filterer
aktuell verweist, mutiert ist.
Auf der anderen Seite können Verschlüsse verwendet werden, um absichtlich einen Effekt abzukapseln, um ein Verhalten zu kapseln, das Verweise auf andere Objekte beinhaltet.
Z.B
var logger = new Logger();
Func<int, int> Add1AndLog = i => {
logger.Log("adding 1 to " + i);
return (i + 1);
};
Verschlüsse können auch zum Modellieren von Zustandsmaschinen verwendet werden:
Func<int, int> MyAddingMachine() {
var i = 0;
return x => i += x;
};
Grundlegende Lambda-Ausdrücke
Func<int, int> add1 = i => i + 1;
Func<int, int, int> add = (i, j) => i + j;
// Behaviourally equivalent to:
int Add1(int i)
{
return i + 1;
}
int Add(int i, int j)
{
return i + j;
}
...
Console.WriteLine(add1(42)); //43
Console.WriteLine(Add1(42)); //43
Console.WriteLine(add(100, 250)); //350
Console.WriteLine(Add(100, 250)); //350
Grundlegende Lambda-Ausdrücke mit LINQ
// assume source is {0, 1, 2, ..., 10}
var evens = source.Where(n => n%2 == 0);
// evens = {0, 2, 4, ... 10}
var strings = source.Select(n => n.ToString());
// strings = {"0", "1", ..., "10"}
Verwenden der Lambda-Syntax zum Erstellen eines Abschlusses
Siehe Anmerkungen zur Erörterung von Schließungen. Angenommen, wir haben eine Schnittstelle:
public interface IMachine<TState, TInput>
{
TState State { get; }
public void Input(TInput input);
}
und dann wird folgendes ausgeführt:
IMachine<int, int> machine = ...;
Func<int, int> machineClosure = i => {
machine.Input(i);
return machine.State;
};
machineClosure
bezieht sich jetzt auf eine Funktion von int
bis int
, die im Hintergrund die Instanz von IMachine
verwendet, auf die sich die machine
bezieht, um die Berechnung durchzuführen. Auch wenn die Referenz machine
außerhalb des Bereichs geht, solange das machineClosure
Objekt beibehalten wird, das ursprüngliche IMachine
wird beispielsweise als Teil einer ‚Schließung‘ zurückgehalten wird, automatisch durch den Compiler definiert.
Achtung: Dies kann bedeuten, dass der gleiche Funktionsaufruf zu unterschiedlichen Zeitpunkten unterschiedliche Werte zurückgibt (z. B. in diesem Beispiel, wenn die Maschine eine Summe ihrer Eingaben enthält). In vielen Fällen kann dies unerwartet sein und ist für jeden Code in einem funktionalen Stil zu vermeiden - versehentliche und unerwartete Schließungen können Fehler verursachen.
Lambda-Syntax mit Anweisungsblockkörper
Func<int, string> doubleThenAddElevenThenQuote = i => {
var doubled = 2 * i;
var addedEleven = 11 + doubled;
return $"'{addedEleven}'";
};
Lambda-Ausdrücke mit System.Linq.Expressions
Expression<Func<int, bool>> checkEvenExpression = i => i%2 == 0;
// lambda expression is automatically converted to an Expression<Func<int, bool>>