C# Language
Lambda-uitdrukkingen
Zoeken…
Opmerkingen
sluitingen
Lambda-expressies zullen impliciet gebruikte variabelen vastleggen en een afsluiting creëren . Een sluiting is een functie samen met een bepaalde toestandscontext. De compiler genereert een afsluiting wanneer een lambda-expressie een waarde uit de omliggende context 'omsluit'.
Bijvoorbeeld wanneer het volgende wordt uitgevoerd
Func<object, bool> safeApplyFiltererPredicate = o => (o != null) && filterer.Predicate(i);
safeApplyFilterPredicate
verwijst naar een nieuw gemaakt object dat een privéverwijzing heeft naar de huidige waarde van filterer
en waarvan de Invoke
methode zich gedraagt als
o => (o != null) && filterer.Predicate(i);
Dit kan belangrijk zijn, want zolang de verwijzing naar de waarde nu in safeApplyFilterPredicate
wordt gehandhaafd, is er een verwijzing naar het object waarnaar het filterer
momenteel verwijst. Dit heeft effect op het verzamelen van afval en kan onverwacht gedrag veroorzaken als het object waarnaar het filterer
momenteel verwijst, is gemuteerd.
Aan de andere kant kunnen sluitingen worden gebruikt om het effect te beraadslagen om een gedrag in te kapselen dat referenties naar andere objecten omvat.
Eg
var logger = new Logger();
Func<int, int> Add1AndLog = i => {
logger.Log("adding 1 to " + i);
return (i + 1);
};
Sluitingen kunnen ook worden gebruikt om state machines te modelleren:
Func<int, int> MyAddingMachine() {
var i = 0;
return x => i += x;
};
Basis lambda-uitdrukkingen
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
Basis lambda-uitdrukkingen met 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"}
Lambda-syntaxis gebruiken om een sluiting te maken
Zie opmerkingen voor bespreking van sluitingen. Stel dat we een interface hebben:
public interface IMachine<TState, TInput>
{
TState State { get; }
public void Input(TInput input);
}
en dan wordt het volgende uitgevoerd:
IMachine<int, int> machine = ...;
Func<int, int> machineClosure = i => {
machine.Input(i);
return machine.State;
};
Nu verwijst machineClosure
naar een functie van int
tot int
, die achter de schermen de IMachine
instantie gebruikt waarnaar de machine
verwijst om de berekening uit te voeren. Zelfs als de referentie machine
buiten het bereik valt, mits de machineClosure
object wordt gehandhaafd, de oorspronkelijke IMachine
instantie wordt gehandhaafd als onderdeel van een 'afsluiting', automatisch gedefinieerd door de compiler.
Waarschuwing: dit kan betekenen dat dezelfde functieaanroep verschillende waarden op verschillende tijdstippen retourneert (bijvoorbeeld in dit voorbeeld als de machine een som van zijn ingangen bewaart). In veel gevallen kan dit onverwacht zijn en moet worden vermeden voor elke code in een functionele stijl - onopzettelijke en onverwachte sluitingen kunnen een bron van fouten zijn.
Lambda-syntaxis met body met statement block
Func<int, string> doubleThenAddElevenThenQuote = i => {
var doubled = 2 * i;
var addedEleven = 11 + doubled;
return $"'{addedEleven}'";
};
Lambda-expressies met System.Linq.Expressions
Expression<Func<int, bool>> checkEvenExpression = i => i%2 == 0;
// lambda expression is automatically converted to an Expression<Func<int, bool>>