C# Academy · Lekcja

Domknięcia i przechwytywane zmienne

Zobacz, jak funkcje lokalne przechwytują stan.

Lekcja 2 z 413 kroki

Domknięcia i przechwytywane zmienne to bezpłatna lekcja C# Academy na CoddyKit. To lekcja 2 z 4. Możesz przeczytać całą lekcję poniżej za darmo — a potem ćwiczyć ją interaktywnie w przeglądarce z wbudowanym edytorem kodu i tutorem AI dostępnym 24/7. To część ścieżki edukacyjnej C# Academy, a Twój postęp synchronizuje się między webem a aplikacją CoddyKit. Kurs C# Academy zawiera 4 lekcji w sumie.

Domknięcia przechwytujące stan lokalny

Funkcja lokalna (lub lambda) może odczytywać i modyfikować zmienne z otaczającej metody. Nazywa się to przechwytywaniem, a funkcja wraz z przechwyconymi zmiennymi tworzy domknięcie.

Przechwytywanie zmiennej

Funkcja lokalna korzysta bezpośrednio ze zmiennej factor z otaczającej metody. Nie otrzymuje jej jako parametru, lecz przechwytuje ją z otaczającego zakresu.

using System;

public class Program
{
    public static void Main()
    {
        int factor = 3;
        int Scale(int n) => n * factor; // captures factor

        Console.WriteLine(Scale(5));
        Console.WriteLine(Scale(10));
    }
}

Przechwycone zmienne są żywymi odwołaniami

Domknięcie przechwytuje zmienną, a nie migawkę jej wartości. Jeśli zmienna zmieni się po zdefiniowaniu funkcji, funkcja zobaczy nową wartość.

using System;

public class Program
{
    public static void Main()
    {
        int offset = 10;
        int Add(int n) => n + offset;

        Console.WriteLine(Add(1)); // 11
        offset = 100;
        Console.WriteLine(Add(1)); // 101 - sees updated offset
    }
}

Modyfikowanie przechwyconych zmiennych

Domknięcie może zapisywać do przechwyconej zmiennej, a zmiana jest również widoczna w otaczającej metodzie. Dzięki temu domknięcia dobrze nadają się do gromadzenia stanu.

using System;

public class Program
{
    public static void Main()
    {
        int count = 0;
        void Tick() => count++;

        Tick(); Tick(); Tick();
        Console.WriteLine(count); // 3
    }
}

Zwracanie domknięcia

Gdy zwracana lambda przechwytuje zmienne lokalne, zmienne te żyją dłużej niż metoda. Przechwycony stan istnieje tak długo, jak długo istnieje delegat.

using System;

public class Program
{
    static Func<int> MakeCounter()
    {
        int count = 0;
        return () => ++count; // captures count
    }

    public static void Main()
    {
        var next = MakeCounter();
        Console.WriteLine(next());
        Console.WriteLine(next());
        Console.WriteLine(next());
    }
}

Niezależne domknięcia

Każde wywołanie fabryki tworzy nowy zestaw przechwyconych zmiennych. Dwa utworzone w ten sposób liczniki nie wpływają na siebie.

using System;

public class Program
{
    static Func<int> MakeCounter()
    {
        int count = 0;
        return () => ++count;
    }

    public static void Main()
    {
        var a = MakeCounter();
        var b = MakeCounter();
        Console.WriteLine(a()); // 1
        Console.WriteLine(a()); // 2
        Console.WriteLine(b()); // 1 - separate state
    }
}

Poprawka dotycząca przechwytywania w foreach

We współczesnym języku C# zmienna pętli foreach jest tworzona na nowo w każdej iteracji, dlatego domknięcia przechwytują oczekiwaną wartość. Każda z poniższych lambd zapamiętuje własny element.

using System;
using System.Collections.Generic;

public class Program
{
    public static void Main()
    {
        var actions = new List<Action>();
        foreach (var i in new[] { 1, 2, 3 })
            actions.Add(() => Console.WriteLine(i));

        foreach (var act in actions) act(); // prints 1, 2, 3
    }
}

Przechwytywanie w pętli for

Klasyczna pułapka polega na tym, że jedna zmienna pętli for jest współdzielona między iteracjami, więc wszystkie domknięcia widzą jej końcową wartość. Aby przechwycić różne wartości, należy skopiować ją do zmiennej lokalnej tworzonej w każdej iteracji.

using System;
using System.Collections.Generic;

public class Program
{
    public static void Main()
    {
        var actions = new List<Action>();
        for (int i = 0; i < 3; i++)
        {
            int copy = i; // fresh local each iteration
            actions.Add(() => Console.WriteLine(copy));
        }
        foreach (var act in actions) act(); // prints 0, 1, 2
    }
}

Domknięcia przechwytują przez odwołanie, nie przez wartość

