Szukaj…


Uwagi

Domknięcia

Wyrażenia lambda domyślnie przechwycą użyte zmienne i utworzą zamknięcie . Zamknięcie jest funkcją wraz z pewnym kontekstem stanu. Kompilator wygeneruje zamknięcie za każdym razem, gdy wyrażenie lambda „otacza” wartość z otaczającego kontekstu.

Np. Po wykonaniu następujących czynności

Func<object, bool> safeApplyFiltererPredicate = o => (o != null) && filterer.Predicate(i);

safeApplyFilterPredicate odnosi się do nowo utworzonego obiektu, który ma prywatne odwołanie do bieżącej wartości filterer i którego metoda Invoke zachowuje się jak

o => (o != null) && filterer.Predicate(i);

Może to być ważne, ponieważ, dopóki odniesienia do wartości już w safeApplyFilterPredicate utrzymuje nastąpi odniesienie do obiektu, który filterer aktualnie dotyczy. Ma to wpływ na zbieranie śmieci i mogą powodować nieoczekiwany sposób, że obiekt, który filterer odnosi się obecnie jest zmutowana.

Z drugiej strony, zamknięcia mogą być wykorzystane do zamierzonego efektu, aby zawrzeć zachowanie, które obejmuje odniesienia do innych obiektów.

Na przykład

var logger = new Logger();
Func<int, int> Add1AndLog = i => {
    logger.Log("adding 1 to " + i);
    return (i + 1);
};

Zamknięć można również używać do modelowania automatów stanów:

Func<int, int> MyAddingMachine() {
    var i = 0;
    return x => i += x;
};

Podstawowe wyrażenia lambda

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

Podstawowe wyrażenia lambda z 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"}

Używanie składni lambda do utworzenia zamknięcia

Zobacz uwagi do dyskusji na temat zamknięć. Załóżmy, że mamy interfejs:

public interface IMachine<TState, TInput>
{
    TState State { get; }
    public void Input(TInput input);
}

a następnie wykonywane są następujące czynności:

IMachine<int, int> machine = ...;
Func<int, int> machineClosure = i => {
    machine.Input(i);
    return machine.State;
};

Teraz machineClosure odnosi się do funkcji od int do int , która za kulisami używa instancji IMachine której odnosi się machine w celu przeprowadzenia obliczeń. Nawet jeśli machine referencyjna wykracza poza zakres, tak długo, jak długo utrzymywany jest obiekt machineClosure , oryginalna instancja IMachine zostanie zachowana jako część „zamknięcia”, automatycznie zdefiniowanego przez kompilator.

Ostrzeżenie: może to oznaczać, że to samo wywołanie funkcji zwraca różne wartości w różnych momentach (np. W tym przykładzie, jeśli maszyna zachowuje sumę swoich danych wejściowych). W wielu przypadkach może to być nieoczekiwane i należy tego unikać w przypadku każdego kodu w funkcjonalnym stylu - przypadkowe i nieoczekiwane zamknięcie może być źródłem błędów.

Składnia lambda z treścią bloku instrukcji

Func<int, string> doubleThenAddElevenThenQuote = i => {
    var doubled = 2 * i;
    var addedEleven = 11 + doubled;
    return $"'{addedEleven}'";
};

Wyrażenia lambda z System.Linq.Expressions

Expression<Func<int, bool>> checkEvenExpression = i => i%2 == 0;
// lambda expression is automatically converted to an Expression<Func<int, bool>>


Modified text is an extract of the original Stack Overflow Documentation
Licencjonowany na podstawie CC BY-SA 3.0
Nie związany z Stack Overflow