C# Language
Lambda Expressions
Ricerca…
Osservazioni
chiusure
Le espressioni lambda catturano implicitamente le variabili utilizzate e creano una chiusura . Una chiusura è una funzione insieme a un contesto statale. Il compilatore genererà una chiusura ogni volta che un'espressione lambda "racchiude" un valore dal suo contesto circostante.
Ad esempio quando viene eseguito quanto segue
Func<object, bool> safeApplyFiltererPredicate = o => (o != null) && filterer.Predicate(i);
safeApplyFilterPredicate
fa riferimento a un oggetto appena creato che ha un riferimento privato al valore corrente del filterer
e il cui metodo Invoke
si comporta come
o => (o != null) && filterer.Predicate(i);
Questo può essere importante, poiché finché viene mantenuto il riferimento al valore ora in safeApplyFilterPredicate
, ci sarà un riferimento all'oggetto cui si riferisce attualmente il filterer
. Ciò ha un effetto sulla garbage collection e può causare comportamenti imprevisti se l'oggetto al quale il filterer
riferisce attualmente è mutato.
D'altra parte, le chiusure possono essere utilizzate per deliberare l'effetto di incapsulare un comportamento che implica riferimenti ad altri oggetti.
Per esempio
var logger = new Logger();
Func<int, int> Add1AndLog = i => {
logger.Log("adding 1 to " + i);
return (i + 1);
};
Le chiusure possono anche essere utilizzate per modellare macchine a stati:
Func<int, int> MyAddingMachine() {
var i = 0;
return x => i += x;
};
Espressioni lambda di base
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
Espressioni lambda di base con 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"}
Usare la sintassi lambda per creare una chiusura
Vedi le osservazioni per la discussione delle chiusure. Supponiamo di avere un'interfaccia:
public interface IMachine<TState, TInput>
{
TState State { get; }
public void Input(TInput input);
}
e quindi viene eseguito quanto segue:
IMachine<int, int> machine = ...;
Func<int, int> machineClosure = i => {
machine.Input(i);
return machine.State;
};
Ora machineClosure
fa riferimento a una funzione da int
a int
, che dietro le quinte usa l'istanza IMachine
cui fa riferimento la machine
per eseguire il calcolo. Anche se la machine
riferimento esce dall'ambito, finché l'oggetto machineClosure
viene mantenuto, l'istanza IMachine
originale verrà mantenuta come parte di una "chiusura", definita automaticamente dal compilatore.
Attenzione: questo può significare che la stessa chiamata di funzione restituisce valori diversi in momenti diversi (es. In questo esempio se la macchina mantiene una somma dei suoi ingressi). In molti casi, questo può essere inaspettato e deve essere evitato per qualsiasi codice in uno stile funzionale: le chiusure accidentali e inaspettate possono essere una fonte di bug.
Sintassi Lambda con corpo del blocco di istruzioni
Func<int, string> doubleThenAddElevenThenQuote = i => {
var doubled = 2 * i;
var addedEleven = 11 + doubled;
return $"'{addedEleven}'";
};
Espressioni Lambda con System.Linq.Expressions
Expression<Func<int, bool>> checkEvenExpression = i => i%2 == 0;
// lambda expression is automatically converted to an Expression<Func<int, bool>>