Ponieważ przechwytywanie odbywa się przez odwołanie, domknięcie zauważy modyfikację oryginalnej zmiennej po jej przechwyceniu. Należy o tym pamiętać, gdy znaczenie ma moment odczytu i zapisu.

using System;

public class Program
{
    public static void Main()
    {
        string message = "first";
        Action print = () => Console.WriteLine(message);

        print();          // first
        message = "second";
        print();          // second
    }
}

Praktyczne zastosowanie: konfigurowalne zachowanie

Domknięcia pozwalają tworzyć małe funkcje wstępnie wyposażone w kontekst. W tym przykładzie funkcja rabatowa przechwytuje stawkę, tworząc wielokrotnego użytku skonfigurowany kalkulator.

using System;

public class Program
{
    static Func<double, double> MakeDiscount(double rate)
        => price => price * (1 - rate);

    public static void Main()
    {
        var tenOff = MakeDiscount(0.10);
        var halfOff = MakeDiscount(0.50);
        Console.WriteLine(tenOff(100));
        Console.WriteLine(halfOff(100));
    }
}

Spróbuj samodzielnie

Utwórz fabrykę liczników, której zwracane domknięcie zachowuje prywatny stan, a następnie utwórz za jej pomocą dwa niezależne liczniki.

using System;

public class Program
{
    static Func<int> MakeStepper(int step)
    {
        int current = 0;
        return () => { current += step; return current; }; // captures current and step
    }

    public static void Main()
    {
        var byTwos = MakeStepper(2);
        var byTens = MakeStepper(10);
        Console.WriteLine(byTwos()); // 2
        Console.WriteLine(byTwos()); // 4
        Console.WriteLine(byTens()); // 10 - separate state
    }
}

Szybkie sprawdzenie

Przeanalizuj sposób działania przechwytywania.

Podsumowanie

Domknięcia przechwytują zmienne z otaczającego zakresu przez odwołanie.

  • Funkcja widzi bieżącą wartość zmiennej, w tym zmiany wprowadzone później.
  • Może modyfikować przechwycone zmienne, współdzieląc stan z metodą.
  • Zwrócone domknięcia utrzymują przy życiu przechwycone zmienne.
  • W pętli for należy używać zmiennej lokalnej tworzonej w każdej iteracji, aby przechwytywać różne wartości.
Bezpłatny start

Ucz się C# dzięki korepetycjom AI — za darmo

Pisz i uruchamiaj kod w przeglądarce, otrzymuj natychmiastową pomoc od korepetytora AI dostępnego 24/7 i kontynuuj naukę w sieci lub w aplikacji.

Kursy
93
Lekcje
346

Często zadawane pytania

Czy lekcja „Domknięcia i przechwytywane zmienne” jest bezpłatna?

Tak — pełny tekst „Domknięcia i przechwytywane zmienne” jest dostępny za darmo tutaj w sieci. Aby ćwiczyć ją interaktywnie (wbudowany edytor kodu i tutor AI dostępny 24/7) i odblokować resztę kursu C# Academy, przejdź na CoddyKit PRO. Kurs C# Academy zawiera 4 lekcji w sumie.

Co nauczysz się w „Domknięcia i przechwytywane zmienne”?

Zobacz, jak funkcje lokalne przechwytują stan. Ćwiczysz C# Academy z praktycznym kodem, który uruchamiasz bezpośrednio w przeglądarce, a tutor AI dostępny 24/7 odpowiada na Twoje pytania podczas pracy nad lekcją.

Czy potrzebuję doświadczenia, aby zacząć C# Academy?

Nie wymagamy żadnego doświadczenia. C# Academy w CoddyKit jest strukturyzowany dla początkujących i zaawansowanych użytkowników, więc możesz zacząć tutaj lub od początku i uczyć się w swoim tempie. To lekcja 2 z 4.

Ile czasu zajmuje lekcja „Domknięcia i przechwytywane zmienne”?

Większość lekcji CoddyKit trwa około 5–10 minut. Każda lekcja to mały, interaktywny krok, dzięki czemu robisz systematyczne postępy i zawsze wracasz dokładnie do tego samego miejsca — na webie i w aplikacji.

Czy mogę pisać i uruchamiać kod w tej lekcji C# Academy?

Tak. Każda lekcja C# Academy zawiera wbudowany edytor kodu, więc piszesz i uruchamiasz prawdziwy kod bezpośrednio w przeglądarce i od razu otrzymujesz sprzężenie zwrotne od AI — bez konfiguracji na komputerze.

Wszystkie lekcje w tym kursie

  1. Deklarowanie funkcji lokalnych
  2. Domknięcia i przechwytywane zmienne
  3. Statyczne funkcje lokalne
  4. Funkcje lokalne a lambdy
← Powrót do C# Academy