C# Language
Lambda-uttryck
Sök…
Anmärkningar
nedläggningar
Lambda-uttryck kommer implicit att fånga variabler som används och skapa en stängning . En nedläggning är en funktion tillsammans med viss statlig kontext. Kompilatorn genererar en stängning närhelst ett lambda-uttryck "stänger" ett värde från dess omgivande sammanhang.
Exempelvis när följande körs
Func<object, bool> safeApplyFiltererPredicate = o => (o != null) && filterer.Predicate(i);
safeApplyFilterPredicate
hänvisar till ett nyligen skapat objekt som har en privat referens till det aktuella värdet för filterer
, och vars Invoke
metod fungerar som
o => (o != null) && filterer.Predicate(i);
Detta kan vara viktigt, eftersom så länge referensen till värdet nu i safeApplyFilterPredicate
upprätthålls kommer det att finnas en hänvisning till det objekt som filterer
närvarande hänvisar till. Detta påverkar skräpuppsamlingen och kan orsaka oväntat beteende om objektet som filterer
närvarande hänvisar till är muterat.
Å andra sidan kan stängningar användas för avsiktlig effekt för att kapsla in ett beteende som involverar referenser till andra objekt.
T.ex
var logger = new Logger();
Func<int, int> Add1AndLog = i => {
logger.Log("adding 1 to " + i);
return (i + 1);
};
Stängningar kan också användas för att modellera tillståndsmaskiner:
Func<int, int> MyAddingMachine() {
var i = 0;
return x => i += x;
};
Grundläggande lambda-uttryck
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
Grundläggande lambda-uttryck med 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"}
Använda lambda-syntax för att skapa en nedläggning
Se anmärkningar för diskussion om nedläggningar. Anta att vi har ett gränssnitt:
public interface IMachine<TState, TInput>
{
TState State { get; }
public void Input(TInput input);
}
och sedan exekveras följande:
IMachine<int, int> machine = ...;
Func<int, int> machineClosure = i => {
machine.Input(i);
return machine.State;
};
Nu hänvisar machineClosure
till en funktion från int
till int
, som bakom kulisserna använder IMachine
instansen som machine
refererar till för att utföra beräkningen. Även om machine
går ut ur räckvidd, så länge som machineClosure
objektet bibehålls, den ursprungliga IMachine
instans kommer att behållas som en del av en 'förslutning', automatiskt definieras av kompilatorn.
Varning: detta kan innebära att samma funktionssamtal returnerar olika värden vid olika tidpunkter (t.ex. i detta exempel om maskinen har en summa av sina ingångar). I många fall kan detta vara oväntat och ska undvikas för alla kod i funktionell stil - oavsiktliga och oväntade stängningar kan vara en källa till buggar.
Lambda syntax med uttalande block body
Func<int, string> doubleThenAddElevenThenQuote = i => {
var doubled = 2 * i;
var addedEleven = 11 + doubled;
return $"'{addedEleven}'";
};
Lambda-uttryck med System.Linq.Expressions
Expression<Func<int, bool>> checkEvenExpression = i => i%2 == 0;
// lambda expression is automatically converted to an Expression<Func<int, bool>>